Implement a DeleteAdCommand
This commit is contained in:
parent
a7c281d740
commit
1701fbbeb1
|
@ -1,49 +1,50 @@
|
||||||
import { Module, Provider } from '@nestjs/common';
|
import { ConfigurationRepository } from '@mobicoop/configuration-module';
|
||||||
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 { MessageBrokerPublisher } from '@mobicoop/message-broker-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 { GeographyModule } from '@modules/geography/geography.module';
|
||||||
import { CreateAdService } from './core/application/commands/create-ad/create-ad.service';
|
import { PostgresDirectionEncoder } from '@modules/geography/infrastructure/postgres-direction-encoder';
|
||||||
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 { CacheModule } from '@nestjs/cache-manager';
|
import { CacheModule } from '@nestjs/cache-manager';
|
||||||
|
import { Module, Provider } from '@nestjs/common';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
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 {
|
import {
|
||||||
RedisClientOptions,
|
RedisClientOptions,
|
||||||
RedisModule,
|
RedisModule,
|
||||||
RedisModuleOptions,
|
RedisModuleOptions,
|
||||||
} from '@songkeys/nestjs-redis';
|
} 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 { GRPC_GEOGRAPHY_PACKAGE_NAME } from '@src/app.constants';
|
||||||
|
import { redisStore } from 'cache-manager-ioredis-yet';
|
||||||
import { join } from 'path';
|
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 = [
|
const imports = [
|
||||||
CqrsModule,
|
CqrsModule,
|
||||||
|
@ -102,7 +103,7 @@ const eventHandlers: Provider[] = [
|
||||||
PublishMessageWhenMatcherAdIsCreatedDomainEventHandler,
|
PublishMessageWhenMatcherAdIsCreatedDomainEventHandler,
|
||||||
];
|
];
|
||||||
|
|
||||||
const commandHandlers: Provider[] = [CreateAdService];
|
const commandHandlers: Provider[] = [CreateAdService, DeleteAdService];
|
||||||
|
|
||||||
const queryHandlers: Provider[] = [MatchQueryHandler];
|
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 { AdProps, CreateAdProps } from './ad.types';
|
||||||
|
import { AdDeletedDomainEvent } from './events/ad-delete.domain-event';
|
||||||
import { MatcherAdCreatedDomainEvent } from './events/matcher-ad-created.domain-event';
|
import { MatcherAdCreatedDomainEvent } from './events/matcher-ad-created.domain-event';
|
||||||
|
|
||||||
export class AdEntity extends AggregateRoot<AdProps> {
|
export class AdEntity extends AggregateRoot<AdProps> {
|
||||||
|
@ -26,6 +27,14 @@ export class AdEntity extends AggregateRoot<AdProps> {
|
||||||
return ad;
|
return ad;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
delete(): void {
|
||||||
|
this.addEvent(
|
||||||
|
new AdDeletedDomainEvent({
|
||||||
|
aggregateId: this.id,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
validate(): void {
|
validate(): void {
|
||||||
// entity business rules validation to protect it's invariant before saving entity to a database
|
// 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 { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||||
import { CreateAdProps, Frequency } from '@modules/ad/core/domain/ad.types';
|
import { createAdProps } from './ad.fixtures';
|
||||||
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: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('Ad entity create', () => {
|
describe('Ad entity create', () => {
|
||||||
it('should create a new entity', async () => {
|
describe('create', () => {
|
||||||
const ad: AdEntity = AdEntity.create(createAdProps);
|
it('should create a new entity', async () => {
|
||||||
expect(ad.id.length).toBe(36);
|
const ad: AdEntity = AdEntity.create(createAdProps());
|
||||||
expect(ad.getProps().schedule.length).toBe(1);
|
expect(ad.id.length).toBe(36);
|
||||||
expect(ad.getProps().schedule[0].day).toBe(3);
|
expect(ad.getProps().schedule.length).toBe(1);
|
||||||
expect(ad.getProps().schedule[0].time).toBe('08:30');
|
expect(ad.getProps().schedule[0].day).toBe(3);
|
||||||
expect(ad.getProps().driver).toBeTruthy();
|
expect(ad.getProps().schedule[0].time).toBe('08:30');
|
||||||
expect(ad.getProps().passenger).toBeTruthy();
|
expect(ad.getProps().driver).toBeTruthy();
|
||||||
expect(ad.getProps().driverDistance).toBe(23000);
|
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