This commit is contained in:
David Arranz 2024-04-24 12:48:49 +02:00
parent ccdf2efef3
commit af1eca6c30
48 changed files with 490 additions and 367 deletions

13
server/jest.config.js Normal file
View File

@ -0,0 +1,13 @@
module.exports = {
globals: {
"ts-jest": {
tsconfig: "tsconfig.json",
},
},
moduleFileExtensions: ["ts", "js"],
transform: {
"^.+\\.(ts|tsx)$": "ts-jest",
},
testMatch: ["**/*.test.(ts|js)"],
testEnvironment: "node",
};

View File

@ -64,7 +64,6 @@
"moment": "^2.29.4",
"morgan": "^1.10.0",
"mysql2": "^3.6.0",
"node-firebird": "^1.1.8",
"path": "^0.12.7",
"remove": "^0.1.5",
"response-time": "^2.3.2",

View File

@ -13,25 +13,25 @@ import {
} from "@shared/contexts";
import { IInfrastructureError } from "@/contexts/common/infrastructure";
import { IFirebirdAdapter } from "@/contexts/common/infrastructure/firebird";
import { ICatalogRepository, Product } from "../domain";
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import { Article, ICatalogRepository } from "../domain";
export interface IListProductsParams {
export interface IListArticlesParams {
queryCriteria: IQueryCriteria;
}
export type ListProductsResult =
export type ListArticlesResult =
| Result<never, IUseCaseError> // Misc errors (value objects)
| Result<ICollection<Product>, never>; // Success!
| Result<ICollection<Article>, never>; // Success!
export class ListProductsUseCase
implements IUseCase<IListProductsParams, Promise<ListProductsResult>>
export class ListArticlesUseCase
implements IUseCase<IListArticlesParams, Promise<ListArticlesResult>>
{
private _adapter: IFirebirdAdapter;
private _adapter: ISequelizeAdapter;
private _repositoryManager: IRepositoryManager;
constructor(props: {
adapter: IFirebirdAdapter;
adapter: ISequelizeAdapter;
repositoryManager: IRepositoryManager;
}) {
this._adapter = props.adapter;
@ -43,19 +43,19 @@ export class ListProductsUseCase
}
async execute(
params: Partial<IListProductsParams>
): Promise<ListProductsResult> {
params: Partial<IListArticlesParams>
): Promise<ListArticlesResult> {
const { queryCriteria } = params;
return this.findProducts(queryCriteria);
return this.findArticles(queryCriteria);
}
private async findProducts(queryCriteria) {
private async findArticles(queryCriteria) {
const transaction = this._adapter.startTransaction();
const productRepoBuilder =
this.getRepositoryByName<ICatalogRepository>("Product");
this.getRepositoryByName<ICatalogRepository>("Article");
let products: ICollection<Product> = new Collection();
let products: ICollection<Article> = new Collection();
try {
transaction.complete(async (t) => {

View File

@ -1 +1 @@
export * from "./ListProductsUseCase";
export * from "./ListArticlesUseCase";

View File

@ -0,0 +1,87 @@
import {
AggregateRoot,
Description,
IDomainError,
Quantity,
Result,
Slug,
UniqueID,
UnitPrice,
} from "@shared/contexts";
export interface IArticleProps {
catalog_name: Slug;
id_article: Description;
reference: Slug;
family: Description;
subfamily: Description;
description: Description;
points: Quantity;
retail_price: UnitPrice;
}
export interface IArticle {
id: UniqueID;
catalog_name: Slug;
id_article: Description;
reference: Slug;
family: Description;
subfamily: Description;
description: Description;
points: Quantity;
retail_price: UnitPrice;
}
export class Article extends AggregateRoot<IArticleProps> implements IArticle {
public static create(
props: IArticleProps,
id?: UniqueID
): Result<Article, IDomainError> {
//const isNew = !!id === false;
// Se hace en el constructor de la Entidad
/* if (isNew) {
id = UniqueEntityID.create();
}*/
const product = new Article(props, id);
return Result.ok<Article>(product);
}
private constructor(props: IArticleProps, id?: UniqueID) {
super(props, id);
}
get catalog_name(): Slug {
return this.props.catalog_name;
}
get id_article(): Description {
return this.props.id_article;
}
get reference(): Slug {
return this.props.reference;
}
get family(): Description {
return this.props.family;
}
get subfamily(): Description {
return this.props.subfamily;
}
get description(): Description {
return this.props.description;
}
get points(): Quantity {
return this.props.points;
}
get retail_price(): UnitPrice {
return this.props.retail_price;
}
}

View File

@ -1,75 +0,0 @@
import {
AggregateRoot,
Description,
IDomainError,
MoneyValueObject,
Result,
StringValueObject,
UniqueID,
ValueObject,
} from "@shared/contexts";
export interface IProductProps {
reference: StringValueObject;
family: StringValueObject;
subfamily: StringValueObject;
description: StringValueObject;
points: ValueObject<number>;
pvp: MoneyValueObject;
}
export interface IProduct {
id: UniqueID;
reference: Description;
family: Description;
subfamily: Description;
description: Description;
points: ValueObject<number>;
pvp: MoneyValueObject;
}
export class Product extends AggregateRoot<IProductProps> implements IProduct {
public static create(
props: IProductProps,
id?: UniqueID
): Result<Product, IDomainError> {
//const isNew = !!id === false;
// Se hace en el constructor de la Entidad
/* if (isNew) {
id = UniqueEntityID.create();
}*/
const product = new Product(props, id);
return Result.ok<Product>(product);
}
private constructor(props: IProductProps, id?: UniqueID) {
super(props, id);
}
get reference(): Description {
return this.props.reference;
}
get family(): Description {
return this.props.family;
}
get subfamily(): Description {
return this.props.subfamily;
}
get description(): Description {
return this.props.description;
}
get points(): ValueObject<number> {
return this.props.points;
}
get pvp(): MoneyValueObject {
return this.props.pvp;
}
}

View File

@ -1 +1 @@
export * from "./Product";
export * from "./Article";

View File

@ -1,7 +1,8 @@
import { IRepository } from "@/contexts/common/domain";
import { ICollection, IQueryCriteria } from "@shared/contexts";
import { Product } from "../entities";
import { ICollection, IQueryCriteria, UniqueID } from "@shared/contexts";
import { Article } from "../entities";
export interface ICatalogRepository extends IRepository<any> {
findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<Product>>;
getById(id: UniqueID): Promise<Article | null>;
findAll(queryCriteria?: IQueryCriteria): Promise<ICollection<Article>>;
}

View File

@ -1,13 +1,12 @@
import {
FirebirdRepository,
IFirebirdAdapter,
} from "@/contexts/common/infrastructure/firebird";
ISequelizeAdapter,
SequelizeRepository,
} from "@/contexts/common/infrastructure/sequelize";
import { ICollection, IQueryCriteria, UniqueID } from "@shared/contexts";
import Firebird from "node-firebird";
import { Product } from "../domain/entities";
import { Transaction } from "sequelize";
import { Article } from "../domain/entities";
import { ICatalogRepository } from "../domain/repository/CatalogRepository.interface";
import { Product_Model } from "./firebird";
import { IProductMapper } from "./mappers/product.mapper";
import { IArticleMapper } from "./mappers/article.mapper";
export type QueryParams = {
pagination: Record<string, any>;
@ -15,55 +14,42 @@ export type QueryParams = {
};
export class CatalogRepository
extends FirebirdRepository<Product>
extends SequelizeRepository<Article>
implements ICatalogRepository
{
protected mapper: IProductMapper;
protected mapper: IArticleMapper;
public constructor(props: {
mapper: IProductMapper;
adapter: IFirebirdAdapter;
transaction: Firebird.Transaction;
mapper: IArticleMapper;
adapter: ISequelizeAdapter;
transaction: Transaction;
}) {
const { adapter, mapper, transaction } = props;
super({ adapter, transaction });
this.mapper = mapper;
}
public async getById(id: UniqueID): Promise<Product | null> {
const rawProduct: Product_Model = await this.adapter.execute(
"SELECT * FROM ARTICULOS WHERE ID=?",
[id.toString()]
);
public async getById(id: UniqueID): Promise<Article | null> {
const rawArticle: any = await this._getById("Article_Model", id);
return this.mapper.mapToDomain(rawProduct);
if (!rawArticle === true) {
return null;
}
return this.mapper.mapToDomain(rawArticle);
}
/*public async count(): number {
const result = await this.adapter.execute<number>(
"SELECT count(*) FROM ARTICULOS",
[]
);
}*/
public async findAll(
queryCriteria?: IQueryCriteria
): Promise<ICollection<Product>> {
let rows: Product_Model[] = [];
const count = await this.adapter.execute<number[]>(
"SELECT count(*) FROM ARTICULOS",
[]
): Promise<ICollection<any>> {
const { rows, count } = await this._findAll(
"Article_Model",
queryCriteria
/*{
include: [], // esto es para quitar las asociaciones al hacer la consulta
}*/
);
if (count[0]) {
rows = await this.adapter.execute<Product_Model[]>(
"SELECT * FROM ARTICULOS",
[]
);
}
return this.mapper.mapArrayAndCountToDomain(rows, count[0]);
return this.mapper.mapArrayAndCountToDomain(rows, count);
}
}

View File

@ -1,8 +1,8 @@
import express, { NextFunction, Request, Response, Router } from "express";
import { RepositoryManager } from "@/contexts/common/domain";
import { createFirebirdAdapter } from "@/contexts/common/infrastructure/firebird";
import { createListProductsController } from "./controllers/listProducts";
import { createSequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import { createListArticlesController } from "./controllers";
const catalogRouter: Router = express.Router({ mergeParams: true });
@ -17,7 +17,7 @@ catalogRouter.use(logMiddleware);
const contextMiddleware = (req: Request, res: Response, next: NextFunction) => {
res.locals["context"] = {
adapter: createFirebirdAdapter(),
adapter: createSequelizeAdapter(),
repositoryManager: RepositoryManager.getInstance(),
services: {},
};
@ -28,7 +28,7 @@ const contextMiddleware = (req: Request, res: Response, next: NextFunction) => {
catalogRouter.use(contextMiddleware);
catalogRouter.get("/", (req: Request, res: Response, next: NextFunction) =>
createListProductsController(res.locals["context"]).execute(req, res, next)
createListArticlesController(res.locals["context"]).execute(req, res, next)
);
/*catalogRouter.get(

View File

@ -1 +1 @@
export * from "./listProducts";
export * from "./listArticles";

View File

@ -1,33 +1,33 @@
import Joi from "joi";
import {
ListProductsResult,
ListProductsUseCase,
ListArticlesResult,
ListArticlesUseCase,
} from "@/contexts/catalog/application";
import { Product } from "@/contexts/catalog/domain";
import { Article } from "@/contexts/catalog/domain";
import { QueryCriteriaService } from "@/contexts/common/application/services";
import { IServerError } from "@/contexts/common/domain/errors";
import { ExpressController } from "@/contexts/common/infrastructure/express";
import {
ICollection,
IListProducts_Response_DTO,
IListArticles_Response_DTO,
IListResponse_DTO,
IQueryCriteria,
Result,
RuleValidator,
} from "@shared/contexts";
import { ICatalogContext } from "../../..";
import { IListProductsPresenter } from "./presenter";
import { IListArticlesPresenter } from "./presenter";
export class ListProductsController extends ExpressController {
private useCase: ListProductsUseCase;
private presenter: IListProductsPresenter;
export class ListArticlesController extends ExpressController {
private useCase: ListArticlesUseCase;
private presenter: IListArticlesPresenter;
private context: ICatalogContext;
constructor(
props: {
useCase: ListProductsUseCase;
presenter: IListProductsPresenter;
useCase: ListArticlesUseCase;
presenter: IListArticlesPresenter;
},
context: ICatalogContext
) {
@ -65,7 +65,7 @@ export class ListProductsController extends ExpressController {
console.log(queryCriteria);
const result: ListProductsResult = await this.useCase.execute({
const result: ListArticlesResult = await this.useCase.execute({
queryCriteria,
});
@ -73,9 +73,9 @@ export class ListProductsController extends ExpressController {
return this.clientError(result.error.message);
}
const customers = <ICollection<Product>>result.object;
const customers = <ICollection<Article>>result.object;
return this.ok<IListResponse_DTO<IListProducts_Response_DTO>>(
return this.ok<IListResponse_DTO<IListArticles_Response_DTO>>(
this.presenter.mapArray(customers, this.context, {
page: queryCriteria.pagination.offset,
limit: queryCriteria.pagination.limit,

View File

@ -0,0 +1,34 @@
import { ListArticlesUseCase } from "@/contexts/catalog/application";
import { ICatalogContext } from "../../..";
import { CatalogRepository } from "../../../Catalog.repository";
import { createArticleMapper } from "../../../mappers/article.mapper";
import { ListArticlesController } from "./ListArticlesController";
import { listArticlesPresenter } from "./presenter";
export const createListArticlesController = (context: ICatalogContext) => {
const adapter = context.adapter;
const repoManager = context.repositoryManager;
repoManager.registerRepository(
"Article",
(params = { transaction: null }) => {
const { transaction } = params;
return new CatalogRepository({
transaction,
adapter,
mapper: createArticleMapper(context),
});
}
);
const listArticlesUseCase = new ListArticlesUseCase(context);
return new ListArticlesController(
{
useCase: listArticlesUseCase,
presenter: listArticlesPresenter,
},
context
);
};

View File

@ -0,0 +1,78 @@
import { Article } from "@/contexts/catalog/domain";
import { ICatalogContext } from "@/contexts/catalog/infrastructure";
import {
ICollection,
IListArticles_Response_DTO,
IListResponse_DTO,
} from "@shared/contexts";
export interface IListArticlesPresenter {
map: (
article: Article,
context: ICatalogContext
) => IListArticles_Response_DTO;
mapArray: (
articles: ICollection<Article>,
context: ICatalogContext,
params: {
page: number;
limit: number;
}
) => IListResponse_DTO<IListArticles_Response_DTO>;
}
export const listArticlesPresenter: IListArticlesPresenter = {
map: (
article: Article,
context: ICatalogContext
): IListArticles_Response_DTO => {
console.time("listArticlesPresenter.map");
const result: IListArticles_Response_DTO = {
id: article.id.toString(),
catalog_name: article.catalog_name.toString(),
id_article: article.id_article.toString(),
reference: article.reference.toString(),
description: article.description.toString(),
family: article.family.toString(),
subfamily: article.subfamily.toString(),
points: article.points.toNumber(),
retail_price: article.retail_price.toObject(),
};
console.timeEnd("listArticlesPresenter.map");
return result;
},
mapArray: (
articles: ICollection<Article>,
context: ICatalogContext,
params: {
page: number;
limit: number;
}
): IListResponse_DTO<IListArticles_Response_DTO> => {
console.time("listArticlesPresenter.mapArray");
const { page, limit } = params;
const totalCount = articles.totalCount ?? 0;
const items = articles.items.map((article: Article) =>
listArticlesPresenter.map(article, context)
);
const result = {
page,
per_page: limit,
total_pages: Math.ceil(totalCount / limit),
total_items: totalCount,
items,
};
console.timeEnd("listArticlesPresenter.mapArray");
return result;
},
};

View File

@ -0,0 +1 @@
export * from "./ListArticles.presenter";

View File

@ -1,34 +0,0 @@
import { ListProductsUseCase } from "@/contexts/catalog/application";
import { ICatalogContext } from "../../..";
import { CatalogRepository } from "../../../Catalog.repository";
import { createProductMapper } from "../../../mappers/product.mapper";
import { ListProductsController } from "./ListProductsController";
import { listProductsPresenter } from "./presenter";
export const createListProductsController = (context: ICatalogContext) => {
const adapter = context.adapter;
const repoManager = context.repositoryManager;
repoManager.registerRepository(
"Product",
(params = { transaction: null }) => {
const { transaction } = params;
return new CatalogRepository({
transaction,
adapter,
mapper: createProductMapper(context),
});
}
);
const listProductsUseCase = new ListProductsUseCase(context);
return new ListProductsController(
{
useCase: listProductsUseCase,
presenter: listProductsPresenter,
},
context
);
};

View File

@ -1,71 +0,0 @@
import { Product } from "@/contexts/catalog/domain";
import { ICatalogContext } from "@/contexts/catalog/infrastructure";
import {
ICollection,
IListProducts_Response_DTO,
IListResponse_DTO,
} from "@shared/contexts";
export interface IListProductsPresenter {
map: (
product: Product,
context: ICatalogContext
) => IListProducts_Response_DTO;
mapArray: (
products: ICollection<Product>,
context: ICatalogContext,
params: {
page: number;
limit: number;
}
) => IListResponse_DTO<IListProducts_Response_DTO>;
}
export const listProductsPresenter: IListProductsPresenter = {
map: (
product: Product,
context: ICatalogContext
): IListProducts_Response_DTO => {
console.time("listProductsPresenter.map");
const result: IListProducts_Response_DTO = {
id: product.id.toString(),
reference: product.reference.toString(),
};
console.timeEnd("listProductsPresenter.map");
return result;
},
mapArray: (
products: ICollection<Product>,
context: ICatalogContext,
params: {
page: number;
limit: number;
}
): IListResponse_DTO<IListProducts_Response_DTO> => {
console.time("listProductsPresenter.mapArray");
const { page, limit } = params;
const totalCount = products.totalCount ?? 0;
const items = products.items.map((product: Product) =>
listProductsPresenter.map(product, context)
);
const result = {
page,
per_page: limit,
total_pages: Math.ceil(totalCount / limit),
total_items: totalCount,
items,
};
console.timeEnd("listProductsPresenter.mapArray");
return result;
},
};

View File

@ -1 +0,0 @@
export * from "./ListProducts.presenter";

View File

@ -1,9 +1,9 @@
import { IApplicationService } from "@/contexts/common/application/services/ApplicationService";
import { IRepositoryManager } from "@/contexts/common/domain";
import { IFirebirdAdapter } from "@/contexts/common/infrastructure/firebird";
import { ISequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
export interface ICatalogContext {
adapter: IFirebirdAdapter;
adapter: ISequelizeAdapter;
repositoryManager: IRepositoryManager;
services: IApplicationService;
}

View File

@ -0,0 +1,62 @@
import {
ISequelizeMapper,
SequelizeMapper,
} from "@/contexts/common/infrastructure";
import {
Description,
Quantity,
Slug,
UniqueID,
UnitPrice,
} from "@shared/contexts";
import { ICatalogContext } from "..";
import { Article, IArticleProps } from "../../domain/entities";
import {
Article_Model,
TCreationArticle_Attributes,
} from "../sequelize/article.model";
export interface IArticleMapper
extends ISequelizeMapper<
Article_Model,
TCreationArticle_Attributes,
Article
> {}
class ArticleMapper
extends SequelizeMapper<Article_Model, TCreationArticle_Attributes, Article>
implements IArticleMapper
{
public constructor(props: { context: ICatalogContext }) {
super(props);
}
protected toDomainMappingImpl(source: Article_Model, params: any): Article {
const props: IArticleProps = {
catalog_name: this.mapsValue(source, "catalog_name", Slug.create),
id_article: this.mapsValue(source, "id_article", Description.create),
reference: this.mapsValue(source, "reference", Slug.create),
family: this.mapsValue(source, "family", Description.create),
subfamily: this.mapsValue(source, "subfamily", Description.create),
description: this.mapsValue(source, "description", Description.create),
points: this.mapsValue(source, "points", Quantity.create),
retail_price: this.mapsValue(source, "retail_price", (value: any) =>
UnitPrice.create({ amount: value, precision: 4 })
),
};
const id = this.mapsValue(source, "id", UniqueID.create);
const productOrError = Article.create(props, id);
if (productOrError.isFailure) {
throw productOrError.error;
}
return productOrError.object;
}
}
export const createArticleMapper = (context: ICatalogContext): IArticleMapper =>
new ArticleMapper({
context,
});

View File

@ -1,47 +0,0 @@
import {
FirebirdMapper,
IFirebirdMapper,
} from "@/contexts/common/infrastructure/mappers/FirebirdMapper";
import { Description, MoneyValue, UniqueID } from "@shared/contexts";
import { ICatalogContext } from "..";
import { IProductProps, Product } from "../../domain/entities";
import { Product_Model } from "../firebird";
export interface IProductMapper
extends IFirebirdMapper<Product_Model, Product> {}
class ProductMapper
extends FirebirdMapper<Product_Model, Product>
implements IProductMapper
{
public constructor(props: { context: ICatalogContext }) {
super(props);
}
protected toDomainMappingImpl(source: Product_Model, params: any): Product {
const props: IProductProps = {
reference: this.mapsValue(source, "reference", Description.create),
family: this.mapsValue(source, "family", Description.create),
subfamily: this.mapsValue(source, "subfamily", Description.create),
description: this.mapsValue(source, "description", Description.create),
points: this.mapsValue(source, "points", Description.create),
pvp: this.mapsValue(source, "pvp", (value: any) =>
MoneyValue.create({ amount: value })
),
};
const id = this.mapsValue(source, "id", UniqueID.create);
const productOrError = Product.create(props, id);
if (productOrError.isFailure) {
throw productOrError.error;
}
return productOrError.object;
}
}
export const createProductMapper = (context: ICatalogContext): IProductMapper =>
new ProductMapper({
context,
});

View File

@ -0,0 +1,76 @@
import {
CreationOptional,
DataTypes,
InferAttributes,
InferCreationAttributes,
Model,
Sequelize,
} from "sequelize";
export type TCreationArticle_Attributes =
InferCreationAttributes<Article_Model>;
export class Article_Model extends Model<
InferAttributes<Article_Model>,
InferCreationAttributes<Article_Model>
> {
// To avoid table creation
/*static async sync(): Promise<any> {
return Promise.resolve();
}*/
static associate(connection: Sequelize) {}
declare id: string;
declare catalog_name: string;
declare id_article: string; // number ??
declare reference: CreationOptional<string>;
declare family: CreationOptional<string>;
declare subfamily: CreationOptional<string>;
declare description: CreationOptional<string>;
declare points: CreationOptional<number>;
declare retail_price: CreationOptional<number>;
}
export default (sequelize: Sequelize) => {
Article_Model.init(
{
id: {
type: new DataTypes.UUID(),
primaryKey: true,
},
catalog_name: DataTypes.STRING(),
id_article: DataTypes.STRING(),
reference: DataTypes.STRING(),
family: DataTypes.STRING(),
subfamily: DataTypes.STRING(),
description: DataTypes.STRING(),
points: DataTypes.SMALLINT().UNSIGNED,
retail_price: DataTypes.BIGINT(),
},
{
sequelize,
tableName: "catalog",
//paranoid: true, // softs deletes
timestamps: true,
//version: true,
createdAt: "created_at",
updatedAt: "updated_at",
deletedAt: "deleted_at",
indexes: [
{ name: "catalog_name_idx", fields: ["catalog_name"] },
{ name: "id_article_idx", fields: ["id_article"] },
{ name: "family_idx", fields: ["family"] },
{ name: "family_subfamily_idx", fields: ["family", "subfamily"] },
{ name: "updated_at_idx", fields: ["updated_at"] },
],
}
);
return Article_Model;
};

View File

@ -15,7 +15,7 @@ export interface IDTOMapper<S, D> {
params: {
page: number;
limit: number;
},
}
) => IListResponse_DTO<D>;
}

View File

@ -1,4 +1,3 @@
import { FirebirdModel } from "@/contexts/catalog/infrastructure/firebird/firebird.model";
import { Collection, Entity, Result } from "@shared/contexts";
import { ValidationError } from "sequelize";
import {
@ -6,6 +5,7 @@ import {
RequiredFieldMissingError,
} from "../../domain/errors";
import { InfrastructureError } from "../InfrastructureError";
import { FirebirdModel } from "../firebird/firebird.model";
export interface IFirebirdMapper<
TModel extends FirebirdModel,

View File

@ -20,7 +20,7 @@ import { ISequelizeQueryBuilder } from "./queryBuilder/SequelizeQueryBuilder";
export interface ISequelizeAdapter extends IAdapter {
queryBuilder: ISequelizeQueryBuilder;
startTransaction: () => SequelizeBusinessTransactionType;
getModel: (modelName: string) => any;
hasModel: (modelName: string) => boolean;
}

View File

@ -4,7 +4,6 @@ import http from "http";
import { assign } from "lodash";
import { DateTime, Settings } from "luxon";
import { createFirebirdAdapter } from "@/contexts/common/infrastructure/firebird";
import { createSequelizeAdapter } from "@/contexts/common/infrastructure/sequelize";
import { trace } from "console";
import { config } from "../../config";
@ -92,13 +91,13 @@ const serverConnection = (conn: any) => {
};
const sequelizeConn = createSequelizeAdapter();
const firebirdConn = createFirebirdAdapter();
//const firebirdConn = createFirebirdAdapter();
const server: http.Server = http
.createServer(app)
.once("listening", () =>
process.on("SIGINT", () => {
firebirdConn.disconnect();
//firebirdConn.disconnect();
serverStop(server);
})
)
@ -111,32 +110,32 @@ const server: http.Server = http
.on("error", serverError);
try {
firebirdConn.sync().then(() => {
sequelizeConn.sync({ force: false, alter: true }).then(() => {
// Launch server
server.listen(currentState.server.port, () => {
const now = DateTime.now();
logger.info(
`Time: ${now.toLocaleString(DateTime.DATETIME_FULL)} ${now.zoneName}`
);
logger.info(
`Launched in: ${now.diff(currentState.launchedAt).toMillis()} ms`
);
logger.info(`Environment: ${currentState.environment}`);
logger.info(`Process PID: ${process.pid}`);
logger.info("To shut down your server, press <CTRL> + C at any time");
logger.info(
`⚡️ Server: http://${currentState.server.hostname}:${currentState.server.port}`
);
});
//firebirdConn.sync().then(() => {
sequelizeConn.sync({ force: false, alter: true }).then(() => {
// Launch server
server.listen(currentState.server.port, () => {
const now = DateTime.now();
logger.info(
`Time: ${now.toLocaleString(DateTime.DATETIME_FULL)} ${now.zoneName}`
);
logger.info(
`Launched in: ${now.diff(currentState.launchedAt).toMillis()} ms`
);
logger.info(`Environment: ${currentState.environment}`);
logger.info(`Process PID: ${process.pid}`);
logger.info("To shut down your server, press <CTRL> + C at any time");
logger.info(
`⚡️ Server: http://${currentState.server.hostname}:${currentState.server.port}`
);
});
});
//});
} catch (error) {
serverError(error);
}
process.on("uncaughtException", (error: any) => {
firebirdConn.disconnect();
//firebirdConn.disconnect();
logger.error(`${new Date().toUTCString()} uncaughtException:`, error.message);
logger.error(error.stack);
//process.exit(1);

View File

@ -1,11 +1,13 @@
import { IMoney_Response_DTO } from "../../../../common";
export interface IListProducts_Response_DTO {
export interface IListArticles_Response_DTO {
id: string;
catalog_name: string;
id_article: string;
reference: string;
family: string;
subfamily: string;
description: string;
points: number;
pvp: IMoney_Response_DTO;
retail_price: IMoney_Response_DTO;
}

View File

@ -0,0 +1 @@
export * from "./IListArticles_Response.dto";

View File

@ -1 +0,0 @@
export * from "./IListProducts_Response.dto";

View File

@ -1 +1 @@
export * from "./IListProducts.dto";
export * from "./IListArticles.dto";

View File

@ -40,7 +40,8 @@ export class AddressTitle extends StringValueObject {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -40,7 +40,8 @@ export class AddressType extends StringValueObject {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -38,7 +38,8 @@ export class City extends StringValueObject {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -40,7 +40,8 @@ export class Country extends StringValueObject {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -41,7 +41,8 @@ export class PostalCode extends StringValueObject {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -40,7 +40,8 @@ export class Province extends StringValueObject {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -40,7 +40,8 @@ export class Street extends StringValueObject {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -42,7 +42,8 @@ export class Email extends StringValueObject {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -39,7 +39,8 @@ export class Name extends StringValueObject {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -42,7 +42,8 @@ export class Note extends StringValueObject {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -40,7 +40,8 @@ export class Phone extends StringValueObject {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -27,8 +27,8 @@ export class Filter extends ValueObject<IFilterProps> implements IFilter {
return Result.ok<Filter>(
new Filter({
...filterProps,
operator: this.sanitizeOperator(filterProps.operator),
}),
operator: Filter.sanitizeOperator(filterProps.operator),
})
);
}

View File

@ -26,9 +26,9 @@ export class Order extends ValueObject<IOrderProps> implements IOrder {
return Result.ok<Order>(
new Order({
field: this.sanitize(orderProps.field),
type: this.sanitize(orderProps.type),
}),
field: Order.sanitize(orderProps.field),
type: Order.sanitize(orderProps.type),
})
);
}
@ -42,7 +42,7 @@ export class Order extends ValueObject<IOrderProps> implements IOrder {
) {
let typeOrError = RuleValidator.validate(
RuleValidator.RULE_IS_TYPE_STRING,
type,
type
);
if (typeOrError.isFailure) {
@ -63,7 +63,7 @@ export class Order extends ValueObject<IOrderProps> implements IOrder {
) {
const fieldOrError = RuleValidator.validate(
RuleValidator.RULE_IS_TYPE_STRING,
field,
field
);
if (fieldOrError.isFailure) {

View File

@ -1,5 +1,6 @@
import { Result, ValueObject } from "@shared/contexts";
import { DomainError } from "../../../errors";
import { Result } from "../../Result";
import { ValueObject } from "../../ValueObject";
import { Order } from "./Order";
import { OrderCollection } from "./OrderCollection";
@ -43,16 +44,14 @@ export class OrderRoot
return Result.ok<IOrderRoot>(
new OrderRoot({
type: this.sanitizeOperator(orderRootProps.type),
type: OrderRoot.sanitizeOperator(orderRootProps.type),
items: OrderCollection.create(orderRootProps.value),
})
);
}
protected static validate(OrderRootRoot: IOrderRootProps): Result<any> {
throw DomainError.create("NOT IMPLEMENT", {
function: "OrderRoot.validate()",
});
throw DomainError.create("NOT IMPLEMENT", "OrderRoot.validate()");
/*return Validator.isOneOf(
{
@ -85,7 +84,7 @@ export class OrderRoot
}
public toString(): string {
return `${this.type} ${this.value.toString()}`;
return `${this.type} ${this.items.toString()}`;
}
public toObject(): Record<string, any> {

View File

@ -14,7 +14,7 @@ export class Slug extends StringValueObject {
protected static validate(
value: UndefinedOr<string>,
options: IStringValueObjectOptions,
options: IStringValueObjectOptions
) {
const rule = Joi.string()
.allow(null)
@ -68,7 +68,7 @@ export class Slug extends StringValueObject {
public static create(
value: UndefinedOr<string>,
options: IStringValueObjectOptions = {},
options: IStringValueObjectOptions = {}
) {
const _options = {
label: "slug",
@ -81,7 +81,8 @@ export class Slug extends StringValueObject {
return Result.fail(validationResult.error);
}
return Result.ok<Slug>(new Slug(this.sanitize(validationResult.object)));
const slugValue = Slug.sanitize(validationResult.object);
return Result.ok<Slug>(new Slug(slugValue));
}
}

View File

@ -50,7 +50,8 @@ export class UniqueID extends NullableValueObject<string> {
return Result.fail(
handleDomainError(
DomainError.INVALID_INPUT_DATA,
validationResult.error
validationResult.error.message,
_options
)
);
}

View File

@ -32,7 +32,7 @@ export function handleDomainError2(
export function handleDomainError(
code: string,
message?: string,
message: string = "",
payload?: Record<string, any>
): DomainError {
return DomainError.create(code, message, payload);

View File

@ -1 +1,2 @@
export * from "./contexts";
export * from "./utilities";