diff --git a/packages/medusa-core-utils/src/compare-objects.js b/packages/medusa-core-utils/src/compare-objects.js new file mode 100644 index 0000000000..d2d3a95e68 --- /dev/null +++ b/packages/medusa-core-utils/src/compare-objects.js @@ -0,0 +1,17 @@ +import _ from "lodash" + +function compareObjectsByProp(object1, object2, prop) { + if (Array.isArray(object1[prop])) { + object2[prop] = object2[prop].map(({ _id, ...rest }) => rest) + return ( + _.differenceWith(object1[prop], object2[prop], _.isEqual).length === 0 + ) + } else if (typeof object1[prop] === "object") { + delete object2[prop]._id + return _.isEqual(object1[prop], object2[prop]) + } else { + return object1[prop] === object2[prop] + } +} + +export default compareObjectsByProp diff --git a/packages/medusa-core-utils/src/index.js b/packages/medusa-core-utils/src/index.js index 1ba0b8ab13..c8b5f91718 100644 --- a/packages/medusa-core-utils/src/index.js +++ b/packages/medusa-core-utils/src/index.js @@ -2,3 +2,4 @@ export { default as Validator } from "./validator" export { default as MedusaError } from "./errors" export { default as getConfigFile } from "./get-config-file" export { default as createRequireFromPath } from "./create-require-from-path" +export { default as compareObjectsByProp } from "./compare-objects" diff --git a/packages/medusa-interfaces/src/base-model.js b/packages/medusa-interfaces/src/base-model.js index 97920fb253..d83611298f 100644 --- a/packages/medusa-interfaces/src/base-model.js +++ b/packages/medusa-interfaces/src/base-model.js @@ -67,8 +67,16 @@ class BaseModel { * @return {Array} the retreived mongoose documents or * an empty array */ - find(query, options) { - return this.mongooseModel_.find(query, options).lean() + find(query, options, offset, limit) { + return this.mongooseModel_ + .find(query, options) + .skip(offset) + .limit(limit) + .lean() + } + + count() { + return this.mongooseModel_.count({}) } /** diff --git a/packages/medusa-plugin-contentful/src/api/routes/hooks/index.js b/packages/medusa-plugin-contentful/src/api/routes/hooks/index.js index 97b2bc4787..ec374b2fbc 100644 --- a/packages/medusa-plugin-contentful/src/api/routes/hooks/index.js +++ b/packages/medusa-plugin-contentful/src/api/routes/hooks/index.js @@ -7,7 +7,11 @@ const route = Router() export default (app) => { app.use("/hooks", route) - route.post("/contentful", middlewares.wrap(require("./contentful").default)) + route.post( + "/contentful", + bodyParser.json(), + middlewares.wrap(require("./contentful").default) + ) return app } diff --git a/packages/medusa-plugin-contentful/src/services/contentful.js b/packages/medusa-plugin-contentful/src/services/contentful.js index a81cfa2cb9..74c416454a 100644 --- a/packages/medusa-plugin-contentful/src/services/contentful.js +++ b/packages/medusa-plugin-contentful/src/services/contentful.js @@ -168,7 +168,9 @@ class ContentfulService extends BaseService { const variantEntries = await this.getVariantEntries_(product._id) const variantLinks = this.getVariantLinks_(variantEntries) - productEntry.fields = _.assignIn(productEntry.fields, { + + const productEntryFields = { + ...productEntry.fields, title: { "en-US": product.title, }, @@ -181,7 +183,9 @@ class ContentfulService extends BaseService { objectId: { "en-US": product._id, }, - }) + } + + productEntry.fields = productEntryFields const updatedEntry = await productEntry.update() const publishedEntry = await updatedEntry.publish() @@ -218,7 +222,8 @@ class ContentfulService extends BaseService { return this.createProductVariantInContentful(variant) } - variantEntry.fields = _.assignIn(variantEntry.fields, { + const variantEntryFields = { + ...variantEntry.fields, title: { "en-US": variant.title, }, @@ -234,7 +239,9 @@ class ContentfulService extends BaseService { objectId: { "en-US": variant._id, }, - }) + } + + variantEntry.fields = variantEntryFields const updatedEntry = await variantEntry.update() const publishedEntry = await updatedEntry.publish() diff --git a/packages/medusa/package.json b/packages/medusa/package.json index abd8ba48cd..72784184b8 100644 --- a/packages/medusa/package.json +++ b/packages/medusa/package.json @@ -75,4 +75,4 @@ "winston": "^3.2.1" }, "gitHead": "27d4e07c5251e43ba6be2d5fa35f1d5287b11043" -} +} \ No newline at end of file diff --git a/packages/medusa/src/api/routes/admin/discounts/update-discount.js b/packages/medusa/src/api/routes/admin/discounts/update-discount.js index 0b9f930d09..b2941cb376 100644 --- a/packages/medusa/src/api/routes/admin/discounts/update-discount.js +++ b/packages/medusa/src/api/routes/admin/discounts/update-discount.js @@ -3,8 +3,9 @@ import { MedusaError, Validator } from "medusa-core-utils" export default async (req, res) => { const { discount_id } = req.params const schema = Validator.object().keys({ - code: Validator.string().required(), + code: Validator.string().optional(), is_dynamic: Validator.boolean().default(false), + is_giftcard: Validator.boolean().optional(), discount_rule: Validator.object() .keys({ description: Validator.string().optional(), @@ -14,7 +15,7 @@ export default async (req, res) => { valid_for: Validator.array().items(Validator.string()), usage_limit: Validator.number().optional(), }) - .required(), + .optional(), usage_count: Validator.number().optional(), disabled: Validator.boolean().optional(), starts_at: Validator.date().optional(), diff --git a/packages/medusa/src/api/routes/admin/orders/list-orders.js b/packages/medusa/src/api/routes/admin/orders/list-orders.js index 2ed0a8829f..5bbc976fbe 100644 --- a/packages/medusa/src/api/routes/admin/orders/list-orders.js +++ b/packages/medusa/src/api/routes/admin/orders/list-orders.js @@ -6,19 +6,25 @@ export default async (req, res) => { const queryBuilderService = req.scope.resolve("queryBuilderService") const query = queryBuilderService.buildQuery(req.query, [ + "display_id", "email", "status", "fulfillment_status", "payment_status", ]) - let orders = await orderService.list(query) + const limit = parseInt(req.query.limit) || 0 + const offset = parseInt(req.query.offset) || 0 + + let orders = await orderService.list(query, offset, limit) orders = await Promise.all( orders.map(order => orderService.decorate(order)) ) - res.json({ orders }) + let numOrders = await orderService.count() + + res.json({ orders, total_count: numOrders }) } catch (error) { throw error } diff --git a/packages/medusa/src/api/routes/admin/products/list-products.js b/packages/medusa/src/api/routes/admin/products/list-products.js index 739d643f01..0355ad96c4 100644 --- a/packages/medusa/src/api/routes/admin/products/list-products.js +++ b/packages/medusa/src/api/routes/admin/products/list-products.js @@ -10,7 +10,10 @@ export default async (req, res) => { "description", ]) - let products = await productService.list(query) + const limit = parseInt(req.query.limit) || 0 + const offset = parseInt(req.query.offset) || 0 + + let products = await productService.list(query, offset, limit) products = await Promise.all( products.map( @@ -32,7 +35,10 @@ export default async (req, res) => { ) ) ) - res.json({ products }) + + const numProducts = await productService.count() + + res.json({ products, total_count: numProducts }) } catch (error) { console.log(error) throw error diff --git a/packages/medusa/src/api/routes/store/products/__tests__/get-product.js b/packages/medusa/src/api/routes/store/products/__tests__/get-product.js index e45284eee5..c5d7ea9ac2 100644 --- a/packages/medusa/src/api/routes/store/products/__tests__/get-product.js +++ b/packages/medusa/src/api/routes/store/products/__tests__/get-product.js @@ -10,14 +10,7 @@ describe("Get product by id", () => { beforeAll(async () => { subject = await request( "GET", - `/admin/products/${IdMap.getId("product1")}`, - { - adminSession: { - jwt: { - userId: IdMap.getId("admin_user"), - }, - }, - } + `/store/products/${IdMap.getId("product1")}` ) }) diff --git a/packages/medusa/src/services/__mocks__/middleware.js b/packages/medusa/src/services/__mocks__/middleware.js index b3ccfafdd5..ec2ffe5f81 100644 --- a/packages/medusa/src/services/__mocks__/middleware.js +++ b/packages/medusa/src/services/__mocks__/middleware.js @@ -1,6 +1,7 @@ export const MiddlewareServiceMock = { usePostAuthentication: jest.fn(), usePreAuthentication: jest.fn(), + usePreCartCreation: jest.fn().mockReturnValue([]), getRouters: jest.fn().mockReturnValue([]), } diff --git a/packages/medusa/src/services/order.js b/packages/medusa/src/services/order.js index 9b4f93fbce..857adecf0a 100644 --- a/packages/medusa/src/services/order.js +++ b/packages/medusa/src/services/order.js @@ -145,8 +145,18 @@ class OrderService extends BaseService { * @param {Object} selector - the query object for find * @return {Promise} the result of the find operation */ - list(selector) { - return this.orderModel_.find(selector) + list(selector, offset, limit) { + return this.orderModel_ + .find(selector, {}, offset, limit) + .sort({ created: -1 }) + } + + /** + * Return the total number of documents in database + * @return {Promise} the result of the count operation + */ + count() { + return this.orderModel_.count() } /** @@ -232,14 +242,6 @@ class OrderService extends BaseService { return true } - /** - * @param {Object} selector - the query object for find - * @return {Promise} the result of the find operation - */ - list(selector) { - return this.orderModel_.find(selector) - } - /** * @param {string} orderId - id of the order to complete * @return {Promise} the result of the find operation @@ -247,9 +249,6 @@ class OrderService extends BaseService { async completeOrder(orderId) { const order = await this.retrieve(orderId) - // Capture the payment - await this.capturePayment(orderId) - // Run all other registered events const completeOrderJob = await this.eventBus_.emit( OrderService.Events.COMPLETED, @@ -593,12 +592,7 @@ class OrderService extends BaseService { ) } - // prepare update object const updateFields = { payment_status: "captured" } - const completed = order.fulfillment_status !== "not_fulfilled" - if (completed) { - updateFields.status = "completed" - } const { provider_id, data } = order.payment_method const paymentProvider = await this.paymentProviderService_.retrieveProvider( diff --git a/packages/medusa/src/services/product.js b/packages/medusa/src/services/product.js index a91ed67639..ca951d129b 100644 --- a/packages/medusa/src/services/product.js +++ b/packages/medusa/src/services/product.js @@ -1,6 +1,6 @@ import mongoose from "mongoose" import _ from "lodash" -import { Validator, MedusaError } from "medusa-core-utils" +import { Validator, MedusaError, compareObjectsByProp } from "medusa-core-utils" import { BaseService } from "medusa-interfaces" /** @@ -49,8 +49,16 @@ class ProductService extends BaseService { * @param {Object} selector - the query object for find * @return {Promise} the result of the find operation */ - list(selector) { - return this.productModel_.find(selector) + list(selector, offset, limit) { + return this.productModel_.find(selector, {}, offset, limit) + } + + /** + * Return the total number of documents in database + * @return {Promise} the result of the count operation + */ + count() { + return this.productModel_.count() } /** @@ -153,32 +161,53 @@ class ProductService extends BaseService { await Promise.all( update.variants.map(async variant => { if (variant._id) { + const variantFromDb = existingVariants.find(v => + v._id.equals(variant._id) + ) if (variant.prices && variant.prices.length) { - for (const price of variant.prices) { - if (price.region_id) { - await this.productVariantService_.setRegionPrice( - variant._id, - price.region_id, - price.amount - ) - } else { - await this.productVariantService_.setCurrencyPrice( - variant._id, - price.currency_code, - price.amount - ) + // if equal we dont want to update + const isPricesEqual = compareObjectsByProp( + variant, + variantFromDb, + "prices" + ) + + if (!isPricesEqual) { + for (const price of variant.prices) { + if (price.region_id) { + await this.productVariantService_.setRegionPrice( + variant._id, + price.region_id, + price.amount + ) + } else { + await this.productVariantService_.setCurrencyPrice( + variant._id, + price.currency_code, + price.amount + ) + } } } } if (variant.options && variant.options.length) { - for (const option of variant.options) { - await this.updateOptionValue( - productId, - variant._id, - option.option_id, - option.value - ) + // if equal we dont want to update + const isOptionsEqual = compareObjectsByProp( + variant, + variantFromDb, + "options" + ) + + if (!isOptionsEqual) { + for (const option of variant.options) { + await this.updateOptionValue( + productId, + variant._id, + option.option_id, + option.value + ) + } } } diff --git a/packages/medusa/yarn.lock b/packages/medusa/yarn.lock index 25164ec6c0..f27d08a532 100644 --- a/packages/medusa/yarn.lock +++ b/packages/medusa/yarn.lock @@ -1965,6 +1965,13 @@ cli-width@^2.0.0: resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= +client-sessions@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/client-sessions/-/client-sessions-0.8.0.tgz#a7d8c5558ad5d56f2a199f3533eb654b5df893fd" + integrity sha1-p9jFVYrV1W8qGZ81M+tlS134k/0= + dependencies: + cookies "^0.7.0" + cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -2171,6 +2178,14 @@ cookiejar@^2.1.0: resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== +cookies@^0.7.0: + version "0.7.3" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.7.3.tgz#7912ce21fbf2e8c2da70cf1c3f351aecf59dadfa" + integrity sha512-+gixgxYSgQLTaTIilDHAdlNPZDENDQernEMiIcZpYYP14zgHsCt4Ce1FEjFtcp6GefhozebB6orvhAAWx/IS0A== + dependencies: + depd "~1.1.2" + keygrip "~1.0.3" + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" @@ -4348,6 +4363,11 @@ kareem@2.3.1: resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.3.1.tgz#def12d9c941017fabfb00f873af95e9c99e1be87" integrity sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw== +keygrip@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.0.3.tgz#399d709f0aed2bab0a059e0cdd3a5023a053e1dc" + integrity sha512-/PpesirAIfaklxUzp4Yb7xBper9MwP6hNRA6BGGUFCgbJ+BM5CKBtsoxinNXkLHAr+GXS1/lSlF2rP7cv5Fl+g== + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"