UNPKG

@orbit/records

Version:

A flexible data access and synchronization layer.

170 lines (139 loc) 4.81 kB
import { Assertion, Orbit } from '@orbit/core'; import { clone } from '@orbit/utils'; import { cloneRecordIdentity, InitializedRecord, isRecordIdentity, RecordIdentity, RecordKeyValue, UninitializedRecord } from './record'; import { KeyNotDefined, ModelNotDefined } from './record-exceptions'; import { RecordKeyMap } from './record-key-map'; import { RecordNormalizer } from './record-normalizer'; import { RecordSchema } from './record-schema'; const { assert, deprecate } = Orbit; export interface StandardRecordNormalizerSettings { schema: RecordSchema; keyMap?: RecordKeyMap; cloneInputs?: boolean; validateInputs?: boolean; } export class StandardRecordNormalizer implements RecordNormalizer< string, RecordIdentity | RecordKeyValue, UninitializedRecord > { schema: RecordSchema; keyMap?: RecordKeyMap; cloneInputs?: boolean; validateInputs?: boolean; constructor(settings: StandardRecordNormalizerSettings) { const { schema, keyMap, cloneInputs, validateInputs } = settings; assert( "StandardRecordNormalizer's `schema` must be specified in the `settings.schema` constructor argument", !!schema ); this.schema = schema; this.keyMap = keyMap; this.cloneInputs = cloneInputs; this.validateInputs = validateInputs; } normalizeRecordType(type: string): string { if (this.validateInputs) { if (typeof type !== 'string') { throw new Assertion( 'StandardRecordNormalizer expects record types to be strings' ); } if (!this.schema.hasModel(type)) { throw new ModelNotDefined(type); } } return type; } normalizeRecordIdentity( identity: RecordIdentity | RecordKeyValue ): RecordIdentity { const { schema, keyMap, cloneInputs, validateInputs } = this; const { type } = identity; if (validateInputs && !schema.hasModel(type)) { throw new ModelNotDefined(type); } if (isRecordIdentity(identity)) { return cloneInputs ? this.cloneRecordIdentity(identity) : identity; } else { if (keyMap === undefined) { throw new Assertion( "StandardRecordNormalizer's `keyMap` must be specified in order to lookup an `id` from a `key`" ); } const { key, value } = identity; if (typeof key !== 'string' || typeof value !== 'string') { throw new Assertion( 'StandardRecordNormalizer expects record identities in the form `{ type: string, id: string }` or `{ type: string, key: string, value: string }`' ); } if (validateInputs && !schema.hasKey(type, key)) { throw new KeyNotDefined(type, key); } let id = keyMap.keyToId(type, key, value); if (id === undefined) { id = this.schema.generateId(type); keyMap.pushRecord({ type, id, keys: { [key]: value } }); } return { type, id }; } } normalizeRecord(record: UninitializedRecord): InitializedRecord { const { keyMap, schema, cloneInputs, validateInputs } = this; const { type } = record; if (validateInputs && !schema.hasModel(type)) { throw new ModelNotDefined(type); } let result = cloneInputs ? this.cloneRecord(record) : record; // If `initializeRecord` has been defined on the schema, continue to call it // but issue a deprecation warning. // TODO: Remove in v0.18 if ((schema as any).initializeRecord !== undefined) { deprecate( "RecordSchema's `initializeRecord` method should NOT be defined. Instead override `normalizeRecord` on the RecordNormalizer to initialize records." ); result = (schema as any).initializeRecord(result); } if (result.id === undefined) { const { keys } = result; // Lookup id from keys if possible if (keyMap !== undefined && keys !== undefined) { if (validateInputs) { Object.keys(keys).forEach((key) => { if (!schema.hasKey(type, key)) { throw new KeyNotDefined(type, key); } }); } const id = keyMap.idFromKeys(result.type, keys); if (id) { result.id = id; } } // Generate an id if one has still not been assigned if (result.id === undefined) { result.id = schema.generateId(result.type); // Register any generated ids in the keyMap, if present if (keyMap && result.keys !== undefined) { keyMap.pushRecord(result); } } } return result as InitializedRecord; } protected cloneRecordIdentity(rid: RecordIdentity): RecordIdentity { return cloneRecordIdentity(rid); } protected cloneRecord(record: UninitializedRecord): UninitializedRecord { return clone(record); } }