This commit is contained in:
David Arranz 2024-05-20 11:49:56 +02:00
parent 1f6de61a4b
commit 42320f2012
6 changed files with 80 additions and 290 deletions

View File

@ -121,35 +121,22 @@ export class CreateUserUseCase
switch (domainError.code) {
case User.ERROR_USER_WITHOUT_NAME:
return Result.fail(
UseCaseError.create(
UseCaseError.INVALID_INPUT_DATA,
"El usuario debe tener un nombre.",
domainError,
),
);
errorCode = UseCaseError.INVALID_INPUT_DATA;
message = "El usuario debe tener un nombre.";
break;
case DomainError.INVALID_INPUT_DATA:
errorCode = UseCaseError.INVALID_INPUT_DATA;
message = domainError.message;
return Result.fail(
UseCaseError.create(
UseCaseError.INVALID_INPUT_DATA,
"El usuario tiene algún dato erróneo.",
domainError,
),
);
message = "El usuario tiene algún dato erróneo.";
break;
default:
errorCode = UseCaseError.UNEXCEPTED_ERROR;
message = domainError.message;
return Result.fail(
UseCaseError.create(errorCode, message, domainError),
);
break;
}
return Result.fail(UseCaseError.create(errorCode, message, domainError));
}
return this._saveUser(userOrError.object);

View File

@ -3,45 +3,20 @@ import {
IUseCaseError,
IUseCaseRequest,
UseCaseError,
} from "@/contexts/common/application/useCases";
import { IRepositoryManager } from "@/contexts/common/domain";
} from "@/contexts/common/application";
import { IRepositoryManager, Password } from "@/contexts/common/domain";
import { IInfrastructureError } from "@/contexts/common/infrastructure";
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import {
AddressTitle,
City,
Collection,
Country,
DomainError,
Email,
IDomainError,
IUpdateUser_DTO,
IUpdateUser_Request_DTO,
Note,
PostalCode,
Province,
Name,
Result,
ResultCollection,
Street,
UniqueID,
UserEmail,
UserJobTitle,
UserName,
UserPhone,
UserTIN,
ensureIdIsValid,
} from "@shared/contexts";
import { IInfrastructureError } from "@/contexts/common/infrastructure";
import {
IBillingAddressUser_DTO,
IShippingAddressUser_DTO,
} from "@shared/contexts/users/application/dto/IUserAddressDTO";
import {
IUserRepository,
User,
UserAddressType,
UserShippingAddress,
} from "../domain";
import { existsUserByID } from "./userServices";
import { IUserRepository, User } from "../domain";
export interface IUpdateUserUseCaseRequest extends IUseCaseRequest {
id: UniqueID;
@ -67,277 +42,64 @@ export class UpdateUserUseCase
this._repositoryManager = props.repositoryManager;
}
private getRepositoryByName<T>(name: string) {
return this._repositoryManager.getRepository<T>(name);
}
async execute(
request: IUpdateUserUseCaseRequest,
): Promise<UpdateUserResponseOrError> {
const { id, userDTO } = request;
const userRepository = this.getRepositoryByName<IUserRepository>("User");
// Validaciones de datos
const userIdOrError = ensureIdIsValid(userDTO.id);
if (userIdOrError.isFailure) {
return Result.fail(
UseCaseError.create(
UseCaseError.INVALID_INPUT_DATA,
"User ID is not valid",
userIdOrError.error,
),
);
}
const userRepository = this._getUserRepository();
// Comprobar que existe el user
const idExists = await existsUserByID(
userIdOrError.object,
this._adapter,
userRepository,
);
const idExists = await userRepository().existsUserWithId(id);
if (!idExists) {
const message = `User with ID ${id.toString()} not found`;
return Result.fail<IUseCaseError>(
UseCaseError.create(UseCaseError.NOT_FOUND_ERROR, message, [
{ path: "id" },
]),
const message = `User ID not found`;
return Result.fail(
UseCaseError.create(UseCaseError.NOT_FOUND_ERROR, message, {
path: "id",
}),
);
}
// Crear user
// Crear usuario
const userOrError = this._tryCreateUserInstance(userDTO, id);
if (userOrError.isFailure) {
const { error: domainError } = userOrError;
let errorCode = "";
let message = "";
let payload = {};
switch (domainError.code) {
// Errores manuales
case User.ERROR_CUSTOMER_WITHOUT_NAME:
case User.ERROR_USER_WITHOUT_NAME:
errorCode = UseCaseError.INVALID_INPUT_DATA;
message = "El usuario debe ser una compañía o tener nombre.";
payload = [{ path: "first_name" }, { path: "company_name" }];
message = "El usuario debe tener un nombre.";
break;
// Value object error
case DomainError.INVALID_INPUT_DATA:
errorCode = UseCaseError.INVALID_INPUT_DATA;
message = domainError.message;
payload = domainError.payload;
message = "El usuario tiene algún dato erróneo.";
break;
default:
errorCode = UseCaseError.UNEXCEPTED_ERROR;
message = domainError.message;
payload = domainError.payload;
break;
}
return Result.fail<IUseCaseError>(
UseCaseError.create(errorCode, message, payload),
);
return Result.fail(UseCaseError.create(errorCode, message, domainError));
}
return this._updateUser(userOrError.object);
}
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);
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);
}
private _tryCreateUserShippingAddresses(
addressesDTO: IShippingAddressUser_DTO[] | undefined,
) {
const shippingAddressesOrError = new ResultCollection<
UserShippingAddress,
DomainError
>();
if (addressesDTO) {
addressesDTO.map((value) => {
const result = this._tryCreateUserAddress(value, "shipping");
shippingAddressesOrError.add(result);
});
}
if (shippingAddressesOrError.hasSomeFaultyResult()) {
return Result.fail(shippingAddressesOrError.getFirstFaultyResult().error);
}
return Result.ok(new Collection(shippingAddressesOrError.objects));
}
private _tryCreateUserInstance(
userDTO: IUpdateUser_DTO,
userId: UniqueID,
): Result<User, IDomainError> {
const userTINOrError = UserTIN.create(userDTO.tin, {
label: "tin",
path: "tin",
});
if (userTINOrError.isFailure) {
return Result.fail(userTINOrError.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);
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,
email: emailOrError.object,
phone: phoneOrError.object,
//taxId: taxIdOrError.object,
notes: notesOrError.object,
billingAddress: billingAddressOrError.object,
shippingAddresses: shippingAddressesOrError.object,
},
userId,
);
}
private async _findUserTIN(tin: UserTIN) {
const userRepoBuilder = this.getRepositoryByName<IUserRepository>("User");
return await userRepoBuilder().existsWithSameTIN(tin);
}
private async _findUserID(id: UniqueID) {
const userRepoBuilder = this.getRepositoryByName<IUserRepository>("User");
return await userRepoBuilder().exists(id);
}
private async _updateUser(user: User) {
// Guardar el contacto
const transaction = this._adapter.startTransaction();
const userRepoBuilder = this.getRepositoryByName<IUserRepository>("User");
const userRepository = this._getUserRepository();
let userRepo: IUserRepository;
try {
await transaction.complete(async (t) => {
const userRepo = userRepoBuilder({ transaction: t });
userRepo = userRepository({ transaction: t });
await userRepo.update(user);
});
@ -345,12 +107,43 @@ export class UpdateUserUseCase
} catch (error: unknown) {
const _error = error as IInfrastructureError;
return Result.fail(
UseCaseError.create(
UseCaseError.REPOSITORY_ERROR,
"Error al guardar el usuario",
_error,
),
UseCaseError.create(UseCaseError.REPOSITORY_ERROR, _error.message),
);
}
}
private _tryCreateUserInstance(
userDTO: IUpdateUser_Request_DTO,
userId: UniqueID,
): Result<User, IDomainError> {
const nameOrError = Name.create(userDTO.name);
if (nameOrError.isFailure) {
return Result.fail(nameOrError.error);
}
const emailOrError = Email.create(userDTO.email);
if (emailOrError.isFailure) {
return Result.fail(emailOrError.error);
}
const passwordOrError = Password.createFromPlainTextPassword(
userDTO.password,
);
if (passwordOrError.isFailure) {
return Result.fail(passwordOrError.error);
}
return User.create(
{
name: nameOrError.object,
email: emailOrError.object,
password: passwordOrError.object,
},
userId,
);
}
private _getUserRepository() {
return this._repositoryManager.getRepository<IUserRepository>("User");
}
}

View File

@ -45,7 +45,7 @@ export class UserRepository
const userData = this.mapper.mapToPersistence(user);
// borrando y luego creando
await this.removeById(user.id, true);
// await this.removeById(user.id, true);
await this._save("User_Model", user.id, userData, {});
}

View File

@ -35,7 +35,7 @@ export class CreateUserController extends ExpressController {
this.context = context;
}
async executeImpl(): Promise<any> {
async executeImpl() {
try {
const userDTO: ICreateUser_Request_DTO = this.req.body;

View File

@ -39,7 +39,7 @@ export class UpdateUserController extends ExpressController {
this.context = context;
}
async executeImpl(): Promise<any> {
async executeImpl() {
try {
const { userId } = this.req.params;
const userDTO: IUpdateUser_Request_DTO = this.req.body;
@ -76,7 +76,7 @@ export class UpdateUserController extends ExpressController {
});
if (result.isFailure) {
return this.handleExecuteError(result.error);
return this._handleExecuteError(result.error);
}
const user = <User>result.object;
@ -89,7 +89,7 @@ export class UpdateUserController extends ExpressController {
}
}
private handleExecuteError(error: IUseCaseError) {
private _handleExecuteError(error: IUseCaseError) {
let errorMessage: string;
let infraError: IInfrastructureError;
@ -117,6 +117,16 @@ export class UpdateUserController extends ExpressController {
return this.invalidInputError(errorMessage, infraError);
break;
case UseCaseError.REPOSITORY_ERROR:
errorMessage = "Error updating user";
infraError = InfrastructureError.create(
InfrastructureError.UNEXCEPTED_ERROR,
errorMessage,
error,
);
return this.conflictError(errorMessage, infraError);
break;
case UseCaseError.UNEXCEPTED_ERROR:
errorMessage = error.message;

View File

@ -2,18 +2,18 @@ import Joi from "joi";
import { Result, RuleValidator } from "../../../../common";
export interface IUpdateUser_Request_DTO {
id: string;
name: string;
email: string;
password: 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(),
password: Joi.string(),
}).unknown(true);
const result = RuleValidator.validate<IUpdateUser_Request_DTO>(