diff --git a/.gitignore b/.gitignore index 6214c43..1bfbc0a 100644 --- a/.gitignore +++ b/.gitignore @@ -78,3 +78,5 @@ typings/ # DynamoDB Local files .dynamodb/ + +.vscode/ diff --git a/.prettierrc b/.prettierrc index e54965b..4bd296e 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,5 +1,5 @@ { - "printWidth": 160, + "printWidth": 140, "singleQuote": true, "trailingComma": "none", "bracketSpacing": true, diff --git a/src/app/operations/product/createProductOperation.js b/src/app/operations/product/createProductOperation.js index cfb81f4..68400cd 100644 --- a/src/app/operations/product/createProductOperation.js +++ b/src/app/operations/product/createProductOperation.js @@ -1,15 +1,22 @@ -module.exports = ({ createProductUsecase, createWarehouseUsecase, bindingProductWarehouse }) => ({ +module.exports = ({ createProductUsecase, createWarehouseUsecase, bindingProductWarehouse, removeProductByIdUsecase }) => ({ execute: async (data) => { - const { warehouse } = data; - const product = await createProductUsecase.execute(data); - if (!product) { - return; + let productId; + try { + const { warehouse } = data; + const product = await createProductUsecase.execute(data); + if (!product) { + return; + } + productId = product.id; + + if (warehouse) { + const warehouseCretead = await Promise.all(warehouse.map((element) => createWarehouseUsecase.execute(element))); + await bindingProductWarehouse.execute(productId, warehouseCretead); + } + return product; + } catch (error) { + await removeProductByIdUsecase.execute(productId); + return error; } - if (warehouse) { - const warehouseCretead = await Promise.all(warehouse.map((element) => createWarehouseUsecase.execute(element))); - // TODO: validate warehouseCreated - await bindingProductWarehouse.execute(product.id, warehouseCretead); - } - return product; } }); diff --git a/src/app/operations/product/removeProductOperation.js b/src/app/operations/product/removeProductOperation.js new file mode 100644 index 0000000..6ffde8b --- /dev/null +++ b/src/app/operations/product/removeProductOperation.js @@ -0,0 +1,36 @@ +module.exports = ({ + removeProductBySkuUsecase, + findProductBySkuUsecase, + removeWarehouseByIdUsecase, + findProductWarehouseByProductIdUsecase, + removeProductWareseByProductIdUsecase, + exceptions +}) => ({ + execute: async (data) => { + const product = await findProductBySkuUsecase.execute(data); + if (!product) { + throw exceptions.notFoundError('[findProductBySkuUsecase] product is not found'); + } + const { id, sku } = product; + const productWarehouse = await findProductWarehouseByProductIdUsecase.execute(id); + if (!productWarehouse || productWarehouse.length === 0) { + throw exceptions.notFoundError('[findProductWarehouseByProductIdUsecase] bind product and warehouse not found'); + } + const productWarehouseIsRemoved = await removeProductWareseByProductIdUsecase.execute(id); + if (!productWarehouseIsRemoved) { + throw exceptions.internalError('[removeProductWareseByProductIdUsecase]'); + } + + const productIsRemoved = await removeProductBySkuUsecase.execute(sku); + if (!productIsRemoved) { + throw exceptions.internalError('[removeProductBySkuUsecase]'); + } + + const warehouseIsRemoved = await Promise.all(productWarehouse.map((item) => removeWarehouseByIdUsecase.execute(item.warehouseId))); + if (!warehouseIsRemoved) { + throw exceptions.internalError('[removeWarehouseByIdUsecase]'); + } + + return productIsRemoved; + } +}); diff --git a/src/app/usecases/createProductUsecase.js b/src/app/usecases/createProductUsecase.js index 8cae9b3..ca30398 100644 --- a/src/app/usecases/createProductUsecase.js +++ b/src/app/usecases/createProductUsecase.js @@ -1,6 +1,10 @@ -module.exports = ({ productRepository }) => ({ +module.exports = ({ productRepository, exceptions }) => ({ execute: async (data) => { - const response = await productRepository.create(data); - return response; + try { + const response = await productRepository.create(data); + return response; + } catch (error) { + throw exceptions.internalError(JSON.stringify(error.errors)); + } } }); diff --git a/src/app/usecases/findProductBySkuUsecase.js b/src/app/usecases/findProductBySkuUsecase.js new file mode 100644 index 0000000..c0110cc --- /dev/null +++ b/src/app/usecases/findProductBySkuUsecase.js @@ -0,0 +1,6 @@ +module.exports = ({ productRepository }) => ({ + execute: async (data) => { + const response = await productRepository.findBySku(data); + return response; + } +}); diff --git a/src/app/usecases/findProductWarehouseByProductIdUsecase.js b/src/app/usecases/findProductWarehouseByProductIdUsecase.js new file mode 100644 index 0000000..2cedd20 --- /dev/null +++ b/src/app/usecases/findProductWarehouseByProductIdUsecase.js @@ -0,0 +1,6 @@ +module.exports = ({ productWarehouseRepository }) => ({ + execute: async (data) => { + const response = await productWarehouseRepository.findAllByProductId(data); + return response; + } +}); diff --git a/src/app/usecases/removeProductByIdUsecase.js b/src/app/usecases/removeProductByIdUsecase.js new file mode 100644 index 0000000..c05b713 --- /dev/null +++ b/src/app/usecases/removeProductByIdUsecase.js @@ -0,0 +1,6 @@ +module.exports = ({ productRepository }) => ({ + execute: async (data) => { + const response = await productRepository.removeById(data); + return response; + } +}); diff --git a/src/app/usecases/removeProductBySkuUsecase.js b/src/app/usecases/removeProductBySkuUsecase.js new file mode 100644 index 0000000..3e53873 --- /dev/null +++ b/src/app/usecases/removeProductBySkuUsecase.js @@ -0,0 +1,6 @@ +module.exports = ({ productRepository }) => ({ + execute: async (data) => { + const response = await productRepository.removeBySku(data); + return response; + } +}); diff --git a/src/app/usecases/removeProductWareseByProductIdUsecase.js b/src/app/usecases/removeProductWareseByProductIdUsecase.js new file mode 100644 index 0000000..6e314cf --- /dev/null +++ b/src/app/usecases/removeProductWareseByProductIdUsecase.js @@ -0,0 +1,6 @@ +module.exports = ({ productWarehouseRepository }) => ({ + execute: async (data) => { + const response = await productWarehouseRepository.removeByProductId(data); + return response; + } +}); diff --git a/src/app/usecases/removeWarehouseByIdUsecase.js b/src/app/usecases/removeWarehouseByIdUsecase.js new file mode 100644 index 0000000..0618d56 --- /dev/null +++ b/src/app/usecases/removeWarehouseByIdUsecase.js @@ -0,0 +1,6 @@ +module.exports = ({ warehouseRepository }) => ({ + execute: async (data) => { + const response = await warehouseRepository.removeById(data); + return response; + } +}); diff --git a/src/infra/errors/BadRequestError.js b/src/infra/errors/BadRequestError.js new file mode 100644 index 0000000..34a5aff --- /dev/null +++ b/src/infra/errors/BadRequestError.js @@ -0,0 +1,7 @@ +class BadRequestError extends Error { + constructor(details) { + super(`Bad Request ${details}`); + this.name = 'BadRequestError'; + } +} +module.exports = () => BadRequestError; diff --git a/src/infra/errors/Exceptions.js b/src/infra/errors/Exceptions.js index f4dd186..02066e8 100644 --- a/src/infra/errors/Exceptions.js +++ b/src/infra/errors/Exceptions.js @@ -1,9 +1,25 @@ -module.exports = ({ internalServerError: InternalServerError }) => ({ +module.exports = ({ internalServerError: InternalServerError, notFoundError: NotFoundError, badRequestError: BadRequestError }) => ({ internalError: (details) => { const error = new InternalServerError(details); return { status: 500, body: error.name }; + }, + + notFound: (details) => { + const error = new NotFoundError(details); + return { + status: 500, + body: error.name + }; + }, + + badRequest: (details) => { + const error = new BadRequestError(details); + return { + status: 500, + body: error.name + }; } }); diff --git a/src/infra/errors/NotFoundError.js b/src/infra/errors/NotFoundError.js new file mode 100644 index 0000000..e99dc67 --- /dev/null +++ b/src/infra/errors/NotFoundError.js @@ -0,0 +1,7 @@ +class NotFoundError extends Error { + constructor(details) { + super(`Not found ${details}`); + this.name = 'NotFoundError'; + } +} +module.exports = () => NotFoundError; diff --git a/src/infra/repositories/Repository.js b/src/infra/repositories/Repository.js index 95913df..1f5e43b 100644 --- a/src/infra/repositories/Repository.js +++ b/src/infra/repositories/Repository.js @@ -6,15 +6,26 @@ class Repository { } async create(domainEntity) { - const model = await new this.ResourceModel(this.ResourceMapper.toInputDatabase(domainEntity)); - const dbCreatedResource = await model.save(); + const resource = await new this.ResourceModel(this.ResourceMapper.toInputDatabase(domainEntity)); + const dbCreatedResource = await resource.save(); return this.ResourceMapper.toOutputDabase(dbCreatedResource); } + async findOne(query) { + const resource = await this.ResourceModel.findOne(query); + if (!resource) return resource; + return this.ResourceMapper.toOutputDabase(resource); + } + async findAll(query) { - const model = await this.ResourceModel.findAll(query); - if (!model) return model; - return model.map(this.ResourceMapper.toOutputDabase); + const resource = await this.ResourceModel.findAll(query); + if (!resource) return resource; + return resource.map(this.ResourceMapper.toOutputDabase); + } + + async remove(query) { + const resource = await this.ResourceModel.destroy(query); + return resource > 0; } } diff --git a/src/infra/repositories/product/ProductRepository.js b/src/infra/repositories/product/ProductRepository.js index d64a20a..e4dac0a 100644 --- a/src/infra/repositories/product/ProductRepository.js +++ b/src/infra/repositories/product/ProductRepository.js @@ -9,6 +9,21 @@ class ProductRepository extends Repository { Exceptions: exceptions }); } + + async findBySku(sku) { + const resource = await this.findOne({ where: { sku } }); + return resource; + } + + async removeById(productId) { + const resource = await this.remove({ where: { id: productId } }); + return resource; + } + + async removeBySku(sku) { + const resource = await this.remove({ where: { sku } }); + return resource; + } } module.exports = ProductRepository; diff --git a/src/infra/repositories/productWarehouse/ProductWarehouseMapper.js b/src/infra/repositories/productWarehouse/ProductWarehouseMapper.js index 949ccbd..883f9ad 100644 --- a/src/infra/repositories/productWarehouse/ProductWarehouseMapper.js +++ b/src/infra/repositories/productWarehouse/ProductWarehouseMapper.js @@ -2,7 +2,6 @@ const ProductWarehouseMapper = { toInputDatabase(entry) { - console.log(entry); return entry; }, toOutputDabase({ dataValues }) { diff --git a/src/infra/repositories/productWarehouse/ProductWarehouseRepository.js b/src/infra/repositories/productWarehouse/ProductWarehouseRepository.js index a9746a5..911b229 100644 --- a/src/infra/repositories/productWarehouse/ProductWarehouseRepository.js +++ b/src/infra/repositories/productWarehouse/ProductWarehouseRepository.js @@ -1,6 +1,6 @@ const Repository = require('../Repository'); -class WarehouseRepository extends Repository { +class ProductWarehouseRepository extends Repository { constructor({ sequelize, productWarehouseMapper, exceptions }) { const { ProductWarehouse } = sequelize.models; super({ @@ -9,6 +9,16 @@ class WarehouseRepository extends Repository { Exceptions: exceptions }); } + + async findAllByProductId(productId) { + const resource = await this.findAll({ where: { productId } }); + return resource; + } + + async removeByProductId(productId) { + const resource = await this.remove({ where: { productId } }); + return resource; + } } -module.exports = WarehouseRepository; +module.exports = ProductWarehouseRepository; diff --git a/src/infra/repositories/warehouse/WarehouseRepository.js b/src/infra/repositories/warehouse/WarehouseRepository.js index 2c41a4b..7e38e3b 100644 --- a/src/infra/repositories/warehouse/WarehouseRepository.js +++ b/src/infra/repositories/warehouse/WarehouseRepository.js @@ -9,6 +9,11 @@ class WarehouseRepository extends Repository { Exceptions: exceptions }); } + + async removeById(id) { + const resource = await this.remove({ where: { id } }); + return resource; + } } module.exports = WarehouseRepository; diff --git a/src/interfaces/http/controllers/productController.js b/src/interfaces/http/controllers/productController.js index 5e4798b..6e93e23 100644 --- a/src/interfaces/http/controllers/productController.js +++ b/src/interfaces/http/controllers/productController.js @@ -1,18 +1,27 @@ const { Router } = require('express'); const Status = require('http-status'); -module.exports = ({ createProductOperation }) => ({ +module.exports = ({ createProductOperation, removeProductOperation }) => ({ create: async (req, res, next) => { try { const { body } = req; - const product = await createProductOperation.execute(body); - return res.status(Status.OK).json(product); + await createProductOperation.execute(body); + return res.status(Status.CREATED).end(); } catch (error) { next(error); } }, + removeBySku: async (req, res, next) => { + try { + const { sku } = req.params; + const responseOperation = await removeProductOperation.execute(typeof sku === 'string' ? Number(sku) : sku); + return res.status(Status.OK).json(responseOperation); + } catch (error) { + next(error); + } + }, get router() { - return Router().post('/', this.create); + return Router().post('/', this.create).delete('/sku/:sku', this.removeBySku); } });