Merge branch 'refactorMessager' into 'main'

Refactor messager

See merge request v3/service/user!22
This commit is contained in:
Sylvain Briat 2023-03-15 16:42:49 +00:00
commit 9ce7dd6a0f
16 changed files with 84 additions and 134 deletions

View File

@ -8,6 +8,7 @@ DATABASE_URL="postgresql://user:user@v3-user-db:5432/user?schema=public"
# RABBIT MQ
RMQ_URI=amqp://v3-broker:5672
RMQ_EXCHANGE=mobicoop
# POSTGRES
POSTGRES_IMAGE=postgres:15.0

View File

@ -16,9 +16,7 @@ export class ConfigurationMessagerController {
) {}
@RabbitSubscribe({
exchange: 'configuration',
routingKey: ['create', 'update'],
queue: 'user-configuration-update',
name: 'setConfiguration',
})
public async setConfigurationHandler(message: string) {
const configuration: Configuration = JSON.parse(message);
@ -38,9 +36,7 @@ export class ConfigurationMessagerController {
}
@RabbitSubscribe({
exchange: 'configuration',
routingKey: 'delete',
queue: 'user-configuration-delete',
name: 'deleteConfiguration',
})
public async configurationDeletedHandler(message: string) {
const deletedConfiguration: Configuration = JSON.parse(message);
@ -58,9 +54,7 @@ export class ConfigurationMessagerController {
}
@RabbitSubscribe({
exchange: 'configuration',
routingKey: 'propagate',
queue: 'user-configuration-propagate',
name: 'propagateConfiguration',
})
public async propagateConfigurationsHandler(message: string) {
const configurations: Array<Configuration> = JSON.parse(message);

View File

@ -32,10 +32,24 @@ import { SetConfigurationUseCase } from './domain/usecases/set-configuration.use
): Promise<RabbitMQConfig> => ({
exchanges: [
{
name: 'configuration',
name: configService.get<string>('RMQ_EXCHANGE'),
type: 'topic',
},
],
handlers: {
setConfiguration: {
exchange: configService.get<string>('RMQ_EXCHANGE'),
routingKey: ['configuration.create', 'configuration.update'],
},
deleteConfiguration: {
exchange: configService.get<string>('RMQ_EXCHANGE'),
routingKey: 'configuration.delete',
},
propagateConfiguration: {
exchange: configService.get<string>('RMQ_EXCHANGE'),
routingKey: 'configuration.propagate',
},
},
uri: configService.get<string>('RMQ_URI'),
connectionInitOptions: { wait: false },
enableControllerDiscovery: true,

View File

@ -1,11 +1,15 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { IMessageBroker } from '../../domain/interfaces/message-broker';
@Injectable()
export class LoggingMessager extends IMessageBroker {
constructor(private readonly _amqpConnection: AmqpConnection) {
super('logging');
export class Messager extends IMessageBroker {
constructor(
private readonly _amqpConnection: AmqpConnection,
configService: ConfigService,
) {
super(configService.get<string>('RMQ_EXCHANGE'));
}
publish(routingKey: string, message: string): void {

View File

@ -1,14 +0,0 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Injectable } from '@nestjs/common';
import { IMessageBroker } from '../../domain/interfaces/message-broker';
@Injectable()
export class UserMessager extends IMessageBroker {
constructor(private readonly _amqpConnection: AmqpConnection) {
super('user');
}
publish(routingKey: string, message: string): void {
this._amqpConnection.publish(this.exchange, routingKey, message);
}
}

View File

@ -1,8 +1,7 @@
import { Mapper } from '@automapper/core';
import { InjectMapper } from '@automapper/nestjs';
import { CommandHandler } from '@nestjs/cqrs';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { Messager } from '../../adapters/secondaries/messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { CreateUserCommand } from '../../commands/create-user.command';
import { CreateUserRequest } from '../dtos/create-user.request';
@ -12,8 +11,7 @@ import { User } from '../entities/user';
export class CreateUserUseCase {
constructor(
private readonly _repository: UsersRepository,
private readonly _userMessager: UserMessager,
private readonly _loggingMessager: LoggingMessager,
private readonly _messager: Messager,
@InjectMapper() private readonly _mapper: Mapper,
) {}
@ -26,15 +24,15 @@ export class CreateUserUseCase {
try {
const user = await this._repository.create(entity);
this._userMessager.publish('create', JSON.stringify(user));
this._loggingMessager.publish('user.create.info', JSON.stringify(user));
this._messager.publish('user.create', JSON.stringify(user));
this._messager.publish('logging.user.create.info', JSON.stringify(user));
return user;
} catch (error) {
let key = 'user.create.crit';
let key = 'logging.user.create.crit';
if (error.message.includes('Already exists')) {
key = 'user.create.warning';
key = 'logging.user.create.warning';
}
this._loggingMessager.publish(
this._messager.publish(
key,
JSON.stringify({
command,

View File

@ -1,6 +1,5 @@
import { CommandHandler } from '@nestjs/cqrs';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { Messager } from '../../adapters/secondaries/messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { DeleteUserCommand } from '../../commands/delete-user.command';
import { User } from '../entities/user';
@ -9,22 +8,24 @@ import { User } from '../entities/user';
export class DeleteUserUseCase {
constructor(
private readonly _repository: UsersRepository,
private readonly _userMessager: UserMessager,
private readonly _loggingMessager: LoggingMessager,
private readonly _messager: Messager,
) {}
async execute(command: DeleteUserCommand): Promise<User> {
try {
const user = await this._repository.delete(command.uuid);
this._userMessager.publish('delete', JSON.stringify({ uuid: user.uuid }));
this._loggingMessager.publish(
'user.delete.info',
this._messager.publish(
'user.delete',
JSON.stringify({ uuid: user.uuid }),
);
this._messager.publish(
'logging.user.delete.info',
JSON.stringify({ uuid: user.uuid }),
);
return user;
} catch (error) {
this._loggingMessager.publish(
'user.delete.crit',
this._messager.publish(
'logging.user.delete.crit',
JSON.stringify({
command,
error,

View File

@ -1,6 +1,6 @@
import { NotFoundException } from '@nestjs/common';
import { QueryHandler } from '@nestjs/cqrs';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
import { Messager } from '../../adapters/secondaries/messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { FindUserByUuidQuery } from '../../queries/find-user-by-uuid.query';
import { User } from '../entities/user';
@ -9,7 +9,7 @@ import { User } from '../entities/user';
export class FindUserByUuidUseCase {
constructor(
private readonly _repository: UsersRepository,
private readonly _loggingMessager: LoggingMessager,
private readonly _messager: Messager,
) {}
async execute(findUserByUuid: FindUserByUuidQuery): Promise<User> {
@ -18,8 +18,8 @@ export class FindUserByUuidUseCase {
if (!user) throw new NotFoundException();
return user;
} catch (error) {
this._loggingMessager.publish(
'user.read.warning',
this._messager.publish(
'logging.user.read.warning',
JSON.stringify({
query: findUserByUuid,
error,

View File

@ -1,8 +1,7 @@
import { Mapper } from '@automapper/core';
import { InjectMapper } from '@automapper/nestjs';
import { CommandHandler } from '@nestjs/cqrs';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { Messager } from '../../adapters/secondaries/messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { UpdateUserCommand } from '../../commands/update-user.command';
import { UpdateUserRequest } from '../dtos/update-user.request';
@ -12,8 +11,7 @@ import { User } from '../entities/user';
export class UpdateUserUseCase {
constructor(
private readonly _repository: UsersRepository,
private readonly _userMessager: UserMessager,
private readonly _loggingMessager: LoggingMessager,
private readonly _messager: Messager,
@InjectMapper() private readonly _mapper: Mapper,
) {}
@ -29,18 +27,18 @@ export class UpdateUserUseCase {
command.updateUserRequest.uuid,
entity,
);
this._userMessager.publish(
'update',
this._messager.publish(
'user.update',
JSON.stringify(command.updateUserRequest),
);
this._loggingMessager.publish(
'user.update.info',
this._messager.publish(
'logging.user.update.info',
JSON.stringify(command.updateUserRequest),
);
return user;
} catch (error) {
this._loggingMessager.publish(
'user.update.crit',
this._messager.publish(
'logging.user.update.crit',
JSON.stringify({
command,
error,

View File

@ -1,8 +1,7 @@
import { classes } from '@automapper/classes';
import { AutomapperModule } from '@automapper/nestjs';
import { Test, TestingModule } from '@nestjs/testing';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { Messager } from '../../adapters/secondaries/messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { CreateUserCommand } from '../../commands/create-user.command';
import { CreateUserRequest } from '../../domain/dtos/create-user.request';
@ -50,11 +49,7 @@ describe('CreateUserUseCase', () => {
CreateUserUseCase,
UserProfile,
{
provide: UserMessager,
useValue: mockMessager,
},
{
provide: LoggingMessager,
provide: Messager,
useValue: mockMessager,
},
],

View File

@ -1,6 +1,5 @@
import { Test, TestingModule } from '@nestjs/testing';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { Messager } from '../../adapters/secondaries/messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { DeleteUserCommand } from '../../commands/delete-user.command';
import { DeleteUserUseCase } from '../../domain/usecases/delete-user.usecase';
@ -63,11 +62,7 @@ describe('DeleteUserUseCase', () => {
},
DeleteUserUseCase,
{
provide: UserMessager,
useValue: mockMessager,
},
{
provide: LoggingMessager,
provide: Messager,
useValue: mockMessager,
},
],

View File

@ -1,6 +1,6 @@
import { NotFoundException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
import { Messager } from '../../adapters/secondaries/messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { FindUserByUuidRequest } from '../../domain/dtos/find-user-by-uuid.request';
import { FindUserByUuidUseCase } from '../../domain/usecases/find-user-by-uuid.usecase';
@ -42,7 +42,7 @@ describe('FindUserByUuidUseCase', () => {
useValue: mockUserRepository,
},
{
provide: LoggingMessager,
provide: Messager,
useValue: mockMessager,
},
FindUserByUuidUseCase,

View File

@ -1,36 +1,47 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
import { Messager } from '../../adapters/secondaries/messager';
const mockAmqpConnection = {
publish: jest.fn().mockImplementation(),
};
describe('LoggingMessager', () => {
let loggingMessager: LoggingMessager;
const mockConfigService = {
get: jest.fn().mockResolvedValue({
RMQ_EXCHANGE: 'mobicoop',
}),
};
describe('Messager', () => {
let messager: Messager;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [],
providers: [
LoggingMessager,
Messager,
{
provide: AmqpConnection,
useValue: mockAmqpConnection,
},
{
provide: ConfigService,
useValue: mockConfigService,
},
],
}).compile();
loggingMessager = module.get<LoggingMessager>(LoggingMessager);
messager = module.get<Messager>(Messager);
});
it('should be defined', () => {
expect(LoggingMessager).toBeDefined();
expect(messager).toBeDefined();
});
it('should publish a message', async () => {
jest.spyOn(mockAmqpConnection, 'publish');
await loggingMessager.publish('user.create.info', 'my-test');
messager.publish('user.create.info', 'my-test');
expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1);
});
});

View File

@ -1,8 +1,7 @@
import { classes } from '@automapper/classes';
import { AutomapperModule } from '@automapper/nestjs';
import { Test, TestingModule } from '@nestjs/testing';
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
import { UserMessager } from '../../adapters/secondaries/user.messager';
import { Messager } from '../../adapters/secondaries/messager';
import { UsersRepository } from '../../adapters/secondaries/users.repository';
import { UpdateUserCommand } from '../../commands/update-user.command';
import { UpdateUserRequest } from '../../domain/dtos/update-user.request';
@ -55,11 +54,7 @@ describe('UpdateUserUseCase', () => {
UpdateUserUseCase,
UserProfile,
{
provide: UserMessager,
useValue: mockMessager,
},
{
provide: LoggingMessager,
provide: Messager,
useValue: mockMessager,
},
],

View File

@ -1,36 +0,0 @@
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
import { Test, TestingModule } from '@nestjs/testing';
import { UserMessager } from '../../adapters/secondaries/user.messager';
const mockAmqpConnection = {
publish: jest.fn().mockImplementation(),
};
describe('UserMessager', () => {
let userMessager: UserMessager;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [],
providers: [
UserMessager,
{
provide: AmqpConnection,
useValue: mockAmqpConnection,
},
],
}).compile();
userMessager = module.get<UserMessager>(UserMessager);
});
it('should be defined', () => {
expect(userMessager).toBeDefined();
});
it('should publish a message', async () => {
jest.spyOn(mockAmqpConnection, 'publish');
await userMessager.publish('user.create.info', 'my-test');
expect(mockAmqpConnection.publish).toHaveBeenCalledTimes(1);
});
});

View File

@ -6,8 +6,7 @@ import { CqrsModule } from '@nestjs/cqrs';
import { redisStore } from 'cache-manager-ioredis-yet';
import { DatabaseModule } from '../database/database.module';
import { UsersController } from './adapters/primaries/users.controller';
import { LoggingMessager } from './adapters/secondaries/logging.messager';
import { UserMessager } from './adapters/secondaries/user.messager';
import { Messager } from './adapters/secondaries/messager';
import { UsersRepository } from './adapters/secondaries/users.repository';
import { CreateUserUseCase } from './domain/usecases/create-user.usecase';
import { DeleteUserUseCase } from './domain/usecases/delete-user.usecase';
@ -25,11 +24,7 @@ import { UserProfile } from './mappers/user.profile';
useFactory: async (configService: ConfigService) => ({
exchanges: [
{
name: 'user',
type: 'topic',
},
{
name: 'logging',
name: configService.get<string>('RMQ_EXCHANGE'),
type: 'topic',
},
],
@ -54,8 +49,7 @@ import { UserProfile } from './mappers/user.profile';
providers: [
UserProfile,
UsersRepository,
UserMessager,
LoggingMessager,
Messager,
FindAllUsersUseCase,
FindUserByUuidUseCase,
CreateUserUseCase,