.
This commit is contained in:
parent
923bd92220
commit
1685a44fc2
@ -8,7 +8,7 @@ import {
|
||||
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||
import { IInfrastructureError } from "@/contexts/common/infrastructure";
|
||||
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||
import { Result, ensureUserEmailIsValid } from "@shared/contexts";
|
||||
import { Result, ensureEmailIsValid } from "@shared/contexts";
|
||||
import { AuthUser } from "../domain";
|
||||
import { findUserByEmail } from "./authServices";
|
||||
|
||||
@ -46,7 +46,7 @@ export class FindUserByEmailUseCase
|
||||
|
||||
// Validaciones de datos
|
||||
|
||||
const emailOrError = ensureUserEmailIsValid(email);
|
||||
const emailOrError = ensureEmailIsValid(email);
|
||||
if (emailOrError.isFailure) {
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
@ -57,7 +57,6 @@ export class FindUserByEmailUseCase
|
||||
);
|
||||
}
|
||||
|
||||
// Crear auth
|
||||
try {
|
||||
const user = await findUserByEmail(
|
||||
emailOrError.object,
|
||||
|
||||
@ -8,11 +8,9 @@ export const findUserByEmail = async (
|
||||
adapter: IAdapter,
|
||||
repository: RepositoryBuilder<IAuthRepository>,
|
||||
): Promise<AuthUser | null> => {
|
||||
const user = await adapter
|
||||
return await adapter
|
||||
.startTransaction()
|
||||
.complete(async (t) =>
|
||||
repository({ transaction: t }).findUserByEmail(email),
|
||||
);
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
@ -46,15 +46,9 @@ export abstract class ExpressController implements IController {
|
||||
}
|
||||
|
||||
public fail(error: IServerError) {
|
||||
console.group("ExpressController FAIL RESPONSE ====================");
|
||||
console.log(error);
|
||||
console.trace("Show me");
|
||||
console.groupEnd();
|
||||
console.error("ExpressController FAIL RESPONSE:", error);
|
||||
|
||||
return this._errorResponse(
|
||||
httpStatus.INTERNAL_SERVER_ERROR,
|
||||
error ? error.toString() : "Fail",
|
||||
);
|
||||
return this._errorResponse(httpStatus.INTERNAL_SERVER_ERROR, error.message);
|
||||
}
|
||||
|
||||
public created<T>(dto?: T) {
|
||||
|
||||
@ -14,25 +14,12 @@ export const generateExpressErrorResponse = (
|
||||
message?: string,
|
||||
error?: Error | InfrastructureError,
|
||||
): Express.Response<IError_Response_DTO> => {
|
||||
const context = {};
|
||||
|
||||
if (Object.keys(res.locals).length) {
|
||||
if ("user" in res.locals) {
|
||||
context["user"] = res.locals.user;
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(req.params).length) {
|
||||
context["params"] = req.params;
|
||||
}
|
||||
|
||||
if (Object.keys(req.query).length) {
|
||||
context["query"] = req.query;
|
||||
}
|
||||
|
||||
if (Object.keys(req.body).length) {
|
||||
context["body"] = req.body;
|
||||
}
|
||||
const context = {
|
||||
user: res.locals.user || undefined,
|
||||
params: req.params || undefined,
|
||||
query: req.query || undefined,
|
||||
body: req.body || undefined,
|
||||
};
|
||||
|
||||
const extension = new ProblemDocumentExtension({
|
||||
context,
|
||||
@ -54,11 +41,11 @@ export const generateExpressErrorResponse = (
|
||||
function generateExpressError(
|
||||
error: Error | InfrastructureError,
|
||||
): IErrorExtra_Response_DTO {
|
||||
const useCaseError = <UseCaseError>error;
|
||||
const useCaseError = error as UseCaseError;
|
||||
|
||||
const payload = !Array.isArray(useCaseError.payload)
|
||||
? Array(useCaseError.payload)
|
||||
: useCaseError.payload;
|
||||
const payload = Array.isArray(useCaseError.payload)
|
||||
? useCaseError.payload
|
||||
: [useCaseError.payload];
|
||||
|
||||
const errors = payload.map((item) => {
|
||||
if (item.path) {
|
||||
|
||||
@ -2,54 +2,30 @@ import {
|
||||
IUseCase,
|
||||
IUseCaseError,
|
||||
UseCaseError,
|
||||
handleInvalidInputDataFailure,
|
||||
handleResourceAlreadyExitsFailure,
|
||||
handleUseCaseError,
|
||||
} from "@/contexts/common/application/useCases";
|
||||
} from "@/contexts/common/application";
|
||||
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||
import {
|
||||
AddressTitle,
|
||||
City,
|
||||
Collection,
|
||||
Country,
|
||||
DomainError,
|
||||
ICreateUser_DTO,
|
||||
Email,
|
||||
ICreateUser_Request_DTO,
|
||||
IDomainError,
|
||||
Note,
|
||||
PostalCode,
|
||||
Province,
|
||||
Name,
|
||||
Result,
|
||||
ResultCollection,
|
||||
Street,
|
||||
UniqueID,
|
||||
UserEmail,
|
||||
UserJobTitle,
|
||||
UserName,
|
||||
UserPhone,
|
||||
UserTIN,
|
||||
ensureUserIdIsValid,
|
||||
ensureUserTINIsValid,
|
||||
ensureIdIsValid,
|
||||
ensureUserEmailIsValid,
|
||||
} from "@shared/contexts";
|
||||
|
||||
import { IInfrastructureError } from "@/contexts/common/infrastructure";
|
||||
import {
|
||||
IBillingAddressUser_DTO,
|
||||
IShippingAddressUser_DTO,
|
||||
} from "@shared/contexts/users/application/dto/IUserAddressDTO";
|
||||
import {
|
||||
IUserRepository,
|
||||
User,
|
||||
UserBillingAddress as UserShippingAddress,
|
||||
} from "../domain";
|
||||
import { UserAddressType } from "../domain/entities/UserAddress";
|
||||
import { IUserRepository, User } from "../domain";
|
||||
import { existsUserByEmail, existsUserByID } from "./userServices";
|
||||
|
||||
export type CreateUserResponseOrError =
|
||||
| Result<never, IUseCaseError> // Misc errors (value objects)
|
||||
| Result<User, never>; // Success!
|
||||
|
||||
export class CreateUserUseCase
|
||||
implements IUseCase<ICreateUser_DTO, Promise<CreateUserResponseOrError>>
|
||||
implements
|
||||
IUseCase<ICreateUser_Request_DTO, Promise<CreateUserResponseOrError>>
|
||||
{
|
||||
private _adapter: ISequelizeAdapter;
|
||||
private _repositoryManager: IRepositoryManager;
|
||||
@ -66,43 +42,65 @@ export class CreateUserUseCase
|
||||
return this._repositoryManager.getRepository<T>(name);
|
||||
}
|
||||
|
||||
async execute(request: ICreateUser_DTO): Promise<CreateUserResponseOrError> {
|
||||
async execute(
|
||||
request: ICreateUser_Request_DTO,
|
||||
): Promise<CreateUserResponseOrError> {
|
||||
const userDTO = request;
|
||||
|
||||
const userRepository = this.getRepositoryByName<IUserRepository>("User");
|
||||
|
||||
// Validaciones de datos
|
||||
|
||||
const userIdOrError = ensureUserIdIsValid(userDTO.id);
|
||||
const userIdOrError = ensureIdIsValid(userDTO.id);
|
||||
if (userIdOrError.isFailure) {
|
||||
return handleInvalidInputDataFailure(
|
||||
"User ID is not valid",
|
||||
userIdOrError.error,
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.INVALID_INPUT_DATA,
|
||||
"User ID is not valid",
|
||||
userIdOrError.error,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const idExists = await this.findUserID(userIdOrError.object);
|
||||
const idExists = await existsUserByID(
|
||||
userIdOrError.object,
|
||||
this._adapter,
|
||||
userRepository,
|
||||
);
|
||||
if (idExists) {
|
||||
return handleResourceAlreadyExitsFailure(
|
||||
`Another user with ID ${userDTO.id} exists`,
|
||||
userIdOrError.error,
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.RESOURCE_ALREADY_EXITS,
|
||||
`Another user with ID ${userDTO.id} exists`,
|
||||
userIdOrError.error,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const tinOrError = ensureUserTINIsValid(userDTO.tin);
|
||||
if (tinOrError.isFailure) {
|
||||
return handleInvalidInputDataFailure(
|
||||
`User TIN is not valid: ${tinOrError.error.message}`,
|
||||
userIdOrError.error,
|
||||
const emailOrError = ensureUserEmailIsValid(userDTO.email);
|
||||
if (emailOrError.isFailure) {
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.INVALID_INPUT_DATA,
|
||||
"Email or password is not valid",
|
||||
emailOrError.error,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const tinExists =
|
||||
!tinOrError.object.isEmpty() &&
|
||||
(await this.findUserTIN(tinOrError.object));
|
||||
const emailExists = await existsUserByEmail(
|
||||
emailOrError.object,
|
||||
this._adapter,
|
||||
userRepository,
|
||||
);
|
||||
|
||||
if (tinExists) {
|
||||
return handleResourceAlreadyExitsFailure(
|
||||
`User with TIN ${tinOrError.object.toString()} exists`,
|
||||
userIdOrError.error,
|
||||
if (emailExists) {
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.RESOURCE_ALREADY_EXITS,
|
||||
`Another user with email ${userDTO.email} exists`,
|
||||
userIdOrError.error,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -142,10 +140,10 @@ export class CreateUserUseCase
|
||||
}
|
||||
}
|
||||
|
||||
return this.createUser(userOrError.object);
|
||||
return this.saveUser(userOrError.object);
|
||||
}
|
||||
|
||||
private async createUser(user: User) {
|
||||
private async saveUser(user: User) {
|
||||
// Guardar el contacto
|
||||
const transaction = this._adapter.startTransaction();
|
||||
const userRepoBuilder = this.getRepositoryByName<IUserRepository>("User");
|
||||
@ -170,197 +168,26 @@ export class CreateUserUseCase
|
||||
}
|
||||
}
|
||||
|
||||
private async findUserID(id: UniqueID) {
|
||||
const userRepoBuilder = this.getRepositoryByName<IUserRepository>("User");
|
||||
return await userRepoBuilder().exists(id);
|
||||
}
|
||||
|
||||
private async findUserTIN(tin: UserTIN) {
|
||||
const userRepoBuilder = this.getRepositoryByName<IUserRepository>("User");
|
||||
return await userRepoBuilder().existsWithSameTIN(tin);
|
||||
}
|
||||
|
||||
private tryCreateUserInstance(
|
||||
userDTO: ICreateUser_DTO,
|
||||
userDTO: ICreateUser_Request_DTO,
|
||||
userId: UniqueID,
|
||||
): Result<User, IDomainError> {
|
||||
const userTINOrError = ensureUserTINIsValid(userDTO.tin);
|
||||
if (userTINOrError.isFailure) {
|
||||
return Result.fail(userTINOrError.error);
|
||||
const nameOrError = Name.create(userDTO.name);
|
||||
if (nameOrError.isFailure) {
|
||||
return Result.fail(nameOrError.error);
|
||||
}
|
||||
|
||||
const companyNameOrError = UserName.create(userDTO.company_name);
|
||||
if (companyNameOrError.isFailure) {
|
||||
return Result.fail(companyNameOrError.error);
|
||||
}
|
||||
|
||||
const firstNameOrError = UserName.create(userDTO.first_name);
|
||||
if (firstNameOrError.isFailure) {
|
||||
return Result.fail(firstNameOrError.error);
|
||||
}
|
||||
|
||||
const lastNameOrError = UserName.create(userDTO.last_name);
|
||||
if (lastNameOrError.isFailure) {
|
||||
return Result.fail(lastNameOrError.error);
|
||||
}
|
||||
|
||||
const jobTitleOrError = UserJobTitle.create(userDTO.job_title);
|
||||
if (jobTitleOrError.isFailure) {
|
||||
return Result.fail(jobTitleOrError.error);
|
||||
}
|
||||
|
||||
const emailOrError = UserEmail.create(userDTO.email);
|
||||
const emailOrError = Email.create(userDTO.email);
|
||||
if (emailOrError.isFailure) {
|
||||
return Result.fail(emailOrError.error);
|
||||
}
|
||||
|
||||
const phoneOrError = UserPhone.create(userDTO.phone);
|
||||
if (phoneOrError.isFailure) {
|
||||
return Result.fail(phoneOrError.error);
|
||||
}
|
||||
|
||||
/*const taxIdOrError = UniqueID.create(userDTO.tax_id);
|
||||
if (taxIdOrError.isFailure) {
|
||||
return Result.fail(taxIdOrError.error);
|
||||
}*/
|
||||
|
||||
const notesOrError = Note.create(userDTO.notes);
|
||||
if (notesOrError.isFailure) {
|
||||
return Result.fail(notesOrError.error);
|
||||
}
|
||||
|
||||
// Billing address
|
||||
const billingAddressOrError = this.tryCreateUserAddress(
|
||||
userDTO.billing_address,
|
||||
"billing",
|
||||
);
|
||||
if (billingAddressOrError.isFailure) {
|
||||
return Result.fail(billingAddressOrError.error);
|
||||
}
|
||||
|
||||
// Shipping address
|
||||
const shippingAddressesOrError = this.tryCreateUserShippingAddresses(
|
||||
userDTO.shipping_addresses,
|
||||
);
|
||||
|
||||
if (shippingAddressesOrError.isFailure) {
|
||||
return Result.fail(shippingAddressesOrError.error);
|
||||
}
|
||||
|
||||
return User.create(
|
||||
{
|
||||
tin: userTINOrError.object,
|
||||
companyName: companyNameOrError.object,
|
||||
firstName: firstNameOrError.object,
|
||||
lastName: lastNameOrError.object,
|
||||
jobTitle: jobTitleOrError.object,
|
||||
name: nameOrError.object,
|
||||
email: emailOrError.object,
|
||||
phone: phoneOrError.object,
|
||||
//taxId: taxIdOrError.object,
|
||||
notes: notesOrError.object,
|
||||
|
||||
billingAddress: billingAddressOrError.object,
|
||||
shippingAddresses: shippingAddressesOrError.object,
|
||||
},
|
||||
userId,
|
||||
);
|
||||
}
|
||||
|
||||
private tryCreateUserShippingAddresses(
|
||||
addressesDTO: IShippingAddressUser_DTO[] | undefined,
|
||||
) {
|
||||
const shippingAddressesOrError = new ResultCollection<
|
||||
UserShippingAddress,
|
||||
DomainError
|
||||
>();
|
||||
|
||||
if (addressesDTO) {
|
||||
addressesDTO.map((value, index) => {
|
||||
const result = this.tryCreateUserAddress(value, "shipping");
|
||||
if (result.error) {
|
||||
const { path } = result.error.payload;
|
||||
const newPath = `shipping_addresses.${index}.${path}`;
|
||||
|
||||
result.error.payload.path = newPath;
|
||||
}
|
||||
shippingAddressesOrError.add(result);
|
||||
});
|
||||
}
|
||||
|
||||
if (shippingAddressesOrError.hasSomeFaultyResult()) {
|
||||
return Result.fail(shippingAddressesOrError.getFirstFaultyResult().error);
|
||||
}
|
||||
|
||||
return Result.ok(new Collection(shippingAddressesOrError.objects));
|
||||
}
|
||||
|
||||
private tryCreateUserAddress(
|
||||
addressDTO: IBillingAddressUser_DTO | IShippingAddressUser_DTO | undefined,
|
||||
addressType: UserAddressType,
|
||||
) {
|
||||
const titleOrError = AddressTitle.create(addressDTO?.title);
|
||||
if (titleOrError.isFailure) {
|
||||
return Result.fail(titleOrError.error);
|
||||
}
|
||||
|
||||
const streetOrError = Street.create(addressDTO?.street);
|
||||
if (streetOrError.isFailure) {
|
||||
return Result.fail(streetOrError.error);
|
||||
}
|
||||
|
||||
const postalCodeOrError = PostalCode.create(addressDTO?.postal_code);
|
||||
if (postalCodeOrError.isFailure) {
|
||||
return Result.fail(postalCodeOrError.error);
|
||||
}
|
||||
|
||||
const cityOrError = City.create(addressDTO?.city);
|
||||
if (cityOrError.isFailure) {
|
||||
return Result.fail(cityOrError.error);
|
||||
}
|
||||
|
||||
const provinceOrError = Province.create(addressDTO?.province);
|
||||
if (provinceOrError.isFailure) {
|
||||
return Result.fail(provinceOrError.error);
|
||||
}
|
||||
|
||||
const countryOrError = Country.create(addressDTO?.country);
|
||||
if (countryOrError.isFailure) {
|
||||
return Result.fail(countryOrError.error);
|
||||
}
|
||||
|
||||
const emailOrError = UserEmail.create(addressDTO?.email, {
|
||||
label: "email",
|
||||
path: "email",
|
||||
});
|
||||
|
||||
if (emailOrError.isFailure) {
|
||||
return Result.fail(emailOrError.error);
|
||||
}
|
||||
|
||||
const phoneOrError = UserPhone.create(addressDTO?.phone);
|
||||
if (phoneOrError.isFailure) {
|
||||
return Result.fail(phoneOrError.error);
|
||||
}
|
||||
|
||||
const notesOrError = Note.create(addressDTO?.notes);
|
||||
if (notesOrError.isFailure) {
|
||||
return Result.fail(notesOrError.error);
|
||||
}
|
||||
|
||||
const addressProps = {
|
||||
title: titleOrError.object,
|
||||
street: streetOrError.object,
|
||||
city: cityOrError.object,
|
||||
province: provinceOrError.object,
|
||||
postalCode: postalCodeOrError.object,
|
||||
country: countryOrError.object,
|
||||
email: emailOrError.object,
|
||||
phone: phoneOrError.object,
|
||||
notes: notesOrError.object,
|
||||
};
|
||||
|
||||
return addressType === "billing"
|
||||
? UserShippingAddress.create(addressProps)
|
||||
: UserShippingAddress.create(addressProps);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export * from "./CreateUser.useCase";
|
||||
export * from "./DeleteUser.useCase";
|
||||
export * from "./GetUser.useCase";
|
||||
export * from "./ListUsers.useCase";
|
||||
//export * from "./CreateUser.useCase";
|
||||
//export * from "./UpdateUser.useCase";
|
||||
export * from "./UpdateUser.useCase";
|
||||
|
||||
47
server/src/contexts/users/application/userServices.ts
Normal file
47
server/src/contexts/users/application/userServices.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { IAdapter, RepositoryBuilder } from "@/contexts/common/domain";
|
||||
import { Email, UniqueID } from "@shared/contexts";
|
||||
import { IUserRepository, User } from "../domain";
|
||||
|
||||
export const existsUserByID = async (
|
||||
id: UniqueID,
|
||||
adapter: IAdapter,
|
||||
repository: RepositoryBuilder<IUserRepository>,
|
||||
): Promise<boolean> => {
|
||||
return await adapter
|
||||
.startTransaction()
|
||||
.complete(async (t) => repository({ transaction: t }).existsUserWithId(id));
|
||||
};
|
||||
|
||||
export const findUserByID = async (
|
||||
id: UniqueID,
|
||||
adapter: IAdapter,
|
||||
repository: RepositoryBuilder<IUserRepository>,
|
||||
): Promise<User | null> => {
|
||||
return await adapter
|
||||
.startTransaction()
|
||||
.complete(async (t) => repository({ transaction: t }).getById(id));
|
||||
};
|
||||
|
||||
export const existsUserByEmail = async (
|
||||
email: Email,
|
||||
adapter: IAdapter,
|
||||
repository: RepositoryBuilder<IUserRepository>,
|
||||
): Promise<boolean> => {
|
||||
return await adapter
|
||||
.startTransaction()
|
||||
.complete(async (t) =>
|
||||
repository({ transaction: t }).existsUserWithEmail(email),
|
||||
);
|
||||
};
|
||||
|
||||
export const findUserByEmail = async (
|
||||
email: Email,
|
||||
adapter: IAdapter,
|
||||
repository: RepositoryBuilder<IUserRepository>,
|
||||
): Promise<User | null> => {
|
||||
return await adapter
|
||||
.startTransaction()
|
||||
.complete(async (t) =>
|
||||
repository({ transaction: t }).findUserByEmail(email),
|
||||
);
|
||||
};
|
||||
@ -1,12 +1,14 @@
|
||||
import { IRepository } from "@/contexts/common/domain";
|
||||
import { ICollection, IQueryCriteria, UniqueID } from "@shared/contexts";
|
||||
import { Email, ICollection, IQueryCriteria, UniqueID } from "@shared/contexts";
|
||||
import { User } from "../entities";
|
||||
|
||||
export interface IUserRepository extends IRepository<any> {
|
||||
getById(id: UniqueID): Promise<User | null>;
|
||||
findUserByEmail(email: Email): Promise<User | null>;
|
||||
findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<User>>;
|
||||
|
||||
removeById(id: UniqueID): Promise<void>;
|
||||
|
||||
exists(id: UniqueID): Promise<boolean>;
|
||||
existsUserWithId(id: UniqueID): Promise<boolean>;
|
||||
existsUserWithEmail(email: Email): Promise<boolean>;
|
||||
}
|
||||
|
||||
@ -72,9 +72,13 @@ export class UserRepository
|
||||
return this._removeById("User_Model", id);
|
||||
}
|
||||
|
||||
public async exists(id: UniqueID): Promise<boolean> {
|
||||
public async existsUserWithId(id: UniqueID): Promise<boolean> {
|
||||
return this._exists("User_Model", "id", id.toString());
|
||||
}
|
||||
|
||||
public async existsUserWithEmail(email: Email): Promise<boolean> {
|
||||
return this._exists("User_Model", "email", email.toString());
|
||||
}
|
||||
}
|
||||
|
||||
export const registerUserRepository = (context: IUserContext) => {
|
||||
|
||||
@ -0,0 +1,120 @@
|
||||
import { IUseCaseError, UseCaseError } from "@/contexts/common/application";
|
||||
import { IServerError } from "@/contexts/common/domain/errors";
|
||||
import {
|
||||
IInfrastructureError,
|
||||
InfrastructureError,
|
||||
handleInfrastructureError,
|
||||
} from "@/contexts/common/infrastructure";
|
||||
import { ExpressController } from "@/contexts/common/infrastructure/express";
|
||||
import {
|
||||
CreateUserResponseOrError,
|
||||
CreateUserUseCase,
|
||||
} from "@/contexts/users/application";
|
||||
import { User } from "@/contexts/users/domain";
|
||||
import {
|
||||
ICreateUser_Request_DTO,
|
||||
ICreateUser_Response_DTO,
|
||||
ensureCreateUser_Request_DTOIsValid,
|
||||
} from "@shared/contexts";
|
||||
import { IUserContext } from "../../../User.context";
|
||||
import { ICreateUserPresenter } from "./presenter/CreateUser.presenter";
|
||||
|
||||
export class CreateUserController extends ExpressController {
|
||||
private useCase: CreateUserUseCase;
|
||||
private presenter: ICreateUserPresenter;
|
||||
private context: IUserContext;
|
||||
|
||||
constructor(
|
||||
props: {
|
||||
useCase: CreateUserUseCase;
|
||||
presenter: ICreateUserPresenter;
|
||||
},
|
||||
context: IUserContext,
|
||||
) {
|
||||
super();
|
||||
|
||||
const { useCase, presenter } = props;
|
||||
this.useCase = useCase;
|
||||
this.presenter = presenter;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
async executeImpl(): Promise<any> {
|
||||
try {
|
||||
const userDTO: ICreateUser_Request_DTO = this.req.body;
|
||||
|
||||
// Validaciones de DTO
|
||||
const userDTOOrError = ensureCreateUser_Request_DTOIsValid(userDTO);
|
||||
|
||||
if (userDTOOrError.isFailure) {
|
||||
const errorMessage = "User data not valid";
|
||||
const infraError = handleInfrastructureError(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
userDTOOrError.error,
|
||||
);
|
||||
return this.invalidInputError(errorMessage, infraError);
|
||||
}
|
||||
|
||||
// Llamar al caso de uso
|
||||
const result: CreateUserResponseOrError = await this.useCase.execute(
|
||||
userDTO,
|
||||
);
|
||||
|
||||
if (result.isFailure) {
|
||||
return this._handleExecuteError(result.error);
|
||||
}
|
||||
|
||||
const user = <User>result.object;
|
||||
|
||||
return this.created<ICreateUser_Response_DTO>(
|
||||
this.presenter.map(user, this.context),
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
return this.fail(e as IServerError);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleExecuteError(error: IUseCaseError) {
|
||||
let errorMessage: string;
|
||||
let infraError: IInfrastructureError;
|
||||
|
||||
switch (error.code) {
|
||||
case UseCaseError.INVALID_INPUT_DATA:
|
||||
errorMessage = "User data not valid";
|
||||
infraError = handleInfrastructureError(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
error,
|
||||
);
|
||||
return this.invalidInputError(errorMessage, infraError);
|
||||
break;
|
||||
|
||||
case UseCaseError.RESOURCE_ALREADY_EXITS:
|
||||
errorMessage = "User already exists";
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
error,
|
||||
);
|
||||
return this.conflictError(error.message, error);
|
||||
break;
|
||||
|
||||
case UseCaseError.UNEXCEPTED_ERROR:
|
||||
errorMessage = error.message;
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
InfrastructureError.UNEXCEPTED_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
);
|
||||
return this.internalServerError(errorMessage, infraError);
|
||||
break;
|
||||
|
||||
default:
|
||||
errorMessage = error.message;
|
||||
return this.clientError(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
import { CreateUserUseCase } from "@/contexts/users/application";
|
||||
import { IUserContext } from "../../../User.context";
|
||||
import { registerUserRepository } from "../../../User.repository";
|
||||
import { CreateUserController } from "./CreateUser.controller";
|
||||
import { CreateUserPresenter } from "./presenter/CreateUser.presenter";
|
||||
|
||||
export const createCreateUserController = (context: IUserContext) => {
|
||||
registerUserRepository(context);
|
||||
|
||||
return new CreateUserController(
|
||||
{
|
||||
useCase: new CreateUserUseCase(context),
|
||||
presenter: CreateUserPresenter,
|
||||
},
|
||||
context,
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,17 @@
|
||||
import { User } from "@/contexts/users/domain";
|
||||
import { IUserContext } from "@/contexts/users/infrastructure/User.context";
|
||||
import { ICreateUser_Response_DTO } from "@shared/contexts";
|
||||
|
||||
export interface ICreateUserPresenter {
|
||||
map: (user: User, context: IUserContext) => ICreateUser_Response_DTO;
|
||||
}
|
||||
|
||||
export const CreateUserPresenter: ICreateUserPresenter = {
|
||||
map: (user: User, context: IUserContext): ICreateUser_Response_DTO => {
|
||||
return {
|
||||
id: user.id.toString(),
|
||||
name: user.name.toString(),
|
||||
email: user.email.toString(),
|
||||
};
|
||||
},
|
||||
};
|
||||
@ -0,0 +1 @@
|
||||
export * from "./CreateUser.presenter";
|
||||
@ -0,0 +1,91 @@
|
||||
import {
|
||||
IUseCaseError,
|
||||
UseCaseError,
|
||||
} from "@/contexts/common/application/useCases";
|
||||
import { IServerError } from "@/contexts/common/domain/errors";
|
||||
import {
|
||||
IInfrastructureError,
|
||||
InfrastructureError,
|
||||
handleInfrastructureError,
|
||||
} from "@/contexts/common/infrastructure";
|
||||
import { ExpressController } from "@/contexts/common/infrastructure/express";
|
||||
import { DeleteUserUseCase } from "@/contexts/users/application";
|
||||
import { ensureIdIsValid } from "@shared/contexts";
|
||||
import { IUserContext } from "../../../User.context";
|
||||
|
||||
export class DeleteUserController extends ExpressController {
|
||||
private useCase: DeleteUserUseCase;
|
||||
private context: IUserContext;
|
||||
|
||||
constructor(props: { useCase: DeleteUserUseCase }, context: IUserContext) {
|
||||
super();
|
||||
|
||||
const { useCase } = props;
|
||||
this.useCase = useCase;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
async executeImpl(): Promise<any> {
|
||||
try {
|
||||
const { userId } = this.req.params;
|
||||
|
||||
// Validar ID
|
||||
const userIdOrError = ensureIdIsValid(userId);
|
||||
if (userIdOrError.isFailure) {
|
||||
const errorMessage = "User ID is not valid";
|
||||
const infraError = handleInfrastructureError(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
userIdOrError.error,
|
||||
);
|
||||
return this.invalidInputError(errorMessage, infraError);
|
||||
}
|
||||
|
||||
// Llamar al caso de uso
|
||||
const result = await this.useCase.execute({
|
||||
id: userIdOrError.object,
|
||||
});
|
||||
|
||||
if (result.isFailure) {
|
||||
return this._handleExecuteError(result.error);
|
||||
}
|
||||
return this.noContent();
|
||||
} catch (e: unknown) {
|
||||
return this.fail(e as IServerError);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleExecuteError(error: IUseCaseError) {
|
||||
let errorMessage: string;
|
||||
let infraError: IInfrastructureError;
|
||||
|
||||
switch (error.code) {
|
||||
case UseCaseError.NOT_FOUND_ERROR:
|
||||
errorMessage = "User not found";
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
InfrastructureError.RESOURCE_NOT_FOUND_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
);
|
||||
|
||||
return this.notFoundError(errorMessage, infraError);
|
||||
break;
|
||||
|
||||
case UseCaseError.UNEXCEPTED_ERROR:
|
||||
errorMessage = error.message;
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
InfrastructureError.UNEXCEPTED_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
);
|
||||
return this.internalServerError(errorMessage, infraError);
|
||||
break;
|
||||
|
||||
default:
|
||||
errorMessage = error.message;
|
||||
return this.clientError(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
import { DeleteUserUseCase } from "@/contexts/users/application";
|
||||
import { IUserContext } from "../../../User.context";
|
||||
import { registerUserRepository } from "../../../User.repository";
|
||||
import { DeleteUserController } from "./DeleteUser.controller";
|
||||
|
||||
export const createDeleteUserController = (context: IUserContext) => {
|
||||
registerUserRepository(context);
|
||||
return new DeleteUserController(
|
||||
{
|
||||
useCase: new DeleteUserUseCase(context),
|
||||
},
|
||||
context,
|
||||
);
|
||||
};
|
||||
@ -5,7 +5,7 @@ import {
|
||||
import { ExpressController } from "@/contexts/common/infrastructure/express";
|
||||
import { GetUserUseCase } from "@/contexts/users/application";
|
||||
import { User } from "@/contexts/users/domain/entities/User";
|
||||
import { IGetUser_Response_DTO, ensureIdIsValid } from "@shared/contexts";
|
||||
import { IGetUserResponse_DTO, ensureIdIsValid } from "@shared/contexts";
|
||||
|
||||
import { IServerError } from "@/contexts/common/domain/errors";
|
||||
import {
|
||||
@ -62,7 +62,7 @@ export class GetUserController extends ExpressController {
|
||||
|
||||
const user = <User>result.object;
|
||||
|
||||
return this.ok<IGetUser_Response_DTO>(
|
||||
return this.ok<IGetUserResponse_DTO>(
|
||||
this.presenter.map(user, this.context),
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import { IGetUser_Response_DTO } from "@shared/contexts";
|
||||
import { IGetUserResponse_DTO } from "@shared/contexts";
|
||||
import { User } from "../../../../../domain";
|
||||
import { IUserContext } from "../../../../User.context";
|
||||
|
||||
export interface IGetUserPresenter {
|
||||
map: (user: User, context: IUserContext) => IGetUser_Response_DTO;
|
||||
map: (user: User, context: IUserContext) => IGetUserResponse_DTO;
|
||||
}
|
||||
|
||||
export const GetUserPresenter: IGetUserPresenter = {
|
||||
map: (user: User, context: IUserContext): IGetUser_Response_DTO => {
|
||||
map: (user: User, context: IUserContext): IGetUserResponse_DTO => {
|
||||
return {
|
||||
id: user.id.toString(),
|
||||
name: user.name.toString(),
|
||||
|
||||
@ -1,2 +1,5 @@
|
||||
export * from "./createUser";
|
||||
export * from "./deleteUser";
|
||||
export * from "./getUser";
|
||||
export * from "./listUsers";
|
||||
export * from "./updateUser";
|
||||
|
||||
@ -0,0 +1,137 @@
|
||||
import { IUseCaseError, UseCaseError } from "@/contexts/common/application";
|
||||
import { IServerError } from "@/contexts/common/domain/errors";
|
||||
import {
|
||||
IInfrastructureError,
|
||||
InfrastructureError,
|
||||
handleInfrastructureError,
|
||||
} from "@/contexts/common/infrastructure";
|
||||
import { ExpressController } from "@/contexts/common/infrastructure/express";
|
||||
import {
|
||||
UpdateUserResponseOrError,
|
||||
UpdateUserUseCase,
|
||||
} from "@/contexts/users/application";
|
||||
import { User } from "@/contexts/users/domain";
|
||||
import {
|
||||
IUpdateUser_Request_DTO,
|
||||
IUpdateUser_Response_DTO,
|
||||
ensureIdIsValid,
|
||||
ensureUpdateUser_Request_DTOIsValid,
|
||||
} from "@shared/contexts";
|
||||
import { IUserContext } from "../../../User.context";
|
||||
import { IUpdateUserPresenter } from "./presenter/UpdateUser.presenter";
|
||||
|
||||
export class UpdateUserController extends ExpressController {
|
||||
private useCase: UpdateUserUseCase;
|
||||
private presenter: IUpdateUserPresenter;
|
||||
private context: IUserContext;
|
||||
|
||||
constructor(
|
||||
props: {
|
||||
useCase: UpdateUserUseCase;
|
||||
presenter: IUpdateUserPresenter;
|
||||
},
|
||||
context: IUserContext,
|
||||
) {
|
||||
super();
|
||||
|
||||
const { useCase, presenter } = props;
|
||||
this.useCase = useCase;
|
||||
this.presenter = presenter;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
async executeImpl(): Promise<any> {
|
||||
try {
|
||||
const { userId } = this.req.params;
|
||||
const userDTO: IUpdateUser_Request_DTO = this.req.body;
|
||||
|
||||
// Validar ID
|
||||
const userIdOrError = ensureIdIsValid(userId);
|
||||
if (userIdOrError.isFailure) {
|
||||
const errorMessage = "User ID is not valid";
|
||||
const infraError = handleInfrastructureError(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
userIdOrError.error,
|
||||
);
|
||||
return this.invalidInputError(errorMessage, infraError);
|
||||
}
|
||||
|
||||
// Validar DTO de datos
|
||||
const userDTOOrError = ensureUpdateUser_Request_DTOIsValid(userDTO);
|
||||
|
||||
if (userDTOOrError.isFailure) {
|
||||
const errorMessage = "User data not valid";
|
||||
const infraError = handleInfrastructureError(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
userDTOOrError.error,
|
||||
);
|
||||
return this.invalidInputError(errorMessage, infraError);
|
||||
}
|
||||
|
||||
// Llamar al caso de uso
|
||||
const result: UpdateUserResponseOrError = await this.useCase.execute({
|
||||
id: userIdOrError.object,
|
||||
userDTO,
|
||||
});
|
||||
|
||||
if (result.isFailure) {
|
||||
return this.handleExecuteError(result.error);
|
||||
}
|
||||
|
||||
const user = <User>result.object;
|
||||
|
||||
return this.ok<IUpdateUser_Response_DTO>(
|
||||
this.presenter.map(user, this.context),
|
||||
);
|
||||
} catch (e: unknown) {
|
||||
return this.fail(e as IServerError);
|
||||
}
|
||||
}
|
||||
|
||||
private handleExecuteError(error: IUseCaseError) {
|
||||
let errorMessage: string;
|
||||
let infraError: IInfrastructureError;
|
||||
|
||||
switch (error.code) {
|
||||
case UseCaseError.NOT_FOUND_ERROR:
|
||||
errorMessage = "User not found";
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
InfrastructureError.RESOURCE_NOT_FOUND_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
);
|
||||
|
||||
return this.notFoundError(errorMessage, infraError);
|
||||
break;
|
||||
|
||||
case UseCaseError.INVALID_INPUT_DATA:
|
||||
errorMessage = "User data not valid";
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
"Datos del cliente a actulizar erróneos",
|
||||
error,
|
||||
);
|
||||
return this.invalidInputError(errorMessage, infraError);
|
||||
break;
|
||||
|
||||
case UseCaseError.UNEXCEPTED_ERROR:
|
||||
errorMessage = error.message;
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
InfrastructureError.UNEXCEPTED_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
);
|
||||
return this.internalServerError(errorMessage, infraError);
|
||||
break;
|
||||
|
||||
default:
|
||||
errorMessage = error.message;
|
||||
return this.clientError(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
import { UpdateUserUseCase } from "@/contexts/users/application";
|
||||
import { IUserContext } from "../../../User.context";
|
||||
import { registerUserRepository } from "../../../User.repository";
|
||||
import { UpdateUserController } from "./UpdateUser.controller";
|
||||
import { UpdateUserPresenter } from "./presenter/UpdateUser.presenter";
|
||||
|
||||
export const createUpdateUserController = (context: IUserContext) => {
|
||||
registerUserRepository(context);
|
||||
return new UpdateUserController(
|
||||
{
|
||||
useCase: new UpdateUserUseCase(context),
|
||||
presenter: UpdateUserPresenter,
|
||||
},
|
||||
context,
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,17 @@
|
||||
import { User } from "@/contexts/users/domain";
|
||||
import { IUserContext } from "@/contexts/users/infrastructure/User.context";
|
||||
import { IUpdateUser_Response_DTO } from "@shared/contexts";
|
||||
|
||||
export interface IUpdateUserPresenter {
|
||||
map: (user: User, context: IUserContext) => IUpdateUser_Response_DTO;
|
||||
}
|
||||
|
||||
export const UpdateUserPresenter: IUpdateUserPresenter = {
|
||||
map: (user: User, context: IUserContext): IUpdateUser_Response_DTO => {
|
||||
return {
|
||||
id: user.id.toString(),
|
||||
name: user.name.toString(),
|
||||
email: user.email.toString(),
|
||||
};
|
||||
},
|
||||
};
|
||||
@ -0,0 +1 @@
|
||||
export * from "./UpdateUser.presenter";
|
||||
@ -1,8 +1,11 @@
|
||||
import { applyMiddleware } from "@/contexts/common/infrastructure/express";
|
||||
import Express from "express";
|
||||
import {
|
||||
createCreateUserController,
|
||||
createDeleteUserController,
|
||||
createGetUserController,
|
||||
createListUsersController,
|
||||
createUpdateUserController,
|
||||
} from "./controllers";
|
||||
|
||||
export const UserRouter = (appRouter: Express.Router) => {
|
||||
@ -22,5 +25,26 @@ export const UserRouter = (appRouter: Express.Router) => {
|
||||
createGetUserController(res.locals["context"]).execute(req, res, next),
|
||||
);
|
||||
|
||||
userRoutes.post(
|
||||
"/",
|
||||
applyMiddleware("isAdminUser"),
|
||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||
createCreateUserController(res.locals["context"]).execute(req, res, next),
|
||||
);
|
||||
|
||||
userRoutes.put(
|
||||
"/:userId",
|
||||
applyMiddleware("isAdminUser"),
|
||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||
createUpdateUserController(res.locals["context"]).execute(req, res, next),
|
||||
);
|
||||
|
||||
userRoutes.delete(
|
||||
"/:userId",
|
||||
applyMiddleware("isAdminUser"),
|
||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||
createDeleteUserController(res.locals["context"]).execute(req, res, next),
|
||||
);
|
||||
|
||||
appRouter.use("/users", userRoutes);
|
||||
};
|
||||
|
||||
@ -1,10 +1 @@
|
||||
import { UndefinedOr } from "../../../utilities";
|
||||
import { Email, Result } from "../../common";
|
||||
|
||||
export const ensureUserEmailIsValid = (value: UndefinedOr<string>) => {
|
||||
const valueOrError = Email.create(value);
|
||||
|
||||
return valueOrError.isSuccess
|
||||
? Result.ok(valueOrError.object)
|
||||
: Result.fail(valueOrError.error);
|
||||
};
|
||||
export {};
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
import Joi from "joi";
|
||||
import { Result, RuleValidator } from "../../../../common";
|
||||
|
||||
export interface ICreateUser_Request_DTO {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export function ensureCreateUser_Request_DTOIsValid(
|
||||
userDTO: ICreateUser_Request_DTO,
|
||||
): Result<boolean, Error> {
|
||||
const schema = Joi.object({
|
||||
id: Joi.string(),
|
||||
name: Joi.string(),
|
||||
email: Joi.string(),
|
||||
}).unknown(true);
|
||||
|
||||
const result = RuleValidator.validate<ICreateUser_Request_DTO>(
|
||||
schema,
|
||||
userDTO,
|
||||
);
|
||||
|
||||
if (result.isFailure) {
|
||||
return Result.fail(result.error);
|
||||
}
|
||||
|
||||
return Result.ok(true);
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
export interface ICreateUser_Response_DTO {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
export * from "./ICreateUser_Request.dto";
|
||||
export * from "./ICreateUser_Response.dto";
|
||||
@ -1,5 +0,0 @@
|
||||
import { UniqueID } from "../../../../common";
|
||||
|
||||
export interface IGetUserRequest_DTO {
|
||||
id: UniqueID;
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
export interface IGetUser_Response_DTO {
|
||||
export interface IGetUserResponse_DTO {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
|
||||
@ -1,2 +1 @@
|
||||
export * from "./IGetUser.dto";
|
||||
export * from "./IGetUser_Response.dto";
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
import Joi from "joi";
|
||||
import { Result, RuleValidator } from "../../../../common";
|
||||
|
||||
export interface IUpdateUser_Request_DTO {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export function ensureUpdateUser_Request_DTOIsValid(
|
||||
userDTO: IUpdateUser_Request_DTO,
|
||||
): Result<boolean, Error> {
|
||||
const schema = Joi.object({
|
||||
id: Joi.string(),
|
||||
name: Joi.string(),
|
||||
email: Joi.string(),
|
||||
}).unknown(true);
|
||||
|
||||
const result = RuleValidator.validate<IUpdateUser_Request_DTO>(
|
||||
schema,
|
||||
userDTO,
|
||||
);
|
||||
|
||||
if (result.isFailure) {
|
||||
return Result.fail(result.error);
|
||||
}
|
||||
|
||||
return Result.ok(true);
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
export interface IUpdateUser_Response_DTO {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
}
|
||||
@ -0,0 +1,2 @@
|
||||
export * from "./IUpdateUser_Request.dto";
|
||||
export * from "./IUpdateUser_Response.dto";
|
||||
@ -1,2 +1,4 @@
|
||||
export * from "./CreateUser.dto";
|
||||
export * from "./GetUser.dto";
|
||||
export * from "./IListUsers.dto";
|
||||
export * from "./UpdateUser.dto";
|
||||
|
||||
Loading…
Reference in New Issue
Block a user