@orbit/record-cache
Version:
Orbit base classes used to access and maintain a set of records.
279 lines (234 loc) • 7.58 kB
text/typescript
import { RequestOptions } from '@orbit/data';
import {
AddRecordOperation,
AddToRelatedRecordsOperation,
cloneRecordIdentity,
equalRecordIdentities,
InitializedRecord,
mergeRecords,
RecordIdentity,
RecordNotFoundException,
RecordOperation,
RecordOperationResult,
recordsInclude,
RemoveFromRelatedRecordsOperation,
RemoveRecordOperation,
ReplaceAttributeOperation,
ReplaceKeyOperation,
ReplaceRelatedRecordOperation,
ReplaceRelatedRecordsOperation,
UpdateRecordOperation
} from '@orbit/records';
import { clone, deepGet, deepSet, Dict } from '@orbit/utils';
import { AsyncRecordAccessor } from '../record-accessor';
export interface AsyncTransformOperator {
(
cache: AsyncRecordAccessor,
operation: RecordOperation,
options?: RequestOptions
): Promise<RecordOperationResult>;
}
export const AsyncTransformOperators: Dict<AsyncTransformOperator> = {
async addRecord(
cache: AsyncRecordAccessor,
operation: RecordOperation,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
options?: RequestOptions
): Promise<RecordOperationResult> {
const op = operation as AddRecordOperation;
const { record } = op;
await cache.setRecordAsync(record);
if (cache.keyMap) {
cache.keyMap.pushRecord(record);
}
return record;
},
async updateRecord(
cache: AsyncRecordAccessor,
operation: RecordOperation,
options?: RequestOptions
): Promise<RecordOperationResult> {
const op = operation as UpdateRecordOperation;
const { record } = op;
const currentRecord = await cache.getRecordAsync(record);
if (currentRecord === undefined) {
if (options?.raiseNotFoundExceptions) {
throw new RecordNotFoundException(record.type, record.id);
}
}
const mergedRecord = mergeRecords(currentRecord || null, record);
await cache.setRecordAsync(mergedRecord);
if (cache.keyMap) {
cache.keyMap.pushRecord(mergedRecord);
}
return mergedRecord;
},
async removeRecord(
cache: AsyncRecordAccessor,
operation: RecordOperation,
options?: RequestOptions
): Promise<RecordOperationResult> {
const op = operation as RemoveRecordOperation;
const record = await cache.removeRecordAsync(op.record);
if (record === undefined) {
if (options?.raiseNotFoundExceptions) {
throw new RecordNotFoundException(op.record.type, op.record.id);
}
}
return record;
},
async replaceKey(
cache: AsyncRecordAccessor,
operation: RecordOperation,
options?: RequestOptions
): Promise<RecordOperationResult> {
const op = operation as ReplaceKeyOperation;
const currentRecord = await cache.getRecordAsync(op.record);
let record: InitializedRecord;
if (currentRecord) {
record = clone(currentRecord);
} else {
record = cloneRecordIdentity(op.record);
if (options?.raiseNotFoundExceptions) {
throw new RecordNotFoundException(record.type, record.id);
}
}
deepSet(record, ['keys', op.key], op.value);
await cache.setRecordAsync(record);
if (cache.keyMap) {
cache.keyMap.pushRecord(record);
}
return record;
},
async replaceAttribute(
cache: AsyncRecordAccessor,
operation: RecordOperation,
options?: RequestOptions
): Promise<RecordOperationResult> {
const op = operation as ReplaceAttributeOperation;
const currentRecord = await cache.getRecordAsync(op.record);
let record: InitializedRecord;
if (currentRecord) {
record = clone(currentRecord);
} else {
record = cloneRecordIdentity(op.record);
if (options?.raiseNotFoundExceptions) {
throw new RecordNotFoundException(record.type, record.id);
}
}
deepSet(record, ['attributes', op.attribute], op.value);
await cache.setRecordAsync(record);
return record;
},
async addToRelatedRecords(
cache: AsyncRecordAccessor,
operation: RecordOperation,
options?: RequestOptions
): Promise<RecordOperationResult> {
const op = operation as AddToRelatedRecordsOperation;
const { relationship, relatedRecord } = op;
const currentRecord = await cache.getRecordAsync(op.record);
let record: InitializedRecord;
if (currentRecord) {
record = clone(currentRecord);
} else {
record = cloneRecordIdentity(op.record);
if (options?.raiseNotFoundExceptions) {
throw new RecordNotFoundException(record.type, record.id);
}
}
const relatedRecords: RecordIdentity[] =
deepGet(record, ['relationships', relationship, 'data']) || [];
if (!recordsInclude(relatedRecords, relatedRecord)) {
relatedRecords.push(relatedRecord);
deepSet(record, ['relationships', relationship, 'data'], relatedRecords);
await cache.setRecordAsync(record);
}
return record;
},
async removeFromRelatedRecords(
cache: AsyncRecordAccessor,
operation: RecordOperation,
options?: RequestOptions
): Promise<RecordOperationResult> {
const op = operation as RemoveFromRelatedRecordsOperation;
const currentRecord = await cache.getRecordAsync(op.record);
const { relationship, relatedRecord } = op;
let record: InitializedRecord;
if (currentRecord) {
record = clone(currentRecord);
let relatedRecords: RecordIdentity[] = deepGet(record, [
'relationships',
relationship,
'data'
]);
if (relatedRecords) {
relatedRecords = relatedRecords.filter(
(r) => !equalRecordIdentities(r, relatedRecord)
);
if (
deepSet(
record,
['relationships', relationship, 'data'],
relatedRecords
)
) {
await cache.setRecordAsync(record);
}
}
return record;
} else {
if (options?.raiseNotFoundExceptions) {
throw new RecordNotFoundException(op.record.type, op.record.id);
}
}
},
async replaceRelatedRecords(
cache: AsyncRecordAccessor,
operation: RecordOperation,
options?: RequestOptions
): Promise<RecordOperationResult> {
const op = operation as ReplaceRelatedRecordsOperation;
const currentRecord = await cache.getRecordAsync(op.record);
const { relationship, relatedRecords } = op;
let record: InitializedRecord;
if (currentRecord) {
record = clone(currentRecord);
} else {
record = cloneRecordIdentity(op.record);
if (options?.raiseNotFoundExceptions) {
throw new RecordNotFoundException(record.type, record.id);
}
}
if (
deepSet(record, ['relationships', relationship, 'data'], relatedRecords)
) {
await cache.setRecordAsync(record);
}
return record;
},
async replaceRelatedRecord(
cache: AsyncRecordAccessor,
operation: RecordOperation,
options?: RequestOptions
): Promise<RecordOperationResult> {
const op = operation as ReplaceRelatedRecordOperation;
const currentRecord = await cache.getRecordAsync(op.record);
const { relationship, relatedRecord } = op;
let record: InitializedRecord;
if (currentRecord) {
record = clone(currentRecord);
} else {
record = cloneRecordIdentity(op.record);
if (options?.raiseNotFoundExceptions) {
throw new RecordNotFoundException(record.type, record.id);
}
}
if (
deepSet(record, ['relationships', relationship, 'data'], relatedRecord)
) {
await cache.setRecordAsync(record);
}
return record;
}
};