diff --git a/packages/@orbit/indexeddb/src/indexeddb-cache.ts b/packages/@orbit/indexeddb/src/indexeddb-cache.ts index e9b29b08..b60deb0d 100644 --- a/packages/@orbit/indexeddb/src/indexeddb-cache.ts +++ b/packages/@orbit/indexeddb/src/indexeddb-cache.ts @@ -13,7 +13,9 @@ import { AsyncRecordCacheSettings, RecordCacheQueryOptions, RecordCacheTransformOptions, - RecordCacheUpdateDetails + RecordCacheUpdateDetails, + RecordTransformBuffer, + SimpleRecordTransformBuffer } from '@orbit/record-cache'; import { supportsIndexedDB } from './lib/indexeddb'; import { RequestOptions } from '@orbit/data'; @@ -564,6 +566,22 @@ export class IndexedDBCache< // Protected methods ///////////////////////////////////////////////////////////////////////////// + /** + * Override `_getTransformBuffer` on base `AsyncRecordCache` to provide a + * `transformBuffer` if a custom one hasn't been provided via the constructor + * setting. + */ + protected _getTransformBuffer(): RecordTransformBuffer { + if (this._transformBuffer === undefined) { + const { schema, keyMap } = this; + this._transformBuffer = new SimpleRecordTransformBuffer({ + schema, + keyMap + }); + } + return this._transformBuffer; + } + protected async _getAllRecords(): Promise { if (!this._db) return Promise.reject(DB_NOT_OPEN); diff --git a/packages/@orbit/local-storage/src/local-storage-cache.ts b/packages/@orbit/local-storage/src/local-storage-cache.ts index ed39d72b..3c807c92 100644 --- a/packages/@orbit/local-storage/src/local-storage-cache.ts +++ b/packages/@orbit/local-storage/src/local-storage-cache.ts @@ -11,6 +11,8 @@ import { RecordCacheTransformOptions, RecordCacheUpdateDetails, RecordRelationshipIdentity, + RecordTransformBuffer, + SimpleRecordTransformBuffer, SyncRecordCache, SyncRecordCacheSettings } from '@orbit/record-cache'; @@ -261,4 +263,24 @@ export class LocalStorageCache< processor.upgrade(); } } + + ///////////////////////////////////////////////////////////////////////////// + // Protected methods + ///////////////////////////////////////////////////////////////////////////// + + /** + * Override `_getTransformBuffer` on base `SyncRecordCache` to provide a + * `transformBuffer` if a custom one hasn't been provided via the constructor + * setting. + */ + protected _getTransformBuffer(): RecordTransformBuffer { + if (this._transformBuffer === undefined) { + const { schema, keyMap } = this; + this._transformBuffer = new SimpleRecordTransformBuffer({ + schema, + keyMap + }); + } + return this._transformBuffer; + } } diff --git a/packages/@orbit/memory/src/memory-cache.ts b/packages/@orbit/memory/src/memory-cache.ts index dc9b43c9..43a631f7 100644 --- a/packages/@orbit/memory/src/memory-cache.ts +++ b/packages/@orbit/memory/src/memory-cache.ts @@ -11,6 +11,8 @@ import { RecordCacheTransformOptions, RecordCacheUpdateDetails, RecordRelationshipIdentity, + RecordTransformBuffer, + SimpleRecordTransformBuffer, SyncRecordCache, SyncRecordCacheSettings } from '@orbit/record-cache'; @@ -256,6 +258,22 @@ export class MemoryCache< // Protected methods ///////////////////////////////////////////////////////////////////////////// + /** + * Override `_getTransformBuffer` on base `SyncRecordCache` to provide a + * `transformBuffer` if a custom one hasn't been provided via the constructor + * setting. + */ + protected _getTransformBuffer(): RecordTransformBuffer { + if (this._transformBuffer === undefined) { + const { schema, keyMap } = this; + this._transformBuffer = new SimpleRecordTransformBuffer({ + schema, + keyMap + }); + } + return this._transformBuffer; + } + protected _resetInverseRelationships( base?: MemoryCache ): void { diff --git a/packages/@orbit/record-cache/src/async-record-cache.ts b/packages/@orbit/record-cache/src/async-record-cache.ts index b4e06f7e..d732c748 100644 --- a/packages/@orbit/record-cache/src/async-record-cache.ts +++ b/packages/@orbit/record-cache/src/async-record-cache.ts @@ -1,4 +1,4 @@ -import { Orbit } from '@orbit/core'; +import { Assertion, Orbit } from '@orbit/core'; import { buildQuery, buildTransform, @@ -60,12 +60,8 @@ import { RecordCacheSettings, RecordCacheTransformOptions } from './record-cache'; -import { - RecordTransformBuffer, - RecordTransformBufferClass -} from './record-transform-buffer'; +import { RecordTransformBuffer } from './record-transform-buffer'; import { PatchResult, RecordCacheUpdateDetails } from './response'; -import { SyncRecordCacheSettings } from './sync-record-cache'; const { assert, deprecate } = Orbit; @@ -80,8 +76,7 @@ export interface AsyncRecordCacheSettings< transformOperators?: Dict; inverseTransformOperators?: Dict; debounceLiveQueries?: boolean; - transformBufferClass?: RecordTransformBufferClass; - transformBufferSettings?: SyncRecordCacheSettings; + transformBuffer?: RecordTransformBuffer; } export abstract class AsyncRecordCache< @@ -103,8 +98,6 @@ export abstract class AsyncRecordCache< protected _inverseTransformOperators: Dict; protected _debounceLiveQueries: boolean; protected _transformBuffer?: RecordTransformBuffer; - protected _transformBufferClass?: RecordTransformBufferClass; - protected _transformBufferSettings?: SyncRecordCacheSettings; constructor(settings: AsyncRecordCacheSettings) { super(settings); @@ -115,6 +108,7 @@ export abstract class AsyncRecordCache< this._inverseTransformOperators = settings.inverseTransformOperators ?? AsyncInverseTransformOperators; this._debounceLiveQueries = settings.debounceLiveQueries !== false; + this._transformBuffer = settings.transformBuffer; const processors: AsyncOperationProcessorClass[] = settings.processors ? settings.processors @@ -494,14 +488,9 @@ export abstract class AsyncRecordCache< protected _getTransformBuffer(): RecordTransformBuffer { if (this._transformBuffer === undefined) { - let transformBufferClass = - this._transformBufferClass ?? RecordTransformBuffer; - let settings = this._transformBufferSettings ?? { schema: this.schema }; - settings.schema = settings.schema ?? this.schema; - settings.keyMap = settings.keyMap ?? this.keyMap; - this._transformBuffer = new transformBufferClass(settings); - } else { - this._transformBuffer.reset(); + throw new Assertion( + 'transformBuffer must be provided to cache via constructor settings' + ); } return this._transformBuffer; } @@ -520,6 +509,7 @@ export abstract class AsyncRecordCache< const relatedRecords = inverseRelationships.map((ir) => ir.record); Array.prototype.push.apply(records, relatedRecords); + buffer.resetState(); buffer.setRecordsSync(await this.getRecordsAsync(records)); buffer.addInverseRelationshipsSync(inverseRelationships); diff --git a/packages/@orbit/record-cache/src/index.ts b/packages/@orbit/record-cache/src/index.ts index efc0540a..e962fd9d 100644 --- a/packages/@orbit/record-cache/src/index.ts +++ b/packages/@orbit/record-cache/src/index.ts @@ -2,6 +2,7 @@ export * from './response'; export * from './record-accessor'; export * from './record-cache'; export * from './record-transform-buffer'; +export * from './simple-record-transform-buffer'; export * from './async-record-cache'; export * from './async-operation-processor'; diff --git a/packages/@orbit/record-cache/src/record-transform-buffer.ts b/packages/@orbit/record-cache/src/record-transform-buffer.ts index be32e9f6..4d24090a 100644 --- a/packages/@orbit/record-cache/src/record-transform-buffer.ts +++ b/packages/@orbit/record-cache/src/record-transform-buffer.ts @@ -1,247 +1,11 @@ -import { objectValues, Dict } from '@orbit/utils'; -import { - deserializeRecordIdentity, - InitializedRecord, - RecordIdentity, - serializeRecordIdentity -} from '@orbit/records'; -import { RecordChangeset, RecordRelationshipIdentity } from './record-accessor'; -import { SyncRecordCache, SyncRecordCacheSettings } from './sync-record-cache'; - -function serializeRecordRelationshipIdentity( - rri: RecordRelationshipIdentity -): string { - return `${serializeRecordIdentity(rri.record)}::${rri.relationship}`; -} - -function deserializeRecordRelationshipIdentity( - rri: string -): { record: RecordIdentity; relationship: string } { - const [record, relationship] = rri.split('::'); - return { record: deserializeRecordIdentity(record), relationship }; -} - -export interface RecordTransformBufferState { - records: Dict; - inverseRelationships: Dict>; -} - -export interface RecordTransformBufferClass { - new (settings: SyncRecordCacheSettings): RecordTransformBuffer; -} - -export class RecordTransformBuffer extends SyncRecordCache { - protected _state!: RecordTransformBufferState; - protected _delta?: RecordTransformBufferState; - - constructor(settings: SyncRecordCacheSettings) { - super(settings); - this.reset(); - } - - reset( - state: RecordTransformBufferState = { - records: {}, - inverseRelationships: {} - } - ): void { - this._state = state; - } - - startTrackingChanges(): void { - this._delta = { - records: {}, - inverseRelationships: {} - }; - } - - stopTrackingChanges(): RecordChangeset { - if (this._delta === undefined) { - throw new Error( - `Changes are not being tracked. Call 'startTrackingChanges' before 'stopTrackingChanges'` - ); - } - - let { records, inverseRelationships } = this._delta; - - // console.log('buffer#stopTrackingChanges - delta', records, inverseRelationships); - - let changeset: RecordChangeset = {}; - - for (let rid of Object.keys(records)) { - let rv = records[rid]; - if (rv === null) { - changeset.removeRecords = changeset.removeRecords ?? []; - changeset.removeRecords.push(deserializeRecordIdentity(rid)); - } else { - changeset.setRecords = changeset.setRecords ?? []; - changeset.setRecords.push(rv); - } - } - - for (let rid of Object.keys(inverseRelationships)) { - let relatedRecord = deserializeRecordIdentity(rid); - let rels = inverseRelationships[rid]; - for (let rel of Object.keys(rels)) { - let rv = rels[rel]; - let { record, relationship } = deserializeRecordRelationshipIdentity( - rel - ); - let rri = { relatedRecord, record, relationship }; - if (rv === null) { - changeset.removeInverseRelationships = - changeset.removeInverseRelationships ?? []; - changeset.removeInverseRelationships.push(rri); - } else { - changeset.addInverseRelationships = - changeset.addInverseRelationships ?? []; - changeset.addInverseRelationships.push(rri); - } - } - } - - this._delta = undefined; - - // console.log('buffer#stopTrackingChanges - changeset', changeset); - - return changeset; - } - - getRecordSync(identity: RecordIdentity): InitializedRecord | undefined { - return this._state.records[serializeRecordIdentity(identity)] ?? undefined; - } - - getRecordsSync( - typeOrIdentities?: string | RecordIdentity[] - ): InitializedRecord[] { - if (typeof typeOrIdentities === 'string') { - return objectValues(this._state.records[typeOrIdentities]); - } else if (Array.isArray(typeOrIdentities)) { - const records: InitializedRecord[] = []; - const identities: RecordIdentity[] = typeOrIdentities; - for (let i of identities) { - let record = this.getRecordSync(i); - if (record) { - records.push(record); - } - } - return records; - } else { - throw new Error('typeOrIdentities must be specified in getRecordsSync'); - } - } - - setRecordSync(record: InitializedRecord): void { - this._state.records[serializeRecordIdentity(record)] = record; - if (this._delta) { - this._delta.records[serializeRecordIdentity(record)] = record; - } - } - - setRecordsSync(records: InitializedRecord[]): void { - records.forEach((record) => this.setRecordSync(record)); - } - - removeRecordSync( - recordIdentity: RecordIdentity - ): InitializedRecord | undefined { - const record = this.getRecordSync(recordIdentity); - if (record) { - delete this._state.records[serializeRecordIdentity(record)]; - if (this._delta) { - this._delta.records[serializeRecordIdentity(record)] = null; - } - return record; - } else { - return undefined; - } - } - - removeRecordsSync(recordIdentities: RecordIdentity[]): InitializedRecord[] { - const records = []; - for (let recordIdentity of recordIdentities) { - let record = this.getRecordSync(recordIdentity); - if (record) { - records.push(record); - delete this._state.records[serializeRecordIdentity(record)]; - if (this._delta) { - this._delta.records[serializeRecordIdentity(record)] = null; - } - } - } - return records; - } - - getInverseRelationshipsSync( - recordIdentityOrIdentities: RecordIdentity | RecordIdentity[] - ): RecordRelationshipIdentity[] { - if (Array.isArray(recordIdentityOrIdentities)) { - let relationships: RecordRelationshipIdentity[] = []; - recordIdentityOrIdentities.forEach((record) => { - Array.prototype.push( - relationships, - this._getInverseRelationshipsSync(record) - ); - }); - return relationships; - } else { - return this._getInverseRelationshipsSync(recordIdentityOrIdentities); - } - } - - addInverseRelationshipsSync( - relationships: RecordRelationshipIdentity[] - ): void { - // console.log('addInverseRelationshipsSync', relationships); - - for (let relationship of relationships) { - const ri = serializeRecordIdentity(relationship.relatedRecord); - const rri = serializeRecordRelationshipIdentity(relationship); - const rels = this._state.inverseRelationships[ri] ?? {}; - rels[rri] = relationship; - this._state.inverseRelationships[ri] = rels; - if (this._delta) { - const rels = this._delta.inverseRelationships[ri] ?? {}; - rels[rri] = relationship; - this._delta.inverseRelationships[ri] = rels; - } - } - } - - removeInverseRelationshipsSync( - relationships: RecordRelationshipIdentity[] - ): void { - // console.log('removeInverseRelationshipsSync', relationships); - - for (let relationship of relationships) { - const ri = serializeRecordIdentity(relationship.relatedRecord); - const rri = serializeRecordRelationshipIdentity(relationship); - const rels = this._state.inverseRelationships[ri]; - - if (rels) { - rels[rri] = null; - if (this._delta) { - const rels = this._delta.inverseRelationships[ri] ?? {}; - rels[rri] = null; - } - } - } - } - - ///////////////////////////////////////////////////////////////////////////// - // Protected methods - ///////////////////////////////////////////////////////////////////////////// - - protected _getInverseRelationshipsSync( - recordIdentity: RecordIdentity - ): RecordRelationshipIdentity[] { - let relationships = this._state.inverseRelationships[ - serializeRecordIdentity(recordIdentity) - ]; - if (relationships) { - return objectValues(relationships).filter((r) => r !== null); - } else { - return []; - } - } +import { SyncRecordUpdatable } from '@orbit/records'; +import { RecordChangeset, SyncRecordAccessor } from './record-accessor'; +import { RecordCacheUpdateDetails } from './response'; + +export interface RecordTransformBuffer + extends SyncRecordUpdatable, + SyncRecordAccessor { + resetState(): void; + startTrackingChanges(): void; + stopTrackingChanges(): RecordChangeset; } diff --git a/packages/@orbit/record-cache/src/simple-record-transform-buffer.ts b/packages/@orbit/record-cache/src/simple-record-transform-buffer.ts new file mode 100644 index 00000000..b04779b8 --- /dev/null +++ b/packages/@orbit/record-cache/src/simple-record-transform-buffer.ts @@ -0,0 +1,235 @@ +import { Dict, objectValues } from '@orbit/utils'; +import { + deserializeRecordIdentity, + InitializedRecord, + RecordIdentity, + serializeRecordIdentity +} from '@orbit/records'; +import { RecordChangeset, RecordRelationshipIdentity } from './record-accessor'; +import { SyncRecordCache, SyncRecordCacheSettings } from './sync-record-cache'; +import { RecordTransformBuffer } from './record-transform-buffer'; + +function serializeRecordRelationshipIdentity( + rri: RecordRelationshipIdentity +): string { + return `${serializeRecordIdentity(rri.record)}::${rri.relationship}`; +} + +function deserializeRecordRelationshipIdentity( + rri: string +): { record: RecordIdentity; relationship: string } { + const [record, relationship] = rri.split('::'); + return { record: deserializeRecordIdentity(record), relationship }; +} + +export interface SimpleRecordTransformBufferState { + records: Dict; + inverseRelationships: Dict>; +} + +export class SimpleRecordTransformBuffer + extends SyncRecordCache + implements RecordTransformBuffer { + protected _state!: SimpleRecordTransformBufferState; + protected _delta?: SimpleRecordTransformBufferState; + + constructor(settings: SyncRecordCacheSettings) { + super(settings); + this.resetState(); + } + + resetState(): void { + this._state = { + records: {}, + inverseRelationships: {} + }; + } + + startTrackingChanges(): void { + this._delta = { + records: {}, + inverseRelationships: {} + }; + } + + stopTrackingChanges(): RecordChangeset { + if (this._delta === undefined) { + throw new Error( + `Changes are not being tracked. Call 'startTrackingChanges' before 'stopTrackingChanges'` + ); + } + + let { records, inverseRelationships } = this._delta; + let changeset: RecordChangeset = {}; + + for (let rid of Object.keys(records)) { + let rv = records[rid]; + if (rv === null) { + changeset.removeRecords = changeset.removeRecords ?? []; + changeset.removeRecords.push(deserializeRecordIdentity(rid)); + } else { + changeset.setRecords = changeset.setRecords ?? []; + changeset.setRecords.push(rv); + } + } + + for (let rid of Object.keys(inverseRelationships)) { + let relatedRecord = deserializeRecordIdentity(rid); + let rels = inverseRelationships[rid]; + for (let rel of Object.keys(rels)) { + let rv = rels[rel]; + let { record, relationship } = deserializeRecordRelationshipIdentity( + rel + ); + let rri = { relatedRecord, record, relationship }; + if (rv === null) { + changeset.removeInverseRelationships = + changeset.removeInverseRelationships ?? []; + changeset.removeInverseRelationships.push(rri); + } else { + changeset.addInverseRelationships = + changeset.addInverseRelationships ?? []; + changeset.addInverseRelationships.push(rri); + } + } + } + + this._delta = undefined; + + return changeset; + } + + getRecordSync(identity: RecordIdentity): InitializedRecord | undefined { + return this._state.records[serializeRecordIdentity(identity)] ?? undefined; + } + + getRecordsSync( + typeOrIdentities?: string | RecordIdentity[] + ): InitializedRecord[] { + if (typeof typeOrIdentities === 'string') { + return objectValues(this._state.records[typeOrIdentities]); + } else if (Array.isArray(typeOrIdentities)) { + const records: InitializedRecord[] = []; + const identities: RecordIdentity[] = typeOrIdentities; + for (let i of identities) { + let record = this.getRecordSync(i); + if (record) { + records.push(record); + } + } + return records; + } else { + throw new Error('typeOrIdentities must be specified in getRecordsSync'); + } + } + + setRecordSync(record: InitializedRecord): void { + this._state.records[serializeRecordIdentity(record)] = record; + if (this._delta) { + this._delta.records[serializeRecordIdentity(record)] = record; + } + } + + setRecordsSync(records: InitializedRecord[]): void { + records.forEach((record) => this.setRecordSync(record)); + } + + removeRecordSync( + recordIdentity: RecordIdentity + ): InitializedRecord | undefined { + const record = this.getRecordSync(recordIdentity); + if (record) { + delete this._state.records[serializeRecordIdentity(record)]; + if (this._delta) { + this._delta.records[serializeRecordIdentity(record)] = null; + } + return record; + } else { + return undefined; + } + } + + removeRecordsSync(recordIdentities: RecordIdentity[]): InitializedRecord[] { + const records = []; + for (let recordIdentity of recordIdentities) { + let record = this.getRecordSync(recordIdentity); + if (record) { + records.push(record); + delete this._state.records[serializeRecordIdentity(record)]; + if (this._delta) { + this._delta.records[serializeRecordIdentity(record)] = null; + } + } + } + return records; + } + + getInverseRelationshipsSync( + recordIdentityOrIdentities: RecordIdentity | RecordIdentity[] + ): RecordRelationshipIdentity[] { + if (Array.isArray(recordIdentityOrIdentities)) { + let relationships: RecordRelationshipIdentity[] = []; + recordIdentityOrIdentities.forEach((record) => { + Array.prototype.push( + relationships, + this._getInverseRelationshipsSync(record) + ); + }); + return relationships; + } else { + return this._getInverseRelationshipsSync(recordIdentityOrIdentities); + } + } + + addInverseRelationshipsSync( + relationships: RecordRelationshipIdentity[] + ): void { + for (let relationship of relationships) { + const ri = serializeRecordIdentity(relationship.relatedRecord); + const rri = serializeRecordRelationshipIdentity(relationship); + const rels = this._state.inverseRelationships[ri] ?? {}; + rels[rri] = relationship; + this._state.inverseRelationships[ri] = rels; + if (this._delta) { + const rels = this._delta.inverseRelationships[ri] ?? {}; + rels[rri] = relationship; + this._delta.inverseRelationships[ri] = rels; + } + } + } + + removeInverseRelationshipsSync( + relationships: RecordRelationshipIdentity[] + ): void { + for (let relationship of relationships) { + const ri = serializeRecordIdentity(relationship.relatedRecord); + const rri = serializeRecordRelationshipIdentity(relationship); + const rels = this._state.inverseRelationships[ri]; + + if (rels) { + rels[rri] = null; + if (this._delta) { + const rels = this._delta.inverseRelationships[ri] ?? {}; + rels[rri] = null; + } + } + } + } + + ///////////////////////////////////////////////////////////////////////////// + // Protected methods + ///////////////////////////////////////////////////////////////////////////// + + protected _getInverseRelationshipsSync( + recordIdentity: RecordIdentity + ): RecordRelationshipIdentity[] { + let relationships = this._state.inverseRelationships[ + serializeRecordIdentity(recordIdentity) + ]; + if (relationships) { + return objectValues(relationships).filter((r) => r !== null); + } else { + return []; + } + } +} diff --git a/packages/@orbit/record-cache/src/sync-record-cache.ts b/packages/@orbit/record-cache/src/sync-record-cache.ts index 6b3d2efa..6d0077c1 100644 --- a/packages/@orbit/record-cache/src/sync-record-cache.ts +++ b/packages/@orbit/record-cache/src/sync-record-cache.ts @@ -1,4 +1,4 @@ -import { Orbit } from '@orbit/core'; +import { Assertion, Orbit } from '@orbit/core'; import { buildQuery, buildTransform, @@ -56,10 +56,7 @@ import { RecordCacheSettings, RecordCacheTransformOptions } from './record-cache'; -import { - RecordTransformBuffer, - RecordTransformBufferClass -} from './record-transform-buffer'; +import { RecordTransformBuffer } from './record-transform-buffer'; import { PatchResult, RecordCacheUpdateDetails } from './response'; import { SyncOperationProcessor, @@ -79,8 +76,7 @@ export interface SyncRecordCacheSettings< transformOperators?: Dict; inverseTransformOperators?: Dict; debounceLiveQueries?: boolean; - transformBufferClass?: RecordTransformBufferClass; - transformBufferSettings?: SyncRecordCacheSettings; + transformBuffer?: RecordTransformBuffer; } export abstract class SyncRecordCache< @@ -102,8 +98,6 @@ export abstract class SyncRecordCache< protected _inverseTransformOperators: Dict; protected _debounceLiveQueries: boolean; protected _transformBuffer?: RecordTransformBuffer; - protected _transformBufferClass?: RecordTransformBufferClass; - protected _transformBufferSettings?: SyncRecordCacheSettings; constructor(settings: SyncRecordCacheSettings) { super(settings); @@ -114,6 +108,7 @@ export abstract class SyncRecordCache< this._inverseTransformOperators = settings.inverseTransformOperators ?? SyncInverseTransformOperators; this._debounceLiveQueries = settings.debounceLiveQueries !== false; + this._transformBuffer = settings.transformBuffer; const processors: SyncOperationProcessorClass[] = settings.processors ? settings.processors @@ -472,14 +467,9 @@ export abstract class SyncRecordCache< protected _getTransformBuffer(): RecordTransformBuffer { if (this._transformBuffer === undefined) { - let transformBufferClass = - this._transformBufferClass ?? RecordTransformBuffer; - let settings = this._transformBufferSettings ?? { schema: this.schema }; - settings.schema = settings.schema ?? this.schema; - settings.keyMap = settings.keyMap ?? this.keyMap; - this._transformBuffer = new transformBufferClass(settings); - } else { - this._transformBuffer.reset(); + throw new Assertion( + 'transformBuffer must be provided to cache via constructor settings' + ); } return this._transformBuffer; } @@ -496,6 +486,7 @@ export abstract class SyncRecordCache< const relatedRecords = inverseRelationships.map((ir) => ir.record); Array.prototype.push.apply(records, relatedRecords); + buffer.resetState(); buffer.setRecordsSync(this.getRecordsSync(records)); buffer.addInverseRelationshipsSync(inverseRelationships); diff --git a/packages/@orbit/record-cache/test/async-record-cache-update-test.ts b/packages/@orbit/record-cache/test/async-record-cache-update-test.ts index fe7bc9d0..83632c9f 100644 --- a/packages/@orbit/record-cache/test/async-record-cache-update-test.ts +++ b/packages/@orbit/record-cache/test/async-record-cache-update-test.ts @@ -8,6 +8,7 @@ import { RecordSchema, RecordNotFoundException } from '@orbit/records'; +import { SimpleRecordTransformBuffer } from '../src/simple-record-transform-buffer'; import { ExampleAsyncRecordCache } from './support/example-async-record-cache'; import { createSchemaWithRemoteKey } from './support/setup'; @@ -16,7 +17,9 @@ const { module, test } = QUnit; QUnit.dump.maxDepth = 7; module('AsyncRecordCache - update', function (hooks) { - let schema: RecordSchema, keyMap: RecordKeyMap; + let schema: RecordSchema; + let keyMap: RecordKeyMap; + let transformBuffer: SimpleRecordTransformBuffer | undefined; [true, false].forEach((useBuffer) => { module(`useBuffer: ${useBuffer}`, function (hooks) { @@ -32,10 +35,21 @@ module('AsyncRecordCache - update', function (hooks) { hooks.beforeEach(function () { schema = createSchemaWithRemoteKey(); + + if (useBuffer) { + transformBuffer = new SimpleRecordTransformBuffer({ + schema, + keyMap + }); + } else { + transformBuffer = undefined; + } + cache = new ExampleAsyncRecordCache({ schema, keyMap, - defaultTransformOptions + defaultTransformOptions, + transformBuffer }); }); @@ -1811,10 +1825,20 @@ module('AsyncRecordCache - update', function (hooks) { } }); + if (useBuffer) { + transformBuffer = new SimpleRecordTransformBuffer({ + schema: hasOneSchema, + keyMap + }); + } else { + transformBuffer = undefined; + } + const cache = new ExampleAsyncRecordCache({ schema: hasOneSchema, keyMap, - defaultTransformOptions + defaultTransformOptions, + transformBuffer }); await cache.update((t) => [ @@ -1893,10 +1917,20 @@ module('AsyncRecordCache - update', function (hooks) { } }); + if (useBuffer) { + transformBuffer = new SimpleRecordTransformBuffer({ + schema: dependentSchema, + keyMap + }); + } else { + transformBuffer = undefined; + } + const cache = new ExampleAsyncRecordCache({ schema: dependentSchema, keyMap, - defaultTransformOptions + defaultTransformOptions, + transformBuffer }); const jupiter: InitializedRecord = { @@ -1961,10 +1995,20 @@ module('AsyncRecordCache - update', function (hooks) { } }); + if (useBuffer) { + transformBuffer = new SimpleRecordTransformBuffer({ + schema: dependentSchema, + keyMap + }); + } else { + transformBuffer = undefined; + } + const cache = new ExampleAsyncRecordCache({ schema: dependentSchema, keyMap, - defaultTransformOptions + defaultTransformOptions, + transformBuffer }); const jupiter: InitializedRecord = { @@ -2029,10 +2073,20 @@ module('AsyncRecordCache - update', function (hooks) { } }); + if (useBuffer) { + transformBuffer = new SimpleRecordTransformBuffer({ + schema: dependentSchema, + keyMap + }); + } else { + transformBuffer = undefined; + } + const cache = new ExampleAsyncRecordCache({ schema: dependentSchema, keyMap, - defaultTransformOptions + defaultTransformOptions, + transformBuffer }); const jupiter: InitializedRecord = { diff --git a/packages/@orbit/record-cache/test/sync-record-cache-update-test.ts b/packages/@orbit/record-cache/test/sync-record-cache-update-test.ts index 118a971b..1f92f9b2 100644 --- a/packages/@orbit/record-cache/test/sync-record-cache-update-test.ts +++ b/packages/@orbit/record-cache/test/sync-record-cache-update-test.ts @@ -8,6 +8,7 @@ import { RecordSchema, RecordNotFoundException } from '@orbit/records'; +import { SimpleRecordTransformBuffer } from '../src/simple-record-transform-buffer'; import { ExampleSyncRecordCache } from './support/example-sync-record-cache'; import { createSchemaWithRemoteKey } from './support/setup'; @@ -16,7 +17,9 @@ const { module, test } = QUnit; QUnit.dump.maxDepth = 7; module('SyncRecordCache', function (hooks) { - let schema: RecordSchema, keyMap: RecordKeyMap; + let schema: RecordSchema; + let keyMap: RecordKeyMap; + let transformBuffer: SimpleRecordTransformBuffer | undefined; [true, false].forEach((useBuffer) => { module(`useBuffer: ${useBuffer}`, function (hooks) { @@ -26,6 +29,15 @@ module('SyncRecordCache', function (hooks) { hooks.beforeEach(function () { schema = createSchemaWithRemoteKey(); keyMap = new RecordKeyMap(); + + if (useBuffer) { + transformBuffer = new SimpleRecordTransformBuffer({ + schema, + keyMap + }); + } else { + transformBuffer = undefined; + } }); test('#update sets data and #records retrieves it', function (assert) { @@ -34,7 +46,8 @@ module('SyncRecordCache', function (hooks) { const cache = new ExampleSyncRecordCache({ schema, keyMap, - defaultTransformOptions + defaultTransformOptions, + transformBuffer }); const earth: InitializedRecord = { @@ -69,7 +82,11 @@ module('SyncRecordCache', function (hooks) { test('#update can replace records', function (assert) { assert.expect(5); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const earth: InitializedRecord = { type: 'planet', @@ -108,7 +125,11 @@ module('SyncRecordCache', function (hooks) { test('#update can replace keys', function (assert) { assert.expect(4); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const earth: InitializedRecord = { type: 'planet', id: '1' }; @@ -141,7 +162,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update updates the cache and returns primary data', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); let p1 = { type: 'planet', id: '1', attributes: { name: 'Earth' } }; let p2 = { type: 'planet', id: '2' }; @@ -159,7 +184,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update updates the cache and returns a full response if requested', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); let p1 = { type: 'planet', id: '1', attributes: { name: 'Earth' } }; let p2 = { type: 'planet', id: '2' }; @@ -191,7 +220,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update updates inverse hasOne relationship when a record with relationships unspecified is added - record added after', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const jupiter: InitializedRecord = { type: 'planet', @@ -224,7 +257,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update updates inverse hasOne relationship when a record with relationships unspecified is added - record added before', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const jupiter: InitializedRecord = { type: 'planet', @@ -257,7 +294,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update updates inverse hasMany relationship when a record with relationships unspecified is added - record added after', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const io: InitializedRecord = { type: 'moon', @@ -290,7 +331,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update updates inverse hasMany relationship when a record with relationships unspecified is added - record added before', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const io: InitializedRecord = { type: 'moon', @@ -323,7 +368,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update updates inverse hasOne relationship when a record with an empty relationship is added', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const io: InitializedRecord = { type: 'moon', @@ -357,7 +406,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update updates inverse hasMany relationship when a record with an empty relationship is added', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const jupiter: InitializedRecord = { type: 'planet', @@ -391,7 +444,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update updates inverse hasMany polymorphic relationship', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const sun: InitializedRecord = { type: 'star', @@ -449,7 +506,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update updates inverse hasOne polymorphic relationship', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const jupiter: InitializedRecord = { type: 'planet', @@ -501,7 +562,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update tracks refs and clears them from hasOne relationships when a referenced record is removed', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const jupiter: InitializedRecord = { type: 'planet', @@ -564,7 +629,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update tracks refs and clears them from hasMany relationships when a referenced record is removed', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const io: InitializedRecord = { type: 'moon', @@ -644,7 +713,11 @@ module('SyncRecordCache', function (hooks) { }); test("#update adds link to hasMany if record doesn't exist", function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); cache.update((t) => t.addToRelatedRecords({ type: 'planet', id: 'p1' }, 'moons', { @@ -673,7 +746,11 @@ module('SyncRecordCache', function (hooks) { test("#update does not remove hasMany relationship if record doesn't exist", function (assert) { assert.expect(1); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); cache.on('patch', () => { assert.ok(false, 'no operations were applied'); @@ -696,7 +773,11 @@ module('SyncRecordCache', function (hooks) { test("#update adds hasOne if record doesn't exist", function (assert) { assert.expect(2); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const tb = cache.transformBuilder; const replacePlanet = tb.replaceRelatedRecord( @@ -737,7 +818,11 @@ module('SyncRecordCache', function (hooks) { test("#update will add empty hasOne link if record doesn't exist", function (assert) { assert.expect(2); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const tb = cache.transformBuilder; const clearPlanet = tb.replaceRelatedRecord( @@ -768,7 +853,11 @@ module('SyncRecordCache', function (hooks) { test('#update does not add link to hasMany if link already exists', function (assert) { assert.expect(1); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const jupiter: InitializedRecord = { id: 'p1', @@ -793,7 +882,11 @@ module('SyncRecordCache', function (hooks) { test("#update does not remove relationship from hasMany if relationship doesn't exist", function (assert) { assert.expect(1); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const jupiter: InitializedRecord = { id: 'p1', @@ -820,7 +913,11 @@ module('SyncRecordCache', function (hooks) { test('#update can add and remove to has-many relationship', function (assert) { assert.expect(2); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const jupiter: InitializedRecord = { id: 'jupiter', type: 'planet' }; cache.update((t) => t.addRecord(jupiter)); @@ -862,7 +959,11 @@ module('SyncRecordCache', function (hooks) { test('#update can add and clear has-one relationship', function (assert) { assert.expect(2); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const jupiter: InitializedRecord = { id: 'jupiter', type: 'planet' }; cache.update((t) => t.addRecord(jupiter)); @@ -899,7 +1000,11 @@ module('SyncRecordCache', function (hooks) { test('does not replace hasOne if relationship already exists', function (assert) { assert.expect(1); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const europa: InitializedRecord = { id: 'm1', @@ -924,7 +1029,11 @@ module('SyncRecordCache', function (hooks) { test("does not remove hasOne if relationship doesn't exist", function (assert) { assert.expect(1); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const europa: InitializedRecord = { type: 'moon', @@ -1219,7 +1328,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update merges records when "replacing" and will not stomp on attributes and relationships that are not replaced', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const tb = cache.transformBuilder; cache.update((t) => @@ -1309,7 +1422,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update can replace related records but only if they are different', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const tb = cache.transformBuilder; cache.update((t) => @@ -1444,7 +1561,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update merges records when "replacing" and _will_ replace specified attributes and relationships', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const tb = cache.transformBuilder; const earth: InitializedRecord = { @@ -1597,7 +1718,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update can update existing record with empty relationship', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const tb = cache.transformBuilder; let result = cache.update( @@ -1706,7 +1831,11 @@ module('SyncRecordCache', function (hooks) { }); test('#update will not overwrite an existing relationship with a missing relationship', function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const tb = cache.transformBuilder; let result = cache.update( @@ -1805,7 +1934,11 @@ module('SyncRecordCache', function (hooks) { test('#update allows replaceRelatedRecord to be called on a relationship with no inverse and to be followed up by removing the replaced record', function (assert) { assert.expect(2); - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const star1 = { id: 'star1', @@ -1871,7 +2004,11 @@ module('SyncRecordCache', function (hooks) { }); test("#update - updateRecord - throws RecordNotFoundException if record doesn't exist with `raiseNotFoundExceptions` option", function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const earth: InitializedRecord = { type: 'planet', @@ -1892,7 +2029,11 @@ module('SyncRecordCache', function (hooks) { }); test("#update - removeRecord - throws RecordNotFoundException if record doesn't exist with `raiseNotFoundExceptions` option", function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const earth: InitializedRecord = { type: 'planet', @@ -1911,7 +2052,11 @@ module('SyncRecordCache', function (hooks) { }); test("#update - replaceKey - throws RecordNotFoundException if record doesn't exist with `raiseNotFoundExceptions` option", function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const earth: InitializedRecord = { type: 'planet', @@ -1932,7 +2077,11 @@ module('SyncRecordCache', function (hooks) { }); test("#update - replaceAttribute - throws RecordNotFoundException if record doesn't exist with `raiseNotFoundExceptions` option", function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); const earth: InitializedRecord = { type: 'planet', @@ -1953,7 +2102,11 @@ module('SyncRecordCache', function (hooks) { }); test("#update - addToRelatedRecords - throws RecordNotFoundException if record doesn't exist with `raiseNotFoundExceptions` option", function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); assert.throws( () => @@ -1972,7 +2125,11 @@ module('SyncRecordCache', function (hooks) { }); test("#update - removeFromRelatedRecords - throws RecordNotFoundException if record doesn't exist with `raiseNotFoundExceptions` option", function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); assert.throws( () => @@ -1995,7 +2152,11 @@ module('SyncRecordCache', function (hooks) { }); test("#update - replaceRelatedRecords - throws RecordNotFoundException if record doesn't exist with `raiseNotFoundExceptions` option", function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); assert.throws( () => @@ -2016,7 +2177,11 @@ module('SyncRecordCache', function (hooks) { }); test("#update - replaceRelatedRecord - throws RecordNotFoundException if record doesn't exist with `raiseNotFoundExceptions` option", function (assert) { - const cache = new ExampleSyncRecordCache({ schema, keyMap }); + const cache = new ExampleSyncRecordCache({ + schema, + keyMap, + transformBuffer + }); assert.throws( () =>