Implement the GRPC controller to update ads
This commit is contained in:
parent
7a84bff260
commit
659c1baea8
|
@ -33,12 +33,14 @@ import { DeleteAdGrpcController } from './interface/grpc-controllers/delete-ad.g
|
||||||
import { FindAdByIdGrpcController } from './interface/grpc-controllers/find-ad-by-id.grpc.controller';
|
import { FindAdByIdGrpcController } from './interface/grpc-controllers/find-ad-by-id.grpc.controller';
|
||||||
import { FindAdsByIdsGrpcController } from './interface/grpc-controllers/find-ads-by-ids.grpc.controller';
|
import { FindAdsByIdsGrpcController } from './interface/grpc-controllers/find-ads-by-ids.grpc.controller';
|
||||||
import { FindAdsByUserIdGrpcController } from './interface/grpc-controllers/find-ads-by-user-id.grpc.controller';
|
import { FindAdsByUserIdGrpcController } from './interface/grpc-controllers/find-ads-by-user-id.grpc.controller';
|
||||||
|
import { UpdateAdGrpcController } from './interface/grpc-controllers/update-ad.grpc.controller';
|
||||||
import { MatcherAdCreatedMessageHandler } from './interface/message-handlers/matcher-ad-created.message-handler';
|
import { MatcherAdCreatedMessageHandler } from './interface/message-handlers/matcher-ad-created.message-handler';
|
||||||
import { MatcherAdCreationFailedMessageHandler } from './interface/message-handlers/matcher-ad-creation-failed.message-handler';
|
import { MatcherAdCreationFailedMessageHandler } from './interface/message-handlers/matcher-ad-creation-failed.message-handler';
|
||||||
import { UserDeletedMessageHandler } from './interface/message-handlers/user-deleted.message-handler';
|
import { UserDeletedMessageHandler } from './interface/message-handlers/user-deleted.message-handler';
|
||||||
|
|
||||||
const grpcControllers = [
|
const grpcControllers = [
|
||||||
CreateAdGrpcController,
|
CreateAdGrpcController,
|
||||||
|
UpdateAdGrpcController,
|
||||||
DeleteAdGrpcController,
|
DeleteAdGrpcController,
|
||||||
FindAdByIdGrpcController,
|
FindAdByIdGrpcController,
|
||||||
FindAdsByIdsGrpcController,
|
FindAdsByIdsGrpcController,
|
||||||
|
|
|
@ -7,7 +7,7 @@ service AdService {
|
||||||
rpc FindAllByIds(AdsById) returns (Ads);
|
rpc FindAllByIds(AdsById) returns (Ads);
|
||||||
rpc FindAllByUserId(UserById) returns (Ads);
|
rpc FindAllByUserId(UserById) returns (Ads);
|
||||||
rpc Create(Ad) returns (AdById);
|
rpc Create(Ad) returns (AdById);
|
||||||
rpc Update(Ad) returns (Ad);
|
rpc Update(Ad) returns (Empty);
|
||||||
rpc Delete(AdById) returns (Empty);
|
rpc Delete(AdById) returns (Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { IsUUID } from 'class-validator';
|
||||||
|
import { CreateAdRequestDto } from './create-ad.request.dto';
|
||||||
|
|
||||||
|
export class UpdateAdRequestDto extends CreateAdRequestDto {
|
||||||
|
@IsUUID(4)
|
||||||
|
id: string;
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
import {
|
||||||
|
NotFoundException,
|
||||||
|
RpcExceptionCode,
|
||||||
|
RpcValidationPipe,
|
||||||
|
} from '@mobicoop/ddd-library';
|
||||||
|
import { UpdateAdCommand } from '@modules/ad/core/application/commands/update-ad/update-ad.command';
|
||||||
|
import { Controller, UsePipes } from '@nestjs/common';
|
||||||
|
import { CommandBus } from '@nestjs/cqrs';
|
||||||
|
import { GrpcMethod, RpcException } from '@nestjs/microservices';
|
||||||
|
import { GRPC_SERVICE_NAME } from '@src/app.constants';
|
||||||
|
import { UpdateAdRequestDto } from './dtos/update-ad.request.dto';
|
||||||
|
|
||||||
|
@UsePipes(
|
||||||
|
new RpcValidationPipe({
|
||||||
|
whitelist: false,
|
||||||
|
forbidUnknownValues: false,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
@Controller()
|
||||||
|
export class UpdateAdGrpcController {
|
||||||
|
constructor(private readonly commandBus: CommandBus) {}
|
||||||
|
|
||||||
|
@GrpcMethod(GRPC_SERVICE_NAME, 'Update')
|
||||||
|
async update(data: UpdateAdRequestDto): Promise<void> {
|
||||||
|
try {
|
||||||
|
const cmdProps = {
|
||||||
|
adId: data.id,
|
||||||
|
...data,
|
||||||
|
};
|
||||||
|
delete (cmdProps as { id?: string }).id;
|
||||||
|
|
||||||
|
await this.commandBus.execute(new UpdateAdCommand(cmdProps));
|
||||||
|
} catch (error) {
|
||||||
|
if (error instanceof NotFoundException) {
|
||||||
|
throw new RpcException({
|
||||||
|
code: RpcExceptionCode.NOT_FOUND,
|
||||||
|
message: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
||||||
|
import { CreateAdRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/create-ad.request.dto';
|
||||||
|
import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto';
|
||||||
|
|
||||||
|
const originWaypoint: WaypointDto = {
|
||||||
|
position: 0,
|
||||||
|
lat: 48.689445,
|
||||||
|
lon: 6.17651,
|
||||||
|
houseNumber: '5',
|
||||||
|
street: 'Avenue Foch',
|
||||||
|
locality: 'Nancy',
|
||||||
|
postalCode: '54000',
|
||||||
|
country: 'France',
|
||||||
|
};
|
||||||
|
const destinationWaypoint: WaypointDto = {
|
||||||
|
position: 1,
|
||||||
|
lat: 48.8566,
|
||||||
|
lon: 2.3522,
|
||||||
|
locality: 'Paris',
|
||||||
|
postalCode: '75000',
|
||||||
|
country: 'France',
|
||||||
|
};
|
||||||
|
export function punctualCreateAdRequest(): CreateAdRequestDto {
|
||||||
|
return {
|
||||||
|
userId: '4eb6a6af-ecfd-41c3-9118-473a507014d4',
|
||||||
|
fromDate: '2023-12-21',
|
||||||
|
toDate: '2023-12-21',
|
||||||
|
schedule: [
|
||||||
|
{
|
||||||
|
time: '08:15',
|
||||||
|
day: 4,
|
||||||
|
margin: 600,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
driver: false,
|
||||||
|
passenger: true,
|
||||||
|
seatsRequested: 1,
|
||||||
|
seatsProposed: 3,
|
||||||
|
strict: false,
|
||||||
|
frequency: Frequency.PUNCTUAL,
|
||||||
|
waypoints: [originWaypoint, destinationWaypoint],
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,51 +1,10 @@
|
||||||
import { IdResponse } from '@mobicoop/ddd-library';
|
import { IdResponse, RpcExceptionCode } from '@mobicoop/ddd-library';
|
||||||
import { RpcExceptionCode } from '@mobicoop/ddd-library';
|
|
||||||
import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors';
|
import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors';
|
||||||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
|
||||||
import { CreateAdGrpcController } from '@modules/ad/interface/grpc-controllers/create-ad.grpc.controller';
|
import { CreateAdGrpcController } from '@modules/ad/interface/grpc-controllers/create-ad.grpc.controller';
|
||||||
import { CreateAdRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/create-ad.request.dto';
|
|
||||||
import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto';
|
|
||||||
import { CommandBus } from '@nestjs/cqrs';
|
import { CommandBus } from '@nestjs/cqrs';
|
||||||
import { RpcException } from '@nestjs/microservices';
|
import { RpcException } from '@nestjs/microservices';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { punctualCreateAdRequest } from './ad.fixtures';
|
||||||
const originWaypoint: WaypointDto = {
|
|
||||||
position: 0,
|
|
||||||
lat: 48.689445,
|
|
||||||
lon: 6.17651,
|
|
||||||
houseNumber: '5',
|
|
||||||
street: 'Avenue Foch',
|
|
||||||
locality: 'Nancy',
|
|
||||||
postalCode: '54000',
|
|
||||||
country: 'France',
|
|
||||||
};
|
|
||||||
const destinationWaypoint: WaypointDto = {
|
|
||||||
position: 1,
|
|
||||||
lat: 48.8566,
|
|
||||||
lon: 2.3522,
|
|
||||||
locality: 'Paris',
|
|
||||||
postalCode: '75000',
|
|
||||||
country: 'France',
|
|
||||||
};
|
|
||||||
const punctualCreateAdRequest: CreateAdRequestDto = {
|
|
||||||
userId: '4eb6a6af-ecfd-41c3-9118-473a507014d4',
|
|
||||||
fromDate: '2023-12-21',
|
|
||||||
toDate: '2023-12-21',
|
|
||||||
schedule: [
|
|
||||||
{
|
|
||||||
time: '08:15',
|
|
||||||
day: 4,
|
|
||||||
margin: 600,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
driver: false,
|
|
||||||
passenger: true,
|
|
||||||
seatsRequested: 1,
|
|
||||||
seatsProposed: 3,
|
|
||||||
strict: false,
|
|
||||||
frequency: Frequency.PUNCTUAL,
|
|
||||||
waypoints: [originWaypoint, destinationWaypoint],
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockCommandBus = {
|
const mockCommandBus = {
|
||||||
execute: jest
|
execute: jest
|
||||||
|
@ -89,7 +48,7 @@ describe('Create Ad Grpc Controller', () => {
|
||||||
it('should create a new ad', async () => {
|
it('should create a new ad', async () => {
|
||||||
jest.spyOn(mockCommandBus, 'execute');
|
jest.spyOn(mockCommandBus, 'execute');
|
||||||
const result: IdResponse = await createAdGrpcController.create(
|
const result: IdResponse = await createAdGrpcController.create(
|
||||||
punctualCreateAdRequest,
|
punctualCreateAdRequest(),
|
||||||
);
|
);
|
||||||
expect(result).toBeInstanceOf(IdResponse);
|
expect(result).toBeInstanceOf(IdResponse);
|
||||||
expect(result.id).toBe('200d61a8-d878-4378-a609-c19ea71633d2');
|
expect(result.id).toBe('200d61a8-d878-4378-a609-c19ea71633d2');
|
||||||
|
@ -100,7 +59,7 @@ describe('Create Ad Grpc Controller', () => {
|
||||||
jest.spyOn(mockCommandBus, 'execute');
|
jest.spyOn(mockCommandBus, 'execute');
|
||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
try {
|
try {
|
||||||
await createAdGrpcController.create(punctualCreateAdRequest);
|
await createAdGrpcController.create(punctualCreateAdRequest());
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
expect(e).toBeInstanceOf(RpcException);
|
expect(e).toBeInstanceOf(RpcException);
|
||||||
expect(e.error.code).toBe(RpcExceptionCode.ALREADY_EXISTS);
|
expect(e.error.code).toBe(RpcExceptionCode.ALREADY_EXISTS);
|
||||||
|
@ -112,7 +71,7 @@ describe('Create Ad Grpc Controller', () => {
|
||||||
jest.spyOn(mockCommandBus, 'execute');
|
jest.spyOn(mockCommandBus, 'execute');
|
||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
try {
|
try {
|
||||||
await createAdGrpcController.create(punctualCreateAdRequest);
|
await createAdGrpcController.create(punctualCreateAdRequest());
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
expect(e).toBeInstanceOf(RpcException);
|
expect(e).toBeInstanceOf(RpcException);
|
||||||
expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN);
|
expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN);
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
import { NotFoundException, RpcExceptionCode } from '@mobicoop/ddd-library';
|
||||||
|
import { UpdateAdGrpcController } from '@modules/ad/interface/grpc-controllers/update-ad.grpc.controller';
|
||||||
|
import { CommandBus } from '@nestjs/cqrs';
|
||||||
|
import { RpcException } from '@nestjs/microservices';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { punctualCreateAdRequest } from './ad.fixtures';
|
||||||
|
|
||||||
|
const validAdId = '200d61a8-d878-4378-a609-c19ea71633d2';
|
||||||
|
const mockCommandBus = {
|
||||||
|
execute: jest.fn().mockImplementation(async (command) => {
|
||||||
|
if (command.adId === '') throw 'Ad id is empty';
|
||||||
|
if (command.adId != validAdId) throw new NotFoundException();
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Update Ad GRPC Controller', () => {
|
||||||
|
let updateAdGrpcController: UpdateAdGrpcController;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: CommandBus,
|
||||||
|
useValue: mockCommandBus,
|
||||||
|
},
|
||||||
|
UpdateAdGrpcController,
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
updateAdGrpcController = module.get<UpdateAdGrpcController>(
|
||||||
|
UpdateAdGrpcController,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(updateAdGrpcController).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should execute the update ad command', async () => {
|
||||||
|
await updateAdGrpcController.update({
|
||||||
|
id: validAdId,
|
||||||
|
...punctualCreateAdRequest(),
|
||||||
|
});
|
||||||
|
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw a dedicated RpcException if ad is not found', async () => {
|
||||||
|
expect.assertions(3);
|
||||||
|
try {
|
||||||
|
await updateAdGrpcController.update({
|
||||||
|
id: 'ac85f5f4-41cd-4c5d-9aee-0a1acb176fb8',
|
||||||
|
...punctualCreateAdRequest(),
|
||||||
|
});
|
||||||
|
} catch (e: any) {
|
||||||
|
expect(e).toBeInstanceOf(RpcException);
|
||||||
|
expect(e.error.code).toBe(RpcExceptionCode.NOT_FOUND);
|
||||||
|
}
|
||||||
|
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should rethrow any other exceptions', async () => {
|
||||||
|
expect.assertions(2);
|
||||||
|
try {
|
||||||
|
await updateAdGrpcController.update({
|
||||||
|
id: '',
|
||||||
|
...punctualCreateAdRequest(),
|
||||||
|
});
|
||||||
|
} catch (e: any) {
|
||||||
|
expect(e).toBe('Ad id is empty');
|
||||||
|
}
|
||||||
|
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue