From aec67dc197b124a65b6d270beb9343ee3f83bee6 Mon Sep 17 00:00:00 2001 From: The Nguyen <6950941+treoden@users.noreply.github.com> Date: Sun, 26 Nov 2023 14:53:25 +0700 Subject: [PATCH] Refactoring the catalog APIs --- .../createAttribute[finish].js | 4 +- .../createCategory/createCategory[finish].js | 4 +- ...[getConnection]createCollection[finish].js | 8 -- .../createCollection[finish].js | 9 ++ .../createCollection/finish[apiResponse].js | 77 ++++--------- .../createCollection/getConnection[finish].js | 12 -- .../api/createCollection/payloadSchema.json | 14 +-- .../createProduct/createProduct[finish].js | 4 +- .../api/deleteAttribute/deleteAttribute.js | 28 +---- .../api/deleteCategory/deleteCategory.js | 34 +----- .../api/deleteCollection/deleteCollection.js | 26 +---- .../api/deleteProduct/deleteProduct.js | 32 +----- .../updateCategory/updateCategory[finish].js | 4 +- ...[getConnection]updateCollection[finish].js | 31 ------ .../updateCollection/finish[apiResponse].js | 75 ++++--------- .../updateCollection/getConnection[finish].js | 12 -- .../api/updateCollection/payloadSchema.json | 9 +- .../updateCollection[finish].js | 9 ++ .../updateProduct/updateProduct[finish].js | 4 +- .../attribute/createProductAttribute.js | 73 +++++++----- .../attribute/deleteProductAttribute.js | 54 +++++++++ .../attribute/updateProductAttribute.js | 85 ++++++++------ .../services/category/createCategory.js | 43 ++++--- .../services/category/deleteCategory.js | 60 ++++++++++ .../services/category/updateCategory.js | 45 +++++--- .../collection/collectionDataSchema.json | 16 +++ .../services/collection/createCollection.js | 81 ++++++++++++++ .../services/collection/deleteCollection.js | 52 +++++++++ .../services/collection/updateCollection.js | 105 ++++++++++++++++++ .../catalog/services/product/createProduct.js | 83 ++++++++------ .../catalog/services/product/deleteProduct.js | 60 ++++++++++ .../catalog/services/product/updateProduct.js | 98 +++++++++------- 32 files changed, 783 insertions(+), 468 deletions(-) delete mode 100644 packages/evershop/src/modules/catalog/api/createCollection/[getConnection]createCollection[finish].js create mode 100644 packages/evershop/src/modules/catalog/api/createCollection/createCollection[finish].js delete mode 100644 packages/evershop/src/modules/catalog/api/createCollection/getConnection[finish].js delete mode 100644 packages/evershop/src/modules/catalog/api/updateCollection/[getConnection]updateCollection[finish].js delete mode 100644 packages/evershop/src/modules/catalog/api/updateCollection/getConnection[finish].js create mode 100644 packages/evershop/src/modules/catalog/api/updateCollection/updateCollection[finish].js create mode 100644 packages/evershop/src/modules/catalog/services/attribute/deleteProductAttribute.js create mode 100644 packages/evershop/src/modules/catalog/services/category/deleteCategory.js create mode 100644 packages/evershop/src/modules/catalog/services/collection/collectionDataSchema.json create mode 100644 packages/evershop/src/modules/catalog/services/collection/createCollection.js create mode 100644 packages/evershop/src/modules/catalog/services/collection/deleteCollection.js create mode 100644 packages/evershop/src/modules/catalog/services/collection/updateCollection.js create mode 100644 packages/evershop/src/modules/catalog/services/product/deleteProduct.js diff --git a/packages/evershop/src/modules/catalog/api/createAttribute/createAttribute[finish].js b/packages/evershop/src/modules/catalog/api/createAttribute/createAttribute[finish].js index a58209fd7..56562f433 100644 --- a/packages/evershop/src/modules/catalog/api/createAttribute/createAttribute[finish].js +++ b/packages/evershop/src/modules/catalog/api/createAttribute/createAttribute[finish].js @@ -2,6 +2,8 @@ const createProductAttribute = require('../../services/attribute/createProductAt // eslint-disable-next-line no-unused-vars module.exports = async (request, response, delegate) => { - const attribute = await createProductAttribute(request.body); + const attribute = await createProductAttribute(request.body, { + routeId: request.currentRoute.id + }); return attribute; }; diff --git a/packages/evershop/src/modules/catalog/api/createCategory/createCategory[finish].js b/packages/evershop/src/modules/catalog/api/createCategory/createCategory[finish].js index bdfce2dc0..fcbbfdddb 100644 --- a/packages/evershop/src/modules/catalog/api/createCategory/createCategory[finish].js +++ b/packages/evershop/src/modules/catalog/api/createCategory/createCategory[finish].js @@ -2,6 +2,8 @@ const createCategory = require('../../services/category/createCategory'); // eslint-disable-next-line no-unused-vars module.exports = async (request, response, delegate) => { - const result = await createCategory(request.body); + const result = await createCategory(request.body, { + routeId: request.currentRoute.id + }); return result; }; diff --git a/packages/evershop/src/modules/catalog/api/createCollection/[getConnection]createCollection[finish].js b/packages/evershop/src/modules/catalog/api/createCollection/[getConnection]createCollection[finish].js deleted file mode 100644 index 6d7f1b482..000000000 --- a/packages/evershop/src/modules/catalog/api/createCollection/[getConnection]createCollection[finish].js +++ /dev/null @@ -1,8 +0,0 @@ -const { insert } = require('@evershop/postgres-query-builder'); - -module.exports = async (request, response, delegate) => { - const connection = await delegate.getConnection; - const data = request.body; - const result = await insert('collection').given(data).execute(connection); - return result; -}; diff --git a/packages/evershop/src/modules/catalog/api/createCollection/createCollection[finish].js b/packages/evershop/src/modules/catalog/api/createCollection/createCollection[finish].js new file mode 100644 index 000000000..b7b9873d4 --- /dev/null +++ b/packages/evershop/src/modules/catalog/api/createCollection/createCollection[finish].js @@ -0,0 +1,9 @@ +const createCollection = require('../../services/collection/createCollection'); + +// eslint-disable-next-line no-unused-vars +module.exports = async (request, response, delegate) => { + const collection = await createCollection(request.body, { + routeId: request.currentRoute.id + }); + return collection; +}; diff --git a/packages/evershop/src/modules/catalog/api/createCollection/finish[apiResponse].js b/packages/evershop/src/modules/catalog/api/createCollection/finish[apiResponse].js index ef454c91b..499754b20 100644 --- a/packages/evershop/src/modules/catalog/api/createCollection/finish[apiResponse].js +++ b/packages/evershop/src/modules/catalog/api/createCollection/finish[apiResponse].js @@ -1,64 +1,27 @@ -const { - commit, - rollback, - select -} = require('@evershop/postgres-query-builder'); -const { pool } = require('@evershop/evershop/src/lib/postgres/connection'); const { buildUrl } = require('@evershop/evershop/src/lib/router/buildUrl'); -const { - OK, - INTERNAL_SERVER_ERROR -} = require('@evershop/evershop/src/lib/util/httpStatus'); +const { OK } = require('@evershop/evershop/src/lib/util/httpStatus'); // eslint-disable-next-line no-unused-vars module.exports = async (request, response, delegate, next) => { - const promises = []; - Object.keys(delegate).forEach((id) => { - // Check if middleware is async - if (delegate[id] instanceof Promise) { - promises.push(delegate[id]); + const collection = await delegate.createCollection; + response.status(OK); + response.json({ + data: { + ...collection, + links: [ + { + rel: 'collectionGrid', + href: buildUrl('collectionGrid'), + action: 'GET', + types: ['text/xml'] + }, + { + rel: 'edit', + href: buildUrl('collectionEdit', { id: collection.uuid }), + action: 'GET', + types: ['text/xml'] + } + ] } }); - const result = await delegate.createCollection; - const connection = await delegate.getConnection; - const results = await Promise.allSettled(promises); - const rejected = results.find((r) => r.status === 'rejected'); - if (!rejected) { - await commit(connection); - // Load the created collection - const query = select().from('collection'); - const collection = await query - .where('collection_id', '=', result.insertId) - .load(pool); - response.status(OK); - response.json({ - data: { - ...collection, - links: [ - { - rel: 'collectionGrid', - href: buildUrl('collectionGrid'), - action: 'GET', - types: ['text/xml'] - }, - { - rel: 'edit', - href: buildUrl('collectionEdit', { id: collection.uuid }), - action: 'GET', - types: ['text/xml'] - } - ] - } - }); - } else { - await rollback(connection); - response.status(INTERNAL_SERVER_ERROR); - response.json({ - data: null, - error: { - status: INTERNAL_SERVER_ERROR, - message: rejected.reason.message - } - }); - } }; diff --git a/packages/evershop/src/modules/catalog/api/createCollection/getConnection[finish].js b/packages/evershop/src/modules/catalog/api/createCollection/getConnection[finish].js deleted file mode 100644 index f2bb244d0..000000000 --- a/packages/evershop/src/modules/catalog/api/createCollection/getConnection[finish].js +++ /dev/null @@ -1,12 +0,0 @@ -const { startTransaction } = require('@evershop/postgres-query-builder'); -const { - getConnection -} = require('@evershop/evershop/src/lib/postgres/connection'); - -// eslint-disable-next-line no-unused-vars -module.exports = async (request, response) => { - const connection = await getConnection(); - await startTransaction(connection); - - return connection; -}; diff --git a/packages/evershop/src/modules/catalog/api/createCollection/payloadSchema.json b/packages/evershop/src/modules/catalog/api/createCollection/payloadSchema.json index 1a2e5e923..c3aaf8829 100644 --- a/packages/evershop/src/modules/catalog/api/createCollection/payloadSchema.json +++ b/packages/evershop/src/modules/catalog/api/createCollection/payloadSchema.json @@ -1,20 +1,10 @@ { "type": "object", "properties": { - "name": { - "type": "string" - }, "description": { - "type": "string" - }, - "code": { - "type": "string" + "type": "string", + "skipEscape": true } }, - "required": [ - "name", - "description", - "code" - ], "additionalProperties": true } diff --git a/packages/evershop/src/modules/catalog/api/createProduct/createProduct[finish].js b/packages/evershop/src/modules/catalog/api/createProduct/createProduct[finish].js index 1d3ab744f..e947dafd7 100644 --- a/packages/evershop/src/modules/catalog/api/createProduct/createProduct[finish].js +++ b/packages/evershop/src/modules/catalog/api/createProduct/createProduct[finish].js @@ -2,6 +2,8 @@ const createProduct = require('../../services/product/createProduct'); // eslint-disable-next-line no-unused-vars module.exports = async (request, response, delegate) => { - const result = await createProduct(request.body); + const result = await createProduct(request.body, { + routeId: request.currentRoute.id + }); return result; }; diff --git a/packages/evershop/src/modules/catalog/api/deleteAttribute/deleteAttribute.js b/packages/evershop/src/modules/catalog/api/deleteAttribute/deleteAttribute.js index f61dfb831..492e709fa 100644 --- a/packages/evershop/src/modules/catalog/api/deleteAttribute/deleteAttribute.js +++ b/packages/evershop/src/modules/catalog/api/deleteAttribute/deleteAttribute.js @@ -1,32 +1,16 @@ -/* eslint-disable no-unused-vars */ -const { del, select } = require('@evershop/postgres-query-builder'); -const { pool } = require('@evershop/evershop/src/lib/postgres/connection'); const { OK, - INTERNAL_SERVER_ERROR, - INVALID_PAYLOAD + INTERNAL_SERVER_ERROR } = require('@evershop/evershop/src/lib/util/httpStatus'); +const deleteProductAttribute = require('../../services/attribute/deleteProductAttribute'); +// eslint-disable-next-line no-unused-vars module.exports = async (request, response, delegate, next) => { try { const { id } = request.params; - const attribute = await select() - .from('attribute') - .where('uuid', '=', id) - .load(pool); - - if (!attribute) { - response.status(INVALID_PAYLOAD); - response.json({ - error: { - status: INVALID_PAYLOAD, - message: 'Invalid attribute id' - } - }); - return; - } - await del('attribute').where('uuid', '=', id).execute(pool); - + const attribute = await deleteProductAttribute(id, { + routeId: request.currentRoute.id + }); response.status(OK); response.json({ data: attribute diff --git a/packages/evershop/src/modules/catalog/api/deleteCategory/deleteCategory.js b/packages/evershop/src/modules/catalog/api/deleteCategory/deleteCategory.js index 057cd4f57..04ce85275 100644 --- a/packages/evershop/src/modules/catalog/api/deleteCategory/deleteCategory.js +++ b/packages/evershop/src/modules/catalog/api/deleteCategory/deleteCategory.js @@ -1,38 +1,16 @@ -/* eslint-disable no-unused-vars */ -const { del, select } = require('@evershop/postgres-query-builder'); -const { pool } = require('@evershop/evershop/src/lib/postgres/connection'); const { OK, - INTERNAL_SERVER_ERROR, - INVALID_PAYLOAD + INTERNAL_SERVER_ERROR } = require('@evershop/evershop/src/lib/util/httpStatus'); +const deleteCategory = require('../../services/category/deleteCategory'); +// eslint-disable-next-line no-unused-vars module.exports = async (request, response, delegate, next) => { try { const { id } = request.params; - const query = select().from('category'); - query - .leftJoin('category_description') - .on( - 'category_description.category_description_category_id', - '=', - 'category.category_id' - ); - - const category = await query.where('uuid', '=', id).load(pool); - - if (!category) { - response.status(INVALID_PAYLOAD); - response.json({ - error: { - status: INVALID_PAYLOAD, - message: 'Category not found' - } - }); - return; - } - - await del('category').where('uuid', '=', id).execute(pool); + const category = await deleteCategory(id, { + routeId: request.currentRoute.id + }); response.status(OK); response.json({ data: category diff --git a/packages/evershop/src/modules/catalog/api/deleteCollection/deleteCollection.js b/packages/evershop/src/modules/catalog/api/deleteCollection/deleteCollection.js index b691d675f..96e451d40 100644 --- a/packages/evershop/src/modules/catalog/api/deleteCollection/deleteCollection.js +++ b/packages/evershop/src/modules/catalog/api/deleteCollection/deleteCollection.js @@ -1,30 +1,16 @@ -/* eslint-disable no-unused-vars */ -const { del, select } = require('@evershop/postgres-query-builder'); -const { pool } = require('@evershop/evershop/src/lib/postgres/connection'); const { OK, - INTERNAL_SERVER_ERROR, - INVALID_PAYLOAD + INTERNAL_SERVER_ERROR } = require('@evershop/evershop/src/lib/util/httpStatus'); +const deleteCollection = require('../../services/collection/deleteCollection'); +// eslint-disable-next-line no-unused-vars module.exports = async (request, response, delegate, next) => { try { const { id } = request.params; - const query = select().from('collection'); - const collection = await query.where('uuid', '=', id).load(pool); - - if (!collection) { - response.status(INVALID_PAYLOAD); - response.json({ - error: { - status: INVALID_PAYLOAD, - message: 'Collection not found' - } - }); - return; - } - - await del('collection').where('uuid', '=', id).execute(pool); + const collection = await deleteCollection(id, { + routeId: request.currentRoute.id + }); response.status(OK); response.json({ data: collection diff --git a/packages/evershop/src/modules/catalog/api/deleteProduct/deleteProduct.js b/packages/evershop/src/modules/catalog/api/deleteProduct/deleteProduct.js index af9354333..486b4920f 100644 --- a/packages/evershop/src/modules/catalog/api/deleteProduct/deleteProduct.js +++ b/packages/evershop/src/modules/catalog/api/deleteProduct/deleteProduct.js @@ -1,38 +1,16 @@ -const { del, select } = require('@evershop/postgres-query-builder'); -const { pool } = require('@evershop/evershop/src/lib/postgres/connection'); const { OK, - INTERNAL_SERVER_ERROR, - INVALID_PAYLOAD + INTERNAL_SERVER_ERROR } = require('@evershop/evershop/src/lib/util/httpStatus'); +const deleteProduct = require('../../services/product/deleteProduct'); // eslint-disable-next-line no-unused-vars module.exports = async (request, response, delegate, next) => { try { const { id } = request.params; - const query = select().from('product'); - query - .leftJoin('product_description') - .on( - 'product_description.product_description_product_id', - '=', - 'product.product_id' - ); - - const product = await query.where('uuid', '=', id).load(pool); - - if (!product) { - response.status(INVALID_PAYLOAD); - response.json({ - error: { - status: INVALID_PAYLOAD, - message: 'Product not found' - } - }); - return; - } - - await del('product').where('uuid', '=', id).execute(pool); + const product = await deleteProduct(id, { + routeId: request.currentRoute.id + }); response.status(OK); response.json({ data: product diff --git a/packages/evershop/src/modules/catalog/api/updateCategory/updateCategory[finish].js b/packages/evershop/src/modules/catalog/api/updateCategory/updateCategory[finish].js index 6583a969f..2d123cfc8 100644 --- a/packages/evershop/src/modules/catalog/api/updateCategory/updateCategory[finish].js +++ b/packages/evershop/src/modules/catalog/api/updateCategory/updateCategory[finish].js @@ -2,6 +2,8 @@ const updateCategory = require('../../services/category/updateCategory'); // eslint-disable-next-line no-unused-vars module.exports = async (request, response, delegate) => { - const category = await updateCategory(request.params.id, request.body); + const category = await updateCategory(request.params.id, request.body, { + routeId: request.currentRoute.id + }); return category; }; diff --git a/packages/evershop/src/modules/catalog/api/updateCollection/[getConnection]updateCollection[finish].js b/packages/evershop/src/modules/catalog/api/updateCollection/[getConnection]updateCollection[finish].js deleted file mode 100644 index e6261bff0..000000000 --- a/packages/evershop/src/modules/catalog/api/updateCollection/[getConnection]updateCollection[finish].js +++ /dev/null @@ -1,31 +0,0 @@ -const { update, select } = require('@evershop/postgres-query-builder'); -const { - INVALID_PAYLOAD -} = require('@evershop/evershop/src/lib/util/httpStatus'); - -module.exports = async (request, response, delegate) => { - const connection = await delegate.getConnection; - - const collection = await select() - .from('collection') - .where('uuid', '=', request.params.id) - .load(connection); - - if (!collection) { - response.status(INVALID_PAYLOAD); - throw new Error('Invalid collection id'); - } - - try { - await update('collection') - .given(request.body) - .where('uuid', '=', request.params.id) - .execute(connection); - } catch (e) { - if (!e.message.includes('No data was provided')) { - throw e; - } - } - - return request.body.id; -}; diff --git a/packages/evershop/src/modules/catalog/api/updateCollection/finish[apiResponse].js b/packages/evershop/src/modules/catalog/api/updateCollection/finish[apiResponse].js index 27b992625..1d6b548e2 100644 --- a/packages/evershop/src/modules/catalog/api/updateCollection/finish[apiResponse].js +++ b/packages/evershop/src/modules/catalog/api/updateCollection/finish[apiResponse].js @@ -1,62 +1,27 @@ -const { - commit, - rollback, - select -} = require('@evershop/postgres-query-builder'); -const { pool } = require('@evershop/evershop/src/lib/postgres/connection'); const { buildUrl } = require('@evershop/evershop/src/lib/router/buildUrl'); -const { - OK, - INTERNAL_SERVER_ERROR -} = require('@evershop/evershop/src/lib/util/httpStatus'); +const { OK } = require('@evershop/evershop/src/lib/util/httpStatus'); // eslint-disable-next-line no-unused-vars module.exports = async (request, response, delegate, next) => { - const promises = []; - Object.keys(delegate).forEach((id) => { - // Check if middleware is async - if (delegate[id] instanceof Promise) { - promises.push(delegate[id]); + const collection = await delegate.updateCollection; + response.status(OK); + response.json({ + data: { + ...collection, + links: [ + { + rel: 'collectionGrid', + href: buildUrl('collectionGrid'), + action: 'GET', + types: ['text/xml'] + }, + { + rel: 'edit', + href: buildUrl('collectionEdit', { id: collection.uuid }), + action: 'GET', + types: ['text/xml'] + } + ] } }); - - const connection = await delegate.getConnection; - const results = await Promise.allSettled(promises); - const rejected = results.find((r) => r.status === 'rejected'); - if (!rejected) { - await commit(connection); - response.status(OK); - const query = select().from('collection'); - const collection = await query - .where('uuid', '=', request.params.id) - .load(pool); - - response.json({ - data: { - ...collection, - links: [ - { - rel: 'collectionGrid', - href: buildUrl('collectionGrid'), - action: 'GET', - types: ['text/xml'] - } - ] - } - }); - } else { - await rollback(connection); - response.status( - response.statusCode !== 200 ? response.statusCode : INTERNAL_SERVER_ERROR - ); - response.json({ - error: { - status: - response.statusCode !== 200 - ? response.statusCode - : INTERNAL_SERVER_ERROR, - message: rejected.reason.message - } - }); - } }; diff --git a/packages/evershop/src/modules/catalog/api/updateCollection/getConnection[finish].js b/packages/evershop/src/modules/catalog/api/updateCollection/getConnection[finish].js deleted file mode 100644 index f2bb244d0..000000000 --- a/packages/evershop/src/modules/catalog/api/updateCollection/getConnection[finish].js +++ /dev/null @@ -1,12 +0,0 @@ -const { startTransaction } = require('@evershop/postgres-query-builder'); -const { - getConnection -} = require('@evershop/evershop/src/lib/postgres/connection'); - -// eslint-disable-next-line no-unused-vars -module.exports = async (request, response) => { - const connection = await getConnection(); - await startTransaction(connection); - - return connection; -}; diff --git a/packages/evershop/src/modules/catalog/api/updateCollection/payloadSchema.json b/packages/evershop/src/modules/catalog/api/updateCollection/payloadSchema.json index 9aa1e43f4..c3aaf8829 100644 --- a/packages/evershop/src/modules/catalog/api/updateCollection/payloadSchema.json +++ b/packages/evershop/src/modules/catalog/api/updateCollection/payloadSchema.json @@ -1,14 +1,9 @@ { "type": "object", "properties": { - "name": { - "type": "string" - }, "description": { - "type": "string" - }, - "code": { - "type": "string" + "type": "string", + "skipEscape": true } }, "additionalProperties": true diff --git a/packages/evershop/src/modules/catalog/api/updateCollection/updateCollection[finish].js b/packages/evershop/src/modules/catalog/api/updateCollection/updateCollection[finish].js new file mode 100644 index 000000000..234ca1ddc --- /dev/null +++ b/packages/evershop/src/modules/catalog/api/updateCollection/updateCollection[finish].js @@ -0,0 +1,9 @@ +const updateCollection = require('../../services/collection/updateCollection'); + +// eslint-disable-next-line no-unused-vars +module.exports = async (request, response, delegate) => { + const collection = await updateCollection(request.params.id, request.body, { + routeId: request.currentRoute.id + }); + return collection; +}; diff --git a/packages/evershop/src/modules/catalog/api/updateProduct/updateProduct[finish].js b/packages/evershop/src/modules/catalog/api/updateProduct/updateProduct[finish].js index defd8f2ab..8d4738a5d 100644 --- a/packages/evershop/src/modules/catalog/api/updateProduct/updateProduct[finish].js +++ b/packages/evershop/src/modules/catalog/api/updateProduct/updateProduct[finish].js @@ -2,6 +2,8 @@ const updateProduct = require('../../services/product/updateProduct'); // eslint-disable-next-line no-unused-vars module.exports = async (request, response, delegate) => { - const product = await updateProduct(request.params.id, request.body); + const product = await updateProduct(request.params.id, request.body, { + routeId: request.currentRoute.id + }); return product; }; diff --git a/packages/evershop/src/modules/catalog/services/attribute/createProductAttribute.js b/packages/evershop/src/modules/catalog/services/attribute/createProductAttribute.js index 56c706a87..4997c0cbf 100644 --- a/packages/evershop/src/modules/catalog/services/attribute/createProductAttribute.js +++ b/packages/evershop/src/modules/catalog/services/attribute/createProductAttribute.js @@ -96,46 +96,59 @@ async function insertAttributeData(data, connection) { /** * Create attribute service. This service will create a attribute with all related data * @param {Object} data + * @param {Object} connection */ -async function createAttribute(data) { +async function createAttribute(data, connection) { + const attributeData = await getValue('attributeDataBeforeCreate', data); + // Validate attribute data + validateAttributeDataBeforeInsert(attributeData); + + // Insert attribute data + const attribute = await hookable(insertAttributeData, { connection })( + attributeData, + connection + ); + + // Save attribute groups + await hookable(insertAttributeGroups, { connection })( + attribute.insertId, + attributeData.groups || [], + connection + ); + + // Save attribute options + await hookable(insertAttributeOptions, { connection })( + attribute.insertId, + attribute.type, + attribute.attribute_code, + attributeData.options || [], + connection + ); + + return attribute; +} + +module.exports = async (data, context) => { const connection = await getConnection(); await startTransaction(connection); try { - const attributeData = await getValue('attributeDataBeforeCreate', data); - // Validate attribute data - validateAttributeDataBeforeInsert(attributeData); - - // Insert attribute data - const attribute = await hookable(insertAttributeData, { connection })( - attributeData, + const hookContext = { connection - ); - - // Save attribute groups - await hookable(insertAttributeGroups, { connection })( - attribute.insertId, - attributeData.groups || [], - connection - ); - - // Save attribute options - await hookable(insertAttributeOptions, { connection })( - attribute.insertId, - attribute.type, - attribute.attribute_code, - attributeData.options || [], + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const result = await hookable(createAttribute, hookContext)( + data, connection ); - await commit(connection); - return attribute; + return result; } catch (e) { await rollback(connection); throw e; } -} - -module.exports = async (data) => { - const result = await hookable(createAttribute)(data); - return result; }; diff --git a/packages/evershop/src/modules/catalog/services/attribute/deleteProductAttribute.js b/packages/evershop/src/modules/catalog/services/attribute/deleteProductAttribute.js new file mode 100644 index 000000000..85bb7e61a --- /dev/null +++ b/packages/evershop/src/modules/catalog/services/attribute/deleteProductAttribute.js @@ -0,0 +1,54 @@ +const { hookable } = require('@evershop/evershop/src/lib/util/hookable'); +const { + startTransaction, + commit, + rollback, + select, + del +} = require('@evershop/postgres-query-builder'); +const { + getConnection +} = require('@evershop/evershop/src/lib/postgres/connection'); + +/** + * Delete attribute service. This service will delete an attribute with all related data + * @param {String} uuid + * @param {Object} connection + */ +async function deleteAttribute(uuid, connection) { + const attribute = await select() + .from('attribute') + .where('uuid', '=', uuid) + .load(connection); + + if (!attribute) { + throw new Error('Invalid attribute id'); + } + await del('attribute').where('uuid', '=', uuid).execute(connection); + return attribute; +} + +module.exports = async (uuid, context) => { + const connection = await getConnection(); + await startTransaction(connection); + try { + const hookContext = { + connection + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const collection = await hookable(deleteAttribute, hookContext)( + uuid, + connection + ); + await commit(connection); + return collection; + } catch (e) { + await rollback(connection); + throw e; + } +}; diff --git a/packages/evershop/src/modules/catalog/services/attribute/updateProductAttribute.js b/packages/evershop/src/modules/catalog/services/attribute/updateProductAttribute.js index 402596087..cb2c9afa2 100644 --- a/packages/evershop/src/modules/catalog/services/attribute/updateProductAttribute.js +++ b/packages/evershop/src/modules/catalog/services/attribute/updateProductAttribute.js @@ -163,52 +163,67 @@ async function updateAttributeData(uuid, data, connection) { /** * Update attribute service. This service will update a attribute with all related data + * @param {String} uuid * @param {Object} data + * @param {Object} connection */ -async function updateAttribute(uuid, data) { - const connection = await getConnection(); - await startTransaction(connection); - try { - const attributeData = await getValue('attributeDataBeforeUpdate', data); - // Delete the attribute_code and type from the data object, we do not allow to update these fields - delete attributeData.attribute_code; - delete attributeData.type; +async function updateAttribute(uuid, data, connection) { + const attributeData = await getValue('attributeDataBeforeUpdate', data); + // Delete the attribute_code and type from the data object, we do not allow to update these fields + delete attributeData.attribute_code; + delete attributeData.type; + + // Validate attribute data + validateAttributeDataBeforeInsert(attributeData); + + // Insert attribute data + const attribute = await hookable(updateAttributeData, { connection })( + uuid, + attributeData, + connection + ); - // Validate attribute data - validateAttributeDataBeforeInsert(attributeData); + // Save attribute groups + await hookable(updateAttributeGroups, { connection })( + attribute.attribute_id, + attributeData.groups || [], + connection + ); - // Insert attribute data - const attribute = await hookable(updateAttributeData, { connection })( - uuid, - attributeData, - connection - ); + // Save attribute options + await hookable(updateAttributeOptions, { connection })( + attribute.attribute_id, + attribute.type, + attribute.attribute_code, + attributeData.options || [], + connection + ); - // Save attribute groups - await hookable(updateAttributeGroups, { connection })( - attribute.attribute_id, - attributeData.groups || [], - connection - ); + return attribute; +} - // Save attribute options - await hookable(updateAttributeOptions, { connection })( - attribute.attribute_id, - attribute.type, - attribute.attribute_code, - attributeData.options || [], +module.exports = async (uuid, data, context) => { + const connection = await getConnection(); + await startTransaction(connection); + try { + const hookContext = { + connection + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const collection = await hookable(updateAttribute, hookContext)( + uuid, + data, connection ); - await commit(connection); - return attribute; + return collection; } catch (e) { await rollback(connection); throw e; } -} - -module.exports = async (uuid, data) => { - const result = await hookable(updateAttribute)(uuid, data); - return result; }; diff --git a/packages/evershop/src/modules/catalog/services/category/createCategory.js b/packages/evershop/src/modules/catalog/services/category/createCategory.js index b33bbd8a7..e1a496830 100644 --- a/packages/evershop/src/modules/catalog/services/category/createCategory.js +++ b/packages/evershop/src/modules/catalog/services/category/createCategory.js @@ -61,30 +61,43 @@ async function insertCategoryData(data, connection) { /** * Create category service. This service will create a category with all related data * @param {Object} data + * @param {Object} connection */ -async function createCategory(data) { +async function createCategory(data, connection) { + const categoryData = await getValue('categoryDataBeforeCreate', data); + // Validate category data + validateCategoryDataBeforeInsert(categoryData); + + // Insert category data + const category = await hookable(insertCategoryData, { connection })( + categoryData, + connection + ); + + return category; +} + +module.exports = async (data, context) => { const connection = await getConnection(); await startTransaction(connection); try { - const categoryData = await getValue('categoryDataBeforeCreate', data); - // Validate category data - validateCategoryDataBeforeInsert(categoryData); - - // Insert category data - const category = await hookable(insertCategoryData, { connection })( - categoryData, + const hookContext = { + connection + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const result = await hookable(createCategory, hookContext)( + data, connection ); - await commit(connection); - return category; + return result; } catch (e) { await rollback(connection); throw e; } -} - -module.exports = async (data) => { - const result = await hookable(createCategory)(data); - return result; }; diff --git a/packages/evershop/src/modules/catalog/services/category/deleteCategory.js b/packages/evershop/src/modules/catalog/services/category/deleteCategory.js new file mode 100644 index 000000000..e45072bbd --- /dev/null +++ b/packages/evershop/src/modules/catalog/services/category/deleteCategory.js @@ -0,0 +1,60 @@ +const { hookable } = require('@evershop/evershop/src/lib/util/hookable'); +const { + startTransaction, + commit, + rollback, + select, + del +} = require('@evershop/postgres-query-builder'); +const { + getConnection +} = require('@evershop/evershop/src/lib/postgres/connection'); + +/** + * Delete category service. This service will delete a category with all related data + * @param {String} uuid + * @param {Object} connection + */ +async function deleteCategory(uuid, connection) { + const query = select().from('category'); + query + .leftJoin('category_description') + .on( + 'category_description.category_description_category_id', + '=', + 'category.category_id' + ); + + const category = await query.where('uuid', '=', uuid).load(connection); + + if (!category) { + throw new Error('Invalid category id'); + } + await del('category').where('uuid', '=', uuid).execute(connection); + return category; +} + +module.exports = async (uuid, context) => { + const connection = await getConnection(); + await startTransaction(connection); + try { + const hookContext = { + connection + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const collection = await hookable(deleteCategory, hookContext)( + uuid, + connection + ); + await commit(connection); + return collection; + } catch (e) { + await rollback(connection); + throw e; + } +}; diff --git a/packages/evershop/src/modules/catalog/services/category/updateCategory.js b/packages/evershop/src/modules/catalog/services/category/updateCategory.js index 516f2cbc5..81029d2e7 100644 --- a/packages/evershop/src/modules/catalog/services/category/updateCategory.js +++ b/packages/evershop/src/modules/catalog/services/category/updateCategory.js @@ -66,32 +66,47 @@ async function updateCategoryData(uuid, data, connection) { /** * Update category service. This service will update a category with all related data + * @param {String} uuid * @param {Object} data + * @param {Object} connection */ -async function updateCategory(uuid, data) { +async function updateCategory(uuid, data, connection) { + const categoryData = await getValue('categoryDataBeforeUpdate', data); + // Validate category data + validateCategoryDataBeforeInsert(categoryData); + + // Insert category data + const category = await hookable(updateCategoryData, { connection })( + uuid, + categoryData, + connection + ); + + return category; +} + +module.exports = async (uuid, data, context) => { const connection = await getConnection(); await startTransaction(connection); try { - const categoryData = await getValue('categoryDataBeforeUpdate', data); - // Validate category data - validateCategoryDataBeforeInsert(categoryData); - - // Insert category data - const category = await hookable(updateCategoryData, { connection })( + const hookContext = { + connection + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const collection = await hookable(updateCategory, hookContext)( uuid, - categoryData, + data, connection ); - await commit(connection); - return category; + return collection; } catch (e) { await rollback(connection); throw e; } -} - -module.exports = async (uuid, data) => { - const result = await hookable(updateCategory)(uuid, data); - return result; }; diff --git a/packages/evershop/src/modules/catalog/services/collection/collectionDataSchema.json b/packages/evershop/src/modules/catalog/services/collection/collectionDataSchema.json new file mode 100644 index 000000000..335723b06 --- /dev/null +++ b/packages/evershop/src/modules/catalog/services/collection/collectionDataSchema.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string", + "skipEscape": true + }, + "code": { + "type": "string" + } + }, + "additionalProperties": true +} diff --git a/packages/evershop/src/modules/catalog/services/collection/createCollection.js b/packages/evershop/src/modules/catalog/services/collection/createCollection.js new file mode 100644 index 000000000..4bce4abd0 --- /dev/null +++ b/packages/evershop/src/modules/catalog/services/collection/createCollection.js @@ -0,0 +1,81 @@ +const { hookable } = require('@evershop/evershop/src/lib/util/hookable'); +const { + getValueSync, + getValue +} = require('@evershop/evershop/src/lib/util/registry'); +const { + startTransaction, + commit, + rollback, + insert +} = require('@evershop/postgres-query-builder'); +const { + getConnection +} = require('@evershop/evershop/src/lib/postgres/connection'); +const { getAjv } = require('../../../base/services/getAjv'); +const collectionDataSchema = require('./collectionDataSchema.json'); + +function validateCollectionDataBeforeInsert(data) { + const ajv = getAjv(); + collectionDataSchema.required = ['name', 'description', 'code']; + const jsonSchema = getValueSync( + 'createCollectionDataJsonSchema', + collectionDataSchema + ); + const validate = ajv.compile(jsonSchema); + const valid = validate(data); + if (valid) { + return data; + } else { + throw new Error(validate.errors[0].message); + } +} + +async function insertCollectionData(data, connection) { + const result = await insert('collection').given(data).execute(connection); + return result; +} + +/** + * Create collection service. This service will create a collection with all related data + * @param {Object} data + * @param {Object} connection + */ +async function createCollection(data, connection) { + const collectionData = await getValue('collectionDataBeforeCreate', data); + // Validate collection data + validateCollectionDataBeforeInsert(collectionData); + + // Insert collection data + const collection = await hookable(insertCollectionData, { connection })( + collectionData, + connection + ); + + return collection; +} + +module.exports = async (data, context) => { + const connection = await getConnection(); + await startTransaction(connection); + try { + const hookContext = { + connection + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const result = await hookable(createCollection, hookContext)( + data, + connection + ); + await commit(connection); + return result; + } catch (e) { + await rollback(connection); + throw e; + } +}; diff --git a/packages/evershop/src/modules/catalog/services/collection/deleteCollection.js b/packages/evershop/src/modules/catalog/services/collection/deleteCollection.js new file mode 100644 index 000000000..47bc20011 --- /dev/null +++ b/packages/evershop/src/modules/catalog/services/collection/deleteCollection.js @@ -0,0 +1,52 @@ +const { hookable } = require('@evershop/evershop/src/lib/util/hookable'); +const { + startTransaction, + commit, + rollback, + select, + del +} = require('@evershop/postgres-query-builder'); +const { + getConnection +} = require('@evershop/evershop/src/lib/postgres/connection'); + +/** + * Delete collection service. This service will delete a collection with all related data + * @param {String} uuid + * @param {Object} connection + */ +async function deleteCollection(uuid, connection) { + const query = select().from('collection'); + const collection = await query.where('uuid', '=', uuid).load(connection); + + if (!collection) { + throw new Error('Invalid collection id'); + } + await del('collection').where('uuid', '=', uuid).execute(connection); + return collection; +} + +module.exports = async (uuid, context) => { + const connection = await getConnection(); + await startTransaction(connection); + try { + const hookContext = { + connection + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const collection = await hookable(deleteCollection, hookContext)( + uuid, + connection + ); + await commit(connection); + return collection; + } catch (e) { + await rollback(connection); + throw e; + } +}; diff --git a/packages/evershop/src/modules/catalog/services/collection/updateCollection.js b/packages/evershop/src/modules/catalog/services/collection/updateCollection.js new file mode 100644 index 000000000..40850b721 --- /dev/null +++ b/packages/evershop/src/modules/catalog/services/collection/updateCollection.js @@ -0,0 +1,105 @@ +const { hookable } = require('@evershop/evershop/src/lib/util/hookable'); +const { + getValueSync, + getValue +} = require('@evershop/evershop/src/lib/util/registry'); +const { + startTransaction, + commit, + rollback, + update, + select +} = require('@evershop/postgres-query-builder'); +const { + getConnection +} = require('@evershop/evershop/src/lib/postgres/connection'); +const { getAjv } = require('../../../base/services/getAjv'); +const collectionDataSchema = require('./collectionDataSchema.json'); + +function validateCollectionDataBeforeInsert(data) { + const ajv = getAjv(); + collectionDataSchema.required = []; + const jsonSchema = getValueSync( + 'updateCollectionDataJsonSchema', + collectionDataSchema + ); + const validate = ajv.compile(jsonSchema); + const valid = validate(data); + if (valid) { + return data; + } else { + throw new Error(validate.errors[0].message); + } +} + +async function updateCollectionData(uuid, data, connection) { + const collection = await select() + .from('collection') + .where('uuid', '=', uuid) + .load(connection); + + if (!collection) { + throw new Error('Requested collection not found'); + } + + try { + const result = await update('collection') + .given(data) + .where('uuid', '=', uuid) + .execute(connection); + + return result; + } catch (e) { + if (!e.message.includes('No data was provided')) { + throw e; + } else { + return collection; + } + } +} + +/** + * Update collection service. This service will update a collection with all related data + * @param {Object} data + * @param {Object} connection + */ +async function updateCollection(uuid, data, connection) { + const collectionData = await getValue('collectionDataBeforeUpdate', data); + // Validate collection data + validateCollectionDataBeforeInsert(collectionData); + + // Insert collection data + const collection = await hookable(updateCollectionData, { connection })( + uuid, + collectionData, + connection + ); + + return collection; +} + +module.exports = async (uuid, data, context) => { + const connection = await getConnection(); + await startTransaction(connection); + try { + const hookContext = { + connection + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const collection = await hookable(updateCollection, hookContext)( + uuid, + data, + connection + ); + await commit(connection); + return collection; + } catch (e) { + await rollback(connection); + throw e; + } +}; diff --git a/packages/evershop/src/modules/catalog/services/product/createProduct.js b/packages/evershop/src/modules/catalog/services/product/createProduct.js index 0723ba893..18aa39ff9 100644 --- a/packages/evershop/src/modules/catalog/services/product/createProduct.js +++ b/packages/evershop/src/modules/catalog/services/product/createProduct.js @@ -176,50 +176,61 @@ async function insertProductData(data, connection) { /** * Create product service. This service will create a product with all related data * @param {Object} data + * @param {Object} connection */ -async function createProduct(data) { - const connection = await getConnection(); - await startTransaction(connection); - try { - const productData = await getValue('productDataBeforeCreate', data); +async function createProduct(data, connection) { + const productData = await getValue('productDataBeforeCreate', data); - // Validate product data - validateProductDataBeforeInsert(productData); + // Validate product data + validateProductDataBeforeInsert(productData); - // Insert product data - const product = await hookable(insertProductData, { connection })( - productData, - connection - ); + // Insert product data + const product = await hookable(insertProductData, { connection })( + productData, + connection + ); - // Insert product inventory - await hookable(insertProductInventory, { connection, product })( - productData, - product.insertId, - connection - ); - - // Insert product attributes - await hookable(insertProductAttributes, { - connection, - product - })(productData.attributes || [], product.insertId, connection); - - // Insert product images - await hookable(insertProductImages, { connection, product })( - productData.images || [], - product.insertId, + // Insert product inventory + await hookable(insertProductInventory, { connection, product })( + productData, + product.insertId, + connection + ); + + // Insert product attributes + await hookable(insertProductAttributes, { + connection, + product + })(productData.attributes || [], product.insertId, connection); + + // Insert product images + await hookable(insertProductImages, { connection, product })( + productData.images || [], + product.insertId, + connection + ); + + return product; +} + +module.exports = async (data, context) => { + const connection = await getConnection(); + await startTransaction(connection); + try { + const hookContext = { connection - ); + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const result = await hookable(createProduct, hookContext)(data, connection); await commit(connection); - return product; + return result; } catch (e) { await rollback(connection); throw e; } -} - -module.exports = async (data) => { - const result = await hookable(createProduct)(data); - return result; }; diff --git a/packages/evershop/src/modules/catalog/services/product/deleteProduct.js b/packages/evershop/src/modules/catalog/services/product/deleteProduct.js new file mode 100644 index 000000000..80feb3639 --- /dev/null +++ b/packages/evershop/src/modules/catalog/services/product/deleteProduct.js @@ -0,0 +1,60 @@ +const { hookable } = require('@evershop/evershop/src/lib/util/hookable'); +const { + startTransaction, + commit, + rollback, + select, + del +} = require('@evershop/postgres-query-builder'); +const { + getConnection +} = require('@evershop/evershop/src/lib/postgres/connection'); + +/** + * Delete product service. This service will delete a product with all related data + * @param {String} uuid + * @param {Object} connection + */ +async function deleteProduct(uuid, connection) { + const query = select().from('product'); + query + .leftJoin('product_description') + .on( + 'product_description.product_description_product_id', + '=', + 'product.product_id' + ); + + const product = await query.where('uuid', '=', uuid).load(connection); + + if (!product) { + throw new Error('Invalid product id'); + } + await del('product').where('uuid', '=', uuid).execute(connection); + return product; +} + +module.exports = async (uuid, context) => { + const connection = await getConnection(); + await startTransaction(connection); + try { + const hookContext = { + connection + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const collection = await hookable(deleteProduct, hookContext)( + uuid, + connection + ); + await commit(connection); + return collection; + } catch (e) { + await rollback(connection); + throw e; + } +}; diff --git a/packages/evershop/src/modules/catalog/services/product/updateProduct.js b/packages/evershop/src/modules/catalog/services/product/updateProduct.js index 3cd2ab7a2..718e87804 100644 --- a/packages/evershop/src/modules/catalog/services/product/updateProduct.js +++ b/packages/evershop/src/modules/catalog/services/product/updateProduct.js @@ -290,64 +290,80 @@ async function updateProductData(uuid, data, connection) { * Update product service. This service will update a product with all related data * @param {String} uuid * @param {Object} data + * @param {Object} connection */ -async function updateProduct(uuid, data) { - const connection = await getConnection(); - await startTransaction(connection); - const product = await select() +async function updateProduct(uuid, data, connection) { + const currentProduct = await select() .from('product') .where('uuid', '=', uuid) .load(connection); - if (!product) { + if (!currentProduct) { throw new Error('Requested product does not exist'); } - try { - const productData = await getValue('productDataBeforeUpdate', data); - // Validate product data - validateProductDataBeforeUpdate(productData); + const productData = await getValue('productDataBeforeUpdate', data); - // Insert product data - const product = await hookable(updateProductData, { connection })( - uuid, - productData, - connection - ); + // Validate product data + validateProductDataBeforeUpdate(productData); - // Update product inventory - await hookable(updateProductInventory, { connection, product })( - productData, - product.product_id, - connection - ); + // Insert product data + const product = await hookable(updateProductData, { connection })( + uuid, + productData, + connection + ); - // Update product attributes - await hookable(updateProductAttributes, { - connection, - product - })( - productData.attributes || [], - product.product_id, - product.variant_group_id, - connection - ); + // Update product inventory + await hookable(updateProductInventory, { connection, product })( + productData, + product.product_id, + connection + ); - // Insert product images - await hookable(updateProductImages, { connection, product })( - productData.images || [], - product.product_id, + // Update product attributes + await hookable(updateProductAttributes, { + connection, + product + })( + productData.attributes || [], + product.product_id, + product.variant_group_id, + connection + ); + + // Insert product images + await hookable(updateProductImages, { connection, product })( + productData.images || [], + product.product_id, + connection + ); + + return product; +} + +module.exports = async (uuid, data, context) => { + const connection = await getConnection(); + await startTransaction(connection); + try { + const hookContext = { + connection + }; + // Make sure the context is either not provided or is an object + if (context && typeof context !== 'object') { + throw new Error('Context must be an object'); + } + // Merge hook context with context + Object.assign(hookContext, context); + const collection = await hookable(updateProduct, hookContext)( + uuid, + data, connection ); await commit(connection); - return product; + return collection; } catch (e) { await rollback(connection); throw e; } -} - -module.exports = async (uuid, data) => { - const result = await hookable(updateProduct)(uuid, data); - return result; };