create, read, findone
This commit is contained in:
parent
48165e1d79
commit
c2ae623e72
|
@ -8,3 +8,6 @@ CREATE TABLE "user" (
|
|||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "user_uuid_key" ON "user"("uuid");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "user_email_key" ON "user"("email");
|
|
@ -14,7 +14,7 @@ model User {
|
|||
uuid String @unique @default(uuid()) @db.Uuid
|
||||
firstName String
|
||||
lastName String
|
||||
email String
|
||||
email String @unique
|
||||
|
||||
@@map("user")
|
||||
}
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
import { classes } from '@automapper/classes';
|
||||
import { AutomapperModule } from '@automapper/nestjs';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { UsersModule } from './modules/users/users.module';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule.forRoot({ isGlobal: true }), UsersModule],
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
AutomapperModule.forRoot({ strategyInitializer: classes() }),
|
||||
UsersModule,
|
||||
],
|
||||
controllers: [],
|
||||
providers: [],
|
||||
})
|
||||
|
|
|
@ -9,7 +9,6 @@ export class DatabaseException implements Error {
|
|||
) {
|
||||
this.name = 'DatabaseException';
|
||||
this.message = message ?? 'An error occured with the database.';
|
||||
|
||||
if (this.message.includes('Unique constraint failed')) {
|
||||
this.message = 'Already exists.';
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ package user;
|
|||
service UsersService {
|
||||
rpc FindOneByUuid(UserByUuid) returns (User);
|
||||
rpc FindAll(UserFilter) returns (Users);
|
||||
rpc Create(CreateUser) returns (User);
|
||||
rpc Create(User) returns (User);
|
||||
rpc Update(User) returns (User);
|
||||
}
|
||||
|
||||
message UserByUuid {
|
||||
|
@ -19,13 +20,6 @@ message User {
|
|||
string email = 4;
|
||||
}
|
||||
|
||||
message CreateUser {
|
||||
string uuid = 1;
|
||||
string firstName = 2;
|
||||
string lastName = 3;
|
||||
string email = 4;
|
||||
}
|
||||
|
||||
message UserFilter {}
|
||||
|
||||
message Users {
|
||||
|
|
|
@ -1,25 +1,80 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { QueryBus } from '@nestjs/cqrs';
|
||||
import { Mapper } from '@automapper/core';
|
||||
import { InjectMapper } from '@automapper/nestjs';
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
ConflictException,
|
||||
Controller,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { CommandBus, QueryBus } from '@nestjs/cqrs';
|
||||
import { GrpcMethod, RpcException } from '@nestjs/microservices';
|
||||
import { FindUserByUuidRequest } from '../../domain/dto/findUserByUuidRequest';
|
||||
import { DatabaseException } from 'src/modules/database/src/exceptions/DatabaseException';
|
||||
import { CreateUserCommand } from '../../commands/create-user.command';
|
||||
import { UpdateUserCommand } from '../../commands/update-user.command';
|
||||
import { CreateUserRequest } from '../../domain/dto/create-user.request';
|
||||
import { FindUserByUuidRequest } from '../../domain/dto/find-user-by-uuid.request';
|
||||
import { UpdateUserRequest } from '../../domain/dto/update-user.request';
|
||||
import { User } from '../../domain/entities/user';
|
||||
import { FindUserByUuidQuery } from '../../queries/find-user-by-uuid.query';
|
||||
import { UserPresenter } from './user.presenter';
|
||||
|
||||
@Controller()
|
||||
export class UsersController {
|
||||
constructor(private readonly _queryBus: QueryBus) {}
|
||||
constructor(
|
||||
private readonly _commandBus: CommandBus,
|
||||
private readonly _queryBus: QueryBus,
|
||||
@InjectMapper() private readonly _mapper: Mapper,
|
||||
) {}
|
||||
|
||||
@GrpcMethod('UsersService', 'FindOneByUuid')
|
||||
async findOneByUuid(data: FindUserByUuidRequest): Promise<User> {
|
||||
async findOneByUuid(data: FindUserByUuidRequest): Promise<UserPresenter> {
|
||||
const user = await this._queryBus.execute(
|
||||
new FindUserByUuidQuery(data.uuid),
|
||||
);
|
||||
if (user) {
|
||||
return user;
|
||||
return this._mapper.map(user, User, UserPresenter);
|
||||
}
|
||||
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({});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { CreateUserRequest } from '../domain/dto/create-user.request';
|
||||
|
||||
export class CreateUserCommand {
|
||||
readonly createUserRequest: CreateUserRequest;
|
||||
|
||||
constructor(request: CreateUserRequest) {
|
||||
this.createUserRequest = request;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import { UpdateUserRequest } from '../domain/dto/update-user.request';
|
||||
|
||||
export class UpdateUserCommand {
|
||||
readonly updateUserRequest: UpdateUserRequest;
|
||||
|
||||
constructor(request: UpdateUserRequest) {
|
||||
this.updateUserRequest = request;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
import { AutoMap } from '@automapper/classes';
|
||||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class CreateUserRequest {
|
||||
@IsString()
|
||||
@AutoMap()
|
||||
uuid: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@AutoMap()
|
||||
firstName: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@AutoMap()
|
||||
lastName: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@AutoMap()
|
||||
email: string;
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { IsNotEmpty, IsString } from 'class-validator';
|
||||
|
||||
export class FindUserByUuidRequest {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
uuid: string;
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
import { IsString } from 'class-validator';
|
||||
|
||||
export class FindUserByUuidRequest {
|
||||
@IsString()
|
||||
uuid: string;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
import { CreateUserRequest } from './create-user.request';
|
||||
|
||||
export class UpdateUserRequest extends CreateUserRequest {}
|
|
@ -0,0 +1,25 @@
|
|||
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 '../dto/create-user.request';
|
||||
import { User } from '../entities/user';
|
||||
|
||||
@CommandHandler(CreateUserCommand)
|
||||
export class CreateUserUseCase {
|
||||
constructor(
|
||||
private readonly _repository: UsersRepository,
|
||||
@InjectMapper() private readonly _mapper: Mapper,
|
||||
) {}
|
||||
|
||||
async execute(command: CreateUserCommand): Promise<User> {
|
||||
const entity = this._mapper.map(
|
||||
command.createUserRequest,
|
||||
CreateUserRequest,
|
||||
User,
|
||||
);
|
||||
|
||||
return this._repository.create(entity);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
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 '../dto/update-user.request';
|
||||
import { User } from '../entities/user';
|
||||
|
||||
@CommandHandler(UpdateUserCommand)
|
||||
export class UpdateUserUseCase {
|
||||
constructor(
|
||||
private readonly _repository: UsersRepository,
|
||||
@InjectMapper() private readonly _mapper: Mapper,
|
||||
) {}
|
||||
|
||||
async execute(command: UpdateUserCommand): Promise<User> {
|
||||
const entity = this._mapper.map(
|
||||
command.updateUserRequest,
|
||||
UpdateUserRequest,
|
||||
User,
|
||||
);
|
||||
|
||||
return this._repository.update(command.updateUserRequest.uuid, entity);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
import { createMap, Mapper } from '@automapper/core';
|
||||
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/dto/create-user.request';
|
||||
import { UpdateUserRequest } from '../domain/dto/update-user.request';
|
||||
import { User } from '../domain/entities/user';
|
||||
|
||||
@Injectable()
|
||||
|
@ -13,6 +15,15 @@ export class UserProfile extends AutomapperProfile {
|
|||
override get profile() {
|
||||
return (mapper) => {
|
||||
createMap(mapper, User, UserPresenter);
|
||||
|
||||
createMap(mapper, CreateUserRequest, User);
|
||||
|
||||
createMap(
|
||||
mapper,
|
||||
UpdateUserRequest,
|
||||
User,
|
||||
forMember((dest) => dest.uuid, ignore()),
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,12 +3,21 @@ import { CqrsModule } from '@nestjs/cqrs';
|
|||
import { DatabaseModule } from '../database/database.module';
|
||||
import { UsersController } from './adapters/primaries/users.controller';
|
||||
import { UsersRepository } from './adapters/secondaries/users.repository';
|
||||
import { CreateUserUseCase } from './domain/usecases/create-user.usecase';
|
||||
import { FindUserByUuidUseCase } from './domain/usecases/find-user-by-uuid.usecase';
|
||||
import { UpdateUserUseCase } from './domain/usecases/update-user.usecase';
|
||||
import { UserProfile } from './mappers/user.profile';
|
||||
|
||||
@Module({
|
||||
imports: [DatabaseModule, CqrsModule],
|
||||
controllers: [UsersController],
|
||||
providers: [UsersRepository, FindUserByUuidUseCase],
|
||||
providers: [
|
||||
UserProfile,
|
||||
UsersRepository,
|
||||
FindUserByUuidUseCase,
|
||||
CreateUserUseCase,
|
||||
UpdateUserUseCase,
|
||||
],
|
||||
exports: [],
|
||||
})
|
||||
export class UsersModule {}
|
||||
|
|
Loading…
Reference in New Issue