Merge branch 'logging' into 'main'
Logging See merge request mobicoop/lab/v3/services/auth!3
This commit is contained in:
commit
5d1f92cb9e
2
.env
2
.env
|
@ -7,8 +7,6 @@ SERVICE_PORT=5002
|
||||||
DATABASE_URL="postgresql://auth:auth@v3-auth-db:5432/auth?schema=public"
|
DATABASE_URL="postgresql://auth:auth@v3-auth-db:5432/auth?schema=public"
|
||||||
|
|
||||||
# RABBIT MQ
|
# RABBIT MQ
|
||||||
RMQ_EXCHANGE_NAME=user
|
|
||||||
RMQ_EXCHANGE_TYPE=topic
|
|
||||||
RMQ_URI=amqp://v3-gateway-broker:5672
|
RMQ_URI=amqp://v3-gateway-broker:5672
|
||||||
|
|
||||||
# POSTGRES
|
# POSTGRES
|
||||||
|
|
|
@ -7,8 +7,6 @@ SERVICE_PORT=5002
|
||||||
DATABASE_URL="postgresql://auth:auth@db:5432/auth?schema=public"
|
DATABASE_URL="postgresql://auth:auth@db:5432/auth?schema=public"
|
||||||
|
|
||||||
# RABBIT MQ
|
# RABBIT MQ
|
||||||
RMQ_EXCHANGES=user
|
|
||||||
RMQ_EXCHANGE_TYPE=topic
|
|
||||||
RMQ_URI=amqp://localhost:5672
|
RMQ_URI=amqp://localhost:5672
|
||||||
|
|
||||||
# POSTGRES
|
# POSTGRES
|
||||||
|
|
|
@ -13,26 +13,27 @@ export class AuthMessagerController {
|
||||||
|
|
||||||
@RabbitSubscribe({
|
@RabbitSubscribe({
|
||||||
exchange: 'user',
|
exchange: 'user',
|
||||||
routingKey: 'user.update',
|
routingKey: 'update',
|
||||||
|
queue: 'auth-user-update',
|
||||||
})
|
})
|
||||||
public async userUpdatedHandler(message: string) {
|
public async userUpdatedHandler(message: string) {
|
||||||
const updatedUser = JSON.parse(message);
|
const updatedUser = JSON.parse(message);
|
||||||
if (!updatedUser.hasOwnProperty('uuid')) throw new Error();
|
if (!updatedUser.hasOwnProperty('uuid')) throw new Error();
|
||||||
if (updatedUser.hasOwnProperty('email')) {
|
if (updatedUser.hasOwnProperty('email') && updatedUser.email) {
|
||||||
const updateUsernameRequest = new UpdateUsernameRequest();
|
const updateUsernameRequest = new UpdateUsernameRequest();
|
||||||
updateUsernameRequest.uuid = updatedUser.uuid;
|
updateUsernameRequest.uuid = updatedUser.uuid;
|
||||||
updateUsernameRequest.username = updatedUser.email;
|
updateUsernameRequest.username = updatedUser.email;
|
||||||
updateUsernameRequest.type = Type.EMAIL;
|
updateUsernameRequest.type = Type.EMAIL;
|
||||||
this._commandBus.execute(
|
await this._commandBus.execute(
|
||||||
new UpdateUsernameCommand(updateUsernameRequest),
|
new UpdateUsernameCommand(updateUsernameRequest),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (updatedUser.hasOwnProperty('phone')) {
|
if (updatedUser.hasOwnProperty('phone') && updatedUser.phone) {
|
||||||
const updateUsernameRequest = new UpdateUsernameRequest();
|
const updateUsernameRequest = new UpdateUsernameRequest();
|
||||||
updateUsernameRequest.uuid = updatedUser.uuid;
|
updateUsernameRequest.uuid = updatedUser.uuid;
|
||||||
updateUsernameRequest.username = updatedUser.phone;
|
updateUsernameRequest.username = updatedUser.phone;
|
||||||
updateUsernameRequest.type = Type.PHONE;
|
updateUsernameRequest.type = Type.PHONE;
|
||||||
this._commandBus.execute(
|
await this._commandBus.execute(
|
||||||
new UpdateUsernameCommand(updateUsernameRequest),
|
new UpdateUsernameCommand(updateUsernameRequest),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -40,13 +41,14 @@ export class AuthMessagerController {
|
||||||
|
|
||||||
@RabbitSubscribe({
|
@RabbitSubscribe({
|
||||||
exchange: 'user',
|
exchange: 'user',
|
||||||
routingKey: 'user.delete',
|
routingKey: 'delete',
|
||||||
|
queue: 'auth-user-delete',
|
||||||
})
|
})
|
||||||
public async userDeletedHandler(message: string) {
|
public async userDeletedHandler(message: string) {
|
||||||
const deletedUser = JSON.parse(message);
|
const deletedUser = JSON.parse(message);
|
||||||
if (!deletedUser.hasOwnProperty('uuid')) throw new Error();
|
if (!deletedUser.hasOwnProperty('uuid')) throw new Error();
|
||||||
const deleteAuthRequest = new DeleteAuthRequest();
|
const deleteAuthRequest = new DeleteAuthRequest();
|
||||||
deleteAuthRequest.uuid = deletedUser.uuid;
|
deleteAuthRequest.uuid = deletedUser.uuid;
|
||||||
this._commandBus.execute(new DeleteAuthCommand(deleteAuthRequest));
|
await this._commandBus.execute(new DeleteAuthCommand(deleteAuthRequest));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Mapper } from '@automapper/core';
|
import { Mapper } from '@automapper/core';
|
||||||
import { InjectMapper } from '@automapper/nestjs';
|
import { InjectMapper } from '@automapper/nestjs';
|
||||||
import { Controller } from '@nestjs/common';
|
import { Controller, UsePipes } from '@nestjs/common';
|
||||||
import { CommandBus, QueryBus } from '@nestjs/cqrs';
|
import { CommandBus, QueryBus } from '@nestjs/cqrs';
|
||||||
import { GrpcMethod, RpcException } from '@nestjs/microservices';
|
import { GrpcMethod, RpcException } from '@nestjs/microservices';
|
||||||
import { DatabaseException } from 'src/modules/database/src/exceptions/DatabaseException';
|
import { DatabaseException } from 'src/modules/database/src/exceptions/DatabaseException';
|
||||||
|
@ -21,8 +21,15 @@ import { Auth } from '../../domain/entities/auth';
|
||||||
import { Username } from '../../domain/entities/username';
|
import { Username } from '../../domain/entities/username';
|
||||||
import { ValidateAuthQuery } from '../../queries/validate-auth.query';
|
import { ValidateAuthQuery } from '../../queries/validate-auth.query';
|
||||||
import { AuthPresenter } from './auth.presenter';
|
import { AuthPresenter } from './auth.presenter';
|
||||||
|
import { RpcValidationPipe } from './rpc.validation-pipe';
|
||||||
import { UsernamePresenter } from './username.presenter';
|
import { UsernamePresenter } from './username.presenter';
|
||||||
|
|
||||||
|
@UsePipes(
|
||||||
|
new RpcValidationPipe({
|
||||||
|
whitelist: true,
|
||||||
|
forbidUnknownValues: false,
|
||||||
|
}),
|
||||||
|
)
|
||||||
@Controller()
|
@Controller()
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
constructor(
|
constructor(
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { Injectable, ValidationPipe } from '@nestjs/common';
|
||||||
|
import { RpcException } from '@nestjs/microservices';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RpcValidationPipe extends ValidationPipe {
|
||||||
|
createExceptionFactory() {
|
||||||
|
return (validationErrors = []) => {
|
||||||
|
return new RpcException({
|
||||||
|
code: 3,
|
||||||
|
message: this.flattenValidationErrors(validationErrors),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { IMessageBroker } from '../../domain/interfaces/message-broker';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthMessager extends IMessageBroker {
|
||||||
|
constructor(private readonly _amqpConnection: AmqpConnection) {
|
||||||
|
super('auth');
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(routingKey: string, message: string): void {
|
||||||
|
this._amqpConnection.publish(this.exchange, routingKey, message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { AmqpConnection } from '@golevelup/nestjs-rabbitmq';
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { IMessageBroker } from '../../domain/interfaces/message-broker';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class LoggingMessager extends IMessageBroker {
|
||||||
|
constructor(private readonly _amqpConnection: AmqpConnection) {
|
||||||
|
super('logging');
|
||||||
|
}
|
||||||
|
|
||||||
|
publish(routingKey: string, message: string): void {
|
||||||
|
this._amqpConnection.publish(this.exchange, routingKey, message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,8 +25,12 @@ import { AuthMessagerController } from './adapters/primaries/auth-messager.contr
|
||||||
useFactory: async (configService: ConfigService) => ({
|
useFactory: async (configService: ConfigService) => ({
|
||||||
exchanges: [
|
exchanges: [
|
||||||
{
|
{
|
||||||
name: configService.get<string>('RMQ_EXCHANGE_NAME'),
|
name: 'user',
|
||||||
type: configService.get<string>('RMQ_EXCHANGE_TYPE'),
|
type: 'topic',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'logging',
|
||||||
|
type: 'topic',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
uri: configService.get<string>('RMQ_URI'),
|
uri: configService.get<string>('RMQ_URI'),
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export abstract class IMessageBroker {
|
||||||
|
exchange: string;
|
||||||
|
|
||||||
|
constructor(exchange: string) {
|
||||||
|
this.exchange = exchange;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract publish(routingKey: string, message: string): void;
|
||||||
|
}
|
|
@ -1,11 +1,15 @@
|
||||||
import { CommandHandler } from '@nestjs/cqrs';
|
import { CommandHandler } from '@nestjs/cqrs';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
||||||
import { AddUsernameCommand } from '../../commands/add-username.command';
|
import { AddUsernameCommand } from '../../commands/add-username.command';
|
||||||
import { Username } from '../entities/username';
|
import { Username } from '../entities/username';
|
||||||
|
|
||||||
@CommandHandler(AddUsernameCommand)
|
@CommandHandler(AddUsernameCommand)
|
||||||
export class AddUsernameUseCase {
|
export class AddUsernameUseCase {
|
||||||
constructor(private readonly _usernameRepository: UsernameRepository) {}
|
constructor(
|
||||||
|
private readonly _usernameRepository: UsernameRepository,
|
||||||
|
private readonly _loggingMessager: LoggingMessager,
|
||||||
|
) {}
|
||||||
|
|
||||||
async execute(command: AddUsernameCommand): Promise<Username> {
|
async execute(command: AddUsernameCommand): Promise<Username> {
|
||||||
const { uuid, username, type } = command.addUsernameRequest;
|
const { uuid, username, type } = command.addUsernameRequest;
|
||||||
|
@ -15,8 +19,15 @@ export class AddUsernameUseCase {
|
||||||
type,
|
type,
|
||||||
username,
|
username,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
throw e;
|
this._loggingMessager.publish(
|
||||||
|
'auth.username.add.warning',
|
||||||
|
JSON.stringify({
|
||||||
|
command,
|
||||||
|
error,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,14 @@ import { CreateAuthCommand } from '../../commands/create-auth.command';
|
||||||
import { Auth } from '../entities/auth';
|
import { Auth } from '../entities/auth';
|
||||||
import * as bcrypt from 'bcrypt';
|
import * as bcrypt from 'bcrypt';
|
||||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
|
|
||||||
@CommandHandler(CreateAuthCommand)
|
@CommandHandler(CreateAuthCommand)
|
||||||
export class CreateAuthUseCase {
|
export class CreateAuthUseCase {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _authRepository: AuthRepository,
|
private readonly _authRepository: AuthRepository,
|
||||||
private readonly _usernameRepository: UsernameRepository,
|
private readonly _usernameRepository: UsernameRepository,
|
||||||
|
private readonly _loggingMessager: LoggingMessager,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(command: CreateAuthCommand): Promise<Auth> {
|
async execute(command: CreateAuthCommand): Promise<Auth> {
|
||||||
|
@ -28,8 +30,15 @@ export class CreateAuthUseCase {
|
||||||
});
|
});
|
||||||
|
|
||||||
return auth;
|
return auth;
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
throw e;
|
this._loggingMessager.publish(
|
||||||
|
'auth.create.critical',
|
||||||
|
JSON.stringify({
|
||||||
|
command,
|
||||||
|
error,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { CommandHandler } from '@nestjs/cqrs';
|
import { CommandHandler } from '@nestjs/cqrs';
|
||||||
import { AuthRepository } from '../../adapters/secondaries/auth.repository';
|
import { AuthRepository } from '../../adapters/secondaries/auth.repository';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
||||||
import { DeleteAuthCommand } from '../../commands/delete-auth.command';
|
import { DeleteAuthCommand } from '../../commands/delete-auth.command';
|
||||||
|
|
||||||
|
@ -8,6 +9,7 @@ export class DeleteAuthUseCase {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _authRepository: AuthRepository,
|
private readonly _authRepository: AuthRepository,
|
||||||
private readonly _usernameRepository: UsernameRepository,
|
private readonly _usernameRepository: UsernameRepository,
|
||||||
|
private readonly _loggingMessager: LoggingMessager,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(command: DeleteAuthCommand) {
|
async execute(command: DeleteAuthCommand) {
|
||||||
|
@ -18,8 +20,15 @@ export class DeleteAuthUseCase {
|
||||||
return await this._authRepository.delete({
|
return await this._authRepository.delete({
|
||||||
uuid: command.deleteAuthRequest.uuid,
|
uuid: command.deleteAuthRequest.uuid,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
throw e;
|
this._loggingMessager.publish(
|
||||||
|
'auth.delete.critical',
|
||||||
|
JSON.stringify({
|
||||||
|
command,
|
||||||
|
error,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
import { UnauthorizedException } from '@nestjs/common';
|
import { UnauthorizedException } from '@nestjs/common';
|
||||||
import { CommandHandler } from '@nestjs/cqrs';
|
import { CommandHandler } from '@nestjs/cqrs';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
||||||
import { DeleteUsernameCommand } from '../../commands/delete-username.command';
|
import { DeleteUsernameCommand } from '../../commands/delete-username.command';
|
||||||
|
|
||||||
@CommandHandler(DeleteUsernameCommand)
|
@CommandHandler(DeleteUsernameCommand)
|
||||||
export class DeleteUsernameUseCase {
|
export class DeleteUsernameUseCase {
|
||||||
constructor(private readonly _usernameRepository: UsernameRepository) {}
|
constructor(
|
||||||
|
private readonly _usernameRepository: UsernameRepository,
|
||||||
|
private readonly _loggingMessager: LoggingMessager,
|
||||||
|
) {}
|
||||||
|
|
||||||
async execute(command: DeleteUsernameCommand) {
|
async execute(command: DeleteUsernameCommand) {
|
||||||
try {
|
try {
|
||||||
|
@ -20,8 +24,15 @@ export class DeleteUsernameUseCase {
|
||||||
return await this._usernameRepository.delete({ username });
|
return await this._usernameRepository.delete({ username });
|
||||||
}
|
}
|
||||||
throw new UnauthorizedException();
|
throw new UnauthorizedException();
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
throw e;
|
this._loggingMessager.publish(
|
||||||
|
'auth.username.delete.warning',
|
||||||
|
JSON.stringify({
|
||||||
|
command,
|
||||||
|
error,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,14 @@ import { AuthRepository } from '../../adapters/secondaries/auth.repository';
|
||||||
import { Auth } from '../entities/auth';
|
import { Auth } from '../entities/auth';
|
||||||
import * as bcrypt from 'bcrypt';
|
import * as bcrypt from 'bcrypt';
|
||||||
import { UpdatePasswordCommand } from '../../commands/update-password.command';
|
import { UpdatePasswordCommand } from '../../commands/update-password.command';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
|
|
||||||
@CommandHandler(UpdatePasswordCommand)
|
@CommandHandler(UpdatePasswordCommand)
|
||||||
export class UpdatePasswordUseCase {
|
export class UpdatePasswordUseCase {
|
||||||
constructor(private readonly _authRepository: AuthRepository) {}
|
constructor(
|
||||||
|
private readonly _authRepository: AuthRepository,
|
||||||
|
private readonly _loggingMessager: LoggingMessager,
|
||||||
|
) {}
|
||||||
|
|
||||||
async execute(command: UpdatePasswordCommand): Promise<Auth> {
|
async execute(command: UpdatePasswordCommand): Promise<Auth> {
|
||||||
const { uuid, password } = command.updatePasswordRequest;
|
const { uuid, password } = command.updatePasswordRequest;
|
||||||
|
@ -16,8 +20,15 @@ export class UpdatePasswordUseCase {
|
||||||
return await this._authRepository.update(uuid, {
|
return await this._authRepository.update(uuid, {
|
||||||
password: hash,
|
password: hash,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
throw e;
|
this._loggingMessager.publish(
|
||||||
|
'auth.password.update.warning',
|
||||||
|
JSON.stringify({
|
||||||
|
command,
|
||||||
|
error,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,33 @@
|
||||||
import { CommandHandler } from '@nestjs/cqrs';
|
import { Mapper } from '@automapper/core';
|
||||||
|
import { InjectMapper } from '@automapper/nestjs';
|
||||||
|
import { BadRequestException } from '@nestjs/common';
|
||||||
|
import { CommandBus, CommandHandler } from '@nestjs/cqrs';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
||||||
|
import { AddUsernameCommand } from '../../commands/add-username.command';
|
||||||
import { UpdateUsernameCommand } from '../../commands/update-username.command';
|
import { UpdateUsernameCommand } from '../../commands/update-username.command';
|
||||||
|
import { AddUsernameRequest } from '../dtos/add-username.request';
|
||||||
|
import { UpdateUsernameRequest } from '../dtos/update-username.request';
|
||||||
import { Username } from '../entities/username';
|
import { Username } from '../entities/username';
|
||||||
|
|
||||||
@CommandHandler(UpdateUsernameCommand)
|
@CommandHandler(UpdateUsernameCommand)
|
||||||
export class UpdateUsernameUseCase {
|
export class UpdateUsernameUseCase {
|
||||||
constructor(private readonly _usernameRepository: UsernameRepository) {}
|
constructor(
|
||||||
|
private readonly _usernameRepository: UsernameRepository,
|
||||||
|
private readonly _commandBus: CommandBus,
|
||||||
|
@InjectMapper() private readonly _mapper: Mapper,
|
||||||
|
private readonly _loggingMessager: LoggingMessager,
|
||||||
|
) {}
|
||||||
|
|
||||||
async execute(command: UpdateUsernameCommand): Promise<Username> {
|
async execute(command: UpdateUsernameCommand): Promise<Username> {
|
||||||
const { uuid, username, type } = command.updateUsernameRequest;
|
const { uuid, username, type } = command.updateUsernameRequest;
|
||||||
|
if (!username) throw new BadRequestException();
|
||||||
|
// update username if it exists, otherwise create it
|
||||||
|
const existingUsername = await this._usernameRepository.findOne({
|
||||||
|
uuid,
|
||||||
|
type,
|
||||||
|
});
|
||||||
|
if (existingUsername) {
|
||||||
try {
|
try {
|
||||||
return await this._usernameRepository.updateWhere(
|
return await this._usernameRepository.updateWhere(
|
||||||
{
|
{
|
||||||
|
@ -21,6 +40,26 @@ export class UpdateUsernameUseCase {
|
||||||
username,
|
username,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
} catch (error) {
|
||||||
|
this._loggingMessager.publish(
|
||||||
|
'auth.username.update.warning',
|
||||||
|
JSON.stringify({
|
||||||
|
command,
|
||||||
|
error,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const addUsernameRequest = this._mapper.map(
|
||||||
|
command.updateUsernameRequest,
|
||||||
|
UpdateUsernameRequest,
|
||||||
|
AddUsernameRequest,
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
return await this._commandBus.execute(
|
||||||
|
new AddUsernameCommand(addUsernameRequest),
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@ import { createMap, Mapper } from '@automapper/core';
|
||||||
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
|
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { UsernamePresenter } from '../adapters/primaries/username.presenter';
|
import { UsernamePresenter } from '../adapters/primaries/username.presenter';
|
||||||
|
import { AddUsernameRequest } from '../domain/dtos/add-username.request';
|
||||||
|
import { UpdateUsernameRequest } from '../domain/dtos/update-username.request';
|
||||||
import { Username } from '../domain/entities/username';
|
import { Username } from '../domain/entities/username';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -13,6 +15,7 @@ export class UsernameProfile extends AutomapperProfile {
|
||||||
override get profile() {
|
override get profile() {
|
||||||
return (mapper: any) => {
|
return (mapper: any) => {
|
||||||
createMap(mapper, Username, UsernamePresenter);
|
createMap(mapper, Username, UsernamePresenter);
|
||||||
|
createMap(mapper, UpdateUsernameRequest, AddUsernameRequest);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { Type } from '../../domain/dtos/type.enum';
|
||||||
import { AddUsernameRequest } from '../../domain/dtos/add-username.request';
|
import { AddUsernameRequest } from '../../domain/dtos/add-username.request';
|
||||||
import { AddUsernameCommand } from '../../commands/add-username.command';
|
import { AddUsernameCommand } from '../../commands/add-username.command';
|
||||||
import { AddUsernameUseCase } from '../../domain/usecases/add-username.usecase';
|
import { AddUsernameUseCase } from '../../domain/usecases/add-username.usecase';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
|
|
||||||
const addUsernameRequest: AddUsernameRequest = {
|
const addUsernameRequest: AddUsernameRequest = {
|
||||||
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||||
|
@ -22,6 +23,10 @@ const mockUsernameRepository = {
|
||||||
create: jest.fn().mockResolvedValue(addUsernameRequest),
|
create: jest.fn().mockResolvedValue(addUsernameRequest),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockMessager = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
describe('AddUsernameUseCase', () => {
|
describe('AddUsernameUseCase', () => {
|
||||||
let addUsernameUseCase: AddUsernameUseCase;
|
let addUsernameUseCase: AddUsernameUseCase;
|
||||||
|
|
||||||
|
@ -33,6 +38,10 @@ describe('AddUsernameUseCase', () => {
|
||||||
provide: UsernameRepository,
|
provide: UsernameRepository,
|
||||||
useValue: mockUsernameRepository,
|
useValue: mockUsernameRepository,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: LoggingMessager,
|
||||||
|
useValue: mockMessager,
|
||||||
|
},
|
||||||
AddUsernameUseCase,
|
AddUsernameUseCase,
|
||||||
AuthProfile,
|
AuthProfile,
|
||||||
],
|
],
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { CreateAuthUseCase } from '../../domain/usecases/create-auth.usecase';
|
||||||
import * as bcrypt from 'bcrypt';
|
import * as bcrypt from 'bcrypt';
|
||||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
||||||
import { Type } from '../../domain/dtos/type.enum';
|
import { Type } from '../../domain/dtos/type.enum';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
|
|
||||||
const newAuthRequest: CreateAuthRequest = {
|
const newAuthRequest: CreateAuthRequest = {
|
||||||
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||||
|
@ -33,6 +34,10 @@ const mockUsernameRepository = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockMessager = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
describe('CreateAuthUseCase', () => {
|
describe('CreateAuthUseCase', () => {
|
||||||
let createAuthUseCase: CreateAuthUseCase;
|
let createAuthUseCase: CreateAuthUseCase;
|
||||||
|
|
||||||
|
@ -48,6 +53,10 @@ describe('CreateAuthUseCase', () => {
|
||||||
provide: UsernameRepository,
|
provide: UsernameRepository,
|
||||||
useValue: mockUsernameRepository,
|
useValue: mockUsernameRepository,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: LoggingMessager,
|
||||||
|
useValue: mockMessager,
|
||||||
|
},
|
||||||
CreateAuthUseCase,
|
CreateAuthUseCase,
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { classes } from '@automapper/classes';
|
||||||
import { AutomapperModule } from '@automapper/nestjs';
|
import { AutomapperModule } from '@automapper/nestjs';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { AuthRepository } from '../../adapters/secondaries/auth.repository';
|
import { AuthRepository } from '../../adapters/secondaries/auth.repository';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
||||||
import { DeleteAuthCommand } from '../../commands/delete-auth.command';
|
import { DeleteAuthCommand } from '../../commands/delete-auth.command';
|
||||||
import { DeleteAuthRequest } from '../../domain/dtos/delete-auth.request';
|
import { DeleteAuthRequest } from '../../domain/dtos/delete-auth.request';
|
||||||
|
@ -44,6 +45,10 @@ const mockUsernameRepository = {
|
||||||
deleteMany: jest.fn().mockResolvedValue(undefined),
|
deleteMany: jest.fn().mockResolvedValue(undefined),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockMessager = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
describe('DeleteAuthUseCase', () => {
|
describe('DeleteAuthUseCase', () => {
|
||||||
let deleteAuthUseCase: DeleteAuthUseCase;
|
let deleteAuthUseCase: DeleteAuthUseCase;
|
||||||
|
|
||||||
|
@ -59,6 +64,10 @@ describe('DeleteAuthUseCase', () => {
|
||||||
provide: UsernameRepository,
|
provide: UsernameRepository,
|
||||||
useValue: mockUsernameRepository,
|
useValue: mockUsernameRepository,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: LoggingMessager,
|
||||||
|
useValue: mockMessager,
|
||||||
|
},
|
||||||
DeleteAuthUseCase,
|
DeleteAuthUseCase,
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { classes } from '@automapper/classes';
|
||||||
import { AutomapperModule } from '@automapper/nestjs';
|
import { AutomapperModule } from '@automapper/nestjs';
|
||||||
import { UnauthorizedException } from '@nestjs/common';
|
import { UnauthorizedException } from '@nestjs/common';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
|
||||||
import { DeleteUsernameCommand } from '../../commands/delete-username.command';
|
import { DeleteUsernameCommand } from '../../commands/delete-username.command';
|
||||||
import { DeleteUsernameRequest } from '../../domain/dtos/delete-username.request';
|
import { DeleteUsernameRequest } from '../../domain/dtos/delete-username.request';
|
||||||
|
@ -65,6 +66,10 @@ const mockUsernameRepository = {
|
||||||
delete: jest.fn().mockResolvedValue(undefined),
|
delete: jest.fn().mockResolvedValue(undefined),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockMessager = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
describe('DeleteUsernameUseCase', () => {
|
describe('DeleteUsernameUseCase', () => {
|
||||||
let deleteUsernameUseCase: DeleteUsernameUseCase;
|
let deleteUsernameUseCase: DeleteUsernameUseCase;
|
||||||
|
|
||||||
|
@ -76,6 +81,10 @@ describe('DeleteUsernameUseCase', () => {
|
||||||
provide: UsernameRepository,
|
provide: UsernameRepository,
|
||||||
useValue: mockUsernameRepository,
|
useValue: mockUsernameRepository,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: LoggingMessager,
|
||||||
|
useValue: mockMessager,
|
||||||
|
},
|
||||||
DeleteUsernameUseCase,
|
DeleteUsernameUseCase,
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
|
@ -7,6 +7,7 @@ import * as bcrypt from 'bcrypt';
|
||||||
import { UpdatePasswordRequest } from '../../domain/dtos/update-password.request';
|
import { UpdatePasswordRequest } from '../../domain/dtos/update-password.request';
|
||||||
import { UpdatePasswordCommand } from '../../commands/update-password.command';
|
import { UpdatePasswordCommand } from '../../commands/update-password.command';
|
||||||
import { UpdatePasswordUseCase } from '../../domain/usecases/update-password.usecase';
|
import { UpdatePasswordUseCase } from '../../domain/usecases/update-password.usecase';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
|
|
||||||
const updatePasswordRequest: UpdatePasswordRequest = {
|
const updatePasswordRequest: UpdatePasswordRequest = {
|
||||||
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||||
|
@ -23,6 +24,10 @@ const mockAuthRepository = {
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockMessager = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
describe('UpdatePasswordUseCase', () => {
|
describe('UpdatePasswordUseCase', () => {
|
||||||
let updatePasswordUseCase: UpdatePasswordUseCase;
|
let updatePasswordUseCase: UpdatePasswordUseCase;
|
||||||
|
|
||||||
|
@ -34,6 +39,10 @@ describe('UpdatePasswordUseCase', () => {
|
||||||
provide: AuthRepository,
|
provide: AuthRepository,
|
||||||
useValue: mockAuthRepository,
|
useValue: mockAuthRepository,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: LoggingMessager,
|
||||||
|
useValue: mockMessager,
|
||||||
|
},
|
||||||
UpdatePasswordUseCase,
|
UpdatePasswordUseCase,
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
|
@ -7,18 +7,61 @@ import { UpdateUsernameRequest } from '../../domain/dtos/update-username.request
|
||||||
import { UpdateUsernameCommand } from '../../commands/update-username.command';
|
import { UpdateUsernameCommand } from '../../commands/update-username.command';
|
||||||
import { Type } from '../../domain/dtos/type.enum';
|
import { Type } from '../../domain/dtos/type.enum';
|
||||||
import { UpdateUsernameUseCase } from '../../domain/usecases/update-username.usecase';
|
import { UpdateUsernameUseCase } from '../../domain/usecases/update-username.usecase';
|
||||||
|
import { CommandBus } from '@nestjs/cqrs';
|
||||||
|
import { UsernameProfile } from '../../mappers/username.profile';
|
||||||
|
import { BadRequestException } from '@nestjs/common';
|
||||||
|
import { LoggingMessager } from '../../adapters/secondaries/logging.messager';
|
||||||
|
|
||||||
|
const existingUsername = {
|
||||||
|
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||||
|
username: 'john.doe@email.com',
|
||||||
|
type: Type.EMAIL,
|
||||||
|
};
|
||||||
|
|
||||||
const updateUsernameRequest: UpdateUsernameRequest = {
|
const updateUsernameRequest: UpdateUsernameRequest = {
|
||||||
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||||
username: 'johnny.doe@email.com',
|
username: 'johnny.doe@email.com',
|
||||||
type: Type.EMAIL,
|
type: Type.EMAIL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const newUsernameRequest: UpdateUsernameRequest = {
|
||||||
|
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||||
|
username: '+33611223344',
|
||||||
|
type: Type.PHONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
const invalidUpdateUsernameRequest: UpdateUsernameRequest = {
|
||||||
|
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||||
|
username: '',
|
||||||
|
type: Type.EMAIL,
|
||||||
|
};
|
||||||
|
|
||||||
const updateUsernameCommand: UpdateUsernameCommand = new UpdateUsernameCommand(
|
const updateUsernameCommand: UpdateUsernameCommand = new UpdateUsernameCommand(
|
||||||
updateUsernameRequest,
|
updateUsernameRequest,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const newUsernameCommand: UpdateUsernameCommand = new UpdateUsernameCommand(
|
||||||
|
newUsernameRequest,
|
||||||
|
);
|
||||||
|
|
||||||
|
const invalidUpdateUsernameCommand: UpdateUsernameCommand =
|
||||||
|
new UpdateUsernameCommand(invalidUpdateUsernameRequest);
|
||||||
|
|
||||||
const mockUsernameRepository = {
|
const mockUsernameRepository = {
|
||||||
updateWhere: jest.fn().mockResolvedValue(updateUsernameRequest),
|
findOne: jest.fn().mockResolvedValue(existingUsername),
|
||||||
|
updateWhere: jest
|
||||||
|
.fn()
|
||||||
|
.mockResolvedValueOnce(updateUsernameRequest)
|
||||||
|
.mockResolvedValueOnce(newUsernameRequest)
|
||||||
|
.mockResolvedValueOnce(invalidUpdateUsernameRequest),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockAddUsernameCommand = {
|
||||||
|
execute: jest.fn().mockResolvedValue(newUsernameRequest),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockMessager = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('UpdateUsernameUseCase', () => {
|
describe('UpdateUsernameUseCase', () => {
|
||||||
|
@ -32,7 +75,16 @@ describe('UpdateUsernameUseCase', () => {
|
||||||
provide: UsernameRepository,
|
provide: UsernameRepository,
|
||||||
useValue: mockUsernameRepository,
|
useValue: mockUsernameRepository,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: CommandBus,
|
||||||
|
useValue: mockAddUsernameCommand,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: LoggingMessager,
|
||||||
|
useValue: mockMessager,
|
||||||
|
},
|
||||||
UpdateUsernameUseCase,
|
UpdateUsernameUseCase,
|
||||||
|
UsernameProfile,
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
|
@ -54,5 +106,20 @@ describe('UpdateUsernameUseCase', () => {
|
||||||
expect(updatedUsername.username).toBe(updateUsernameRequest.username);
|
expect(updatedUsername.username).toBe(updateUsernameRequest.username);
|
||||||
expect(updatedUsername.type).toBe(updateUsernameRequest.type);
|
expect(updatedUsername.type).toBe(updateUsernameRequest.type);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should create a new username', async () => {
|
||||||
|
const newUsername: Username = await updateUsernameUseCase.execute(
|
||||||
|
newUsernameCommand,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(newUsername.username).toBe(newUsernameRequest.username);
|
||||||
|
expect(newUsername.type).toBe(newUsernameRequest.type);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an exception if username is invalid', async () => {
|
||||||
|
await expect(
|
||||||
|
updateUsernameUseCase.execute(invalidUpdateUsernameCommand),
|
||||||
|
).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -131,6 +131,7 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
|
||||||
|
|
||||||
return updatedEntity;
|
return updatedEntity;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.log('error', e);
|
||||||
if (e instanceof PrismaClientKnownRequestError) {
|
if (e instanceof PrismaClientKnownRequestError) {
|
||||||
throw new DatabaseException(
|
throw new DatabaseException(
|
||||||
PrismaClientKnownRequestError.name,
|
PrismaClientKnownRequestError.name,
|
||||||
|
|
Loading…
Reference in New Issue