Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bulk backports to v4-main (3/7 edition) #9751

Merged
merged 61 commits into from
Mar 10, 2025
Merged
Changes from 1 commit
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
4d24763
Add perf info for re-materialization (#9681)
gitKrystan Feb 13, 2025
3ffb4bc
feat: runtime logging activation (#9683)
runspired Feb 19, 2025
9b80ccb
Fix bug where race condition causes errors during Request refresh (#9…
gitKrystan Feb 20, 2025
9532b93
feat(cache): add peekRemoteState to cache to view remote state (#9624)
richgt Feb 21, 2025
7a73dbb
feat: add patchRecord util to JSON:API (#9687)
runspired Feb 22, 2025
2b1cf4b
feat: immutable records should show only remote state, make builder t…
runspired Feb 22, 2025
ead24ba
Fix: support all known http methods (#9689)
runspired Feb 22, 2025
1af9a68
chore: fixup SchemaRecord readme (#9690)
runspired Feb 22, 2025
5e83d6e
chore: minor docs fixes
runspired Feb 22, 2025
7da6373
Feat: hasMany linksMode (#9676)
leoeuclids Feb 22, 2025
8738378
fix: make serialize utils smarter (#9692)
runspired Feb 22, 2025
7ac6b20
chore: add env vars to turbo config (#9693)
runspired Feb 23, 2025
6c8eae4
docs: small cleanup for SchemaRecord intro (#9694)
runspired Feb 23, 2025
d0a034b
chore: [BREAKING to prior alpha/betas] prepare SchemaRecord for stabl…
runspired Feb 23, 2025
fc7c9b4
doc: link to jsonapi.org when referencing
runspired Feb 23, 2025
dc1a602
doc: cleanup wording in SchemaRecord README
runspired Feb 23, 2025
83c1ce6
fix: Don't notify changes for attributes not registered with the sche…
gitKrystan Feb 25, 2025
cb4e391
internal: fixup diagnostic reporting (#9704)
runspired Mar 1, 2025
aac9918
chore: update to pnpm 10 (#9699)
runspired Mar 1, 2025
834a32e
chore: make diagnostic shutdown safer, use bun for holodeck server (#…
runspired Mar 4, 2025
f4e871b
fix(deps): update dependency vite to ^5.4.14 (#9666)
renovate[bot] Mar 4, 2025
76d9eca
chore(deps): update dependency globby to ^14.1.0 (#9708)
renovate[bot] Mar 4, 2025
060ad0b
chore(deps): update dependency bun-types to v1.2.4 (#9706)
renovate[bot] Mar 4, 2025
ed6388c
fix(deps): update dependency inflection to ~3.0.2 (#9664)
renovate[bot] Mar 4, 2025
c70d130
chore(deps): update dependency terser-webpack-plugin to ^5.3.12 (#9710)
renovate[bot] Mar 4, 2025
80b1ac2
chore(deps): update node.js to >= 18.20.7 (#9711)
renovate[bot] Mar 4, 2025
727caaf
fix(deps): update dependency globals to ^15.15.0 (#9715)
renovate[bot] Mar 4, 2025
893a89a
fix(deps): update dependency rollup to ^4.34.9 (#9716)
renovate[bot] Mar 4, 2025
4ea2c59
fix(deps): update dependency semver to ^7.7.1 (#9717)
renovate[bot] Mar 4, 2025
a0294b8
chore(deps): update ember-core (#9719)
renovate[bot] Mar 4, 2025
35459b9
fix(deps): update dependency debug to ^4.4.0 (#9714)
renovate[bot] Mar 4, 2025
2f762d6
fix(deps): update dependency @ember/optional-features to ^2.2.0 (#9713)
renovate[bot] Mar 4, 2025
9f41388
fix(deps): update asset-compilation (#9712)
renovate[bot] Mar 4, 2025
bd69798
chore(deps): update dependency @ember/string to v4.0.1 (#9721)
renovate[bot] Mar 4, 2025
eef0f66
chore(deps): update dependency command-line-args to v6 (#9722)
renovate[bot] Mar 4, 2025
1686b9e
chore(deps): update dependency expect-type to v1 (#9724)
renovate[bot] Mar 4, 2025
2222b67
chore(deps): update dependency ember-inflector to v5 (#9723)
renovate[bot] Mar 4, 2025
55dbced
chore(deps): update dependency mocha to v11 (#9726)
renovate[bot] Mar 4, 2025
194c709
chore(deps): update dependency rimraf to v6 (#9727)
renovate[bot] Mar 4, 2025
2638a3f
chore(deps): update oven-sh/setup-bun action to v2 (#9729)
renovate[bot] Mar 4, 2025
9e2269f
fix(deps): update dependency ember-cli to v6 (#9733)
renovate[bot] Mar 4, 2025
095178f
fix(deps): update build-tools (#9570)
renovate[bot] Mar 4, 2025
a6b8622
fix(deps): update dependency ember-load-initializers to v3 (#9736)
renovate[bot] Mar 4, 2025
65d051f
fix(deps): update dependency globals to v16 (#9737)
renovate[bot] Mar 4, 2025
a4f3ba8
Fix metric count data typo
gitKrystan Mar 4, 2025
f381240
fix(deps): update code-quality (#9571)
renovate[bot] Mar 4, 2025
cc65a28
bump version of tracerbench
runspired Mar 5, 2025
29e95f4
chore: add logging to proxy server for debugging
runspired Mar 5, 2025
780e2ea
chore: Modernize Performance App (use Vite) (#9707)
runspired Mar 5, 2025
7aa8cdd
chore: update main version of perf checks
runspired Mar 5, 2025
64c5e18
chore: fixup vite config for perf (#9744)
runspired Mar 5, 2025
f5e474e
feat: new perf scenario for complex records (#9745)
runspired Mar 6, 2025
ff35399
chore: update scenario
runspired Mar 6, 2025
8d0b992
chore: remove mixin workaround
runspired Mar 6, 2025
410f733
More dramatic update for update-with-same-state perf test (#9743)
gitKrystan Mar 6, 2025
ecc3574
chore: bump size of benchmark (#9746)
runspired Mar 6, 2025
8835a34
Actually add the m2m test case to the perf test output (#9748)
gitKrystan Mar 6, 2025
3121543
chore: update perf suite (#9749)
runspired Mar 7, 2025
499dff2
Don't overnotify for updates to added state that match local updates …
gitKrystan Mar 8, 2025
355e99d
Backport tests/main/tsconfig
gitKrystan Mar 9, 2025
acd9114
Remove unneccessary test assertions
gitKrystan Mar 10, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix: Don't notify changes for attributes not registered with the sche…
…ma (#9698)
  • Loading branch information
gitKrystan committed Mar 9, 2025
commit 83c1ce6fb17d5aad1743d7ff455c812c1f058e47
22 changes: 14 additions & 8 deletions packages/json-api/src/-private/cache.ts
Original file line number Diff line number Diff line change
@@ -656,10 +656,12 @@ export default class JSONAPICache implements Cache {
this._capabilities.notifyChange(identifier, 'state', null);
}

const fields = this._capabilities.schema.fields(identifier);

// if no cache entry existed, no record exists / property has been accessed
// and thus we do not need to notify changes to any properties.
if (calculateChanges && existed && data.attributes) {
changedKeys = calculateChangedKeys(cached, data.attributes);
changedKeys = calculateChangedKeys(cached, data.attributes, fields);
}

cached.remoteAttrs = Object.assign(
@@ -682,7 +684,7 @@ export default class JSONAPICache implements Cache {
}

if (data.relationships) {
setupRelationships(this.__graph, this._capabilities, identifier, data);
setupRelationships(this.__graph, fields, identifier, data);
}

if (changedKeys?.size) {
@@ -1007,6 +1009,7 @@ export default class JSONAPICache implements Cache {
}
}

const fields = this._capabilities.schema.fields(identifier);
cached.isNew = false;
let newCanonicalAttributes: ExistingResourceObject['attributes'];
if (data) {
@@ -1027,7 +1030,6 @@ export default class JSONAPICache implements Cache {
if (!DEPRECATE_RELATIONSHIP_REMOTE_UPDATE_CLEARING_LOCAL_STATE) {
// assert against bad API behavior where a belongsTo relationship
// is saved but the return payload indicates a different final state.
const fields = this._capabilities.schema.fields(identifier);
fields.forEach((field, name) => {
if (field.kind === 'belongsTo') {
const relationshipData = data.relationships![name]?.data;
@@ -1053,11 +1055,11 @@ export default class JSONAPICache implements Cache {
cached.inflightRelationships = null;
}
}
setupRelationships(this.__graph, this._capabilities, identifier, data);
setupRelationships(this.__graph, fields, identifier, data);
}
newCanonicalAttributes = data.attributes;
}
const changedKeys = newCanonicalAttributes && calculateChangedKeys(cached, newCanonicalAttributes);
const changedKeys = newCanonicalAttributes && calculateChangedKeys(cached, newCanonicalAttributes, fields);

cached.remoteAttrs = Object.assign(
cached.remoteAttrs || (Object.create(null) as Record<string, unknown>),
@@ -1872,7 +1874,8 @@ function notifyAttributes(
*/
function calculateChangedKeys(
cached: CachedResource,
updates: Exclude<ExistingResourceObject['attributes'], undefined>
updates: Exclude<ExistingResourceObject['attributes'], undefined>,
fields: ReturnType<Store['schema']['fields']>
): Set<string> {
const changedKeys = new Set<string>();
const keys = Object.keys(updates);
@@ -1887,6 +1890,10 @@ function calculateChangedKeys(

for (let i = 0; i < length; i++) {
const key = keys[i];
if (!fields.has(key)) {
continue;
}

const value = updates[key];

// A value in localAttrs means the user has a local change to
@@ -1962,15 +1969,14 @@ function _isLoading(

function setupRelationships(
graph: Graph,
capabilities: CacheCapabilitiesManager,
fields: ReturnType<Store['schema']['fields']>,
identifier: StableRecordIdentifier,
data: ExistingResourceObject
) {
// TODO @runspired iterating by definitions instead of by payload keys
// allows relationship payloads to be ignored silently if no relationship
// definition exists. Ensure there's a test for this and then consider
// moving this to an assertion. This check should possibly live in the graph.
const fields = capabilities.schema.fields(identifier);
for (const [name, field] of fields) {
if (!isRelationship(field)) continue;

2 changes: 1 addition & 1 deletion packages/model/src/-private/model.ts
Original file line number Diff line number Diff line change
@@ -1936,7 +1936,7 @@ class Model extends EmberObject implements MinimalLegacyRecord {
this.eachComputedProperty((name, meta) => {
if (isAttributeSchema(meta)) {
assert(
"You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: attr('<type>')` from " +
"You may not set 'id' as an attribute on your model. Please remove any lines that look like: `id: attr('<type>')` from " +
this.toString(),
name !== 'id'
);
1 change: 1 addition & 0 deletions tests/main/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@ const AllowedImports = [
'@ember/test-helpers',
'@ember/test-waiters',
'@glimmer/component',
'@glimmer/tracking',
'qunit',
];

Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@ import { action } from '@ember/object';
import { inject } from '@ember/service';
import { click, findAll, render } from '@ember/test-helpers';
import Component from '@glimmer/component';
// eslint-disable-next-line no-restricted-imports
import { tracked } from '@glimmer/tracking';

import { module, test } from 'qunit';
1 change: 0 additions & 1 deletion tests/main/tests/acceptance/tracking-create-record-test.js
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@ import { setComponentTemplate } from '@ember/component';
import { inject as service } from '@ember/service';
import { render, settled } from '@ember/test-helpers';
import Component from '@glimmer/component';
// eslint-disable-next-line no-restricted-imports
import { tracked } from '@glimmer/tracking';

import { module, test } from 'qunit';
1 change: 0 additions & 1 deletion tests/main/tests/helpers/create-tracking-context.gjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { render, settled } from '@ember/test-helpers';
import Component from '@glimmer/component';
// eslint-disable-next-line no-restricted-imports
import { tracked } from '@glimmer/tracking';

// eslint-disable-next-line @typescript-eslint/require-await
70 changes: 70 additions & 0 deletions tests/main/tests/integration/cache/json-api-cache-2-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { settled } from '@ember/test-helpers';
import { tracked } from '@glimmer/tracking';

import { module, test } from 'qunit';

import { setupRenderingTest } from 'ember-qunit';

import Model, { attr } from '@ember-data/model';
import type Store from '@ember-data/store';
import { Type } from '@warp-drive/core-types/symbols';

import { reactiveContext } from '../../helpers/reactive-context';

class User extends Model {
@attr declare name: string;

// Not an attr, so shouldn't notify if a similarly named attribute changes.
@tracked brotherName: string | undefined;

declare [Type]: 'user';
}

module('@ember-data/json-api | Cache (2)', function (hooks) {
setupRenderingTest(hooks);

hooks.beforeEach(function () {
this.owner.register('model:user', User);
});

test('Does not notify for attributes not in schema', async function (assert) {
const store = this.owner.lookup('service:store') as Store;
const user: User = store.push({
data: {
id: '1',
type: 'user',
attributes: {
name: 'Aino',
},
},
});

const rc = await reactiveContext.call(this, user, {
type: 'user',
identity: null,
fields: [
{ name: 'name', kind: 'field' },
{ name: 'brotherName', kind: 'field' },
],
});

const { counters } = rc;

assert.deepEqual(counters, { name: 1, brotherName: 1 }, 'Test setup: counters initialized');

store.push({
data: {
id: '1',
type: 'user',
attributes: {
name: 'Aino',
brotherName: 'Ellis',
},
},
});

await settled();

assert.deepEqual(counters, { name: 1, brotherName: 1 }, 'brotherName did not notify');
});
});
2 changes: 1 addition & 1 deletion tests/main/tests/unit/model-test.js
Original file line number Diff line number Diff line change
@@ -210,7 +210,7 @@ module('unit/model - Model', function (hooks) {
assert.expectAssertion(() => {
const ModelClass = store.modelFor('test-model');
get(ModelClass, 'attributes');
}, /You may not set `id` as an attribute on your model/);
}, /You may not set 'id' as an attribute on your model/);

assert.expectAssertion(() => {
store.push({
18 changes: 0 additions & 18 deletions tests/main/tests/unit/store/push-test.js
Original file line number Diff line number Diff line change
@@ -527,24 +527,6 @@ module('unit/store/push - Store#push', function (hooks) {
assert.notOk(store._instanceCache.peek(pushResult, { bucket: 'record' }), 'record is not materialized');
});

test('_push does not require a modelName to resolve to a modelClass', function (assert) {
const store = this.owner.lookup('service:store');
const originalCall = store.modelFor;
store.modelFor = function () {
assert.notOk('modelFor was triggered as a result of a call to store._push');
};

store._push({
data: {
id: '1',
type: 'person',
},
});

store.modelFor = originalCall;
assert.ok('We made it');
});

test('_push returns an array of identifiers if an array is pushed', function (assert) {
const store = this.owner.lookup('service:store');
const pushResult = store._push({