Skip to content

fix(json-api-nestjs): Fix validate for patch method #86

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

Merged
merged 10 commits into from
Apr 21, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,43 @@ describe('PATCH method:', () => {

await jsonSdk.jonApiSdkService.deleteOne(newCommentsAfterSave);
});

it('Should be update empty attributes with relations', async () => {
const newAddress = new Addresses();
newAddress.city = faker.___location.city();
newAddress.state = faker.___location.state();
newAddress.country = faker.___location.country();

const newComments = new Comments();
newComments.text = faker.string.alpha();
newComments.kind = CommentKind.Comment;

const newAddressAfterSave = await jsonSdk.jonApiSdkService.postOne(
newAddress
);

const newCommentsAfterSave = await jsonSdk.jonApiSdkService.postOne(
newComments
);

userAfterSave.addresses = newAddressAfterSave;
userAfterSave.comments = [newCommentsAfterSave];

const userWithEmptyAttr = new Users();
userWithEmptyAttr.id = userAfterSave.id;
userWithEmptyAttr.addresses = newAddressAfterSave;
userWithEmptyAttr.comments = [newCommentsAfterSave];

await jsonSdk.jonApiSdkService.patchOne(userWithEmptyAttr);
const userAfterUpdate = await jsonSdk.jonApiSdkService.getOne(
Users,
userWithEmptyAttr.id,
{ include: ['addresses', 'comments'] }
);
expect(userAfterUpdate.addresses).toEqual(newAddressAfterSave);
newCommentsAfterSave.updatedAt = userAfterUpdate.comments[0].updatedAt;
expect(userAfterUpdate.comments[0]).toEqual(newCommentsAfterSave);

await jsonSdk.jonApiSdkService.deleteOne(newCommentsAfterSave);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
QueryParamsForOneItem,
RelationBodyData,
ResourceObject,
ResourceObjectRelationships,
ReturnIfArray,
} from '../types';
import { EntityArray, getTypeForReq } from '../utils';
Expand Down Expand Up @@ -172,7 +171,7 @@ export class JsonApiSdkService {
data: {
id: entity[this.jsonApiSdkConfig.idKey].toString(),
type: getTypeForReq(entity.constructor.name),
attributes,
...(Object.keys(attributes).length > 0 ? { attributes } : {}),
...(Object.keys(relationships).length > 0 ? { relationships } : {}),
},
};
Expand Down
4 changes: 2 additions & 2 deletions libs/json-api/json-api-nestjs-sdk/src/lib/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ type MainData<E> = {
export type PostData<E> = {
data: MainData<E>;
};

export type PatchData<E> = {
data: { id: string } & MainData<E>;
data: { id: string } & Omit<MainData<E>, 'attributes'> &
Partial<Pick<MainData<E>, 'attributes'>>;
};
export type RelationData = { id: string; type: string };
export type RelationBodyData = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,9 +573,24 @@ describe('zod-helper', () => {
attributes,
},
};
const check3 = {
data: {
id: '1',
type: 'users',
relationships,
},
};

expect(zodInputPatchSchemaTest.parse(check)).toEqual(check);
expect(zodInputPatchSchemaTest.parse(check2)).toEqual(check2);
expect(zodInputPatchSchemaTest.parse(check3)).toEqual({
data: {
id: '1',
type: 'users',
relationships,
attributes: {},
},
});
});

it('should be not ok', () => {
Expand Down Expand Up @@ -608,7 +623,13 @@ describe('zod-helper', () => {
},
},
};
const arrayCheck = [check1, check2, check3];
const check4 = {
data: {
id: '1',
type: 'users',
},
};
const arrayCheck = [check1, check2, check3, check4];
expect.assertions(arrayCheck.length);
for (const item of arrayCheck) {
try {
Expand Down
31 changes: 27 additions & 4 deletions libs/json-api/json-api-nestjs/src/lib/helper/zod/zod-helper.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Repository } from 'typeorm';
import { z, ZodObject } from 'zod';
import { z, ZodObject, ZodUnion } from 'zod';
import { QueryField } from 'json-shared-type';

import {
Expand Down Expand Up @@ -45,6 +45,7 @@ import {

import {
PatchShape,
PatchShapeDefault,
zodPatchRelationshipsSchema,
} from './zod-input-patch-schema';

Expand Down Expand Up @@ -93,7 +94,12 @@ export type PostData<E extends Entity> = z.infer<ZodInputPostSchema<E>>['data'];

export type ZodInputPatchSchema<E extends Entity> = ZodObject<
{
data: ZodObject<PatchShape<E>, 'strict'>;
data: ZodUnion<
[
ZodObject<PatchShapeDefault<E>, 'strict'>,
ZodObject<PatchShape<E>, 'strict'>
]
>;
},
'strict'
>;
Expand Down Expand Up @@ -236,7 +242,8 @@ export const zodInputPatchSchema = <E extends Entity>(
{} as FieldWithType<E>
);
const typeName = camelToKebab(getEntityName(repository.target));
const postShape: PatchShape<E> = {

const patchShapeDefault: PatchShapeDefault<E> = {
id: zodIdSchema(primaryType),
type: zodTypeSchema(typeName),
attributes: zodAttributesSchema(fieldWithType),
Expand All @@ -247,9 +254,25 @@ export const zodInputPatchSchema = <E extends Entity>(
).optional(),
};

const patchShape: PatchShape<E> = {
id: zodIdSchema(primaryType),
type: zodTypeSchema(typeName),
attributes: zodAttributesSchema(fieldWithType)
.optional()
.default({} as any),
relationships: zodPatchRelationshipsSchema(
relationArrayProps,
relationPopsName,
primaryColumnType
),
};

return z
.object({
data: z.object(postShape).strict(),
data: z.union([
z.object(patchShapeDefault).strict(),
z.object(patchShape).strict(),
]),
})
.strict();
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,20 @@ import { ZodAttributesSchema } from '../zod-input-post-schema/attributes';
import { ZodTypeSchema } from '../zod-input-post-schema/type';
import { ZodIdSchema } from '../zod-input-post-schema/id';

import { ZodObject, ZodOptional } from 'zod';
import { ZodDefault, ZodObject, ZodOptional } from 'zod';
import {
ZodPatchRelationshipsSchema,
zodPatchRelationshipsSchema,
} from './relationships';

export type PatchShape<E extends Entity> = {
id: ZodIdSchema;
type: ZodTypeSchema<string>;
attributes: ZodDefault<ZodOptional<ZodAttributesSchema<E>>>;
relationships: ZodPatchRelationshipsSchema<E>;
};

export type PatchShapeDefault<E extends Entity> = {
id: ZodIdSchema;
type: ZodTypeSchema<string>;
attributes: ZodAttributesSchema<E>;
Expand Down