From 84414837365dc131ff3d9ae4af6a9d97f0a9a100 Mon Sep 17 00:00:00 2001 From: Carolina Rocha Floro Date: Fri, 7 Jun 2024 13:32:26 -0300 Subject: [PATCH 1/6] Created validation to avoid duplicated itens on supply table, slugifying both existing and new data before comparrison. Called the service on Post method on supply.service.ts. --- src/supply/supply.controller.ts | 10 ++++++++- src/supply/supply.service.ts | 37 +++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/supply/supply.controller.ts b/src/supply/supply.controller.ts index 55133484..87166fef 100644 --- a/src/supply/supply.controller.ts +++ b/src/supply/supply.controller.ts @@ -33,9 +33,17 @@ export class SupplyController { @Post('') async store(@Body() body) { - try { + try { + + const isDuplicate = await this.supplyServices.isDuplicate(body); + + if (isDuplicate) { + return new ServerResponse(400, 'This supply already exists') + } + const data = await this.supplyServices.store(body); return new ServerResponse(200, 'Successfully created supply', data); + } catch (err: any) { this.logger.error(`Failed to create supply: ${err}`); throw new HttpException(err?.code ?? err?.name ?? `${err}`, 400); diff --git a/src/supply/supply.service.ts b/src/supply/supply.service.ts index a3caf57e..efc40ee7 100644 --- a/src/supply/supply.service.ts +++ b/src/supply/supply.service.ts @@ -10,6 +10,7 @@ export class SupplyService { async store(body: z.infer) { const payload = CreateSupplySchema.parse(body); + return await this.prismaService.supply.create({ data: { ...payload, @@ -53,4 +54,40 @@ export class SupplyService { return data; } + + async isDuplicate(body: z.infer):Promise { + + function slugify(name: string | void):string { + + const slugName = String(name).normalize('NFKD').replace(/[\u0300-\u036f]/g, '').trim().toLowerCase() + .replace(/[^a-z0-9 -]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-') + return slugName + } + + const existingData = await this.prismaService.supply.findFirst({ + select: { + id: false, + name: true, + supplyCategory: { + select: { + id: true, + name: false, + }, + }, + createdAt: false, + updatedAt: false, + }, + }); + const existingDataName = slugify(existingData?.name) + + const payload = CreateSupplySchema.parse(body); + const payloadName = slugify(payload.name) + + if (existingDataName === payloadName + && payload.supplyCategoryId === existingData?.supplyCategory.id) { + return true + } + + return false; + } } From 00a6aa1cb49a6a8ebd4d0b5434700af843aa7a17 Mon Sep 17 00:00:00 2001 From: Carolina Rocha Floro Date: Fri, 7 Jun 2024 14:09:47 -0300 Subject: [PATCH 2/6] function slugify moved to utils --- src/supply/supply.service.ts | 15 +++------------ src/utils/utils.ts | 8 ++++++++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/supply/supply.service.ts b/src/supply/supply.service.ts index efc40ee7..b905eea0 100644 --- a/src/supply/supply.service.ts +++ b/src/supply/supply.service.ts @@ -4,6 +4,8 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { CreateSupplySchema, UpdateSupplySchema } from './types'; +import { slugify } from '@/utils/utils'; + @Injectable() export class SupplyService { constructor(private readonly prismaService: PrismaService) {} @@ -56,26 +58,14 @@ export class SupplyService { } async isDuplicate(body: z.infer):Promise { - - function slugify(name: string | void):string { - - const slugName = String(name).normalize('NFKD').replace(/[\u0300-\u036f]/g, '').trim().toLowerCase() - .replace(/[^a-z0-9 -]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-') - return slugName - } - const existingData = await this.prismaService.supply.findFirst({ select: { - id: false, name: true, supplyCategory: { select: { id: true, - name: false, }, }, - createdAt: false, - updatedAt: false, }, }); const existingDataName = slugify(existingData?.name) @@ -90,4 +80,5 @@ export class SupplyService { return false; } + } diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 378e9914..c4b1ae03 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -117,6 +117,13 @@ function removeEmptyStrings(obj): T { ) as T; } +function slugify(str:string | void):string { + + const slugfied = String(str).normalize('NFKD').replace(/[\u0300-\u036f]/g, '').trim().toLowerCase() + .replace(/[^a-z0-9 -]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-') + return slugfied + +} export { ServerResponse, calculateGeolocationBounds, @@ -125,4 +132,5 @@ export { getSessionData, removeNotNumbers, removeEmptyStrings, + slugify }; From 5591e15bf65358f5f97379cf4641e9d0e449213d Mon Sep 17 00:00:00 2001 From: Carolina Rocha Floro Date: Fri, 7 Jun 2024 14:15:13 -0300 Subject: [PATCH 3/6] Created test for slugify function --- src/utils/utils.spec.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 src/utils/utils.spec.ts diff --git a/src/utils/utils.spec.ts b/src/utils/utils.spec.ts new file mode 100644 index 00000000..1a1169ba --- /dev/null +++ b/src/utils/utils.spec.ts @@ -0,0 +1,20 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { slugify } from './utils'; + +describe('slugify function', () => { + + it('should handle accented characters', () => { + const result = slugify('tést'); + expect(result).toBe('test'); + }); + + it('should handle uppercase characters', () => { + const result = slugify('Test'); + expect(result).toBe('test'); + }); + + it('should handle double spaces', () => { + const result = slugify('test test'); + expect(result).toBe('test-test'); + }); +}); From 714a3b9b5af125d92f101a71c9c42e47271db85a Mon Sep 17 00:00:00 2001 From: Carolina Rocha Floro Date: Fri, 7 Jun 2024 14:15:30 -0300 Subject: [PATCH 4/6] Created test fo idDuplicate service --- src/supply/supply.service.spec.ts | 32 +++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/src/supply/supply.service.spec.ts b/src/supply/supply.service.spec.ts index 7868d160..d59380e5 100644 --- a/src/supply/supply.service.spec.ts +++ b/src/supply/supply.service.spec.ts @@ -4,22 +4,38 @@ import { PrismaService } from 'src/prisma/prisma.service'; describe('SupplyService', () => { let service: SupplyService; + let prisma: PrismaService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [SupplyService], - }) - .useMocker((token) => { - if (token === PrismaService) { - return {}; - } - }) - .compile(); + providers: [SupplyService, PrismaService], + }).compile(); service = module.get(SupplyService); + prisma = module.get(PrismaService); }); it('should be defined', () => { expect(service).toBeDefined(); }); + + it('should check for duplicates', async () => { + const body = { + name: 'Test', + supplyCategoryId: '1', + }; + + const mockSupply = { + name: 'Test', + supplyCategory: { + id: '1', + }, + } as any; + + jest.spyOn(prisma.supply, 'findFirst').mockResolvedValue(mockSupply); + + const result = await service.isDuplicate(body); + expect(result).toBe(true); + }); + }); From 7a75a3933dbcddab882f41e82e3efee068e76480 Mon Sep 17 00:00:00 2001 From: Carolina Rocha Floro Date: Fri, 7 Jun 2024 14:27:59 -0300 Subject: [PATCH 5/6] Fixed isDuplicateMethod --- src/supply-categories/supply-categories.service.ts | 6 ++++++ src/supply/supply.service.ts | 10 +++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/supply-categories/supply-categories.service.ts b/src/supply-categories/supply-categories.service.ts index 4ebd796a..629bb0fd 100644 --- a/src/supply-categories/supply-categories.service.ts +++ b/src/supply-categories/supply-categories.service.ts @@ -37,4 +37,10 @@ export class SupplyCategoriesService { async index() { return await this.prismaService.supplyCategory.findMany({}); } + + async isDuplicate(body: z.infer) : boolean { + + const existingData = return await this.prismaService.supplyCategory.findMany({}); + + } } diff --git a/src/supply/supply.service.ts b/src/supply/supply.service.ts index b905eea0..a65e6234 100644 --- a/src/supply/supply.service.ts +++ b/src/supply/supply.service.ts @@ -58,7 +58,14 @@ export class SupplyService { } async isDuplicate(body: z.infer):Promise { + + const payload = CreateSupplySchema.parse(body); + const payloadName = slugify(payload.name) + const existingData = await this.prismaService.supply.findFirst({ + where: { + name: payload.name + }, select: { name: true, supplyCategory: { @@ -70,9 +77,6 @@ export class SupplyService { }); const existingDataName = slugify(existingData?.name) - const payload = CreateSupplySchema.parse(body); - const payloadName = slugify(payload.name) - if (existingDataName === payloadName && payload.supplyCategoryId === existingData?.supplyCategory.id) { return true From 1cf397d07eee17f64ba18a62efa7520318565ed8 Mon Sep 17 00:00:00 2001 From: Carolina Rocha Floro Date: Fri, 7 Jun 2024 14:58:00 -0300 Subject: [PATCH 6/6] Created isDuplicate validation for supplyCategory and correspondent test. --- .../supply-categories.controller.ts | 7 +++++ .../supply-categories.service.spec.ts | 28 +++++++++++++------ .../supply-categories.service.ts | 22 +++++++++++++-- src/utils/utils.ts | 4 +-- 4 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/supply-categories/supply-categories.controller.ts b/src/supply-categories/supply-categories.controller.ts index 96106050..7c821f18 100644 --- a/src/supply-categories/supply-categories.controller.ts +++ b/src/supply-categories/supply-categories.controller.ts @@ -43,12 +43,19 @@ export class SupplyCategoriesController { @UseGuards(AdminGuard) async store(@Body() body) { try { + const isDuplicate = await this.supplyCategoryServices.isDuplicate(body); + + if (isDuplicate) { + return new ServerResponse(400, 'This category already exists') + } + const data = await this.supplyCategoryServices.store(body); return new ServerResponse( 200, 'Successfully created supply category', data, ); + } catch (err: any) { this.logger.error(`Failed to create supply category: ${err}`); throw new HttpException(err?.code ?? err?.name ?? `${err}`, 400); diff --git a/src/supply-categories/supply-categories.service.spec.ts b/src/supply-categories/supply-categories.service.spec.ts index 80f980c8..ba48f651 100644 --- a/src/supply-categories/supply-categories.service.spec.ts +++ b/src/supply-categories/supply-categories.service.spec.ts @@ -4,22 +4,34 @@ import { SupplyCategoriesService } from './supply-categories.service'; describe('SupplyCategoriesService', () => { let service: SupplyCategoriesService; + let prisma: PrismaService; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ - providers: [SupplyCategoriesService], - }) - .useMocker((token) => { - if (token === PrismaService) { - return {}; - } - }) - .compile(); + providers: [SupplyCategoriesService, PrismaService], + }).compile(); service = module.get(SupplyCategoriesService); + prisma = module.get(PrismaService); + prisma.supplyCategory.findFirst = jest.fn(); // Adicione esta linha }); it('should be defined', () => { expect(service).toBeDefined(); }); + + it('should check for duplicates', async () => { + const body = { + name: 'Test', + }; + + const mockSupply = { + name: 'Test' + } as any; + + jest.spyOn(prisma.supplyCategory, 'findFirst').mockResolvedValue(mockSupply); + + const result = await service.isDuplicate(body); + expect(result).toBe(true); + }); }); diff --git a/src/supply-categories/supply-categories.service.ts b/src/supply-categories/supply-categories.service.ts index 629bb0fd..abcebf06 100644 --- a/src/supply-categories/supply-categories.service.ts +++ b/src/supply-categories/supply-categories.service.ts @@ -7,6 +7,8 @@ import { UpdateSupplyCategorySchema, } from './types'; +import { slugify } from '@/utils/utils'; + @Injectable() export class SupplyCategoriesService { constructor(private readonly prismaService: PrismaService) {} @@ -38,9 +40,25 @@ export class SupplyCategoriesService { return await this.prismaService.supplyCategory.findMany({}); } - async isDuplicate(body: z.infer) : boolean { + async isDuplicate(body: z.infer) : Promise { + + const payload = CreateSupplyCategorySchema.parse(body); + const existingData = await this.prismaService.supplyCategory.findFirst({ + where: { + name: payload.name + }, + select: { + name: true, + }, + }); + + const payloadName = slugify(payload.name) + const existingDataName = slugify(existingData?.name) - const existingData = return await this.prismaService.supplyCategory.findMany({}); + if (payloadName === existingDataName) { + return true + } + return false } } diff --git a/src/utils/utils.ts b/src/utils/utils.ts index c4b1ae03..70a91791 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -119,9 +119,9 @@ function removeEmptyStrings(obj): T { function slugify(str:string | void):string { - const slugfied = String(str).normalize('NFKD').replace(/[\u0300-\u036f]/g, '').trim().toLowerCase() + const slugified = String(str).normalize('NFKD').replace(/[\u0300-\u036f]/g, '').trim().toLowerCase() .replace(/[^a-z0-9 -]/g, '').replace(/\s+/g, '-').replace(/-+/g, '-') - return slugfied + return slugified } export {