.
This commit is contained in:
parent
1685a44fc2
commit
7cd155f331
@ -1,10 +1,11 @@
|
||||
{
|
||||
"semi": true,
|
||||
"printWidth": 80,
|
||||
"printWidth": 130,
|
||||
"tabWidth": 4,
|
||||
"useTabs": false,
|
||||
"endOfLine": "auto",
|
||||
|
||||
"trailingComma": "all",
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"bracketSpacing": true
|
||||
"trailingComma": "all",
|
||||
"bracketSpacing": true,
|
||||
"jsxBracketSameLine": true,
|
||||
"arrowParens": "always"
|
||||
}
|
||||
|
||||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -1,5 +1,4 @@
|
||||
{
|
||||
//"typescript.surveys.enabled": false,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": "explicit",
|
||||
"source.fixAll.eslint": "explicit"
|
||||
|
||||
@ -4,8 +4,8 @@ module.exports = {
|
||||
"9d6c903873c341816995a8be0355c6f0d6d471fc6aedacf50790e9b1e49c45b3",
|
||||
refresh_secret_key:
|
||||
"3972dc40c69327b65352ed097419213b0b75561169dba562410b85660bb1f305",
|
||||
token_expiration: "5m",
|
||||
refresh_token_expiration: "7d",
|
||||
token_expiration: "7d",
|
||||
refresh_token_expiration: "30d",
|
||||
},
|
||||
|
||||
database: {
|
||||
|
||||
@ -3,7 +3,6 @@ import {
|
||||
IUseCaseError,
|
||||
IUseCaseRequest,
|
||||
UseCaseError,
|
||||
handleUseCaseError,
|
||||
} from "@/contexts/common/application";
|
||||
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||
import { IInfrastructureError } from "@/contexts/common/infrastructure";
|
||||
@ -49,7 +48,7 @@ export class FindUserByEmailUseCase
|
||||
const emailOrError = ensureEmailIsValid(email);
|
||||
if (emailOrError.isFailure) {
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.INVALID_INPUT_DATA,
|
||||
"Email or password is not valid",
|
||||
emailOrError.error,
|
||||
@ -66,7 +65,7 @@ export class FindUserByEmailUseCase
|
||||
|
||||
if (user === null) {
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.NOT_FOUND_ERROR,
|
||||
`User with email ${email} not found`,
|
||||
),
|
||||
@ -76,7 +75,7 @@ export class FindUserByEmailUseCase
|
||||
} catch (error: unknown) {
|
||||
const _error = error as IInfrastructureError;
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.REPOSITORY_ERROR,
|
||||
"Error al buscar el usuario",
|
||||
_error,
|
||||
|
||||
@ -2,12 +2,11 @@ import {
|
||||
IUseCase,
|
||||
IUseCaseError,
|
||||
UseCaseError,
|
||||
handleUseCaseError,
|
||||
} from "@/contexts/common/application";
|
||||
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||
import { IInfrastructureError } from "@/contexts/common/infrastructure";
|
||||
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||
import { ILogin_DTO, Result, ensureUserEmailIsValid } from "@shared/contexts";
|
||||
import { ILogin_DTO, Result, ensureEmailIsValid } from "@shared/contexts";
|
||||
import { AuthUser } from "../domain";
|
||||
import { findUserByEmail } from "./authServices";
|
||||
|
||||
@ -38,10 +37,10 @@ export class LoginUseCase
|
||||
|
||||
// Validaciones de datos
|
||||
|
||||
const emailOrError = ensureUserEmailIsValid(email);
|
||||
const emailOrError = ensureEmailIsValid(email);
|
||||
if (emailOrError.isFailure) {
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.INVALID_INPUT_DATA,
|
||||
"Email or password is not valid",
|
||||
emailOrError.error,
|
||||
@ -58,7 +57,7 @@ export class LoginUseCase
|
||||
);
|
||||
if (user === null || !user.verifyPassword(password)) {
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.INVALID_INPUT_DATA,
|
||||
"Email or password is not valid",
|
||||
),
|
||||
@ -68,7 +67,7 @@ export class LoginUseCase
|
||||
} catch (error: unknown) {
|
||||
const _error = error as IInfrastructureError;
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.REPOSITORY_ERROR,
|
||||
"Error al buscar el usuario",
|
||||
_error,
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import { config } from "@/config";
|
||||
import { AuthUser } from "@/contexts/auth/domain";
|
||||
import { IServerError } from "@/contexts/common/domain/errors";
|
||||
import {
|
||||
InfrastructureError,
|
||||
handleInfrastructureError,
|
||||
} from "@/contexts/common/infrastructure";
|
||||
import { InfrastructureError } from "@/contexts/common/infrastructure";
|
||||
import { ExpressController } from "@/contexts/common/infrastructure/express";
|
||||
import { ILogin_Response_DTO } from "@shared/contexts";
|
||||
import JWT from "jsonwebtoken";
|
||||
@ -34,7 +31,7 @@ export class LoginController extends ExpressController {
|
||||
|
||||
if (!user) {
|
||||
const errorMessage = "Unexpected missing user data";
|
||||
const infraError = handleInfrastructureError(
|
||||
const infraError = InfrastructureError.create(
|
||||
InfrastructureError.UNEXCEPTED_ERROR,
|
||||
errorMessage,
|
||||
);
|
||||
|
||||
@ -2,7 +2,7 @@ import {
|
||||
ISequelizeMapper,
|
||||
SequelizeMapper,
|
||||
} from "@/contexts/common/infrastructure";
|
||||
import { Email, Name, UniqueID } from "@shared/contexts";
|
||||
import { Email, Name, Password, UniqueID } from "@shared/contexts";
|
||||
import { AuthUser, IAuthUserProps } from "../../domain/entities";
|
||||
import { IAuthContext } from "../Auth.context";
|
||||
import {
|
||||
@ -29,7 +29,11 @@ class UserMapper
|
||||
const props: IAuthUserProps = {
|
||||
name: this.mapsValue(source, "name", Name.create),
|
||||
email: this.mapsValue(source, "email", Email.create),
|
||||
hashed_password: source.password,
|
||||
hashed_password: this.mapsValue(
|
||||
source,
|
||||
"password",
|
||||
Password.createFromHashedText,
|
||||
),
|
||||
};
|
||||
|
||||
const id = this.mapsValue(source, "id", UniqueID.create);
|
||||
|
||||
@ -2,7 +2,6 @@ import {
|
||||
IUseCase,
|
||||
IUseCaseError,
|
||||
UseCaseError,
|
||||
handleUseCaseError,
|
||||
} from "@/contexts/common/application/useCases";
|
||||
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||
import {
|
||||
@ -67,7 +66,7 @@ export class ListArticlesUseCase
|
||||
} catch (error: unknown) {
|
||||
const _error = error as IInfrastructureError;
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.REPOSITORY_ERROR,
|
||||
"Error al listar el catálogo",
|
||||
_error,
|
||||
|
||||
@ -2,6 +2,10 @@ import { IServerError, ServerError } from "../../domain/errors";
|
||||
|
||||
export interface IUseCaseError extends IServerError {}
|
||||
|
||||
export type UseCaseErrorDetails = {
|
||||
path?: string;
|
||||
} & Record<string, any>;
|
||||
|
||||
export class UseCaseError extends ServerError implements IUseCaseError {
|
||||
public static readonly INVALID_REQUEST_PARAM = "INVALID_REQUEST_PARAM";
|
||||
public static readonly INVALID_INPUT_DATA = "INVALID_INPUT_DATA";
|
||||
@ -13,16 +17,8 @@ export class UseCaseError extends ServerError implements IUseCaseError {
|
||||
public static create(
|
||||
code: string,
|
||||
message: string,
|
||||
details?: Record<string, any>,
|
||||
details?: UseCaseErrorDetails,
|
||||
): UseCaseError {
|
||||
return new UseCaseError(code, message, details);
|
||||
}
|
||||
}
|
||||
|
||||
export function handleUseCaseError(
|
||||
code: string,
|
||||
message: string,
|
||||
payload?: Record<string, any>,
|
||||
): IUseCaseError {
|
||||
return UseCaseError.create(code, message, payload);
|
||||
}
|
||||
|
||||
@ -18,42 +18,28 @@ export class InfrastructureError
|
||||
public static create(
|
||||
code: string,
|
||||
message: string,
|
||||
payload?: Record<string, any>,
|
||||
error?: UseCaseError | ValidationError,
|
||||
): InfrastructureError {
|
||||
let payload = {};
|
||||
|
||||
if (error) {
|
||||
if (error.name === "ValidationError") {
|
||||
//Joi error => error.details
|
||||
payload = (<ValidationError>error).details;
|
||||
} else {
|
||||
// UseCaseError
|
||||
const _error = <UseCaseError>error;
|
||||
const _payload = Array.isArray(_error.payload)
|
||||
? _error.payload
|
||||
: [_error.payload];
|
||||
|
||||
payload = _payload.map((item: Record<string, any>) => ({
|
||||
path: item.path,
|
||||
message: error.message,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
return new InfrastructureError(code, message, payload);
|
||||
}
|
||||
}
|
||||
|
||||
function _isJoiError(error: Error) {
|
||||
return error.name === "ValidationError";
|
||||
}
|
||||
|
||||
export function handleInfrastructureError(
|
||||
code: string,
|
||||
message: string,
|
||||
error?: Error, // UseCaseError | ValidationError
|
||||
): IInfrastructureError {
|
||||
let payload = {};
|
||||
|
||||
if (error) {
|
||||
if (_isJoiError(error)) {
|
||||
//Joi => error.details
|
||||
payload = (<ValidationError>error).details;
|
||||
} else {
|
||||
// UseCaseError
|
||||
/*const useCaseError = <UseCaseError>error;
|
||||
if (useCaseError.payload.path) {
|
||||
const errorItem = {};
|
||||
errorItem[`${useCaseError.payload.path}`] = useCaseError.message;
|
||||
payload = {+
|
||||
errors: [errorItem],
|
||||
};
|
||||
}*/
|
||||
payload = (<UseCaseError>error).payload;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(payload);
|
||||
|
||||
return InfrastructureError.create(code, message, payload);
|
||||
}
|
||||
|
||||
@ -51,7 +51,7 @@ function generateExpressError(
|
||||
if (item.path) {
|
||||
return item.path
|
||||
? {
|
||||
[String(item.path)]: useCaseError.message,
|
||||
[String(item.path)]: item.message || useCaseError.message,
|
||||
}
|
||||
: {};
|
||||
} else {
|
||||
|
||||
@ -33,4 +33,8 @@ function applyMiddleware(middlewares: string | Array<string>) {
|
||||
};
|
||||
}
|
||||
|
||||
export { applyMiddleware, registerMiddleware };
|
||||
function createMiddlewareMap() {
|
||||
return new Map<string, Express.RequestHandler>();
|
||||
}
|
||||
|
||||
export { applyMiddleware, createMiddlewareMap, registerMiddleware };
|
||||
|
||||
@ -73,7 +73,7 @@ export class SequelizeAdapter implements ISequelizeAdapter {
|
||||
return this._connection.sync(params);
|
||||
}
|
||||
|
||||
public getModel(modelName: string) {
|
||||
public getModel(modelName: string): any {
|
||||
if (this.hasModel(modelName)) {
|
||||
return this._models[modelName];
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { Model, Sequelize } from "sequelize";
|
||||
import { Model, ModelStatic, Sequelize } from "sequelize";
|
||||
|
||||
interface ISequelizeModel extends Model {}
|
||||
interface ISequelizeModel extends InstanceType<ModelStatic<Model>> {}
|
||||
|
||||
interface ISequelizeModels {
|
||||
[prop: string]: ISequelizeModel;
|
||||
}
|
||||
|
||||
interface ISequelizeModel extends Model {
|
||||
associate?: (connection: Sequelize, models?: ISequelizeModels) => void;
|
||||
hooks?: (connection: Sequelize) => void;
|
||||
|
||||
@ -86,10 +86,6 @@ export abstract class SequelizeRepository<T> implements IRepository<T> {
|
||||
queryCriteria,
|
||||
});
|
||||
|
||||
if (!_model) {
|
||||
throw new Error(`[SequelizeRepository] Model ${modelName} not found!`);
|
||||
}
|
||||
|
||||
const args = {
|
||||
...query,
|
||||
distinct: true,
|
||||
@ -110,7 +106,7 @@ export abstract class SequelizeRepository<T> implements IRepository<T> {
|
||||
value: any,
|
||||
params: any = {},
|
||||
): Promise<boolean> {
|
||||
const _model = this.adapter.getModel(modelName);
|
||||
const _model = this.adapter.getModel(modelName) as ModelDefined<any, any>;
|
||||
const where = {};
|
||||
where[field] = value;
|
||||
|
||||
|
||||
@ -2,19 +2,21 @@ import {
|
||||
IUseCase,
|
||||
IUseCaseError,
|
||||
UseCaseError,
|
||||
handleUseCaseError,
|
||||
} from "@/contexts/common/application";
|
||||
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||
import { IInfrastructureError } from "@/contexts/common/infrastructure";
|
||||
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||
import {
|
||||
DomainError,
|
||||
Email,
|
||||
ICreateUser_Request_DTO,
|
||||
IDomainError,
|
||||
Name,
|
||||
Password,
|
||||
Result,
|
||||
UniqueID,
|
||||
ensureEmailIsValid,
|
||||
ensureIdIsValid,
|
||||
ensureUserEmailIsValid,
|
||||
} from "@shared/contexts";
|
||||
import { IUserRepository, User } from "../domain";
|
||||
import { existsUserByEmail, existsUserByID } from "./userServices";
|
||||
@ -38,27 +40,20 @@ export class CreateUserUseCase
|
||||
this._repositoryManager = props.repositoryManager;
|
||||
}
|
||||
|
||||
private getRepositoryByName<T>(name: string) {
|
||||
return this._repositoryManager.getRepository<T>(name);
|
||||
}
|
||||
async execute(request: ICreateUser_Request_DTO) {
|
||||
const { id, email } = request;
|
||||
|
||||
async execute(
|
||||
request: ICreateUser_Request_DTO,
|
||||
): Promise<CreateUserResponseOrError> {
|
||||
const userDTO = request;
|
||||
|
||||
const userRepository = this.getRepositoryByName<IUserRepository>("User");
|
||||
const userRepository = this._getUserRepository();
|
||||
|
||||
// Validaciones de datos
|
||||
|
||||
const userIdOrError = ensureIdIsValid(userDTO.id);
|
||||
const userIdOrError = ensureIdIsValid(id);
|
||||
if (userIdOrError.isFailure) {
|
||||
const message = userIdOrError.error.message; //`User ID ${userDTO.id} is not valid`;
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.INVALID_INPUT_DATA,
|
||||
"User ID is not valid",
|
||||
userIdOrError.error,
|
||||
),
|
||||
UseCaseError.create(UseCaseError.INVALID_INPUT_DATA, message, [
|
||||
{ path: "id" },
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@ -68,19 +63,18 @@ export class CreateUserUseCase
|
||||
userRepository,
|
||||
);
|
||||
if (idExists) {
|
||||
const message = `Another user with ID ${id} exists`;
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.RESOURCE_ALREADY_EXITS,
|
||||
`Another user with ID ${userDTO.id} exists`,
|
||||
userIdOrError.error,
|
||||
),
|
||||
UseCaseError.create(UseCaseError.RESOURCE_ALREADY_EXITS, message, {
|
||||
path: "id",
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const emailOrError = ensureUserEmailIsValid(userDTO.email);
|
||||
const emailOrError = ensureEmailIsValid(email);
|
||||
if (emailOrError.isFailure) {
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.INVALID_INPUT_DATA,
|
||||
"Email or password is not valid",
|
||||
emailOrError.error,
|
||||
@ -96,17 +90,17 @@ export class CreateUserUseCase
|
||||
|
||||
if (emailExists) {
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.RESOURCE_ALREADY_EXITS,
|
||||
`Another user with email ${userDTO.email} exists`,
|
||||
userIdOrError.error,
|
||||
`Another user with email ${email} exists`,
|
||||
{ path: "email" },
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Crear user
|
||||
const userOrError = this.tryCreateUserInstance(
|
||||
userDTO,
|
||||
const userOrError = this._tryCreateUserInstance(
|
||||
request,
|
||||
userIdOrError.object,
|
||||
);
|
||||
|
||||
@ -116,42 +110,50 @@ export class CreateUserUseCase
|
||||
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,
|
||||
case User.ERROR_USER_WITHOUT_NAME:
|
||||
return Result.fail(
|
||||
UseCaseError.create(
|
||||
UseCaseError.INVALID_INPUT_DATA,
|
||||
"El usuario debe tener un nombre.",
|
||||
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,
|
||||
return Result.fail(
|
||||
UseCaseError.create(
|
||||
UseCaseError.INVALID_INPUT_DATA,
|
||||
"El usuario tiene algún dato erróneo.",
|
||||
domainError,
|
||||
),
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
errorCode = UseCaseError.UNEXCEPTED_ERROR;
|
||||
message = domainError.message;
|
||||
return handleUseCaseError(errorCode, message, domainError);
|
||||
return Result.fail(
|
||||
UseCaseError.create(errorCode, message, domainError),
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this.saveUser(userOrError.object);
|
||||
return this._saveUser(userOrError.object);
|
||||
}
|
||||
|
||||
private async saveUser(user: User) {
|
||||
private async _saveUser(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) => {
|
||||
userRepo = userRepoBuilder({ transaction: t });
|
||||
userRepo = userRepository({ transaction: t });
|
||||
await userRepo.create(user);
|
||||
});
|
||||
|
||||
@ -159,16 +161,12 @@ export class CreateUserUseCase
|
||||
} catch (error: unknown) {
|
||||
const _error = error as IInfrastructureError;
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.REPOSITORY_ERROR,
|
||||
"Error al guardar el usuario",
|
||||
_error,
|
||||
),
|
||||
UseCaseError.create(UseCaseError.REPOSITORY_ERROR, _error.message),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private tryCreateUserInstance(
|
||||
private _tryCreateUserInstance(
|
||||
userDTO: ICreateUser_Request_DTO,
|
||||
userId: UniqueID,
|
||||
): Result<User, IDomainError> {
|
||||
@ -182,12 +180,22 @@ export class CreateUserUseCase
|
||||
return Result.fail(emailOrError.error);
|
||||
}
|
||||
|
||||
const passwordOrError = Password.createFromPlainText(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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,6 @@ import {
|
||||
IUseCaseError,
|
||||
IUseCaseRequest,
|
||||
UseCaseError,
|
||||
handleUseCaseError,
|
||||
} from "@/contexts/common/application/useCases";
|
||||
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||
@ -55,7 +54,7 @@ export class DeleteUserUseCase
|
||||
} catch (error: unknown) {
|
||||
//const _error = error as IInfrastructureError;
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.REPOSITORY_ERROR,
|
||||
"Error al eliminar el usuario",
|
||||
),
|
||||
|
||||
@ -3,7 +3,6 @@ import {
|
||||
IUseCaseError,
|
||||
IUseCaseRequest,
|
||||
UseCaseError,
|
||||
handleUseCaseError,
|
||||
} from "@/contexts/common/application/useCases";
|
||||
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||
@ -64,7 +63,7 @@ export class GetUserUseCase
|
||||
|
||||
if (!user) {
|
||||
return Result.fail(
|
||||
handleUseCaseError(UseCaseError.NOT_FOUND_ERROR, "User not found"),
|
||||
UseCaseError.create(UseCaseError.NOT_FOUND_ERROR, "User not found"),
|
||||
);
|
||||
}
|
||||
|
||||
@ -72,7 +71,7 @@ export class GetUserUseCase
|
||||
} catch (error: unknown) {
|
||||
const _error = error as IInfrastructureError;
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.REPOSITORY_ERROR,
|
||||
"Error al consultar el usuario",
|
||||
_error,
|
||||
|
||||
@ -2,7 +2,6 @@ import {
|
||||
IUseCase,
|
||||
IUseCaseError,
|
||||
UseCaseError,
|
||||
handleUseCaseError,
|
||||
} from "@/contexts/common/application/useCases";
|
||||
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||
import {
|
||||
@ -65,7 +64,7 @@ export class ListUsersUseCase
|
||||
} catch (error: unknown) {
|
||||
const _error = error as IInfrastructureError;
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.REPOSITORY_ERROR,
|
||||
"Error al listar los usurios",
|
||||
_error,
|
||||
|
||||
@ -3,7 +3,6 @@ import {
|
||||
IUseCaseError,
|
||||
IUseCaseRequest,
|
||||
UseCaseError,
|
||||
handleUseCaseError,
|
||||
} from "@/contexts/common/application/useCases";
|
||||
import { IRepositoryManager } from "@/contexts/common/domain";
|
||||
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||
@ -15,6 +14,7 @@ import {
|
||||
DomainError,
|
||||
IDomainError,
|
||||
IUpdateUser_DTO,
|
||||
IUpdateUser_Request_DTO,
|
||||
Note,
|
||||
PostalCode,
|
||||
Province,
|
||||
@ -27,6 +27,7 @@ import {
|
||||
UserName,
|
||||
UserPhone,
|
||||
UserTIN,
|
||||
ensureIdIsValid,
|
||||
} from "@shared/contexts";
|
||||
|
||||
import { IInfrastructureError } from "@/contexts/common/infrastructure";
|
||||
@ -40,10 +41,11 @@ import {
|
||||
UserAddressType,
|
||||
UserShippingAddress,
|
||||
} from "../domain";
|
||||
import { existsUserByID } from "./userServices";
|
||||
|
||||
export interface IUpdateUserUseCaseRequest extends IUseCaseRequest {
|
||||
id: UniqueID;
|
||||
userDTO: IUpdateUser_DTO;
|
||||
userDTO: IUpdateUser_Request_DTO;
|
||||
}
|
||||
|
||||
export type UpdateUserResponseOrError =
|
||||
@ -73,13 +75,30 @@ export class UpdateUserUseCase
|
||||
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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Comprobar que existe el user
|
||||
const idExists = await this._findUserID(id);
|
||||
const idExists = await existsUserByID(
|
||||
userIdOrError.object,
|
||||
this._adapter,
|
||||
userRepository,
|
||||
);
|
||||
if (!idExists) {
|
||||
const message = `User with ID ${id.toString()} not found`;
|
||||
return Result.fail<IUseCaseError>(
|
||||
handleUseCaseError(UseCaseError.NOT_FOUND_ERROR, message, [
|
||||
UseCaseError.create(UseCaseError.NOT_FOUND_ERROR, message, [
|
||||
{ path: "id" },
|
||||
]),
|
||||
);
|
||||
@ -117,7 +136,7 @@ export class UpdateUserUseCase
|
||||
}
|
||||
|
||||
return Result.fail<IUseCaseError>(
|
||||
handleUseCaseError(errorCode, message, payload),
|
||||
UseCaseError.create(errorCode, message, payload),
|
||||
);
|
||||
}
|
||||
|
||||
@ -326,7 +345,7 @@ export class UpdateUserUseCase
|
||||
} catch (error: unknown) {
|
||||
const _error = error as IInfrastructureError;
|
||||
return Result.fail(
|
||||
handleUseCaseError(
|
||||
UseCaseError.create(
|
||||
UseCaseError.REPOSITORY_ERROR,
|
||||
"Error al guardar el usuario",
|
||||
_error,
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { CompositeSpecification } from "@/contexts/common/domain";
|
||||
|
||||
import { User } from "./User";
|
||||
|
||||
export class UserHasName extends CompositeSpecification<User> {
|
||||
public isSatisfiedBy(candidate: User): boolean {
|
||||
return !candidate.name.isEmpty();
|
||||
}
|
||||
}
|
||||
@ -5,22 +5,25 @@ import {
|
||||
Email,
|
||||
IDomainError,
|
||||
Name,
|
||||
Password,
|
||||
Result,
|
||||
UniqueID,
|
||||
handleDomainError,
|
||||
} from "@shared/contexts";
|
||||
import { UserHasName } from "./User.specifications";
|
||||
|
||||
export interface IUserProps {
|
||||
name: Name;
|
||||
email: Email;
|
||||
password?: string;
|
||||
hashed_password?: string;
|
||||
password: Password;
|
||||
}
|
||||
|
||||
//type ISecuredUserProps = Omit<IUserProps, "password">;
|
||||
|
||||
export interface IUser {
|
||||
id: UniqueID;
|
||||
name: Name;
|
||||
email: Email;
|
||||
hashed_password: string;
|
||||
isUser: boolean;
|
||||
isAdmin: boolean;
|
||||
|
||||
@ -28,33 +31,29 @@ export interface IUser {
|
||||
}
|
||||
|
||||
export class User extends AggregateRoot<IUserProps> implements IUser {
|
||||
static readonly ERROR_USER_WITHOUT_NAME = "ERROR_USER_WITHOUT_NAME";
|
||||
|
||||
public static create(
|
||||
props: IUserProps,
|
||||
id?: UniqueID,
|
||||
): Result<User, IDomainError> {
|
||||
//const isNew = !!id === false;
|
||||
|
||||
// Se hace en el constructor de la Entidad
|
||||
/* if (isNew) {
|
||||
id = UniqueEntityID.create();
|
||||
}*/
|
||||
|
||||
const user = new User(props, id);
|
||||
|
||||
// Reglas de negocio / validaciones
|
||||
const isValidUser = new UserHasName().isSatisfiedBy(user);
|
||||
|
||||
if (!isValidUser) {
|
||||
return Result.fail(handleDomainError(User.ERROR_USER_WITHOUT_NAME));
|
||||
}
|
||||
|
||||
return Result.ok<User>(user);
|
||||
}
|
||||
|
||||
public static async hashPassword(password): Promise<string> {
|
||||
return hashPassword(password, await genSalt());
|
||||
return Password.hashPassword(password);
|
||||
}
|
||||
|
||||
private _hashed_password: string;
|
||||
|
||||
private constructor(props: IUserProps, id?: UniqueID) {
|
||||
super({ ...props, password: "", hashed_password: "" }, id);
|
||||
|
||||
this._protectPassword(props);
|
||||
}
|
||||
private _password: string;
|
||||
|
||||
get name(): Name {
|
||||
return this.props.name;
|
||||
@ -65,7 +64,7 @@ export class User extends AggregateRoot<IUserProps> implements IUser {
|
||||
}
|
||||
|
||||
get hashed_password(): string {
|
||||
return this._hashed_password;
|
||||
return this._password;
|
||||
}
|
||||
|
||||
get isUser(): boolean {
|
||||
@ -77,17 +76,7 @@ export class User extends AggregateRoot<IUserProps> implements IUser {
|
||||
}
|
||||
|
||||
public verifyPassword(candidatePassword: string): boolean {
|
||||
return bCrypt.compareSync(candidatePassword, this._hashed_password!);
|
||||
}
|
||||
|
||||
private async _protectPassword(props: IUserProps) {
|
||||
const { password, hashed_password } = props;
|
||||
|
||||
if (password) {
|
||||
this._hashed_password = await User.hashPassword(password);
|
||||
} else {
|
||||
this._hashed_password = hashed_password!;
|
||||
}
|
||||
return bCrypt.compareSync(candidatePassword, this._password!);
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,5 +97,3 @@ async function hashPassword(password: string, salt: string): Promise<string> {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
User.hashPassword("123456").then((value) => console.log(value));
|
||||
|
||||
@ -3,6 +3,9 @@ import { Email, ICollection, IQueryCriteria, UniqueID } from "@shared/contexts";
|
||||
import { User } from "../entities";
|
||||
|
||||
export interface IUserRepository extends IRepository<any> {
|
||||
create(user: User): Promise<void>;
|
||||
update(user: User): Promise<void>;
|
||||
|
||||
getById(id: UniqueID): Promise<User | null>;
|
||||
findUserByEmail(email: Email): Promise<User | null>;
|
||||
findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<User>>;
|
||||
|
||||
@ -30,6 +30,20 @@ export class UserRepository
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
public async create(user: User): Promise<void> {
|
||||
const userData = this.mapper.mapToPersistence(user);
|
||||
await this._save("User_Model", user.id, userData);
|
||||
}
|
||||
|
||||
public async update(user: User): Promise<void> {
|
||||
const userData = this.mapper.mapToPersistence(user);
|
||||
|
||||
// borrando y luego creando
|
||||
await this.removeById(user.id, true);
|
||||
|
||||
await this._save("User_Model", user.id, userData, {});
|
||||
}
|
||||
|
||||
public async getById(id: UniqueID): Promise<User | null> {
|
||||
const rawUser: any = await this._getById("User_Model", id);
|
||||
|
||||
|
||||
@ -3,13 +3,9 @@ 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 { CreateUserUseCase } from "@/contexts/users/application";
|
||||
import { User } from "@/contexts/users/domain";
|
||||
import {
|
||||
ICreateUser_Request_DTO,
|
||||
@ -48,7 +44,7 @@ export class CreateUserController extends ExpressController {
|
||||
|
||||
if (userDTOOrError.isFailure) {
|
||||
const errorMessage = "User data not valid";
|
||||
const infraError = handleInfrastructureError(
|
||||
const infraError = InfrastructureError.create(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
userDTOOrError.error,
|
||||
@ -57,9 +53,7 @@ export class CreateUserController extends ExpressController {
|
||||
}
|
||||
|
||||
// Llamar al caso de uso
|
||||
const result: CreateUserResponseOrError = await this.useCase.execute(
|
||||
userDTO,
|
||||
);
|
||||
const result = await this.useCase.execute(userDTO);
|
||||
|
||||
if (result.isFailure) {
|
||||
return this._handleExecuteError(result.error);
|
||||
@ -82,7 +76,7 @@ export class CreateUserController extends ExpressController {
|
||||
switch (error.code) {
|
||||
case UseCaseError.INVALID_INPUT_DATA:
|
||||
errorMessage = "User data not valid";
|
||||
infraError = handleInfrastructureError(
|
||||
infraError = InfrastructureError.create(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
error,
|
||||
@ -93,18 +87,28 @@ export class CreateUserController extends ExpressController {
|
||||
case UseCaseError.RESOURCE_ALREADY_EXITS:
|
||||
errorMessage = "User already exists";
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
infraError = InfrastructureError.create(
|
||||
InfrastructureError.RESOURCE_ALREADY_REGISTERED,
|
||||
errorMessage,
|
||||
error,
|
||||
);
|
||||
return this.conflictError(error.message, error);
|
||||
return this.conflictError(errorMessage, infraError);
|
||||
break;
|
||||
|
||||
case UseCaseError.REPOSITORY_ERROR:
|
||||
errorMessage = "Error saving user";
|
||||
infraError = InfrastructureError.create(
|
||||
InfrastructureError.UNEXCEPTED_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
);
|
||||
return this.conflictError(errorMessage, infraError);
|
||||
break;
|
||||
|
||||
case UseCaseError.UNEXCEPTED_ERROR:
|
||||
errorMessage = error.message;
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
infraError = InfrastructureError.create(
|
||||
InfrastructureError.UNEXCEPTED_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
|
||||
@ -6,7 +6,6 @@ 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";
|
||||
@ -33,7 +32,7 @@ export class DeleteUserController extends ExpressController {
|
||||
const userIdOrError = ensureIdIsValid(userId);
|
||||
if (userIdOrError.isFailure) {
|
||||
const errorMessage = "User ID is not valid";
|
||||
const infraError = handleInfrastructureError(
|
||||
const infraError = InfrastructureError.create(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
userIdOrError.error,
|
||||
@ -63,7 +62,7 @@ export class DeleteUserController extends ExpressController {
|
||||
case UseCaseError.NOT_FOUND_ERROR:
|
||||
errorMessage = "User not found";
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
infraError = InfrastructureError.create(
|
||||
InfrastructureError.RESOURCE_NOT_FOUND_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
@ -75,7 +74,7 @@ export class DeleteUserController extends ExpressController {
|
||||
case UseCaseError.UNEXCEPTED_ERROR:
|
||||
errorMessage = error.message;
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
infraError = InfrastructureError.create(
|
||||
InfrastructureError.UNEXCEPTED_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
|
||||
@ -11,7 +11,6 @@ import { IServerError } from "@/contexts/common/domain/errors";
|
||||
import {
|
||||
IInfrastructureError,
|
||||
InfrastructureError,
|
||||
handleInfrastructureError,
|
||||
} from "@/contexts/common/infrastructure";
|
||||
import { IUserContext } from "../../../User.context";
|
||||
import { IGetUserPresenter } from "./presenter";
|
||||
@ -43,7 +42,7 @@ export class GetUserController extends ExpressController {
|
||||
const userIdOrError = ensureIdIsValid(userId);
|
||||
if (userIdOrError.isFailure) {
|
||||
const errorMessage = "User ID is not valid";
|
||||
const infraError = handleInfrastructureError(
|
||||
const infraError = InfrastructureError.create(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
userIdOrError.error,
|
||||
@ -78,7 +77,7 @@ export class GetUserController extends ExpressController {
|
||||
case UseCaseError.NOT_FOUND_ERROR:
|
||||
errorMessage = "User not found";
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
infraError = InfrastructureError.create(
|
||||
InfrastructureError.RESOURCE_NOT_FOUND_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
@ -90,7 +89,7 @@ export class GetUserController extends ExpressController {
|
||||
case UseCaseError.UNEXCEPTED_ERROR:
|
||||
errorMessage = error.message;
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
infraError = InfrastructureError.create(
|
||||
InfrastructureError.UNEXCEPTED_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
|
||||
@ -3,7 +3,6 @@ import { IServerError } from "@/contexts/common/domain/errors";
|
||||
import {
|
||||
IInfrastructureError,
|
||||
InfrastructureError,
|
||||
handleInfrastructureError,
|
||||
} from "@/contexts/common/infrastructure";
|
||||
import { ExpressController } from "@/contexts/common/infrastructure/express";
|
||||
import {
|
||||
@ -49,7 +48,7 @@ export class UpdateUserController extends ExpressController {
|
||||
const userIdOrError = ensureIdIsValid(userId);
|
||||
if (userIdOrError.isFailure) {
|
||||
const errorMessage = "User ID is not valid";
|
||||
const infraError = handleInfrastructureError(
|
||||
const infraError = InfrastructureError.create(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
userIdOrError.error,
|
||||
@ -62,7 +61,7 @@ export class UpdateUserController extends ExpressController {
|
||||
|
||||
if (userDTOOrError.isFailure) {
|
||||
const errorMessage = "User data not valid";
|
||||
const infraError = handleInfrastructureError(
|
||||
const infraError = InfrastructureError.create(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
errorMessage,
|
||||
userDTOOrError.error,
|
||||
@ -98,7 +97,7 @@ export class UpdateUserController extends ExpressController {
|
||||
case UseCaseError.NOT_FOUND_ERROR:
|
||||
errorMessage = "User not found";
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
infraError = InfrastructureError.create(
|
||||
InfrastructureError.RESOURCE_NOT_FOUND_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
@ -110,7 +109,7 @@ export class UpdateUserController extends ExpressController {
|
||||
case UseCaseError.INVALID_INPUT_DATA:
|
||||
errorMessage = "User data not valid";
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
infraError = InfrastructureError.create(
|
||||
InfrastructureError.INVALID_INPUT_DATA,
|
||||
"Datos del cliente a actulizar erróneos",
|
||||
error,
|
||||
@ -121,7 +120,7 @@ export class UpdateUserController extends ExpressController {
|
||||
case UseCaseError.UNEXCEPTED_ERROR:
|
||||
errorMessage = error.message;
|
||||
|
||||
infraError = handleInfrastructureError(
|
||||
infraError = InfrastructureError.create(
|
||||
InfrastructureError.UNEXCEPTED_ERROR,
|
||||
errorMessage,
|
||||
error,
|
||||
|
||||
@ -2,7 +2,7 @@ import {
|
||||
ISequelizeMapper,
|
||||
SequelizeMapper,
|
||||
} from "@/contexts/common/infrastructure";
|
||||
import { Email, Name, UniqueID } from "@shared/contexts";
|
||||
import { Email, Name, Password, UniqueID } from "@shared/contexts";
|
||||
import { IUserProps, User } from "../../domain";
|
||||
import { IUserContext } from "../User.context";
|
||||
import { TCreationUser_Attributes, User_Model } from "../sequelize/user.model";
|
||||
@ -22,7 +22,11 @@ class UserMapper
|
||||
const props: IUserProps = {
|
||||
name: this.mapsValue(source, "name", Name.create),
|
||||
email: this.mapsValue(source, "email", Email.create),
|
||||
hashed_password: source.password,
|
||||
password: this.mapsValue(
|
||||
source,
|
||||
"password",
|
||||
Password.createFromHashedText,
|
||||
),
|
||||
};
|
||||
|
||||
const id = this.mapsValue(source, "id", UniqueID.create);
|
||||
@ -43,7 +47,7 @@ class UserMapper
|
||||
id: source.id.toPrimitive(),
|
||||
name: source.name.toPrimitive(),
|
||||
email: source.email.toPrimitive(),
|
||||
password: source.hashed_password,
|
||||
password: "",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
import { RepositoryManager } from "@/contexts/common/domain";
|
||||
import { createSequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||
|
||||
export const createContextMiddleware = () => ({
|
||||
adapter: createSequelizeAdapter(),
|
||||
repositoryManager: RepositoryManager.getInstance(),
|
||||
services: {},
|
||||
});
|
||||
@ -1,9 +1,9 @@
|
||||
import { AuthRouter } from "@/contexts/auth";
|
||||
import { CatalogRouter } from "@/contexts/catalog";
|
||||
import { RepositoryManager } from "@/contexts/common/domain";
|
||||
import { createSequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
|
||||
import { createMiddlewareMap } from "@/contexts/common/infrastructure/express";
|
||||
import { UserRouter } from "@/contexts/users";
|
||||
import Express from "express";
|
||||
import { createContextMiddleware } from "./context.middleware";
|
||||
|
||||
export const v1Routes = () => {
|
||||
const routes = Express.Router({ mergeParams: true });
|
||||
@ -12,22 +12,14 @@ export const v1Routes = () => {
|
||||
res.send("Hello world!");
|
||||
});
|
||||
|
||||
//v1Routes.use("/auth", authRoutes);
|
||||
//v1Routes.use("/catalog", catalogRoutes);
|
||||
|
||||
routes.use(
|
||||
(
|
||||
req: Express.Request,
|
||||
res: Express.Response,
|
||||
next: Express.NextFunction,
|
||||
) => {
|
||||
res.locals["context"] = {
|
||||
adapter: createSequelizeAdapter(),
|
||||
repositoryManager: RepositoryManager.getInstance(),
|
||||
services: {},
|
||||
};
|
||||
|
||||
res.locals["middlewares"] = new Map<string, Express.RequestHandler>();
|
||||
res.locals["context"] = createContextMiddleware();
|
||||
res.locals["middlewares"] = createMiddlewareMap();
|
||||
|
||||
return next();
|
||||
},
|
||||
|
||||
110
shared/lib/contexts/common/domain/entities/Password.ts
Normal file
110
shared/lib/contexts/common/domain/entities/Password.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import bCrypt from "bcryptjs";
|
||||
|
||||
import Joi from "joi";
|
||||
import { UndefinedOr } from "../../../../utilities";
|
||||
import { RuleValidator } from "../RuleValidator";
|
||||
import { DomainError, handleDomainError } from "../errors";
|
||||
import { Result } from "./Result";
|
||||
import {
|
||||
IStringValueObjectOptions,
|
||||
StringValueObject,
|
||||
} from "./StringValueObject";
|
||||
|
||||
export interface IPasswordOptions extends IStringValueObjectOptions {}
|
||||
|
||||
export class Password extends StringValueObject {
|
||||
private static readonly MIN_LENGTH = 4;
|
||||
private static readonly MAX_LENGTH = 255;
|
||||
|
||||
protected static validate(
|
||||
value: UndefinedOr<string>,
|
||||
options: IPasswordOptions,
|
||||
) {
|
||||
const rule = Joi.string()
|
||||
.allow(null)
|
||||
.allow("")
|
||||
.default("")
|
||||
.trim()
|
||||
.min(Password.MIN_LENGTH)
|
||||
.max(Password.MAX_LENGTH)
|
||||
.label(options.label ? options.label : "value");
|
||||
|
||||
return RuleValidator.validate<string>(rule, value);
|
||||
}
|
||||
|
||||
public static createFromHashedText(
|
||||
value: UndefinedOr<string>,
|
||||
options: IPasswordOptions = {},
|
||||
) {
|
||||
const _options = {
|
||||
label: "password",
|
||||
...options,
|
||||
};
|
||||
|
||||
const validationResult = Password.validate(value, _options);
|
||||
|
||||
if (validationResult.isFailure) {
|
||||
return Result.fail(
|
||||
handleDomainError(
|
||||
DomainError.INVALID_INPUT_DATA,
|
||||
validationResult.error.message,
|
||||
_options,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Result.ok(new Password(validationResult.object));
|
||||
}
|
||||
|
||||
public static async createFromPlainText(
|
||||
value: UndefinedOr<string>,
|
||||
options: IPasswordOptions = {},
|
||||
) {
|
||||
const _options = {
|
||||
label: "password",
|
||||
...options,
|
||||
};
|
||||
|
||||
const validationResult = Password.validate(value, _options);
|
||||
|
||||
if (validationResult.isFailure) {
|
||||
return Result.fail(
|
||||
handleDomainError(
|
||||
DomainError.INVALID_INPUT_DATA,
|
||||
validationResult.error.message,
|
||||
_options,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Result.ok(
|
||||
new Password(await Password.hashPassword(validationResult.object)),
|
||||
);
|
||||
}
|
||||
|
||||
public static async hashPassword(plainText: string): Promise<string> {
|
||||
return hashPassword(plainText, await genSalt());
|
||||
}
|
||||
|
||||
public verifyPassword(candidatePassword: string): boolean {
|
||||
return bCrypt.compareSync(candidatePassword, this.value!);
|
||||
}
|
||||
}
|
||||
|
||||
async function genSalt(rounds = 10): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
bCrypt.genSalt(rounds, function (err, salt) {
|
||||
if (err) return reject(err);
|
||||
return resolve(salt);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function hashPassword(password: string, salt: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
bCrypt.hash(password, salt, function (err, hash) {
|
||||
if (err) return reject(err);
|
||||
return resolve(hash);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -11,6 +11,7 @@ export * from "./MoneyValue";
|
||||
export * from "./Name";
|
||||
export * from "./Note";
|
||||
export * from "./NullableValueObject";
|
||||
export * from "./Password";
|
||||
export * from "./Percentage";
|
||||
export * from "./Phone";
|
||||
export * from "./Quantity";
|
||||
|
||||
@ -5,15 +5,17 @@ export interface ICreateUser_Request_DTO {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
password: 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(),
|
||||
password: Joi.string(),
|
||||
}).unknown(true);
|
||||
|
||||
const result = RuleValidator.validate<ICreateUser_Request_DTO>(
|
||||
|
||||
Loading…
Reference in New Issue
Block a user