This commit is contained in:
David Arranz 2024-05-20 10:59:15 +02:00
parent e350e65f37
commit c040a247e7
5 changed files with 76 additions and 48 deletions

View File

@ -57,7 +57,7 @@ export class Password extends StringValueObject {
return Result.ok(new Password(validationResult.object)); return Result.ok(new Password(validationResult.object));
} }
public static async createFromPlainTextPassword( public static createFromPlainTextPassword(
value: UndefinedOr<string>, value: UndefinedOr<string>,
options: IPasswordOptions = {}, options: IPasswordOptions = {},
) { ) {
@ -79,7 +79,7 @@ export class Password extends StringValueObject {
} }
return Result.ok( return Result.ok(
new Password(await Password.hashPassword(validationResult.object)), new Password(Password.hashPassword(validationResult.object)),
); );
} }

View File

@ -126,14 +126,14 @@ export abstract class SequelizeRepository<T> implements IRepository<T> {
): Promise<void> { ): Promise<void> {
const _model = this.adapter.getModel(modelName); const _model = this.adapter.getModel(modelName);
if (await this._exists(modelName, "id", id.toString())) { if (await this._exists(modelName, "id", id.toPrimitive())) {
await _model.update( await _model.update(
{ {
...data, ...data,
id: undefined, id: undefined,
}, },
{ {
where: { id: id.toString() }, where: { id: id.toPrimitive() },
transaction: this.transaction, transaction: this.transaction,
...params, ...params,
}, },
@ -142,7 +142,7 @@ export abstract class SequelizeRepository<T> implements IRepository<T> {
await _model.create( await _model.create(
{ {
...data, ...data,
id: id.toString(), id: id.toPrimitive(),
}, },
{ {
include: [{ all: true }], include: [{ all: true }],
@ -163,7 +163,7 @@ export abstract class SequelizeRepository<T> implements IRepository<T> {
await model.destroy({ await model.destroy({
where: { where: {
id: id.toString(), id: id.toPrimitive(),
}, },
transaction: this.transaction, transaction: this.transaction,
force, force,
@ -192,7 +192,7 @@ export abstract class SequelizeRepository<T> implements IRepository<T> {
modelName: string, modelName: string,
ids: UniqueID[] | string[] ids: UniqueID[] | string[]
): Promise<boolean> { ): Promise<boolean> {
const _ids = ids.map((id: UniqueID | string) => id.toString()); const _ids = ids.map((id: UniqueID | string) => id.toPrimitive());
const destroyedRows: Promise<number> = await this.adapter.models[ const destroyedRows: Promise<number> = await this.adapter.models[
modelName modelName

View File

@ -3,7 +3,7 @@ import {
IUseCaseError, IUseCaseError,
UseCaseError, UseCaseError,
} from "@/contexts/common/application"; } from "@/contexts/common/application";
import { IRepositoryManager } from "@/contexts/common/domain"; import { IRepositoryManager, Password } from "@/contexts/common/domain";
import { IInfrastructureError } from "@/contexts/common/infrastructure"; import { IInfrastructureError } from "@/contexts/common/infrastructure";
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize"; import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import { import {
@ -12,14 +12,13 @@ import {
ICreateUser_Request_DTO, ICreateUser_Request_DTO,
IDomainError, IDomainError,
Name, Name,
Password,
Result, Result,
UniqueID, UniqueID,
ensureEmailIsValid, ensureEmailIsValid,
ensureIdIsValid, ensureIdIsValid,
ensureNameIsValid,
} from "@shared/contexts"; } from "@shared/contexts";
import { IUserRepository, User } from "../domain"; import { IUserRepository, User } from "../domain";
import { existsUserByEmail, existsUserByID } from "./userServices";
export type CreateUserResponseOrError = export type CreateUserResponseOrError =
| Result<never, IUseCaseError> // Misc errors (value objects) | Result<never, IUseCaseError> // Misc errors (value objects)
@ -41,15 +40,12 @@ export class CreateUserUseCase
} }
async execute(request: ICreateUser_Request_DTO) { async execute(request: ICreateUser_Request_DTO) {
const { id, email } = request; const { id, name, email } = request;
const userRepository = this._getUserRepository();
// Validaciones de datos // Validaciones de datos
const idOrError = ensureIdIsValid(id);
const userIdOrError = ensureIdIsValid(id); if (idOrError.isFailure) {
if (userIdOrError.isFailure) { const message = idOrError.error.message; //`User ID ${userDTO.id} is not valid`;
const message = userIdOrError.error.message; //`User ID ${userDTO.id} is not valid`;
return Result.fail( return Result.fail(
UseCaseError.create(UseCaseError.INVALID_INPUT_DATA, message, [ UseCaseError.create(UseCaseError.INVALID_INPUT_DATA, message, [
{ path: "id" }, { path: "id" },
@ -57,13 +53,32 @@ export class CreateUserUseCase
); );
} }
const idExists = await existsUserByID( const nameOrError = ensureNameIsValid(name);
userIdOrError.object, if (nameOrError.isFailure) {
this._adapter, const message = nameOrError.error.message; //`User ID ${userDTO.id} is not valid`;
userRepository, return Result.fail(
); UseCaseError.create(UseCaseError.INVALID_INPUT_DATA, message, [
{ path: "name" },
]),
);
}
const emailOrError = ensureEmailIsValid(email);
if (emailOrError.isFailure) {
const message = emailOrError.error.message;
return Result.fail(
UseCaseError.create(UseCaseError.INVALID_INPUT_DATA, message, [
{ path: "email" },
]),
);
}
// Comprobar que no existe un usuario previo con esos datos
const userRepository = this._getUserRepository();
const idExists = await userRepository().existsUserWithId(idOrError.object);
if (idExists) { if (idExists) {
const message = `Another user with ID ${id} exists`; const message = `Another user with same ID exists`;
return Result.fail( return Result.fail(
UseCaseError.create(UseCaseError.RESOURCE_ALREADY_EXITS, message, { UseCaseError.create(UseCaseError.RESOURCE_ALREADY_EXITS, message, {
path: "id", path: "id",
@ -71,38 +86,33 @@ export class CreateUserUseCase
); );
} }
const emailOrError = ensureEmailIsValid(email); const nameExists = await userRepository().existsUserWithName(
if (emailOrError.isFailure) { nameOrError.object,
);
if (nameExists) {
const message = `Another user with same name exists`;
return Result.fail( return Result.fail(
UseCaseError.create( UseCaseError.create(UseCaseError.RESOURCE_ALREADY_EXITS, message, {
UseCaseError.INVALID_INPUT_DATA, path: "name",
"Email or password is not valid", }),
emailOrError.error,
),
); );
} }
const emailExists = await existsUserByEmail( const emailExists = await userRepository().existsUserWithEmail(
emailOrError.object, emailOrError.object,
this._adapter,
userRepository,
); );
if (emailExists) { if (emailExists) {
const message = `Another user with same email exists`;
return Result.fail( return Result.fail(
UseCaseError.create( UseCaseError.create(UseCaseError.RESOURCE_ALREADY_EXITS, message, {
UseCaseError.RESOURCE_ALREADY_EXITS, path: "email",
`Another user with email ${email} exists`, }),
{ path: "email" },
),
); );
} }
// Crear user // Crear user
const userOrError = this._tryCreateUserInstance( const userOrError = this._tryCreateUserInstance(request, idOrError.object);
request,
userIdOrError.object,
);
if (userOrError.isFailure) { if (userOrError.isFailure) {
const { error: domainError } = userOrError; const { error: domainError } = userOrError;
@ -180,7 +190,9 @@ export class CreateUserUseCase
return Result.fail(emailOrError.error); return Result.fail(emailOrError.error);
} }
const passwordOrError = Password.createFromPlainText(userDTO.password); const passwordOrError = Password.createFromPlainTextPassword(
userDTO.password,
);
if (passwordOrError.isFailure) { if (passwordOrError.isFailure) {
return Result.fail(passwordOrError.error); return Result.fail(passwordOrError.error);
} }

View File

@ -1,5 +1,11 @@
import { IRepository } from "@/contexts/common/domain"; import { IRepository } from "@/contexts/common/domain";
import { Email, ICollection, IQueryCriteria, UniqueID } from "@shared/contexts"; import {
Email,
ICollection,
IQueryCriteria,
Name,
UniqueID,
} from "@shared/contexts";
import { User } from "../entities"; import { User } from "../entities";
export interface IUserRepository extends IRepository<any> { export interface IUserRepository extends IRepository<any> {
@ -13,5 +19,6 @@ export interface IUserRepository extends IRepository<any> {
removeById(id: UniqueID): Promise<void>; removeById(id: UniqueID): Promise<void>;
existsUserWithId(id: UniqueID): Promise<boolean>; existsUserWithId(id: UniqueID): Promise<boolean>;
existsUserWithName(name: Name): Promise<boolean>;
existsUserWithEmail(email: Email): Promise<boolean>; existsUserWithEmail(email: Email): Promise<boolean>;
} }

View File

@ -2,7 +2,13 @@ import {
ISequelizeAdapter, ISequelizeAdapter,
SequelizeRepository, SequelizeRepository,
} from "@/contexts/common/infrastructure/sequelize"; } from "@/contexts/common/infrastructure/sequelize";
import { Email, ICollection, IQueryCriteria, UniqueID } from "@shared/contexts"; import {
Email,
ICollection,
IQueryCriteria,
Name,
UniqueID,
} from "@shared/contexts";
import { Transaction } from "sequelize"; import { Transaction } from "sequelize";
import { User } from "../domain"; import { User } from "../domain";
import { IUserRepository } from "../domain/repository"; import { IUserRepository } from "../domain/repository";
@ -40,7 +46,6 @@ export class UserRepository
// borrando y luego creando // borrando y luego creando
await this.removeById(user.id, true); await this.removeById(user.id, true);
await this._save("User_Model", user.id, userData, {}); await this._save("User_Model", user.id, userData, {});
} }
@ -87,11 +92,15 @@ export class UserRepository
} }
public async existsUserWithId(id: UniqueID): Promise<boolean> { public async existsUserWithId(id: UniqueID): Promise<boolean> {
return this._exists("User_Model", "id", id.toString()); return this._exists("User_Model", "id", id.toPrimitive());
}
public async existsUserWithName(name: Name): Promise<boolean> {
return this._exists("User_Model", "name", name.toPrimitive());
} }
public async existsUserWithEmail(email: Email): Promise<boolean> { public async existsUserWithEmail(email: Email): Promise<boolean> {
return this._exists("User_Model", "email", email.toString()); return this._exists("User_Model", "email", email.toPrimitive());
} }
} }