From fcd33aa9b31f3181cf6d81cf3035629552158723 Mon Sep 17 00:00:00 2001 From: Alejandro Peralta Date: Fri, 13 Dec 2024 15:54:23 +0100 Subject: [PATCH] feat(api): Enable partialProjectName search for custom-projects --- .../custom-projects.service.ts | 15 +++++++-- .../custom-projects-read.spec.ts | 33 +++++++++++++++++++ shared/contracts/custom-projects.contract.ts | 9 +++-- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/api/src/modules/custom-projects/custom-projects.service.ts b/api/src/modules/custom-projects/custom-projects.service.ts index e68daf65..e900a875 100644 --- a/api/src/modules/custom-projects/custom-projects.service.ts +++ b/api/src/modules/custom-projects/custom-projects.service.ts @@ -20,7 +20,12 @@ import { EventBus } from '@nestjs/cqrs'; import { SaveCustomProjectEvent } from '@api/modules/custom-projects/events/save-custom-project.event'; import { FetchSpecification } from 'nestjs-base-service'; import { GetActivityTypesDefaults } from '@shared/dtos/custom-projects/get-activity-types-defaults.dto'; -import { Country } from '@shared/entities/country.entity'; +import { z } from 'zod'; +import { customProjecsQuerySchema } from '@shared/contracts/custom-projects.contract'; + +export type CustomProjectFetchSpecificacion = z.infer< + typeof customProjecsQuerySchema +>; @Injectable() export class CustomProjectsService extends AppBaseService< @@ -121,7 +126,7 @@ export class CustomProjectsService extends AppBaseService< async extendFindAllQuery( query: SelectQueryBuilder, - fetchSpecification: FetchSpecification, + fetchSpecification: CustomProjectFetchSpecificacion, info?: { user: User }, ): Promise> { const { user } = info; @@ -130,6 +135,12 @@ export class CustomProjectsService extends AppBaseService< .leftJoinAndSelect('customProject.user', 'user') .andWhere('user.id = :userId', { userId: user.id }); + if (fetchSpecification.partialProjectName) { + query = query.andWhere('project_name ILIKE :projectName', { + projectName: `%${fetchSpecification.partialProjectName}%`, + }); + } + return query; } } diff --git a/api/test/integration/custom-projects/custom-projects-read.spec.ts b/api/test/integration/custom-projects/custom-projects-read.spec.ts index 194fd98f..b599e401 100644 --- a/api/test/integration/custom-projects/custom-projects-read.spec.ts +++ b/api/test/integration/custom-projects/custom-projects-read.spec.ts @@ -116,5 +116,38 @@ describe('Read Custom projects', () => { responseData.find((cp: CustomProject) => cp.id === customProject2.id), ).toBeDefined(); }); + + test('An authenticated user should be able to retrieve its custom projects filtering by partialProjectName', async () => { + // Given + const { createCustomProject } = testManager.mocks(); + const [customProject1, customProject2] = await Promise.all([ + createCustomProject({ + id: '2d8fdf38-3295-4970-b194-af503a2a6031', + projectName: 'Should not be found', + user: { id: user.id } as User, + }), + createCustomProject({ + id: '2d8fdf38-3295-4970-b194-af503a2a6039', + projectName: 'Seagrass', + user: { id: user.id } as User, + }), + ]); + + // When + const response = await testManager + .request() + .get(customProjectContract.getCustomProjects.path) + .set('Authorization', `Bearer ${jwtToken}`) + .query({ partialProjectName: 'Sea' }) + .send(); + + // Then + expect(response.status).toBe(200); + const responseData = response.body.data; + expect(responseData.length).toBe(1); + expect( + responseData.find((cp: CustomProject) => cp.id === customProject2.id), + ).toBeDefined(); + }); }); }); diff --git a/shared/contracts/custom-projects.contract.ts b/shared/contracts/custom-projects.contract.ts index 482158cd..6cc56d43 100644 --- a/shared/contracts/custom-projects.contract.ts +++ b/shared/contracts/custom-projects.contract.ts @@ -18,8 +18,13 @@ import { generateEntityQuerySchema } from "@shared/schemas/query-param.schema"; import { ActivityTypesDefaults } from "@shared/dtos/custom-projects/activity-types-defaults"; import { GetActivityTypesDefaultsSchema } from "@shared/schemas/custom-projects/activity-types-defaults.schema"; -export const customProjecsQuerySchema = - generateEntityQuerySchema(CustomProject); +export const customProjecsQuerySchema = generateEntityQuerySchema( + CustomProject, +).merge( + z.object({ + partialProjectName: z.string().optional(), + }), +); const contract = initContract(); export const customProjectContract = contract.router({