delete authentication, use custom logger

This commit is contained in:
sbriat 2023-07-05 17:32:21 +02:00
parent f33f679e12
commit bbcd2cdb9e
24 changed files with 269 additions and 54 deletions

View File

@ -6,10 +6,10 @@ HEALTH_SERVICE_PORT=6002
# PRISMA # PRISMA
DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=auth" DATABASE_URL="postgresql://mobicoop:mobicoop@v3-db:5432/mobicoop?schema=auth"
# RABBIT MQ # MESSAGE BROKER
RMQ_URI=amqp://v3-broker:5672 MESSAGE_BROKER_URI=amqp://v3-broker:5672
RMQ_EXCHANGE=mobicoop MESSAGE_BROKER_EXCHANGE=mobicoop
# OPA # OPA
OPA_IMAGE=openpolicyagent/opa:0.48.0-rootless OPA_IMAGE=openpolicyagent/opa:0.54.0
OPA_URL=http://v3-auth-opa:8181/v1/data/ OPA_URL=http://v3-auth-opa:8181/v1/data/

View File

@ -6,5 +6,5 @@ SERVICE_PORT=5002
DATABASE_URL="postgresql://mobicoop:mobicoop@localhost:5432/mobicoop-test?schema=auth" DATABASE_URL="postgresql://mobicoop:mobicoop@localhost:5432/mobicoop-test?schema=auth"
# OPA # OPA
OPA_IMAGE=openpolicyagent/opa:0.48.0-rootless OPA_IMAGE=openpolicyagent/opa:0.54.0
OPA_URL=http://v3-auth-opa:8181/v1/data/ OPA_URL=http://v3-auth-opa:8181/v1/data/

54
package-lock.json generated
View File

@ -16,6 +16,7 @@
"@grpc/grpc-js": "^1.8.0", "@grpc/grpc-js": "^1.8.0",
"@grpc/proto-loader": "^0.7.4", "@grpc/proto-loader": "^0.7.4",
"@mobicoop/ddd-library": "file:../../packages/dddlibrary", "@mobicoop/ddd-library": "file:../../packages/dddlibrary",
"@mobicoop/message-broker-module": "^1.2.0",
"@nestjs/axios": "^1.0.1", "@nestjs/axios": "^1.0.1",
"@nestjs/common": "^9.0.0", "@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.2.0", "@nestjs/config": "^2.2.0",
@ -72,6 +73,24 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/@acuminous/bitsyntax": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/@acuminous/bitsyntax/-/bitsyntax-0.1.2.tgz",
"integrity": "sha512-29lUK80d1muEQqiUsSo+3A0yP6CdspgC95EnKBMi22Xlwt79i/En4Vr67+cXhU+cZjbti3TgGGC5wy1stIywVQ==",
"dependencies": {
"buffer-more-ints": "~1.0.0",
"debug": "^4.3.4",
"safe-buffer": "~5.1.2"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/@acuminous/bitsyntax/node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
@ -1500,6 +1519,33 @@
"reflect-metadata": "^0.1.12" "reflect-metadata": "^0.1.12"
} }
}, },
"node_modules/@mobicoop/message-broker-module": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@mobicoop/message-broker-module/-/message-broker-module-1.2.0.tgz",
"integrity": "sha512-RoSHHK1GyQ/QVDmm3JS/wBfh171oChvyEp6YWmJd12krFLrPVn9MoEvZdyT3I5J31oBiUabMPle5Kdpw+Nrmww==",
"dependencies": {
"@golevelup/nestjs-rabbitmq": "^3.6.0",
"@types/amqplib": "^0.10.1",
"amqplib": "^0.10.3"
},
"peerDependencies": {
"@nestjs/common": "^9.4.2"
}
},
"node_modules/@mobicoop/message-broker-module/node_modules/amqplib": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.10.3.tgz",
"integrity": "sha512-UHmuSa7n8vVW/a5HGh2nFPqAEr8+cD4dEZ6u9GjP91nHfr1a54RyAKyra7Sb5NH7NBKOUlyQSMXIp0qAixKexw==",
"dependencies": {
"@acuminous/bitsyntax": "^0.1.2",
"buffer-more-ints": "~1.0.0",
"readable-stream": "1.x >=1.1.9",
"url-parse": "~1.5.10"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@nestjs/axios": { "node_modules/@nestjs/axios": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-1.0.1.tgz",
@ -2189,6 +2235,14 @@
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true "dev": true
}, },
"node_modules/@types/amqplib": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/@types/amqplib/-/amqplib-0.10.1.tgz",
"integrity": "sha512-j6ANKT79ncUDnAs/+9r9eDujxbeJoTjoVu33gHHcaPfmLQaMhvfbH2GqSe8KUM444epAp1Vl3peVOQfZk3UIqA==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/babel__core": { "node_modules/@types/babel__core": {
"version": "7.20.1", "version": "7.20.1",
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz",

View File

@ -37,6 +37,7 @@
"@grpc/grpc-js": "^1.8.0", "@grpc/grpc-js": "^1.8.0",
"@grpc/proto-loader": "^0.7.4", "@grpc/proto-loader": "^0.7.4",
"@mobicoop/ddd-library": "file:../../packages/dddlibrary", "@mobicoop/ddd-library": "file:../../packages/dddlibrary",
"@mobicoop/message-broker-module": "^1.2.0",
"@nestjs/axios": "^1.0.1", "@nestjs/axios": "^1.0.1",
"@nestjs/common": "^9.0.0", "@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.2.0", "@nestjs/config": "^2.2.0",

View File

@ -3,6 +3,7 @@
generator client { generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
binaryTargets = ["linux-musl", "debian-openssl-3.0.x"]
} }
datasource db { datasource db {

1
src/app.di-tokens.ts Normal file
View File

@ -0,0 +1 @@
export const MESSAGE_PUBLISHER = Symbol();

View File

@ -1,20 +1,35 @@
import { classes } from '@automapper/classes'; // import { classes } from '@automapper/classes';
import { AutomapperModule } from '@automapper/nestjs'; // import { AutomapperModule } from '@automapper/nestjs';
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config'; import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthorizationModule } from './modules/authorization/authorization.module'; // import { AuthorizationModule } from './modules/authorization/authorization.module';
import { HealthModule } from './modules/health/health.module'; // import { HealthModule } from './modules/health/health.module';
import { AuthenticationModule } from '@modules/newauthentication/authentication.module'; import { AuthenticationModule } from '@modules/newauthentication/authentication.module';
import { EventEmitterModule } from '@nestjs/event-emitter'; import { EventEmitterModule } from '@nestjs/event-emitter';
import {
MessageBrokerModule,
MessageBrokerModuleOptions,
} from '@mobicoop/message-broker-module';
@Module({ @Module({
imports: [ imports: [
ConfigModule.forRoot({ isGlobal: true }), ConfigModule.forRoot({ isGlobal: true }),
EventEmitterModule.forRoot(), EventEmitterModule.forRoot(),
AutomapperModule.forRoot({ strategyInitializer: classes() }), MessageBrokerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (
configService: ConfigService,
): Promise<MessageBrokerModuleOptions> => ({
uri: configService.get<string>('MESSAGE_BROKER_URI'),
exchange: configService.get<string>('MESSAGE_BROKER_EXCHANGE'),
name: 'auth',
}),
}),
// AutomapperModule.forRoot({ strategyInitializer: classes() }),
AuthenticationModule, AuthenticationModule,
AuthorizationModule, // AuthorizationModule,
HealthModule, // HealthModule,
], ],
controllers: [], controllers: [],
providers: [], providers: [],

View File

@ -49,6 +49,7 @@ export class AuthenticationMapper
createdAt: new Date(record.createdAt), createdAt: new Date(record.createdAt),
updatedAt: new Date(record.updatedAt), updatedAt: new Date(record.updatedAt),
props: { props: {
userId: record.uuid,
password: record.password, password: record.password,
usernames: record.usernames.map((username: UsernameModel) => ({ usernames: record.usernames.map((username: UsernameModel) => ({
name: username.username, name: username.username,

View File

@ -6,10 +6,20 @@ import { AUTHENTICATION_REPOSITORY } from './authentication.di-tokens';
import { AuthenticationRepository } from './infrastructure/authentication.repository'; import { AuthenticationRepository } from './infrastructure/authentication.repository';
import { PrismaService } from './infrastructure/prisma.service'; import { PrismaService } from './infrastructure/prisma.service';
import { CqrsModule } from '@nestjs/cqrs'; import { CqrsModule } from '@nestjs/cqrs';
import { DeleteAuthenticationGrpcController } from './interface/grpc-controllers/delete-authentication.grpc.controller';
import { DeleteAuthenticationService } from './core/application/commands/delete-authentication/delete-authentication.service';
import { MESSAGE_PUBLISHER } from '@src/app.di-tokens';
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
const grpcControllers = [CreateAuthenticationGrpcController]; const grpcControllers = [
CreateAuthenticationGrpcController,
DeleteAuthenticationGrpcController,
];
const commandHandlers: Provider[] = [CreateAuthenticationService]; const commandHandlers: Provider[] = [
CreateAuthenticationService,
DeleteAuthenticationService,
];
const mappers: Provider[] = [AuthenticationMapper]; const mappers: Provider[] = [AuthenticationMapper];
@ -20,12 +30,25 @@ const repositories: Provider[] = [
}, },
]; ];
const messageBrokers: Provider[] = [
{
provide: MESSAGE_PUBLISHER,
useClass: MessageBrokerPublisher,
},
];
const orms: Provider[] = [PrismaService]; const orms: Provider[] = [PrismaService];
@Module({ @Module({
imports: [CqrsModule], imports: [CqrsModule],
controllers: [...grpcControllers], controllers: [...grpcControllers],
providers: [...commandHandlers, ...mappers, ...repositories, ...orms], providers: [
...commandHandlers,
...mappers,
...repositories,
...messageBrokers,
...orms,
],
exports: [PrismaService, AuthenticationMapper, AUTHENTICATION_REPOSITORY], exports: [PrismaService, AuthenticationMapper, AUTHENTICATION_REPOSITORY],
}) })
export class AuthenticationModule {} export class AuthenticationModule {}

View File

@ -2,11 +2,13 @@ import { Command, CommandProps } from '@mobicoop/ddd-library';
import { Username } from '../types/username'; import { Username } from '../types/username';
export class CreateAuthenticationCommand extends Command { export class CreateAuthenticationCommand extends Command {
readonly userId: string;
readonly password: string; readonly password: string;
readonly usernames: Username[]; readonly usernames: Username[];
constructor(props: CommandProps<CreateAuthenticationCommand>) { constructor(props: CommandProps<CreateAuthenticationCommand>) {
super(props); super(props);
this.userId = props.userId;
this.password = props.password; this.password = props.password;
this.usernames = props.usernames; this.usernames = props.usernames;
} }

View File

@ -3,7 +3,7 @@ import { Inject } from '@nestjs/common';
import { import {
AggregateID, AggregateID,
ConflictException, ConflictException,
UniqueConflictException, UniqueConstraintException,
} from '@mobicoop/ddd-library'; } from '@mobicoop/ddd-library';
import { CreateAuthenticationCommand } from './create-authentication.command'; import { CreateAuthenticationCommand } from './create-authentication.command';
import { AUTHENTICATION_REPOSITORY } from '@modules/newauthentication/authentication.di-tokens'; import { AUTHENTICATION_REPOSITORY } from '@modules/newauthentication/authentication.di-tokens';
@ -22,21 +22,27 @@ export class CreateAuthenticationService implements ICommandHandler {
) {} ) {}
async execute(command: CreateAuthenticationCommand): Promise<AggregateID> { async execute(command: CreateAuthenticationCommand): Promise<AggregateID> {
const authentication = await AuthenticationEntity.create({ const authentication: AuthenticationEntity =
await AuthenticationEntity.create({
userId: command.userId,
password: command.password, password: command.password,
usernames: command.usernames, usernames: command.usernames,
}); });
try { try {
await this.repository.insert(authentication); await this.repository.insert(authentication);
return authentication.id; return authentication.getProps().userId;
} catch (error: any) { } catch (error: any) {
console.log('error', error.cause);
if (error instanceof ConflictException) { if (error instanceof ConflictException) {
throw new AuthenticationAlreadyExistsException(error); throw new AuthenticationAlreadyExistsException(error);
} }
if ( if (
error instanceof UniqueConflictException && error instanceof UniqueConstraintException &&
error.message.includes('uuid')
) {
throw new AuthenticationAlreadyExistsException(error);
}
if (
error instanceof UniqueConstraintException &&
error.message.includes('username') error.message.includes('username')
) { ) {
throw new UsernameAlreadyExistsException(error); throw new UsernameAlreadyExistsException(error);

View File

@ -0,0 +1,10 @@
import { Command, CommandProps } from '@mobicoop/ddd-library';
export class DeleteAuthenticationCommand extends Command {
readonly userId: string;
constructor(props: CommandProps<DeleteAuthenticationCommand>) {
super(props);
this.userId = props.userId;
}
}

View File

@ -0,0 +1,26 @@
import { Inject } from '@nestjs/common';
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
import { DeleteAuthenticationCommand } from './delete-authentication.command';
import { AUTHENTICATION_REPOSITORY } from '@modules/newauthentication/authentication.di-tokens';
import { AuthenticationRepositoryPort } from '../ports/authentication.repository.port';
import { AuthenticationEntity } from '@modules/newauthentication/core/domain/authentication.entity';
@CommandHandler(DeleteAuthenticationCommand)
export class DeleteAuthenticationService implements ICommandHandler {
constructor(
@Inject(AUTHENTICATION_REPOSITORY)
private readonly authenticationRepository: AuthenticationRepositoryPort,
) {}
async execute(command: DeleteAuthenticationCommand): Promise<boolean> {
const authentication: AuthenticationEntity =
await this.authenticationRepository.findOneById(command.userId, {
usernames: true,
});
authentication.delete();
const deleted: boolean = await this.authenticationRepository.delete(
authentication,
);
return deleted;
}
}

View File

@ -1,11 +1,11 @@
import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library'; import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library';
import { v4 } from 'uuid';
import * as bcrypt from 'bcrypt'; import * as bcrypt from 'bcrypt';
import { import {
AuthenticationProps, AuthenticationProps,
CreateAuthenticationProps, CreateAuthenticationProps,
} from './authentication.types'; } from './authentication.types';
import { AuthenticationCreatedDomainEvent } from './events/authentication-created.domain-events'; import { AuthenticationCreatedDomainEvent } from './events/authentication-created.domain-events';
import { AuthenticationDeletedDomainEvent } from './events/authentication-deleted.domain-event';
export class AuthenticationEntity extends AggregateRoot<AuthenticationProps> { export class AuthenticationEntity extends AggregateRoot<AuthenticationProps> {
protected readonly _id: AggregateID; protected readonly _id: AggregateID;
@ -13,22 +13,30 @@ export class AuthenticationEntity extends AggregateRoot<AuthenticationProps> {
static create = async ( static create = async (
create: CreateAuthenticationProps, create: CreateAuthenticationProps,
): Promise<AuthenticationEntity> => { ): Promise<AuthenticationEntity> => {
const id = v4();
const props: AuthenticationProps = { ...create }; const props: AuthenticationProps = { ...create };
const hash = await bcrypt.hash(props.password, 10); const hash = await bcrypt.hash(props.password, 10);
const authentication = new AuthenticationEntity({ const authentication = new AuthenticationEntity({
id, id: props.userId,
props: { props: {
userId: props.userId,
password: hash, password: hash,
usernames: props.usernames, usernames: props.usernames,
}, },
}); });
authentication.addEvent( authentication.addEvent(
new AuthenticationCreatedDomainEvent({ aggregateId: id }), new AuthenticationCreatedDomainEvent({ aggregateId: props.userId }),
); );
return authentication; return authentication;
}; };
delete(): void {
this.addEvent(
new AuthenticationDeletedDomainEvent({
aggregateId: this.id,
}),
);
}
validate(): void { validate(): void {
// entity business rules validation to protect it's invariant before saving entity to a database // entity business rules validation to protect it's invariant before saving entity to a database
} }

View File

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

View File

@ -0,0 +1,7 @@
import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library';
export class AuthenticationDeletedDomainEvent extends DomainEvent {
constructor(props: DomainEventProps<AuthenticationDeletedDomainEvent>) {
super(props);
}
}

View File

@ -1,9 +0,0 @@
import { Entity } from '@mobicoop/ddd-library';
import { UsernameProps } from './username.types';
export class UsernameEntity extends Entity<UsernameProps> {
protected _id: string;
validate(): void {
throw new Error('Method not implemented.');
}
}

View File

@ -4,12 +4,6 @@ export interface UsernameProps {
type: Type; type: Type;
} }
// Properties that are needed for a Username creation
export interface CreateUsernameProps {
name: string;
type: Type;
}
export enum Type { export enum Type {
EMAIL = 'EMAIL', EMAIL = 'EMAIL',
PHONE = 'PHONE', PHONE = 'PHONE',

View File

@ -1,10 +1,15 @@
import { Injectable, Logger } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { EventEmitter2 } from '@nestjs/event-emitter'; import { EventEmitter2 } from '@nestjs/event-emitter';
import { PrismaRepositoryBase } from '@mobicoop/ddd-library'; import {
LoggerBase,
MessagePublisherPort,
PrismaRepositoryBase,
} from '@mobicoop/ddd-library';
import { AuthenticationEntity } from '../core/domain/authentication.entity'; import { AuthenticationEntity } from '../core/domain/authentication.entity';
import { AuthenticationRepositoryPort } from '../core/application/commands/ports/authentication.repository.port'; import { AuthenticationRepositoryPort } from '../core/application/commands/ports/authentication.repository.port';
import { PrismaService } from './prisma.service'; import { PrismaService } from './prisma.service';
import { AuthenticationMapper } from '../authentication.mapper'; import { AuthenticationMapper } from '../authentication.mapper';
import { MESSAGE_PUBLISHER } from '@src/app.di-tokens';
export type AuthenticationBaseModel = { export type AuthenticationBaseModel = {
uuid: string; uuid: string;
@ -44,13 +49,20 @@ export class AuthenticationRepository
prisma: PrismaService, prisma: PrismaService,
mapper: AuthenticationMapper, mapper: AuthenticationMapper,
eventEmitter: EventEmitter2, eventEmitter: EventEmitter2,
@Inject(MESSAGE_PUBLISHER)
protected readonly messagePublisher: MessagePublisherPort,
) { ) {
super( super(
prisma.auth, prisma.auth,
prisma, prisma,
mapper, mapper,
eventEmitter, eventEmitter,
new Logger(AuthenticationRepository.name), new LoggerBase(
AuthenticationRepository.name,
'logging',
'auth',
messagePublisher,
),
); );
} }
} }

View File

@ -9,7 +9,7 @@ service AuthenticationService {
rpc UpdatePassword(Password) returns (Id); rpc UpdatePassword(Password) returns (Id);
rpc UpdateUsername(Username) returns (Id); rpc UpdateUsername(Username) returns (Id);
rpc DeleteUsername(Username) returns (Id); rpc DeleteUsername(Username) returns (Id);
rpc Delete(Id) returns (Empty); rpc Delete(UserId) returns (Empty);
} }
message AuthenticationByUsernamePassword { message AuthenticationByUsernamePassword {
@ -18,17 +18,18 @@ message AuthenticationByUsernamePassword {
} }
message Authentication { message Authentication {
repeated Username usernames = 1; string userId = 1;
string password = 2; repeated Username usernames = 2;
string password = 3;
} }
message Password { message Password {
string id = 1; string userId = 1;
string password = 2; string password = 2;
} }
message Username { message Username {
string id = 1; string userId = 1;
string name = 2; string name = 2;
string type = 3; string type = 3;
} }
@ -37,4 +38,8 @@ message Id {
string id = 1; string id = 1;
} }
message UserId {
string userId = 1;
}
message Empty {} message Empty {}

View File

@ -22,7 +22,7 @@ export class CreateAuthenticationGrpcController {
constructor(private readonly commandBus: CommandBus) {} constructor(private readonly commandBus: CommandBus) {}
@GrpcMethod('AuthenticationService', 'Create') @GrpcMethod('AuthenticationService', 'Create')
async validate(data: CreateAuthenticationRequestDto): Promise<IdResponse> { async create(data: CreateAuthenticationRequestDto): Promise<IdResponse> {
try { try {
const aggregateID: AggregateID = await this.commandBus.execute( const aggregateID: AggregateID = await this.commandBus.execute(
new CreateAuthenticationCommand(data), new CreateAuthenticationCommand(data),

View File

@ -0,0 +1,45 @@
import {
DatabaseErrorException,
NotFoundException,
RpcExceptionCode,
RpcValidationPipe,
} from '@mobicoop/ddd-library';
import { Controller, UsePipes } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { GrpcMethod, RpcException } from '@nestjs/microservices';
import { DeleteAuthenticationCommand } from '@modules/newauthentication/core/application/commands/delete-authentication/delete-authentication.command';
import { DeleteAuthenticationRequestDto } from './dtos/delete-authentication.request.dto';
@UsePipes(
new RpcValidationPipe({
whitelist: true,
forbidUnknownValues: false,
}),
)
@Controller()
export class DeleteAuthenticationGrpcController {
constructor(private readonly commandBus: CommandBus) {}
@GrpcMethod('AuthenticationService', 'Delete')
async delete(data: DeleteAuthenticationRequestDto): Promise<void> {
try {
await this.commandBus.execute(new DeleteAuthenticationCommand(data));
} catch (error: any) {
if (error instanceof NotFoundException)
throw new RpcException({
code: RpcExceptionCode.NOT_FOUND,
message: error.message,
});
if (error instanceof DatabaseErrorException)
throw new RpcException({
code: RpcExceptionCode.INTERNAL,
message: error.message,
});
throw new RpcException({
code: RpcExceptionCode.UNKNOWN,
message: error.message,
});
}
}
}

View File

@ -9,6 +9,10 @@ import {
import { UsernameDto } from './username.dto'; import { UsernameDto } from './username.dto';
export class CreateAuthenticationRequestDto { export class CreateAuthenticationRequestDto {
@IsString()
@IsNotEmpty()
userId: string;
@Type(() => UsernameDto) @Type(() => UsernameDto)
@IsArray() @IsArray()
@ArrayMinSize(1) @ArrayMinSize(1)

View File

@ -0,0 +1,7 @@
import { IsNotEmpty, IsString } from 'class-validator';
export class DeleteAuthenticationRequestDto {
@IsString()
@IsNotEmpty()
userId: string;
}