Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: make serialize utils smarter #9692

Merged
merged 2 commits into from
Feb 22, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 54 additions & 11 deletions packages/json-api/src/-private/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@ import type { StableRecordIdentifier } from '@warp-drive/core-types';
import type { Cache } from '@warp-drive/core-types/cache';
import type { Relationship } from '@warp-drive/core-types/cache/relationship';
import type { Value } from '@warp-drive/core-types/json/raw';
import type { ResourceObject } from '@warp-drive/core-types/spec/json-api-raw';
import type { InnerRelationshipDocument, ResourceObject } from '@warp-drive/core-types/spec/json-api-raw';

type ChangedRelationshipData = {
data: Relationship['data'];
};
type ChangedRelationshipData = InnerRelationshipDocument;

export type JsonApiResourcePatch = {
type: string;
Expand Down Expand Up @@ -44,16 +42,62 @@ export function serializeResources(
};
}

type SerializedRef =
| {
id: string;
type: string;
}
| { id: null; lid: string; type: string };

function fixRef({
id,
lid,
type,
}: { id: string; lid?: string; type: string } | { id: null; lid: string; type: string }): SerializedRef {
if (id !== null) {
return { id, type };
}
return { id, lid, type };
}

function fixRelData(
rel: Relationship['data'] | InnerRelationshipDocument['data']
): SerializedRef | SerializedRef[] | null {
if (Array.isArray(rel)) {
return rel.map((ref) => fixRef(ref));
} else if (typeof rel === 'object' && rel !== null) {
return fixRef(rel);
}
return null;
}

function _serializeResource(cache: Cache, identifier: StableRecordIdentifier): ResourceObject {
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 ResourceObject;
// peek gives us everything we want, but since its referentially the same data
// as is in the cache we clone it to avoid any accidental mutations
const record = structuredClone(cache.peek(identifier)) as ResourceObject;
assert(
`A record with id ${String(id)} and type ${type} for lid ${lid} was not found not in the supplied Cache.`,
record
);

// remove lid from anything that has an ID and slice any relationship arrays
if (record.id !== null) {
delete record.lid;
}

if (record.relationships) {
for (const key of Object.keys(record.relationships)) {
const relationship = record.relationships[key];
relationship.data = fixRelData(relationship.data);
if (Array.isArray(relationship.data)) {
relationship.data = relationship.data.map((ref) => fixRef(ref));
} else if (typeof relationship.data === 'object' && relationship.data !== null) {
relationship.data = fixRef(relationship.data);
}
}
}

return record;
}

Expand Down Expand Up @@ -87,10 +131,9 @@ export function serializePatch(
// options: { include?: string[] } = {}
): { data: JsonApiResourcePatch } {
const { id, lid, type } = identifier;
const record = cache.peek(identifier) as ResourceObject;
assert(
`A record with id ${String(id)} and type ${type} for lid ${lid} was not found not in the supplied Cache.`,
record
cache.peek(identifier)
);

const data: JsonApiResourcePatch = {
Expand All @@ -106,7 +149,7 @@ export function serializePatch(
Object.keys(attrsChanges).forEach((key) => {
const change = attrsChanges[key];
const newVal = change[1];
attributes[key] = newVal === undefined ? null : newVal;
attributes[key] = newVal === undefined ? null : structuredClone(newVal);
});

data.attributes = attributes;
Expand All @@ -116,7 +159,7 @@ export function serializePatch(
if (changedRelationships.size) {
const relationships: Record<string, ChangedRelationshipData> = {};
changedRelationships.forEach((diff, key) => {
relationships[key] = { data: diff.localState };
relationships[key] = { data: fixRelData(diff.localState) } as ChangedRelationshipData;
});

data.relationships = relationships;
Expand Down
Loading