Merge branch 'updatePackages' into 'main'

Update packages

See merge request v3/service/auth!51
This commit is contained in:
Sylvain Briat 2023-10-17 12:59:57 +00:00
commit 03f328b922
38 changed files with 3619 additions and 1980 deletions

5202
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "@mobicoop/auth",
"version": "0.5.0",
"version": "0.6.0",
"description": "Mobicoop V3 Auth Service",
"author": "sbriat",
"private": true,
@ -30,57 +30,57 @@
"migrate:deploy": "npx prisma migrate deploy"
},
"dependencies": {
"@golevelup/nestjs-rabbitmq": "^3.4.0",
"@grpc/grpc-js": "^1.8.0",
"@grpc/proto-loader": "^0.7.4",
"@mobicoop/ddd-library": "^1.0.0",
"@mobicoop/health-module": "^2.0.0",
"@mobicoop/message-broker-module": "^1.2.0",
"@nestjs/axios": "^1.0.1",
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.2.0",
"@nestjs/core": "^9.0.0",
"@nestjs/cqrs": "^9.0.1",
"@nestjs/event-emitter": "^2.0.0",
"@nestjs/microservices": "^9.2.1",
"@nestjs/platform-express": "^9.0.0",
"@nestjs/terminus": "^9.2.2",
"@prisma/client": "^4.7.1",
"axios": "^1.2.2",
"bcrypt": "^5.1.0",
"@golevelup/nestjs-rabbitmq": "^4.0.0",
"@grpc/grpc-js": "^1.9.5",
"@grpc/proto-loader": "^0.7.10",
"@mobicoop/ddd-library": "^2.0.0",
"@mobicoop/health-module": "^2.3.1",
"@mobicoop/message-broker-module": "^2.1.1",
"@nestjs/axios": "^3.0.0",
"@nestjs/common": "^10.2.7",
"@nestjs/config": "^3.1.1",
"@nestjs/core": "^10.2.7",
"@nestjs/cqrs": "^10.2.6",
"@nestjs/event-emitter": "^2.0.2",
"@nestjs/microservices": "^10.2.7",
"@nestjs/platform-express": "^10.2.7",
"@nestjs/terminus": "^10.1.1",
"@prisma/client": "^5.4.2",
"axios": "^1.5.1",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.2.0"
"rimraf": "^5.0.5",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@nestjs/cli": "^10.1.18",
"@nestjs/schematics": "^10.0.2",
"@nestjs/testing": "^10.2.7",
"@types/bcrypt": "^5.0.0",
"@types/express": "^4.17.13",
"@types/jest": "28.1.8",
"@types/node": "^16.0.0",
"@types/supertest": "^2.0.11",
"@types/uuid": "^9.0.0",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"dotenv-cli": "^7.2.1",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "28.1.3",
"prettier": "^2.3.2",
"prisma": "^4.7.1",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "28.0.8",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.0",
"typescript": "^4.7.4",
"uuid": "^9.0.0"
"@types/express": "^4.17.19",
"@types/jest": "29.5.5",
"@types/node": "^20.8.6",
"@types/supertest": "^2.0.14",
"@types/uuid": "^9.0.5",
"@typescript-eslint/eslint-plugin": "^6.8.0",
"@typescript-eslint/parser": "^6.8.0",
"dotenv-cli": "^7.3.0",
"eslint": "^8.51.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.1",
"jest": "29.7.0",
"prettier": "^3.0.3",
"prisma": "^5.4.2",
"source-map-support": "^0.5.21",
"supertest": "^6.3.3",
"ts-jest": "29.1.1",
"ts-loader": "^9.5.0",
"ts-node": "^10.9.1",
"tsconfig-paths": "4.2.0",
"typescript": "^5.2.2",
"uuid": "^9.0.1"
},
"jest": {
"moduleFileExtensions": [
@ -91,6 +91,7 @@
"modulePathIgnorePatterns": [
".module.ts",
".dto.ts",
".constants.ts",
".di-tokens.ts",
".response.ts",
".port.ts",
@ -108,6 +109,7 @@
"coveragePathIgnorePatterns": [
".module.ts",
".dto.ts",
".constants.ts",
".di-tokens.ts",
".response.ts",
".port.ts",

22
src/app.constants.ts Normal file
View File

@ -0,0 +1,22 @@
// service
export const SERVICE_NAME = 'auth';
// grpc
export const GRPC_AUTHENTICATION_PACKAGE_NAME = 'authentication';
export const GRPC_AUTHORIZATION_PACKAGE_NAME = 'authorization';
export const GRPC_AUTHENTICATION_SERVICE_NAME = 'AuthenticationService';
export const GRPC_AUTHORIZATION_SERVICE_NAME = 'AuthorizationService';
// messaging
export const USER_UPDATED_MESSAGE_HANDLER = 'userUpdated';
export const USER_UPDATED_ROUTING_KEY = 'user.updated';
export const USER_UPDATED_QUEUE = 'auth-user-updated';
export const USER_DELETED_MESSAGE_HANDLER = 'userDeleted';
export const USER_DELETED_ROUTING_KEY = 'user.deleted';
export const USER_DELETED_QUEUE = 'auth-user-deleted';
// health
export const GRPC_HEALTH_PACKAGE_NAME = 'health';
export const HEALTH_AUTHENTICATION_REPOSITORY = 'AuthenticationRepository';
export const HEALTH_USERNAME_REPOSITORY = 'UsernameRepository';
export const HEALTH_CRITICAL_LOGGING_KEY = 'logging.auth.health.crit';

View File

@ -15,6 +15,12 @@ import {
import { MESSAGE_PUBLISHER } from './modules/messager/messager.di-tokens';
import { MessagePublisherPort } from '@mobicoop/ddd-library';
import { MessagerModule } from './modules/messager/messager.module';
import {
HEALTH_AUTHENTICATION_REPOSITORY,
HEALTH_CRITICAL_LOGGING_KEY,
HEALTH_USERNAME_REPOSITORY,
SERVICE_NAME,
} from './app.constants';
@Module({
imports: [
@ -32,15 +38,15 @@ import { MessagerModule } from './modules/messager/messager.module';
usernameRepository: HealthRepositoryPort,
messagePublisher: MessagePublisherPort,
): Promise<HealthModuleOptions> => ({
serviceName: 'auth',
criticalLoggingKey: 'logging.auth.health.crit',
serviceName: SERVICE_NAME,
criticalLoggingKey: HEALTH_CRITICAL_LOGGING_KEY,
checkRepositories: [
{
name: 'AuthenticationRepository',
name: HEALTH_AUTHENTICATION_REPOSITORY,
repository: authenticationRepository,
},
{
name: 'UsernameRepository',
name: HEALTH_USERNAME_REPOSITORY,
repository: usernameRepository,
},
],

View File

@ -2,6 +2,11 @@ import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';
import {
GRPC_AUTHENTICATION_PACKAGE_NAME,
GRPC_AUTHORIZATION_PACKAGE_NAME,
GRPC_HEALTH_PACKAGE_NAME,
} from './app.constants';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
@ -11,7 +16,11 @@ async function bootstrap() {
app.connectMicroservice<MicroserviceOptions>({
transport: Transport.GRPC,
options: {
package: ['authentication', 'authorization', 'health'],
package: [
GRPC_AUTHENTICATION_PACKAGE_NAME,
GRPC_AUTHORIZATION_PACKAGE_NAME,
GRPC_HEALTH_PACKAGE_NAME,
],
protoPath: [
join(
__dirname,
@ -23,11 +32,11 @@ async function bootstrap() {
),
join(__dirname, 'health.proto'),
],
url: process.env.SERVICE_URL + ':' + process.env.SERVICE_PORT,
url: `${process.env.SERVICE_URL}:${process.env.SERVICE_PORT}`,
loader: { keepCase: true, enums: String },
},
});
await app.startAllMicroservices();
await app.listen(process.env.HEALTH_SERVICE_PORT);
await app.listen(process.env.HEALTH_SERVICE_PORT as unknown as number);
}
bootstrap();

View File

@ -31,14 +31,12 @@ export class AuthenticationMapper
const record: AuthenticationWriteModel = {
uuid: copy.id,
password: copy.password,
usernames: copy.usernames
? {
create: copy.usernames.map((username: UsernameProps) => ({
username: username.name,
type: username.type,
})),
}
: undefined,
usernames: {
create: copy.usernames.map((username: UsernameProps) => ({
username: username.name,
type: username.type,
})),
},
};
return record;
};
@ -54,7 +52,7 @@ export class AuthenticationMapper
usernames: record.usernames?.map((username: UsernameModel) => ({
userId: record.uuid,
name: username.username,
type: Type[username.type],
type: username.type as Type,
})),
},
});

View File

@ -13,6 +13,7 @@ import {
AuthenticationAlreadyExistsException,
UsernameAlreadyExistsException,
} from '@modules/authentication/core/domain/authentication.errors';
import { Username } from '../../types/username';
@CommandHandler(CreateAuthenticationCommand)
export class CreateAuthenticationService implements ICommandHandler {
@ -26,7 +27,11 @@ export class CreateAuthenticationService implements ICommandHandler {
await AuthenticationEntity.create({
userId: command.userId,
password: command.password,
usernames: command.usernames,
usernames: command.usernames.map((username: Username) => ({
name: username.name,
type: username.type,
userId: command.userId,
})),
});
try {
await this.authenticationRepository.insert(authentication);

View File

@ -18,9 +18,8 @@ export class DeleteAuthenticationService implements ICommandHandler {
usernames: true,
});
authentication.delete();
const isDeleted: boolean = await this.authenticationRepository.delete(
authentication,
);
const isDeleted: boolean =
await this.authenticationRepository.delete(authentication);
return isDeleted;
}
}

View File

@ -24,9 +24,8 @@ export class DeleteUsernameService implements ICommandHandler {
'Authentication must have at least one username',
);
username.delete();
const isDeleted: boolean = await this.usernameRepository.deleteUsername(
username,
);
const isDeleted: boolean =
await this.usernameRepository.deleteUsername(username);
return isDeleted;
}
}

View File

@ -1,15 +1,15 @@
import { UsernameProps } from './username.types';
import { NameProps } from './username.types';
// All properties that an Authentication has
export interface AuthenticationProps {
userId: string;
password: string;
usernames: UsernameProps[];
usernames: NameProps[];
}
// Properties that are needed for an Authentication creation
export interface CreateAuthenticationProps {
userId: string;
password: string;
usernames: UsernameProps[];
usernames: NameProps[];
}

View File

@ -18,8 +18,8 @@ export class UsernameEntity extends AggregateRoot<UsernameProps> {
const username = new UsernameEntity({
id: props.name,
props: {
name: props.name,
userId: props.userId,
name: props.name,
type: props.type,
},
});

View File

@ -1,12 +1,13 @@
export interface UsernameProps {
name: string;
userId?: string;
type: Type;
export interface UsernameProps extends NameProps {
userId: string;
}
export interface CreateUsernameProps {
name: string;
export interface CreateUsernameProps extends NameProps {
userId: string;
}
export interface NameProps {
name: string;
type: Type;
}

View File

@ -1,4 +1,4 @@
import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
import { Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
@ -6,10 +6,4 @@ export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}

View File

@ -19,8 +19,8 @@ type UsernameBaseModel = {
};
export type UsernameReadModel = UsernameBaseModel & {
createdAt?: Date;
updatedAt?: Date;
createdAt: Date;
updatedAt: Date;
};
export type UsernameWriteModel = UsernameBaseModel;

View File

@ -10,6 +10,7 @@ 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-username/add-username.command';
import { GRPC_AUTHENTICATION_SERVICE_NAME } from '@src/app.constants';
@UsePipes(
new RpcValidationPipe({
@ -21,7 +22,7 @@ import { AddUsernameCommand } from '@modules/authentication/core/application/com
export class AddUsernameGrpcController {
constructor(private readonly commandBus: CommandBus) {}
@GrpcMethod('AuthenticationService', 'AddUsername')
@GrpcMethod(GRPC_AUTHENTICATION_SERVICE_NAME, 'AddUsername')
async addUsername(data: AddUsernameRequestDto): Promise<IdResponse> {
try {
const aggregateID: AggregateID = await this.commandBus.execute(

View File

@ -10,6 +10,7 @@ import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { CreateAuthenticationRequestDto } from './dtos/create-authentication.request.dto';
import { CreateAuthenticationCommand } from '@modules/authentication/core/application/commands/create-authentication/create-authentication.command';
import { AuthenticationAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors';
import { GRPC_AUTHENTICATION_SERVICE_NAME } from '@src/app.constants';
@UsePipes(
new RpcValidationPipe({
@ -21,7 +22,7 @@ import { AuthenticationAlreadyExistsException } from '@modules/authentication/co
export class CreateAuthenticationGrpcController {
constructor(private readonly commandBus: CommandBus) {}
@GrpcMethod('AuthenticationService', 'Create')
@GrpcMethod(GRPC_AUTHENTICATION_SERVICE_NAME, 'Create')
async create(data: CreateAuthenticationRequestDto): Promise<IdResponse> {
try {
const aggregateID: AggregateID = await this.commandBus.execute(

View File

@ -9,6 +9,7 @@ import { CommandBus } from '@nestjs/cqrs';
import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { DeleteAuthenticationCommand } from '@modules/authentication/core/application/commands/delete-authentication/delete-authentication.command';
import { DeleteAuthenticationRequestDto } from './dtos/delete-authentication.request.dto';
import { GRPC_AUTHENTICATION_SERVICE_NAME } from '@src/app.constants';
@UsePipes(
new RpcValidationPipe({
@ -20,7 +21,7 @@ import { DeleteAuthenticationRequestDto } from './dtos/delete-authentication.req
export class DeleteAuthenticationGrpcController {
constructor(private readonly commandBus: CommandBus) {}
@GrpcMethod('AuthenticationService', 'Delete')
@GrpcMethod(GRPC_AUTHENTICATION_SERVICE_NAME, 'Delete')
async delete(data: DeleteAuthenticationRequestDto): Promise<void> {
try {
await this.commandBus.execute(new DeleteAuthenticationCommand(data));

View File

@ -9,6 +9,7 @@ import { CommandBus } from '@nestjs/cqrs';
import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { DeleteUsernameRequestDto } from './dtos/delete-username.request.dto';
import { DeleteUsernameCommand } from '@modules/authentication/core/application/commands/delete-username/delete-username.command';
import { GRPC_AUTHENTICATION_SERVICE_NAME } from '@src/app.constants';
@UsePipes(
new RpcValidationPipe({
@ -20,7 +21,7 @@ import { DeleteUsernameCommand } from '@modules/authentication/core/application/
export class DeleteUsernameGrpcController {
constructor(private readonly commandBus: CommandBus) {}
@GrpcMethod('AuthenticationService', 'DeleteUsername')
@GrpcMethod(GRPC_AUTHENTICATION_SERVICE_NAME, 'DeleteUsername')
async delete(data: DeleteUsernameRequestDto): Promise<void> {
try {
await this.commandBus.execute(new DeleteUsernameCommand(data));

View File

@ -16,7 +16,7 @@ export function IsValidUsername(validationOptions?: ValidationOptions) {
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {
const usernameType: Type = args.object['type'];
const usernameType: Type = (args.object as any)['type'];
switch (usernameType) {
case Type.PHONE:
return isPhoneNumber(value);

View File

@ -9,6 +9,7 @@ import { CommandBus } from '@nestjs/cqrs';
import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { UpdatePasswordRequestDto } from './dtos/update-password.request.dto';
import { UpdatePasswordCommand } from '@modules/authentication/core/application/commands/update-password/update-password.command';
import { GRPC_AUTHENTICATION_SERVICE_NAME } from '@src/app.constants';
@UsePipes(
new RpcValidationPipe({
@ -20,7 +21,7 @@ import { UpdatePasswordCommand } from '@modules/authentication/core/application/
export class UpdatePasswordGrpcController {
constructor(private readonly commandBus: CommandBus) {}
@GrpcMethod('AuthenticationService', 'UpdatePassword')
@GrpcMethod(GRPC_AUTHENTICATION_SERVICE_NAME, 'UpdatePassword')
async updatePassword(data: UpdatePasswordRequestDto): Promise<IdResponse> {
try {
const aggregateID: AggregateID = await this.commandBus.execute(

View File

@ -10,6 +10,7 @@ import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { UsernameAlreadyExistsException } from '@modules/authentication/core/domain/authentication.errors';
import { UpdateUsernameRequestDto } from './dtos/update-username.request.dto';
import { UpdateUsernameCommand } from '@modules/authentication/core/application/commands/update-username/update-username.command';
import { GRPC_AUTHENTICATION_SERVICE_NAME } from '@src/app.constants';
@UsePipes(
new RpcValidationPipe({
@ -21,7 +22,7 @@ import { UpdateUsernameCommand } from '@modules/authentication/core/application/
export class UpdateUsernameGrpcController {
constructor(private readonly commandBus: CommandBus) {}
@GrpcMethod('AuthenticationService', 'UpdateUsername')
@GrpcMethod(GRPC_AUTHENTICATION_SERVICE_NAME, 'UpdateUsername')
async updateUsername(data: UpdateUsernameRequestDto): Promise<IdResponse> {
try {
const aggregateID: AggregateID = await this.commandBus.execute(

View File

@ -9,6 +9,7 @@ import { QueryBus } from '@nestjs/cqrs';
import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { ValidateAuthenticationRequestDto } from './dtos/validate-authentication.request.dto';
import { ValidateAuthenticationQuery } from '@modules/authentication/core/application/queries/validate-authentication/validate-authentication.query';
import { GRPC_AUTHENTICATION_SERVICE_NAME } from '@src/app.constants';
@UsePipes(
new RpcValidationPipe({
@ -20,7 +21,7 @@ import { ValidateAuthenticationQuery } from '@modules/authentication/core/applic
export class ValidateAuthenticationGrpcController {
constructor(private readonly queryBus: QueryBus) {}
@GrpcMethod('AuthenticationService', 'Validate')
@GrpcMethod(GRPC_AUTHENTICATION_SERVICE_NAME, 'Validate')
async validate(data: ValidateAuthenticationRequestDto): Promise<IdResponse> {
try {
const aggregateID: AggregateID = await this.queryBus.execute(

View File

@ -2,13 +2,14 @@ import { Injectable } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { RabbitSubscribe } from '@mobicoop/message-broker-module';
import { DeleteAuthenticationCommand } from '@modules/authentication/core/application/commands/delete-authentication/delete-authentication.command';
import { USER_DELETED_MESSAGE_HANDLER } from '@src/app.constants';
@Injectable()
export class UserDeletedMessageHandler {
constructor(private readonly commandBus: CommandBus) {}
@RabbitSubscribe({
name: 'userDeleted',
name: USER_DELETED_MESSAGE_HANDLER,
})
public async userDeleted(message: string) {
const deletedUser = JSON.parse(message);

View File

@ -3,13 +3,14 @@ import { CommandBus } from '@nestjs/cqrs';
import { RabbitSubscribe } from '@mobicoop/message-broker-module';
import { Type } from '@modules/authentication/core/domain/username.types';
import { UpdateUsernameCommand } from '@modules/authentication/core/application/commands/update-username/update-username.command';
import { USER_UPDATED_MESSAGE_HANDLER } from '@src/app.constants';
@Injectable()
export class UserUpdatedMessageHandler {
constructor(private readonly commandBus: CommandBus) {}
@RabbitSubscribe({
name: 'userUpdated',
name: USER_UPDATED_MESSAGE_HANDLER,
})
public async userUpdated(message: string) {
const updatedUser = JSON.parse(message);

View File

@ -147,9 +147,9 @@ describe('AuthenticationRepository', () => {
},
});
expect(updatedAuthentication.uuid).toBe(uuid);
expect((updatedAuthentication as any).uuid).toBe(uuid);
expect(authenticationToUpdate.updatedAt.getTime()).toBeLessThan(
updatedAuthentication.updatedAt.getTime(),
(updatedAuthentication as any).updatedAt.getTime(),
);
});

View File

@ -84,9 +84,8 @@ describe('UsernameRepository', () => {
},
});
const username = await usernameRepository.findByName(
'john.doe@email.com',
);
const username =
await usernameRepository.findByName('john.doe@email.com');
expect(username.id).toBe('john.doe@email.com');
});
@ -103,9 +102,8 @@ describe('UsernameRepository', () => {
it('should create a username', async () => {
const beforeCount = await prismaService.username.count();
const usernameToCreate: UsernameEntity = await UsernameEntity.create(
createUsernameProps,
);
const usernameToCreate: UsernameEntity =
await UsernameEntity.create(createUsernameProps);
await usernameRepository.insert(usernameToCreate);
const afterCount = await prismaService.username.count();
@ -122,9 +120,8 @@ describe('UsernameRepository', () => {
},
});
const usernameToCreate: UsernameEntity = await UsernameEntity.create(
createUsernameProps,
);
const usernameToCreate: UsernameEntity =
await UsernameEntity.create(createUsernameProps);
await expect(
usernameRepository.insert(usernameToCreate),
).rejects.toBeInstanceOf(UniqueConstraintException);
@ -141,9 +138,8 @@ describe('UsernameRepository', () => {
},
});
const toUpdate: UsernameEntity = await UsernameEntity.create(
createUsernameProps,
);
const toUpdate: UsernameEntity =
await UsernameEntity.create(createUsernameProps);
await usernameRepository.updateUsername(
usernameToUpdate.username,
toUpdate,
@ -155,16 +151,15 @@ describe('UsernameRepository', () => {
},
});
expect(updatedUsername.username).toBe('john.doe@email.com');
expect((updatedUsername as any).username).toBe('john.doe@email.com');
expect(usernameToUpdate.updatedAt.getTime()).toBeLessThan(
updatedUsername.updatedAt.getTime(),
(updatedUsername as any).updatedAt.getTime(),
);
});
it('should throw a DatabaseException if id is unknown', async () => {
const toUpdate: UsernameEntity = await UsernameEntity.create(
createUsernameProps,
);
const toUpdate: UsernameEntity =
await UsernameEntity.create(createUsernameProps);
await expect(
usernameRepository.updateUsername('jane.doe@email.com', toUpdate),
@ -181,9 +176,8 @@ describe('UsernameRepository', () => {
username: 'john.doe@email.com',
},
});
const toDelete: UsernameEntity = await UsernameEntity.create(
createUsernameProps,
);
const toDelete: UsernameEntity =
await UsernameEntity.create(createUsernameProps);
await usernameRepository.deleteUsername(toDelete);
const count = await prismaService.username.count();
@ -191,9 +185,8 @@ describe('UsernameRepository', () => {
});
it('should throw a DatabaseException if username does not exist', async () => {
const toDelete: UsernameEntity = await UsernameEntity.create(
createUsernameProps,
);
const toDelete: UsernameEntity =
await UsernameEntity.create(createUsernameProps);
await expect(usernameRepository.delete(toDelete)).rejects.toBeInstanceOf(
DatabaseErrorException,
);

View File

@ -82,9 +82,8 @@ describe('Add Username Service', () => {
})),
});
it('should add a new username', async () => {
const result: AggregateID = await addUsernameService.execute(
addUsernameCommand,
);
const result: AggregateID =
await addUsernameService.execute(addUsernameCommand);
expect(result).toBe('john.doe@email.com');
});
it('should throw a dedicated exception if username already exists for this type', async () => {

View File

@ -80,17 +80,15 @@ describe('Authentication password validation', () => {
it('should validate a valid password', async () => {
const authenticationEntity: AuthenticationEntity =
await AuthenticationEntity.create(createAuthenticationProps);
const result: boolean = await authenticationEntity.authenticate(
'somePassword',
);
const result: boolean =
await authenticationEntity.authenticate('somePassword');
expect(result).toBeTruthy();
});
it('should not validate an invalid password', async () => {
const authenticationEntity: AuthenticationEntity =
await AuthenticationEntity.create(createAuthenticationProps);
const result: boolean = await authenticationEntity.authenticate(
'someWrongPassword',
);
const result: boolean =
await authenticationEntity.authenticate('someWrongPassword');
expect(result).toBeFalsy();
});
});

View File

@ -14,18 +14,16 @@ const createUsernameProps: CreateUsernameProps = {
describe('Username entity create', () => {
it('should create a new username entity', async () => {
const usernameEntity: UsernameEntity = await UsernameEntity.create(
createUsernameProps,
);
const usernameEntity: UsernameEntity =
await UsernameEntity.create(createUsernameProps);
expect(usernameEntity.id).toBe('john.doe@email.com');
expect(usernameEntity.domainEvents.length).toBe(1);
});
});
describe('Username entity update', () => {
it('should update a username entity', async () => {
const usernameEntity: UsernameEntity = await UsernameEntity.create(
createUsernameProps,
);
const usernameEntity: UsernameEntity =
await UsernameEntity.create(createUsernameProps);
usernameEntity.update({
name: 'new-john.doe@email.com',
});
@ -38,9 +36,8 @@ describe('Username entity update', () => {
});
describe('Username entity delete', () => {
it('should delete a username entity', async () => {
const usernameEntity: UsernameEntity = await UsernameEntity.create(
createUsernameProps,
);
const usernameEntity: UsernameEntity =
await UsernameEntity.create(createUsernameProps);
usernameEntity.delete();
expect(usernameEntity.domainEvents.length).toBe(2);
expect(usernameEntity.domainEvents[1]).toBeInstanceOf(

View File

@ -85,9 +85,8 @@ describe('Username repository', () => {
it('should find a username by its name', async () => {
jest.spyOn(usernameRepository, 'findOne');
const username: UsernameEntity = await usernameRepository.findByName(
'john.doe@email.com',
);
const username: UsernameEntity =
await usernameRepository.findByName('john.doe@email.com');
expect(usernameRepository.findOne).toHaveBeenCalledTimes(1);
expect(usernameRepository.findOne).toHaveBeenCalledWith({
username: 'john.doe@email.com',

View File

@ -55,9 +55,8 @@ describe('Add Username Grpc Controller', () => {
it('should add a new username', async () => {
jest.spyOn(mockCommandBus, 'execute');
const result: IdResponse = await addUsernameGrpcController.addUsername(
addUsernameRequest,
);
const result: IdResponse =
await addUsernameGrpcController.addUsername(addUsernameRequest);
expect(result).toBeInstanceOf(IdResponse);
expect(result.id).toBe('john.doe@email.com');
expect(mockCommandBus.execute).toHaveBeenCalledTimes(1);

View File

@ -42,15 +42,13 @@ export class UsernameMapper
updatedAt: new Date(record.updatedAt),
props: {
name: record.username,
type: Type[record.type],
type: record.type as Type,
userId: record.authUuid,
},
});
return entity;
};
toResponse = (entity: UsernameEntity): UsernameResponseDto => {
const response = new UsernameResponseDto(entity);
return response;
};
toResponse = (entity: UsernameEntity): UsernameResponseDto =>
new UsernameResponseDto(entity);
}

View File

@ -6,6 +6,6 @@ export abstract class DecisionMakerPort {
abstract decide(
domain: Domain,
action: Action,
context: ContextItem[],
context?: ContextItem[],
): Promise<boolean>;
}

View File

@ -21,12 +21,14 @@ export class OpaDecisionMaker extends DecisionMakerPort {
decide = async (
domain: Domain,
action: Action,
context: ContextItem[],
context?: ContextItem[],
): Promise<boolean> => {
const reducedContext = context.reduce(
(obj, item) => Object.assign(obj, { [item.name]: item.value }),
{},
);
const reducedContext = context
? context.reduce(
(obj, item) => Object.assign(obj, { [item.name]: item.value }),
{},
)
: undefined;
try {
const { data } = await lastValueFrom(
this.httpService.post<Decision>(

View File

@ -5,6 +5,7 @@ import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { DecisionRequestDto } from './dtos/decision.request.dto';
import { DecisionQuery } from '@modules/authorization/core/application/queries/decision/decision.query';
import { DecisionResponseDto } from '../dtos/decision.response.dto';
import { GRPC_AUTHORIZATION_SERVICE_NAME } from '@src/app.constants';
@UsePipes(
new RpcValidationPipe({
@ -16,7 +17,7 @@ import { DecisionResponseDto } from '../dtos/decision.response.dto';
export class DecideGrpcController {
constructor(private readonly queryBus: QueryBus) {}
@GrpcMethod('AuthorizationService', 'Decide')
@GrpcMethod(GRPC_AUTHORIZATION_SERVICE_NAME, 'Decide')
async decide(data: DecisionRequestDto): Promise<DecisionResponseDto> {
try {
const allow: boolean = await this.queryBus.execute(

View File

@ -49,9 +49,8 @@ describe('Decision Query Handler', () => {
value: '96d99d44-e0a6-458e-a656-de2a400d60a8',
},
]);
const decision: boolean = await decisionQueryHandler.execute(
decisionQuery,
);
const decision: boolean =
await decisionQueryHandler.execute(decisionQuery);
expect(decision).toBeTruthy();
});
it('should return a negative decision', async () => {
@ -65,9 +64,8 @@ describe('Decision Query Handler', () => {
value: '96d99d44-e0a6-458e-a656-de2a400d60a9',
},
]);
const decision: boolean = await decisionQueryHandler.execute(
decisionQuery,
);
const decision: boolean =
await decisionQueryHandler.execute(decisionQuery);
expect(decision).toBeFalsy();
});
});

View File

@ -6,6 +6,15 @@ import {
MessageBrokerPublisher,
} from '@mobicoop/message-broker-module';
import { ConfigModule, ConfigService } from '@nestjs/config';
import {
SERVICE_NAME,
USER_DELETED_MESSAGE_HANDLER,
USER_DELETED_QUEUE,
USER_DELETED_ROUTING_KEY,
USER_UPDATED_MESSAGE_HANDLER,
USER_UPDATED_QUEUE,
USER_UPDATED_ROUTING_KEY,
} from '@src/app.constants';
const imports = [
MessageBrokerModule.forRootAsync({
@ -14,17 +23,22 @@ const imports = [
useFactory: async (
configService: ConfigService,
): Promise<MessageBrokerModuleOptions> => ({
uri: configService.get<string>('MESSAGE_BROKER_URI'),
exchange: configService.get<string>('MESSAGE_BROKER_EXCHANGE'),
name: 'auth',
uri: configService.get<string>('MESSAGE_BROKER_URI') as string,
exchange: {
name: configService.get<string>('MESSAGE_BROKER_EXCHANGE') as string,
durable: configService.get<boolean>(
'MESSAGE_BROKER_EXCHANGE_DURABILITY',
) as boolean,
},
name: SERVICE_NAME,
handlers: {
userUpdated: {
routingKey: 'user.updated',
queue: 'auth-user-updated',
[USER_UPDATED_MESSAGE_HANDLER]: {
routingKey: USER_UPDATED_ROUTING_KEY,
queue: USER_UPDATED_QUEUE,
},
userDeleted: {
routingKey: 'user.deleted',
queue: 'auth-user-deleted',
[USER_DELETED_MESSAGE_HANDLER]: {
routingKey: USER_DELETED_ROUTING_KEY,
queue: USER_DELETED_QUEUE,
},
},
}),

View File

@ -12,12 +12,13 @@
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictNullChecks": true,
"noImplicitAny": true,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": false,
"paths": {
"@libs/*": ["src/libs/*"],
"@modules/*": ["src/modules/*"],
"@src/*": ["src/*"]
}