Implement a DeleteAdCommand
This commit is contained in:
parent
a7c281d740
commit
1701fbbeb1
|
@ -1,49 +1,50 @@
|
|||
import { Module, Provider } from '@nestjs/common';
|
||||
import { CqrsModule } from '@nestjs/cqrs';
|
||||
import {
|
||||
AD_MESSAGE_PUBLISHER,
|
||||
AD_REPOSITORY,
|
||||
AD_DIRECTION_ENCODER,
|
||||
AD_ROUTE_PROVIDER,
|
||||
TIMEZONE_FINDER,
|
||||
TIME_CONVERTER,
|
||||
INPUT_DATETIME_TRANSFORMER,
|
||||
OUTPUT_DATETIME_TRANSFORMER,
|
||||
MATCHING_REPOSITORY,
|
||||
AD_CONFIGURATION_REPOSITORY,
|
||||
GEOGRAPHY_PACKAGE,
|
||||
} from './ad.di-tokens';
|
||||
import { ConfigurationRepository } from '@mobicoop/configuration-module';
|
||||
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||
import { AdRepository } from './infrastructure/ad.repository';
|
||||
import { PrismaService } from './infrastructure/prisma.service';
|
||||
import { AdMapper } from './ad.mapper';
|
||||
import { AdCreatedMessageHandler } from './interface/message-handlers/ad-created.message-handler';
|
||||
import { PostgresDirectionEncoder } from '@modules/geography/infrastructure/postgres-direction-encoder';
|
||||
import { GeographyModule } from '@modules/geography/geography.module';
|
||||
import { CreateAdService } from './core/application/commands/create-ad/create-ad.service';
|
||||
import { MatchGrpcController } from './interface/grpc-controllers/match.grpc-controller';
|
||||
import { MatchQueryHandler } from './core/application/queries/match/match.query-handler';
|
||||
import { TimezoneFinder } from './infrastructure/timezone-finder';
|
||||
import { TimeConverter } from './infrastructure/time-converter';
|
||||
import { InputDateTimeTransformer } from './infrastructure/input-datetime-transformer';
|
||||
import { MatchMapper } from './match.mapper';
|
||||
import { OutputDateTimeTransformer } from './infrastructure/output-datetime-transformer';
|
||||
import { MatchingRepository } from './infrastructure/matching.repository';
|
||||
import { MatchingMapper } from './matching.mapper';
|
||||
import { PostgresDirectionEncoder } from '@modules/geography/infrastructure/postgres-direction-encoder';
|
||||
import { CacheModule } from '@nestjs/cache-manager';
|
||||
import { Module, Provider } from '@nestjs/common';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { redisStore } from 'cache-manager-ioredis-yet';
|
||||
import { CqrsModule } from '@nestjs/cqrs';
|
||||
import { ClientsModule, Transport } from '@nestjs/microservices';
|
||||
import {
|
||||
RedisClientOptions,
|
||||
RedisModule,
|
||||
RedisModuleOptions,
|
||||
} from '@songkeys/nestjs-redis';
|
||||
import { ConfigurationRepository } from '@mobicoop/configuration-module';
|
||||
import { PublishMessageWhenMatcherAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-matcher-ad-is-created.domain-event-handler';
|
||||
import { Georouter } from './infrastructure/georouter';
|
||||
import { ClientsModule, Transport } from '@nestjs/microservices';
|
||||
import { GRPC_GEOGRAPHY_PACKAGE_NAME } from '@src/app.constants';
|
||||
import { redisStore } from 'cache-manager-ioredis-yet';
|
||||
import { join } from 'path';
|
||||
import {
|
||||
AD_CONFIGURATION_REPOSITORY,
|
||||
AD_DIRECTION_ENCODER,
|
||||
AD_MESSAGE_PUBLISHER,
|
||||
AD_REPOSITORY,
|
||||
AD_ROUTE_PROVIDER,
|
||||
GEOGRAPHY_PACKAGE,
|
||||
INPUT_DATETIME_TRANSFORMER,
|
||||
MATCHING_REPOSITORY,
|
||||
OUTPUT_DATETIME_TRANSFORMER,
|
||||
TIMEZONE_FINDER,
|
||||
TIME_CONVERTER,
|
||||
} from './ad.di-tokens';
|
||||
import { AdMapper } from './ad.mapper';
|
||||
import { CreateAdService } from './core/application/commands/create-ad/create-ad.service';
|
||||
import { DeleteAdService } from './core/application/commands/delete-ad/delete-ad.service';
|
||||
import { PublishMessageWhenMatcherAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-matcher-ad-is-created.domain-event-handler';
|
||||
import { MatchQueryHandler } from './core/application/queries/match/match.query-handler';
|
||||
import { AdRepository } from './infrastructure/ad.repository';
|
||||
import { Georouter } from './infrastructure/georouter';
|
||||
import { InputDateTimeTransformer } from './infrastructure/input-datetime-transformer';
|
||||
import { MatchingRepository } from './infrastructure/matching.repository';
|
||||
import { OutputDateTimeTransformer } from './infrastructure/output-datetime-transformer';
|
||||
import { PrismaService } from './infrastructure/prisma.service';
|
||||
import { TimeConverter } from './infrastructure/time-converter';
|
||||
import { TimezoneFinder } from './infrastructure/timezone-finder';
|
||||
import { MatchGrpcController } from './interface/grpc-controllers/match.grpc-controller';
|
||||
import { AdCreatedMessageHandler } from './interface/message-handlers/ad-created.message-handler';
|
||||
import { MatchMapper } from './match.mapper';
|
||||
import { MatchingMapper } from './matching.mapper';
|
||||
|
||||
const imports = [
|
||||
CqrsModule,
|
||||
|
@ -102,7 +103,7 @@ const eventHandlers: Provider[] = [
|
|||
PublishMessageWhenMatcherAdIsCreatedDomainEventHandler,
|
||||
];
|
||||
|
||||
const commandHandlers: Provider[] = [CreateAdService];
|
||||
const commandHandlers: Provider[] = [CreateAdService, DeleteAdService];
|
||||
|
||||
const queryHandlers: Provider[] = [MatchQueryHandler];
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import { Command, CommandProps } from '@mobicoop/ddd-library';
|
||||
|
||||
export class DeleteAdCommand extends Command {
|
||||
constructor(props: CommandProps<DeleteAdCommand>) {
|
||||
super(props);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
|
||||
import { AdRepositoryPort } from '../../ports/ad.repository.port';
|
||||
import { DeleteAdCommand } from './delete-ad.command';
|
||||
|
||||
@CommandHandler(DeleteAdCommand)
|
||||
export class DeleteAdService implements ICommandHandler {
|
||||
constructor(
|
||||
@Inject(AD_REPOSITORY) private readonly adRepository: AdRepositoryPort,
|
||||
) {}
|
||||
|
||||
async execute(command: DeleteAdCommand): Promise<boolean> {
|
||||
const ad = await this.adRepository.findOneById(command.id);
|
||||
ad.delete();
|
||||
return this.adRepository.delete(ad);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library';
|
||||
import { AggregateID, AggregateRoot } from '@mobicoop/ddd-library';
|
||||
import { AdProps, CreateAdProps } from './ad.types';
|
||||
import { AdDeletedDomainEvent } from './events/ad-delete.domain-event';
|
||||
import { MatcherAdCreatedDomainEvent } from './events/matcher-ad-created.domain-event';
|
||||
|
||||
export class AdEntity extends AggregateRoot<AdProps> {
|
||||
|
@ -26,6 +27,14 @@ export class AdEntity extends AggregateRoot<AdProps> {
|
|||
return ad;
|
||||
};
|
||||
|
||||
delete(): void {
|
||||
this.addEvent(
|
||||
new AdDeletedDomainEvent({
|
||||
aggregateId: this.id,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
validate(): void {
|
||||
// entity business rules validation to protect it's invariant before saving entity to a database
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library';
|
||||
|
||||
export class AdDeletedDomainEvent extends DomainEvent {
|
||||
constructor(props: DomainEventProps<AdDeletedDomainEvent>) {
|
||||
super(props);
|
||||
}
|
||||
}
|
|
@ -1,52 +1,17 @@
|
|||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { PointProps } from '@modules/ad/core/domain/value-objects/point.value-object';
|
||||
|
||||
const originPointProps: PointProps = {
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
};
|
||||
const destinationPointProps: PointProps = {
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
};
|
||||
|
||||
const createAdProps: CreateAdProps = {
|
||||
id: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
|
||||
driver: true,
|
||||
passenger: true,
|
||||
fromDate: '2023-06-21',
|
||||
toDate: '2023-06-21',
|
||||
schedule: [
|
||||
{
|
||||
day: 3,
|
||||
time: '08:30',
|
||||
margin: 900,
|
||||
},
|
||||
],
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [originPointProps, destinationPointProps],
|
||||
driverDistance: 23000,
|
||||
driverDuration: 900,
|
||||
passengerDistance: 23000,
|
||||
passengerDuration: 900,
|
||||
fwdAzimuth: 283,
|
||||
backAzimuth: 93,
|
||||
points: [],
|
||||
};
|
||||
import { createAdProps } from './ad.fixtures';
|
||||
|
||||
describe('Ad entity create', () => {
|
||||
it('should create a new entity', async () => {
|
||||
const ad: AdEntity = AdEntity.create(createAdProps);
|
||||
expect(ad.id.length).toBe(36);
|
||||
expect(ad.getProps().schedule.length).toBe(1);
|
||||
expect(ad.getProps().schedule[0].day).toBe(3);
|
||||
expect(ad.getProps().schedule[0].time).toBe('08:30');
|
||||
expect(ad.getProps().driver).toBeTruthy();
|
||||
expect(ad.getProps().passenger).toBeTruthy();
|
||||
expect(ad.getProps().driverDistance).toBe(23000);
|
||||
describe('create', () => {
|
||||
it('should create a new entity', async () => {
|
||||
const ad: AdEntity = AdEntity.create(createAdProps());
|
||||
expect(ad.id.length).toBe(36);
|
||||
expect(ad.getProps().schedule.length).toBe(1);
|
||||
expect(ad.getProps().schedule[0].day).toBe(3);
|
||||
expect(ad.getProps().schedule[0].time).toBe('08:30');
|
||||
expect(ad.getProps().driver).toBeTruthy();
|
||||
expect(ad.getProps().passenger).toBeTruthy();
|
||||
expect(ad.getProps().driverDistance).toBe(23000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
|
||||
import { PointProps } from '@modules/ad/core/domain/value-objects/point.value-object';
|
||||
|
||||
const originPointProps: PointProps = {
|
||||
lat: 48.689445,
|
||||
lon: 6.17651,
|
||||
};
|
||||
const destinationPointProps: PointProps = {
|
||||
lat: 48.8566,
|
||||
lon: 2.3522,
|
||||
};
|
||||
|
||||
export function createAdProps(): CreateAdProps {
|
||||
return {
|
||||
id: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
|
||||
driver: true,
|
||||
passenger: true,
|
||||
fromDate: '2023-06-21',
|
||||
toDate: '2023-06-21',
|
||||
schedule: [
|
||||
{
|
||||
day: 3,
|
||||
time: '08:30',
|
||||
margin: 900,
|
||||
},
|
||||
],
|
||||
frequency: Frequency.PUNCTUAL,
|
||||
seatsProposed: 3,
|
||||
seatsRequested: 1,
|
||||
strict: false,
|
||||
waypoints: [originPointProps, destinationPointProps],
|
||||
driverDistance: 23000,
|
||||
driverDuration: 900,
|
||||
passengerDistance: 23000,
|
||||
passengerDuration: 900,
|
||||
fwdAzimuth: 283,
|
||||
backAzimuth: 93,
|
||||
points: [],
|
||||
};
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
import { AD_REPOSITORY } from '@modules/ad/ad.di-tokens';
|
||||
import { DeleteAdCommand } from '@modules/ad/core/application/commands/delete-ad/delete-ad.command';
|
||||
import { DeleteAdService } from '@modules/ad/core/application/commands/delete-ad/delete-ad.service';
|
||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { createAdProps } from './ad.fixtures';
|
||||
|
||||
const ad: AdEntity = AdEntity.create(createAdProps());
|
||||
const mockAdRepository = {
|
||||
findOneById: jest.fn().mockImplementation(() => ad),
|
||||
delete: jest.fn(),
|
||||
};
|
||||
|
||||
describe('DeleteAdService', () => {
|
||||
let deleteAdService: DeleteAdService;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: AD_REPOSITORY,
|
||||
useValue: mockAdRepository,
|
||||
},
|
||||
DeleteAdService,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
deleteAdService = module.get<DeleteAdService>(DeleteAdService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(deleteAdService).toBeDefined();
|
||||
});
|
||||
|
||||
it('should execute the delete logic and delete the ad from the repository', async () => {
|
||||
jest.spyOn(ad, 'delete');
|
||||
await deleteAdService.execute(new DeleteAdCommand(ad.id));
|
||||
expect(ad.delete).toHaveBeenCalled();
|
||||
expect(mockAdRepository.delete).toHaveBeenCalledWith(ad);
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue