Skip to content

Commit

Permalink
feat: JSON:API serialization utils (#8741)
Browse files Browse the repository at this point in the history
* feat: serialization utils

* fix docs test
  • Loading branch information
runspired authored Jul 28, 2023
1 parent 8ad83d7 commit 6a61c47
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 2 deletions.
8 changes: 7 additions & 1 deletion ember-data-types/cache/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
* ```
* { <field>: [<old>, <new>] }
* ```
*
* @method changedAttrs
* @public
* @param identifier
* @returns { <field>: [<old>, <new>] }
* @returns {Record<string, [unknown, unknown]>} { <field>: [<old>, <new>] }
*/
changedAttrs(identifier: StableRecordIdentifier): Record<string, [unknown, unknown]>;

Expand Down
6 changes: 6 additions & 0 deletions ember-data-types/q/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, [unknown, unknown]>;

export interface MergeOperation {
Expand Down
2 changes: 1 addition & 1 deletion packages/json-api/src/-private/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,7 @@ export default class JSONAPICache implements Cache {
* @method changedAttrs
* @public
* @param identifier
* @returns { <field>: [<old>, <new>] }
* @returns {ChangedAttributesHash} { <field>: [<old>, <new>] }
*/
changedAttrs(identifier: StableRecordIdentifier): ChangedAttributesHash {
// TODO freeze in dev
Expand Down
63 changes: 63 additions & 0 deletions packages/json-api/src/-private/serialize.ts
Original file line number Diff line number Diff line change
@@ -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 };
}

0 comments on commit 6a61c47

Please sign in to comment.