Skip to content

Commit

Permalink
fix: relationship object serialization (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanhaticus authored Dec 9, 2024
1 parent 87f7d89 commit 113ea51
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 21 deletions.
13 changes: 0 additions & 13 deletions __tests__/serializers/serializeResourceLinkage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,6 @@ describe('`serializeResourceLinkage`', () => {
chance = new Chance();
});

describe('when given a resource linkage', () => {
it('should return the resource linkage as is', () => {
const resourceLinkage = {
type: chance.string(),
id: chance.string(),
};

expect(serializeResourceLinkage(resourceLinkage)).toEqual(
resourceLinkage,
);
});
});

describe('when given an array of class instances', () => {
it('should throw an error if some element in the array is not an object', () => {
const classInstances = [1];
Expand Down
49 changes: 47 additions & 2 deletions __tests__/serializers/serializeResourceObject.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ describe('`serializeResourceObject`', () => {
});
});

describe('when the related class instance is a single object', () => {
it('should throw an error if the related class instance is not an object', () => {
describe('when the related class instance is not an object', () => {
it('should throw an error', () => {
const key = chance.string();

getMetadataBySymbolMocked.mockImplementation(
Expand All @@ -144,7 +144,9 @@ describe('`serializeResourceObject`', () => {
`Failed to serialize relationship object for ${key} because the value is not an object.`,
);
});
});

describe('when the related class instance is an object', () => {
it('should serialize the relationships object and return it', () => {
const key = chance.string();

Expand Down Expand Up @@ -201,6 +203,49 @@ describe('`serializeResourceObject`', () => {
});
});

describe('when the related class instance is already a relationship object', () => {
it('should return without serializing the relationship object', () => {
const key = chance.string();

getMetadataBySymbolMocked.mockImplementation(
(_: object, symbol: symbol) => {
if (symbol === resourceSymbol) {
return 'type';
}

if (symbol === relationshipsSymbol) {
return [[key, '']];
}

return undefined;
},
);

collectMocked.mockImplementation((_: object, symbol: symbol) => {
if (symbol === idSymbol) {
return 'id';
}

return undefined;
});

const relatedClassInstance = {
id: 'some id',
type: 'some type',
};

const classInstance = {
[key]: {
data: relatedClassInstance,
},
};

const result = serializeResourceObject(classInstance);

expect(result.relationships).toBeUndefined();
});
});

it('should return without the `relationships` field if no relationships are found', () => {
getMetadataBySymbolMocked.mockImplementation(
(_: object, symbol: symbol) => {
Expand Down
35 changes: 35 additions & 0 deletions __tests__/serializers/utils/isRelationshipObject.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { isRelationshipObject } from '../../../src/serializers/utils/isRelationshipObject';
import { isResourceLinkage } from '../../../src/serializers/utils/isResourceLinkage';

jest.mock('../../../src/serializers/utils/isResourceLinkage');
const isResourceLinkageMocked = jest.mocked(isResourceLinkage);

describe('`isRelationshipObject`', () => {
it('should return false if `relationshipObjectCandidate` is `null`', () => {
const relationshipObjectCandidate = null;

expect(isRelationshipObject(relationshipObjectCandidate)).toBe(false);
});

it('should return false if `relationshipObjectCandidate` is not an object', () => {
const relationshipObjectCandidate = 'not an object';

expect(isRelationshipObject(relationshipObjectCandidate)).toBe(false);
});

it('should return false if `relationshipObjectCandidate` does not have a `data` property', () => {
const relationshipObjectCandidate = {};

expect(isRelationshipObject(relationshipObjectCandidate)).toBe(false);
});

it('should return true if `relationshipObjectCandidate.data` is a `JSONAPIResourceLinkage`', () => {
isResourceLinkageMocked.mockReturnValue(true);

const relationshipObjectCandidate = {
data: 'foo',
};

expect(isRelationshipObject(relationshipObjectCandidate)).toBe(true);
});
});
7 changes: 2 additions & 5 deletions src/serializers/serializeResourceLinkage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@ import { resourceSymbol } from '../decorators/resource';
import { collect } from './utils/collect';
import { getMetadataBySymbol } from './utils/getMetadataBySymbol';
import { isObject } from './utils/isObject';
import { isResourceLinkage } from './utils/isResourceLinkage';

import type { JSONAPIResourceLinkage } from '../types/resourceLinkage';

export const serializeResourceLinkage = <I extends object>(
classInstance_s: I | JSONAPIResourceLinkage,
classInstanceCandidate_s: I | JSONAPIResourceLinkage,
): JSONAPIResourceLinkage => {
if (isResourceLinkage(classInstance_s)) {
return classInstance_s;
}
const classInstance_s = classInstanceCandidate_s as I;

if (Array.isArray(classInstance_s)) {
if (!classInstance_s.every(isObject)) {
Expand Down
5 changes: 5 additions & 0 deletions src/serializers/serializeResourceObject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
JSONAPIResourceObject,
JSONObject,
} from '../types';
import { isRelationshipObject } from './utils/isRelationshipObject';

export const serializeResourceObject = <I extends object>(
classInstance: I,
Expand All @@ -43,6 +44,10 @@ export const serializeResourceObject = <I extends object>(
);
}

if (isRelationshipObject(relatedClassInstance_s)) {
return acc;
}

acc[key.toString()] = {
data: serializeResourceLinkage(relatedClassInstance_s),
};
Expand Down
3 changes: 2 additions & 1 deletion src/serializers/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ export * from './getMetadataBySymbol';
export * from './collect';
export * from './clean';
export * from './isObject';
export * from './isRelationshipObject';
export * from './isResourceIdentifierObject';
export * from './isResourceLinkage';
export * from './isResourceLinkage';
23 changes: 23 additions & 0 deletions src/serializers/utils/isRelationshipObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { isResourceLinkage } from './isResourceLinkage';

import type { JSONAPIRelationshipObject } from '../../types/relationshipObject';

export const isRelationshipObject = (
relationshipObjectCandidate: unknown,
): relationshipObjectCandidate is JSONAPIRelationshipObject => {
if (relationshipObjectCandidate === null) {
return false;
}

if (typeof relationshipObjectCandidate !== 'object') {
return false;
}

if (!('data' in relationshipObjectCandidate)) {
return false;
}

const { data } = relationshipObjectCandidate;

return isResourceLinkage(data);
};

0 comments on commit 113ea51

Please sign in to comment.