From c523571e1bd9ebb3e23901d930349a41ddc932d9 Mon Sep 17 00:00:00 2001 From: Ryan Huellen Date: Sun, 8 Dec 2024 19:56:56 -0600 Subject: [PATCH 1/2] fix: checking if the relationship is already a relationship object prior to serialization --- .../serializeResourceLinkage.test.ts | 13 ----- .../serializeResourceObject.test.ts | 49 ++++++++++++++++++- .../utils/isRelationshipObject.test.ts | 35 +++++++++++++ src/serializers/serializeResourceLinkage.ts | 7 +-- src/serializers/serializeResourceObject.ts | 5 ++ src/serializers/utils/isRelationshipObject.ts | 23 +++++++++ 6 files changed, 112 insertions(+), 20 deletions(-) create mode 100644 __tests__/serializers/utils/isRelationshipObject.test.ts create mode 100644 src/serializers/utils/isRelationshipObject.ts diff --git a/__tests__/serializers/serializeResourceLinkage.test.ts b/__tests__/serializers/serializeResourceLinkage.test.ts index efe6f47..e86ecec 100644 --- a/__tests__/serializers/serializeResourceLinkage.test.ts +++ b/__tests__/serializers/serializeResourceLinkage.test.ts @@ -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]; diff --git a/__tests__/serializers/serializeResourceObject.test.ts b/__tests__/serializers/serializeResourceObject.test.ts index 1fb7444..2eb49cd 100644 --- a/__tests__/serializers/serializeResourceObject.test.ts +++ b/__tests__/serializers/serializeResourceObject.test.ts @@ -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( @@ -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(); @@ -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) => { diff --git a/__tests__/serializers/utils/isRelationshipObject.test.ts b/__tests__/serializers/utils/isRelationshipObject.test.ts new file mode 100644 index 0000000..ce0864a --- /dev/null +++ b/__tests__/serializers/utils/isRelationshipObject.test.ts @@ -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); + }); +}); diff --git a/src/serializers/serializeResourceLinkage.ts b/src/serializers/serializeResourceLinkage.ts index 9776233..671b6f6 100644 --- a/src/serializers/serializeResourceLinkage.ts +++ b/src/serializers/serializeResourceLinkage.ts @@ -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 = ( - 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)) { diff --git a/src/serializers/serializeResourceObject.ts b/src/serializers/serializeResourceObject.ts index ed034d2..48bf413 100644 --- a/src/serializers/serializeResourceObject.ts +++ b/src/serializers/serializeResourceObject.ts @@ -17,6 +17,7 @@ import type { JSONAPIResourceObject, JSONObject, } from '../types'; +import { isRelationshipObject } from './utils/isRelationshipObject'; export const serializeResourceObject = ( classInstance: I, @@ -43,6 +44,10 @@ export const serializeResourceObject = ( ); } + if (isRelationshipObject(relatedClassInstance_s)) { + return acc; + } + acc[key.toString()] = { data: serializeResourceLinkage(relatedClassInstance_s), }; diff --git a/src/serializers/utils/isRelationshipObject.ts b/src/serializers/utils/isRelationshipObject.ts new file mode 100644 index 0000000..89b78f8 --- /dev/null +++ b/src/serializers/utils/isRelationshipObject.ts @@ -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); +}; From a3fc678d429fc7b8f8af090a56f05e7d9fbb1751 Mon Sep 17 00:00:00 2001 From: Ryan Huellen Date: Sun, 8 Dec 2024 19:58:19 -0600 Subject: [PATCH 2/2] fix: exporting `isRelationshipObject` --- src/serializers/utils/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/serializers/utils/index.ts b/src/serializers/utils/index.ts index 67ffb17..c46674a 100644 --- a/src/serializers/utils/index.ts +++ b/src/serializers/utils/index.ts @@ -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'; \ No newline at end of file