diff --git a/packages/nestjs-cache/src/__fixtures__/entities/user-cache-entity.fixture.ts b/packages/nestjs-cache/src/__fixtures__/entities/user-cache-entity.fixture.ts index d1c0ccee4..04378ec39 100644 --- a/packages/nestjs-cache/src/__fixtures__/entities/user-cache-entity.fixture.ts +++ b/packages/nestjs-cache/src/__fixtures__/entities/user-cache-entity.fixture.ts @@ -1,4 +1,4 @@ -import { Entity, ManyToOne } from 'typeorm'; +import { Entity, ManyToOne, Unique } from 'typeorm'; import { ReferenceIdInterface } from '@concepta/ts-core'; import { UserEntityFixture } from './user-entity.fixture'; import { CacheSqliteEntity } from '../../entities/cache-sqlite.entity'; @@ -7,7 +7,10 @@ import { CacheSqliteEntity } from '../../entities/cache-sqlite.entity'; * Cache Entity Fixture */ @Entity() +@Unique(['key', 'type', 'assignee.id']) export class UserCacheEntityFixture extends CacheSqliteEntity { - @ManyToOne(() => UserEntityFixture, (user) => user.userCaches) + @ManyToOne(() => UserEntityFixture, (user) => user.userCaches, { + nullable: false, + }) assignee!: ReferenceIdInterface; } diff --git a/packages/nestjs-cache/src/cache.seeder.ts b/packages/nestjs-cache/src/cache.seeder.ts index cf93f14f3..59b35a9e3 100644 --- a/packages/nestjs-cache/src/cache.seeder.ts +++ b/packages/nestjs-cache/src/cache.seeder.ts @@ -1,5 +1,6 @@ import { Seeder } from '@concepta/typeorm-seeding'; import { CacheFactory } from './cache.factory'; +import { UserFactoryFixture } from './__fixtures__/factories/user.factory.fixture'; /** * Cache seeder @@ -16,8 +17,14 @@ export class CacheSeeder extends Seeder { // the factory const cacheFactory = this.factory(CacheFactory); + const userFactory = this.factory(UserFactoryFixture); + const user = await userFactory.create(); // create a bunch - await cacheFactory.createMany(createAmount); + await cacheFactory.createMany(createAmount, { + assignee: { + id: user.id, + }, + }); } } 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 517539f8d..9f7883697 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 @@ -130,6 +130,36 @@ describe('CacheAssignmentController (e2e)', () => { }); }); + it('POST /cache/user assignee id null', async () => { + const payload = { + key: 'dashboard-1', + type: 'filter', + data: '{}', + expiresIn: '1d', + assignee: { id: null }, + }; + + await supertest(app.getHttpServer()) + .post('/cache/user') + .send(payload) + .expect(500); + }); + + it('POST /cache/user wrong assignee id', async () => { + const payload = { + key: 'dashboard-1', + type: 'filter', + data: '{}', + expiresIn: '1d', + assignee: { id: 'test' }, + }; + + await supertest(app.getHttpServer()) + .post('/cache/user') + .send(payload) + .expect(500); + }); + it('POST /cache/user Duplicated', async () => { const payload: CacheCreatableInterface = { key: 'dashboard-1', @@ -148,15 +178,45 @@ describe('CacheAssignmentController (e2e)', () => { expect(res.body.assignee.id).toBe(user.id); }); + payload.data = '{ "name": "John Doe" }'; + payload.expiresIn = null; await supertest(app.getHttpServer()) .post('/cache/user') .send(payload) + .expect(201) .then((res) => { - // check error message - expect(res.body.message).toBe( - 'userCache already exists with the given key, type, and assignee ID.', - ); - expect(res.status).toBe(400); + expect(res.body.key).toBe(payload.key); + expect(res.body.data).toBe(payload.data); + expect(res.body.assignee.id).toBe(user.id); + }); + }); + + it('POST /cache/user Update', async () => { + const payload: CacheCreatableInterface = { + key: 'dashboard-1', + type: 'filter', + data: '{}', + expiresIn: '1d', + assignee: { id: user.id }, + }; + await supertest(app.getHttpServer()) + .post('/cache/user') + .send(payload) + .expect(201) + .then((res) => { + 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()) + .post('/cache/user/') + .send(payload) + .expect(201) + .then((res) => { + expect(res.body.key).toBe(payload.key); + expect(res.body.data).toBe(payload.data); + expect(res.body.assignee.id).toBe(user.id); }); }); diff --git a/packages/nestjs-cache/src/controllers/cache-crud.controller.ts b/packages/nestjs-cache/src/controllers/cache-crud.controller.ts index 335832c78..d17d027a3 100644 --- a/packages/nestjs-cache/src/controllers/cache-crud.controller.ts +++ b/packages/nestjs-cache/src/controllers/cache-crud.controller.ts @@ -38,7 +38,7 @@ import { CacheSettingsInterface } from '../interfaces/cache-settings.interface'; import { CacheCrudService } from '../services/cache-crud.service'; import getExpirationDate from '../utils/get-expiration-date.util'; import { CacheService } from '../services/cache.service'; -import { CacheEntityAlreadyExistsException } from '../exceptions/cache-entity-already-exists.exception'; +import { CacheCreateDto } from '../dto/cache-create.dto'; /** * Cache assignment controller. */ @@ -121,7 +121,7 @@ export class CacheCrudController @AccessControlCreateOne(CacheResource.One) async createOne( @CrudRequest() crudRequest: CrudRequestInterface, - @CrudBody() cacheCreateDto: CacheCreatableInterface, + @CrudBody() cacheCreateDto: CacheCreateDto, @Param('assignment') assignment: ReferenceAssignment, ) { const expirationDate = getExpirationDate( @@ -133,17 +133,30 @@ export class CacheCrudController cacheCreateDto, ); + // update or create if (existingCache) { - throw new CacheEntityAlreadyExistsException( - this.getEntityKey(assignment), + 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, + }); } - - // call crud service to create - return this.getCrudService(assignment).createOne(crudRequest, { - ...cacheCreateDto, - expirationDate, - }); } /** diff --git a/packages/nestjs-cache/src/entities/cache-postgres.entity.ts b/packages/nestjs-cache/src/entities/cache-postgres.entity.ts index 1867d89ef..9b0924446 100644 --- a/packages/nestjs-cache/src/entities/cache-postgres.entity.ts +++ b/packages/nestjs-cache/src/entities/cache-postgres.entity.ts @@ -1,4 +1,4 @@ -import { Column, Index } from 'typeorm'; +import { Column, Unique } from 'typeorm'; import { ReferenceIdInterface } from '@concepta/ts-core'; import { CacheInterface } from '@concepta/ts-common'; import { CommonPostgresEntity } from '@concepta/typeorm-common'; @@ -6,7 +6,7 @@ import { CommonPostgresEntity } from '@concepta/typeorm-common'; /** * Cache Postgres Entity */ -@Index('key_unique_index', ['key', 'type', 'assignee.id'], { unique: true }) +@Unique(['key', 'type', 'assignee.id']) export abstract class CachePostgresEntity extends CommonPostgresEntity implements CacheInterface diff --git a/packages/nestjs-cache/src/entities/cache-sqlite.entity.ts b/packages/nestjs-cache/src/entities/cache-sqlite.entity.ts index 1854c5527..7a8611d25 100644 --- a/packages/nestjs-cache/src/entities/cache-sqlite.entity.ts +++ b/packages/nestjs-cache/src/entities/cache-sqlite.entity.ts @@ -1,4 +1,4 @@ -import { Column, Index } from 'typeorm'; +import { Column, Unique } from 'typeorm'; import { CommonSqliteEntity } from '@concepta/typeorm-common'; import { ReferenceIdInterface } from '@concepta/ts-core'; import { CacheInterface } from '@concepta/ts-common'; @@ -7,7 +7,7 @@ import { CacheInterface } from '@concepta/ts-common'; * Cache Sqlite Entity */ -@Index('key_unique_index', ['key', 'type', 'assignee.id'], { unique: true }) +@Unique(['key', 'type', 'assignee.id']) export abstract class CacheSqliteEntity extends CommonSqliteEntity implements CacheInterface diff --git a/packages/nestjs-cache/src/services/cache.service.ts b/packages/nestjs-cache/src/services/cache.service.ts index 658977e69..a6d5d810f 100644 --- a/packages/nestjs-cache/src/services/cache.service.ts +++ b/packages/nestjs-cache/src/services/cache.service.ts @@ -2,11 +2,7 @@ import { plainToInstance } from 'class-transformer'; import { validate } from 'class-validator'; import { DeepPartial, Repository } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import { - CacheCreatableInterface, - CacheInterface, - CacheUpdatableInterface, -} from '@concepta/ts-common'; +import { CacheInterface, CacheUpdatableInterface } from '@concepta/ts-common'; import { ReferenceAssignment, ReferenceId, Type } from '@concepta/ts-core'; import { QueryOptionsInterface, @@ -44,7 +40,7 @@ export class CacheService implements CacheServiceInterface { */ async create( assignment: ReferenceAssignment, - cache: CacheCreatableInterface, + cache: CacheCreateDto, queryOptions?: QueryOptionsInterface, ): Promise { // get the assignment repo