replace configuration module by dedicated package

This commit is contained in:
sbriat
2023-05-30 11:41:30 +02:00
parent a14ac74d5b
commit 0bf3048d0e
21 changed files with 113 additions and 499 deletions

View File

@@ -1,6 +1,6 @@
import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
import { Controller } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { CommandBus, QueryBus } from '@nestjs/cqrs';
import { CreateAdCommand } from '../../commands/create-ad.command';
import { CreateAdRequest } from '../../domain/dtos/create-ad.request';
import { validateOrReject } from 'class-validator';
@@ -14,6 +14,7 @@ export class AdMessagerController {
constructor(
private readonly messager: Messager,
private readonly commandBus: CommandBus,
private readonly queryBus: QueryBus,
) {}
@RabbitSubscribe({

View File

@@ -1,77 +0,0 @@
import { RabbitSubscribe } from '@golevelup/nestjs-rabbitmq';
import { Controller } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { CommandBus } from '@nestjs/cqrs';
import { DeleteConfigurationCommand } from '../../commands/delete-configuration.command';
import { SetConfigurationCommand } from '../../commands/set-configuration.command';
import { DeleteConfigurationRequest } from '../../domain/dtos/delete-configuration.request';
import { SetConfigurationRequest } from '../../domain/dtos/set-configuration.request';
import { Configuration } from '../../domain/entities/configuration';
@Controller()
export class ConfigurationMessagerController {
constructor(
private readonly commandBus: CommandBus,
private readonly configService: ConfigService,
) {}
@RabbitSubscribe({
name: 'setConfiguration',
})
public async setConfigurationHandler(message: string) {
const configuration: Configuration = JSON.parse(message);
if (
configuration.domain ==
this.configService.get<string>('SERVICE_CONFIGURATION_DOMAIN')
) {
const setConfigurationRequest: SetConfigurationRequest =
new SetConfigurationRequest();
setConfigurationRequest.domain = configuration.domain;
setConfigurationRequest.key = configuration.key;
setConfigurationRequest.value = configuration.value;
await this.commandBus.execute(
new SetConfigurationCommand(setConfigurationRequest),
);
}
}
@RabbitSubscribe({
name: 'deleteConfiguration',
})
public async configurationDeletedHandler(message: string) {
const deletedConfiguration: Configuration = JSON.parse(message);
if (
deletedConfiguration.domain ==
this.configService.get<string>('SERVICE_CONFIGURATION_DOMAIN')
) {
const deleteConfigurationRequest = new DeleteConfigurationRequest();
deleteConfigurationRequest.domain = deletedConfiguration.domain;
deleteConfigurationRequest.key = deletedConfiguration.key;
await this.commandBus.execute(
new DeleteConfigurationCommand(deleteConfigurationRequest),
);
}
}
@RabbitSubscribe({
name: 'propagateConfiguration',
})
public async propagateConfigurationsHandler(message: string) {
const configurations: Configuration[] = JSON.parse(message);
configurations.forEach(async (configuration) => {
if (
configuration.domain ==
this.configService.get<string>('SERVICE_CONFIGURATION_DOMAIN')
) {
const setConfigurationRequest: SetConfigurationRequest =
new SetConfigurationRequest();
setConfigurationRequest.domain = configuration.domain;
setConfigurationRequest.key = configuration.key;
setConfigurationRequest.value = configuration.value;
await this.commandBus.execute(
new SetConfigurationCommand(setConfigurationRequest),
);
}
});
}
}

View File

@@ -1,21 +0,0 @@
import { InjectRedis } from '@liaoliaots/nestjs-redis';
import { Injectable } from '@nestjs/common';
import { Redis } from 'ioredis';
import { IConfigurationRepository } from '../../domain/interfaces/configuration.repository';
@Injectable()
export class RedisConfigurationRepository extends IConfigurationRepository {
constructor(@InjectRedis() private readonly redis: Redis) {
super();
}
get = async (key: string): Promise<string> => await this.redis.get(key);
set = async (key: string, value: string): Promise<void> => {
await this.redis.set(key, value);
};
del = async (key: string): Promise<void> => {
await this.redis.del(key);
};
}

View File

@@ -1,9 +0,0 @@
import { DeleteConfigurationRequest } from '../domain/dtos/delete-configuration.request';
export class DeleteConfigurationCommand {
readonly deleteConfigurationRequest: DeleteConfigurationRequest;
constructor(deleteConfigurationRequest: DeleteConfigurationRequest) {
this.deleteConfigurationRequest = deleteConfigurationRequest;
}
}

View File

@@ -1,9 +0,0 @@
import { SetConfigurationRequest } from '../domain/dtos/set-configuration.request';
export class SetConfigurationCommand {
readonly setConfigurationRequest: SetConfigurationRequest;
constructor(setConfigurationRequest: SetConfigurationRequest) {
this.setConfigurationRequest = setConfigurationRequest;
}
}

View File

@@ -1,71 +0,0 @@
import { RabbitMQConfig, RabbitMQModule } from '@golevelup/nestjs-rabbitmq';
import { RedisModule, RedisModuleOptions } from '@liaoliaots/nestjs-redis';
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { CqrsModule } from '@nestjs/cqrs';
import { ConfigurationMessagerController } from './adapters/primaries/configuration-messager.controller';
import { RedisConfigurationRepository } from './adapters/secondaries/redis-configuration.repository';
import { DeleteConfigurationUseCase } from './domain/usecases/delete-configuration.usecase';
import { GetConfigurationUseCase } from './domain/usecases/get-configuration.usecase';
import { SetConfigurationUseCase } from './domain/usecases/set-configuration.usecase';
@Module({
imports: [
CqrsModule,
RedisModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (
configService: ConfigService,
): Promise<RedisModuleOptions> => ({
config: {
host: configService.get<string>('REDIS_HOST'),
port: configService.get<number>('REDIS_PORT'),
password: configService.get<string>('REDIS_PASSWORD'),
},
}),
}),
RabbitMQModule.forRootAsync(RabbitMQModule, {
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (
configService: ConfigService,
): Promise<RabbitMQConfig> => ({
exchanges: [
{
name: configService.get<string>('RMQ_EXCHANGE'),
type: 'topic',
},
],
handlers: {
setConfiguration: {
exchange: configService.get<string>('RMQ_EXCHANGE'),
routingKey: ['configuration.create', 'configuration.update'],
queue: 'matcher-configuration-create-update',
},
deleteConfiguration: {
exchange: configService.get<string>('RMQ_EXCHANGE'),
routingKey: 'configuration.delete',
queue: 'matcher-configuration-delete',
},
propagateConfiguration: {
exchange: configService.get<string>('RMQ_EXCHANGE'),
routingKey: 'configuration.propagate',
queue: 'matcher-configuration-propagate',
},
},
uri: configService.get<string>('RMQ_URI'),
connectionInitOptions: { wait: false },
enableControllerDiscovery: true,
}),
}),
],
controllers: [ConfigurationMessagerController],
providers: [
GetConfigurationUseCase,
SetConfigurationUseCase,
DeleteConfigurationUseCase,
RedisConfigurationRepository,
],
})
export class ConfigurationModule {}

View File

@@ -1,11 +0,0 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class DeleteConfigurationRequest {
@IsString()
@IsNotEmpty()
domain: string;
@IsString()
@IsNotEmpty()
key: string;
}

View File

@@ -1,15 +0,0 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class SetConfigurationRequest {
@IsString()
@IsNotEmpty()
domain: string;
@IsString()
@IsNotEmpty()
key: string;
@IsString()
@IsNotEmpty()
value: string;
}

View File

@@ -1,12 +0,0 @@
import { AutoMap } from '@automapper/classes';
export class Configuration {
@AutoMap()
domain: string;
@AutoMap()
key: string;
@AutoMap()
value: string;
}

View File

@@ -1,8 +0,0 @@
import { Injectable } from '@nestjs/common';
@Injectable()
export abstract class IConfigurationRepository {
abstract get(key: string): Promise<string>;
abstract set(key: string, value: string): Promise<void>;
abstract del(key: string): Promise<void>;
}

View File

@@ -1,18 +0,0 @@
import { CommandHandler } from '@nestjs/cqrs';
import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository';
import { DeleteConfigurationCommand } from '../../commands/delete-configuration.command';
@CommandHandler(DeleteConfigurationCommand)
export class DeleteConfigurationUseCase {
constructor(private configurationRepository: RedisConfigurationRepository) {}
execute = async (
deleteConfigurationCommand: DeleteConfigurationCommand,
): Promise<void> => {
await this.configurationRepository.del(
`${deleteConfigurationCommand.deleteConfigurationRequest.domain}
:
${deleteConfigurationCommand.deleteConfigurationRequest.key}`,
);
};
}

View File

@@ -1,15 +0,0 @@
import { QueryHandler } from '@nestjs/cqrs';
import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository';
import { GetConfigurationQuery } from '../../queries/get-configuration.query';
@QueryHandler(GetConfigurationQuery)
export class GetConfigurationUseCase {
constructor(private configurationRepository: RedisConfigurationRepository) {}
execute = async (
getConfigurationQuery: GetConfigurationQuery,
): Promise<string> =>
this.configurationRepository.get(
`${getConfigurationQuery.domain}:${getConfigurationQuery.key}`,
);
}

View File

@@ -1,19 +0,0 @@
import { CommandHandler } from '@nestjs/cqrs';
import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository';
import { SetConfigurationCommand } from '../../commands/set-configuration.command';
@CommandHandler(SetConfigurationCommand)
export class SetConfigurationUseCase {
constructor(private configurationRepository: RedisConfigurationRepository) {}
execute = async (
setConfigurationCommand: SetConfigurationCommand,
): Promise<void> => {
await this.configurationRepository.set(
`${setConfigurationCommand.setConfigurationRequest.domain}
:
${setConfigurationCommand.setConfigurationRequest.key}`,
setConfigurationCommand.setConfigurationRequest.value,
);
};
}

View File

@@ -1,9 +0,0 @@
export class GetConfigurationQuery {
readonly domain: string;
readonly key: string;
constructor(domain: string, key: string) {
this.domain = domain;
this.key = key;
}
}

View File

@@ -1,49 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository';
import { DeleteConfigurationCommand } from '../../commands/delete-configuration.command';
import { DeleteConfigurationRequest } from '../../domain/dtos/delete-configuration.request';
import { DeleteConfigurationUseCase } from '../../domain/usecases/delete-configuration.usecase';
const mockRedisConfigurationRepository = {
del: jest.fn().mockResolvedValue(undefined),
};
describe('DeleteConfigurationUseCase', () => {
let deleteConfigurationUseCase: DeleteConfigurationUseCase;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: RedisConfigurationRepository,
useValue: mockRedisConfigurationRepository,
},
DeleteConfigurationUseCase,
],
}).compile();
deleteConfigurationUseCase = module.get<DeleteConfigurationUseCase>(
DeleteConfigurationUseCase,
);
});
it('should be defined', () => {
expect(deleteConfigurationUseCase).toBeDefined();
});
describe('execute', () => {
it('should delete a key', async () => {
jest.spyOn(mockRedisConfigurationRepository, 'del');
const deleteConfigurationRequest: DeleteConfigurationRequest = {
domain: 'my-domain',
key: 'my-key',
};
await deleteConfigurationUseCase.execute(
new DeleteConfigurationCommand(deleteConfigurationRequest),
);
expect(mockRedisConfigurationRepository.del).toHaveBeenCalledTimes(1);
});
});
});

View File

@@ -1,43 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository';
import { GetConfigurationUseCase } from '../../domain/usecases/get-configuration.usecase';
import { GetConfigurationQuery } from '../../queries/get-configuration.query';
const mockRedisConfigurationRepository = {
get: jest.fn().mockResolvedValue('my-value'),
};
describe('GetConfigurationUseCase', () => {
let getConfigurationUseCase: GetConfigurationUseCase;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: RedisConfigurationRepository,
useValue: mockRedisConfigurationRepository,
},
GetConfigurationUseCase,
],
}).compile();
getConfigurationUseCase = module.get<GetConfigurationUseCase>(
GetConfigurationUseCase,
);
});
it('should be defined', () => {
expect(getConfigurationUseCase).toBeDefined();
});
describe('execute', () => {
it('should get a value for a key', async () => {
const value: string = await getConfigurationUseCase.execute(
new GetConfigurationQuery('my-domain', 'my-key'),
);
expect(value).toBe('my-value');
});
});
});

View File

@@ -1,47 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository';
import { getRedisToken } from '@liaoliaots/nestjs-redis';
const mockRedis = {
get: jest.fn().mockResolvedValue('myValue'),
set: jest.fn().mockImplementation(),
del: jest.fn().mockImplementation(),
};
describe('RedisConfigurationRepository', () => {
let redisConfigurationRepository: RedisConfigurationRepository;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: getRedisToken('default'),
useValue: mockRedis,
},
RedisConfigurationRepository,
],
}).compile();
redisConfigurationRepository = module.get<RedisConfigurationRepository>(
RedisConfigurationRepository,
);
});
it('should be defined', () => {
expect(redisConfigurationRepository).toBeDefined();
});
describe('interact', () => {
it('should get a value', async () => {
expect(await redisConfigurationRepository.get('myKey')).toBe('myValue');
});
it('should set a value', async () => {
expect(
await redisConfigurationRepository.set('myKey', 'myValue'),
).toBeUndefined();
});
it('should delete a value', async () => {
expect(await redisConfigurationRepository.del('myKey')).toBeUndefined();
});
});
});

View File

@@ -1,50 +0,0 @@
import { Test, TestingModule } from '@nestjs/testing';
import { RedisConfigurationRepository } from '../../adapters/secondaries/redis-configuration.repository';
import { SetConfigurationCommand } from '../../commands/set-configuration.command';
import { SetConfigurationRequest } from '../../domain/dtos/set-configuration.request';
import { SetConfigurationUseCase } from '../../domain/usecases/set-configuration.usecase';
const mockRedisConfigurationRepository = {
set: jest.fn().mockResolvedValue(undefined),
};
describe('SetConfigurationUseCase', () => {
let setConfigurationUseCase: SetConfigurationUseCase;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: RedisConfigurationRepository,
useValue: mockRedisConfigurationRepository,
},
SetConfigurationUseCase,
],
}).compile();
setConfigurationUseCase = module.get<SetConfigurationUseCase>(
SetConfigurationUseCase,
);
});
it('should be defined', () => {
expect(setConfigurationUseCase).toBeDefined();
});
describe('execute', () => {
it('should set a value for a key', async () => {
jest.spyOn(mockRedisConfigurationRepository, 'set');
const setConfigurationRequest: SetConfigurationRequest = {
domain: 'my-domain',
key: 'my-key',
value: 'my-value',
};
await setConfigurationUseCase.execute(
new SetConfigurationCommand(setConfigurationRequest),
);
expect(mockRedisConfigurationRepository.set).toHaveBeenCalledTimes(1);
});
});
});