Skip to content

Commit

Permalink
fix: serializing resource linkage returns itself if already a linkage (
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanhaticus authored Dec 7, 2024
1 parent 4a6343b commit c10507d
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 2 deletions.
13 changes: 13 additions & 0 deletions __tests__/serializers/serializeResourceLinkage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,19 @@ 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
80 changes: 80 additions & 0 deletions __tests__/serializers/utils/isResourceIdentifierObject.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { isResourceIdentifierObject } from '../../../src/serializers/utils/isResourceIdentifierObject';

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

expect(isResourceIdentifierObject(resourceIdentifierObjectCandidate)).toBe(
false,
);
});

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

expect(isResourceIdentifierObject(resourceIdentifierObjectCandidate)).toBe(
false,
);
});

it('it should return `false` if `resourceIdentifierObjectCandidate` is an array', () => {
const resourceIdentifierObjectCandidate: unknown[] = [];

expect(isResourceIdentifierObject(resourceIdentifierObjectCandidate)).toBe(
false,
);
});

it('it should return `false` if `resourceIdentifierObjectCandidate` does not have a `type` property', () => {
const resourceIdentifierObjectCandidate = {
id: '1',
};

expect(isResourceIdentifierObject(resourceIdentifierObjectCandidate)).toBe(
false,
);
});

it('it should return `false` if `resourceIdentifierObjectCandidate.type` is not a string', () => {
const resourceIdentifierObjectCandidate = {
type: 1,
id: '1',
};

expect(isResourceIdentifierObject(resourceIdentifierObjectCandidate)).toBe(
false,
);
});

it('it should return `false` if `resourceIdentifierObjectCandidate` does not have an `id` property', () => {
const resourceIdentifierObjectCandidate = {
type: 'test',
};

expect(isResourceIdentifierObject(resourceIdentifierObjectCandidate)).toBe(
false,
);
});

it('it should return `false` if `resourceIdentifierObjectCandidate.id` is not a string', () => {
const resourceIdentifierObjectCandidate = {
type: 'test',
id: 1,
};

expect(isResourceIdentifierObject(resourceIdentifierObjectCandidate)).toBe(
false,
);
});

it('it should return `true` if `resourceIdentifierObjectCandidate` is a valid JSON:API Resource Identifier Object', () => {
const resourceIdentifierObjectCandidate = {
type: 'test',
id: '1',
};

expect(isResourceIdentifierObject(resourceIdentifierObjectCandidate)).toBe(
true,
);
});
});
33 changes: 33 additions & 0 deletions __tests__/serializers/utils/isResourceLinkage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { isResourceLinkage } from '../../../src/serializers/utils/isResourceLinkage';

import { isResourceIdentifierObject } from '../../../src/serializers/utils/isResourceIdentifierObject';

jest.mock('../../../src/serializers/utils/isResourceIdentifierObject');

const isResourceIdentifierObjectMocked = jest.mocked(
isResourceIdentifierObject,
);

describe('`isResourceLinkage`', () => {
it('should return true if `resourceLinkageCandidate` is `null`', () => {
const resourceLinkageCandidate = null;

expect(isResourceLinkage(resourceLinkageCandidate)).toBe(true);
});

it('should return true if `resourceLinkageCandidate` is an array of `JSONAPIResourceIdentifierObject`', () => {
isResourceIdentifierObjectMocked.mockReturnValue(true);

const resourceLinkageCandidate = ['foo', 'bar'];

expect(isResourceLinkage(resourceLinkageCandidate)).toBe(true);
});

it('should return true if `resourceLinkageCandidate` is a `JSONAPIResourceIdentifierObject`', () => {
isResourceIdentifierObjectMocked.mockReturnValue(true);

const resourceLinkageCandidate = 'foo';

expect(isResourceLinkage(resourceLinkageCandidate)).toBe(true);
});
});
9 changes: 7 additions & 2 deletions src/serializers/serializeResourceLinkage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ 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,
): Exclude<JSONAPIResourceLinkage, null> => {
classInstance_s: I | JSONAPIResourceLinkage,
): JSONAPIResourceLinkage => {
if (isResourceLinkage(classInstance_s)) {
return classInstance_s;
}

if (Array.isArray(classInstance_s)) {
if (!classInstance_s.every(isObject)) {
throw new Error(
Expand Down
34 changes: 34 additions & 0 deletions src/serializers/utils/isResourceIdentifierObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { JSONAPIResourceIdentifierObject } from '../../types/resourceIdentifierObject';

export const isResourceIdentifierObject = (
resourceIdentifierObjectCandidate: unknown,
): resourceIdentifierObjectCandidate is JSONAPIResourceIdentifierObject => {
if (resourceIdentifierObjectCandidate === null) {
return false;
}

if (
typeof resourceIdentifierObjectCandidate !== 'object' ||
Array.isArray(resourceIdentifierObjectCandidate)
) {
return false;
}

if ('type' in resourceIdentifierObjectCandidate === false) {
return false;
}

if (typeof resourceIdentifierObjectCandidate.type !== 'string') {
return false;
}

if ('id' in resourceIdentifierObjectCandidate === false) {
return false;
}

if (typeof resourceIdentifierObjectCandidate.id !== 'string') {
return false;
}

return true;
};
17 changes: 17 additions & 0 deletions src/serializers/utils/isResourceLinkage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { isResourceIdentifierObject } from './isResourceIdentifierObject';

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

export const isResourceLinkage = (
resourceLinkageCandidate: unknown,
): resourceLinkageCandidate is JSONAPIResourceLinkage => {
if (resourceLinkageCandidate === null) {
return true;
}

if (Array.isArray(resourceLinkageCandidate)) {
return resourceLinkageCandidate.every(isResourceIdentifierObject);
}

return isResourceIdentifierObject(resourceLinkageCandidate);
};

0 comments on commit c10507d

Please sign in to comment.