From 6f28db8719166129bdc10fa0ee0d94301ce1225b Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Mon, 15 Jul 2024 23:40:43 -0300 Subject: [PATCH 1/5] chore: move save to service --- .../cache-crud.controller.e2e-spec.ts | 19 ++++++++- .../src/controllers/cache-crud.controller.ts | 40 +++---------------- .../src/interfaces/cache-service.interface.ts | 9 +++++ .../src/services/cache.service.ts | 13 ++++++ 4 files changed, 45 insertions(+), 36 deletions(-) diff --git a/packages/nestjs-cache/src/controllers/cache-crud.controller.e2e-spec.ts b/packages/nestjs-cache/src/controllers/cache-crud.controller.e2e-spec.ts index 3673bd788..e140ed213 100644 --- a/packages/nestjs-cache/src/controllers/cache-crud.controller.e2e-spec.ts +++ b/packages/nestjs-cache/src/controllers/cache-crud.controller.e2e-spec.ts @@ -111,7 +111,7 @@ describe('CacheAssignmentController (e2e)', () => { }); }); - it('POST /cache/user', async () => { + it('POST /cache/user creating user with success', async () => { const payload: CacheCreatableInterface = { key: 'dashboard-1', type: 'filter', @@ -265,6 +265,23 @@ describe('CacheAssignmentController (e2e)', () => { expect(res.body.data).toBe(payload.data); expect(res.body.assignee.id).toBe(user.id); }); + + const url = + `/cache/user/` + + `?filter[0]=key||$eq||${payload.key}` + + `&filter[1]=type||$eq||${payload.type}` + + `&filter[2]=assignee.id||$eq||${payload.assignee.id}`; + // Assuming your endpoint can filter by key and type + await supertest(app.getHttpServer()) + .get(url) + .expect(200) + .then((res) => { + const response = res.body[0]; + assert.strictEqual(response.assignee.id, user.id); + assert.strictEqual(response.key, payload.key); + assert.strictEqual(response.type, payload.type); + assert.strictEqual(response.data, payload.data); + }); }); it('DELETE /cache/user/:id', async () => { diff --git a/packages/nestjs-cache/src/controllers/cache-crud.controller.ts b/packages/nestjs-cache/src/controllers/cache-crud.controller.ts index 31df075cf..4735b3100 100644 --- a/packages/nestjs-cache/src/controllers/cache-crud.controller.ts +++ b/packages/nestjs-cache/src/controllers/cache-crud.controller.ts @@ -115,50 +115,20 @@ export class CacheCrudController /** * Create one * - * @param crudRequest - the CRUD request object + * @param _crudRequest - the CRUD request object * @param cacheCreateDto - cache create dto * @param assignment - The cache assignment */ @CrudCreateOne() @AccessControlCreateOne(CacheResource.One) async createOne( - @CrudRequest() crudRequest: CrudRequestInterface, + // TODO: this is not being used anymore + @CrudRequest() _crudRequest: CrudRequestInterface, @CrudBody() cacheCreateDto: CacheCreateDto, @Param('assignment') assignment: ReferenceAssignment, ) { - const expirationDate = getExpirationDate( - cacheCreateDto.expiresIn ?? this.settings.expiresIn, - ); - - const existingCache = await this.cacheService.get( - assignment, - cacheCreateDto, - ); - - // update or create - if (existingCache) { - crudRequest.parsed.search.$and?.push({ - id: { - $eq: existingCache.id, - }, - }); - // call crud service to create - const response = await this.getCrudService(assignment).updateOne( - crudRequest, - { - id: existingCache.id, - ...cacheCreateDto, - expirationDate, - }, - ); - return response; - } else { - // call crud service to create - return this.getCrudService(assignment).createOne(crudRequest, { - ...cacheCreateDto, - expirationDate, - }); - } + const response = await this.cacheService.save(assignment, cacheCreateDto); + return response; } /** diff --git a/packages/nestjs-cache/src/interfaces/cache-service.interface.ts b/packages/nestjs-cache/src/interfaces/cache-service.interface.ts index 00eb3407b..3d47a4b8d 100644 --- a/packages/nestjs-cache/src/interfaces/cache-service.interface.ts +++ b/packages/nestjs-cache/src/interfaces/cache-service.interface.ts @@ -2,6 +2,7 @@ import { ReferenceAssignment } from '@concepta/ts-core'; import { QueryOptionsInterface } from '@concepta/typeorm-common'; import { CacheClearInterface, + CacheCreatableInterface, CacheCreateInterface, CacheDeleteInterface, CacheGetOneInterface, @@ -15,6 +16,14 @@ export interface CacheServiceInterface CacheUpdateInterface, CacheGetOneInterface, CacheClearInterface { + + // TODO: should i create a unique interface for save? + save( + assignment: ReferenceAssignment, + cache: CacheCreatableInterface, + options?: QueryOptionsInterface, + ): Promise; + getAssignedCaches( assignment: ReferenceAssignment, cache: Pick, diff --git a/packages/nestjs-cache/src/services/cache.service.ts b/packages/nestjs-cache/src/services/cache.service.ts index ac10b6172..b01aced54 100644 --- a/packages/nestjs-cache/src/services/cache.service.ts +++ b/packages/nestjs-cache/src/services/cache.service.ts @@ -32,6 +32,19 @@ export class CacheService implements CacheServiceInterface { protected readonly settings: CacheSettingsInterface, ) {} + async save( + assignment: ReferenceAssignment, + cache: CacheCreateDto, + queryOptions?: QueryOptionsInterface, + ): Promise { + const existingCache = await this.get(assignment, cache, queryOptions); + if (existingCache) { + return await this.update(assignment, cache, queryOptions); + } else { + return await this.create(assignment, cache, queryOptions); + } + } + /** * Create a cache with a for the given assignee. * From 31d66b7b1348c971b687c45e1c7084d82b0afd16 Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Mon, 15 Jul 2024 23:48:18 -0300 Subject: [PATCH 2/5] chore: lint --- packages/nestjs-cache/src/interfaces/cache-service.interface.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/nestjs-cache/src/interfaces/cache-service.interface.ts b/packages/nestjs-cache/src/interfaces/cache-service.interface.ts index 3d47a4b8d..a989a4507 100644 --- a/packages/nestjs-cache/src/interfaces/cache-service.interface.ts +++ b/packages/nestjs-cache/src/interfaces/cache-service.interface.ts @@ -16,7 +16,6 @@ export interface CacheServiceInterface CacheUpdateInterface, CacheGetOneInterface, CacheClearInterface { - // TODO: should i create a unique interface for save? save( assignment: ReferenceAssignment, From f704712caa5ead93bc95effcdf805d3f4601491a Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Tue, 16 Jul 2024 17:27:18 -0300 Subject: [PATCH 3/5] chore: update method name --- .../src/controllers/cache-crud.controller.ts | 3 +-- .../src/interfaces/cache-service.interface.ts | 3 +-- .../src/services/cache.service.ts | 26 +++++++++---------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/packages/nestjs-cache/src/controllers/cache-crud.controller.ts b/packages/nestjs-cache/src/controllers/cache-crud.controller.ts index 4735b3100..174f534c9 100644 --- a/packages/nestjs-cache/src/controllers/cache-crud.controller.ts +++ b/packages/nestjs-cache/src/controllers/cache-crud.controller.ts @@ -122,12 +122,11 @@ export class CacheCrudController @CrudCreateOne() @AccessControlCreateOne(CacheResource.One) async createOne( - // TODO: this is not being used anymore @CrudRequest() _crudRequest: CrudRequestInterface, @CrudBody() cacheCreateDto: CacheCreateDto, @Param('assignment') assignment: ReferenceAssignment, ) { - const response = await this.cacheService.save(assignment, cacheCreateDto); + const response = await this.cacheService.updateOrCreate(assignment, cacheCreateDto); return response; } diff --git a/packages/nestjs-cache/src/interfaces/cache-service.interface.ts b/packages/nestjs-cache/src/interfaces/cache-service.interface.ts index a989a4507..372928903 100644 --- a/packages/nestjs-cache/src/interfaces/cache-service.interface.ts +++ b/packages/nestjs-cache/src/interfaces/cache-service.interface.ts @@ -16,8 +16,7 @@ export interface CacheServiceInterface CacheUpdateInterface, CacheGetOneInterface, CacheClearInterface { - // TODO: should i create a unique interface for save? - save( + updateOrCreate( assignment: ReferenceAssignment, cache: CacheCreatableInterface, options?: QueryOptionsInterface, diff --git a/packages/nestjs-cache/src/services/cache.service.ts b/packages/nestjs-cache/src/services/cache.service.ts index b01aced54..481b3b881 100644 --- a/packages/nestjs-cache/src/services/cache.service.ts +++ b/packages/nestjs-cache/src/services/cache.service.ts @@ -32,19 +32,6 @@ export class CacheService implements CacheServiceInterface { protected readonly settings: CacheSettingsInterface, ) {} - async save( - assignment: ReferenceAssignment, - cache: CacheCreateDto, - queryOptions?: QueryOptionsInterface, - ): Promise { - const existingCache = await this.get(assignment, cache, queryOptions); - if (existingCache) { - return await this.update(assignment, cache, queryOptions); - } else { - return await this.create(assignment, cache, queryOptions); - } - } - /** * Create a cache with a for the given assignee. * @@ -252,6 +239,19 @@ export class CacheService implements CacheServiceInterface { } } + async updateOrCreate( + assignment: ReferenceAssignment, + cache: CacheCreateDto, + queryOptions?: QueryOptionsInterface, + ): Promise { + const existingCache = await this.get(assignment, cache, queryOptions); + if (existingCache) { + return await this.update(assignment, cache, queryOptions); + } else { + return await this.create(assignment, cache, queryOptions); + } + } + // Should this be on nestjs-common? protected async validateDto>( type: Type, From 1ed750b9691a6c52149538e56f39072d4b9ae0cb Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Tue, 16 Jul 2024 17:28:30 -0300 Subject: [PATCH 4/5] chore: lint --- .../nestjs-cache/src/controllers/cache-crud.controller.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/nestjs-cache/src/controllers/cache-crud.controller.ts b/packages/nestjs-cache/src/controllers/cache-crud.controller.ts index 174f534c9..3947f708f 100644 --- a/packages/nestjs-cache/src/controllers/cache-crud.controller.ts +++ b/packages/nestjs-cache/src/controllers/cache-crud.controller.ts @@ -126,7 +126,10 @@ export class CacheCrudController @CrudBody() cacheCreateDto: CacheCreateDto, @Param('assignment') assignment: ReferenceAssignment, ) { - const response = await this.cacheService.updateOrCreate(assignment, cacheCreateDto); + const response = await this.cacheService.updateOrCreate( + assignment, + cacheCreateDto, + ); return response; } From 2e357c53d76325c160e96ec1bb96f1442f2a1300 Mon Sep 17 00:00:00 2001 From: Thiago Ramalho Date: Tue, 23 Jul 2024 18:37:37 -0300 Subject: [PATCH 5/5] chore: update cache replace --- .../cache-crud.controller.e2e-spec.ts | 48 ++++++++++++++ .../src/controllers/cache-crud.controller.ts | 65 ++++++++++++++++--- 2 files changed, 103 insertions(+), 10 deletions(-) diff --git a/packages/nestjs-cache/src/controllers/cache-crud.controller.e2e-spec.ts b/packages/nestjs-cache/src/controllers/cache-crud.controller.e2e-spec.ts index e140ed213..046fa813d 100644 --- a/packages/nestjs-cache/src/controllers/cache-crud.controller.e2e-spec.ts +++ b/packages/nestjs-cache/src/controllers/cache-crud.controller.e2e-spec.ts @@ -284,6 +284,54 @@ describe('CacheAssignmentController (e2e)', () => { }); }); + it.only('PUT /cache/user replace', async () => { + const payload: CacheCreatableInterface = { + key: 'dashboard-1', + type: 'filter', + data: '{}', + expiresIn: '1d', + assignee: { id: user.id }, + }; + let cacheId = ''; + await supertest(app.getHttpServer()) + .put('/cache/user') + .send(payload) + .expect(200) + .then((res) => { + cacheId = res.body.id; + expect(res.body.key).toBe(payload.key); + expect(res.body.assignee.id).toBe(user.id); + }); + payload.data = '{ "name": "John Doe" }'; + payload.expiresIn = null; + await supertest(app.getHttpServer()) + .put(`/cache/user/${cacheId}`) + .send(payload) + .expect(200) + .then((res) => { + expect(res.body.key).toBe(payload.key); + expect(res.body.data).toBe(payload.data); + expect(res.body.assignee.id).toBe(user.id); + }); + + const url = + `/cache/user/` + + `?filter[0]=key||$eq||${payload.key}` + + `&filter[1]=type||$eq||${payload.type}` + + `&filter[2]=assignee.id||$eq||${payload.assignee.id}`; + // Assuming your endpoint can filter by key and type + await supertest(app.getHttpServer()) + .get(url) + .expect(200) + .then((res) => { + const response = res.body[0]; + assert.strictEqual(response.assignee.id, user.id); + assert.strictEqual(response.key, payload.key); + assert.strictEqual(response.type, payload.type); + assert.strictEqual(response.data, payload.data); + }); + }); + it('DELETE /cache/user/:id', async () => { const userCache = await userCacheFactory .map((userCache) => { diff --git a/packages/nestjs-cache/src/controllers/cache-crud.controller.ts b/packages/nestjs-cache/src/controllers/cache-crud.controller.ts index 3947f708f..37a81ea20 100644 --- a/packages/nestjs-cache/src/controllers/cache-crud.controller.ts +++ b/packages/nestjs-cache/src/controllers/cache-crud.controller.ts @@ -1,5 +1,5 @@ -import { Inject, Param } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { Inject, Param, Put } from '@nestjs/common'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; import { AccessControlCreateOne, AccessControlDeleteOne, @@ -14,6 +14,7 @@ import { CrudDeleteOne, CrudReadMany, CrudReadOne, + CrudReplaceOne, CrudRequest, CrudRequestInterface, CrudUpdateOne, @@ -64,7 +65,7 @@ export class CacheCrudController CacheInterface, CacheCreatableInterface, CacheUpdatableInterface, - never + CacheCreatableInterface > { /** @@ -115,29 +116,33 @@ export class CacheCrudController /** * Create one * - * @param _crudRequest - the CRUD request object + * @param crudRequest - the CRUD request object * @param cacheCreateDto - cache create dto * @param assignment - The cache assignment */ @CrudCreateOne() @AccessControlCreateOne(CacheResource.One) async createOne( - @CrudRequest() _crudRequest: CrudRequestInterface, + @CrudRequest() crudRequest: CrudRequestInterface, @CrudBody() cacheCreateDto: CacheCreateDto, @Param('assignment') assignment: ReferenceAssignment, ) { - const response = await this.cacheService.updateOrCreate( - assignment, - cacheCreateDto, + const expirationDate = getExpirationDate( + cacheCreateDto.expiresIn ?? this.settings.expiresIn, ); - return response; + + // call crud service to create + return this.getCrudService(assignment).createOne(crudRequest, { + ...cacheCreateDto, + expirationDate, + }); } /** * Create one * * @param crudRequest - the CRUD request object - * @param cacheUpdateDto - cache create dto + * @param cacheUpdateDto - cache update dto * @param assignment - The cache assignment */ @CrudUpdateOne() @@ -191,6 +196,46 @@ export class CacheCrudController } } + /** + * Do a Upsert operation for cache + * + * @param crudRequest - the CRUD request object + * @param cacheUpdateDto - cache update dto + * @param assignment - The cache assignment + */ + @Put(':id?') + @ApiOkResponse({ + type: CacheDto, + }) + @CrudReplaceOne() + @AccessControlCreateOne(CacheResource.One) + async replaceOne( + @CrudRequest() crudRequest: CrudRequestInterface, + @CrudBody() cacheUpdateDto: CacheUpdateDto, + @Param('assignment') assignment: ReferenceAssignment, + ) { + let cache; + try { + cache = await this.getOne(crudRequest, assignment); + } catch (error) { + // TODO: find a better aproach + // getOne throws and error if id does not match + } + if (cache && cache?.id) { + const expirationDate = getExpirationDate( + cacheUpdateDto.expiresIn ?? this.settings.expiresIn, + ); + + // call crud service to create + return this.getCrudService(assignment).replaceOne(crudRequest, { + ...cacheUpdateDto, + expirationDate, + }); + } else { + return this.createOne(crudRequest, cacheUpdateDto, assignment); + } + } + /** * Get the entity key for the given assignment. *