diff --git a/package.json b/package.json index ceef582..44a6478 100644 --- a/package.json +++ b/package.json @@ -91,12 +91,12 @@ "ts" ], "modulePathIgnorePatterns": [ - ".controller.ts", ".module.ts", - ".request.ts", - ".presenter.ts", - ".profile.ts", - ".exception.ts", + ".dto.ts", + ".di-tokens.ts", + ".response.ts", + ".port.ts", + "prisma.service.ts", "main.ts" ], "rootDir": "src", @@ -108,15 +108,19 @@ "**/*.(t|j)s" ], "coveragePathIgnorePatterns": [ - ".controller.ts", ".module.ts", - ".request.ts", - ".presenter.ts", - ".profile.ts", - ".exception.ts", + ".dto.ts", + ".di-tokens.ts", + ".response.ts", + ".port.ts", + "prisma.service.ts", "main.ts" ], "coverageDirectory": "../coverage", + "moduleNameMapper": { + "^@modules(.*)": "/modules/$1", + "^@src(.*)": "$1" + }, "testEnvironment": "node" } } diff --git a/src/app.module.ts b/src/app.module.ts index 3fc541b..adaf99c 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,7 +4,7 @@ import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; // import { AuthorizationModule } from './modules/authorization/authorization.module'; // import { HealthModule } from './modules/health/health.module'; -import { AuthenticationModule } from '@modules/newauthentication/authentication.module'; +import { AuthenticationModule } from '@modules/authentication/authentication.module'; import { EventEmitterModule } from '@nestjs/event-emitter'; import { MessageBrokerModule, diff --git a/src/main.ts b/src/main.ts index 1d38e94..82277f9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,7 +15,7 @@ async function bootstrap() { protoPath: [ join( __dirname, - 'modules/newauthentication/interface/grpc-controllers/authentication.proto', + 'modules/authentication/interface/grpc-controllers/authentication.proto', ), join( __dirname, diff --git a/src/modules/newauthentication/authentication.di-tokens.ts b/src/modules/authentication/authentication.di-tokens.ts similarity index 54% rename from src/modules/newauthentication/authentication.di-tokens.ts rename to src/modules/authentication/authentication.di-tokens.ts index c1afd82..5160269 100644 --- a/src/modules/newauthentication/authentication.di-tokens.ts +++ b/src/modules/authentication/authentication.di-tokens.ts @@ -1 +1,2 @@ export const AUTHENTICATION_REPOSITORY = Symbol('AUTHENTICATION_REPOSITORY'); +export const USERNAME_REPOSITORY = Symbol('USERNAME_REPOSITORY'); diff --git a/src/modules/newauthentication/authentication.mapper.ts b/src/modules/authentication/authentication.mapper.ts similarity index 98% rename from src/modules/newauthentication/authentication.mapper.ts rename to src/modules/authentication/authentication.mapper.ts index 92f6e30..367fd07 100644 --- a/src/modules/newauthentication/authentication.mapper.ts +++ b/src/modules/authentication/authentication.mapper.ts @@ -52,6 +52,7 @@ export class AuthenticationMapper userId: record.uuid, password: record.password, usernames: record.usernames.map((username: UsernameModel) => ({ + userId: record.uuid, name: username.username, type: Type[username.type], })), diff --git a/src/modules/authentication/authentication.module.ts b/src/modules/authentication/authentication.module.ts index 3b7ed79..8a9401d 100644 --- a/src/modules/authentication/authentication.module.ts +++ b/src/modules/authentication/authentication.module.ts @@ -1,66 +1,72 @@ -import { Module } from '@nestjs/common'; +import { Module, Provider } from '@nestjs/common'; +import { CreateAuthenticationGrpcController } from './interface/grpc-controllers/create-authentication.grpc.controller'; +import { CreateAuthenticationService } from './core/application/commands/create-authentication/create-authentication.service'; +import { AuthenticationMapper } from './authentication.mapper'; +import { + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, +} from './authentication.di-tokens'; +import { AuthenticationRepository } from './infrastructure/authentication.repository'; +import { PrismaService } from './infrastructure/prisma.service'; import { CqrsModule } from '@nestjs/cqrs'; -import { DatabaseModule } from '../database/database.module'; -import { AuthenticationController } from './adapters/primaries/authentication.controller'; -import { CreateAuthenticationUseCase } from './domain/usecases/create-authentication.usecase'; -import { ValidateAuthenticationUseCase } from './domain/usecases/validate-authentication.usecase'; -import { AuthenticationProfile } from './mappers/authentication.profile'; -import { AuthenticationRepository } from './adapters/secondaries/authentication.repository'; -import { UpdateUsernameUseCase } from './domain/usecases/update-username.usecase'; -import { UsernameProfile } from './mappers/username.profile'; -import { AddUsernameUseCase } from './domain/usecases/add-username.usecase'; -import { UpdatePasswordUseCase } from './domain/usecases/update-password.usecase'; -import { DeleteUsernameUseCase } from './domain/usecases/delete-username.usecase'; -import { DeleteAuthenticationUseCase } from './domain/usecases/delete-authentication.usecase'; -import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq'; -import { ConfigModule, ConfigService } from '@nestjs/config'; -import { AuthenticationMessagerController } from './adapters/primaries/authentication-messager.controller'; -import { Messager } from './adapters/secondaries/messager'; +import { DeleteAuthenticationGrpcController } from './interface/grpc-controllers/delete-authentication.grpc.controller'; +import { DeleteAuthenticationService } from './core/application/commands/delete-authentication/delete-authentication.service'; +import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; +import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; +import { UsernameRepository } from './infrastructure/username.repository'; +import { UsernameMapper } from './username.mapper'; +import { AddUsernameGrpcController } from './interface/grpc-controllers/add-username.grpc.controller'; +import { AddUsernameService } from './core/application/commands/add-usernames/add-username.service'; + +const grpcControllers = [ + CreateAuthenticationGrpcController, + DeleteAuthenticationGrpcController, + AddUsernameGrpcController, +]; + +const commandHandlers: Provider[] = [ + CreateAuthenticationService, + DeleteAuthenticationService, + AddUsernameService, +]; + +const mappers: Provider[] = [AuthenticationMapper, UsernameMapper]; + +const repositories: Provider[] = [ + { + provide: AUTHENTICATION_REPOSITORY, + useClass: AuthenticationRepository, + }, + { + provide: USERNAME_REPOSITORY, + useClass: UsernameRepository, + }, +]; + +const messageBrokers: Provider[] = [ + { + provide: MESSAGE_PUBLISHER, + useClass: MessageBrokerPublisher, + }, +]; + +const orms: Provider[] = [PrismaService]; @Module({ - imports: [ - DatabaseModule, - CqrsModule, - RabbitMQModule.forRootAsync(RabbitMQModule, { - imports: [ConfigModule], - useFactory: async (configService: ConfigService) => ({ - exchanges: [ - { - name: configService.get('RMQ_EXCHANGE'), - type: 'topic', - }, - ], - handlers: { - userUpdate: { - exchange: configService.get('RMQ_EXCHANGE'), - routingKey: 'user.update', - }, - userDelete: { - exchange: configService.get('RMQ_EXCHANGE'), - routingKey: 'user.delete', - }, - }, - uri: configService.get('RMQ_URI'), - connectionInitOptions: { wait: false }, - enableControllerDiscovery: true, - }), - inject: [ConfigService], - }), - ], - controllers: [AuthenticationController, AuthenticationMessagerController], + imports: [CqrsModule], + controllers: [...grpcControllers], providers: [ - AuthenticationProfile, - UsernameProfile, - AuthenticationRepository, - Messager, - ValidateAuthenticationUseCase, - CreateAuthenticationUseCase, - AddUsernameUseCase, - UpdateUsernameUseCase, - UpdatePasswordUseCase, - DeleteUsernameUseCase, - DeleteAuthenticationUseCase, + ...commandHandlers, + ...mappers, + ...repositories, + ...messageBrokers, + ...orms, + ], + exports: [ + PrismaService, + AuthenticationMapper, + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, ], - exports: [], }) export class AuthenticationModule {} diff --git a/src/modules/authentication/core/application/commands/add-usernames/add-username.command.ts b/src/modules/authentication/core/application/commands/add-usernames/add-username.command.ts new file mode 100644 index 0000000..3eaf1fb --- /dev/null +++ b/src/modules/authentication/core/application/commands/add-usernames/add-username.command.ts @@ -0,0 +1,13 @@ +import { Command, CommandProps } from '@mobicoop/ddd-library'; +import { Username } from '../../types/username'; + +export class AddUsernameCommand extends Command { + readonly userId: string; + readonly username: Username; + + constructor(props: CommandProps) { + super(props); + this.userId = props.userId; + this.username = props.username; + } +} diff --git a/src/modules/authentication/core/application/commands/add-usernames/add-username.service.ts b/src/modules/authentication/core/application/commands/add-usernames/add-username.service.ts new file mode 100644 index 0000000..e290e51 --- /dev/null +++ b/src/modules/authentication/core/application/commands/add-usernames/add-username.service.ts @@ -0,0 +1,52 @@ +import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; +import { Inject } from '@nestjs/common'; +import { + AggregateID, + ConflictException, + UniqueConstraintException, +} from '@mobicoop/ddd-library'; +import { + AUTHENTICATION_REPOSITORY, + USERNAME_REPOSITORY, +} from '@modules/authentication/authentication.di-tokens'; +import { AuthenticationRepositoryPort } from '../../ports/authentication.repository.port'; +import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { AddUsernameCommand } from './add-username.command'; +import { UsernameRepositoryPort } from '../../ports/username.repository.port'; +import { UsernameEntity } from '@modules/authentication/core/domain/username.entity'; + +@CommandHandler(AddUsernameCommand) +export class AddUsernameService implements ICommandHandler { + constructor( + @Inject(AUTHENTICATION_REPOSITORY) + private readonly authenticationRepository: AuthenticationRepositoryPort, + @Inject(USERNAME_REPOSITORY) + private readonly usernameRepository: UsernameRepositoryPort, + ) {} + + async execute(command: AddUsernameCommand): Promise { + await this.authenticationRepository.findOneById(command.userId, { + usernames: true, + }); + try { + const newUsername = await UsernameEntity.create({ + name: command.username.name, + userId: command.userId, + type: command.username.type, + }); + await this.usernameRepository.insert(newUsername); + return newUsername.id; + } catch (error: any) { + if (error instanceof ConflictException) { + throw new UsernameAlreadyExistsException(error); + } + if ( + error instanceof UniqueConstraintException && + error.message.includes('username') + ) { + throw new UsernameAlreadyExistsException(error); + } + throw error; + } + } +} diff --git a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts b/src/modules/authentication/core/application/commands/create-authentication/create-authentication.command.ts similarity index 89% rename from src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts rename to src/modules/authentication/core/application/commands/create-authentication/create-authentication.command.ts index 5ec8c04..e60ffe1 100644 --- a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.command.ts +++ b/src/modules/authentication/core/application/commands/create-authentication/create-authentication.command.ts @@ -1,5 +1,5 @@ import { Command, CommandProps } from '@mobicoop/ddd-library'; -import { Username } from '../types/username'; +import { Username } from '../../types/username'; export class CreateAuthenticationCommand extends Command { readonly userId: string; diff --git a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts b/src/modules/authentication/core/application/commands/create-authentication/create-authentication.service.ts similarity index 74% rename from src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts rename to src/modules/authentication/core/application/commands/create-authentication/create-authentication.service.ts index 2ca3188..3ff15f1 100644 --- a/src/modules/newauthentication/core/application/commands/create-authentication/create-authentication.service.ts +++ b/src/modules/authentication/core/application/commands/create-authentication/create-authentication.service.ts @@ -6,19 +6,19 @@ import { UniqueConstraintException, } from '@mobicoop/ddd-library'; import { CreateAuthenticationCommand } from './create-authentication.command'; -import { AUTHENTICATION_REPOSITORY } from '@modules/newauthentication/authentication.di-tokens'; -import { AuthenticationRepositoryPort } from '../ports/authentication.repository.port'; -import { AuthenticationEntity } from '@modules/newauthentication/core/domain/authentication.entity'; +import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { AuthenticationRepositoryPort } from '../../ports/authentication.repository.port'; +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; import { AuthenticationAlreadyExistsException, UsernameAlreadyExistsException, -} from '@modules/newauthentication/core/domain/authentication.errors'; +} from '@modules/authentication/core/domain/authentication.errors'; @CommandHandler(CreateAuthenticationCommand) export class CreateAuthenticationService implements ICommandHandler { constructor( @Inject(AUTHENTICATION_REPOSITORY) - private readonly repository: AuthenticationRepositoryPort, + private readonly authenticationRepository: AuthenticationRepositoryPort, ) {} async execute(command: CreateAuthenticationCommand): Promise { @@ -29,7 +29,7 @@ export class CreateAuthenticationService implements ICommandHandler { usernames: command.usernames, }); try { - await this.repository.insert(authentication); + await this.authenticationRepository.insert(authentication); return authentication.getProps().userId; } catch (error: any) { if (error instanceof ConflictException) { diff --git a/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command.ts b/src/modules/authentication/core/application/commands/delete-authentication/delete-authentication.command.ts similarity index 100% rename from src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command.ts rename to src/modules/authentication/core/application/commands/delete-authentication/delete-authentication.command.ts diff --git a/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.service.ts b/src/modules/authentication/core/application/commands/delete-authentication/delete-authentication.service.ts similarity index 65% rename from src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.service.ts rename to src/modules/authentication/core/application/commands/delete-authentication/delete-authentication.service.ts index d97f7fa..f365e22 100644 --- a/src/modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.service.ts +++ b/src/modules/authentication/core/application/commands/delete-authentication/delete-authentication.service.ts @@ -1,9 +1,9 @@ import { Inject } from '@nestjs/common'; import { CommandHandler, ICommandHandler } from '@nestjs/cqrs'; import { DeleteAuthenticationCommand } from './delete-authentication.command'; -import { AUTHENTICATION_REPOSITORY } from '@modules/newauthentication/authentication.di-tokens'; -import { AuthenticationRepositoryPort } from '../ports/authentication.repository.port'; -import { AuthenticationEntity } from '@modules/newauthentication/core/domain/authentication.entity'; +import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens'; +import { AuthenticationRepositoryPort } from '../../ports/authentication.repository.port'; +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; @CommandHandler(DeleteAuthenticationCommand) export class DeleteAuthenticationService implements ICommandHandler { @@ -18,9 +18,9 @@ export class DeleteAuthenticationService implements ICommandHandler { usernames: true, }); authentication.delete(); - const deleted: boolean = await this.authenticationRepository.delete( + const isDeleted: boolean = await this.authenticationRepository.delete( authentication, ); - return deleted; + return isDeleted; } } diff --git a/src/modules/newauthentication/core/application/commands/ports/authentication.repository.port.ts b/src/modules/authentication/core/application/ports/authentication.repository.port.ts similarity index 57% rename from src/modules/newauthentication/core/application/commands/ports/authentication.repository.port.ts rename to src/modules/authentication/core/application/ports/authentication.repository.port.ts index 3b8ad0a..775db19 100644 --- a/src/modules/newauthentication/core/application/commands/ports/authentication.repository.port.ts +++ b/src/modules/authentication/core/application/ports/authentication.repository.port.ts @@ -1,4 +1,4 @@ import { RepositoryPort } from '@mobicoop/ddd-library'; -import { AuthenticationEntity } from '@modules/newauthentication/core/domain/authentication.entity'; +import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity'; export type AuthenticationRepositoryPort = RepositoryPort; diff --git a/src/modules/authentication/core/application/ports/username.repository.port.ts b/src/modules/authentication/core/application/ports/username.repository.port.ts new file mode 100644 index 0000000..1e11ac4 --- /dev/null +++ b/src/modules/authentication/core/application/ports/username.repository.port.ts @@ -0,0 +1,4 @@ +import { RepositoryPort } from '@mobicoop/ddd-library'; +import { UsernameEntity } from '../../domain/username.entity'; + +export type UsernameRepositoryPort = RepositoryPort; diff --git a/src/modules/authentication/core/application/types/username.ts b/src/modules/authentication/core/application/types/username.ts new file mode 100644 index 0000000..aa123cc --- /dev/null +++ b/src/modules/authentication/core/application/types/username.ts @@ -0,0 +1,6 @@ +import { Type } from '@modules/authentication/core/domain/username.types'; + +export type Username = { + name: string; + type: Type; +}; diff --git a/src/modules/newauthentication/core/domain/authentication.entity.ts b/src/modules/authentication/core/domain/authentication.entity.ts similarity index 97% rename from src/modules/newauthentication/core/domain/authentication.entity.ts rename to src/modules/authentication/core/domain/authentication.entity.ts index d0467c0..81ed651 100644 --- a/src/modules/newauthentication/core/domain/authentication.entity.ts +++ b/src/modules/authentication/core/domain/authentication.entity.ts @@ -4,7 +4,7 @@ import { AuthenticationProps, CreateAuthenticationProps, } from './authentication.types'; -import { AuthenticationCreatedDomainEvent } from './events/authentication-created.domain-events'; +import { AuthenticationCreatedDomainEvent } from './events/authentication-created.domain-event'; import { AuthenticationDeletedDomainEvent } from './events/authentication-deleted.domain-event'; export class AuthenticationEntity extends AggregateRoot { diff --git a/src/modules/newauthentication/core/domain/authentication.errors.ts b/src/modules/authentication/core/domain/authentication.errors.ts similarity index 100% rename from src/modules/newauthentication/core/domain/authentication.errors.ts rename to src/modules/authentication/core/domain/authentication.errors.ts diff --git a/src/modules/newauthentication/core/domain/authentication.types.ts b/src/modules/authentication/core/domain/authentication.types.ts similarity index 100% rename from src/modules/newauthentication/core/domain/authentication.types.ts rename to src/modules/authentication/core/domain/authentication.types.ts diff --git a/src/modules/newauthentication/core/domain/events/authentication-created.domain-events.ts b/src/modules/authentication/core/domain/events/authentication-created.domain-event.ts similarity index 100% rename from src/modules/newauthentication/core/domain/events/authentication-created.domain-events.ts rename to src/modules/authentication/core/domain/events/authentication-created.domain-event.ts diff --git a/src/modules/newauthentication/core/domain/events/authentication-deleted.domain-event.ts b/src/modules/authentication/core/domain/events/authentication-deleted.domain-event.ts similarity index 100% rename from src/modules/newauthentication/core/domain/events/authentication-deleted.domain-event.ts rename to src/modules/authentication/core/domain/events/authentication-deleted.domain-event.ts diff --git a/src/modules/authentication/core/domain/events/username-added.domain-event.ts b/src/modules/authentication/core/domain/events/username-added.domain-event.ts new file mode 100644 index 0000000..aea5849 --- /dev/null +++ b/src/modules/authentication/core/domain/events/username-added.domain-event.ts @@ -0,0 +1,7 @@ +import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library'; + +export class UsernameAddedDomainEvent extends DomainEvent { + constructor(props: DomainEventProps) { + super(props); + } +} diff --git a/src/modules/authentication/core/domain/username.entity.ts b/src/modules/authentication/core/domain/username.entity.ts new file mode 100644 index 0000000..5fd83fa --- /dev/null +++ b/src/modules/authentication/core/domain/username.entity.ts @@ -0,0 +1,27 @@ +import { AggregateID, AggregateRoot } from '@mobicoop/ddd-library'; +import { UsernameProps } from './username.types'; +import { UsernameAddedDomainEvent } from './events/username-added.domain-event'; + +export class UsernameEntity extends AggregateRoot { + protected readonly _id: AggregateID; + + static create = async (create: UsernameProps): Promise => { + const props: UsernameProps = { ...create }; + const username = new UsernameEntity({ + id: props.name, + props: { + name: props.name, + userId: props.userId, + type: props.type, + }, + }); + username.addEvent( + new UsernameAddedDomainEvent({ aggregateId: props.name }), + ); + return username; + }; + + validate(): void { + // entity business rules validation to protect it's invariant before saving entity to a database + } +} diff --git a/src/modules/newauthentication/core/domain/username.types.ts b/src/modules/authentication/core/domain/username.types.ts similarity index 89% rename from src/modules/newauthentication/core/domain/username.types.ts rename to src/modules/authentication/core/domain/username.types.ts index 7b1f1c5..382ce83 100644 --- a/src/modules/newauthentication/core/domain/username.types.ts +++ b/src/modules/authentication/core/domain/username.types.ts @@ -1,6 +1,7 @@ // All properties that a Username has export interface UsernameProps { name: string; + userId?: string; type: Type; } diff --git a/src/modules/newauthentication/infrastructure/authentication.repository.ts b/src/modules/authentication/infrastructure/authentication.repository.ts similarity index 87% rename from src/modules/newauthentication/infrastructure/authentication.repository.ts rename to src/modules/authentication/infrastructure/authentication.repository.ts index be535df..f176613 100644 --- a/src/modules/newauthentication/infrastructure/authentication.repository.ts +++ b/src/modules/authentication/infrastructure/authentication.repository.ts @@ -1,4 +1,4 @@ -import { Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable, Logger } from '@nestjs/common'; import { EventEmitter2 } from '@nestjs/event-emitter'; import { LoggerBase, @@ -6,7 +6,7 @@ import { PrismaRepositoryBase, } from '@mobicoop/ddd-library'; import { AuthenticationEntity } from '../core/domain/authentication.entity'; -import { AuthenticationRepositoryPort } from '../core/application/commands/ports/authentication.repository.port'; +import { AuthenticationRepositoryPort } from '../core/application/ports/authentication.repository.port'; import { PrismaService } from './prisma.service'; import { AuthenticationMapper } from '../authentication.mapper'; import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; @@ -57,12 +57,11 @@ export class AuthenticationRepository prisma, mapper, eventEmitter, - new LoggerBase( - AuthenticationRepository.name, - 'logging', - 'auth', + new LoggerBase({ + logger: new Logger(AuthenticationRepository.name), + domain: 'auth', messagePublisher, - ), + }), ); } } diff --git a/src/modules/newauthentication/infrastructure/prisma.service.ts b/src/modules/authentication/infrastructure/prisma.service.ts similarity index 100% rename from src/modules/newauthentication/infrastructure/prisma.service.ts rename to src/modules/authentication/infrastructure/prisma.service.ts diff --git a/src/modules/authentication/infrastructure/username.repository.ts b/src/modules/authentication/infrastructure/username.repository.ts new file mode 100644 index 0000000..1ee3e68 --- /dev/null +++ b/src/modules/authentication/infrastructure/username.repository.ts @@ -0,0 +1,49 @@ +import { Inject, Injectable, Logger } from '@nestjs/common'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { + LoggerBase, + MessagePublisherPort, + PrismaRepositoryBase, +} from '@mobicoop/ddd-library'; +import { PrismaService } from './prisma.service'; +import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; +import { UsernameEntity } from '../core/domain/username.entity'; +import { UsernameRepositoryPort } from '../core/application/ports/username.repository.port'; +import { UsernameMapper } from '../username.mapper'; + +export type UsernameModel = { + username: string; + authUuid: string; + type: string; + createdAt: Date; + updatedAt: Date; +}; + +/** + * Repository is used for retrieving/saving domain entities + * */ +@Injectable() +export class UsernameRepository + extends PrismaRepositoryBase + implements UsernameRepositoryPort +{ + constructor( + prisma: PrismaService, + mapper: UsernameMapper, + eventEmitter: EventEmitter2, + @Inject(MESSAGE_PUBLISHER) + protected readonly messagePublisher: MessagePublisherPort, + ) { + super( + prisma.username, + prisma, + mapper, + eventEmitter, + new LoggerBase({ + logger: new Logger(UsernameRepository.name), + domain: 'auth', + messagePublisher, + }), + ); + } +} diff --git a/src/modules/newauthentication/interface/dtos/authentication.paginated.response.dto.ts b/src/modules/authentication/interface/dtos/authentication.paginated.response.dto.ts similarity index 100% rename from src/modules/newauthentication/interface/dtos/authentication.paginated.response.dto.ts rename to src/modules/authentication/interface/dtos/authentication.paginated.response.dto.ts diff --git a/src/modules/newauthentication/interface/dtos/authentication.response.dto.ts b/src/modules/authentication/interface/dtos/authentication.response.dto.ts similarity index 100% rename from src/modules/newauthentication/interface/dtos/authentication.response.dto.ts rename to src/modules/authentication/interface/dtos/authentication.response.dto.ts diff --git a/src/modules/authentication/interface/dtos/username.response.dto.ts b/src/modules/authentication/interface/dtos/username.response.dto.ts new file mode 100644 index 0000000..0212b77 --- /dev/null +++ b/src/modules/authentication/interface/dtos/username.response.dto.ts @@ -0,0 +1,3 @@ +import { ResponseBase } from '@mobicoop/ddd-library'; + +export class UsernameResponseDto extends ResponseBase {} diff --git a/src/modules/authentication/interface/grpc-controllers/add-username.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/add-username.grpc.controller.ts new file mode 100644 index 0000000..a3d51c6 --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/add-username.grpc.controller.ts @@ -0,0 +1,49 @@ +import { + AggregateID, + IdResponse, + RpcExceptionCode, + RpcValidationPipe, +} from '@mobicoop/ddd-library'; +import { Controller, UsePipes } from '@nestjs/common'; +import { CommandBus } from '@nestjs/cqrs'; +import { GrpcMethod, RpcException } from '@nestjs/microservices'; +import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { AddUsernameRequestDto } from './dtos/add-username.request.dto'; +import { AddUsernameCommand } from '@modules/authentication/core/application/commands/add-usernames/add-username.command'; + +@UsePipes( + new RpcValidationPipe({ + whitelist: true, + forbidUnknownValues: false, + }), +) +@Controller() +export class AddUsernameGrpcController { + constructor(private readonly commandBus: CommandBus) {} + + @GrpcMethod('AuthenticationService', 'AddUsername') + async addUsername(data: AddUsernameRequestDto): Promise { + try { + const aggregateID: AggregateID = await this.commandBus.execute( + new AddUsernameCommand({ + userId: data.userId, + username: { + name: data.name, + type: data.type, + }, + }), + ); + return new IdResponse(aggregateID); + } catch (error: any) { + if (error instanceof UsernameAlreadyExistsException) + throw new RpcException({ + code: RpcExceptionCode.ALREADY_EXISTS, + message: error.message, + }); + throw new RpcException({ + code: RpcExceptionCode.UNKNOWN, + message: error.message, + }); + } + } +} diff --git a/src/modules/newauthentication/interface/grpc-controllers/authentication.proto b/src/modules/authentication/interface/grpc-controllers/authentication.proto similarity index 100% rename from src/modules/newauthentication/interface/grpc-controllers/authentication.proto rename to src/modules/authentication/interface/grpc-controllers/authentication.proto diff --git a/src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/create-authentication.grpc.controller.ts similarity index 82% rename from src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts rename to src/modules/authentication/interface/grpc-controllers/create-authentication.grpc.controller.ts index 4ad7de5..4abae17 100644 --- a/src/modules/newauthentication/interface/grpc-controllers/create-authentication.grpc.controller.ts +++ b/src/modules/authentication/interface/grpc-controllers/create-authentication.grpc.controller.ts @@ -8,8 +8,8 @@ import { Controller, UsePipes } from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; import { GrpcMethod, RpcException } from '@nestjs/microservices'; import { CreateAuthenticationRequestDto } from './dtos/create-authentication.request.dto'; -import { CreateAuthenticationCommand } from '@modules/newauthentication/core/application/commands/create-authentication/create-authentication.command'; -import { AuthenticationAlreadyExistsException } from '@modules/newauthentication/core/domain/authentication.errors'; +import { CreateAuthenticationCommand } from '@modules/authentication/core/application/commands/create-authentication/create-authentication.command'; +import { AuthenticationAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; @UsePipes( new RpcValidationPipe({ diff --git a/src/modules/newauthentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts b/src/modules/authentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts similarity index 89% rename from src/modules/newauthentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts rename to src/modules/authentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts index c66ecd9..6769e39 100644 --- a/src/modules/newauthentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts +++ b/src/modules/authentication/interface/grpc-controllers/delete-authentication.grpc.controller.ts @@ -7,7 +7,7 @@ import { import { Controller, UsePipes } from '@nestjs/common'; import { CommandBus } from '@nestjs/cqrs'; import { GrpcMethod, RpcException } from '@nestjs/microservices'; -import { DeleteAuthenticationCommand } from '@modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command'; +import { DeleteAuthenticationCommand } from '@modules/authentication/core/application/commands/delete-authentication/delete-authentication.command'; import { DeleteAuthenticationRequestDto } from './dtos/delete-authentication.request.dto'; @UsePipes( diff --git a/src/modules/authentication/interface/grpc-controllers/dtos/add-username.request.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/add-username.request.dto.ts new file mode 100644 index 0000000..74de9f6 --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/dtos/add-username.request.dto.ts @@ -0,0 +1,8 @@ +import { IsNotEmpty, IsString } from 'class-validator'; +import { UsernameDto } from './username.dto'; + +export class AddUsernameRequestDto extends UsernameDto { + @IsString() + @IsNotEmpty() + userId: string; +} diff --git a/src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts similarity index 100% rename from src/modules/newauthentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts rename to src/modules/authentication/interface/grpc-controllers/dtos/create-authentication.request.dto.ts diff --git a/src/modules/newauthentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts similarity index 100% rename from src/modules/newauthentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts rename to src/modules/authentication/interface/grpc-controllers/dtos/delete-authentication.request.dto.ts diff --git a/src/modules/authentication/interface/grpc-controllers/dtos/username.dto.ts b/src/modules/authentication/interface/grpc-controllers/dtos/username.dto.ts new file mode 100644 index 0000000..b744d75 --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/dtos/username.dto.ts @@ -0,0 +1,16 @@ +import { Type } from '@modules/authentication/core/domain/username.types'; +import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; +import { IsValidUsername } from './validators/decorators/is-valid-username.decorator'; + +export class UsernameDto { + @IsString() + @IsNotEmpty() + @IsValidUsername({ + message: 'Invalid username', + }) + name: string; + + @IsEnum(Type) + @IsNotEmpty() + type: Type; +} diff --git a/src/modules/authentication/interface/grpc-controllers/dtos/validators/decorators/is-valid-username.decorator.ts b/src/modules/authentication/interface/grpc-controllers/dtos/validators/decorators/is-valid-username.decorator.ts new file mode 100644 index 0000000..5785b39 --- /dev/null +++ b/src/modules/authentication/interface/grpc-controllers/dtos/validators/decorators/is-valid-username.decorator.ts @@ -0,0 +1,32 @@ +import { Type } from '@modules/authentication/core/domain/username.types'; +import { + registerDecorator, + ValidationOptions, + ValidationArguments, + isEmail, + isPhoneNumber, +} from 'class-validator'; + +export function IsValidUsername(validationOptions?: ValidationOptions) { + return function (object: any, propertyName: string) { + registerDecorator({ + name: 'isValidUsername', + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + validator: { + validate(value: any, args: ValidationArguments) { + const usernameType: Type = args.object['type']; + switch (usernameType) { + case Type.PHONE: + return isPhoneNumber(value); + case Type.EMAIL: + return isEmail(value); + default: + return false; + } + }, + }, + }); + }; +} diff --git a/src/modules/authentication/tests/unit/interface/add-username.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/add-username.grpc.controller.spec.ts new file mode 100644 index 0000000..b4786b9 --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/add-username.grpc.controller.spec.ts @@ -0,0 +1,89 @@ +import { IdResponse } from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { AddUsernameGrpcController } from '@modules/authentication/interface/grpc-controllers/add-username.grpc.controller'; +import { AddUsernameRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/add-username.request.dto'; +import { CommandBus } from '@nestjs/cqrs'; +import { RpcException } from '@nestjs/microservices'; +import { Test, TestingModule } from '@nestjs/testing'; + +const addUsernameRequest: AddUsernameRequestDto = { + userId: '78153e03-4861-4f58-a705-88526efee53b', + name: 'john.doe@email.com', + type: Type.EMAIL, +}; + +const mockCommandBus = { + execute: jest + .fn() + .mockImplementationOnce(() => 'john.doe@email.com') + .mockImplementationOnce(() => { + throw new UsernameAlreadyExistsException(); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Add Username Grpc Controller', () => { + let addUsernameGrpcController: AddUsernameGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: CommandBus, + useValue: mockCommandBus, + }, + AddUsernameGrpcController, + ], + }).compile(); + + addUsernameGrpcController = module.get( + AddUsernameGrpcController, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(addUsernameGrpcController).toBeDefined(); + }); + + it('should add a new username', async () => { + jest.spyOn(mockCommandBus, 'execute'); + const result: IdResponse = await addUsernameGrpcController.addUsername( + addUsernameRequest, + ); + expect(result).toBeInstanceOf(IdResponse); + expect(result.id).toBe('john.doe@email.com'); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if username already exists', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await addUsernameGrpcController.addUsername(addUsernameRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.ALREADY_EXISTS); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a generic RpcException', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await addUsernameGrpcController.addUsername(addUsernameRequest); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/authentication/tests/unit/interface/create-authentication.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/create-authentication.grpc.controller.spec.ts new file mode 100644 index 0000000..e45942a --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/create-authentication.grpc.controller.spec.ts @@ -0,0 +1,99 @@ +import { IdResponse } from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { AuthenticationAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors'; +import { Type } from '@modules/authentication/core/domain/username.types'; +import { CreateAuthenticationGrpcController } from '@modules/authentication/interface/grpc-controllers/create-authentication.grpc.controller'; +import { CreateAuthenticationRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/create-authentication.request.dto'; +import { CommandBus } from '@nestjs/cqrs'; +import { RpcException } from '@nestjs/microservices'; +import { Test, TestingModule } from '@nestjs/testing'; + +const createAuthenticationRequest: CreateAuthenticationRequestDto = { + userId: '78153e03-4861-4f58-a705-88526efee53b', + password: 'John123', + usernames: [ + { + name: 'john.doe@email.com', + type: Type.EMAIL, + }, + ], +}; + +const mockCommandBus = { + execute: jest + .fn() + .mockImplementationOnce(() => '78153e03-4861-4f58-a705-88526efee53b') + .mockImplementationOnce(() => { + throw new AuthenticationAlreadyExistsException(); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Create Authentication Grpc Controller', () => { + let createAuthenticationGrpcController: CreateAuthenticationGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: CommandBus, + useValue: mockCommandBus, + }, + CreateAuthenticationGrpcController, + ], + }).compile(); + + createAuthenticationGrpcController = + module.get( + CreateAuthenticationGrpcController, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(createAuthenticationGrpcController).toBeDefined(); + }); + + it('should create a new authentication', async () => { + jest.spyOn(mockCommandBus, 'execute'); + const result: IdResponse = await createAuthenticationGrpcController.create( + createAuthenticationRequest, + ); + expect(result).toBeInstanceOf(IdResponse); + expect(result.id).toBe('78153e03-4861-4f58-a705-88526efee53b'); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if authentication already exists', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await createAuthenticationGrpcController.create( + createAuthenticationRequest, + ); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.ALREADY_EXISTS); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a generic RpcException', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await createAuthenticationGrpcController.create( + createAuthenticationRequest, + ); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/authentication/tests/unit/interface/delete-authentication.grpc.controller.spec.ts b/src/modules/authentication/tests/unit/interface/delete-authentication.grpc.controller.spec.ts new file mode 100644 index 0000000..8f070aa --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/delete-authentication.grpc.controller.spec.ts @@ -0,0 +1,108 @@ +import { + DatabaseErrorException, + NotFoundException, +} from '@mobicoop/ddd-library'; +import { RpcExceptionCode } from '@mobicoop/ddd-library'; +import { DeleteAuthenticationGrpcController } from '@modules/authentication/interface/grpc-controllers/delete-authentication.grpc.controller'; +import { DeleteAuthenticationRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/delete-authentication.request.dto'; +import { CommandBus } from '@nestjs/cqrs'; +import { RpcException } from '@nestjs/microservices'; +import { Test, TestingModule } from '@nestjs/testing'; + +const deleteAuthenticationRequest: DeleteAuthenticationRequestDto = { + userId: '78153e03-4861-4f58-a705-88526efee53b', +}; + +const mockCommandBus = { + execute: jest + .fn() + .mockImplementationOnce(() => ({})) + .mockImplementationOnce(() => { + throw new NotFoundException(); + }) + .mockImplementationOnce(() => { + throw new DatabaseErrorException(); + }) + .mockImplementationOnce(() => { + throw new Error(); + }), +}; + +describe('Delete Authentication Grpc Controller', () => { + let deleteAuthenticationGrpcController: DeleteAuthenticationGrpcController; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + { + provide: CommandBus, + useValue: mockCommandBus, + }, + DeleteAuthenticationGrpcController, + ], + }).compile(); + + deleteAuthenticationGrpcController = + module.get( + DeleteAuthenticationGrpcController, + ); + }); + + afterEach(async () => { + jest.clearAllMocks(); + }); + + it('should be defined', () => { + expect(deleteAuthenticationGrpcController).toBeDefined(); + }); + + it('should create a new authentication', async () => { + jest.spyOn(mockCommandBus, 'execute'); + await deleteAuthenticationGrpcController.delete( + deleteAuthenticationRequest, + ); + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if authentication does not exist', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await deleteAuthenticationGrpcController.delete( + deleteAuthenticationRequest, + ); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.NOT_FOUND); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a dedicated RpcException if a database error occurs', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await deleteAuthenticationGrpcController.delete( + deleteAuthenticationRequest, + ); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.INTERNAL); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); + + it('should throw a generic RpcException', async () => { + jest.spyOn(mockCommandBus, 'execute'); + expect.assertions(3); + try { + await deleteAuthenticationGrpcController.delete( + deleteAuthenticationRequest, + ); + } catch (e: any) { + expect(e).toBeInstanceOf(RpcException); + expect(e.error.code).toBe(RpcExceptionCode.UNKNOWN); + } + expect(mockCommandBus.execute).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/modules/authentication/tests/unit/interface/is-valid-username.decorator.spec.ts b/src/modules/authentication/tests/unit/interface/is-valid-username.decorator.spec.ts new file mode 100644 index 0000000..3b6d59f --- /dev/null +++ b/src/modules/authentication/tests/unit/interface/is-valid-username.decorator.spec.ts @@ -0,0 +1,57 @@ +import { Type } from '@modules/authentication/core/domain/username.types'; +import { IsValidUsername } from '@modules/authentication/interface/grpc-controllers/dtos/validators/decorators/is-valid-username.decorator'; +import { Validator } from 'class-validator'; + +describe('Username decorator', () => { + class MyClass { + @IsValidUsername({ + message: 'Invalid username', + }) + name: string; + + type: Type; + } + it('should return a property decorator has a function', () => { + const isValidUsername = IsValidUsername(); + expect(typeof isValidUsername).toBe('function'); + }); + it('should validate a valid phone username', async () => { + const myClassInstance = new MyClass(); + myClassInstance.name = '+33611223344'; + myClassInstance.type = Type.PHONE; + const validator = new Validator(); + const validation = await validator.validate(myClassInstance); + expect(validation.length).toBe(0); + }); + it('should validate a valid email username', async () => { + const myClassInstance = new MyClass(); + myClassInstance.name = 'john.doe@email.com'; + myClassInstance.type = Type.EMAIL; + const validator = new Validator(); + const validation = await validator.validate(myClassInstance); + expect(validation.length).toBe(0); + }); + it('should not validate an invalid phone username', async () => { + const myClassInstance = new MyClass(); + myClassInstance.name = '11223344'; + myClassInstance.type = Type.PHONE; + const validator = new Validator(); + const validation = await validator.validate(myClassInstance); + expect(validation.length).toBe(1); + }); + it('should not validate an invalid email username', async () => { + const myClassInstance = new MyClass(); + myClassInstance.name = 'john.doe.email.com'; + myClassInstance.type = Type.EMAIL; + const validator = new Validator(); + const validation = await validator.validate(myClassInstance); + expect(validation.length).toBe(1); + }); + it('should not validate if type is not set', async () => { + const myClassInstance = new MyClass(); + myClassInstance.name = 'john.doe@email.com'; + const validator = new Validator(); + const validation = await validator.validate(myClassInstance); + expect(validation.length).toBe(1); + }); +}); diff --git a/src/modules/authentication/username.mapper.ts b/src/modules/authentication/username.mapper.ts new file mode 100644 index 0000000..b31a906 --- /dev/null +++ b/src/modules/authentication/username.mapper.ts @@ -0,0 +1,49 @@ +import { Mapper } from '@mobicoop/ddd-library'; +import { Injectable } from '@nestjs/common'; +import { Type } from './core/domain/username.types'; +import { UsernameEntity } from './core/domain/username.entity'; +import { UsernameModel } from './infrastructure/username.repository'; +import { UsernameResponseDto } from './interface/dtos/username.response.dto'; + +/** + * Mapper constructs objects that are used in different layers: + * Record is an object that is stored in a database, + * Entity is an object that is used in application domain layer, + * and a ResponseDTO is an object returned to a user (usually as json). + */ + +@Injectable() +export class UsernameMapper + implements + Mapper +{ + toPersistence = (entity: UsernameEntity): UsernameModel => { + const copy = entity.getProps(); + const record: UsernameModel = { + authUuid: copy.userId, + username: copy.name, + type: copy.type, + createdAt: copy.createdAt, + updatedAt: copy.updatedAt, + }; + return record; + }; + + toDomain = (record: UsernameModel): UsernameEntity => { + const entity = new UsernameEntity({ + id: record.authUuid, + createdAt: new Date(record.createdAt), + updatedAt: new Date(record.updatedAt), + props: { + name: record.username, + type: Type[record.type], + }, + }); + return entity; + }; + + toResponse = (entity: UsernameEntity): UsernameResponseDto => { + const response = new UsernameResponseDto(entity); + return response; + }; +} diff --git a/src/modules/database/database.module.ts b/src/modules/database/database.module.ts index 2240bdb..40d3fc7 100644 --- a/src/modules/database/database.module.ts +++ b/src/modules/database/database.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AuthenticationRepository } from '../authentication/adapters/secondaries/authentication.repository'; -import { UsernameRepository } from '../authentication/adapters/secondaries/username.repository'; +import { AuthenticationRepository } from '../oldauthentication/adapters/secondaries/authentication.repository'; +import { UsernameRepository } from '../oldauthentication/adapters/secondaries/username.repository'; import { PrismaService } from './adapters/secondaries/prisma-service'; @Module({ diff --git a/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts b/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts index 35b4b32..2d77a7b 100644 --- a/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts +++ b/src/modules/health/domain/usecases/prisma.health-indicator.usecase.ts @@ -4,7 +4,7 @@ import { HealthIndicator, HealthIndicatorResult, } from '@nestjs/terminus'; -import { AuthenticationRepository } from '../../../authentication/adapters/secondaries/authentication.repository'; +import { AuthenticationRepository } from '../../../oldauthentication/adapters/secondaries/authentication.repository'; @Injectable() export class PrismaHealthIndicatorUseCase extends HealthIndicator { diff --git a/src/modules/health/health.module.ts b/src/modules/health/health.module.ts index c8ec6ee..374fe2f 100644 --- a/src/modules/health/health.module.ts +++ b/src/modules/health/health.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { HealthServerController } from './adapters/primaries/health-server.controller'; import { PrismaHealthIndicatorUseCase } from './domain/usecases/prisma.health-indicator.usecase'; -import { AuthenticationRepository } from '../authentication/adapters/secondaries/authentication.repository'; +import { AuthenticationRepository } from '../oldauthentication/adapters/secondaries/authentication.repository'; import { DatabaseModule } from '../database/database.module'; import { HealthController } from './adapters/primaries/health.controller'; import { TerminusModule } from '@nestjs/terminus'; diff --git a/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts b/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts index 27d21d6..d7d9971 100644 --- a/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts +++ b/src/modules/health/tests/unit/prisma.health-indicator.usecase.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { PrismaHealthIndicatorUseCase } from '../../domain/usecases/prisma.health-indicator.usecase'; -import { AuthenticationRepository } from '../../../authentication/adapters/secondaries/authentication.repository'; +import { AuthenticationRepository } from '../../../oldauthentication/adapters/secondaries/authentication.repository'; import { PrismaClientKnownRequestError } from '@prisma/client/runtime'; import { HealthCheckError, HealthIndicatorResult } from '@nestjs/terminus'; diff --git a/src/modules/newauthentication/authentication.module.ts b/src/modules/newauthentication/authentication.module.ts deleted file mode 100644 index e636dc5..0000000 --- a/src/modules/newauthentication/authentication.module.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { Module, Provider } from '@nestjs/common'; -import { CreateAuthenticationGrpcController } from './interface/grpc-controllers/create-authentication.grpc.controller'; -import { CreateAuthenticationService } from './core/application/commands/create-authentication/create-authentication.service'; -import { AuthenticationMapper } from './authentication.mapper'; -import { AUTHENTICATION_REPOSITORY } from './authentication.di-tokens'; -import { AuthenticationRepository } from './infrastructure/authentication.repository'; -import { PrismaService } from './infrastructure/prisma.service'; -import { CqrsModule } from '@nestjs/cqrs'; -import { DeleteAuthenticationGrpcController } from './interface/grpc-controllers/delete-authentication.grpc.controller'; -import { DeleteAuthenticationService } from './core/application/commands/delete-authentication/delete-authentication.service'; -import { MESSAGE_PUBLISHER } from '@src/app.di-tokens'; -import { MessageBrokerPublisher } from '@mobicoop/message-broker-module'; - -const grpcControllers = [ - CreateAuthenticationGrpcController, - DeleteAuthenticationGrpcController, -]; - -const commandHandlers: Provider[] = [ - CreateAuthenticationService, - DeleteAuthenticationService, -]; - -const mappers: Provider[] = [AuthenticationMapper]; - -const repositories: Provider[] = [ - { - provide: AUTHENTICATION_REPOSITORY, - useClass: AuthenticationRepository, - }, -]; - -const messageBrokers: Provider[] = [ - { - provide: MESSAGE_PUBLISHER, - useClass: MessageBrokerPublisher, - }, -]; - -const orms: Provider[] = [PrismaService]; - -@Module({ - imports: [CqrsModule], - controllers: [...grpcControllers], - providers: [ - ...commandHandlers, - ...mappers, - ...repositories, - ...messageBrokers, - ...orms, - ], - exports: [PrismaService, AuthenticationMapper, AUTHENTICATION_REPOSITORY], -}) -export class AuthenticationModule {} diff --git a/src/modules/newauthentication/core/application/commands/types/username.ts b/src/modules/newauthentication/core/application/commands/types/username.ts deleted file mode 100644 index b8c4452..0000000 --- a/src/modules/newauthentication/core/application/commands/types/username.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Type } from '@modules/newauthentication/core/domain/username.types'; - -export type Username = { - name: string; - type: Type; -}; diff --git a/src/modules/newauthentication/interface/grpc-controllers/dtos/username.dto.ts b/src/modules/newauthentication/interface/grpc-controllers/dtos/username.dto.ts deleted file mode 100644 index 665ee21..0000000 --- a/src/modules/newauthentication/interface/grpc-controllers/dtos/username.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Type } from '@modules/newauthentication/core/domain/username.types'; -import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; - -export class UsernameDto { - @IsString() - name: string; - - @IsEnum(Type) - @IsNotEmpty() - type: Type; -} diff --git a/src/modules/authentication/adapters/primaries/authentication-messager.controller.ts b/src/modules/oldauthentication/adapters/primaries/authentication-messager.controller.ts similarity index 100% rename from src/modules/authentication/adapters/primaries/authentication-messager.controller.ts rename to src/modules/oldauthentication/adapters/primaries/authentication-messager.controller.ts diff --git a/src/modules/authentication/adapters/primaries/authentication.controller.ts b/src/modules/oldauthentication/adapters/primaries/authentication.controller.ts similarity index 100% rename from src/modules/authentication/adapters/primaries/authentication.controller.ts rename to src/modules/oldauthentication/adapters/primaries/authentication.controller.ts diff --git a/src/modules/authentication/adapters/primaries/authentication.presenter.ts b/src/modules/oldauthentication/adapters/primaries/authentication.presenter.ts similarity index 100% rename from src/modules/authentication/adapters/primaries/authentication.presenter.ts rename to src/modules/oldauthentication/adapters/primaries/authentication.presenter.ts diff --git a/src/modules/authentication/adapters/primaries/authentication.proto b/src/modules/oldauthentication/adapters/primaries/authentication.proto similarity index 100% rename from src/modules/authentication/adapters/primaries/authentication.proto rename to src/modules/oldauthentication/adapters/primaries/authentication.proto diff --git a/src/modules/authentication/adapters/primaries/username.presenter.ts b/src/modules/oldauthentication/adapters/primaries/username.presenter.ts similarity index 100% rename from src/modules/authentication/adapters/primaries/username.presenter.ts rename to src/modules/oldauthentication/adapters/primaries/username.presenter.ts diff --git a/src/modules/authentication/adapters/secondaries/authentication.repository.ts b/src/modules/oldauthentication/adapters/secondaries/authentication.repository.ts similarity index 100% rename from src/modules/authentication/adapters/secondaries/authentication.repository.ts rename to src/modules/oldauthentication/adapters/secondaries/authentication.repository.ts diff --git a/src/modules/authentication/adapters/secondaries/messager.ts b/src/modules/oldauthentication/adapters/secondaries/messager.ts similarity index 100% rename from src/modules/authentication/adapters/secondaries/messager.ts rename to src/modules/oldauthentication/adapters/secondaries/messager.ts diff --git a/src/modules/authentication/adapters/secondaries/username.repository.ts b/src/modules/oldauthentication/adapters/secondaries/username.repository.ts similarity index 100% rename from src/modules/authentication/adapters/secondaries/username.repository.ts rename to src/modules/oldauthentication/adapters/secondaries/username.repository.ts diff --git a/src/modules/oldauthentication/authentication.module.ts b/src/modules/oldauthentication/authentication.module.ts new file mode 100644 index 0000000..3b7ed79 --- /dev/null +++ b/src/modules/oldauthentication/authentication.module.ts @@ -0,0 +1,66 @@ +import { Module } from '@nestjs/common'; +import { CqrsModule } from '@nestjs/cqrs'; +import { DatabaseModule } from '../database/database.module'; +import { AuthenticationController } from './adapters/primaries/authentication.controller'; +import { CreateAuthenticationUseCase } from './domain/usecases/create-authentication.usecase'; +import { ValidateAuthenticationUseCase } from './domain/usecases/validate-authentication.usecase'; +import { AuthenticationProfile } from './mappers/authentication.profile'; +import { AuthenticationRepository } from './adapters/secondaries/authentication.repository'; +import { UpdateUsernameUseCase } from './domain/usecases/update-username.usecase'; +import { UsernameProfile } from './mappers/username.profile'; +import { AddUsernameUseCase } from './domain/usecases/add-username.usecase'; +import { UpdatePasswordUseCase } from './domain/usecases/update-password.usecase'; +import { DeleteUsernameUseCase } from './domain/usecases/delete-username.usecase'; +import { DeleteAuthenticationUseCase } from './domain/usecases/delete-authentication.usecase'; +import { RabbitMQModule } from '@golevelup/nestjs-rabbitmq'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { AuthenticationMessagerController } from './adapters/primaries/authentication-messager.controller'; +import { Messager } from './adapters/secondaries/messager'; + +@Module({ + imports: [ + DatabaseModule, + CqrsModule, + RabbitMQModule.forRootAsync(RabbitMQModule, { + imports: [ConfigModule], + useFactory: async (configService: ConfigService) => ({ + exchanges: [ + { + name: configService.get('RMQ_EXCHANGE'), + type: 'topic', + }, + ], + handlers: { + userUpdate: { + exchange: configService.get('RMQ_EXCHANGE'), + routingKey: 'user.update', + }, + userDelete: { + exchange: configService.get('RMQ_EXCHANGE'), + routingKey: 'user.delete', + }, + }, + uri: configService.get('RMQ_URI'), + connectionInitOptions: { wait: false }, + enableControllerDiscovery: true, + }), + inject: [ConfigService], + }), + ], + controllers: [AuthenticationController, AuthenticationMessagerController], + providers: [ + AuthenticationProfile, + UsernameProfile, + AuthenticationRepository, + Messager, + ValidateAuthenticationUseCase, + CreateAuthenticationUseCase, + AddUsernameUseCase, + UpdateUsernameUseCase, + UpdatePasswordUseCase, + DeleteUsernameUseCase, + DeleteAuthenticationUseCase, + ], + exports: [], +}) +export class AuthenticationModule {} diff --git a/src/modules/authentication/commands/add-username.command.ts b/src/modules/oldauthentication/commands/add-username.command.ts similarity index 100% rename from src/modules/authentication/commands/add-username.command.ts rename to src/modules/oldauthentication/commands/add-username.command.ts diff --git a/src/modules/authentication/commands/create-authentication.command.ts b/src/modules/oldauthentication/commands/create-authentication.command.ts similarity index 100% rename from src/modules/authentication/commands/create-authentication.command.ts rename to src/modules/oldauthentication/commands/create-authentication.command.ts diff --git a/src/modules/authentication/commands/delete-authentication.command.ts b/src/modules/oldauthentication/commands/delete-authentication.command.ts similarity index 100% rename from src/modules/authentication/commands/delete-authentication.command.ts rename to src/modules/oldauthentication/commands/delete-authentication.command.ts diff --git a/src/modules/authentication/commands/delete-username.command.ts b/src/modules/oldauthentication/commands/delete-username.command.ts similarity index 100% rename from src/modules/authentication/commands/delete-username.command.ts rename to src/modules/oldauthentication/commands/delete-username.command.ts diff --git a/src/modules/authentication/commands/update-password.command.ts b/src/modules/oldauthentication/commands/update-password.command.ts similarity index 100% rename from src/modules/authentication/commands/update-password.command.ts rename to src/modules/oldauthentication/commands/update-password.command.ts diff --git a/src/modules/authentication/commands/update-username.command.ts b/src/modules/oldauthentication/commands/update-username.command.ts similarity index 100% rename from src/modules/authentication/commands/update-username.command.ts rename to src/modules/oldauthentication/commands/update-username.command.ts diff --git a/src/modules/authentication/domain/dtos/add-username.request.ts b/src/modules/oldauthentication/domain/dtos/add-username.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/add-username.request.ts rename to src/modules/oldauthentication/domain/dtos/add-username.request.ts diff --git a/src/modules/authentication/domain/dtos/create-authentication.request.ts b/src/modules/oldauthentication/domain/dtos/create-authentication.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/create-authentication.request.ts rename to src/modules/oldauthentication/domain/dtos/create-authentication.request.ts diff --git a/src/modules/authentication/domain/dtos/delete-authentication.request.ts b/src/modules/oldauthentication/domain/dtos/delete-authentication.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/delete-authentication.request.ts rename to src/modules/oldauthentication/domain/dtos/delete-authentication.request.ts diff --git a/src/modules/authentication/domain/dtos/delete-username.request.ts b/src/modules/oldauthentication/domain/dtos/delete-username.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/delete-username.request.ts rename to src/modules/oldauthentication/domain/dtos/delete-username.request.ts diff --git a/src/modules/authentication/domain/dtos/type.enum.ts b/src/modules/oldauthentication/domain/dtos/type.enum.ts similarity index 100% rename from src/modules/authentication/domain/dtos/type.enum.ts rename to src/modules/oldauthentication/domain/dtos/type.enum.ts diff --git a/src/modules/authentication/domain/dtos/update-password.request.ts b/src/modules/oldauthentication/domain/dtos/update-password.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/update-password.request.ts rename to src/modules/oldauthentication/domain/dtos/update-password.request.ts diff --git a/src/modules/authentication/domain/dtos/update-username.request.ts b/src/modules/oldauthentication/domain/dtos/update-username.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/update-username.request.ts rename to src/modules/oldauthentication/domain/dtos/update-username.request.ts diff --git a/src/modules/authentication/domain/dtos/validate-authentication.request.ts b/src/modules/oldauthentication/domain/dtos/validate-authentication.request.ts similarity index 100% rename from src/modules/authentication/domain/dtos/validate-authentication.request.ts rename to src/modules/oldauthentication/domain/dtos/validate-authentication.request.ts diff --git a/src/modules/authentication/domain/entities/authentication.ts b/src/modules/oldauthentication/domain/entities/authentication.ts similarity index 100% rename from src/modules/authentication/domain/entities/authentication.ts rename to src/modules/oldauthentication/domain/entities/authentication.ts diff --git a/src/modules/authentication/domain/entities/username.ts b/src/modules/oldauthentication/domain/entities/username.ts similarity index 100% rename from src/modules/authentication/domain/entities/username.ts rename to src/modules/oldauthentication/domain/entities/username.ts diff --git a/src/modules/authentication/domain/interfaces/message-broker.ts b/src/modules/oldauthentication/domain/interfaces/message-broker.ts similarity index 100% rename from src/modules/authentication/domain/interfaces/message-broker.ts rename to src/modules/oldauthentication/domain/interfaces/message-broker.ts diff --git a/src/modules/authentication/domain/usecases/add-username.usecase.ts b/src/modules/oldauthentication/domain/usecases/add-username.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/add-username.usecase.ts rename to src/modules/oldauthentication/domain/usecases/add-username.usecase.ts diff --git a/src/modules/authentication/domain/usecases/create-authentication.usecase.ts b/src/modules/oldauthentication/domain/usecases/create-authentication.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/create-authentication.usecase.ts rename to src/modules/oldauthentication/domain/usecases/create-authentication.usecase.ts diff --git a/src/modules/authentication/domain/usecases/delete-authentication.usecase.ts b/src/modules/oldauthentication/domain/usecases/delete-authentication.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/delete-authentication.usecase.ts rename to src/modules/oldauthentication/domain/usecases/delete-authentication.usecase.ts diff --git a/src/modules/authentication/domain/usecases/delete-username.usecase.ts b/src/modules/oldauthentication/domain/usecases/delete-username.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/delete-username.usecase.ts rename to src/modules/oldauthentication/domain/usecases/delete-username.usecase.ts diff --git a/src/modules/authentication/domain/usecases/update-password.usecase.ts b/src/modules/oldauthentication/domain/usecases/update-password.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/update-password.usecase.ts rename to src/modules/oldauthentication/domain/usecases/update-password.usecase.ts diff --git a/src/modules/authentication/domain/usecases/update-username.usecase.ts b/src/modules/oldauthentication/domain/usecases/update-username.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/update-username.usecase.ts rename to src/modules/oldauthentication/domain/usecases/update-username.usecase.ts diff --git a/src/modules/authentication/domain/usecases/validate-authentication.usecase.ts b/src/modules/oldauthentication/domain/usecases/validate-authentication.usecase.ts similarity index 100% rename from src/modules/authentication/domain/usecases/validate-authentication.usecase.ts rename to src/modules/oldauthentication/domain/usecases/validate-authentication.usecase.ts diff --git a/src/modules/authentication/mappers/authentication.profile.ts b/src/modules/oldauthentication/mappers/authentication.profile.ts similarity index 100% rename from src/modules/authentication/mappers/authentication.profile.ts rename to src/modules/oldauthentication/mappers/authentication.profile.ts diff --git a/src/modules/authentication/mappers/username.profile.ts b/src/modules/oldauthentication/mappers/username.profile.ts similarity index 100% rename from src/modules/authentication/mappers/username.profile.ts rename to src/modules/oldauthentication/mappers/username.profile.ts diff --git a/src/modules/authentication/queries/validate-authentication.query.ts b/src/modules/oldauthentication/queries/validate-authentication.query.ts similarity index 100% rename from src/modules/authentication/queries/validate-authentication.query.ts rename to src/modules/oldauthentication/queries/validate-authentication.query.ts diff --git a/src/modules/authentication/tests/integration/authentication.repository.spec.ts b/src/modules/oldauthentication/tests/integration/authentication.repository.spec.ts similarity index 100% rename from src/modules/authentication/tests/integration/authentication.repository.spec.ts rename to src/modules/oldauthentication/tests/integration/authentication.repository.spec.ts diff --git a/src/modules/authentication/tests/integration/username.repository.spec.ts b/src/modules/oldauthentication/tests/integration/username.repository.spec.ts similarity index 100% rename from src/modules/authentication/tests/integration/username.repository.spec.ts rename to src/modules/oldauthentication/tests/integration/username.repository.spec.ts diff --git a/src/modules/authentication/tests/unit/add-username.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/add-username.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/add-username.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/add-username.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/create-authentication.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/create-authentication.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/create-authentication.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/create-authentication.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/delete-authentication.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/delete-authentication.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/delete-authentication.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/delete-authentication.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/delete-username.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/delete-username.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/delete-username.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/delete-username.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/messager.spec.ts b/src/modules/oldauthentication/tests/unit/messager.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/messager.spec.ts rename to src/modules/oldauthentication/tests/unit/messager.spec.ts diff --git a/src/modules/authentication/tests/unit/update-password.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/update-password.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/update-password.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/update-password.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/update-username.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/update-username.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/update-username.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/update-username.usecase.spec.ts diff --git a/src/modules/authentication/tests/unit/validate-authentication.usecase.spec.ts b/src/modules/oldauthentication/tests/unit/validate-authentication.usecase.spec.ts similarity index 100% rename from src/modules/authentication/tests/unit/validate-authentication.usecase.spec.ts rename to src/modules/oldauthentication/tests/unit/validate-authentication.usecase.spec.ts diff --git a/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts b/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts index b02a9b7..3f5af01 100644 --- a/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts +++ b/src/utils/tests/unit/rpc-validation-pipe.usecase.spec.ts @@ -1,5 +1,5 @@ import { ArgumentMetadata } from '@nestjs/common'; -import { ValidateAuthenticationRequest } from '../../../modules/authentication/domain/dtos/validate-authentication.request'; +import { ValidateAuthenticationRequest } from '../../../modules/oldauthentication/domain/dtos/validate-authentication.request'; import { RpcValidationPipe } from '../../pipes/rpc.validation-pipe'; describe('RpcValidationPipe', () => {