integration events
This commit is contained in:
parent
3ac7460c83
commit
e8c40a6386
|
@ -1,121 +0,0 @@
|
|||
import { Mapper } from '@automapper/core';
|
||||
import { InjectMapper } from '@automapper/nestjs';
|
||||
import { Controller, UseInterceptors, UsePipes } from '@nestjs/common';
|
||||
import { CommandBus, QueryBus } from '@nestjs/cqrs';
|
||||
import { GrpcMethod, RpcException } from '@nestjs/microservices';
|
||||
import { DatabaseException } from '../../../database/exceptions/database.exception';
|
||||
import { CreateUserCommand } from '../../commands/create-user.command';
|
||||
import { DeleteUserCommand } from '../../commands/delete-user.command';
|
||||
import { UpdateUserCommand } from '../../commands/update-user.command';
|
||||
import { CreateUserRequest } from '../../domain/dtos/create-user.request';
|
||||
import { FindAllUsersRequest } from '../../domain/dtos/find-all-users.request';
|
||||
import { FindUserByUuidRequest } from '../../domain/dtos/find-user-by-uuid.request';
|
||||
import { UpdateUserRequest } from '../../domain/dtos/update-user.request';
|
||||
import { User } from '../../domain/entities/user';
|
||||
import { FindAllUsersQuery } from '../../queries/find-all-users.query';
|
||||
import { FindUserByUuidQuery } from '../../queries/find-user-by-uuid.query';
|
||||
import { UserPresenter } from './user.presenter';
|
||||
import { ICollection } from '../../../database/interfaces/collection.interface';
|
||||
import { RpcValidationPipe } from '../../../../utils/pipes/rpc.validation-pipe';
|
||||
import { CacheInterceptor, CacheKey } from '@nestjs/cache-manager';
|
||||
|
||||
@UsePipes(
|
||||
new RpcValidationPipe({
|
||||
whitelist: true,
|
||||
forbidUnknownValues: false,
|
||||
}),
|
||||
)
|
||||
@Controller()
|
||||
export class UserController {
|
||||
constructor(
|
||||
private readonly commandBus: CommandBus,
|
||||
private readonly queryBus: QueryBus,
|
||||
@InjectMapper() private readonly mapper: Mapper,
|
||||
) {}
|
||||
|
||||
@GrpcMethod('UsersService', 'FindAll')
|
||||
@UseInterceptors(CacheInterceptor)
|
||||
@CacheKey('UsersServiceFindAll')
|
||||
async findAll(data: FindAllUsersRequest): Promise<ICollection<User>> {
|
||||
const userCollection = await this.queryBus.execute(
|
||||
new FindAllUsersQuery(data),
|
||||
);
|
||||
return Promise.resolve({
|
||||
data: userCollection.data.map((user: User) =>
|
||||
this.mapper.map(user, User, UserPresenter),
|
||||
),
|
||||
total: userCollection.total,
|
||||
});
|
||||
}
|
||||
|
||||
@GrpcMethod('UsersService', 'FindOneByUuid')
|
||||
@UseInterceptors(CacheInterceptor)
|
||||
@CacheKey('UsersServiceFindOneByUuid')
|
||||
async findOneByUuid(data: FindUserByUuidRequest): Promise<UserPresenter> {
|
||||
try {
|
||||
const user = await this.queryBus.execute(new FindUserByUuidQuery(data));
|
||||
return this.mapper.map(user, User, UserPresenter);
|
||||
} catch (error) {
|
||||
throw new RpcException({
|
||||
code: 5,
|
||||
message: 'User not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@GrpcMethod('UsersService', 'Create')
|
||||
async createUser(data: CreateUserRequest): Promise<UserPresenter> {
|
||||
try {
|
||||
const user = await this.commandBus.execute(new CreateUserCommand(data));
|
||||
return this.mapper.map(user, User, UserPresenter);
|
||||
} catch (e) {
|
||||
if (e instanceof DatabaseException) {
|
||||
if (e.message.includes('Already exists')) {
|
||||
throw new RpcException({
|
||||
code: 6,
|
||||
message: 'User already exists',
|
||||
});
|
||||
}
|
||||
}
|
||||
throw new RpcException({});
|
||||
}
|
||||
}
|
||||
|
||||
@GrpcMethod('UsersService', 'Update')
|
||||
async updateUser(data: UpdateUserRequest): Promise<UserPresenter> {
|
||||
try {
|
||||
const user = await this.commandBus.execute(new UpdateUserCommand(data));
|
||||
|
||||
return this.mapper.map(user, User, UserPresenter);
|
||||
} catch (e) {
|
||||
if (e instanceof DatabaseException) {
|
||||
if (e.message.includes('not found')) {
|
||||
throw new RpcException({
|
||||
code: 5,
|
||||
message: 'User not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
throw new RpcException({});
|
||||
}
|
||||
}
|
||||
|
||||
@GrpcMethod('UsersService', 'Delete')
|
||||
async deleteUser(data: FindUserByUuidRequest): Promise<void> {
|
||||
try {
|
||||
await this.commandBus.execute(new DeleteUserCommand(data.uuid));
|
||||
|
||||
return Promise.resolve();
|
||||
} catch (e) {
|
||||
if (e instanceof DatabaseException) {
|
||||
if (e.message.includes('not found')) {
|
||||
throw new RpcException({
|
||||
code: 5,
|
||||
message: 'User not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
throw new RpcException({});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import { AutoMap } from '@automapper/classes';
|
||||
|
||||
export class UserPresenter {
|
||||
@AutoMap()
|
||||
uuid: string;
|
||||
|
||||
@AutoMap()
|
||||
firstName: string;
|
||||
|
||||
@AutoMap()
|
||||
lastName: string;
|
||||
|
||||
@AutoMap()
|
||||
email: string;
|
||||
|
||||
@AutoMap()
|
||||
phone: string;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
syntax = "proto3";
|
||||
|
||||
package user;
|
||||
|
||||
service UsersService {
|
||||
rpc FindOneByUuid(UserByUuid) returns (User);
|
||||
rpc FindAll(UserFilter) returns (Users);
|
||||
rpc Create(User) returns (User);
|
||||
rpc Update(User) returns (User);
|
||||
rpc Delete(UserByUuid) returns (Empty);
|
||||
}
|
||||
|
||||
message UserByUuid {
|
||||
string uuid = 1;
|
||||
}
|
||||
|
||||
message User {
|
||||
string uuid = 1;
|
||||
string firstName = 2;
|
||||
string lastName = 3;
|
||||
string email = 4;
|
||||
string phone = 5;
|
||||
}
|
||||
|
||||
message UserFilter {
|
||||
optional int32 page = 1;
|
||||
optional int32 perPage = 2;
|
||||
}
|
||||
|
||||
message Users {
|
||||
repeated User data = 1;
|
||||
int32 total = 2;
|
||||
}
|
||||
|
||||
message Empty {}
|
|
@ -1,16 +0,0 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { IPublishMessage } from '../../../../interfaces/message-publisher';
|
||||
import { MESSAGE_BROKER_PUBLISHER } from '../../../../app.constants';
|
||||
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||
|
||||
@Injectable()
|
||||
export class MessagePublisher implements IPublishMessage {
|
||||
constructor(
|
||||
@Inject(MESSAGE_BROKER_PUBLISHER)
|
||||
private readonly messageBrokerPublisher: MessageBrokerPublisher,
|
||||
) {}
|
||||
|
||||
publish = (routingKey: string, message: string): void => {
|
||||
this.messageBrokerPublisher.publish(routingKey, message);
|
||||
};
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { UserRepository } from '../../../database/domain/user-repository';
|
||||
import { User } from '../../domain/entities/user';
|
||||
|
||||
@Injectable()
|
||||
export class UsersRepository extends UserRepository<User> {
|
||||
protected model = 'user';
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import { CreateUserRequest } from '../domain/dtos/create-user.request';
|
||||
|
||||
export class CreateUserCommand {
|
||||
readonly createUserRequest: CreateUserRequest;
|
||||
|
||||
constructor(request: CreateUserRequest) {
|
||||
this.createUserRequest = request;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
export class DeleteUserCommand {
|
||||
readonly uuid: string;
|
||||
|
||||
constructor(uuid: string) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import { UpdateUserRequest } from '../domain/dtos/update-user.request';
|
||||
|
||||
export class UpdateUserCommand {
|
||||
readonly updateUserRequest: UpdateUserRequest;
|
||||
|
||||
constructor(request: UpdateUserRequest) {
|
||||
this.updateUserRequest = request;
|
||||
}
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import { AutoMap } from '@automapper/classes';
|
||||
import { IsEmail, IsOptional, IsPhoneNumber, IsString } from 'class-validator';
|
||||
|
||||
export class CreateUserRequest {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@AutoMap()
|
||||
uuid?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@AutoMap()
|
||||
firstName?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@AutoMap()
|
||||
lastName?: string;
|
||||
|
||||
@IsEmail()
|
||||
@IsOptional()
|
||||
@AutoMap()
|
||||
email?: string;
|
||||
|
||||
@IsPhoneNumber()
|
||||
@IsOptional()
|
||||
@AutoMap()
|
||||
phone?: string;
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
import { IsInt, IsOptional } from 'class-validator';
|
||||
|
||||
export class FindAllUsersRequest {
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
page?: number;
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
perPage?: number;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class FindUserByUuidRequest {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
uuid: string;
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
import { AutoMap } from '@automapper/classes';
|
||||
import {
|
||||
IsEmail,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsPhoneNumber,
|
||||
IsString,
|
||||
} from 'class-validator';
|
||||
|
||||
export class UpdateUserRequest {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@AutoMap()
|
||||
uuid: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@AutoMap()
|
||||
firstName?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@AutoMap()
|
||||
lastName?: string;
|
||||
|
||||
@IsEmail()
|
||||
@IsOptional()
|
||||
@AutoMap()
|
||||
email?: string;
|
||||
|
||||
@IsPhoneNumber()
|
||||
@IsOptional()
|
||||
@AutoMap()
|
||||
phone?: string;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import { AutoMap } from '@automapper/classes';
|
||||
|
||||
export class User {
|
||||
@AutoMap()
|
||||
uuid: string;
|
||||
|
||||
@AutoMap()
|
||||
firstName?: string;
|
||||
|
||||
@AutoMap()
|
||||
lastName?: string;
|
||||
|
||||
@AutoMap()
|
||||
email?: string;
|
||||
|
||||
@AutoMap()
|
||||
phone?: string;
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
import { Mapper } from '@automapper/core';
|
||||
import { InjectMapper } from '@automapper/nestjs';
|
||||
import { CommandHandler } from '@nestjs/cqrs';
|
||||
import { UsersRepository } from '../../adapters/secondaries/users.repository';
|
||||
import { CreateUserCommand } from '../../commands/create-user.command';
|
||||
import { CreateUserRequest } from '../dtos/create-user.request';
|
||||
import { User } from '../entities/user';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { MESSAGE_PUBLISHER } from '../../../../app.constants';
|
||||
import { IPublishMessage } from '../../../../interfaces/message-publisher';
|
||||
|
||||
@CommandHandler(CreateUserCommand)
|
||||
export class CreateUserUseCase {
|
||||
constructor(
|
||||
private readonly repository: UsersRepository,
|
||||
@Inject(MESSAGE_PUBLISHER)
|
||||
private readonly messagePublisher: IPublishMessage,
|
||||
@InjectMapper() private readonly mapper: Mapper,
|
||||
) {}
|
||||
|
||||
execute = async (command: CreateUserCommand): Promise<User> => {
|
||||
const entity = this.mapper.map(
|
||||
command.createUserRequest,
|
||||
CreateUserRequest,
|
||||
User,
|
||||
);
|
||||
|
||||
try {
|
||||
const user = await this.repository.create(entity);
|
||||
this.messagePublisher.publish('user.create', JSON.stringify(user));
|
||||
this.messagePublisher.publish(
|
||||
'logging.user.create.info',
|
||||
JSON.stringify(user),
|
||||
);
|
||||
return user;
|
||||
} catch (error) {
|
||||
let key = 'logging.user.create.crit';
|
||||
if (error.message.includes('Already exists')) {
|
||||
key = 'logging.user.create.warning';
|
||||
}
|
||||
this.messagePublisher.publish(
|
||||
key,
|
||||
JSON.stringify({
|
||||
command,
|
||||
error,
|
||||
}),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
import { CommandHandler } from '@nestjs/cqrs';
|
||||
import { UsersRepository } from '../../adapters/secondaries/users.repository';
|
||||
import { DeleteUserCommand } from '../../commands/delete-user.command';
|
||||
import { User } from '../entities/user';
|
||||
import { IPublishMessage } from '../../../../interfaces/message-publisher';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { MESSAGE_PUBLISHER } from '../../../../app.constants';
|
||||
|
||||
@CommandHandler(DeleteUserCommand)
|
||||
export class DeleteUserUseCase {
|
||||
constructor(
|
||||
private readonly repository: UsersRepository,
|
||||
@Inject(MESSAGE_PUBLISHER)
|
||||
private readonly messagePublisher: IPublishMessage,
|
||||
) {}
|
||||
|
||||
execute = async (command: DeleteUserCommand): Promise<User> => {
|
||||
try {
|
||||
const user = await this.repository.delete(command.uuid);
|
||||
this.messagePublisher.publish(
|
||||
'user.delete',
|
||||
JSON.stringify({ uuid: user.uuid }),
|
||||
);
|
||||
this.messagePublisher.publish(
|
||||
'logging.user.delete.info',
|
||||
JSON.stringify({ uuid: user.uuid }),
|
||||
);
|
||||
return user;
|
||||
} catch (error) {
|
||||
this.messagePublisher.publish(
|
||||
'logging.user.delete.crit',
|
||||
JSON.stringify({
|
||||
command,
|
||||
error,
|
||||
}),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
import { QueryHandler } from '@nestjs/cqrs';
|
||||
import { ICollection } from 'src/modules/database/interfaces/collection.interface';
|
||||
import { UsersRepository } from '../../adapters/secondaries/users.repository';
|
||||
import { FindAllUsersQuery } from '../../queries/find-all-users.query';
|
||||
import { User } from '../entities/user';
|
||||
|
||||
@QueryHandler(FindAllUsersQuery)
|
||||
export class FindAllUsersUseCase {
|
||||
constructor(private readonly repository: UsersRepository) {}
|
||||
|
||||
execute = async (
|
||||
findAllUsersQuery: FindAllUsersQuery,
|
||||
): Promise<ICollection<User>> =>
|
||||
this.repository.findAll(findAllUsersQuery.page, findAllUsersQuery.perPage);
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
import { Inject, NotFoundException } from '@nestjs/common';
|
||||
import { QueryHandler } from '@nestjs/cqrs';
|
||||
import { UsersRepository } from '../../adapters/secondaries/users.repository';
|
||||
import { FindUserByUuidQuery } from '../../queries/find-user-by-uuid.query';
|
||||
import { User } from '../entities/user';
|
||||
import { MESSAGE_PUBLISHER } from '../../../../app.constants';
|
||||
import { IPublishMessage } from '../../../../interfaces/message-publisher';
|
||||
|
||||
@QueryHandler(FindUserByUuidQuery)
|
||||
export class FindUserByUuidUseCase {
|
||||
constructor(
|
||||
private readonly repository: UsersRepository,
|
||||
@Inject(MESSAGE_PUBLISHER)
|
||||
private readonly messagePublisher: IPublishMessage,
|
||||
) {}
|
||||
|
||||
execute = async (findUserByUuid: FindUserByUuidQuery): Promise<User> => {
|
||||
try {
|
||||
const user = await this.repository.findOneByUuid(findUserByUuid.uuid);
|
||||
if (!user) throw new NotFoundException();
|
||||
return user;
|
||||
} catch (error) {
|
||||
this.messagePublisher.publish(
|
||||
'logging.user.read.warning',
|
||||
JSON.stringify({
|
||||
query: findUserByUuid,
|
||||
error,
|
||||
}),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
import { Mapper } from '@automapper/core';
|
||||
import { InjectMapper } from '@automapper/nestjs';
|
||||
import { CommandHandler } from '@nestjs/cqrs';
|
||||
import { UsersRepository } from '../../adapters/secondaries/users.repository';
|
||||
import { UpdateUserCommand } from '../../commands/update-user.command';
|
||||
import { UpdateUserRequest } from '../dtos/update-user.request';
|
||||
import { User } from '../entities/user';
|
||||
import { MESSAGE_PUBLISHER } from '../../../../app.constants';
|
||||
import { Inject } from '@nestjs/common';
|
||||
import { IPublishMessage } from '../../../../interfaces/message-publisher';
|
||||
|
||||
@CommandHandler(UpdateUserCommand)
|
||||
export class UpdateUserUseCase {
|
||||
constructor(
|
||||
private readonly repository: UsersRepository,
|
||||
@Inject(MESSAGE_PUBLISHER)
|
||||
private readonly messagePublisher: IPublishMessage,
|
||||
@InjectMapper() private readonly mapper: Mapper,
|
||||
) {}
|
||||
|
||||
execute = async (command: UpdateUserCommand): Promise<User> => {
|
||||
const entity = this.mapper.map(
|
||||
command.updateUserRequest,
|
||||
UpdateUserRequest,
|
||||
User,
|
||||
);
|
||||
|
||||
try {
|
||||
const user = await this.repository.update(
|
||||
command.updateUserRequest.uuid,
|
||||
entity,
|
||||
);
|
||||
this.messagePublisher.publish(
|
||||
'user.update',
|
||||
JSON.stringify(command.updateUserRequest),
|
||||
);
|
||||
this.messagePublisher.publish(
|
||||
'logging.user.update.info',
|
||||
JSON.stringify(command.updateUserRequest),
|
||||
);
|
||||
return user;
|
||||
} catch (error) {
|
||||
this.messagePublisher.publish(
|
||||
'logging.user.update.crit',
|
||||
JSON.stringify({
|
||||
command,
|
||||
error,
|
||||
}),
|
||||
);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,29 +0,0 @@
|
|||
import { createMap, forMember, ignore, Mapper } from '@automapper/core';
|
||||
import { AutomapperProfile, InjectMapper } from '@automapper/nestjs';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { UserPresenter } from '../adapters/primaries/user.presenter';
|
||||
import { CreateUserRequest } from '../domain/dtos/create-user.request';
|
||||
import { UpdateUserRequest } from '../domain/dtos/update-user.request';
|
||||
import { User } from '../domain/entities/user';
|
||||
|
||||
@Injectable()
|
||||
export class UserProfile extends AutomapperProfile {
|
||||
constructor(@InjectMapper() mapper: Mapper) {
|
||||
super(mapper);
|
||||
}
|
||||
|
||||
override get profile() {
|
||||
return (mapper) => {
|
||||
createMap(mapper, User, UserPresenter);
|
||||
|
||||
createMap(mapper, CreateUserRequest, User);
|
||||
|
||||
createMap(
|
||||
mapper,
|
||||
UpdateUserRequest,
|
||||
User,
|
||||
forMember((dest) => dest.uuid, ignore()),
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
import { FindAllUsersRequest } from '../domain/dtos/find-all-users.request';
|
||||
|
||||
export class FindAllUsersQuery {
|
||||
page: number;
|
||||
perPage: number;
|
||||
|
||||
constructor(findAllUsersRequest?: FindAllUsersRequest) {
|
||||
this.page = findAllUsersRequest?.page ?? 1;
|
||||
this.perPage = findAllUsersRequest?.perPage ?? 10;
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
import { FindUserByUuidRequest } from '../domain/dtos/find-user-by-uuid.request';
|
||||
|
||||
export class FindUserByUuidQuery {
|
||||
readonly uuid: string;
|
||||
|
||||
constructor(findUserByUuidRequest: FindUserByUuidRequest) {
|
||||
this.uuid = findUserByUuidRequest.uuid;
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersRepository } from '../../adapters/secondaries/users.repository';
|
||||
import { FindAllUsersRequest } from '../../domain/dtos/find-all-users.request';
|
||||
import { FindAllUsersUseCase } from '../../domain/usecases/find-all-users.usecase';
|
||||
import { FindAllUsersQuery } from '../../queries/find-all-users.query';
|
||||
|
||||
const findAllUsersRequest: FindAllUsersRequest = new FindAllUsersRequest();
|
||||
findAllUsersRequest.page = 1;
|
||||
findAllUsersRequest.perPage = 10;
|
||||
|
||||
const findAllUsersQuery: FindAllUsersQuery = new FindAllUsersQuery(
|
||||
findAllUsersRequest,
|
||||
);
|
||||
|
||||
const mockUsers = [
|
||||
{
|
||||
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
email: 'john.doe@email.com',
|
||||
phone: '0601020304',
|
||||
},
|
||||
{
|
||||
uuid: 'bb281075-1b98-4456-89d6-c643d3044a92',
|
||||
firstName: 'Jane',
|
||||
lastName: 'Doe',
|
||||
email: 'jane.doe@email.com',
|
||||
phone: '0602030405',
|
||||
},
|
||||
{
|
||||
uuid: 'bb281075-1b98-4456-89d6-c643d3044a93',
|
||||
firstName: 'Jimmy',
|
||||
lastName: 'Doe',
|
||||
email: 'jimmy.doe@email.com',
|
||||
phone: '0603040506',
|
||||
},
|
||||
];
|
||||
|
||||
const mockUsersRepository = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
findAll: jest.fn().mockImplementation((query?: FindAllUsersQuery) => {
|
||||
return Promise.resolve(mockUsers);
|
||||
}),
|
||||
};
|
||||
|
||||
describe('FindAllUsersUseCase', () => {
|
||||
let findAllUsersUseCase: FindAllUsersUseCase;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: UsersRepository,
|
||||
useValue: mockUsersRepository,
|
||||
},
|
||||
FindAllUsersUseCase,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
findAllUsersUseCase = module.get<FindAllUsersUseCase>(FindAllUsersUseCase);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(findAllUsersUseCase).toBeDefined();
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
it('should return an array filled with users', async () => {
|
||||
const users = await findAllUsersUseCase.execute(findAllUsersQuery);
|
||||
|
||||
expect(users).toBe(mockUsers);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,82 +0,0 @@
|
|||
import { NotFoundException } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersRepository } from '../../adapters/secondaries/users.repository';
|
||||
import { FindUserByUuidRequest } from '../../domain/dtos/find-user-by-uuid.request';
|
||||
import { FindUserByUuidUseCase } from '../../domain/usecases/find-user-by-uuid.usecase';
|
||||
import { FindUserByUuidQuery } from '../../queries/find-user-by-uuid.query';
|
||||
import { MESSAGE_PUBLISHER } from '../../../../app.constants';
|
||||
|
||||
const mockUser = {
|
||||
uuid: 'bb281075-1b98-4456-89d6-c643d3044a91',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
email: 'john.doe@email.com',
|
||||
phone: '0601020304',
|
||||
};
|
||||
|
||||
const mockUserRepository = {
|
||||
findOneByUuid: jest
|
||||
.fn()
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
.mockImplementationOnce((query?: FindUserByUuidQuery) => {
|
||||
return Promise.resolve(mockUser);
|
||||
})
|
||||
.mockImplementation(() => {
|
||||
return Promise.resolve(undefined);
|
||||
}),
|
||||
};
|
||||
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
};
|
||||
|
||||
describe('FindUserByUuidUseCase', () => {
|
||||
let findUserByUuidUseCase: FindUserByUuidUseCase;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
imports: [],
|
||||
providers: [
|
||||
{
|
||||
provide: UsersRepository,
|
||||
useValue: mockUserRepository,
|
||||
},
|
||||
{
|
||||
provide: MESSAGE_PUBLISHER,
|
||||
useValue: mockMessagePublisher,
|
||||
},
|
||||
FindUserByUuidUseCase,
|
||||
],
|
||||
}).compile();
|
||||
|
||||
findUserByUuidUseCase = module.get<FindUserByUuidUseCase>(
|
||||
FindUserByUuidUseCase,
|
||||
);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(findUserByUuidUseCase).toBeDefined();
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
it('should return a user', async () => {
|
||||
const findUserByUuidRequest: FindUserByUuidRequest =
|
||||
new FindUserByUuidRequest();
|
||||
findUserByUuidRequest.uuid = 'bb281075-1b98-4456-89d6-c643d3044a91';
|
||||
const user = await findUserByUuidUseCase.execute(
|
||||
new FindUserByUuidQuery(findUserByUuidRequest),
|
||||
);
|
||||
expect(user).toBe(mockUser);
|
||||
});
|
||||
it('should throw an error if user does not exist', async () => {
|
||||
const findUserByUuidRequest: FindUserByUuidRequest =
|
||||
new FindUserByUuidRequest();
|
||||
findUserByUuidRequest.uuid = 'bb281075-1b98-4456-89d6-c643d3044a90';
|
||||
await expect(
|
||||
findUserByUuidUseCase.execute(
|
||||
new FindUserByUuidQuery(findUserByUuidRequest),
|
||||
),
|
||||
).rejects.toBeInstanceOf(NotFoundException);
|
||||
});
|
||||
});
|
||||
});
|
File diff suppressed because it is too large
Load Diff
|
@ -34,7 +34,7 @@
|
|||
"@grpc/proto-loader": "^0.7.4",
|
||||
"@liaoliaots/nestjs-redis": "^9.0.5",
|
||||
"@mobicoop/configuration-module": "^1.2.0",
|
||||
"@mobicoop/ddd-library": "^0.3.0",
|
||||
"@mobicoop/ddd-library": "file:../../packages/dddlibrary",
|
||||
"@mobicoop/health-module": "^2.0.0",
|
||||
"@mobicoop/message-broker-module": "^1.2.0",
|
||||
"@nestjs/cache-manager": "^1.0.0",
|
||||
|
@ -87,6 +87,7 @@
|
|||
"modulePathIgnorePatterns": [
|
||||
".module.ts",
|
||||
".dto.ts",
|
||||
".constants.ts",
|
||||
".di-tokens.ts",
|
||||
".response.ts",
|
||||
".port.ts",
|
||||
|
@ -104,6 +105,7 @@
|
|||
"coveragePathIgnorePatterns": [
|
||||
".module.ts",
|
||||
".dto.ts",
|
||||
".constants.ts",
|
||||
".di-tokens.ts",
|
||||
".response.ts",
|
||||
".port.ts",
|
||||
|
|
|
@ -2,7 +2,9 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { MessagePublisherPort } from '@mobicoop/ddd-library';
|
||||
import { USER_MESSAGE_PUBLISHER } from '@modules/user/user.di-tokens';
|
||||
import { UserCreatedDomainEvent } from '../../domain/events/user-created.domain-events';
|
||||
import { UserCreatedDomainEvent } from '../../domain/events/user-created.domain-event';
|
||||
import { UserCreatedIntegrationEvent } from '../events/user-created.integration-event';
|
||||
import { USER_CREATED_ROUTING_KEY } from '@modules/user/user.constants';
|
||||
|
||||
@Injectable()
|
||||
export class PublishMessageWhenUserIsCreatedDomainEventHandler {
|
||||
|
@ -13,6 +15,17 @@ export class PublishMessageWhenUserIsCreatedDomainEventHandler {
|
|||
|
||||
@OnEvent(UserCreatedDomainEvent.name, { async: true, promisify: true })
|
||||
async handle(event: UserCreatedDomainEvent): Promise<any> {
|
||||
this.messagePublisher.publish('user.created', JSON.stringify(event));
|
||||
const userCreatedIntegrationEvent = new UserCreatedIntegrationEvent({
|
||||
id: event.aggregateId,
|
||||
firstName: event.firstName,
|
||||
lastName: event.lastName,
|
||||
email: event.email,
|
||||
phone: event.phone,
|
||||
metadata: event.metadata,
|
||||
});
|
||||
this.messagePublisher.publish(
|
||||
USER_CREATED_ROUTING_KEY,
|
||||
JSON.stringify(userCreatedIntegrationEvent),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ import { OnEvent } from '@nestjs/event-emitter';
|
|||
import { MessagePublisherPort } from '@mobicoop/ddd-library';
|
||||
import { USER_MESSAGE_PUBLISHER } from '@modules/user/user.di-tokens';
|
||||
import { UserDeletedDomainEvent } from '../../domain/events/user-deleted.domain-event';
|
||||
import { UserDeletedIntegrationEvent } from '../events/user-deleted.integration-event';
|
||||
import { USER_DELETED_ROUTING_KEY } from '@modules/user/user.constants';
|
||||
|
||||
@Injectable()
|
||||
export class PublishMessageWhenUserIsDeletedDomainEventHandler {
|
||||
|
@ -13,6 +15,13 @@ export class PublishMessageWhenUserIsDeletedDomainEventHandler {
|
|||
|
||||
@OnEvent(UserDeletedDomainEvent.name, { async: true, promisify: true })
|
||||
async handle(event: UserDeletedDomainEvent): Promise<any> {
|
||||
this.messagePublisher.publish('user.deleted', JSON.stringify(event));
|
||||
const userDeletedIntegrationEvent = new UserDeletedIntegrationEvent({
|
||||
id: event.aggregateId,
|
||||
metadata: event.metadata,
|
||||
});
|
||||
this.messagePublisher.publish(
|
||||
USER_DELETED_ROUTING_KEY,
|
||||
JSON.stringify(userDeletedIntegrationEvent),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import { MessagePublisherPort } from '@mobicoop/ddd-library';
|
||||
import { USER_MESSAGE_PUBLISHER } from '@modules/user/user.di-tokens';
|
||||
import { UserUpdatedDomainEvent } from '../../domain/events/user-updated.domain-events';
|
||||
import { UserUpdatedDomainEvent } from '../../domain/events/user-updated.domain-event';
|
||||
import { UserUpdatedIntegrationEvent } from '../events/user-updated.integration-event';
|
||||
import { USER_UPDATED_ROUTING_KEY } from '@modules/user/user.constants';
|
||||
|
||||
@Injectable()
|
||||
export class PublishMessageWhenUserIsUpdatedDomainEventHandler {
|
||||
|
@ -13,6 +15,17 @@ export class PublishMessageWhenUserIsUpdatedDomainEventHandler {
|
|||
|
||||
@OnEvent(UserUpdatedDomainEvent.name, { async: true, promisify: true })
|
||||
async handle(event: UserUpdatedDomainEvent): Promise<any> {
|
||||
this.messagePublisher.publish('user.updated', JSON.stringify(event));
|
||||
const userUpdatedIntegrationEvent = new UserUpdatedIntegrationEvent({
|
||||
id: event.aggregateId,
|
||||
firstName: event.firstName,
|
||||
lastName: event.lastName,
|
||||
email: event.email,
|
||||
phone: event.phone,
|
||||
metadata: event.metadata,
|
||||
});
|
||||
this.messagePublisher.publish(
|
||||
USER_UPDATED_ROUTING_KEY,
|
||||
JSON.stringify(userUpdatedIntegrationEvent),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { IntegrationEvent, IntegrationEventProps } from '@mobicoop/ddd-library';
|
||||
|
||||
export class UserCreatedIntegrationEvent extends IntegrationEvent {
|
||||
readonly firstName: string;
|
||||
readonly lastName: string;
|
||||
readonly email: string;
|
||||
readonly phone: string;
|
||||
|
||||
constructor(props: IntegrationEventProps<UserCreatedIntegrationEvent>) {
|
||||
super(props);
|
||||
this.firstName = props.firstName;
|
||||
this.lastName = props.lastName;
|
||||
this.email = props.email;
|
||||
this.phone = props.phone;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { IntegrationEvent, IntegrationEventProps } from '@mobicoop/ddd-library';
|
||||
|
||||
export class UserDeletedIntegrationEvent extends IntegrationEvent {
|
||||
constructor(props: IntegrationEventProps<UserDeletedIntegrationEvent>) {
|
||||
super(props);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import { IntegrationEvent, IntegrationEventProps } from '@mobicoop/ddd-library';
|
||||
|
||||
export class UserUpdatedIntegrationEvent extends IntegrationEvent {
|
||||
readonly firstName: string;
|
||||
readonly lastName: string;
|
||||
readonly email: string;
|
||||
readonly phone: string;
|
||||
|
||||
constructor(props: IntegrationEventProps<UserUpdatedIntegrationEvent>) {
|
||||
super(props);
|
||||
this.firstName = props.firstName;
|
||||
this.lastName = props.lastName;
|
||||
this.email = props.email;
|
||||
this.phone = props.phone;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library';
|
||||
import { v4 } from 'uuid';
|
||||
import { CreateUserProps, UpdateUserProps, UserProps } from './user.types';
|
||||
import { UserCreatedDomainEvent } from './events/user-created.domain-events';
|
||||
import { UserUpdatedDomainEvent } from './events/user-updated.domain-events';
|
||||
import { UserCreatedDomainEvent } from './events/user-created.domain-event';
|
||||
import { UserUpdatedDomainEvent } from './events/user-updated.domain-event';
|
||||
import { UserDeletedDomainEvent } from './events/user-deleted.domain-event';
|
||||
|
||||
export class UserEntity extends AggregateRoot<UserProps> {
|
||||
|
|
|
@ -11,22 +11,27 @@ import { UserRepositoryPort } from '../core/application/ports/user.repository.po
|
|||
import { UserMapper } from '../user.mapper';
|
||||
import { USER_MESSAGE_PUBLISHER } from '../user.di-tokens';
|
||||
|
||||
export type UserModel = {
|
||||
export type UserBaseModel = {
|
||||
uuid: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
export type UserReadModel = UserBaseModel & {
|
||||
createdAt?: Date;
|
||||
updatedAt?: Date;
|
||||
};
|
||||
|
||||
export type UserWriteModel = UserBaseModel;
|
||||
|
||||
/**
|
||||
* Repository is used for retrieving/saving domain entities
|
||||
* */
|
||||
@Injectable()
|
||||
export class UserRepository
|
||||
extends PrismaRepositoryBase<UserEntity, UserModel, UserModel>
|
||||
extends PrismaRepositoryBase<UserEntity, UserReadModel, UserWriteModel>
|
||||
implements UserRepositoryPort
|
||||
{
|
||||
constructor(
|
||||
|
|
|
@ -25,11 +25,12 @@ import {
|
|||
export class UpdateUserGrpcController {
|
||||
constructor(private readonly commandBus: CommandBus) {}
|
||||
|
||||
@GrpcMethod('UserService', 'UpdateUser')
|
||||
@GrpcMethod('UserService', 'Update')
|
||||
async updateUser(data: UpdateUserRequestDto): Promise<IdResponse> {
|
||||
try {
|
||||
const aggregateID: AggregateID = await this.commandBus.execute(
|
||||
new UpdateUserCommand({
|
||||
id: data.id,
|
||||
firstName: data.firstName,
|
||||
lastName: data.lastName,
|
||||
email: data.email,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PublishMessageWhenUserIsCreatedDomainEventHandler } from '@modules/user/core/application/event-handlers/publish-message-when-user-is-created.domain-event-handler';
|
||||
import { USER_MESSAGE_PUBLISHER } from '@modules/user/user.di-tokens';
|
||||
import { UserCreatedDomainEvent } from '@modules/user/core/domain/events/user-created.domain-events';
|
||||
import { UserCreatedDomainEvent } from '@modules/user/core/domain/events/user-created.domain-event';
|
||||
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
|
@ -48,7 +48,7 @@ describe('Publish message when user is created domain event handler', () => {
|
|||
expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
|
||||
expect(mockMessagePublisher.publish).toHaveBeenCalledWith(
|
||||
'user.created',
|
||||
'{"id":"some-domain-event-id","aggregateId":"some-aggregate-id","firstName":"John","lastName":"Doe","email":"john.doe@email.com","phone":"+33611223344","metadata":{"timestamp":1687928400000,"correlationId":"some-correlation-id"}}',
|
||||
'{"id":"some-aggregate-id","metadata":{"correlationId":"some-correlation-id","timestamp":1687928400000},"firstName":"John","lastName":"Doe","email":"john.doe@email.com","phone":"+33611223344"}',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { USER_MESSAGE_PUBLISHER } from '@modules/user/user.di-tokens';
|
||||
import { UserCreatedDomainEvent } from '@modules/user/core/domain/events/user-created.domain-events';
|
||||
import { UserCreatedDomainEvent } from '@modules/user/core/domain/events/user-created.domain-event';
|
||||
import { PublishMessageWhenUserIsDeletedDomainEventHandler } from '@modules/user/core/application/event-handlers/publish-message-when-user-is-deleted.domain-event-handler';
|
||||
|
||||
const mockMessagePublisher = {
|
||||
|
@ -48,7 +48,7 @@ describe('Publish message when user is deleted domain event handler', () => {
|
|||
expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
|
||||
expect(mockMessagePublisher.publish).toHaveBeenCalledWith(
|
||||
'user.deleted',
|
||||
'{"id":"some-domain-event-id","aggregateId":"some-aggregate-id","firstName":"John","lastName":"Doe","email":"john.doe@email.com","phone":"+33611223344","metadata":{"timestamp":1687928400000,"correlationId":"some-correlation-id"}}',
|
||||
'{"id":"some-aggregate-id","metadata":{"correlationId":"some-correlation-id","timestamp":1687928400000}}',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { USER_MESSAGE_PUBLISHER } from '@modules/user/user.di-tokens';
|
||||
import { PublishMessageWhenUserIsUpdatedDomainEventHandler } from '@modules/user/core/application/event-handlers/publish-message-when-user-is-updated.domain-event-handler';
|
||||
import { UserUpdatedDomainEvent } from '@modules/user/core/domain/events/user-updated.domain-events';
|
||||
import { UserUpdatedDomainEvent } from '@modules/user/core/domain/events/user-updated.domain-event';
|
||||
|
||||
const mockMessagePublisher = {
|
||||
publish: jest.fn().mockImplementation(),
|
||||
|
@ -48,7 +48,7 @@ describe('Publish message when user is updated domain event handler', () => {
|
|||
expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
|
||||
expect(mockMessagePublisher.publish).toHaveBeenCalledWith(
|
||||
'user.updated',
|
||||
'{"id":"some-domain-event-id","aggregateId":"some-aggregate-id","firstName":"Jane","lastName":"Doe","email":"jane.doe@email.com","phone":"+33611223344","metadata":{"timestamp":1687928400000,"correlationId":"some-correlation-id"}}',
|
||||
'{"id":"some-aggregate-id","metadata":{"correlationId":"some-correlation-id","timestamp":1687928400000},"firstName":"Jane","lastName":"Doe","email":"jane.doe@email.com","phone":"+33611223344"}',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { UserCreatedDomainEvent } from '@modules/user/core/domain/events/user-created.domain-events';
|
||||
import { UserCreatedDomainEvent } from '@modules/user/core/domain/events/user-created.domain-event';
|
||||
import { UserDeletedDomainEvent } from '@modules/user/core/domain/events/user-deleted.domain-event';
|
||||
import { UserUpdatedDomainEvent } from '@modules/user/core/domain/events/user-updated.domain-events';
|
||||
import { UserUpdatedDomainEvent } from '@modules/user/core/domain/events/user-updated.domain-event';
|
||||
import { UserEntity } from '@modules/user/core/domain/user.entity';
|
||||
import {
|
||||
CreateUserProps,
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import { UserEntity } from '@modules/user/core/domain/user.entity';
|
||||
import { UserModel } from '@modules/user/infrastructure/user.repository';
|
||||
import {
|
||||
UserReadModel,
|
||||
UserWriteModel,
|
||||
} from '@modules/user/infrastructure/user.repository';
|
||||
import { UserResponseDto } from '@modules/user/interface/dtos/user.response.dto';
|
||||
import { UserMapper } from '@modules/user/user.mapper';
|
||||
import { Test } from '@nestjs/testing';
|
||||
|
@ -16,7 +19,7 @@ const userEntity: UserEntity = new UserEntity({
|
|||
createdAt: now,
|
||||
updatedAt: now,
|
||||
});
|
||||
const userModel: UserModel = {
|
||||
const userReadModel: UserReadModel = {
|
||||
uuid: 'c160cf8c-f057-4962-841f-3ad68346df44',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
|
@ -41,12 +44,12 @@ describe('User Mapper', () => {
|
|||
});
|
||||
|
||||
it('should map domain entity to persistence data', async () => {
|
||||
const mapped: UserModel = userMapper.toPersistence(userEntity);
|
||||
const mapped: UserWriteModel = userMapper.toPersistence(userEntity);
|
||||
expect(mapped.lastName).toBe('Doe');
|
||||
});
|
||||
|
||||
it('should map persisted data to domain entity', async () => {
|
||||
const mapped: UserEntity = userMapper.toDomain(userModel);
|
||||
const mapped: UserEntity = userMapper.toDomain(userReadModel);
|
||||
expect(mapped.getProps().firstName).toBe('John');
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
export const USER_CREATED_ROUTING_KEY = 'user.created';
|
||||
export const USER_UPDATED_ROUTING_KEY = 'user.updated';
|
||||
export const USER_DELETED_ROUTING_KEY = 'user.deleted';
|
|
@ -1,7 +1,10 @@
|
|||
import { Mapper } from '@mobicoop/ddd-library';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { UserEntity } from './core/domain/user.entity';
|
||||
import { UserModel } from './infrastructure/user.repository';
|
||||
import {
|
||||
UserReadModel,
|
||||
UserWriteModel,
|
||||
} from './infrastructure/user.repository';
|
||||
import { UserResponseDto } from './interface/dtos/user.response.dto';
|
||||
|
||||
/**
|
||||
|
@ -13,23 +16,21 @@ import { UserResponseDto } from './interface/dtos/user.response.dto';
|
|||
|
||||
@Injectable()
|
||||
export class UserMapper
|
||||
implements Mapper<UserEntity, UserModel, UserModel, UserResponseDto>
|
||||
implements Mapper<UserEntity, UserReadModel, UserWriteModel, UserResponseDto>
|
||||
{
|
||||
toPersistence = (entity: UserEntity): UserModel => {
|
||||
toPersistence = (entity: UserEntity): UserWriteModel => {
|
||||
const copy = entity.getProps();
|
||||
const record: UserModel = {
|
||||
const record: UserWriteModel = {
|
||||
uuid: copy.id,
|
||||
firstName: copy.firstName,
|
||||
lastName: copy.lastName,
|
||||
email: copy.email,
|
||||
phone: copy.phone,
|
||||
createdAt: copy.createdAt,
|
||||
updatedAt: copy.updatedAt,
|
||||
};
|
||||
return record;
|
||||
};
|
||||
|
||||
toDomain = (record: UserModel): UserEntity => {
|
||||
toDomain = (record: UserReadModel): UserEntity => {
|
||||
const entity = new UserEntity({
|
||||
id: record.uuid,
|
||||
createdAt: new Date(record.createdAt),
|
||||
|
@ -53,12 +54,4 @@ export class UserMapper
|
|||
response.phone = props.phone;
|
||||
return response;
|
||||
};
|
||||
|
||||
/* ^ Data returned to the user is whitelisted to avoid leaks.
|
||||
If a new property is added, like password or a
|
||||
credit card number, it won't be returned
|
||||
unless you specifically allow this.
|
||||
(avoid blacklisting, which will return everything
|
||||
but blacklisted items, which can lead to a data leak).
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -13,6 +13,13 @@ import { USER_MESSAGE_PUBLISHER, USER_REPOSITORY } from './user.di-tokens';
|
|||
import { UserRepository } from './infrastructure/user.repository';
|
||||
import { UserMapper } from './user.mapper';
|
||||
import { PrismaService } from './infrastructure/prisma.service';
|
||||
import { DeleteUserGrpcController } from './interface/grpc-controllers/delete-user.grpc.controller';
|
||||
import { FindUserByIdGrpcController } from './interface/grpc-controllers/find-user-by-id.grpc.controller';
|
||||
import { DeleteUserService } from './core/application/commands/delete-user/delete-user.service';
|
||||
import { FindUserByIdQueryHandler } from './core/application/queries/find-user-by-id/find-user-by-id.query-handler';
|
||||
import { PublishMessageWhenUserIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-user-is-created.domain-event-handler';
|
||||
import { PublishMessageWhenUserIsUpdatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-user-is-updated.domain-event-handler';
|
||||
import { PublishMessageWhenUserIsDeletedDomainEventHandler } from './core/application/event-handlers/publish-message-when-user-is-deleted.domain-event-handler';
|
||||
|
||||
const imports = [
|
||||
CqrsModule,
|
||||
|
@ -30,9 +37,26 @@ const imports = [
|
|||
}),
|
||||
];
|
||||
|
||||
const grpcControllers = [CreateUserGrpcController, UpdateUserGrpcController];
|
||||
const grpcControllers = [
|
||||
CreateUserGrpcController,
|
||||
UpdateUserGrpcController,
|
||||
DeleteUserGrpcController,
|
||||
FindUserByIdGrpcController,
|
||||
];
|
||||
|
||||
const commandHandlers: Provider[] = [CreateUserService, UpdateUserService];
|
||||
const eventHandlers: Provider[] = [
|
||||
PublishMessageWhenUserIsCreatedDomainEventHandler,
|
||||
PublishMessageWhenUserIsUpdatedDomainEventHandler,
|
||||
PublishMessageWhenUserIsDeletedDomainEventHandler,
|
||||
];
|
||||
|
||||
const commandHandlers: Provider[] = [
|
||||
CreateUserService,
|
||||
UpdateUserService,
|
||||
DeleteUserService,
|
||||
];
|
||||
|
||||
const queryHandlers: Provider[] = [FindUserByIdQueryHandler];
|
||||
|
||||
const mappers: Provider[] = [UserMapper];
|
||||
|
||||
|
@ -56,7 +80,9 @@ const orms: Provider[] = [PrismaService];
|
|||
imports,
|
||||
controllers: [...grpcControllers],
|
||||
providers: [
|
||||
...eventHandlers,
|
||||
...commandHandlers,
|
||||
...queryHandlers,
|
||||
...mappers,
|
||||
...repositories,
|
||||
...messagePublishers,
|
||||
|
|
Loading…
Reference in New Issue