From 6a61c47685e2ca0fabe1f900f5cf1f5a59245f6d Mon Sep 17 00:00:00 2001 From: Chris Thoburn Date: Thu, 27 Jul 2023 21:24:05 -0700 Subject: [PATCH] feat: JSON:API serialization utils (#8741) * feat: serialization utils * fix docs test --- ember-data-types/cache/cache.ts | 8 ++- ember-data-types/q/cache.ts | 6 ++ packages/json-api/src/-private/cache.ts | 2 +- packages/json-api/src/-private/serialize.ts | 63 +++++++++++++++++++++ 4 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 packages/json-api/src/-private/serialize.ts diff --git a/ember-data-types/cache/cache.ts b/ember-data-types/cache/cache.ts index 6ef96eafd3b..d705ed6a68e 100644 --- a/ember-data-types/cache/cache.ts +++ b/ember-data-types/cache/cache.ts @@ -330,10 +330,16 @@ export interface Cache { /** * Query the cache for the changed attributes of a resource. * + * Returns a map of field names to tuples of [old, new] values + * + * ``` + * { : [, ] } + * ``` + * * @method changedAttrs * @public * @param identifier - * @returns { : [, ] } + * @returns {Record} { : [, ] } */ changedAttrs(identifier: StableRecordIdentifier): Record; diff --git a/ember-data-types/q/cache.ts b/ember-data-types/q/cache.ts index e9d99e5bf55..ade3bb2e734 100644 --- a/ember-data-types/q/cache.ts +++ b/ember-data-types/q/cache.ts @@ -6,6 +6,12 @@ import type { JsonApiError, JsonApiResource } from './record-data-json-api'; @module @ember-data/store */ +/** + * A hash of changed attributes with the key being the attribute name and the value being an + * array of `[oldValue, newValue]`. + * + * @internal + */ export type ChangedAttributesHash = Record; export interface MergeOperation { diff --git a/packages/json-api/src/-private/cache.ts b/packages/json-api/src/-private/cache.ts index 2460108d364..5fa5c1bc99c 100644 --- a/packages/json-api/src/-private/cache.ts +++ b/packages/json-api/src/-private/cache.ts @@ -951,7 +951,7 @@ export default class JSONAPICache implements Cache { * @method changedAttrs * @public * @param identifier - * @returns { : [, ] } + * @returns {ChangedAttributesHash} { : [, ] } */ changedAttrs(identifier: StableRecordIdentifier): ChangedAttributesHash { // TODO freeze in dev diff --git a/packages/json-api/src/-private/serialize.ts b/packages/json-api/src/-private/serialize.ts new file mode 100644 index 00000000000..28d9c3b189e --- /dev/null +++ b/packages/json-api/src/-private/serialize.ts @@ -0,0 +1,63 @@ +import { assert } from '@ember/debug'; + +import type { Cache } from '@ember-data/types/cache/cache'; +import type { StableRecordIdentifier } from '@ember-data/types/q/identifier'; +import type { JsonApiResource } from '@ember-data/types/q/record-data-json-api'; + +export function serializeResources(cache: Cache, identifiers: StableRecordIdentifier): { data: JsonApiResource }; +export function serializeResources(cache: Cache, identifiers: StableRecordIdentifier[]): { data: JsonApiResource[] }; +export function serializeResources( + cache: Cache, + identifiers: StableRecordIdentifier | StableRecordIdentifier[] +): { data: JsonApiResource | JsonApiResource[] } { + return { + data: Array.isArray(identifiers) + ? identifiers.map((identifier) => _serializeResource(cache, identifier)) + : _serializeResource(cache, identifiers), + }; +} + +function _serializeResource(cache: Cache, identifier: StableRecordIdentifier): JsonApiResource { + const { id, lid, type } = identifier; + // yup! this method actually does nothing. It's just here for the dev assertion + // and to assist in providing a little sugar to the consuming app via the `serializeResources` utility + const record = cache.peek(identifier) as JsonApiResource; + assert( + `A record with id ${String(id)} and type ${type} for lid ${lid} was not found not in the supplied Cache.`, + record + ); + + return record; +} + +export function serializePatch( + cache: Cache, + identifier: StableRecordIdentifier + // options: { include?: string[] } = {} +): { data: JsonApiResource } { + const { id, lid, type } = identifier; + const record = cache.peek(identifier) as JsonApiResource; + assert( + `A record with id ${String(id)} and type ${type} for lid ${lid} was not found not in the supplied Cache.`, + record + ); + + const attrsChanges = cache.changedAttrs(identifier); + const attributes = {}; + + Object.keys(attrsChanges).forEach((key) => { + const newVal = attrsChanges[key][1]; + attributes[key] = newVal === undefined ? null : newVal; + }); + + const data: JsonApiResource = { + type, + lid, + id, + attributes, + // TODO we don't patch relationships yet ... + // ... but we will as soon as we land the diff PR + }; + + return { data }; +}