Skip to content

Commit

Permalink
feat: serializers, serializers, oh my
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanhaticus committed Nov 10, 2024
1 parent 7c32b3a commit c2bf559
Show file tree
Hide file tree
Showing 10 changed files with 117 additions and 185 deletions.
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ import '@tsmetadata/polyfill';

export * from './decorators';
export * from './types';
export * from './serializers';
3 changes: 3 additions & 0 deletions src/serializers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './utils';
export * from './serializeResourceObject';
export * from './serializeResourceRelationshipObject';
184 changes: 0 additions & 184 deletions src/serializers/poc.ts

This file was deleted.

27 changes: 27 additions & 0 deletions src/serializers/serializeResourceObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { attributesSymbol, idSymbol, linksSymbol, metaSymbol, relationshipsSymbol, resourceSymbol } from "../decorators";
import { collect } from "./utils/collect";
import { getMetadataBySymbol } from "./utils/getMetadataBySymbol";
import { serializeResourceRelationshipObject } from "./serializeResourceRelationshipObject";

import type { JSONAPILinksObject, JSONAPIMetaObject, JSONAPIRelationshipObject, JSONAPIResourceObject, JSONObject } from "../types";

export const serializeResourceObject = <I extends object>(classInstance: I): JSONAPIResourceObject => {
const relationshipTuples = getMetadataBySymbol<[keyof I, string][]>(classInstance, relationshipsSymbol);

const relationships = relationshipTuples.reduce((acc, [key]) => {
const related = classInstance[key] as object;

acc[key] = Array.isArray(related) ? related.map(serializeResourceRelationshipObject) : serializeResourceRelationshipObject(related);

return acc;
}, {} as Record<keyof I, JSONAPIRelationshipObject | JSONAPIRelationshipObject[]>);

return {
type: getMetadataBySymbol<string>(classInstance, resourceSymbol),
id: collect<string>(classInstance, idSymbol),
attributes: collect<JSONObject>(classInstance, attributesSymbol),
relationships,
links: collect<JSONAPILinksObject>(classInstance, linksSymbol),
meta: collect<JSONAPIMetaObject>(classInstance, metaSymbol),
};
};
14 changes: 14 additions & 0 deletions src/serializers/serializeResourceRelationshipObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { idSymbol, linksSymbol, metaSymbol, resourceSymbol } from "../decorators";
import { collect } from "./utils/collect";
import { getMetadataBySymbol } from "./utils/getMetadataBySymbol";

import type { JSONAPILinksObject, JSONAPIMetaObject, JSONAPIRelationshipObject } from "../types";

export const serializeResourceRelationshipObject = <I extends object>(classInstance: I): JSONAPIRelationshipObject => ({
data: {
type: getMetadataBySymbol<string>(classInstance, resourceSymbol),
id: collect<string>(classInstance, idSymbol),
},
links: collect<JSONAPILinksObject>(classInstance, linksSymbol),
meta: collect<JSONAPIMetaObject>(classInstance, metaSymbol),
});
27 changes: 27 additions & 0 deletions src/serializers/utils/assertMetadataIsPresent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
export const assertMetadataIsPresent = (candidate: unknown | unknown[]): true => {
if (Symbol.metadata === undefined) {
throw new Error(
'Failed to assert the presence of metadata because the metadata symbol is undefined. You may need to import the `@tsmetadata/polyfill` package.',
);
}

const objects = Array.isArray(candidate) ? candidate : [candidate];

for (const object of objects) {
if(typeof object !== 'object') {
throw new Error(
'Failed to assert the presence of metadata because at least one candidate is not an object, meaning no constructor is present.',
);
}

const metadata = object.constructor[Symbol.metadata];

if (metadata === undefined || metadata === null) {
throw new Error(
'No metadata was found on an object constructor.'
);
}
}

return true;
};
32 changes: 32 additions & 0 deletions src/serializers/utils/collect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { assertMetadataIsPresent } from "./assertMetadataIsPresent";
import { getMetadataBySymbol } from "./getMetadataBySymbol";

export const collect = <T = unknown, O extends object = object>(object: O, symbol: symbol) => {
assertMetadataIsPresent(object);

const keyOrKeys = getMetadataBySymbol<keyof O | (keyof O)[]>(object, symbol);

if(Array.isArray(keyOrKeys)) {
const keys = keyOrKeys;

if (keys.length === 0) {
return {} as T;
}

return keys.reduce((acc, key) => {
const value = object[key];

if (value === undefined) {
return acc;
}

acc[key] = value;

return acc;
}, {} as Record<keyof O, unknown>) as T;
}

const key = keyOrKeys;

return object[key] as T;
};
9 changes: 9 additions & 0 deletions src/serializers/utils/getMetadataBySymbol.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { assertMetadataIsPresent } from "./assertMetadataIsPresent";

export const getMetadataBySymbol = <T = unknown, O extends object = object>(object: O, symbol: symbol) => {
assertMetadataIsPresent(object);

const metadata = object.constructor[Symbol.metadata] as DecoratorMetadataObject;

return metadata[symbol] as T;
};
3 changes: 3 additions & 0 deletions src/serializers/utils/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './assertMetadataIsPresent';
export * from './getMetadataBySymbol';
export * from './collect';
2 changes: 1 addition & 1 deletion src/types/relationshipsObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import type { JSONObject } from './json/object';
import type { JSONAPIRelationshipObject } from './relationshipObject';

export type JSONAPIRelationshipsObject = Satisfies<
{ [key: string]: JSONAPIRelationshipObject },
{ [key: string]: JSONAPIRelationshipObject | JSONAPIRelationshipObject[] },
JSONObject
>;

0 comments on commit c2bf559

Please sign in to comment.