Adds plugin for Contentful synchronization (#75)
Adds plugin for Contentful Will create product and product variants in Contentful, when created in Medusa Closes #47
This commit is contained in:
Generated
+6951
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-proposal-class-properties",
|
||||||
|
"@babel/plugin-transform-instanceof",
|
||||||
|
"@babel/plugin-transform-classes"
|
||||||
|
],
|
||||||
|
"presets": ["@babel/preset-env"],
|
||||||
|
"env": {
|
||||||
|
"test": {
|
||||||
|
"plugins": ["@babel/plugin-transform-runtime"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["prettier"],
|
||||||
|
"extends": ["prettier"],
|
||||||
|
"rules": {
|
||||||
|
"prettier/prettier": "error",
|
||||||
|
"semi": "error",
|
||||||
|
"no-unused-expressions": "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
/lib
|
||||||
|
node_modules
|
||||||
|
.DS_store
|
||||||
|
.env*
|
||||||
|
/*.js
|
||||||
|
!index.js
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
/dist
|
||||||
|
|
||||||
|
/api
|
||||||
|
/services
|
||||||
|
/models
|
||||||
|
/subscribers
|
||||||
|
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
/lib
|
||||||
|
node_modules
|
||||||
|
.DS_store
|
||||||
|
.env*
|
||||||
|
/*.js
|
||||||
|
!index.js
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"endOfLine": "lf",
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": false,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"trailingComma": "es5"
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// noop
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"name": "medusa-plugin-contentful",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Contentful plugin for Medusa Commerce",
|
||||||
|
"main": "index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/medusajs/medusa",
|
||||||
|
"directory": "packages/medusa-plugin-contentful"
|
||||||
|
},
|
||||||
|
"author": "Oliver Juhl",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "^7.7.5",
|
||||||
|
"@babel/core": "^7.7.5",
|
||||||
|
"@babel/node": "^7.7.4",
|
||||||
|
"@babel/plugin-proposal-class-properties": "^7.7.4",
|
||||||
|
"@babel/plugin-transform-instanceof": "^7.8.3",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.7.6",
|
||||||
|
"@babel/preset-env": "^7.7.5",
|
||||||
|
"@babel/register": "^7.7.4",
|
||||||
|
"@babel/runtime": "^7.9.6",
|
||||||
|
"client-sessions": "^0.8.0",
|
||||||
|
"cross-env": "^5.2.1",
|
||||||
|
"eslint": "^6.8.0",
|
||||||
|
"jest": "^25.5.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "babel src -d dist",
|
||||||
|
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||||
|
"watch": "babel -w src --out-dir . --ignore **/__tests__",
|
||||||
|
"test": "jest"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/plugin-transform-classes": "^7.9.5",
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
|
"contentful-management": "^5.27.1",
|
||||||
|
"express": "^4.17.1",
|
||||||
|
"medusa-core-utils": "^0.3.0",
|
||||||
|
"medusa-interfaces": "^0.3.0",
|
||||||
|
"medusa-test-utils": "^0.3.0",
|
||||||
|
"redis": "^3.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { Router } from "express"
|
||||||
|
import hooks from "./routes/hooks"
|
||||||
|
|
||||||
|
export default (container) => {
|
||||||
|
const app = Router()
|
||||||
|
|
||||||
|
hooks(app)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export default (fn) => (...args) => fn(...args).catch(args[2])
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { default as wrap } from "./await-middleware"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
wrap,
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
export default async (req, res) => {
|
||||||
|
try {
|
||||||
|
const contentfulService = req.scope.resolve("contentfulService")
|
||||||
|
|
||||||
|
const contentfulType = req.body.contentType.sys.id
|
||||||
|
|
||||||
|
let updated = {}
|
||||||
|
switch (contentfulType) {
|
||||||
|
case "product":
|
||||||
|
updated = await contentfulService.sendContentfulProductToAdmin(req.body)
|
||||||
|
break
|
||||||
|
case "productVariant":
|
||||||
|
updated = await contentfulService.sendContentfulProductVariantToAdmin(
|
||||||
|
req.body
|
||||||
|
)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(200).send(updated)
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).send(`Webhook error: ${error.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { Router } from "express"
|
||||||
|
import bodyParser from "body-parser"
|
||||||
|
import middlewares from "../../middlewares"
|
||||||
|
|
||||||
|
const route = Router()
|
||||||
|
|
||||||
|
export default (app) => {
|
||||||
|
app.use("/hooks", route)
|
||||||
|
|
||||||
|
route.post("/contentful", middlewares.wrap(require("./contentful").default))
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
@@ -0,0 +1,254 @@
|
|||||||
|
import _ from "lodash"
|
||||||
|
import { BaseService } from "medusa-interfaces"
|
||||||
|
import { createClient } from "contentful-management"
|
||||||
|
import redis from "redis"
|
||||||
|
|
||||||
|
class ContentfulService extends BaseService {
|
||||||
|
constructor(
|
||||||
|
{ productService, productVariantService, eventBusService },
|
||||||
|
options
|
||||||
|
) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
this.productService_ = productService
|
||||||
|
|
||||||
|
this.productVariantService_ = productVariantService
|
||||||
|
|
||||||
|
this.eventBus_ = eventBusService
|
||||||
|
|
||||||
|
this.options_ = options
|
||||||
|
|
||||||
|
this.contentful_ = createClient({
|
||||||
|
accessToken: options.access_token,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.redis_ = redis.createClient()
|
||||||
|
}
|
||||||
|
|
||||||
|
async getIgnoreIds_(type) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.redis_.get(`${type}_ignore_ids`, (err, reply) => {
|
||||||
|
if (err) {
|
||||||
|
return reject(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reply) {
|
||||||
|
return reject("Missing key")
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolve(JSON.parse(reply))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async getContentfulEnvironment_() {
|
||||||
|
try {
|
||||||
|
const space = await this.contentful_.getSpace(this.options_.space_id)
|
||||||
|
const environment = await space.getEnvironment(this.options_.environment)
|
||||||
|
return environment
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVariantEntries_(variantEntryIds) {
|
||||||
|
if (!variantEntryIds) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const environment = await this.getContentfulEnvironment_()
|
||||||
|
return Promise.all(variantEntryIds.map((v) => environment.getEntry(v)))
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getVariantLinks_(variantEntries) {
|
||||||
|
if (!variantEntries) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return variantEntries.map((v) => ({
|
||||||
|
sys: {
|
||||||
|
type: "Link",
|
||||||
|
linkType: "Entry",
|
||||||
|
id: v.sys.id,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
async createProductInContentful(product) {
|
||||||
|
try {
|
||||||
|
const environment = await this.getContentfulEnvironment_()
|
||||||
|
const variantEntries = await this.getVariantEntries_(product.variants)
|
||||||
|
return environment.createEntryWithId("product", product._id, {
|
||||||
|
fields: {
|
||||||
|
title: {
|
||||||
|
"en-US": product.title,
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
"en-US": this.getVariantLinks_(variantEntries),
|
||||||
|
},
|
||||||
|
objectId: {
|
||||||
|
"en-US": product._id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async createProductVariantInContentful(variant) {
|
||||||
|
try {
|
||||||
|
const environment = await this.getContentfulEnvironment_()
|
||||||
|
return environment.createEntryWithId("productVariant", variant._id, {
|
||||||
|
fields: {
|
||||||
|
title: {
|
||||||
|
"en-US": variant.title,
|
||||||
|
},
|
||||||
|
sku: {
|
||||||
|
"en-US": variant.sku,
|
||||||
|
},
|
||||||
|
prices: {
|
||||||
|
"en-US": variant.prices,
|
||||||
|
},
|
||||||
|
objectId: {
|
||||||
|
"en-US": variant._id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateProductInContentful(product) {
|
||||||
|
try {
|
||||||
|
const environment = await this.getContentfulEnvironment_()
|
||||||
|
// check if product exists
|
||||||
|
let productEntry = undefined
|
||||||
|
try {
|
||||||
|
productEntry = await environment.getEntry(product._id)
|
||||||
|
} catch (error) {
|
||||||
|
return this.createProductInContentful(product)
|
||||||
|
}
|
||||||
|
|
||||||
|
const variantEntries = await this.getVariantEntries_(product.variants)
|
||||||
|
productEntry.fields = _.assignIn(productEntry.fields, {
|
||||||
|
title: {
|
||||||
|
"en-US": product.title,
|
||||||
|
},
|
||||||
|
variants: {
|
||||||
|
"en-US": this.getVariantLinks_(variantEntries),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await productEntry.update()
|
||||||
|
const publishedEntry = await productEntry.publish()
|
||||||
|
|
||||||
|
const ignoreIds = await this.getIgnoreIds_("product")
|
||||||
|
if (ignoreIds.includes(publishedEntry.sys.id)) {
|
||||||
|
ignoreIds.filter((id) => id !== publishedEntry.sys.id)
|
||||||
|
} else {
|
||||||
|
this.eventBus_.emit("product.updated", publishedEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return publishedEntry
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateProductVariantInContentful(variant) {
|
||||||
|
try {
|
||||||
|
const environment = await this.getContentfulEnvironment_()
|
||||||
|
// check if product exists
|
||||||
|
let variantEntry = undefined
|
||||||
|
variantEntry = await environment.getEntry(variant._id)
|
||||||
|
// if not, we create a new one
|
||||||
|
if (!variantEntry) {
|
||||||
|
return this.createProductVariantInContentful(variant)
|
||||||
|
}
|
||||||
|
|
||||||
|
variantEntry.fields = _.assignIn(variantEntry.fields, {
|
||||||
|
title: {
|
||||||
|
"en-US": variant.title,
|
||||||
|
},
|
||||||
|
sku: {
|
||||||
|
"en-US": variant.sku,
|
||||||
|
},
|
||||||
|
prices: {
|
||||||
|
"en-US": variant.prices,
|
||||||
|
},
|
||||||
|
objectId: {
|
||||||
|
"en-US": variant._id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await variantEntry.update()
|
||||||
|
const publishedEntry = await variantEntry.publish()
|
||||||
|
|
||||||
|
const ignoreIds = await this.getIgnoreIds_("product_variant")
|
||||||
|
if (ignoreIds.includes(publishedEntry.sys.id)) {
|
||||||
|
ignoreIds.filter((id) => id !== publishedEntry.sys.id)
|
||||||
|
} else {
|
||||||
|
this.eventBus_.emit("product-variant.updated", publishedEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return publishedEntry
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendContentfulProductToAdmin(product) {
|
||||||
|
try {
|
||||||
|
const environment = await this.getContentfulEnvironment_()
|
||||||
|
const productEntry = await environment.getEntry(product.sys.id)
|
||||||
|
|
||||||
|
const ignoreIds = await this.getIgnoreIds_("product")
|
||||||
|
ignoreIds.push(product.sys.id)
|
||||||
|
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
|
||||||
|
|
||||||
|
const updatedProduct = await this.productService_.update(
|
||||||
|
productEntry.objectId,
|
||||||
|
{
|
||||||
|
title: productEntry.fields.title["en-US"],
|
||||||
|
variants: productEntry.fields.variants["en-US"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return updatedProduct
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async sendContentfulProductVariantToAdmin(variant) {
|
||||||
|
try {
|
||||||
|
const environment = await this.getContentfulEnvironment_()
|
||||||
|
const variantEntry = await environment.getEntry(variant.sys.id)
|
||||||
|
|
||||||
|
const ignoreIds = await this.getIgnoreIds_("product_variant")
|
||||||
|
ignoreIds.push(variant.sys.id)
|
||||||
|
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
|
||||||
|
|
||||||
|
const updatedVariant = await this.variantService_.update(
|
||||||
|
variantEntry.objectId,
|
||||||
|
{
|
||||||
|
title: variantEntry.fields.title["en-US"],
|
||||||
|
sku: variantEntry.fields.sku["en-US"],
|
||||||
|
prices: variantEntry.fields.prices["en-US"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return updatedVariant
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContentfulService
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
class ContentfulSubscriber {
|
||||||
|
constructor({ contentfulService, eventBusService }) {
|
||||||
|
this.contentfulService_ = contentfulService
|
||||||
|
this.eventBus_ = eventBusService
|
||||||
|
|
||||||
|
this.eventBus_.subscribe("product-variant.updated", async (data) => {
|
||||||
|
await this.contentfulService_.updateProductVariantInContentful(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.eventBus_.subscribe("product.updated", async (data) => {
|
||||||
|
await this.contentfulService_.updateProductInContentful(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.eventBus_.subscribe("product.created", async (data) => {
|
||||||
|
await this.contentfulService_.createProductVariantInContentful(data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ContentfulSubscriber
|
||||||
@@ -13,6 +13,8 @@ class ProductVariantModel extends BaseModel {
|
|||||||
static schema = {
|
static schema = {
|
||||||
title: { type: String, required: true },
|
title: { type: String, required: true },
|
||||||
prices: { type: [MoneyAmountSchema], default: [], required: true },
|
prices: { type: [MoneyAmountSchema], default: [], required: true },
|
||||||
|
sku: { type: String, default: "" },
|
||||||
|
barcode: { type: String, default: "" },
|
||||||
options: { type: [OptionValueSchema], default: [] },
|
options: { type: [OptionValueSchema], default: [] },
|
||||||
image: { type: String, default: "" },
|
image: { type: String, default: "" },
|
||||||
published: { type: Boolean, default: false },
|
published: { type: Boolean, default: false },
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import ProductVariantService from "../product-variant"
|
|||||||
import { ProductVariantModelMock } from "../../models/__mocks__/product-variant"
|
import { ProductVariantModelMock } from "../../models/__mocks__/product-variant"
|
||||||
import { ProductServiceMock } from "../__mocks__/product"
|
import { ProductServiceMock } from "../__mocks__/product"
|
||||||
import { RegionServiceMock } from "../__mocks__/region"
|
import { RegionServiceMock } from "../__mocks__/region"
|
||||||
|
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||||
|
|
||||||
describe("ProductVariantService", () => {
|
describe("ProductVariantService", () => {
|
||||||
describe("retrieve", () => {
|
describe("retrieve", () => {
|
||||||
@@ -12,6 +13,7 @@ describe("ProductVariantService", () => {
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
res = await productVariantService.retrieve(IdMap.getId("validId"))
|
res = await productVariantService.retrieve(IdMap.getId("validId"))
|
||||||
@@ -38,6 +40,7 @@ describe("ProductVariantService", () => {
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
await productVariantService
|
await productVariantService
|
||||||
@@ -69,6 +72,7 @@ describe("ProductVariantService", () => {
|
|||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
productVariantService.createDraft({
|
productVariantService.createDraft({
|
||||||
@@ -106,6 +110,7 @@ describe("ProductVariantService", () => {
|
|||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
productVariantService.publish(IdMap.getId("variantId"))
|
productVariantService.publish(IdMap.getId("variantId"))
|
||||||
@@ -124,6 +129,7 @@ describe("ProductVariantService", () => {
|
|||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -168,6 +174,7 @@ describe("ProductVariantService", () => {
|
|||||||
describe("decorate", () => {
|
describe("decorate", () => {
|
||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
const fakeVariant = {
|
const fakeVariant = {
|
||||||
@@ -216,6 +223,7 @@ describe("ProductVariantService", () => {
|
|||||||
describe("setMetadata", () => {
|
describe("setMetadata", () => {
|
||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -264,6 +272,7 @@ describe("ProductVariantService", () => {
|
|||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
productService: ProductServiceMock,
|
productService: ProductServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -331,6 +340,7 @@ describe("ProductVariantService", () => {
|
|||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
productService: ProductServiceMock,
|
productService: ProductServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -354,6 +364,7 @@ describe("ProductVariantService", () => {
|
|||||||
describe("delete", () => {
|
describe("delete", () => {
|
||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -373,6 +384,7 @@ describe("ProductVariantService", () => {
|
|||||||
describe("canCoverQuantity", () => {
|
describe("canCoverQuantity", () => {
|
||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -419,6 +431,7 @@ describe("ProductVariantService", () => {
|
|||||||
describe("setCurrencyPrice", () => {
|
describe("setCurrencyPrice", () => {
|
||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -519,6 +532,7 @@ describe("ProductVariantService", () => {
|
|||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
regionService: RegionServiceMock,
|
regionService: RegionServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -619,6 +633,7 @@ describe("ProductVariantService", () => {
|
|||||||
const productVariantService = new ProductVariantService({
|
const productVariantService = new ProductVariantService({
|
||||||
productVariantModel: ProductVariantModelMock,
|
productVariantModel: ProductVariantModelMock,
|
||||||
regionService: RegionServiceMock,
|
regionService: RegionServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
ProductVariantServiceMock,
|
ProductVariantServiceMock,
|
||||||
variants,
|
variants,
|
||||||
} from "../__mocks__/product-variant"
|
} from "../__mocks__/product-variant"
|
||||||
|
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||||
|
|
||||||
describe("ProductService", () => {
|
describe("ProductService", () => {
|
||||||
describe("retrieve", () => {
|
describe("retrieve", () => {
|
||||||
@@ -14,6 +15,7 @@ describe("ProductService", () => {
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
res = await productService.retrieve(IdMap.getId("validId"))
|
res = await productService.retrieve(IdMap.getId("validId"))
|
||||||
@@ -40,6 +42,7 @@ describe("ProductService", () => {
|
|||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
await productService.retrieve(IdMap.getId("failId")).catch(err => {
|
await productService.retrieve(IdMap.getId("failId")).catch(err => {
|
||||||
@@ -70,6 +73,7 @@ describe("ProductService", () => {
|
|||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
productService.createDraft({
|
productService.createDraft({
|
||||||
@@ -108,6 +112,7 @@ describe("ProductService", () => {
|
|||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
productVariantService: ProductVariantServiceMock,
|
productVariantService: ProductVariantServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
productService.publish(IdMap.getId("productId"))
|
productService.publish(IdMap.getId("productId"))
|
||||||
@@ -127,6 +132,7 @@ describe("ProductService", () => {
|
|||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
productVariantService: ProductVariantServiceMock,
|
productVariantService: ProductVariantServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
const fakeProduct = {
|
const fakeProduct = {
|
||||||
@@ -193,6 +199,7 @@ describe("ProductService", () => {
|
|||||||
describe("setMetadata", () => {
|
describe("setMetadata", () => {
|
||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -236,6 +243,7 @@ describe("ProductService", () => {
|
|||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -290,6 +298,7 @@ describe("ProductService", () => {
|
|||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
productVariantService: ProductVariantServiceMock,
|
productVariantService: ProductVariantServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -314,6 +323,7 @@ describe("ProductService", () => {
|
|||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
productVariantService: ProductVariantServiceMock,
|
productVariantService: ProductVariantServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -479,6 +489,7 @@ describe("ProductService", () => {
|
|||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
productVariantService: ProductVariantServiceMock,
|
productVariantService: ProductVariantServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -571,6 +582,7 @@ describe("ProductService", () => {
|
|||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
productVariantService: ProductVariantServiceMock,
|
productVariantService: ProductVariantServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -632,6 +644,7 @@ describe("ProductService", () => {
|
|||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
productVariantService: ProductVariantServiceMock,
|
productVariantService: ProductVariantServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -659,6 +672,7 @@ describe("ProductService", () => {
|
|||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
productVariantService: ProductVariantServiceMock,
|
productVariantService: ProductVariantServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -719,6 +733,7 @@ describe("ProductService", () => {
|
|||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
productVariantService: ProductVariantServiceMock,
|
productVariantService: ProductVariantServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -788,6 +803,7 @@ describe("ProductService", () => {
|
|||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
productVariantService: ProductVariantServiceMock,
|
productVariantService: ProductVariantServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -843,6 +859,7 @@ describe("ProductService", () => {
|
|||||||
const productService = new ProductService({
|
const productService = new ProductService({
|
||||||
productModel: ProductModelMock,
|
productModel: ProductModelMock,
|
||||||
productVariantService: ProductVariantServiceMock,
|
productVariantService: ProductVariantServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -7,6 +7,11 @@ import { Validator, MedusaError } from "medusa-core-utils"
|
|||||||
* @implements BaseService
|
* @implements BaseService
|
||||||
*/
|
*/
|
||||||
class ProductVariantService extends BaseService {
|
class ProductVariantService extends BaseService {
|
||||||
|
static Events = {
|
||||||
|
UPDATED: "product-variant.updated",
|
||||||
|
CREATED: "product-variant.created",
|
||||||
|
}
|
||||||
|
|
||||||
/** @param { productVariantModel: (ProductVariantModel) } */
|
/** @param { productVariantModel: (ProductVariantModel) } */
|
||||||
constructor({ productVariantModel, eventBusService, regionService }) {
|
constructor({ productVariantModel, eventBusService, regionService }) {
|
||||||
super()
|
super()
|
||||||
@@ -73,6 +78,10 @@ class ProductVariantService extends BaseService {
|
|||||||
...productVariant,
|
...productVariant,
|
||||||
published: false,
|
published: false,
|
||||||
})
|
})
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductVariantService.Events.CREATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
})
|
})
|
||||||
@@ -83,9 +92,13 @@ class ProductVariantService extends BaseService {
|
|||||||
* @param {string} variantId - ID of the variant to publish.
|
* @param {string} variantId - ID of the variant to publish.
|
||||||
* @return {Promise} resolves to the creation result.
|
* @return {Promise} resolves to the creation result.
|
||||||
*/
|
*/
|
||||||
publish(variantId) {
|
async publish(variantId) {
|
||||||
return this.productVariantModel_
|
return this.productVariantModel_
|
||||||
.updateOne({ _id: variantId }, { $set: { published: true } })
|
.updateOne({ _id: variantId }, { $set: { published: true } })
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
})
|
})
|
||||||
@@ -123,6 +136,10 @@ class ProductVariantService extends BaseService {
|
|||||||
{ $set: update },
|
{ $set: update },
|
||||||
{ runValidators: true }
|
{ runValidators: true }
|
||||||
)
|
)
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
})
|
})
|
||||||
@@ -163,33 +180,49 @@ class ProductVariantService extends BaseService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.productVariantModel_.updateOne(
|
return this.productVariantModel_
|
||||||
|
.updateOne(
|
||||||
|
{
|
||||||
|
_id: variant._id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
prices: newPrices,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.productVariantModel_
|
||||||
|
.updateOne(
|
||||||
{
|
{
|
||||||
_id: variant._id,
|
_id: variant._id,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
prices: newPrices,
|
prices: [
|
||||||
|
{
|
||||||
|
currency_code: currencyCode,
|
||||||
|
amount,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||||
return this.productVariantModel_.updateOne(
|
return result
|
||||||
{
|
})
|
||||||
_id: variant._id,
|
.catch(err => {
|
||||||
},
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
{
|
})
|
||||||
$set: {
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
currency_code: currencyCode,
|
|
||||||
amount,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -262,39 +295,55 @@ class ProductVariantService extends BaseService {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.productVariantModel_.updateOne(
|
return this.productVariantModel_
|
||||||
|
.updateOne(
|
||||||
|
{
|
||||||
|
_id: variant._id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
prices: newPrices,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the price both for default currency price and for the region
|
||||||
|
return this.productVariantModel_
|
||||||
|
.updateOne(
|
||||||
{
|
{
|
||||||
_id: variant._id,
|
_id: variant._id,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
prices: newPrices,
|
prices: [
|
||||||
|
{
|
||||||
|
region_id: region._id,
|
||||||
|
currency_code: region.currency_code,
|
||||||
|
amount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
currency_code: region.currency_code,
|
||||||
|
amount,
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||||
// Set the price both for default currency price and for the region
|
return result
|
||||||
return this.productVariantModel_.updateOne(
|
})
|
||||||
{
|
.catch(err => {
|
||||||
_id: variant._id,
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
},
|
})
|
||||||
{
|
|
||||||
$set: {
|
|
||||||
prices: [
|
|
||||||
{
|
|
||||||
region_id: region._id,
|
|
||||||
currency_code: region.currency_code,
|
|
||||||
amount,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
currency_code: region.currency_code,
|
|
||||||
amount,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -313,10 +362,18 @@ class ProductVariantService extends BaseService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.productVariantModel_.updateOne(
|
return this.productVariantModel_
|
||||||
{ _id: variantId, "options.option_id": optionId },
|
.updateOne(
|
||||||
{ $set: { "options.$.value": `${optionValue}` } }
|
{ _id: variantId, "options.option_id": optionId },
|
||||||
)
|
{ $set: { "options.$.value": `${optionValue}` } }
|
||||||
|
)
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -340,10 +397,18 @@ class ProductVariantService extends BaseService {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.productVariantModel_.updateOne(
|
return this.productVariantModel_
|
||||||
{ _id: variant._id },
|
.updateOne(
|
||||||
{ $push: { options: { option_id: optionId, value: `${optionValue}` } } }
|
{ _id: variant._id },
|
||||||
)
|
{ $push: { options: { option_id: optionId, value: `${optionValue}` } } }
|
||||||
|
)
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -357,10 +422,18 @@ class ProductVariantService extends BaseService {
|
|||||||
* @return {Promise} the result of the update operation.
|
* @return {Promise} the result of the update operation.
|
||||||
*/
|
*/
|
||||||
async deleteOptionValue(variantId, optionId) {
|
async deleteOptionValue(variantId, optionId) {
|
||||||
return this.productVariantModel_.updateOne(
|
return this.productVariantModel_
|
||||||
{ _id: variantId },
|
.updateOne(
|
||||||
{ $pull: { options: { option_id: optionId } } }
|
{ _id: variantId },
|
||||||
)
|
{ $pull: { options: { option_id: optionId } } }
|
||||||
|
)
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -384,7 +457,7 @@ class ProductVariantService extends BaseService {
|
|||||||
* @param {Object} selector - the query object for find
|
* @param {Object} selector - the query object for find
|
||||||
* @return {Promise} the result of the find operation
|
* @return {Promise} the result of the find operation
|
||||||
*/
|
*/
|
||||||
list(selector) {
|
async list(selector) {
|
||||||
return this.productVariantModel_.find(selector)
|
return this.productVariantModel_.find(selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ import { BaseService } from "medusa-interfaces"
|
|||||||
* @implements BaseService
|
* @implements BaseService
|
||||||
*/
|
*/
|
||||||
class ProductService extends BaseService {
|
class ProductService extends BaseService {
|
||||||
|
static Events = {
|
||||||
|
UPDATED: "product.updated",
|
||||||
|
CREATED: "product.created",
|
||||||
|
}
|
||||||
|
|
||||||
/** @param { productModel: (ProductModel) } */
|
/** @param { productModel: (ProductModel) } */
|
||||||
constructor({ productModel, eventBusService, productVariantService }) {
|
constructor({ productModel, eventBusService, productVariantService }) {
|
||||||
super()
|
super()
|
||||||
@@ -86,12 +91,16 @@ class ProductService extends BaseService {
|
|||||||
* @param {object} product - the product to create
|
* @param {object} product - the product to create
|
||||||
* @return {Promise} resolves to the creation result.
|
* @return {Promise} resolves to the creation result.
|
||||||
*/
|
*/
|
||||||
createDraft(product) {
|
async createDraft(product) {
|
||||||
return this.productModel_
|
return this.productModel_
|
||||||
.create({
|
.create({
|
||||||
...product,
|
...product,
|
||||||
published: false,
|
published: false,
|
||||||
})
|
})
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductService.Events.CREATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
})
|
})
|
||||||
@@ -102,9 +111,13 @@ class ProductService extends BaseService {
|
|||||||
* @param {string} productId - ID of the product to publish.
|
* @param {string} productId - ID of the product to publish.
|
||||||
* @return {Promise} resolves to the creation result.
|
* @return {Promise} resolves to the creation result.
|
||||||
*/
|
*/
|
||||||
publish(productId) {
|
async publish(productId) {
|
||||||
return this.productModel_
|
return this.productModel_
|
||||||
.updateOne({ _id: productId }, { $set: { published: true } })
|
.updateOne({ _id: productId }, { $set: { published: true } })
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
})
|
})
|
||||||
@@ -119,7 +132,7 @@ class ProductService extends BaseService {
|
|||||||
* @param {object} update - an object with the update values.
|
* @param {object} update - an object with the update values.
|
||||||
* @return {Promise} resolves to the update result.
|
* @return {Promise} resolves to the update result.
|
||||||
*/
|
*/
|
||||||
update(productId, update) {
|
async update(productId, update) {
|
||||||
const validatedId = this.validateId_(productId)
|
const validatedId = this.validateId_(productId)
|
||||||
|
|
||||||
if (update.metadata) {
|
if (update.metadata) {
|
||||||
@@ -142,6 +155,10 @@ class ProductService extends BaseService {
|
|||||||
{ $set: update },
|
{ $set: update },
|
||||||
{ runValidators: true }
|
{ runValidators: true }
|
||||||
)
|
)
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
})
|
})
|
||||||
@@ -225,10 +242,15 @@ class ProductService extends BaseService {
|
|||||||
|
|
||||||
const newVariant = await this.productVariantService_.createDraft(variant)
|
const newVariant = await this.productVariantService_.createDraft(variant)
|
||||||
|
|
||||||
return this.productModel_.updateOne(
|
return this.productModel_
|
||||||
{ _id: product._id },
|
.updateOne({ _id: product._id }, { $push: { variants: newVariant._id } })
|
||||||
{ $push: { variants: newVariant._id } }
|
.then(result => {
|
||||||
)
|
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -262,7 +284,7 @@ class ProductService extends BaseService {
|
|||||||
"Default Value"
|
"Default Value"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
).catch(err => {
|
).catch(async err => {
|
||||||
// If any of the variants failed to add the new option value we clean up
|
// If any of the variants failed to add the new option value we clean up
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
product.variants.map(async variantId =>
|
product.variants.map(async variantId =>
|
||||||
@@ -288,7 +310,11 @@ class ProductService extends BaseService {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.catch(err => {
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
.catch(async err => {
|
||||||
// If we failed to update the product clean up its variants
|
// If we failed to update the product clean up its variants
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
product.variants.map(async variantId =>
|
product.variants.map(async variantId =>
|
||||||
@@ -322,14 +348,22 @@ class ProductService extends BaseService {
|
|||||||
return variant
|
return variant
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.productModel_.updateOne(
|
return this.productModel_
|
||||||
{
|
.updateOne(
|
||||||
_id: productId,
|
{
|
||||||
},
|
_id: productId,
|
||||||
{
|
},
|
||||||
$set: { variants: newOrder },
|
{
|
||||||
}
|
$set: { variants: newOrder },
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -363,14 +397,22 @@ class ProductService extends BaseService {
|
|||||||
return option
|
return option
|
||||||
})
|
})
|
||||||
|
|
||||||
return this.productModel_.updateOne(
|
return this.productModel_
|
||||||
{
|
.updateOne(
|
||||||
_id: productId,
|
{
|
||||||
},
|
_id: productId,
|
||||||
{
|
},
|
||||||
$set: { options: newOrder },
|
{
|
||||||
}
|
$set: { options: newOrder },
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -407,15 +449,23 @@ class ProductService extends BaseService {
|
|||||||
const update = {}
|
const update = {}
|
||||||
update["options.$.title"] = title
|
update["options.$.title"] = title
|
||||||
|
|
||||||
return this.productModel_.updateOne(
|
return this.productModel_
|
||||||
{
|
.updateOne(
|
||||||
_id: productId,
|
{
|
||||||
"options._id": optionId,
|
_id: productId,
|
||||||
},
|
"options._id": optionId,
|
||||||
{
|
},
|
||||||
$set: update,
|
{
|
||||||
}
|
$set: update,
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -463,16 +513,23 @@ class ProductService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await this.productModel_.updateOne(
|
const result = await this.productModel_
|
||||||
{ _id: productId },
|
.updateOne(
|
||||||
{
|
{ _id: productId },
|
||||||
$pull: {
|
{
|
||||||
options: {
|
$pull: {
|
||||||
_id: optionId,
|
options: {
|
||||||
|
_id: optionId,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
}
|
)
|
||||||
)
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
|
|
||||||
// If we reached this point, we can delete option value from variants
|
// If we reached this point, we can delete option value from variants
|
||||||
if (product.variants) {
|
if (product.variants) {
|
||||||
@@ -497,14 +554,22 @@ class ProductService extends BaseService {
|
|||||||
|
|
||||||
await this.productVariantService_.delete(variantId)
|
await this.productVariantService_.delete(variantId)
|
||||||
|
|
||||||
return this.productModel_.updateOne(
|
return this.productModel_
|
||||||
{ _id: product._id },
|
.updateOne(
|
||||||
{
|
{ _id: product._id },
|
||||||
$pull: {
|
{
|
||||||
variants: variantId,
|
$pull: {
|
||||||
},
|
variants: variantId,
|
||||||
}
|
},
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateOptionValue(productId, variantId, optionId, value) {
|
async updateOptionValue(productId, variantId, optionId, value) {
|
||||||
@@ -597,6 +662,10 @@ class ProductService extends BaseService {
|
|||||||
const keyPath = `metadata.${key}`
|
const keyPath = `metadata.${key}`
|
||||||
return this.productModel_
|
return this.productModel_
|
||||||
.updateOne({ _id: validatedId }, { $set: { [keyPath]: value } })
|
.updateOne({ _id: validatedId }, { $set: { [keyPath]: value } })
|
||||||
|
.then(result => {
|
||||||
|
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||||
|
return result
|
||||||
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user