Merge branch 'usernames' into 'main'
multiple usernames See merge request mobicoop/lab/v3/services/auth!1
This commit is contained in:
		
						commit
						9f5c0b66c3
					
				| 
						 | 
				
			
			@ -1,10 +0,0 @@
 | 
			
		|||
-- CreateTable
 | 
			
		||||
CREATE TABLE "auth" (
 | 
			
		||||
    "uuid" UUID NOT NULL,
 | 
			
		||||
    "username" TEXT NOT NULL,
 | 
			
		||||
    "password" TEXT NOT NULL,
 | 
			
		||||
    "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
    "updatedAt" TIMESTAMP(3) NOT NULL,
 | 
			
		||||
 | 
			
		||||
    CONSTRAINT "auth_pkey" PRIMARY KEY ("uuid")
 | 
			
		||||
);
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
-- CreateEnum
 | 
			
		||||
CREATE TYPE "Type" AS ENUM ('EMAIL', 'PHONE');
 | 
			
		||||
 | 
			
		||||
-- CreateTable
 | 
			
		||||
CREATE TABLE "auth" (
 | 
			
		||||
    "uuid" UUID NOT NULL,
 | 
			
		||||
    "password" TEXT NOT NULL,
 | 
			
		||||
    "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
    "updatedAt" TIMESTAMP(3) NOT NULL,
 | 
			
		||||
 | 
			
		||||
    CONSTRAINT "auth_pkey" PRIMARY KEY ("uuid")
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
-- CreateTable
 | 
			
		||||
CREATE TABLE "username" (
 | 
			
		||||
    "username" TEXT NOT NULL,
 | 
			
		||||
    "uuid" UUID NOT NULL,
 | 
			
		||||
    "type" "Type" NOT NULL DEFAULT 'EMAIL',
 | 
			
		||||
    "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
    "updatedAt" TIMESTAMP(3) NOT NULL,
 | 
			
		||||
 | 
			
		||||
    CONSTRAINT "username_pkey" PRIMARY KEY ("username")
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
-- CreateIndex
 | 
			
		||||
CREATE UNIQUE INDEX "username_uuid_type_key" ON "username"("uuid", "type");
 | 
			
		||||
| 
						 | 
				
			
			@ -12,10 +12,25 @@ datasource db {
 | 
			
		|||
 | 
			
		||||
model Auth {
 | 
			
		||||
  uuid      String   @id @db.Uuid
 | 
			
		||||
  username  String
 | 
			
		||||
  password  String
 | 
			
		||||
  createdAt DateTime @default(now())
 | 
			
		||||
  updatedAt DateTime @updatedAt
 | 
			
		||||
 | 
			
		||||
  @@map("auth")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
model Username {
 | 
			
		||||
  username  String   @id
 | 
			
		||||
  uuid      String   @db.Uuid
 | 
			
		||||
  type      Type     @default(EMAIL) // type is needed in case of username update
 | 
			
		||||
  createdAt DateTime @default(now())
 | 
			
		||||
  updatedAt DateTime @updatedAt
 | 
			
		||||
 | 
			
		||||
  @@unique([uuid, type])
 | 
			
		||||
  @@map("username")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum Type {
 | 
			
		||||
  EMAIL
 | 
			
		||||
  PHONE
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ async function bootstrap() {
 | 
			
		|||
          'modules/auth/adapters/primaries/auth.proto',
 | 
			
		||||
        ),
 | 
			
		||||
        url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT,
 | 
			
		||||
        loader: { keepCase: true },
 | 
			
		||||
        loader: { keepCase: true, enums: String },
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  );
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,14 +4,24 @@ import { Controller } from '@nestjs/common';
 | 
			
		|||
import { CommandBus, QueryBus } from '@nestjs/cqrs';
 | 
			
		||||
import { GrpcMethod, RpcException } from '@nestjs/microservices';
 | 
			
		||||
import { DatabaseException } from 'src/modules/database/src/exceptions/DatabaseException';
 | 
			
		||||
import { AddUsernameCommand } from '../../commands/add-username.command';
 | 
			
		||||
import { CreateAuthCommand } from '../../commands/create-auth.command';
 | 
			
		||||
import { UpdateAuthCommand } from '../../commands/update-auth.command';
 | 
			
		||||
import { DeleteAuthCommand } from '../../commands/delete-auth.command';
 | 
			
		||||
import { DeleteUsernameCommand } from '../../commands/delete-username.command';
 | 
			
		||||
import { UpdatePasswordCommand } from '../../commands/update-password.command';
 | 
			
		||||
import { UpdateUsernameCommand } from '../../commands/update-username.command';
 | 
			
		||||
import { AddUsernameRequest } from '../../domain/dtos/add-username.request';
 | 
			
		||||
import { CreateAuthRequest } from '../../domain/dtos/create-auth.request';
 | 
			
		||||
import { UpdateAuthRequest } from '../../domain/dtos/update-auth.request';
 | 
			
		||||
import { DeleteAuthRequest } from '../../domain/dtos/delete-auth.request';
 | 
			
		||||
import { DeleteUsernameRequest } from '../../domain/dtos/delete-username.request';
 | 
			
		||||
import { UpdatePasswordRequest } from '../../domain/dtos/update-password.request';
 | 
			
		||||
import { UpdateUsernameRequest } from '../../domain/dtos/update-username.request';
 | 
			
		||||
import { ValidateAuthRequest } from '../../domain/dtos/validate-auth.request';
 | 
			
		||||
import { Auth } from '../../domain/entities/auth';
 | 
			
		||||
import { Username } from '../../domain/entities/username';
 | 
			
		||||
import { ValidateAuthQuery } from '../../queries/validate-auth.query';
 | 
			
		||||
import { AuthPresenter } from './auth.presenter';
 | 
			
		||||
import { UsernamePresenter } from './username.presenter';
 | 
			
		||||
 | 
			
		||||
@Controller()
 | 
			
		||||
export class AuthController {
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +34,7 @@ export class AuthController {
 | 
			
		|||
  @GrpcMethod('AuthService', 'Validate')
 | 
			
		||||
  async validate(data: ValidateAuthRequest): Promise<AuthPresenter> {
 | 
			
		||||
    try {
 | 
			
		||||
      const auth = await this._queryBus.execute(
 | 
			
		||||
      const auth: Auth = await this._queryBus.execute(
 | 
			
		||||
        new ValidateAuthQuery(data.username, data.password),
 | 
			
		||||
      );
 | 
			
		||||
      return this._mapper.map(auth, Auth, AuthPresenter);
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +49,9 @@ export class AuthController {
 | 
			
		|||
  @GrpcMethod('AuthService', 'Create')
 | 
			
		||||
  async createUser(data: CreateAuthRequest): Promise<AuthPresenter> {
 | 
			
		||||
    try {
 | 
			
		||||
      const auth = await this._commandBus.execute(new CreateAuthCommand(data));
 | 
			
		||||
      const auth: Auth = await this._commandBus.execute(
 | 
			
		||||
        new CreateAuthCommand(data),
 | 
			
		||||
      );
 | 
			
		||||
      return this._mapper.map(auth, Auth, AuthPresenter);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      if (e instanceof DatabaseException) {
 | 
			
		||||
| 
						 | 
				
			
			@ -50,14 +62,69 @@ export class AuthController {
 | 
			
		|||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      throw new RpcException({});
 | 
			
		||||
      throw new RpcException({
 | 
			
		||||
        code: 7,
 | 
			
		||||
        message: 'Permission denied',
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @GrpcMethod('AuthService', 'Update')
 | 
			
		||||
  async updateAuth(data: UpdateAuthRequest): Promise<AuthPresenter> {
 | 
			
		||||
  @GrpcMethod('AuthService', 'AddUsername')
 | 
			
		||||
  async addUsername(data: AddUsernameRequest): Promise<UsernamePresenter> {
 | 
			
		||||
    try {
 | 
			
		||||
      const auth = await this._commandBus.execute(new UpdateAuthCommand(data));
 | 
			
		||||
      const username: Username = await this._commandBus.execute(
 | 
			
		||||
        new AddUsernameCommand(data),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return this._mapper.map(username, Username, UsernamePresenter);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      if (e instanceof DatabaseException) {
 | 
			
		||||
        if (e.message.includes('Already exists')) {
 | 
			
		||||
          throw new RpcException({
 | 
			
		||||
            code: 6,
 | 
			
		||||
            message: 'Username already exists',
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      throw new RpcException({
 | 
			
		||||
        code: 7,
 | 
			
		||||
        message: 'Permission denied',
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @GrpcMethod('AuthService', 'UpdateUsername')
 | 
			
		||||
  async updateUsername(
 | 
			
		||||
    data: UpdateUsernameRequest,
 | 
			
		||||
  ): Promise<UsernamePresenter> {
 | 
			
		||||
    try {
 | 
			
		||||
      const username: Username = await this._commandBus.execute(
 | 
			
		||||
        new UpdateUsernameCommand(data),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return this._mapper.map(username, Username, UsernamePresenter);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      if (e instanceof DatabaseException) {
 | 
			
		||||
        if (e.message.includes('Already exists')) {
 | 
			
		||||
          throw new RpcException({
 | 
			
		||||
            code: 6,
 | 
			
		||||
            message: 'Username already exists',
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      throw new RpcException({
 | 
			
		||||
        code: 7,
 | 
			
		||||
        message: 'Permission denied',
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @GrpcMethod('AuthService', 'UpdatePassword')
 | 
			
		||||
  async updatePassword(data: UpdatePasswordRequest): Promise<AuthPresenter> {
 | 
			
		||||
    try {
 | 
			
		||||
      const auth: Auth = await this._commandBus.execute(
 | 
			
		||||
        new UpdatePasswordCommand(data),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      return this._mapper.map(auth, Auth, AuthPresenter);
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
| 
						 | 
				
			
			@ -67,4 +134,28 @@ export class AuthController {
 | 
			
		|||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @GrpcMethod('AuthService', 'DeleteUsername')
 | 
			
		||||
  async deleteUsername(data: DeleteUsernameRequest) {
 | 
			
		||||
    try {
 | 
			
		||||
      return await this._commandBus.execute(new DeleteUsernameCommand(data));
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      throw new RpcException({
 | 
			
		||||
        code: 7,
 | 
			
		||||
        message: 'Permission denied',
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @GrpcMethod('AuthService', 'Delete')
 | 
			
		||||
  async deleteAuth(data: DeleteAuthRequest) {
 | 
			
		||||
    try {
 | 
			
		||||
      return await this._commandBus.execute(new DeleteAuthCommand(data));
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      throw new RpcException({
 | 
			
		||||
        code: 7,
 | 
			
		||||
        message: 'Permission denied',
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,4 @@ import { AutoMap } from '@automapper/classes';
 | 
			
		|||
export class AuthPresenter {
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  uuid: string;
 | 
			
		||||
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  username: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,10 +3,13 @@ syntax = "proto3";
 | 
			
		|||
package auth;
 | 
			
		||||
 | 
			
		||||
service AuthService {
 | 
			
		||||
  rpc Validate(AuthByUsernamePassword) returns (AuthByUuid);
 | 
			
		||||
  rpc Create(AuthWithPassword) returns (Auth);
 | 
			
		||||
  rpc Update(AuthWithPassword) returns (Auth);
 | 
			
		||||
  rpc Delete(AuthByUuid) returns (Empty);
 | 
			
		||||
  rpc Validate(AuthByUsernamePassword) returns (Uuid);
 | 
			
		||||
  rpc Create(Auth) returns (Uuid);
 | 
			
		||||
  rpc AddUsername(Username) returns (Uuid);
 | 
			
		||||
  rpc UpdatePassword(Password) returns (Uuid);
 | 
			
		||||
  rpc UpdateUsername(Username) returns (Uuid);
 | 
			
		||||
  rpc DeleteUsername(Username) returns (Uuid);
 | 
			
		||||
  rpc Delete(Uuid) returns (Empty);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message AuthByUsernamePassword {
 | 
			
		||||
| 
						 | 
				
			
			@ -14,19 +17,31 @@ message AuthByUsernamePassword {
 | 
			
		|||
  string password = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message AuthWithPassword {
 | 
			
		||||
  string uuid = 1;
 | 
			
		||||
  string username = 2;
 | 
			
		||||
  string password = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message AuthByUuid {
 | 
			
		||||
  string uuid = 1;
 | 
			
		||||
enum Type {
 | 
			
		||||
  EMAIL = 0;
 | 
			
		||||
  PHONE = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message Auth {
 | 
			
		||||
  string uuid = 1;
 | 
			
		||||
  string username = 2;
 | 
			
		||||
  string password = 3;
 | 
			
		||||
  Type type = 4;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message Password {
 | 
			
		||||
  string uuid = 1;
 | 
			
		||||
  string password = 2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message Username {
 | 
			
		||||
  string uuid = 1;
 | 
			
		||||
  string username = 2;
 | 
			
		||||
  Type type = 3;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message Uuid {
 | 
			
		||||
  string uuid = 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
message Empty {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
import { AutoMap } from '@automapper/classes';
 | 
			
		||||
 | 
			
		||||
export class UsernamePresenter {
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  uuid: string;
 | 
			
		||||
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  username: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
import { Injectable } from '@nestjs/common';
 | 
			
		||||
import { AuthNZRepository } from '../../../database/src/domain/authnz-repository';
 | 
			
		||||
import { Username } from '../../domain/entities/username';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class UsernameRepository extends AuthNZRepository<Username> {
 | 
			
		||||
  protected _model = 'username';
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -6,17 +6,27 @@ import { CreateAuthUseCase } from './domain/usecases/create-auth.usecase';
 | 
			
		|||
import { ValidateAuthUseCase } from './domain/usecases/validate-auth.usecase';
 | 
			
		||||
import { AuthProfile } from './mappers/auth.profile';
 | 
			
		||||
import { AuthRepository } from './adapters/secondaries/auth.repository';
 | 
			
		||||
import { UpdateAuthUseCase } from './domain/usecases/update-auth.usecase';
 | 
			
		||||
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 { DeleteAuthUseCase } from './domain/usecases/delete-auth.usecase';
 | 
			
		||||
 | 
			
		||||
@Module({
 | 
			
		||||
  imports: [DatabaseModule, CqrsModule],
 | 
			
		||||
  controllers: [AuthController],
 | 
			
		||||
  providers: [
 | 
			
		||||
    AuthProfile,
 | 
			
		||||
    UsernameProfile,
 | 
			
		||||
    AuthRepository,
 | 
			
		||||
    ValidateAuthUseCase,
 | 
			
		||||
    CreateAuthUseCase,
 | 
			
		||||
    UpdateAuthUseCase,
 | 
			
		||||
    AddUsernameUseCase,
 | 
			
		||||
    UpdateUsernameUseCase,
 | 
			
		||||
    UpdatePasswordUseCase,
 | 
			
		||||
    DeleteUsernameUseCase,
 | 
			
		||||
    DeleteAuthUseCase,
 | 
			
		||||
  ],
 | 
			
		||||
  exports: [],
 | 
			
		||||
})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
import { AddUsernameRequest } from '../domain/dtos/add-username.request';
 | 
			
		||||
 | 
			
		||||
export class AddUsernameCommand {
 | 
			
		||||
  readonly addUsernameRequest: AddUsernameRequest;
 | 
			
		||||
 | 
			
		||||
  constructor(request: AddUsernameRequest) {
 | 
			
		||||
    this.addUsernameRequest = request;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
import { DeleteAuthRequest } from '../domain/dtos/delete-auth.request';
 | 
			
		||||
 | 
			
		||||
export class DeleteAuthCommand {
 | 
			
		||||
  readonly deleteAuthRequest: DeleteAuthRequest;
 | 
			
		||||
 | 
			
		||||
  constructor(request: DeleteAuthRequest) {
 | 
			
		||||
    this.deleteAuthRequest = request;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
import { DeleteUsernameRequest } from '../domain/dtos/delete-username.request';
 | 
			
		||||
 | 
			
		||||
export class DeleteUsernameCommand {
 | 
			
		||||
  readonly deleteUsernameRequest: DeleteUsernameRequest;
 | 
			
		||||
 | 
			
		||||
  constructor(request: DeleteUsernameRequest) {
 | 
			
		||||
    this.deleteUsernameRequest = request;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,9 +0,0 @@
 | 
			
		|||
import { UpdateAuthRequest } from '../domain/dtos/update-auth.request';
 | 
			
		||||
 | 
			
		||||
export class UpdateAuthCommand {
 | 
			
		||||
  readonly updateAuthRequest: UpdateAuthRequest;
 | 
			
		||||
 | 
			
		||||
  constructor(request: UpdateAuthRequest) {
 | 
			
		||||
    this.updateAuthRequest = request;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
import { UpdatePasswordRequest } from '../domain/dtos/update-password.request';
 | 
			
		||||
 | 
			
		||||
export class UpdatePasswordCommand {
 | 
			
		||||
  readonly updatePasswordRequest: UpdatePasswordRequest;
 | 
			
		||||
 | 
			
		||||
  constructor(request: UpdatePasswordRequest) {
 | 
			
		||||
    this.updatePasswordRequest = request;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
import { UpdateUsernameRequest } from '../domain/dtos/update-username.request';
 | 
			
		||||
 | 
			
		||||
export class UpdateUsernameCommand {
 | 
			
		||||
  readonly updateUsernameRequest: UpdateUsernameRequest;
 | 
			
		||||
 | 
			
		||||
  constructor(request: UpdateUsernameRequest) {
 | 
			
		||||
    this.updateUsernameRequest = request;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
import { AutoMap } from '@automapper/classes';
 | 
			
		||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
 | 
			
		||||
import { Type } from './type.enum';
 | 
			
		||||
 | 
			
		||||
export class AddUsernameRequest {
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  uuid: string;
 | 
			
		||||
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  username: string;
 | 
			
		||||
 | 
			
		||||
  @IsEnum(Type)
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  type: Type;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,8 +1,10 @@
 | 
			
		|||
import { AutoMap } from '@automapper/classes';
 | 
			
		||||
import { IsNotEmpty, IsString } from 'class-validator';
 | 
			
		||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
 | 
			
		||||
import { Type } from './type.enum';
 | 
			
		||||
 | 
			
		||||
export class CreateAuthRequest {
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  uuid: string;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -15,4 +17,9 @@ export class CreateAuthRequest {
 | 
			
		|||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  password: string;
 | 
			
		||||
 | 
			
		||||
  @IsEnum(Type)
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  type: Type;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
import { AutoMap } from '@automapper/classes';
 | 
			
		||||
import { IsNotEmpty, IsString } from 'class-validator';
 | 
			
		||||
 | 
			
		||||
export class DeleteAuthRequest {
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  uuid: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,9 @@
 | 
			
		|||
import { AutoMap } from '@automapper/classes';
 | 
			
		||||
import { IsNotEmpty, IsString } from 'class-validator';
 | 
			
		||||
 | 
			
		||||
export class DeleteUsernameRequest {
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  username: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
export enum Type {
 | 
			
		||||
  EMAIL = 'EMAIL',
 | 
			
		||||
  PHONE = 'PHONE',
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,16 +0,0 @@
 | 
			
		|||
import { AutoMap } from '@automapper/classes';
 | 
			
		||||
import { IsString } from 'class-validator';
 | 
			
		||||
 | 
			
		||||
export class UpdateAuthRequest {
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  uuid: string;
 | 
			
		||||
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  username?: string;
 | 
			
		||||
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  password?: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
import { AutoMap } from '@automapper/classes';
 | 
			
		||||
import { IsNotEmpty, IsString } from 'class-validator';
 | 
			
		||||
 | 
			
		||||
export class UpdatePasswordRequest {
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  uuid: string;
 | 
			
		||||
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  password: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
import { AutoMap } from '@automapper/classes';
 | 
			
		||||
import { IsEnum, IsNotEmpty, IsString } from 'class-validator';
 | 
			
		||||
import { Type } from './type.enum';
 | 
			
		||||
 | 
			
		||||
export class UpdateUsernameRequest {
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  uuid: string;
 | 
			
		||||
 | 
			
		||||
  @IsString()
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  username: string;
 | 
			
		||||
 | 
			
		||||
  @IsEnum(Type)
 | 
			
		||||
  @IsNotEmpty()
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  type: Type;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,9 +4,5 @@ export class Auth {
 | 
			
		|||
  @AutoMap()
 | 
			
		||||
  uuid: string;
 | 
			
		||||
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  username: string;
 | 
			
		||||
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  password: string;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
import { AutoMap } from '@automapper/classes';
 | 
			
		||||
import { Type } from '../dtos/type.enum';
 | 
			
		||||
 | 
			
		||||
export class Username {
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  uuid: string;
 | 
			
		||||
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  username: string;
 | 
			
		||||
 | 
			
		||||
  @AutoMap()
 | 
			
		||||
  type: Type;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
import { CommandHandler } from '@nestjs/cqrs';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
import { AddUsernameCommand } from '../../commands/add-username.command';
 | 
			
		||||
import { Username } from '../entities/username';
 | 
			
		||||
 | 
			
		||||
@CommandHandler(AddUsernameCommand)
 | 
			
		||||
export class AddUsernameUseCase {
 | 
			
		||||
  constructor(private readonly _usernameRepository: UsernameRepository) {}
 | 
			
		||||
 | 
			
		||||
  async execute(command: AddUsernameCommand): Promise<Username> {
 | 
			
		||||
    const { uuid, username, type } = command.addUsernameRequest;
 | 
			
		||||
    try {
 | 
			
		||||
      return await this._usernameRepository.create({
 | 
			
		||||
        uuid,
 | 
			
		||||
        type,
 | 
			
		||||
        username,
 | 
			
		||||
      });
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      throw e;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3,18 +3,33 @@ import { AuthRepository } from '../../adapters/secondaries/auth.repository';
 | 
			
		|||
import { CreateAuthCommand } from '../../commands/create-auth.command';
 | 
			
		||||
import { Auth } from '../entities/auth';
 | 
			
		||||
import * as bcrypt from 'bcrypt';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
 | 
			
		||||
@CommandHandler(CreateAuthCommand)
 | 
			
		||||
export class CreateAuthUseCase {
 | 
			
		||||
  constructor(private readonly _repository: AuthRepository) {}
 | 
			
		||||
  constructor(
 | 
			
		||||
    private readonly _authRepository: AuthRepository,
 | 
			
		||||
    private readonly _usernameRepository: UsernameRepository,
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  async execute(command: CreateAuthCommand): Promise<Auth> {
 | 
			
		||||
    const { password, ...authWithoutPassword } = command.createAuthRequest;
 | 
			
		||||
    const { uuid, password, ...username } = command.createAuthRequest;
 | 
			
		||||
    const hash = await bcrypt.hash(password, 10);
 | 
			
		||||
 | 
			
		||||
    return this._repository.create({
 | 
			
		||||
      password: hash,
 | 
			
		||||
      ...authWithoutPassword,
 | 
			
		||||
    });
 | 
			
		||||
    try {
 | 
			
		||||
      const auth = await this._authRepository.create({
 | 
			
		||||
        uuid,
 | 
			
		||||
        password: hash,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      await this._usernameRepository.create({
 | 
			
		||||
        uuid,
 | 
			
		||||
        ...username,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return auth;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      throw e;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
import { CommandHandler } from '@nestjs/cqrs';
 | 
			
		||||
import { AuthRepository } from '../../adapters/secondaries/auth.repository';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
import { DeleteAuthCommand } from '../../commands/delete-auth.command';
 | 
			
		||||
 | 
			
		||||
@CommandHandler(DeleteAuthCommand)
 | 
			
		||||
export class DeleteAuthUseCase {
 | 
			
		||||
  constructor(
 | 
			
		||||
    private readonly _authRepository: AuthRepository,
 | 
			
		||||
    private readonly _usernameRepository: UsernameRepository,
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  async execute(command: DeleteAuthCommand) {
 | 
			
		||||
    try {
 | 
			
		||||
      const usernames = await this._usernameRepository.findAll(1, 99, {
 | 
			
		||||
        uuid: command.deleteAuthRequest.uuid,
 | 
			
		||||
      });
 | 
			
		||||
      usernames.data.map(
 | 
			
		||||
        async (username) =>
 | 
			
		||||
          await this._usernameRepository.delete({
 | 
			
		||||
            username: username.username,
 | 
			
		||||
          }),
 | 
			
		||||
      );
 | 
			
		||||
      return await this._authRepository.delete({
 | 
			
		||||
        uuid: command.deleteAuthRequest.uuid,
 | 
			
		||||
      });
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      throw e;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,27 @@
 | 
			
		|||
import { UnauthorizedException } from '@nestjs/common';
 | 
			
		||||
import { CommandHandler } from '@nestjs/cqrs';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
import { DeleteUsernameCommand } from '../../commands/delete-username.command';
 | 
			
		||||
 | 
			
		||||
@CommandHandler(DeleteUsernameCommand)
 | 
			
		||||
export class DeleteUsernameUseCase {
 | 
			
		||||
  constructor(private readonly _usernameRepository: UsernameRepository) {}
 | 
			
		||||
 | 
			
		||||
  async execute(command: DeleteUsernameCommand) {
 | 
			
		||||
    try {
 | 
			
		||||
      const { username } = command.deleteUsernameRequest;
 | 
			
		||||
      const usernameFound = await this._usernameRepository.findOne({
 | 
			
		||||
        username,
 | 
			
		||||
      });
 | 
			
		||||
      const usernames = await this._usernameRepository.findAll(1, 1, {
 | 
			
		||||
        uuid: usernameFound.uuid,
 | 
			
		||||
      });
 | 
			
		||||
      if (usernames.total > 1) {
 | 
			
		||||
        return await this._usernameRepository.delete({ username });
 | 
			
		||||
      }
 | 
			
		||||
      throw new UnauthorizedException();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      throw e;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,29 +0,0 @@
 | 
			
		|||
import { Mapper } from '@automapper/core';
 | 
			
		||||
import { InjectMapper } from '@automapper/nestjs';
 | 
			
		||||
import { CommandHandler } from '@nestjs/cqrs';
 | 
			
		||||
import { AuthRepository } from '../../adapters/secondaries/auth.repository';
 | 
			
		||||
import { UpdateAuthCommand } from '../../commands/update-auth.command';
 | 
			
		||||
import { Auth } from '../entities/auth';
 | 
			
		||||
import * as bcrypt from 'bcrypt';
 | 
			
		||||
 | 
			
		||||
@CommandHandler(UpdateAuthCommand)
 | 
			
		||||
export class UpdateAuthUseCase {
 | 
			
		||||
  constructor(
 | 
			
		||||
    private readonly _repository: AuthRepository,
 | 
			
		||||
    @InjectMapper() private readonly _mapper: Mapper,
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  async execute(command: UpdateAuthCommand): Promise<Auth> {
 | 
			
		||||
    const { uuid, username, password } = command.updateAuthRequest;
 | 
			
		||||
    const request = {};
 | 
			
		||||
    if (username) {
 | 
			
		||||
      request['username'] = username;
 | 
			
		||||
    }
 | 
			
		||||
    if (password) {
 | 
			
		||||
      const hash = await bcrypt.hash(password, 10);
 | 
			
		||||
      request['password'] = hash;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return this._repository.update(uuid, request);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,23 @@
 | 
			
		|||
import { CommandHandler } from '@nestjs/cqrs';
 | 
			
		||||
import { AuthRepository } from '../../adapters/secondaries/auth.repository';
 | 
			
		||||
import { Auth } from '../entities/auth';
 | 
			
		||||
import * as bcrypt from 'bcrypt';
 | 
			
		||||
import { UpdatePasswordCommand } from '../../commands/update-password.command';
 | 
			
		||||
 | 
			
		||||
@CommandHandler(UpdatePasswordCommand)
 | 
			
		||||
export class UpdatePasswordUseCase {
 | 
			
		||||
  constructor(private readonly _authRepository: AuthRepository) {}
 | 
			
		||||
 | 
			
		||||
  async execute(command: UpdatePasswordCommand): Promise<Auth> {
 | 
			
		||||
    const { uuid, password } = command.updatePasswordRequest;
 | 
			
		||||
    const hash = await bcrypt.hash(password, 10);
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      return await this._authRepository.update(uuid, {
 | 
			
		||||
        password: hash,
 | 
			
		||||
      });
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      throw e;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
import { CommandHandler } from '@nestjs/cqrs';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
import { UpdateUsernameCommand } from '../../commands/update-username.command';
 | 
			
		||||
import { Username } from '../entities/username';
 | 
			
		||||
 | 
			
		||||
@CommandHandler(UpdateUsernameCommand)
 | 
			
		||||
export class UpdateUsernameUseCase {
 | 
			
		||||
  constructor(private readonly _usernameRepository: UsernameRepository) {}
 | 
			
		||||
 | 
			
		||||
  async execute(command: UpdateUsernameCommand): Promise<Username> {
 | 
			
		||||
    const { uuid, username, type } = command.updateUsernameRequest;
 | 
			
		||||
    try {
 | 
			
		||||
      return await this._usernameRepository.updateWhere(
 | 
			
		||||
        {
 | 
			
		||||
          uuid_type: {
 | 
			
		||||
            uuid,
 | 
			
		||||
            type,
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          username,
 | 
			
		||||
        },
 | 
			
		||||
      );
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      throw e;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -4,20 +4,31 @@ import { ValidateAuthQuery } from '../../queries/validate-auth.query';
 | 
			
		|||
import { Auth } from '../entities/auth';
 | 
			
		||||
import * as bcrypt from 'bcrypt';
 | 
			
		||||
import { NotFoundException, UnauthorizedException } from '@nestjs/common';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
 | 
			
		||||
@QueryHandler(ValidateAuthQuery)
 | 
			
		||||
export class ValidateAuthUseCase {
 | 
			
		||||
  constructor(private readonly _authRepository: AuthRepository) {}
 | 
			
		||||
  constructor(
 | 
			
		||||
    private readonly _authRepository: AuthRepository,
 | 
			
		||||
    private readonly _usernameRepository: UsernameRepository,
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  async execute(validate: ValidateAuthQuery): Promise<Auth> {
 | 
			
		||||
    const auth = await this._authRepository.findOne({
 | 
			
		||||
      username: validate.username,
 | 
			
		||||
    });
 | 
			
		||||
    if (auth) {
 | 
			
		||||
      const isMatch = await bcrypt.compare(validate.password, auth.password);
 | 
			
		||||
      if (isMatch) return auth;
 | 
			
		||||
    try {
 | 
			
		||||
      const username = await this._usernameRepository.findOne({
 | 
			
		||||
        username: validate.username,
 | 
			
		||||
      });
 | 
			
		||||
      const auth = await this._authRepository.findOne({
 | 
			
		||||
        uuid: username.uuid,
 | 
			
		||||
      });
 | 
			
		||||
      if (auth) {
 | 
			
		||||
        const isMatch = await bcrypt.compare(validate.password, auth.password);
 | 
			
		||||
        if (isMatch) return auth;
 | 
			
		||||
        throw new UnauthorizedException();
 | 
			
		||||
      }
 | 
			
		||||
      throw new NotFoundException();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      throw new UnauthorizedException();
 | 
			
		||||
    }
 | 
			
		||||
    throw new NotFoundException();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ export class AuthProfile extends AutomapperProfile {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  override get profile() {
 | 
			
		||||
    return (mapper) => {
 | 
			
		||||
    return (mapper: any) => {
 | 
			
		||||
      createMap(mapper, Auth, AuthPresenter);
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
import { createMap, Mapper } from '@automapper/core';
 | 
			
		||||
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
 | 
			
		||||
import { Injectable } from '@nestjs/common';
 | 
			
		||||
import { UsernamePresenter } from '../adapters/primaries/username.presenter';
 | 
			
		||||
import { Username } from '../domain/entities/username';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class UsernameProfile extends AutomapperProfile {
 | 
			
		||||
  constructor(@InjectMapper() mapper: Mapper) {
 | 
			
		||||
    super(mapper);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  override get profile() {
 | 
			
		||||
    return (mapper: any) => {
 | 
			
		||||
      createMap(mapper, Username, UsernamePresenter);
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
import { classes } from '@automapper/classes';
 | 
			
		||||
import { AutomapperModule } from '@automapper/nestjs';
 | 
			
		||||
import { Test, TestingModule } from '@nestjs/testing';
 | 
			
		||||
import { AuthProfile } from '../../mappers/auth.profile';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
import { Username } from '../../domain/entities/username';
 | 
			
		||||
import { Type } from '../../domain/dtos/type.enum';
 | 
			
		||||
import { AddUsernameRequest } from '../../domain/dtos/add-username.request';
 | 
			
		||||
import { AddUsernameCommand } from '../../commands/add-username.command';
 | 
			
		||||
import { AddUsernameUseCase } from '../../domain/usecases/add-username.usecase';
 | 
			
		||||
 | 
			
		||||
const addUsernameRequest: AddUsernameRequest = {
 | 
			
		||||
  uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
 | 
			
		||||
  username: '0611223344',
 | 
			
		||||
  type: Type.PHONE,
 | 
			
		||||
};
 | 
			
		||||
const addUsernameCommand: AddUsernameCommand = new AddUsernameCommand(
 | 
			
		||||
  addUsernameRequest,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const mockUsernameRepository = {
 | 
			
		||||
  create: jest.fn().mockResolvedValue(addUsernameRequest),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('AddUsernameUseCase', () => {
 | 
			
		||||
  let addUsernameUseCase: AddUsernameUseCase;
 | 
			
		||||
 | 
			
		||||
  beforeAll(async () => {
 | 
			
		||||
    const module: TestingModule = await Test.createTestingModule({
 | 
			
		||||
      imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })],
 | 
			
		||||
      providers: [
 | 
			
		||||
        {
 | 
			
		||||
          provide: UsernameRepository,
 | 
			
		||||
          useValue: mockUsernameRepository,
 | 
			
		||||
        },
 | 
			
		||||
        AddUsernameUseCase,
 | 
			
		||||
        AuthProfile,
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
    addUsernameUseCase = module.get<AddUsernameUseCase>(AddUsernameUseCase);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should be defined', () => {
 | 
			
		||||
    expect(addUsernameUseCase).toBeDefined();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('execute', () => {
 | 
			
		||||
    it('should add a username for phone type', async () => {
 | 
			
		||||
      const addedUsername: Username = await addUsernameUseCase.execute(
 | 
			
		||||
        addUsernameCommand,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      expect(addedUsername.username).toBe(addUsernameRequest.username);
 | 
			
		||||
      expect(addedUsername.type).toBe(addUsernameRequest.type);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -6,24 +6,33 @@ import { CreateAuthCommand } from '../../commands/create-auth.command';
 | 
			
		|||
import { CreateAuthRequest } from '../../domain/dtos/create-auth.request';
 | 
			
		||||
import { Auth } from '../../domain/entities/auth';
 | 
			
		||||
import { CreateAuthUseCase } from '../../domain/usecases/create-auth.usecase';
 | 
			
		||||
import { AuthProfile } from '../../mappers/auth.profile';
 | 
			
		||||
import * as bcrypt from 'bcrypt';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
import { Type } from '../../domain/dtos/type.enum';
 | 
			
		||||
 | 
			
		||||
const newAuthRequest: CreateAuthRequest = {
 | 
			
		||||
  uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
 | 
			
		||||
  username: 'john.doe@email.com',
 | 
			
		||||
  password: 'John123',
 | 
			
		||||
  type: Type.EMAIL,
 | 
			
		||||
};
 | 
			
		||||
const newAuthCommand: CreateAuthCommand = new CreateAuthCommand(newAuthRequest);
 | 
			
		||||
 | 
			
		||||
const mockAuthRepository = {
 | 
			
		||||
  create: jest.fn().mockResolvedValue({
 | 
			
		||||
    uuid: newAuthRequest.uuid,
 | 
			
		||||
    username: newAuthRequest.username,
 | 
			
		||||
    password: bcrypt.hashSync(newAuthRequest.password, 10),
 | 
			
		||||
  }),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const mockUsernameRepository = {
 | 
			
		||||
  create: jest.fn().mockResolvedValue({
 | 
			
		||||
    uuid: newAuthRequest.uuid,
 | 
			
		||||
    username: newAuthRequest.username,
 | 
			
		||||
    type: newAuthRequest.type,
 | 
			
		||||
  }),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('CreateAuthUseCase', () => {
 | 
			
		||||
  let createAuthUseCase: CreateAuthUseCase;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -35,8 +44,11 @@ describe('CreateAuthUseCase', () => {
 | 
			
		|||
          provide: AuthRepository,
 | 
			
		||||
          useValue: mockAuthRepository,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          provide: UsernameRepository,
 | 
			
		||||
          useValue: mockUsernameRepository,
 | 
			
		||||
        },
 | 
			
		||||
        CreateAuthUseCase,
 | 
			
		||||
        AuthProfile,
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -48,11 +60,9 @@ describe('CreateAuthUseCase', () => {
 | 
			
		|||
  });
 | 
			
		||||
 | 
			
		||||
  describe('execute', () => {
 | 
			
		||||
    it('should create an auth and returns new entity object', async () => {
 | 
			
		||||
    it('should create an auth with an encrypted password', async () => {
 | 
			
		||||
      const newAuth: Auth = await createAuthUseCase.execute(newAuthCommand);
 | 
			
		||||
 | 
			
		||||
      expect(newAuth.username).toBe(newAuthRequest.username);
 | 
			
		||||
 | 
			
		||||
      expect(
 | 
			
		||||
        bcrypt.compareSync(newAuthRequest.password, newAuth.password),
 | 
			
		||||
      ).toBeTruthy();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,78 @@
 | 
			
		|||
import { classes } from '@automapper/classes';
 | 
			
		||||
import { AutomapperModule } from '@automapper/nestjs';
 | 
			
		||||
import { Test, TestingModule } from '@nestjs/testing';
 | 
			
		||||
import { AuthRepository } from '../../adapters/secondaries/auth.repository';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
import { DeleteAuthCommand } from '../../commands/delete-auth.command';
 | 
			
		||||
import { DeleteAuthRequest } from '../../domain/dtos/delete-auth.request';
 | 
			
		||||
import { Type } from '../../domain/dtos/type.enum';
 | 
			
		||||
import { DeleteAuthUseCase } from '../../domain/usecases/delete-auth.usecase';
 | 
			
		||||
 | 
			
		||||
const usernames = {
 | 
			
		||||
  data: [
 | 
			
		||||
    {
 | 
			
		||||
      uuid: 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e',
 | 
			
		||||
      username: 'john.doe@email.com',
 | 
			
		||||
      type: Type.EMAIL,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      uuid: 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e',
 | 
			
		||||
      username: '0611223344',
 | 
			
		||||
      type: Type.PHONE,
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
  total: 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const deleteAuthRequest: DeleteAuthRequest = {
 | 
			
		||||
  uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
 | 
			
		||||
};
 | 
			
		||||
const deleteAuthCommand: DeleteAuthCommand = new DeleteAuthCommand(
 | 
			
		||||
  deleteAuthRequest,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const mockAuthRepository = {
 | 
			
		||||
  delete: jest.fn().mockResolvedValue(undefined),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const mockUsernameRepository = {
 | 
			
		||||
  findAll: jest.fn().mockImplementation((page, perPage, query) => {
 | 
			
		||||
    return Promise.resolve(usernames);
 | 
			
		||||
  }),
 | 
			
		||||
  delete: jest.fn().mockResolvedValue(undefined),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('DeleteAuthUseCase', () => {
 | 
			
		||||
  let deleteAuthUseCase: DeleteAuthUseCase;
 | 
			
		||||
 | 
			
		||||
  beforeAll(async () => {
 | 
			
		||||
    const module: TestingModule = await Test.createTestingModule({
 | 
			
		||||
      imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })],
 | 
			
		||||
      providers: [
 | 
			
		||||
        {
 | 
			
		||||
          provide: AuthRepository,
 | 
			
		||||
          useValue: mockAuthRepository,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          provide: UsernameRepository,
 | 
			
		||||
          useValue: mockUsernameRepository,
 | 
			
		||||
        },
 | 
			
		||||
        DeleteAuthUseCase,
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
    deleteAuthUseCase = module.get<DeleteAuthUseCase>(DeleteAuthUseCase);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should be defined', () => {
 | 
			
		||||
    expect(deleteAuthUseCase).toBeDefined();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('execute', () => {
 | 
			
		||||
    it('should delete an auth and its usernames', async () => {
 | 
			
		||||
      const deletedAuth = await deleteAuthUseCase.execute(deleteAuthCommand);
 | 
			
		||||
 | 
			
		||||
      expect(deletedAuth).toBe(undefined);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,106 @@
 | 
			
		|||
import { classes } from '@automapper/classes';
 | 
			
		||||
import { AutomapperModule } from '@automapper/nestjs';
 | 
			
		||||
import { UnauthorizedException } from '@nestjs/common';
 | 
			
		||||
import { Test, TestingModule } from '@nestjs/testing';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
import { DeleteUsernameCommand } from '../../commands/delete-username.command';
 | 
			
		||||
import { DeleteUsernameRequest } from '../../domain/dtos/delete-username.request';
 | 
			
		||||
import { Type } from '../../domain/dtos/type.enum';
 | 
			
		||||
import { DeleteUsernameUseCase } from '../../domain/usecases/delete-username.usecase';
 | 
			
		||||
 | 
			
		||||
const usernamesEmail = {
 | 
			
		||||
  data: [
 | 
			
		||||
    {
 | 
			
		||||
      uuid: 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e',
 | 
			
		||||
      username: 'john.doe@email.com',
 | 
			
		||||
      type: Type.EMAIL,
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      uuid: 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e',
 | 
			
		||||
      username: '0611223344',
 | 
			
		||||
      type: Type.PHONE,
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
  total: 2,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const usernamesPhone = {
 | 
			
		||||
  data: [
 | 
			
		||||
    {
 | 
			
		||||
      uuid: 'a7fa221f-dd77-481c-bb77-ae89da662c87',
 | 
			
		||||
      username: '0611223344',
 | 
			
		||||
      type: Type.PHONE,
 | 
			
		||||
    },
 | 
			
		||||
  ],
 | 
			
		||||
  total: 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const deleteUsernameEmailRequest: DeleteUsernameRequest = {
 | 
			
		||||
  username: 'john.doe@email.com',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const deleteUsernamePhoneRequest: DeleteUsernameRequest = {
 | 
			
		||||
  username: '0611223344',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const deleteUsernameEmailCommand: DeleteUsernameCommand =
 | 
			
		||||
  new DeleteUsernameCommand(deleteUsernameEmailRequest);
 | 
			
		||||
 | 
			
		||||
const deleteUsernamePhoneCommand: DeleteUsernameCommand =
 | 
			
		||||
  new DeleteUsernameCommand(deleteUsernamePhoneRequest);
 | 
			
		||||
 | 
			
		||||
const mockUsernameRepository = {
 | 
			
		||||
  findOne: jest.fn().mockImplementation((where) => {
 | 
			
		||||
    if (where.username == 'john.doe@email.com') {
 | 
			
		||||
      return { uuid: 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e' };
 | 
			
		||||
    }
 | 
			
		||||
    return { uuid: 'a7fa221f-dd77-481c-bb77-ae89da662c87' };
 | 
			
		||||
  }),
 | 
			
		||||
  findAll: jest.fn().mockImplementation((page, perPage, query) => {
 | 
			
		||||
    if (query.uuid == 'cf76af29-f75d-4f6e-bb40-4ecbcfa3356e') {
 | 
			
		||||
      return Promise.resolve(usernamesEmail);
 | 
			
		||||
    }
 | 
			
		||||
    return Promise.resolve(usernamesPhone);
 | 
			
		||||
  }),
 | 
			
		||||
  delete: jest.fn().mockResolvedValue(undefined),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('DeleteUsernameUseCase', () => {
 | 
			
		||||
  let deleteUsernameUseCase: DeleteUsernameUseCase;
 | 
			
		||||
 | 
			
		||||
  beforeAll(async () => {
 | 
			
		||||
    const module: TestingModule = await Test.createTestingModule({
 | 
			
		||||
      imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })],
 | 
			
		||||
      providers: [
 | 
			
		||||
        {
 | 
			
		||||
          provide: UsernameRepository,
 | 
			
		||||
          useValue: mockUsernameRepository,
 | 
			
		||||
        },
 | 
			
		||||
        DeleteUsernameUseCase,
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
    deleteUsernameUseCase = module.get<DeleteUsernameUseCase>(
 | 
			
		||||
      DeleteUsernameUseCase,
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should be defined', () => {
 | 
			
		||||
    expect(deleteUsernameUseCase).toBeDefined();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('execute', () => {
 | 
			
		||||
    it('should delete a username', async () => {
 | 
			
		||||
      const deletedEmailUsername = await deleteUsernameUseCase.execute(
 | 
			
		||||
        deleteUsernameEmailCommand,
 | 
			
		||||
      );
 | 
			
		||||
      expect(deletedEmailUsername).toBe(undefined);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    it('should throw an exception if auth has only one username', async () => {
 | 
			
		||||
      await expect(
 | 
			
		||||
        deleteUsernameUseCase.execute(deleteUsernamePhoneCommand),
 | 
			
		||||
      ).rejects.toBeInstanceOf(UnauthorizedException);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -1,119 +0,0 @@
 | 
			
		|||
import { classes } from '@automapper/classes';
 | 
			
		||||
import { AutomapperModule } from '@automapper/nestjs';
 | 
			
		||||
import { Test, TestingModule } from '@nestjs/testing';
 | 
			
		||||
import { UpdateAuthCommand } from '../../commands/update-auth.command';
 | 
			
		||||
import { UpdateAuthRequest } from '../../domain/dtos/update-auth.request';
 | 
			
		||||
import { Auth } from '../../domain/entities/auth';
 | 
			
		||||
import * as bcrypt from 'bcrypt';
 | 
			
		||||
import { UpdateAuthUseCase } from '../../domain/usecases/update-auth.usecase';
 | 
			
		||||
import { AuthRepository } from '../../adapters/secondaries/auth.repository';
 | 
			
		||||
import { AuthProfile } from '../../mappers/auth.profile';
 | 
			
		||||
 | 
			
		||||
const originalAuth: Auth = new Auth();
 | 
			
		||||
originalAuth.uuid = 'bb281075-1b98-4456-89d6-c643d3044a91';
 | 
			
		||||
originalAuth.username = 'john.doe@email.com';
 | 
			
		||||
originalAuth.password = 'encrypted_password';
 | 
			
		||||
 | 
			
		||||
const updateUsernameAuthRequest: UpdateAuthRequest = {
 | 
			
		||||
  uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
 | 
			
		||||
  username: 'johnny.doe@email.com',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const updatePasswordAuthRequest: UpdateAuthRequest = {
 | 
			
		||||
  uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
 | 
			
		||||
  password: 'John1234',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const updateAuthRequest: UpdateAuthRequest = {
 | 
			
		||||
  uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
 | 
			
		||||
  username: 'johnny.doe@email.com',
 | 
			
		||||
  password: 'John1234',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const updateUsernameAuthCommand: UpdateAuthCommand = new UpdateAuthCommand(
 | 
			
		||||
  updateUsernameAuthRequest,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const updatePasswordAuthCommand: UpdateAuthCommand = new UpdateAuthCommand(
 | 
			
		||||
  updatePasswordAuthRequest,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const updateAuthCommand: UpdateAuthCommand = new UpdateAuthCommand(
 | 
			
		||||
  updateAuthRequest,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const mockAuthRepository = {
 | 
			
		||||
  update: jest.fn().mockImplementation((uuid: string, params: any) => {
 | 
			
		||||
    if (params.username && params.password) {
 | 
			
		||||
      const auth: Auth = { ...originalAuth };
 | 
			
		||||
      auth.username = params.username;
 | 
			
		||||
      auth.password = params.password;
 | 
			
		||||
      return Promise.resolve(auth);
 | 
			
		||||
    }
 | 
			
		||||
    if (params.username) {
 | 
			
		||||
      const auth: Auth = { ...originalAuth };
 | 
			
		||||
      auth.username = params.username;
 | 
			
		||||
      return Promise.resolve(auth);
 | 
			
		||||
    }
 | 
			
		||||
    if (params.password) {
 | 
			
		||||
      const auth: Auth = { ...originalAuth };
 | 
			
		||||
      auth.password = params.password;
 | 
			
		||||
      return Promise.resolve(auth);
 | 
			
		||||
    }
 | 
			
		||||
  }),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('UpdateAuthUseCase', () => {
 | 
			
		||||
  let updateAuthUseCase: UpdateAuthUseCase;
 | 
			
		||||
 | 
			
		||||
  beforeAll(async () => {
 | 
			
		||||
    const module: TestingModule = await Test.createTestingModule({
 | 
			
		||||
      imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })],
 | 
			
		||||
 | 
			
		||||
      providers: [
 | 
			
		||||
        {
 | 
			
		||||
          provide: AuthRepository,
 | 
			
		||||
          useValue: mockAuthRepository,
 | 
			
		||||
        },
 | 
			
		||||
        UpdateAuthUseCase,
 | 
			
		||||
        AuthProfile,
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
    updateAuthUseCase = module.get<UpdateAuthUseCase>(UpdateAuthUseCase);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should be defined', () => {
 | 
			
		||||
    expect(updateAuthUseCase).toBeDefined();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('execute', () => {
 | 
			
		||||
    it('should update the username of an Auth', async () => {
 | 
			
		||||
      const updatedAuth: Auth = await updateAuthUseCase.execute(
 | 
			
		||||
        updateUsernameAuthCommand,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      expect(updatedAuth.username).toBe(updateUsernameAuthRequest.username);
 | 
			
		||||
    });
 | 
			
		||||
    it('should update the password of an Auth', async () => {
 | 
			
		||||
      const updatedAuth: Auth = await updateAuthUseCase.execute(
 | 
			
		||||
        updatePasswordAuthCommand,
 | 
			
		||||
      );
 | 
			
		||||
      expect(
 | 
			
		||||
        bcrypt.compareSync(
 | 
			
		||||
          updatePasswordAuthRequest.password,
 | 
			
		||||
          updatedAuth.password,
 | 
			
		||||
        ),
 | 
			
		||||
      ).toBeTruthy();
 | 
			
		||||
    });
 | 
			
		||||
    it('should update the username and the password of an Auth', async () => {
 | 
			
		||||
      const updatedAuth: Auth = await updateAuthUseCase.execute(
 | 
			
		||||
        updateAuthCommand,
 | 
			
		||||
      );
 | 
			
		||||
      expect(updatedAuth.username).toBe(updateAuthRequest.username);
 | 
			
		||||
      expect(
 | 
			
		||||
        bcrypt.compareSync(updateAuthRequest.password, updatedAuth.password),
 | 
			
		||||
      ).toBeTruthy();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
import { classes } from '@automapper/classes';
 | 
			
		||||
import { AutomapperModule } from '@automapper/nestjs';
 | 
			
		||||
import { Test, TestingModule } from '@nestjs/testing';
 | 
			
		||||
import { AuthRepository } from '../../adapters/secondaries/auth.repository';
 | 
			
		||||
import { Auth } from '../../domain/entities/auth';
 | 
			
		||||
import * as bcrypt from 'bcrypt';
 | 
			
		||||
import { UpdatePasswordRequest } from '../../domain/dtos/update-password.request';
 | 
			
		||||
import { UpdatePasswordCommand } from '../../commands/update-password.command';
 | 
			
		||||
import { UpdatePasswordUseCase } from '../../domain/usecases/update-password.usecase';
 | 
			
		||||
 | 
			
		||||
const updatePasswordRequest: UpdatePasswordRequest = {
 | 
			
		||||
  uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
 | 
			
		||||
  password: 'John123',
 | 
			
		||||
};
 | 
			
		||||
const updatePasswordCommand: UpdatePasswordCommand = new UpdatePasswordCommand(
 | 
			
		||||
  updatePasswordRequest,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const mockAuthRepository = {
 | 
			
		||||
  update: jest.fn().mockResolvedValue({
 | 
			
		||||
    uuid: updatePasswordRequest.uuid,
 | 
			
		||||
    password: bcrypt.hashSync(updatePasswordRequest.password, 10),
 | 
			
		||||
  }),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('UpdatePasswordUseCase', () => {
 | 
			
		||||
  let updatePasswordUseCase: UpdatePasswordUseCase;
 | 
			
		||||
 | 
			
		||||
  beforeAll(async () => {
 | 
			
		||||
    const module: TestingModule = await Test.createTestingModule({
 | 
			
		||||
      imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })],
 | 
			
		||||
      providers: [
 | 
			
		||||
        {
 | 
			
		||||
          provide: AuthRepository,
 | 
			
		||||
          useValue: mockAuthRepository,
 | 
			
		||||
        },
 | 
			
		||||
        UpdatePasswordUseCase,
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
    updatePasswordUseCase = module.get<UpdatePasswordUseCase>(
 | 
			
		||||
      UpdatePasswordUseCase,
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should be defined', () => {
 | 
			
		||||
    expect(updatePasswordUseCase).toBeDefined();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('execute', () => {
 | 
			
		||||
    it('should update an auth with an new encrypted password', async () => {
 | 
			
		||||
      const newAuth: Auth = await updatePasswordUseCase.execute(
 | 
			
		||||
        updatePasswordCommand,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      expect(
 | 
			
		||||
        bcrypt.compareSync(updatePasswordRequest.password, newAuth.password),
 | 
			
		||||
      ).toBeTruthy();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,58 @@
 | 
			
		|||
import { classes } from '@automapper/classes';
 | 
			
		||||
import { AutomapperModule } from '@automapper/nestjs';
 | 
			
		||||
import { Test, TestingModule } from '@nestjs/testing';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
import { Username } from '../../domain/entities/username';
 | 
			
		||||
import { UpdateUsernameRequest } from '../../domain/dtos/update-username.request';
 | 
			
		||||
import { UpdateUsernameCommand } from '../../commands/update-username.command';
 | 
			
		||||
import { Type } from '../../domain/dtos/type.enum';
 | 
			
		||||
import { UpdateUsernameUseCase } from '../../domain/usecases/update-username.usecase';
 | 
			
		||||
 | 
			
		||||
const updateUsernameRequest: UpdateUsernameRequest = {
 | 
			
		||||
  uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
 | 
			
		||||
  username: 'johnny.doe@email.com',
 | 
			
		||||
  type: Type.EMAIL,
 | 
			
		||||
};
 | 
			
		||||
const updateUsernameCommand: UpdateUsernameCommand = new UpdateUsernameCommand(
 | 
			
		||||
  updateUsernameRequest,
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const mockUsernameRepository = {
 | 
			
		||||
  updateWhere: jest.fn().mockResolvedValue(updateUsernameRequest),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('UpdateUsernameUseCase', () => {
 | 
			
		||||
  let updateUsernameUseCase: UpdateUsernameUseCase;
 | 
			
		||||
 | 
			
		||||
  beforeAll(async () => {
 | 
			
		||||
    const module: TestingModule = await Test.createTestingModule({
 | 
			
		||||
      imports: [AutomapperModule.forRoot({ strategyInitializer: classes() })],
 | 
			
		||||
      providers: [
 | 
			
		||||
        {
 | 
			
		||||
          provide: UsernameRepository,
 | 
			
		||||
          useValue: mockUsernameRepository,
 | 
			
		||||
        },
 | 
			
		||||
        UpdateUsernameUseCase,
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
    updateUsernameUseCase = module.get<UpdateUsernameUseCase>(
 | 
			
		||||
      UpdateUsernameUseCase,
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should be defined', () => {
 | 
			
		||||
    expect(updateUsernameUseCase).toBeDefined();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  describe('execute', () => {
 | 
			
		||||
    it('should update a username for email type', async () => {
 | 
			
		||||
      const updatedUsername: Username = await updateUsernameUseCase.execute(
 | 
			
		||||
        updateUsernameCommand,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      expect(updatedUsername.username).toBe(updateUsernameRequest.username);
 | 
			
		||||
      expect(updatedUsername.type).toBe(updateUsernameRequest.type);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -5,17 +5,25 @@ import { AuthRepository } from '../../adapters/secondaries/auth.repository';
 | 
			
		|||
import { Auth } from '../../domain/entities/auth';
 | 
			
		||||
import * as bcrypt from 'bcrypt';
 | 
			
		||||
import { ValidateAuthUseCase } from '../../domain/usecases/validate-auth.usecase';
 | 
			
		||||
import { AuthProfile } from '../../mappers/auth.profile';
 | 
			
		||||
import { ValidateAuthQuery } from '../../queries/validate-auth.query';
 | 
			
		||||
import { UsernameRepository } from '../../adapters/secondaries/username.repository';
 | 
			
		||||
import { Type } from '../../domain/dtos/type.enum';
 | 
			
		||||
 | 
			
		||||
const mockAuthRepository = {
 | 
			
		||||
  findOne: jest.fn().mockResolvedValue({
 | 
			
		||||
    uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
 | 
			
		||||
    username: 'john.doe@email.com',
 | 
			
		||||
    password: bcrypt.hashSync('John123', 10),
 | 
			
		||||
  }),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const mockUsernameRepository = {
 | 
			
		||||
  findOne: jest.fn().mockResolvedValue({
 | 
			
		||||
    uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
 | 
			
		||||
    username: 'john.doe@email.com',
 | 
			
		||||
    type: Type.EMAIL,
 | 
			
		||||
  }),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('ValidateAuthUseCase', () => {
 | 
			
		||||
  let validateAuthUseCase: ValidateAuthUseCase;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -27,8 +35,11 @@ describe('ValidateAuthUseCase', () => {
 | 
			
		|||
          provide: AuthRepository,
 | 
			
		||||
          useValue: mockAuthRepository,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          provide: UsernameRepository,
 | 
			
		||||
          useValue: mockUsernameRepository,
 | 
			
		||||
        },
 | 
			
		||||
        ValidateAuthUseCase,
 | 
			
		||||
        AuthProfile,
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +56,6 @@ describe('ValidateAuthUseCase', () => {
 | 
			
		|||
        new ValidateAuthQuery('john.doe@email.com', 'John123'),
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      expect(auth.username).toBe('john.doe@email.com');
 | 
			
		||||
      expect(bcrypt.compareSync('John123', auth.password)).toBeTruthy();
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,9 +1,10 @@
 | 
			
		|||
import { Module } from '@nestjs/common';
 | 
			
		||||
import { AuthRepository } from '../auth/adapters/secondaries/auth.repository';
 | 
			
		||||
import { UsernameRepository } from '../auth/adapters/secondaries/username.repository';
 | 
			
		||||
import { PrismaService } from './src/adapters/secondaries/prisma-service';
 | 
			
		||||
 | 
			
		||||
@Module({
 | 
			
		||||
  providers: [PrismaService, AuthRepository],
 | 
			
		||||
  exports: [PrismaService, AuthRepository],
 | 
			
		||||
  providers: [PrismaService, AuthRepository, UsernameRepository],
 | 
			
		||||
  exports: [PrismaService, AuthRepository, UsernameRepository],
 | 
			
		||||
})
 | 
			
		||||
export class DatabaseModule {}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -143,14 +143,15 @@ export abstract class PrismaRepository<T> implements IRepository<T> {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  async delete(uuid: string): Promise<void> {
 | 
			
		||||
  async delete(where: any): Promise<void> {
 | 
			
		||||
    try {
 | 
			
		||||
      const entity = await this._prisma[this._model].delete({
 | 
			
		||||
        where: { uuid },
 | 
			
		||||
        where: where,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      return entity;
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      console.log(e);
 | 
			
		||||
      if (e instanceof PrismaClientKnownRequestError) {
 | 
			
		||||
        throw new DatabaseException(
 | 
			
		||||
          PrismaClientKnownRequestError.name,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -217,7 +217,7 @@ describe('PrismaRepository', () => {
 | 
			
		|||
      const savedUuid = fakeEntities[0].uuid;
 | 
			
		||||
 | 
			
		||||
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
 | 
			
		||||
      const res = await fakeRepository.delete(savedUuid);
 | 
			
		||||
      const res = await fakeRepository.delete({ uuid: savedUuid });
 | 
			
		||||
 | 
			
		||||
      const deletedEntity = fakeEntities.find(
 | 
			
		||||
        (entity) => entity.uuid === savedUuid,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue