diff --git a/app/assets/javascripts/discourse/app/services/store.js b/app/assets/javascripts/discourse/app/services/store.js index 4eaf4b52115..4b0cfeeb822 100644 --- a/app/assets/javascripts/discourse/app/services/store.js +++ b/app/assets/javascripts/discourse/app/services/store.js @@ -141,7 +141,13 @@ export default Service.extend({ hydrated.get("content").map((item) => { let staleItem = stale.content.findBy(primaryKey, item.get(primaryKey)); if (staleItem) { - staleItem.setProperties(item); + for (const [key, value] of Object.entries( + Object.getOwnPropertyDescriptors(staleItem) + )) { + if (value.writable && value.enumerable) { + staleItem.set(key, value.value); + } + } } else { staleItem = item; } diff --git a/app/assets/javascripts/discourse/tests/helpers/create-store.js b/app/assets/javascripts/discourse/tests/helpers/create-store.js index f9ef5e5c8f2..aa224971c1b 100644 --- a/app/assets/javascripts/discourse/tests/helpers/create-store.js +++ b/app/assets/javascripts/discourse/tests/helpers/create-store.js @@ -6,11 +6,32 @@ import TopicTrackingState from "discourse/models/topic-tracking-state"; import { buildResolver } from "discourse-common/resolver"; import { currentSettings } from "discourse/tests/helpers/site-settings"; import Site from "discourse/models/site"; +import RestModel from "discourse/models/rest"; const CatAdapter = RestAdapter.extend({ primaryKey: "cat_id", }); +const CachedCatAdapter = RestAdapter.extend({ + primaryKey: "cat_id", + cache: true, + apiNameFor() { + return "cat"; + }, +}); + +const CachedCat = RestModel.extend({ + init(...args) { + // Simulate an implicit injection + Object.defineProperty(this, "injectedProperty", { + writable: false, + enumerable: true, + value: "hello world", + }); + this._super(...args); + }, +}); + export default function (customLookup = () => {}) { const resolver = buildResolver("discourse").create({ namespace: { modulePrefix: "discourse" }, @@ -28,6 +49,11 @@ export default function (customLookup = () => {}) { this._catAdapter || CatAdapter.create({ owner: this }); return this._catAdapter; } + if (type === "adapter:cached-cat") { + this._cachedCatAdapter = + this._cachedCatAdapter || CachedCatAdapter.create({ owner: this }); + return this._cachedCatAdapter; + } if (type === "adapter:rest") { if (!this._restAdapter) { this._restAdapter = RestAdapter.create({ owner: this }); @@ -56,6 +82,9 @@ export default function (customLookup = () => {}) { lookupFactory(type) { const split = type.split(":"); + if (type === "model:cached-cat") { + return CachedCat; + } return resolver.resolveOther({ type: split[0], fullNameWithoutType: split[1], diff --git a/app/assets/javascripts/discourse/tests/unit/services/store-test.js b/app/assets/javascripts/discourse/tests/unit/services/store-test.js index 639eac6134d..64caea852e6 100644 --- a/app/assets/javascripts/discourse/tests/unit/services/store-test.js +++ b/app/assets/javascripts/discourse/tests/unit/services/store-test.js @@ -96,6 +96,19 @@ module("Unit | Service | store", function () { ); }); + test("rehydrating stale results with implicit injections", async function (assert) { + const store = createStore(); + + const cat = (await store.find("cached-cat", { name: "souna" })).content[0]; + + assert.strictEqual(cat.name, "souna"); + + const stale = store.findStale("cached-cat", { name: "souna" }); + const refreshed = await stale.refresh(); + + assert.strictEqual(refreshed.content[0].name, "souna"); + }); + test("update", async function (assert) { const store = createStore(); const result = await store.update("widget", 123, { name: "hello" });