Skip to content

Commit

Permalink
more wip
Browse files Browse the repository at this point in the history
  • Loading branch information
richgt committed May 1, 2024
1 parent 488fec5 commit 7ab4fd8
Show file tree
Hide file tree
Showing 6 changed files with 300 additions and 19 deletions.
16 changes: 14 additions & 2 deletions packages/json-api/src/-private/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1231,8 +1231,20 @@ export default class JSONAPICache implements Cache {
// appear to be dirty unnecessarily, and for folks that open an issue we can guide them
// to make their cache data less stateful.
} else if (cached.localAttrs) {
delete cached.localAttrs[basePath];
delete cached.changes![basePath];
try {
if (!existing) {
return;
}
const existingStr = JSON.stringify(existing);
const newStr = JSON.stringify(cached.localAttrs[basePath]);

if (existingStr !== newStr) {
delete cached.localAttrs[basePath];
delete cached.changes![basePath];
}
} catch (e) {
// noop
}
}

this._capabilities.notifyChange(identifier, 'attributes', basePath);
Expand Down
24 changes: 13 additions & 11 deletions packages/schema-record/src/managed-array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,21 +169,23 @@ export class ManagedArray {
subscribe(_SIGNAL);
}
if (isSchemaArray) {
const record = new SchemaRecord(
store,
self.owner[Identifier],
{ [Editable]: self.owner[Editable], [Legacy]: self.owner[Legacy] },
true,
field.type,
`${self.key}.${index}`
);
Object.assign(record, val);
return record;
if (val) {
const record = new SchemaRecord(
store,
self.owner[Identifier],
{ [Editable]: self.owner[Editable], [Legacy]: self.owner[Legacy] },
true,
field.type,
`${self.key}.${index}`
);
Object.assign(record, val);
return record;
}
return val;
} else {
if (field.type !== null) {
const transform = schema.transforms.get(field.type);
if (!transform) {
debugger;
throw new Error(`No '${field.type}' transform defined for use by ${address.type}.${String(prop)}`);
}
return transform.hydrate(val as Value, field.options ?? null, self.owner);
Expand Down
9 changes: 8 additions & 1 deletion packages/schema-record/src/record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,6 @@ export class SchemaRecord {
}
},
set(target: SchemaRecord, prop: string | number | symbol, value: unknown, receiver: typeof Proxy<SchemaRecord>) {
debugger;
if (!IS_EDITABLE) {
throw new Error(`Cannot set ${String(prop)} on ${identifier.type} because the record is not editable`);
}
Expand Down Expand Up @@ -564,6 +563,14 @@ export class SchemaRecord {
ManagedArrayMap.delete(target);
}
cache.setAttr(identifier, propArray, arrayValue);
const peeked = peekManagedArray(self, field);
if (peeked) {
const arrSignal = peeked[ARRAY_SIGNAL];
arrSignal.shouldReset = true;
}
if (!Array.isArray(value)) {
ManagedArrayMap.delete(target);
}
return true;
}
case 'object': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ export async function reactiveContext<T extends OpaqueRecordInstance>(
field.kind === 'attribute' ||
field.kind === 'field' ||
field.kind === 'derived' ||
field.kind === 'array'
field.kind === 'array' ||
field.kind === 'object' ||
field.kind === 'schema-array'
) {
return record[field.name as keyof T] as unknown;
} else if (field.kind === 'resource') {
Expand Down
134 changes: 134 additions & 0 deletions tests/warp-drive__schema-record/tests/reactivity/schema-array-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import { rerender } from '@ember/test-helpers';

import { module, test } from 'qunit';

import { setupRenderingTest } from 'ember-qunit';

import type Store from '@ember-data/store';
import type { FieldSchema } from '@ember-data/store/-types/q/schema-service';
import { registerDerivations, SchemaService, withFields } from '@warp-drive/schema-record/schema';

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

interface address {
street: string;
city: string;
state: string;
zip: string | number;
}

interface User {
id: string | null;
$type: 'user';
name: string;
addresses: Array<address | null> | null;
favoriteNumbers: string[];
age: number;
netWorth: number;
coolometer: number;
rank: number;
}

module('Reactivity | schema-array fields can receive remote updates', function (hooks) {
setupRenderingTest(hooks);

test('we can use simple fields with no `type`', async function (assert) {
const store = this.owner.lookup('service:store') as Store;
const schema = new SchemaService();
store.registerSchema(schema);
registerDerivations(schema);

schema.defineSchema('address', {
fields: [
{
name: 'street',
type: null,
kind: 'field',
},
{
name: 'city',
type: null,
kind: 'field',
},
{
name: 'state',
type: null,
kind: 'field',
},
{
name: 'zip',
type: null,
kind: 'field',
},
],
});

schema.defineSchema('user', {
fields: withFields([
{
name: 'favoriteNumbers',
type: null,
kind: 'array',
},
{
name: 'addresses',
type: 'address',
kind: 'schema-array',
},
]),
});
const fieldsMap = schema.schemas.get('user')!.fields;
const fields: FieldSchema[] = [...fieldsMap.values()];

const record = store.push({
data: {
type: 'user',
id: '1',
attributes: {
favoriteNumbers: ['1', '2'],
addresses: [
{ street: '123 Main St', city: 'Anytown', state: 'NY', zip: '12345' },
{ street: '456 Elm St', city: 'Anytown', state: 'NY', zip: '12345' },
],
},
},
}) as User;

assert.strictEqual(record.id, '1', 'id is accessible');
assert.strictEqual(record.$type, 'user', '$type is accessible');
assert.deepEqual(record.favoriteNumbers, ['1', '2'], 'favoriteNumbers is accessible');

const { counters, fieldOrder } = await reactiveContext.call(this, record, fields);
const favoriteNumbersIndex = fieldOrder.indexOf('favoriteNumbers');

assert.strictEqual(counters.id, 1, 'idCount is 1');
assert.strictEqual(counters.$type, 1, '$typeCount is 1');
assert.strictEqual(counters.favoriteNumbers, 1, 'favoriteNumbersCount is 1');
assert
.dom(`li:nth-child(${favoriteNumbersIndex + 1})`)
.hasText('favoriteNumbers: 1,2', 'favoriteNumbers is rendered');

// remote update
store.push({
data: {
type: 'user',
id: '1',
attributes: { favoriteNumbers: ['3', '4'] },
},
});

assert.strictEqual(record.id, '1', 'id is accessible');
assert.strictEqual(record.$type, 'user', '$type is accessible');
assert.deepEqual(record.favoriteNumbers, ['3', '4'], 'favoriteNumbers is accessible');

await rerender();

assert.strictEqual(counters.id, 1, 'idCount is 1');
assert.strictEqual(counters.$type, 1, '$typeCount is 1');
assert.strictEqual(counters.favoriteNumbers, 2, 'favoriteNumbersCount is 2');

assert
.dom(`li:nth-child(${favoriteNumbersIndex + 1})`)
.hasText('favoriteNumbers: 3,4', 'favoriteNumbers is rendered');
});
});
132 changes: 128 additions & 4 deletions tests/warp-drive__schema-record/tests/writes/schema-array-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import type Store from '@ember-data/store';
import { recordIdentifierFor } from '@ember-data/store';
import type { JsonApiResource } from '@ember-data/store/-types/q/record-data-json-api';
import type { ResourceType } from '@warp-drive/core-types/symbols';
import type { Transform } from '@warp-drive/schema-record/schema';
import { registerDerivations, SchemaService, withFields } from '@warp-drive/schema-record/schema';

interface address {
Expand All @@ -20,7 +19,7 @@ interface CreateUserType {
id: string | null;
$type: 'user';
name: string | null;
addresses: address[] | null;
addresses: Array<address | null> | null;
[ResourceType]: 'user';
}

Expand Down Expand Up @@ -317,6 +316,132 @@ module('Writes | schema-array fields', function (hooks) {
);
});

test('we can update individual objects in the array to null', function (assert) {
const store = this.owner.lookup('service:store') as Store;
const schema = new SchemaService();
store.registerSchema(schema);
registerDerivations(schema);
schema.defineSchema('address', {
fields: [
{
name: 'street',
type: null,
kind: 'field',
},
{
name: 'city',
type: null,
kind: 'field',
},
{
name: 'state',
type: null,
kind: 'field',
},
{
name: 'zip',
type: null,
kind: 'field',
},
],
});
schema.defineSchema('user', {
fields: withFields([
{
name: 'name',
type: null,
kind: 'field',
},
{
name: 'addresses',
type: 'address',
kind: 'schema-array',
},
]),
});

const sourceArray = [
{
street: '123 Main St',
city: 'Anytown',
state: 'NY',
zip: '12345',
},
{
street: '456 Elm St',
city: 'Othertown',
state: 'CA',
zip: '54321',
},
];
const record = store.createRecord<CreateUserType>('user', {
name: 'Rey Skybarker',
addresses: sourceArray,
});

assert.strictEqual(record.id, null, 'id is accessible');
assert.strictEqual(record.$type, 'user', '$type is accessible');
assert.strictEqual(record.name, 'Rey Skybarker', 'name is accessible');
assert.true(Array.isArray(record.addresses), 'we can access favoriteNumber array');
assert.propContains(
record.addresses?.slice(),
[
{
street: '123 Main St',
city: 'Anytown',
state: 'NY',
zip: '12345',
},
{
street: '456 Elm St',
city: 'Othertown',
state: 'CA',
zip: '54321',
},
],
'We have the correct array members'
);
record.addresses![0] = null;
assert.propContains(
record.addresses?.slice(),
[
null,
{
street: '456 Elm St',
city: 'Othertown',
state: 'CA',
zip: '54321',
},
],
'We have the correct array members'
);
assert.strictEqual(record.addresses, record.addresses, 'We have a stable array reference');
assert.notStrictEqual(record.addresses, sourceArray);

// test that the data entered the cache properly
const identifier = recordIdentifierFor(record);
const cachedResourceData = store.cache.peek<JsonApiResource>(identifier);

assert.notStrictEqual(
cachedResourceData?.attributes?.favoriteNumbers,
sourceArray,
'with no transform we will still divorce the array reference'
);
assert.deepEqual(
cachedResourceData?.attributes?.addresses,
[
null,
{
street: '456 Elm St',
city: 'Othertown',
state: 'CA',
zip: '54321',
},
],
'the cache values are correct for the array field'
);
});

test('we can update individual fields in objects in the array to new values', function (assert) {
const store = this.owner.lookup('service:store') as Store;
const schema = new SchemaService();
Expand Down Expand Up @@ -402,8 +527,7 @@ module('Writes | schema-array fields', function (hooks) {
],
'We have the correct array members'
);
debugger;
record.addresses![0].street = '789 Maple St';
record.addresses![0]!.street = '789 Maple St';

assert.propContains(
record.addresses?.slice(),
Expand Down

0 comments on commit 7ab4fd8

Please sign in to comment.