.
This commit is contained in:
parent
38ead39cb3
commit
923bd92220
@ -36,7 +36,7 @@ export const isLoggedUser = compose([
|
|||||||
}),
|
}),
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) => {
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) => {
|
||||||
const user = <AuthUser>req.user;
|
const user = <AuthUser>req.user;
|
||||||
if (user.isUser) {
|
if (!user.isUser) {
|
||||||
return generateExpressErrorResponse(req, res, httpStatus.UNAUTHORIZED);
|
return generateExpressErrorResponse(req, res, httpStatus.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { registerMiddleware } from "@/contexts/common/infrastructure/express";
|
|||||||
import Express from "express";
|
import Express from "express";
|
||||||
import passport from "passport";
|
import passport from "passport";
|
||||||
import { createLoginController } from "./controllers";
|
import { createLoginController } from "./controllers";
|
||||||
import { isLoggedUser } from "./passport";
|
import { isAdminUser, isLoggedUser } from "./passport";
|
||||||
|
|
||||||
/*authRoutes.post(
|
/*authRoutes.post(
|
||||||
"/login",
|
"/login",
|
||||||
@ -41,6 +41,7 @@ export const AuthRouter = (appRouter: Express.Router) => {
|
|||||||
const authRoutes: Express.Router = Express.Router({ mergeParams: true });
|
const authRoutes: Express.Router = Express.Router({ mergeParams: true });
|
||||||
|
|
||||||
appRouter.use(registerMiddleware("isLoggedUser", isLoggedUser));
|
appRouter.use(registerMiddleware("isLoggedUser", isLoggedUser));
|
||||||
|
appRouter.use(registerMiddleware("isAdminUser", isAdminUser));
|
||||||
|
|
||||||
authRoutes.post(
|
authRoutes.post(
|
||||||
"/login",
|
"/login",
|
||||||
|
|||||||
366
server/src/contexts/users/application/CreateUser.useCase.ts
Normal file
366
server/src/contexts/users/application/CreateUser.useCase.ts
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
import {
|
||||||
|
IUseCase,
|
||||||
|
IUseCaseError,
|
||||||
|
UseCaseError,
|
||||||
|
handleInvalidInputDataFailure,
|
||||||
|
handleResourceAlreadyExitsFailure,
|
||||||
|
handleUseCaseError,
|
||||||
|
} from "@/contexts/common/application/useCases";
|
||||||
|
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||||
|
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||||
|
import {
|
||||||
|
AddressTitle,
|
||||||
|
City,
|
||||||
|
Collection,
|
||||||
|
Country,
|
||||||
|
DomainError,
|
||||||
|
ICreateUser_DTO,
|
||||||
|
IDomainError,
|
||||||
|
Note,
|
||||||
|
PostalCode,
|
||||||
|
Province,
|
||||||
|
Result,
|
||||||
|
ResultCollection,
|
||||||
|
Street,
|
||||||
|
UniqueID,
|
||||||
|
UserEmail,
|
||||||
|
UserJobTitle,
|
||||||
|
UserName,
|
||||||
|
UserPhone,
|
||||||
|
UserTIN,
|
||||||
|
ensureUserIdIsValid,
|
||||||
|
ensureUserTINIsValid,
|
||||||
|
} 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";
|
||||||
|
|
||||||
|
export type CreateUserResponseOrError =
|
||||||
|
| Result<never, IUseCaseError> // Misc errors (value objects)
|
||||||
|
| Result<User, never>; // Success!
|
||||||
|
|
||||||
|
export class CreateUserUseCase
|
||||||
|
implements IUseCase<ICreateUser_DTO, Promise<CreateUserResponseOrError>>
|
||||||
|
{
|
||||||
|
private _adapter: ISequelizeAdapter;
|
||||||
|
private _repositoryManager: IRepositoryManager;
|
||||||
|
|
||||||
|
constructor(props: {
|
||||||
|
adapter: ISequelizeAdapter;
|
||||||
|
repositoryManager: IRepositoryManager;
|
||||||
|
}) {
|
||||||
|
this._adapter = props.adapter;
|
||||||
|
this._repositoryManager = props.repositoryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRepositoryByName<T>(name: string) {
|
||||||
|
return this._repositoryManager.getRepository<T>(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(request: ICreateUser_DTO): Promise<CreateUserResponseOrError> {
|
||||||
|
const userDTO = request;
|
||||||
|
|
||||||
|
// Validaciones de datos
|
||||||
|
|
||||||
|
const userIdOrError = ensureUserIdIsValid(userDTO.id);
|
||||||
|
if (userIdOrError.isFailure) {
|
||||||
|
return handleInvalidInputDataFailure(
|
||||||
|
"User ID is not valid",
|
||||||
|
userIdOrError.error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const idExists = await this.findUserID(userIdOrError.object);
|
||||||
|
if (idExists) {
|
||||||
|
return handleResourceAlreadyExitsFailure(
|
||||||
|
`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 tinExists =
|
||||||
|
!tinOrError.object.isEmpty() &&
|
||||||
|
(await this.findUserTIN(tinOrError.object));
|
||||||
|
|
||||||
|
if (tinExists) {
|
||||||
|
return handleResourceAlreadyExitsFailure(
|
||||||
|
`User with TIN ${tinOrError.object.toString()} exists`,
|
||||||
|
userIdOrError.error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear user
|
||||||
|
const userOrError = this.tryCreateUserInstance(
|
||||||
|
userDTO,
|
||||||
|
userIdOrError.object,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (userOrError.isFailure) {
|
||||||
|
const { error: domainError } = userOrError;
|
||||||
|
let errorCode = "";
|
||||||
|
let message = "";
|
||||||
|
|
||||||
|
switch (domainError.code) {
|
||||||
|
case User.ERROR_CUSTOMER_WITHOUT_NAME:
|
||||||
|
return handleInvalidInputDataFailure(
|
||||||
|
"El usuario debe ser una compañía o tener nombre y apellidos.",
|
||||||
|
domainError,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DomainError.INVALID_INPUT_DATA:
|
||||||
|
errorCode = UseCaseError.INVALID_INPUT_DATA;
|
||||||
|
message = domainError.message;
|
||||||
|
return handleInvalidInputDataFailure(
|
||||||
|
"El usuario debe ser una compañía o tener nombre y apellidos.",
|
||||||
|
domainError,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
errorCode = UseCaseError.UNEXCEPTED_ERROR;
|
||||||
|
message = domainError.message;
|
||||||
|
return handleUseCaseError(errorCode, message, domainError);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.createUser(userOrError.object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async createUser(user: User) {
|
||||||
|
// Guardar el contacto
|
||||||
|
const transaction = this._adapter.startTransaction();
|
||||||
|
const userRepoBuilder = this.getRepositoryByName<IUserRepository>("User");
|
||||||
|
let userRepo: IUserRepository;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await transaction.complete(async (t) => {
|
||||||
|
userRepo = userRepoBuilder({ transaction: t });
|
||||||
|
await userRepo.create(user);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Result.ok<User>(user);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const _error = error as IInfrastructureError;
|
||||||
|
return Result.fail(
|
||||||
|
handleUseCaseError(
|
||||||
|
UseCaseError.REPOSITORY_ERROR,
|
||||||
|
"Error al guardar el usuario",
|
||||||
|
_error,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
userId: UniqueID,
|
||||||
|
): Result<User, IDomainError> {
|
||||||
|
const userTINOrError = ensureUserTINIsValid(userDTO.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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
65
server/src/contexts/users/application/DeleteUser.useCase.ts
Normal file
65
server/src/contexts/users/application/DeleteUser.useCase.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import {
|
||||||
|
IUseCase,
|
||||||
|
IUseCaseError,
|
||||||
|
IUseCaseRequest,
|
||||||
|
UseCaseError,
|
||||||
|
handleUseCaseError,
|
||||||
|
} from "@/contexts/common/application/useCases";
|
||||||
|
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||||
|
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||||
|
import { Result, UniqueID } from "@shared/contexts";
|
||||||
|
import { IUserRepository } from "../domain";
|
||||||
|
|
||||||
|
export interface IDeleteUserUseCaseRequest extends IUseCaseRequest {
|
||||||
|
id: UniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type DeleteUserResponseOrError =
|
||||||
|
| Result<never, IUseCaseError> // Misc errors (value objects)
|
||||||
|
| Result<void, never>; // Success!
|
||||||
|
|
||||||
|
export class DeleteUserUseCase
|
||||||
|
implements
|
||||||
|
IUseCase<IDeleteUserUseCaseRequest, Promise<DeleteUserResponseOrError>>
|
||||||
|
{
|
||||||
|
private _adapter: ISequelizeAdapter;
|
||||||
|
private _repositoryManager: IRepositoryManager;
|
||||||
|
|
||||||
|
constructor(props: {
|
||||||
|
adapter: ISequelizeAdapter;
|
||||||
|
repositoryManager: IRepositoryManager;
|
||||||
|
}) {
|
||||||
|
this._adapter = props.adapter;
|
||||||
|
this._repositoryManager = props.repositoryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRepositoryByName<T>(name: string) {
|
||||||
|
return this._repositoryManager.getRepository<T>(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
request: IDeleteUserUseCaseRequest,
|
||||||
|
): Promise<DeleteUserResponseOrError> {
|
||||||
|
const { id: userId } = request;
|
||||||
|
|
||||||
|
const transaction = this._adapter.startTransaction();
|
||||||
|
const userRepoBuilder = this.getRepositoryByName<IUserRepository>("User");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await transaction.complete(async (t) => {
|
||||||
|
const invoiceRepo = userRepoBuilder({ transaction: t });
|
||||||
|
await invoiceRepo.removeById(userId);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Result.ok<void>();
|
||||||
|
} catch (error: unknown) {
|
||||||
|
//const _error = error as IInfrastructureError;
|
||||||
|
return Result.fail(
|
||||||
|
handleUseCaseError(
|
||||||
|
UseCaseError.REPOSITORY_ERROR,
|
||||||
|
"Error al eliminar el usuario",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
server/src/contexts/users/application/GetUser.useCase.ts
Normal file
83
server/src/contexts/users/application/GetUser.useCase.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import {
|
||||||
|
IUseCase,
|
||||||
|
IUseCaseError,
|
||||||
|
IUseCaseRequest,
|
||||||
|
UseCaseError,
|
||||||
|
handleUseCaseError,
|
||||||
|
} from "@/contexts/common/application/useCases";
|
||||||
|
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||||
|
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||||
|
import { Result, UniqueID } from "@shared/contexts";
|
||||||
|
import { IUserRepository } from "../domain";
|
||||||
|
|
||||||
|
import { IInfrastructureError } from "@/contexts/common/infrastructure";
|
||||||
|
import { User } from "../domain/entities/User";
|
||||||
|
|
||||||
|
export interface IGetUserUseCaseRequest extends IUseCaseRequest {
|
||||||
|
id: UniqueID;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GetUserResponseOrError =
|
||||||
|
| Result<never, IUseCaseError> // Misc errors (value objects)
|
||||||
|
| Result<User, never>; // Success!
|
||||||
|
|
||||||
|
export class GetUserUseCase
|
||||||
|
implements IUseCase<IGetUserUseCaseRequest, Promise<GetUserResponseOrError>>
|
||||||
|
{
|
||||||
|
private _adapter: ISequelizeAdapter;
|
||||||
|
private _repositoryManager: IRepositoryManager;
|
||||||
|
|
||||||
|
constructor(props: {
|
||||||
|
adapter: ISequelizeAdapter;
|
||||||
|
repositoryManager: IRepositoryManager;
|
||||||
|
}) {
|
||||||
|
this._adapter = props.adapter;
|
||||||
|
this._repositoryManager = props.repositoryManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getRepositoryByName<T>(name: string) {
|
||||||
|
return this._repositoryManager.getRepository<T>(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
async execute(
|
||||||
|
request: IGetUserUseCaseRequest,
|
||||||
|
): Promise<GetUserResponseOrError> {
|
||||||
|
const { id } = request;
|
||||||
|
|
||||||
|
// Validación de datos
|
||||||
|
// No hay en este caso
|
||||||
|
|
||||||
|
return await this.findUser(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async findUser(id: UniqueID) {
|
||||||
|
const transaction = this._adapter.startTransaction();
|
||||||
|
const userRepoBuilder = this.getRepositoryByName<IUserRepository>("User");
|
||||||
|
|
||||||
|
let user: User | null = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await transaction.complete(async (t) => {
|
||||||
|
const userRepo = userRepoBuilder({ transaction: t });
|
||||||
|
user = await userRepo.getById(id);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
return Result.fail(
|
||||||
|
handleUseCaseError(UseCaseError.NOT_FOUND_ERROR, "User not found"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok<User>(user!);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const _error = error as IInfrastructureError;
|
||||||
|
return Result.fail(
|
||||||
|
handleUseCaseError(
|
||||||
|
UseCaseError.REPOSITORY_ERROR,
|
||||||
|
"Error al consultar el usuario",
|
||||||
|
_error,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
337
server/src/contexts/users/application/UpdateUser.useCase.ts
Normal file
337
server/src/contexts/users/application/UpdateUser.useCase.ts
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
import {
|
||||||
|
IUseCase,
|
||||||
|
IUseCaseError,
|
||||||
|
IUseCaseRequest,
|
||||||
|
UseCaseError,
|
||||||
|
handleUseCaseError,
|
||||||
|
} from "@/contexts/common/application/useCases";
|
||||||
|
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||||
|
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||||
|
import {
|
||||||
|
AddressTitle,
|
||||||
|
City,
|
||||||
|
Collection,
|
||||||
|
Country,
|
||||||
|
DomainError,
|
||||||
|
IDomainError,
|
||||||
|
IUpdateUser_DTO,
|
||||||
|
Note,
|
||||||
|
PostalCode,
|
||||||
|
Province,
|
||||||
|
Result,
|
||||||
|
ResultCollection,
|
||||||
|
Street,
|
||||||
|
UniqueID,
|
||||||
|
UserEmail,
|
||||||
|
UserJobTitle,
|
||||||
|
UserName,
|
||||||
|
UserPhone,
|
||||||
|
UserTIN,
|
||||||
|
} 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";
|
||||||
|
|
||||||
|
export interface IUpdateUserUseCaseRequest extends IUseCaseRequest {
|
||||||
|
id: UniqueID;
|
||||||
|
userDTO: IUpdateUser_DTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UpdateUserResponseOrError =
|
||||||
|
| Result<never, IUseCaseError> // Misc errors (value objects)
|
||||||
|
| Result<User, never>; // Success!
|
||||||
|
|
||||||
|
export class UpdateUserUseCase
|
||||||
|
implements
|
||||||
|
IUseCase<IUpdateUserUseCaseRequest, Promise<UpdateUserResponseOrError>>
|
||||||
|
{
|
||||||
|
private _adapter: ISequelizeAdapter;
|
||||||
|
private _repositoryManager: IRepositoryManager;
|
||||||
|
|
||||||
|
constructor(props: {
|
||||||
|
adapter: ISequelizeAdapter;
|
||||||
|
repositoryManager: IRepositoryManager;
|
||||||
|
}) {
|
||||||
|
this._adapter = props.adapter;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Comprobar que existe el user
|
||||||
|
const idExists = await this._findUserID(id);
|
||||||
|
if (!idExists) {
|
||||||
|
const message = `User with ID ${id.toString()} not found`;
|
||||||
|
return Result.fail<IUseCaseError>(
|
||||||
|
handleUseCaseError(UseCaseError.NOT_FOUND_ERROR, message, [
|
||||||
|
{ path: "id" },
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Crear user
|
||||||
|
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:
|
||||||
|
errorCode = UseCaseError.INVALID_INPUT_DATA;
|
||||||
|
message = "El usuario debe ser una compañía o tener nombre.";
|
||||||
|
payload = [{ path: "first_name" }, { path: "company_name" }];
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Value object error
|
||||||
|
case DomainError.INVALID_INPUT_DATA:
|
||||||
|
errorCode = UseCaseError.INVALID_INPUT_DATA;
|
||||||
|
message = domainError.message;
|
||||||
|
payload = domainError.payload;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
errorCode = UseCaseError.UNEXCEPTED_ERROR;
|
||||||
|
message = domainError.message;
|
||||||
|
payload = domainError.payload;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.fail<IUseCaseError>(
|
||||||
|
handleUseCaseError(errorCode, message, payload),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await transaction.complete(async (t) => {
|
||||||
|
const userRepo = userRepoBuilder({ transaction: t });
|
||||||
|
await userRepo.update(user);
|
||||||
|
});
|
||||||
|
|
||||||
|
return Result.ok<User>(user);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const _error = error as IInfrastructureError;
|
||||||
|
return Result.fail(
|
||||||
|
handleUseCaseError(
|
||||||
|
UseCaseError.REPOSITORY_ERROR,
|
||||||
|
"Error al guardar el usuario",
|
||||||
|
_error,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1 +1,5 @@
|
|||||||
export * from "./ListUsersUseCase";
|
export * from "./DeleteUser.useCase";
|
||||||
|
export * from "./GetUser.useCase";
|
||||||
|
export * from "./ListUsers.useCase";
|
||||||
|
//export * from "./CreateUser.useCase";
|
||||||
|
//export * from "./UpdateUser.useCase";
|
||||||
|
|||||||
@ -5,4 +5,8 @@ import { User } from "../entities";
|
|||||||
export interface IUserRepository extends IRepository<any> {
|
export interface IUserRepository extends IRepository<any> {
|
||||||
getById(id: UniqueID): Promise<User | null>;
|
getById(id: UniqueID): Promise<User | null>;
|
||||||
findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<User>>;
|
findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<User>>;
|
||||||
|
|
||||||
|
removeById(id: UniqueID): Promise<void>;
|
||||||
|
|
||||||
|
exists(id: UniqueID): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,6 +67,14 @@ export class UserRepository
|
|||||||
|
|
||||||
return this.mapper.mapArrayAndCountToDomain(rows, count);
|
return this.mapper.mapArrayAndCountToDomain(rows, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async removeById(id: UniqueID, force: boolean = false): Promise<void> {
|
||||||
|
return this._removeById("User_Model", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async exists(id: UniqueID): Promise<boolean> {
|
||||||
|
return this._exists("User_Model", "id", id.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const registerUserRepository = (context: IUserContext) => {
|
export const registerUserRepository = (context: IUserContext) => {
|
||||||
|
|||||||
@ -0,0 +1,106 @@
|
|||||||
|
import {
|
||||||
|
IUseCaseError,
|
||||||
|
UseCaseError,
|
||||||
|
} from "@/contexts/common/application/useCases";
|
||||||
|
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 { IServerError } from "@/contexts/common/domain/errors";
|
||||||
|
import {
|
||||||
|
IInfrastructureError,
|
||||||
|
InfrastructureError,
|
||||||
|
handleInfrastructureError,
|
||||||
|
} from "@/contexts/common/infrastructure";
|
||||||
|
import { IUserContext } from "../../../User.context";
|
||||||
|
import { IGetUserPresenter } from "./presenter";
|
||||||
|
|
||||||
|
export class GetUserController extends ExpressController {
|
||||||
|
private useCase: GetUserUseCase;
|
||||||
|
private presenter: IGetUserPresenter;
|
||||||
|
private context: IUserContext;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
props: {
|
||||||
|
useCase: GetUserUseCase;
|
||||||
|
presenter: IGetUserPresenter;
|
||||||
|
},
|
||||||
|
context: IUserContext,
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
|
||||||
|
const { useCase, presenter } = props;
|
||||||
|
this.useCase = useCase;
|
||||||
|
this.presenter = presenter;
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeImpl(): Promise<any> {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await this.useCase.execute({
|
||||||
|
id: userIdOrError.object,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.isFailure) {
|
||||||
|
return this._handleExecuteError(result.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = <User>result.object;
|
||||||
|
|
||||||
|
return this.ok<IGetUser_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.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 { GetUserUseCase } from "@/contexts/users/application";
|
||||||
|
import { IUserContext } from "../../../User.context";
|
||||||
|
import { registerUserRepository } from "../../../User.repository";
|
||||||
|
import { GetUserController } from "./GetUser.controller";
|
||||||
|
import { GetUserPresenter } from "./presenter";
|
||||||
|
|
||||||
|
export const createGetUserController = (context: IUserContext) => {
|
||||||
|
registerUserRepository(context);
|
||||||
|
return new GetUserController(
|
||||||
|
{
|
||||||
|
useCase: new GetUserUseCase(context),
|
||||||
|
presenter: GetUserPresenter,
|
||||||
|
},
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
import { IGetUser_Response_DTO } from "@shared/contexts";
|
||||||
|
import { User } from "../../../../../domain";
|
||||||
|
import { IUserContext } from "../../../../User.context";
|
||||||
|
|
||||||
|
export interface IGetUserPresenter {
|
||||||
|
map: (user: User, context: IUserContext) => IGetUser_Response_DTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GetUserPresenter: IGetUserPresenter = {
|
||||||
|
map: (user: User, context: IUserContext): IGetUser_Response_DTO => {
|
||||||
|
return {
|
||||||
|
id: user.id.toString(),
|
||||||
|
name: user.name.toString(),
|
||||||
|
email: user.email.toString(),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1 @@
|
|||||||
|
export * from "./GetUser.presenter";
|
||||||
@ -1 +1,2 @@
|
|||||||
|
export * from "./getUser";
|
||||||
export * from "./listUsers";
|
export * from "./listUsers";
|
||||||
|
|||||||
@ -73,10 +73,10 @@ export class ListUsersController extends ExpressController {
|
|||||||
return this.clientError(result.error.message);
|
return this.clientError(result.error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const customers = <ICollection<User>>result.object;
|
const users = <ICollection<User>>result.object;
|
||||||
|
|
||||||
return this.ok<IListResponse_DTO<IListUsers_Response_DTO>>(
|
return this.ok<IListResponse_DTO<IListUsers_Response_DTO>>(
|
||||||
this.presenter.mapArray(customers, this.context, {
|
this.presenter.mapArray(users, this.context, {
|
||||||
page: queryCriteria.pagination.offset,
|
page: queryCriteria.pagination.offset,
|
||||||
limit: queryCriteria.pagination.limit,
|
limit: queryCriteria.pagination.limit,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
import { applyMiddleware } from "@/contexts/common/infrastructure/express";
|
import { applyMiddleware } from "@/contexts/common/infrastructure/express";
|
||||||
import Express from "express";
|
import Express from "express";
|
||||||
import { createListUsersController } from "./controllers";
|
import {
|
||||||
|
createGetUserController,
|
||||||
|
createListUsersController,
|
||||||
|
} from "./controllers";
|
||||||
|
|
||||||
export const UserRouter = (appRouter: Express.Router) => {
|
export const UserRouter = (appRouter: Express.Router) => {
|
||||||
const userRoutes: Express.Router = Express.Router({ mergeParams: true });
|
const userRoutes: Express.Router = Express.Router({ mergeParams: true });
|
||||||
@ -12,12 +15,12 @@ export const UserRouter = (appRouter: Express.Router) => {
|
|||||||
createListUsersController(res.locals["context"]).execute(req, res, next),
|
createListUsersController(res.locals["context"]).execute(req, res, next),
|
||||||
);
|
);
|
||||||
|
|
||||||
/*userRoutes.get(
|
userRoutes.get(
|
||||||
"/:id",
|
"/:userId",
|
||||||
applyMiddleware("isAdminUser"),
|
applyMiddleware("isAdminUser"),
|
||||||
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
(req: Express.Request, res: Express.Response, next: Express.NextFunction) =>
|
||||||
createGettUserController(res.locals["context"]).execute(req, res, next),
|
createGetUserController(res.locals["context"]).execute(req, res, next),
|
||||||
);*/
|
);
|
||||||
|
|
||||||
appRouter.use("/users", userRoutes);
|
appRouter.use("/users", userRoutes);
|
||||||
};
|
};
|
||||||
|
|||||||
42
shared/lib/contexts/common/application/Common.service.ts
Normal file
42
shared/lib/contexts/common/application/Common.service.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { Email, Name, Phone, Result, UniqueID } from "..";
|
||||||
|
import { UndefinedOr } from "../../../utilities";
|
||||||
|
|
||||||
|
export const ensureIdIsValid = (value: string) =>
|
||||||
|
UniqueID.create(value, { generateOnEmpty: false });
|
||||||
|
|
||||||
|
export const ensureNameIsValid = (
|
||||||
|
value: UndefinedOr<string>,
|
||||||
|
label: string = "name",
|
||||||
|
) => {
|
||||||
|
const valueOrError = Name.create(value, {
|
||||||
|
label,
|
||||||
|
});
|
||||||
|
|
||||||
|
return valueOrError.isSuccess
|
||||||
|
? Result.ok(valueOrError.object)
|
||||||
|
: Result.fail(valueOrError.error);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ensureEmailIsValid = (
|
||||||
|
value: UndefinedOr<string>,
|
||||||
|
label: string = "email",
|
||||||
|
) => {
|
||||||
|
const valueOrError = Email.create(value, {
|
||||||
|
label,
|
||||||
|
});
|
||||||
|
|
||||||
|
return valueOrError.isSuccess
|
||||||
|
? Result.ok(valueOrError.object)
|
||||||
|
: Result.fail(valueOrError.error);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ensurePhoneIsValid = (
|
||||||
|
value: UndefinedOr<string>,
|
||||||
|
label: string = "phone",
|
||||||
|
) => {
|
||||||
|
const valueOrError = Phone.create(value, { label });
|
||||||
|
|
||||||
|
return valueOrError.isSuccess
|
||||||
|
? Result.ok(valueOrError.object)
|
||||||
|
: Result.fail(valueOrError.error);
|
||||||
|
};
|
||||||
@ -1 +1,2 @@
|
|||||||
|
export * from "./Common.service";
|
||||||
export * from "./dto";
|
export * from "./dto";
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export interface IUniqueIDOptions extends INullableValueObjectOptions {
|
|||||||
export class UniqueID extends NullableValueObject<string> {
|
export class UniqueID extends NullableValueObject<string> {
|
||||||
protected static validate(
|
protected static validate(
|
||||||
value: UndefinedOr<string>,
|
value: UndefinedOr<string>,
|
||||||
options: IUniqueIDOptions
|
options: IUniqueIDOptions,
|
||||||
) {
|
) {
|
||||||
const ruleIsEmpty = RuleValidator.RULE_ALLOW_EMPTY.default("");
|
const ruleIsEmpty = RuleValidator.RULE_ALLOW_EMPTY.default("");
|
||||||
|
|
||||||
@ -43,6 +43,15 @@ export class UniqueID extends NullableValueObject<string> {
|
|||||||
...options,
|
...options,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!value && !_options.generateOnEmpty) {
|
||||||
|
return Result.fail(
|
||||||
|
handleDomainError(
|
||||||
|
DomainError.INVALID_INPUT_DATA,
|
||||||
|
"ID is null or empty",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
const validationResult = UniqueID.validate(value, _options);
|
const validationResult = UniqueID.validate(value, _options);
|
||||||
|
|
||||||
@ -51,13 +60,13 @@ export class UniqueID extends NullableValueObject<string> {
|
|||||||
handleDomainError(
|
handleDomainError(
|
||||||
DomainError.INVALID_INPUT_DATA,
|
DomainError.INVALID_INPUT_DATA,
|
||||||
validationResult.error.message,
|
validationResult.error.message,
|
||||||
_options
|
_options,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.ok<UniqueID>(
|
return Result.ok<UniqueID>(
|
||||||
new UniqueID(UniqueID.sanitize(validationResult.object))
|
new UniqueID(UniqueID.sanitize(validationResult.object)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
shared/lib/contexts/users/application/User.service.ts
Normal file
1
shared/lib/contexts/users/application/User.service.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export {};
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { UniqueID } from "../../../../common";
|
||||||
|
|
||||||
|
export interface IGetUserRequest_DTO {
|
||||||
|
id: UniqueID;
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
export interface IGetUser_Response_DTO {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./IGetUser.dto";
|
||||||
|
export * from "./IGetUser_Response.dto";
|
||||||
@ -1 +1,2 @@
|
|||||||
|
export * from "./GetUser.dto";
|
||||||
export * from "./IListUsers.dto";
|
export * from "./IListUsers.dto";
|
||||||
|
|||||||
@ -1 +1,2 @@
|
|||||||
|
export * from "./User.service";
|
||||||
export * from "./dto";
|
export * from "./dto";
|
||||||
|
|||||||
@ -8,6 +8,11 @@
|
|||||||
"resolveJsonModule": true
|
"resolveJsonModule": true
|
||||||
},
|
},
|
||||||
"references": [{ "path": "./shared/tsconfig.json" }],
|
"references": [{ "path": "./shared/tsconfig.json" }],
|
||||||
"include": ["server/**/*.ts", "client/**/*.ts", "shared/**/*.ts"],
|
"include": [
|
||||||
|
"server/**/*.ts",
|
||||||
|
"client/**/*.ts",
|
||||||
|
"shared/**/*.ts",
|
||||||
|
"server/src/contexts/users/application/CreateUser.useCase.ts"
|
||||||
|
],
|
||||||
"exclude": ["**/node_modules"]
|
"exclude": ["**/node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user