From 41c6e0c11be1b94befd91509440aa4e0c74c070b Mon Sep 17 00:00:00 2001 From: christophersansone Date: Fri, 12 Apr 2024 19:09:05 -0400 Subject: [PATCH 1/4] defaultValue function results should persist after initialization --- packages/json-api/src/-private/cache.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/json-api/src/-private/cache.ts b/packages/json-api/src/-private/cache.ts index a2f94212e5c..e4181554849 100644 --- a/packages/json-api/src/-private/cache.ts +++ b/packages/json-api/src/-private/cache.ts @@ -70,6 +70,7 @@ interface CachedResource { id: string | null; remoteAttrs: Record | null; localAttrs: Record | null; + defaultAttrs: Record | null; inflightAttrs: Record | null; changes: Record | null; errors: JsonApiError[] | null; @@ -1036,6 +1037,7 @@ export default class JSONAPICache implements Cache { // we report as `isEmpty` during teardown. cached.localAttrs = null; cached.remoteAttrs = null; + cached.defaultAttrs = null; cached.inflightAttrs = null; const relatedIdentifiers = _allRelatedIdentifiers(storeWrapper, identifier); @@ -1096,11 +1098,18 @@ export default class JSONAPICache implements Cache { return cached.inflightAttrs[attr]; } else if (cached.remoteAttrs && attr in cached.remoteAttrs) { return cached.remoteAttrs[attr]; + } else if (cached.defaultAttrs && attr in cached.defaultAttrs) { + return cached.defaultAttrs[attr]; } else { const attrSchema = this._capabilities.schema.fields(identifier).get(attr); upgradeCapabilities(this._capabilities); - return getDefaultValue(attrSchema, identifier, this._capabilities._store); + const defaultValue = getDefaultValue(attrSchema, identifier, this._capabilities._store); + if (typeof attrSchema?.options?.defaultValue === 'function') { + cached.defaultAttrs = cached.defaultAttrs || (Object.create(null) as Record); + cached.defaultAttrs[attr] = defaultValue; + } + return defaultValue; } } @@ -1133,6 +1142,10 @@ export default class JSONAPICache implements Cache { delete cached.changes![attr]; } + if (cached.defaultAttrs && attr in cached.defaultAttrs) { + delete cached.defaultAttrs[attr]; + } + this._capabilities.notifyChange(identifier, 'attributes', attr); } @@ -1195,6 +1208,7 @@ export default class JSONAPICache implements Cache { } cached.inflightAttrs = null; + cached.defaultAttrs = null; if (cached.errors) { cached.errors = null; From d788966a9f870dfc9e71d4ace8a3131d5b12a3cf Mon Sep 17 00:00:00 2001 From: christophersansone Date: Fri, 12 Apr 2024 19:40:28 -0400 Subject: [PATCH 2/4] defaultValue function results should persist after initialization --- packages/json-api/src/-private/cache.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/json-api/src/-private/cache.ts b/packages/json-api/src/-private/cache.ts index e4181554849..8179e2ae340 100644 --- a/packages/json-api/src/-private/cache.ts +++ b/packages/json-api/src/-private/cache.ts @@ -91,6 +91,7 @@ function makeCache(): CachedResource { id: null, remoteAttrs: null, localAttrs: null, + defaultAttrs: null, inflightAttrs: null, changes: null, errors: null, From 2e38bedca873bd2344e0724b89cc5ea311f54b9d Mon Sep 17 00:00:00 2001 From: christophersansone Date: Tue, 30 Apr 2024 16:27:25 -0400 Subject: [PATCH 3/4] clear default values on patch changes --- packages/json-api/src/-private/cache.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/json-api/src/-private/cache.ts b/packages/json-api/src/-private/cache.ts index 8179e2ae340..e35261d95be 100644 --- a/packages/json-api/src/-private/cache.ts +++ b/packages/json-api/src/-private/cache.ts @@ -1650,7 +1650,7 @@ function setupRelationships( } function patchLocalAttributes(cached: CachedResource): boolean { - const { localAttrs, remoteAttrs, inflightAttrs, changes } = cached; + const { localAttrs, remoteAttrs, inflightAttrs, defaultAttrs, changes } = cached; if (!localAttrs) { cached.changes = null; return false; @@ -1672,6 +1672,10 @@ function patchLocalAttributes(cached: CachedResource): boolean { delete localAttrs[attr]; delete changes![attr]; } + + if (defaultAttrs && attr in defaultAttrs) { + delete defaultAttrs[attr]; + } } return hasAppliedPatch; } From f96cb82985acf45e082c988cd995a52fc440d863 Mon Sep 17 00:00:00 2001 From: christophersansone Date: Tue, 30 Apr 2024 17:28:47 -0400 Subject: [PATCH 4/4] defaultValue caching test --- .../cache/resource-data-documents-test.ts | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests/ember-data__json-api/tests/integration/cache/resource-data-documents-test.ts b/tests/ember-data__json-api/tests/integration/cache/resource-data-documents-test.ts index 24d16f2904d..610d5ae4e7a 100644 --- a/tests/ember-data__json-api/tests/integration/cache/resource-data-documents-test.ts +++ b/tests/ember-data__json-api/tests/integration/cache/resource-data-documents-test.ts @@ -491,4 +491,77 @@ module('Integration | @ember-data/json-api Cache.put()', f 'We can fetch more included data from the cache' ); }); + + test('generated default values are retained', function (assert) { + const store = new TestStore(); + let i = 0; + + store.registerSchema( + new TestSchema<'user'>({ + user: { + attributes: { + name: { + kind: 'attribute', + name: 'name', + type: null, + options: { + defaultValue: () => { + i++; + return `Name ${i}`; + }, + }, + }, + }, + relationships: {}, + }, + }) + ); + + store._run(() => { + store.cache.put({ + content: { + data: { + type: 'user', + id: '1', + attributes: {}, + }, + }, + }) as SingleResourceDataDocument; + }); + const identifier = store.identifierCache.getOrCreateRecordIdentifier({ type: 'user', id: '1' }); + + const name1 = store.cache.getAttr(identifier, 'name'); + assert.equal(name1, 'Name 1', 'The default value was generated'); + const name2 = store.cache.getAttr(identifier, 'name'); + assert.equal(name2, 'Name 1', 'The default value was cached'); + + store.cache.setAttr(identifier, 'name', 'Chris'); + const name3 = store.cache.getAttr(identifier, 'name'); + assert.equal(name3, 'Chris', 'The value was updated'); + + store.cache.setAttr(identifier, 'name', null); + const name4 = store.cache.getAttr(identifier, 'name'); + assert.equal(name4, null, 'Null was set and maintained'); + + store.cache.rollbackAttrs(identifier); + const name5 = store.cache.getAttr(identifier, 'name'); + assert.equal(name5, 'Name 2', 'The default value was regenerated'); + + store._run(() => { + store.cache.put({ + content: { + data: { + type: 'user', + id: '1', + attributes: { + name: 'Tomster', + }, + }, + }, + }) as SingleResourceDataDocument; + }); + + const name6 = store.cache.getAttr(identifier, 'name'); + assert.equal(name6, 'Tomster', 'The value was updated on put'); + }); });