working crud
This commit is contained in:
parent
4e1fb9a8d6
commit
368ce98174
|
@ -215,4 +215,25 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
async updateWithFields(uuid: string, entity: Partial<T>): Promise<number> {
|
||||
entity['"updatedAt"'] = `to_timestamp(${Date.now()} / 1000.0)`;
|
||||
const values = Object.keys(entity).map((key) => `${key} = ${entity[key]}`);
|
||||
try {
|
||||
const command = `UPDATE ${this._model} SET ${values.join(
|
||||
', ',
|
||||
)} WHERE uuid = '${uuid}'`;
|
||||
return await this._prisma.$executeRawUnsafe(command);
|
||||
} catch (e) {
|
||||
if (e instanceof PrismaClientKnownRequestError) {
|
||||
throw new DatabaseException(
|
||||
PrismaClientKnownRequestError.name,
|
||||
e.code,
|
||||
e.message,
|
||||
);
|
||||
} else {
|
||||
throw new DatabaseException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,4 +32,24 @@ export class TerritoryRepository<T> extends PrismaRepository<T> {
|
|||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
async updateTerritory(uuid: string, territory: Territory): Promise<T> {
|
||||
try {
|
||||
const fields = {};
|
||||
if (territory.name) fields['name'] = `'${territory.name}'`;
|
||||
if (territory.shape)
|
||||
fields[
|
||||
'shape'
|
||||
] = `ST_GeomFromGeoJSON('{"type":"MultiPolygon","coordinates":${territory.shape}}')`;
|
||||
const affectedRowNumber = await this.updateWithFields(uuid, fields);
|
||||
if (affectedRowNumber == 1) {
|
||||
return this.findOne({
|
||||
name: territory.name,
|
||||
});
|
||||
}
|
||||
throw new DatabaseException();
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,6 +72,18 @@ const mockPrismaService = {
|
|||
});
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
.mockImplementationOnce((fields: object) => {
|
||||
throw new Error('an unknown error');
|
||||
})
|
||||
.mockResolvedValueOnce(fakeEntityCreated)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
.mockImplementationOnce((fields: object) => {
|
||||
throw new PrismaClientKnownRequestError('unknown request', {
|
||||
code: 'code',
|
||||
clientVersion: 'version',
|
||||
});
|
||||
})
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
.mockImplementationOnce((fields: object) => {
|
||||
throw new Error('an unknown error');
|
||||
}),
|
||||
|
@ -454,7 +466,7 @@ describe('PrismaRepository', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('createwithFields', () => {
|
||||
describe('createWithFields', () => {
|
||||
it('should create an entity', async () => {
|
||||
jest.spyOn(prisma, '$queryRawUnsafe');
|
||||
|
||||
|
@ -483,4 +495,41 @@ describe('PrismaRepository', () => {
|
|||
).rejects.toBeInstanceOf(DatabaseException);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateWithFields', () => {
|
||||
it('should update an entity', async () => {
|
||||
jest.spyOn(prisma, '$queryRawUnsafe');
|
||||
|
||||
const updatedEntity = await fakeRepository.updateWithFields(
|
||||
'804319b3-a09b-4491-9f82-7976bfce0aff',
|
||||
{
|
||||
name: 'my-name',
|
||||
},
|
||||
);
|
||||
expect(updatedEntity).toBe(fakeEntityCreated);
|
||||
expect(prisma.$queryRawUnsafe).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should throw a DatabaseException for client error', async () => {
|
||||
await expect(
|
||||
fakeRepository.updateWithFields(
|
||||
'804319b3-a09b-4491-9f82-7976bfce0aff',
|
||||
{
|
||||
name: 'my-name',
|
||||
},
|
||||
),
|
||||
).rejects.toBeInstanceOf(DatabaseException);
|
||||
});
|
||||
|
||||
it('should throw a DatabaseException if uuid is not found', async () => {
|
||||
await expect(
|
||||
fakeRepository.updateWithFields(
|
||||
'804319b3-a09b-4491-9f82-7976bfce0aff',
|
||||
{
|
||||
name: 'my-name',
|
||||
},
|
||||
),
|
||||
).rejects.toBeInstanceOf(DatabaseException);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
import { Mapper } from '@automapper/core';
|
||||
import { InjectMapper } from '@automapper/nestjs';
|
||||
import {
|
||||
CacheInterceptor,
|
||||
CacheKey,
|
||||
Controller,
|
||||
UseInterceptors,
|
||||
UsePipes,
|
||||
} from '@nestjs/common';
|
||||
import { Controller, UsePipes } from '@nestjs/common';
|
||||
import { CommandBus, QueryBus } from '@nestjs/cqrs';
|
||||
import { GrpcMethod, RpcException } from '@nestjs/microservices';
|
||||
import { Territory } from '../../domain/entities/territory';
|
||||
|
@ -41,8 +35,6 @@ export class TerritoriesController {
|
|||
) {}
|
||||
|
||||
@GrpcMethod('TerritoriesService', 'FindAllForPoint')
|
||||
@UseInterceptors(CacheInterceptor)
|
||||
@CacheKey('TerritoriesServiceFindAllForPoint')
|
||||
async findAllTerritoriesForPoint(
|
||||
data: FindAllTerritoriesForPointRequest,
|
||||
): Promise<ICollection<Territory>> {
|
||||
|
@ -58,8 +50,6 @@ export class TerritoriesController {
|
|||
}
|
||||
|
||||
@GrpcMethod('TerritoriesService', 'FindAll')
|
||||
@UseInterceptors(CacheInterceptor)
|
||||
@CacheKey('TerritoriesServiceFindAll')
|
||||
async findAll(
|
||||
data: FindAllTerritoriesRequest,
|
||||
): Promise<ICollection<Territory>> {
|
||||
|
@ -75,8 +65,6 @@ export class TerritoriesController {
|
|||
}
|
||||
|
||||
@GrpcMethod('TerritoriesService', 'FindOneByUuid')
|
||||
@UseInterceptors(CacheInterceptor)
|
||||
@CacheKey('TerritoriesServiceFindOneByUuid')
|
||||
async findOneByUuid(
|
||||
data: FindTerritoryByUuidRequest,
|
||||
): Promise<TerritoryPresenter> {
|
||||
|
|
|
@ -6,8 +6,8 @@ service TerritoriesService {
|
|||
rpc FindOneByUuid(TerritoryByUuid) returns (Territory);
|
||||
rpc FindAll(TerritoryFilter) returns (Territories);
|
||||
rpc FindAllForPoint(Point) returns (Territories);
|
||||
rpc Create(Territory) returns (Territory);
|
||||
rpc Update(Territory) returns (Territory);
|
||||
rpc Create(TerritoryShape) returns (Territory);
|
||||
rpc Update(TerritoryShape) returns (Territory);
|
||||
rpc Delete(TerritoryByUuid) returns (Empty);
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,17 @@ message TerritoryByUuid {
|
|||
string uuid = 1;
|
||||
}
|
||||
|
||||
message Territory {
|
||||
message TerritoryShape {
|
||||
string uuid = 1;
|
||||
string name = 2;
|
||||
string shape = 3;
|
||||
}
|
||||
|
||||
message Territory {
|
||||
string uuid = 1;
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
message TerritoryFilter {
|
||||
optional int32 page = 1;
|
||||
optional int32 perPage = 2;
|
||||
|
|
|
@ -18,16 +18,14 @@ export class UpdateTerritoryUseCase {
|
|||
) {}
|
||||
|
||||
async execute(command: UpdateTerritoryCommand): Promise<Territory> {
|
||||
const entity = this._mapper.map(
|
||||
try {
|
||||
const territory = await this._repository.updateTerritory(
|
||||
command.updateTerritoryRequest.uuid,
|
||||
this._mapper.map(
|
||||
command.updateTerritoryRequest,
|
||||
UpdateTerritoryRequest,
|
||||
Territory,
|
||||
);
|
||||
|
||||
try {
|
||||
const territory = await this._repository.update(
|
||||
command.updateTerritoryRequest.uuid,
|
||||
entity,
|
||||
),
|
||||
);
|
||||
this._territoryMessager.publish(
|
||||
'update',
|
||||
|
|
|
@ -45,7 +45,7 @@ import { TerritoryProfile } from './mappers/territory.profile';
|
|||
store: await redisStore({
|
||||
host: configService.get<string>('REDIS_HOST'),
|
||||
port: configService.get<number>('REDIS_PORT'),
|
||||
ttl: configService.get('CACHE_TTL'),
|
||||
ttl: configService.get<number>('CACHE_TTL'),
|
||||
}),
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
|
|
|
@ -23,7 +23,7 @@ const updateTerritoryCommand: UpdateTerritoryCommand =
|
|||
new UpdateTerritoryCommand(updateTerritoryRequest);
|
||||
|
||||
const mockTerritoriesRepository = {
|
||||
update: jest
|
||||
updateTerritory: jest
|
||||
.fn()
|
||||
.mockImplementationOnce((uuid: string, params: any) => {
|
||||
originalTerritory.name = params.name;
|
||||
|
|
Loading…
Reference in New Issue