Skip to content

Commit

Permalink
Fix: @attr defaultValue() results should persist after init (#9355) (#…
Browse files Browse the repository at this point in the history
…9441)

* Fix: @attr defaultValue() results should persist after initialization (#9355)

* Backport of #9355

* defaultValue function results should persist after initialization

* clear default values on patch changes

* defaultValue caching test

---------

Co-authored-by: christophersansone <chris@voltage.io>

* Fix lint errors

---------

Co-authored-by: Christopher Sansone <chris@legendary.io>
Co-authored-by: christophersansone <chris@voltage.io>
Co-authored-by: Daniel Lubovich <dlubovich@cradlepoint.com>
  • Loading branch information
4 people committed May 16, 2024
1 parent 36b6513 commit cd70bee
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 2 deletions.
23 changes: 21 additions & 2 deletions packages/json-api/src/-private/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ interface CachedResource {
id: string | null;
remoteAttrs: Record<string, unknown> | null;
localAttrs: Record<string, unknown> | null;
defaultAttrs: Record<string, unknown> | null;
inflightAttrs: Record<string, unknown> | null;
changes: Record<string, unknown[]> | null;
errors: JsonApiError[] | null;
Expand All @@ -72,6 +73,7 @@ function makeCache(): CachedResource {
id: null,
remoteAttrs: null,
localAttrs: null,
defaultAttrs: null,
inflightAttrs: null,
changes: null,
errors: null,
Expand Down Expand Up @@ -871,6 +873,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;

let relatedIdentifiers = _allRelatedIdentifiers(storeWrapper, identifier);
Expand Down Expand Up @@ -939,9 +942,16 @@ 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.__storeWrapper.getSchemaDefinitionService().attributesDefinitionFor(identifier)[attr];
return getDefaultValue(attrSchema?.options);
const defaultValue = getDefaultValue(attrSchema?.options);
if (typeof attrSchema?.options?.defaultValue === 'function') {
cached.defaultAttrs = cached.defaultAttrs || (Object.create(null) as Record<string, unknown>);
cached.defaultAttrs[attr] = defaultValue;
}
return defaultValue;
}
}

Expand Down Expand Up @@ -974,6 +984,10 @@ export default class JSONAPICache implements Cache {
delete cached.changes![attr];
}

if (cached.defaultAttrs && attr in cached.defaultAttrs) {
delete cached.defaultAttrs[attr];
}

this.__storeWrapper.notifyChange(identifier, 'attributes', attr);
}

Expand Down Expand Up @@ -1056,6 +1070,7 @@ export default class JSONAPICache implements Cache {
}

cached.inflightAttrs = null;
cached.defaultAttrs = null;

if (cached.errors) {
cached.errors = null;
Expand Down Expand Up @@ -1408,7 +1423,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;
Expand All @@ -1430,6 +1445,10 @@ function patchLocalAttributes(cached: CachedResource): boolean {
delete localAttrs[attr];
delete changes![attr];
}

if (defaultAttrs && attr in defaultAttrs) {
delete defaultAttrs[attr];
}
}
return hasAppliedPatch;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -460,4 +460,77 @@ module('Integration | @ember-data/json-api Cache.put(<ResourceDataDocument>)', 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: 'string',
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.strictEqual(name1, 'Name 1', 'The default value was generated');
const name2 = store.cache.getAttr(identifier, 'name');
assert.strictEqual(name2, 'Name 1', 'The default value was cached');

store.cache.setAttr(identifier, 'name', 'Chris');
const name3 = store.cache.getAttr(identifier, 'name');
assert.strictEqual(name3, 'Chris', 'The value was updated');

store.cache.setAttr(identifier, 'name', null);
const name4 = store.cache.getAttr(identifier, 'name');
assert.strictEqual(name4, null, 'Null was set and maintained');

store.cache.rollbackAttrs(identifier);
const name5 = store.cache.getAttr(identifier, 'name');
assert.strictEqual(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.strictEqual(name6, 'Tomster', 'The value was updated on put');
});
});

0 comments on commit cd70bee

Please sign in to comment.