Merge branch 'integration/dummy-project' of github.com:medusajs/medusa into integration/dummy-project
This commit is contained in:
Generated
+6951
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-transform-instanceof"
|
||||
"@babel/plugin-transform-instanceof",
|
||||
"@babel/plugin-transform-classes"
|
||||
],
|
||||
"presets": ["@babel/preset-env"],
|
||||
"env": {
|
||||
@@ -9,4 +10,4 @@
|
||||
"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,7 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports["default"] = void 0;
|
||||
|
||||
var _medusaInterfaces = _interopRequireDefault(require("medusa-interfaces"));
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
||||
|
||||
function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } }
|
||||
|
||||
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
||||
|
||||
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
||||
|
||||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
||||
|
||||
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
||||
|
||||
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function () { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
|
||||
|
||||
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
|
||||
|
||||
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
||||
|
||||
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
|
||||
|
||||
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
||||
|
||||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
||||
|
||||
var ManualFulfillmentService = /*#__PURE__*/function (_BaseFulfillmentServi) {
|
||||
_inherits(ManualFulfillmentService, _BaseFulfillmentServi);
|
||||
|
||||
var _super = _createSuper(ManualFulfillmentService);
|
||||
|
||||
function ManualFulfillmentService() {
|
||||
_classCallCheck(this, ManualFulfillmentService);
|
||||
|
||||
return _super.call(this);
|
||||
}
|
||||
|
||||
_createClass(ManualFulfillmentService, [{
|
||||
key: "getFulfillmentOptions",
|
||||
value: function getFulfillmentOptions() {
|
||||
return [{
|
||||
id: "manual-fulfillment"
|
||||
}];
|
||||
}
|
||||
}, {
|
||||
key: "validateFulfillmentData",
|
||||
value: function validateFulfillmentData(data, cart) {
|
||||
return data;
|
||||
}
|
||||
}, {
|
||||
key: "validateOption",
|
||||
value: function validateOption(data) {
|
||||
if (data.id === "manual-fulfillment") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}, {
|
||||
key: "canCalculate",
|
||||
value: function canCalculate() {
|
||||
return false;
|
||||
}
|
||||
}, {
|
||||
key: "calculatePrice",
|
||||
value: function calculatePrice() {
|
||||
throw Error("Manual Fulfillment service cannot calculatePrice");
|
||||
}
|
||||
}, {
|
||||
key: "createOrder",
|
||||
value: function createOrder() {
|
||||
// No data is being sent anywhere
|
||||
return;
|
||||
}
|
||||
}]);
|
||||
|
||||
return ManualFulfillmentService;
|
||||
}(_medusaInterfaces["default"]);
|
||||
|
||||
_defineProperty(ManualFulfillmentService, "identifier", "manual");
|
||||
|
||||
var _default = ManualFulfillmentService;
|
||||
exports["default"] = _default;
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-fulfillment-manual",
|
||||
"version": "0.1.27-alpha.0",
|
||||
"version": "1.0",
|
||||
"description": "A manual fulfillment provider for Medusa",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -23,7 +23,7 @@
|
||||
"jest": "^25.5.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel src --out-dir . --ignore **/__tests__",
|
||||
"build": "babel src -d dist",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||
"watch": "babel -w src --out-dir . --ignore **/__tests__"
|
||||
},
|
||||
@@ -32,7 +32,6 @@
|
||||
"@babel/plugin-transform-instanceof": "^7.8.3",
|
||||
"@babel/runtime": "^7.7.6",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^0.3.0",
|
||||
"medusa-interfaces": "^0.1.27-alpha.0"
|
||||
"medusa-core-utils": "^0.3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FulfillmentService } from "medusa-interfaces"
|
||||
|
||||
class ManualFulfillmentService extends FulfillmentService {
|
||||
class ManualFulfillmentService extends FulfillmentService {
|
||||
static identifier = "manual"
|
||||
|
||||
constructor() {
|
||||
@@ -8,9 +8,11 @@ class ManualFulfillmentService extends FulfillmentService {
|
||||
}
|
||||
|
||||
getFulfillmentOptions() {
|
||||
return [{
|
||||
id: "manual-fulfillment"
|
||||
}]
|
||||
return [
|
||||
{
|
||||
id: "manual-fulfillment",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
validateFulfillmentData(data, cart) {
|
||||
@@ -18,11 +20,7 @@ class ManualFulfillmentService extends FulfillmentService {
|
||||
}
|
||||
|
||||
validateOption(data) {
|
||||
if (data.id === "manual-fulfillment") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
canCalculate() {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"medusa-test-utils": "^0.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel src -d dist",
|
||||
"build": "babel src -d .",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||
"watch": "babel -w src --out-dir . --ignore **/__tests__",
|
||||
"test": "jest"
|
||||
@@ -42,4 +42,4 @@
|
||||
"body-parser": "^1.19.0"
|
||||
},
|
||||
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ export default async (req, res) => {
|
||||
|
||||
let event
|
||||
try {
|
||||
const stripeProviderService = req.resolve("pp_stripe")
|
||||
const stripeProviderService = req.scope.resolve("pp_stripe")
|
||||
event = stripeProviderService.constructWebhookEvent(req.body, signature)
|
||||
} catch (err) {
|
||||
res.status(400).send(`Webhook Error: ${err.message}`)
|
||||
|
||||
@@ -103,7 +103,7 @@ class StripeProviderService extends PaymentService {
|
||||
const paymentIntent = await this.stripe_.paymentIntents.create({
|
||||
customer: stripeCustomerId,
|
||||
amount: amount * 100, // Stripe amount is in cents
|
||||
currency: currency_code
|
||||
currency: currency_code,
|
||||
})
|
||||
|
||||
return paymentIntent
|
||||
@@ -134,7 +134,7 @@ class StripeProviderService extends PaymentService {
|
||||
const { id } = data
|
||||
const amount = this.totalsService_.getTotal(cart)
|
||||
return this.stripe_.paymentIntents.update(id, {
|
||||
amount
|
||||
amount,
|
||||
})
|
||||
} catch (error) {
|
||||
throw error
|
||||
|
||||
@@ -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,26 @@
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const contentfulService = req.scope.resolve("contentfulService")
|
||||
|
||||
const contentfulType = req.body.sys.contentType.sys.id
|
||||
const entryId = req.body.sys.id
|
||||
|
||||
let updated = {}
|
||||
switch (contentfulType) {
|
||||
case "product":
|
||||
updated = await contentfulService.sendContentfulProductToAdmin(entryId)
|
||||
break
|
||||
case "productVariant":
|
||||
updated = await contentfulService.sendContentfulProductVariantToAdmin(
|
||||
entryId
|
||||
)
|
||||
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,296 @@
|
||||
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)
|
||||
}
|
||||
|
||||
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_(productId) {
|
||||
try {
|
||||
const productVariants = await this.productService_.retrieveVariants(
|
||||
productId
|
||||
)
|
||||
|
||||
const contentfulVariants = await Promise.all(
|
||||
productVariants.map((variant) =>
|
||||
this.updateProductVariantInContentful(variant)
|
||||
)
|
||||
)
|
||||
|
||||
return contentfulVariants
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
getVariantLinks_(variantEntries) {
|
||||
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._id)
|
||||
const variantLinks = this.getVariantLinks_(variantEntries)
|
||||
const result = await environment.createEntryWithId(
|
||||
"product",
|
||||
product._id,
|
||||
{
|
||||
fields: {
|
||||
title: {
|
||||
"en-US": product.title,
|
||||
},
|
||||
variants: {
|
||||
"en-US": variantLinks,
|
||||
},
|
||||
objectId: {
|
||||
"en-US": product._id,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const ignoreIds = (await this.getIgnoreIds_("product")) || []
|
||||
ignoreIds.push(product._id)
|
||||
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
|
||||
return result
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async createProductVariantInContentful(variant) {
|
||||
try {
|
||||
const environment = await this.getContentfulEnvironment_()
|
||||
const result = await 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,
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
|
||||
ignoreIds.push(variant._id)
|
||||
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
|
||||
return result
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async updateProductInContentful(product) {
|
||||
try {
|
||||
const ignoreIds = (await this.getIgnoreIds_("product")) || []
|
||||
|
||||
if (ignoreIds.includes(product._id)) {
|
||||
const newIgnoreIds = ignoreIds.filter((id) => id !== product._id)
|
||||
this.redis_.set("product_ignore_ids", JSON.stringify(newIgnoreIds))
|
||||
return
|
||||
} else {
|
||||
ignoreIds.push(product._id)
|
||||
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
|
||||
}
|
||||
|
||||
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)
|
||||
const variantLinks = this.getVariantLinks_(variantEntries)
|
||||
productEntry.fields = _.assignIn(productEntry.fields, {
|
||||
title: {
|
||||
"en-US": product.title,
|
||||
},
|
||||
variants: {
|
||||
"en-US": variantLinks,
|
||||
},
|
||||
objectId: {
|
||||
"en-US": product._id,
|
||||
},
|
||||
})
|
||||
|
||||
const updatedEntry = await productEntry.update()
|
||||
const publishedEntry = await updatedEntry.publish()
|
||||
|
||||
return publishedEntry
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async updateProductVariantInContentful(variant) {
|
||||
try {
|
||||
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
|
||||
|
||||
if (ignoreIds.includes(variant._id)) {
|
||||
const newIgnoreIds = ignoreIds.filter((id) => id !== variant._id)
|
||||
this.redis_.set(
|
||||
"product_variant_ignore_ids",
|
||||
JSON.stringify(newIgnoreIds)
|
||||
)
|
||||
return
|
||||
} else {
|
||||
ignoreIds.push(variant._id)
|
||||
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
|
||||
}
|
||||
|
||||
const environment = await this.getContentfulEnvironment_()
|
||||
// check if product exists
|
||||
let variantEntry = undefined
|
||||
// if not, we create a new one
|
||||
try {
|
||||
variantEntry = await environment.getEntry(variant._id)
|
||||
} catch (error) {
|
||||
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,
|
||||
},
|
||||
})
|
||||
|
||||
const updatedEntry = await variantEntry.update()
|
||||
const publishedEntry = await updatedEntry.publish()
|
||||
|
||||
return publishedEntry
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async sendContentfulProductToAdmin(productId) {
|
||||
try {
|
||||
const environment = await this.getContentfulEnvironment_()
|
||||
const productEntry = await environment.getEntry(productId)
|
||||
|
||||
const ignoreIds = (await this.getIgnoreIds_("product")) || []
|
||||
if (ignoreIds.includes(productId)) {
|
||||
const newIgnoreIds = ignoreIds.filter((id) => id !== productId)
|
||||
this.redis_.set("product_ignore_ids", JSON.stringify(newIgnoreIds))
|
||||
return
|
||||
} else {
|
||||
ignoreIds.push(productId)
|
||||
this.redis_.set("product_ignore_ids", JSON.stringify(ignoreIds))
|
||||
}
|
||||
|
||||
const updatedProduct = await this.productService_.update(productId, {
|
||||
title: productEntry.fields.title["en-US"],
|
||||
})
|
||||
|
||||
return updatedProduct
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async sendContentfulProductVariantToAdmin(variantId) {
|
||||
try {
|
||||
const environment = await this.getContentfulEnvironment_()
|
||||
const variantEntry = await environment.getEntry(variantId)
|
||||
|
||||
const ignoreIds = (await this.getIgnoreIds_("product_variant")) || []
|
||||
if (ignoreIds.includes(variantId)) {
|
||||
const newIgnoreIds = ignoreIds.filter((id) => id !== variantId)
|
||||
this.redis_.set(
|
||||
"product_variant_ignore_ids",
|
||||
JSON.stringify(newIgnoreIds)
|
||||
)
|
||||
return
|
||||
} else {
|
||||
ignoreIds.push(variantId)
|
||||
this.redis_.set("product_variant_ignore_ids", JSON.stringify(ignoreIds))
|
||||
}
|
||||
|
||||
const updatedVariant = await this.productVariantService_.update(
|
||||
variantId,
|
||||
{
|
||||
title: variantEntry.fields.title["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_.createProductInContentful(data)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default ContentfulSubscriber
|
||||
@@ -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
|
||||
!jest.config.js
|
||||
|
||||
/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,3 @@
|
||||
module.exports = {
|
||||
testEnvironment: "node",
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "medusa-plugin-sendgrid",
|
||||
"version": "0.3.0",
|
||||
"description": "SendGrid transactional emails",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/medusajs/medusa",
|
||||
"directory": "packages/medusa-plugin-sendgrid"
|
||||
},
|
||||
"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 .",
|
||||
"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",
|
||||
"@sendgrid/mail": "^7.1.1",
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^0.3.0",
|
||||
"medusa-interfaces": "^0.3.0",
|
||||
"medusa-test-utils": "^0.3.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Router } from "express"
|
||||
import routes from "./routes"
|
||||
|
||||
export default (container) => {
|
||||
const app = Router()
|
||||
|
||||
routes(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,16 @@
|
||||
import { Router } from "express"
|
||||
import bodyParser from "body-parser"
|
||||
import middlewares from "../middleware"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default (app) => {
|
||||
app.use("/sendgrid", route)
|
||||
|
||||
route.post(
|
||||
"/send",
|
||||
bodyParser.raw({ type: "application/json" }),
|
||||
middlewares.wrap(require("./send-email").default)
|
||||
)
|
||||
return app
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
template_id: Validator.string().required(),
|
||||
from: Validator.string().required(),
|
||||
to: Validator.string().required(),
|
||||
data: Validator.object().optional().default({}),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const sendgridService = req.scope.resolve("sendgridService")
|
||||
await sendgridService.sendEmail(
|
||||
value.template_id,
|
||||
value.from,
|
||||
value.to,
|
||||
value.data
|
||||
)
|
||||
res.sendStatus(200)
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import SendGrid from "@sendgrid/mail"
|
||||
|
||||
class SendGridService extends BaseService {
|
||||
/**
|
||||
* @param {Object} options - options defined in `medusa-config.js`
|
||||
* e.g.
|
||||
* {
|
||||
* api_key: SendGrid api key
|
||||
* from: Medusa <hello@medusa.example>,
|
||||
* order_placed_template: 01234,
|
||||
* order_updated_template: 56789,
|
||||
* order_updated_cancelled_template: 4242,
|
||||
* user_password_reset_template: 0000,
|
||||
* customer_password_reset_template: 1111,
|
||||
* }
|
||||
*/
|
||||
constructor({}, options) {
|
||||
super()
|
||||
|
||||
this.options_ = options
|
||||
|
||||
SendGrid.setApiKey(options.api_key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a transactional email based on an event using SendGrid.
|
||||
* @param {string} event - event related to the order
|
||||
* @param {Object} order - the order object sent to SendGrid, that must
|
||||
* correlate with the structure specificed in the dynamic template
|
||||
* @returns {Promise} result of the send operation
|
||||
*/
|
||||
async transactionalEmail(event, order) {
|
||||
let templateId
|
||||
switch (event) {
|
||||
case "order.placed":
|
||||
templateId = this.options_.order_placed_template
|
||||
break
|
||||
case "order.updated":
|
||||
templateId = this.options_.order_updated_template
|
||||
break
|
||||
case "order.cancelled":
|
||||
templateId = this.options_.order_cancelled_template
|
||||
break
|
||||
case "user.password_reset":
|
||||
templateId = this.options_.user_password_reset_template
|
||||
break
|
||||
case "customer.password_reset":
|
||||
templateId = this.options_.customer_password_reset_template
|
||||
break
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
return SendGrid.send({
|
||||
template_id: templateId,
|
||||
from: options.from,
|
||||
to: order.email,
|
||||
dynamic_template_data: order,
|
||||
})
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an email using SendGrid.
|
||||
* @param {string} templateId - id of template in SendGrid
|
||||
* @param {string} from - sender of email
|
||||
* @param {string} to - receiver of email
|
||||
* @param {Object} data - data to send in mail (match with template)
|
||||
* @returns {Promise} result of the send operation
|
||||
*/
|
||||
async sendEmail(templateId, from, to, data) {
|
||||
try {
|
||||
return SendGrid.send({
|
||||
to,
|
||||
from,
|
||||
template_id: templateId,
|
||||
dynamic_template_data: data,
|
||||
})
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SendGridService
|
||||
@@ -0,0 +1,21 @@
|
||||
class OrderSubscriber {
|
||||
constructor({ sendgridService, eventBusService }) {
|
||||
this.sendgridService_ = sendgridService
|
||||
|
||||
this.eventBus_ = eventBusService
|
||||
|
||||
this.eventBus_.subscribe("order.placed", async (order) => {
|
||||
await this.sendgridService_.transactionalEmail("order.placed", order)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("order.cancelled", async (order) => {
|
||||
await this.sendgridService_.transactionalEmail("order.cancelled", order)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("order.updated", async (order) => {
|
||||
await this.sendgridService_.transactionalEmail("order.updated", order)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default OrderSubscriber
|
||||
@@ -0,0 +1,23 @@
|
||||
class UserSubscriber {
|
||||
constructor({ sendgridService, eventBusService }) {
|
||||
this.sendgridService_ = sendgridService
|
||||
|
||||
this.eventBus_ = eventBusService
|
||||
|
||||
this.eventBus_.subscribe("user.password_reset", async (data) => {
|
||||
await this.sendgridService_.transactionalEmail(
|
||||
"user.password_reset",
|
||||
data
|
||||
)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("customer.password_reset", async (data) => {
|
||||
await this.sendgridService_.transactionalEmail(
|
||||
"customer.password_reset",
|
||||
data
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default UserSubscriber
|
||||
File diff suppressed because it is too large
Load Diff
@@ -72,4 +72,4 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,8 @@ export default async (req, res) => {
|
||||
delete value.variants
|
||||
|
||||
const productService = req.scope.resolve("productService")
|
||||
const shippingProfileService = req.scope.resolve("shippingProfileService")
|
||||
|
||||
let newProduct = await productService.createDraft(value)
|
||||
|
||||
if (variants) {
|
||||
@@ -64,6 +66,10 @@ export default async (req, res) => {
|
||||
)
|
||||
}
|
||||
|
||||
// Add to default shipping profile
|
||||
const { _id } = await shippingProfileService.retrieveDefault()
|
||||
await shippingProfileService.addProduct(_id, newProduct._id)
|
||||
|
||||
newProduct = await productService.decorate(newProduct, [
|
||||
"title",
|
||||
"description",
|
||||
|
||||
+5
@@ -12,6 +12,8 @@ describe("POST /admin/shipping-options", () => {
|
||||
name: "Test option",
|
||||
region_id: "testregion",
|
||||
provider_id: "test_provider",
|
||||
data: { id: "test" },
|
||||
profile_id: "test",
|
||||
price: {
|
||||
type: "flat_rate",
|
||||
amount: 100,
|
||||
@@ -32,6 +34,7 @@ describe("POST /admin/shipping-options", () => {
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
console.log(subject)
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
@@ -41,6 +44,8 @@ describe("POST /admin/shipping-options", () => {
|
||||
name: "Test option",
|
||||
region_id: "testregion",
|
||||
provider_id: "test_provider",
|
||||
data: { id: "test" },
|
||||
profile_id: "test",
|
||||
price: {
|
||||
type: "flat_rate",
|
||||
amount: 100,
|
||||
|
||||
@@ -5,11 +5,14 @@ export default async (req, res) => {
|
||||
name: Validator.string().required(),
|
||||
region_id: Validator.string().required(),
|
||||
provider_id: Validator.string().required(),
|
||||
data: Validator.object(),
|
||||
price: Validator.object().keys({
|
||||
type: Validator.string().required(),
|
||||
amount: Validator.number().optional(),
|
||||
}),
|
||||
profile_id: Validator.string().required(),
|
||||
data: Validator.object().required(),
|
||||
price: Validator.object()
|
||||
.keys({
|
||||
type: Validator.string().required(),
|
||||
amount: Validator.number().optional(),
|
||||
})
|
||||
.required(),
|
||||
requirements: Validator.array()
|
||||
.items(
|
||||
Validator.object({
|
||||
@@ -27,10 +30,17 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const optionService = req.scope.resolve("shippingOptionService")
|
||||
const shippingProfileService = req.scope.resolve("shippingProfileService")
|
||||
|
||||
const data = await optionService.create(value)
|
||||
|
||||
// Add to default shipping profile
|
||||
const { _id } = await shippingProfileService.retrieveDefault()
|
||||
await shippingProfileService.addShippingOption(_id, data._id)
|
||||
|
||||
res.status(200).json({ shipping_option: data })
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export default async (req, res) => {
|
||||
discounts: Validator.array().items({
|
||||
code: Validator.string(),
|
||||
}),
|
||||
customer_id: Validator.string(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
@@ -27,6 +28,10 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
try {
|
||||
if (value.customer_id) {
|
||||
await cartService.updateCustomerId(id, value.customer_id)
|
||||
}
|
||||
|
||||
if (value.region_id) {
|
||||
await cartService.setRegion(id, value.region_id)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ export default async (req, res) => {
|
||||
try {
|
||||
const customerService = req.scope.resolve("customerService")
|
||||
const customer = await customerService.create(value)
|
||||
const data = await customerService.decorate(customer)
|
||||
const data = await customerService.decorate(customer, ["_id", "email"])
|
||||
res.status(201).json({ customer: data })
|
||||
} catch (err) {
|
||||
throw err
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export default async ({ container }) => {
|
||||
const storeService = container.resolve("storeService")
|
||||
const profileService = container.resolve("shippingProfileService")
|
||||
|
||||
await storeService.create()
|
||||
await profileService.createDefault()
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import modelsLoader from "./models"
|
||||
import servicesLoader from "./services"
|
||||
import passportLoader from "./passport"
|
||||
import pluginsLoader from "./plugins"
|
||||
import storeLoader from "./store"
|
||||
import defaultsLoader from "./defaults"
|
||||
import Logger from "./logger"
|
||||
|
||||
export default async ({ directory: rootDirectory, expressApp }) => {
|
||||
@@ -58,8 +58,8 @@ export default async ({ directory: rootDirectory, expressApp }) => {
|
||||
await apiLoader({ container, rootDirectory, app: expressApp })
|
||||
Logger.info("API initialized")
|
||||
|
||||
await storeLoader({ container })
|
||||
Logger.info("Store initialized")
|
||||
await defaultsLoader({ container })
|
||||
Logger.info("Defaults initialized")
|
||||
|
||||
return { container, dbConnection, app: expressApp }
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* Registers all services in the services directory
|
||||
*/
|
||||
export default ({ container }) => {
|
||||
const storeService = container.resolve("storeService")
|
||||
return storeService.create()
|
||||
}
|
||||
@@ -13,6 +13,8 @@ class ProductVariantModel extends BaseModel {
|
||||
static schema = {
|
||||
title: { type: String, required: true },
|
||||
prices: { type: [MoneyAmountSchema], default: [], required: true },
|
||||
sku: { type: String, default: "" },
|
||||
barcode: { type: String, default: "" },
|
||||
options: { type: [OptionValueSchema], default: [] },
|
||||
sku: { type: String, unique: true, sparse: true },
|
||||
ean: { type: String, unique: true, sparse: true },
|
||||
|
||||
@@ -31,6 +31,9 @@ export const ShippingProfileServiceMock = {
|
||||
}
|
||||
return Promise.resolve()
|
||||
}),
|
||||
retrieveDefault: jest.fn().mockImplementation(data => {
|
||||
return Promise.resolve({ _id: IdMap.getId("default_shipping_profile") })
|
||||
}),
|
||||
list: jest.fn().mockImplementation(selector => {
|
||||
if (!selector) {
|
||||
return Promise.resolve([])
|
||||
|
||||
@@ -5,11 +5,13 @@ import { PaymentProviderServiceMock } from "../__mocks__/payment-provider"
|
||||
import { FulfillmentProviderServiceMock } from "../__mocks__/fulfillment-provider"
|
||||
import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile"
|
||||
import { TotalsServiceMock } from "../__mocks__/totals"
|
||||
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||
|
||||
describe("OrderService", () => {
|
||||
describe("create", () => {
|
||||
const orderService = new OrderService({
|
||||
orderModel: OrderModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -54,6 +56,7 @@ describe("OrderService", () => {
|
||||
describe("update", () => {
|
||||
const orderService = new OrderService({
|
||||
orderModel: OrderModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -182,6 +185,7 @@ describe("OrderService", () => {
|
||||
describe("cancel", () => {
|
||||
const orderService = new OrderService({
|
||||
orderModel: OrderModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -252,6 +256,7 @@ describe("OrderService", () => {
|
||||
paymentProviderService: PaymentProviderServiceMock,
|
||||
fulfillmentProviderService: FulfillmentProviderServiceMock,
|
||||
shippingProfileService: ShippingProfileServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
|
||||
@@ -4,6 +4,7 @@ import ProductVariantService from "../product-variant"
|
||||
import { ProductVariantModelMock } from "../../models/__mocks__/product-variant"
|
||||
import { ProductServiceMock } from "../__mocks__/product"
|
||||
import { RegionServiceMock } from "../__mocks__/region"
|
||||
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||
|
||||
describe("ProductVariantService", () => {
|
||||
describe("retrieve", () => {
|
||||
@@ -12,6 +13,7 @@ describe("ProductVariantService", () => {
|
||||
beforeAll(async () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
res = await productVariantService.retrieve(IdMap.getId("validId"))
|
||||
@@ -38,6 +40,7 @@ describe("ProductVariantService", () => {
|
||||
beforeAll(async () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
await productVariantService
|
||||
@@ -69,6 +72,7 @@ describe("ProductVariantService", () => {
|
||||
jest.clearAllMocks()
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
productVariantService.createDraft({
|
||||
@@ -106,6 +110,7 @@ describe("ProductVariantService", () => {
|
||||
jest.clearAllMocks()
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
productVariantService.publish(IdMap.getId("variantId"))
|
||||
@@ -124,6 +129,7 @@ describe("ProductVariantService", () => {
|
||||
describe("update", () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -168,6 +174,7 @@ describe("ProductVariantService", () => {
|
||||
describe("decorate", () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
const fakeVariant = {
|
||||
@@ -216,6 +223,7 @@ describe("ProductVariantService", () => {
|
||||
describe("setMetadata", () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -264,6 +272,7 @@ describe("ProductVariantService", () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
productService: ProductServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -331,6 +340,7 @@ describe("ProductVariantService", () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
productService: ProductServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -354,6 +364,7 @@ describe("ProductVariantService", () => {
|
||||
describe("delete", () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -373,6 +384,7 @@ describe("ProductVariantService", () => {
|
||||
describe("canCoverQuantity", () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -419,6 +431,7 @@ describe("ProductVariantService", () => {
|
||||
describe("setCurrencyPrice", () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -519,6 +532,7 @@ describe("ProductVariantService", () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
regionService: RegionServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -619,6 +633,7 @@ describe("ProductVariantService", () => {
|
||||
const productVariantService = new ProductVariantService({
|
||||
productVariantModel: ProductVariantModelMock,
|
||||
regionService: RegionServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
ProductVariantServiceMock,
|
||||
variants,
|
||||
} from "../__mocks__/product-variant"
|
||||
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||
|
||||
describe("ProductService", () => {
|
||||
describe("retrieve", () => {
|
||||
@@ -14,6 +15,7 @@ describe("ProductService", () => {
|
||||
beforeAll(async () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
res = await productService.retrieve(IdMap.getId("validId"))
|
||||
@@ -40,6 +42,7 @@ describe("ProductService", () => {
|
||||
beforeAll(async () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
await productService.retrieve(IdMap.getId("failId")).catch(err => {
|
||||
@@ -70,6 +73,7 @@ describe("ProductService", () => {
|
||||
jest.clearAllMocks()
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
productService.createDraft({
|
||||
@@ -108,6 +112,7 @@ describe("ProductService", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
productVariantService: ProductVariantServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
productService.publish(IdMap.getId("productId"))
|
||||
@@ -127,6 +132,7 @@ describe("ProductService", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
productVariantService: ProductVariantServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
const fakeProduct = {
|
||||
@@ -193,6 +199,7 @@ describe("ProductService", () => {
|
||||
describe("setMetadata", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -236,6 +243,7 @@ describe("ProductService", () => {
|
||||
describe("update", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -290,6 +298,7 @@ describe("ProductService", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
productVariantService: ProductVariantServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -314,6 +323,7 @@ describe("ProductService", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
productVariantService: ProductVariantServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -479,6 +489,7 @@ describe("ProductService", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
productVariantService: ProductVariantServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -571,6 +582,7 @@ describe("ProductService", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
productVariantService: ProductVariantServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -632,6 +644,7 @@ describe("ProductService", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
productVariantService: ProductVariantServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -659,6 +672,7 @@ describe("ProductService", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
productVariantService: ProductVariantServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -719,6 +733,7 @@ describe("ProductService", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
productVariantService: ProductVariantServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -788,6 +803,7 @@ describe("ProductService", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
productVariantService: ProductVariantServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
@@ -843,6 +859,7 @@ describe("ProductService", () => {
|
||||
const productService = new ProductService({
|
||||
productModel: ProductModelMock,
|
||||
productVariantService: ProductVariantServiceMock,
|
||||
eventBusService: EventBusServiceMock,
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -370,6 +370,40 @@ class CartService extends BaseService {
|
||||
return result
|
||||
})
|
||||
}
|
||||
/**
|
||||
* Sets the customer id of a cart
|
||||
* @param {string} cartId - the id of the cart to add email to
|
||||
* @param {string} customerId - the customer to add to cart
|
||||
* @return {Promise} the result of the update operation
|
||||
*/
|
||||
async updateCustomerId(cartId, customerId) {
|
||||
const cart = await this.retrieve(cartId)
|
||||
const schema = Validator.string()
|
||||
.objectId()
|
||||
.required()
|
||||
const { value, error } = schema.validate(customerId)
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"The customerId is not valid"
|
||||
)
|
||||
}
|
||||
|
||||
return this.cartModel_
|
||||
.updateOne(
|
||||
{
|
||||
_id: cart._id,
|
||||
},
|
||||
{
|
||||
$set: { customer_id: value },
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
// Notify subscribers
|
||||
this.eventBus_.emit(CartService.Events.UPDATED, result)
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the email of a cart
|
||||
|
||||
@@ -9,6 +9,10 @@ import { BaseService } from "medusa-interfaces"
|
||||
* @implements BaseService
|
||||
*/
|
||||
class CustomerService extends BaseService {
|
||||
static Events = {
|
||||
PASSWORD_RESET: "customer.password_reset",
|
||||
}
|
||||
|
||||
constructor({ customerModel, eventBusService }) {
|
||||
super()
|
||||
|
||||
@@ -26,7 +30,7 @@ class CustomerService extends BaseService {
|
||||
*/
|
||||
validateId_(rawId) {
|
||||
const schema = Validator.objectId()
|
||||
const { value, error } = schema.validate(rawId)
|
||||
const { value, error } = schema.validate(rawId.toString())
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
@@ -92,10 +96,11 @@ class CustomerService extends BaseService {
|
||||
const expiry = Math.floor(Date.now() / 1000) + 60 * 15 // 15 minutes ahead
|
||||
const payload = { customer_id: customer._id, exp: expiry }
|
||||
const token = jwt.sign(payload, secret)
|
||||
|
||||
// TODO: Call event layer to ensure that there is an email service that
|
||||
// sends the token.
|
||||
|
||||
// Notify subscribers
|
||||
this.eventBus_.emit(CustomerService.Events.PASSWORD_RESET, {
|
||||
email: customer.email,
|
||||
token,
|
||||
})
|
||||
return token
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class DiscountService extends BaseService {
|
||||
*/
|
||||
validateId_(rawId) {
|
||||
const schema = Validator.objectId()
|
||||
const { value, error } = schema.validate(rawId)
|
||||
const { value, error } = schema.validate(rawId.toString())
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
|
||||
@@ -14,7 +14,7 @@ class FulfillmentProviderService {
|
||||
*/
|
||||
retrieveProvider(provider_id) {
|
||||
try {
|
||||
return this.container_.resolve(`fp_${provider_id}`)
|
||||
return this.container_[`fp_${provider_id}`]
|
||||
} catch (err) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
|
||||
@@ -3,6 +3,12 @@ import { Validator, MedusaError } from "medusa-core-utils"
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
|
||||
class OrderService extends BaseService {
|
||||
static Events = {
|
||||
PLACED: "order.placed",
|
||||
UPDATED: "order.updated",
|
||||
CANCELLED: "order.cancelled",
|
||||
}
|
||||
|
||||
constructor({
|
||||
orderModel,
|
||||
paymentProviderService,
|
||||
@@ -43,7 +49,7 @@ class OrderService extends BaseService {
|
||||
*/
|
||||
validateId_(rawId) {
|
||||
const schema = Validator.objectId()
|
||||
const { value, error } = schema.validate(rawId)
|
||||
const { value, error } = schema.validate(rawId.toString())
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
@@ -139,15 +145,30 @@ class OrderService extends BaseService {
|
||||
return order
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Object} selector - the query object for find
|
||||
* @return {Promise} the result of the find operation
|
||||
*/
|
||||
list(selector) {
|
||||
return this.orderModel_.find(selector)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an order
|
||||
* @param {object} order - the order to create
|
||||
* @return {Promise} resolves to the creation result.
|
||||
*/
|
||||
async create(order) {
|
||||
return this.orderModel_.create(order).catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
return this.orderModel_
|
||||
.create(order)
|
||||
.then(result => {
|
||||
// Notify subscribers
|
||||
this.eventBus_.emit(OrderService.Events.PLACED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -217,6 +238,11 @@ class OrderService extends BaseService {
|
||||
{ $set: updateFields },
|
||||
{ runValidators: true }
|
||||
)
|
||||
.then(result => {
|
||||
// Notify subscribers
|
||||
this.eventBus_.emit(OrderService.Events.UPDATED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
@@ -248,14 +274,23 @@ class OrderService extends BaseService {
|
||||
|
||||
// TODO: cancel payment method
|
||||
|
||||
return this.orderModel_.updateOne(
|
||||
{
|
||||
_id: orderId,
|
||||
},
|
||||
{
|
||||
$set: { status: "cancelled" },
|
||||
}
|
||||
)
|
||||
return this.orderModel_
|
||||
.updateOne(
|
||||
{
|
||||
_id: orderId,
|
||||
},
|
||||
{
|
||||
$set: { status: "cancelled" },
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
// Notify subscribers
|
||||
this.eventBus_.emit(OrderService.Events.CANCELLED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,14 +371,23 @@ class OrderService extends BaseService {
|
||||
})
|
||||
)
|
||||
|
||||
return this.orderModel_.updateOne(
|
||||
{
|
||||
_id: orderId,
|
||||
},
|
||||
{
|
||||
$set: updateFields,
|
||||
}
|
||||
)
|
||||
return this.orderModel_
|
||||
.updateOne(
|
||||
{
|
||||
_id: orderId,
|
||||
},
|
||||
{
|
||||
$set: updateFields,
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
// Notify subscribers
|
||||
this.eventBus_.emit(OrderService.Events.UPDATED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,6 +7,11 @@ import { Validator, MedusaError } from "medusa-core-utils"
|
||||
* @implements BaseService
|
||||
*/
|
||||
class ProductVariantService extends BaseService {
|
||||
static Events = {
|
||||
UPDATED: "product-variant.updated",
|
||||
CREATED: "product-variant.created",
|
||||
}
|
||||
|
||||
/** @param { productVariantModel: (ProductVariantModel) } */
|
||||
constructor({ productVariantModel, eventBusService, regionService }) {
|
||||
super()
|
||||
@@ -73,6 +78,10 @@ class ProductVariantService extends BaseService {
|
||||
...productVariant,
|
||||
published: false,
|
||||
})
|
||||
.then(result => {
|
||||
this.eventBus_.emit(ProductVariantService.Events.CREATED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
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.
|
||||
* @return {Promise} resolves to the creation result.
|
||||
*/
|
||||
publish(variantId) {
|
||||
async publish(variantId) {
|
||||
return this.productVariantModel_
|
||||
.updateOne({ _id: variantId }, { $set: { published: true } })
|
||||
.then(result => {
|
||||
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
@@ -123,6 +136,10 @@ class ProductVariantService extends BaseService {
|
||||
{ $set: update },
|
||||
{ runValidators: true }
|
||||
)
|
||||
.then(result => {
|
||||
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
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,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
prices: newPrices,
|
||||
prices: [
|
||||
{
|
||||
currency_code: currencyCode,
|
||||
amount,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return this.productVariantModel_.updateOne(
|
||||
{
|
||||
_id: variant._id,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
prices: [
|
||||
{
|
||||
currency_code: currencyCode,
|
||||
amount,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
this.eventBus_.emit(ProductVariantService.Events.UPDATED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
prices: newPrices,
|
||||
prices: [
|
||||
{
|
||||
region_id: region._id,
|
||||
currency_code: region.currency_code,
|
||||
amount,
|
||||
},
|
||||
{
|
||||
currency_code: region.currency_code,
|
||||
amount,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Set the price both for default currency price and for the region
|
||||
return this.productVariantModel_.updateOne(
|
||||
{
|
||||
_id: variant._id,
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
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)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -313,10 +362,18 @@ class ProductVariantService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
return this.productVariantModel_.updateOne(
|
||||
{ _id: variantId, "options.option_id": optionId },
|
||||
{ $set: { "options.$.value": `${optionValue}` } }
|
||||
)
|
||||
return this.productVariantModel_
|
||||
.updateOne(
|
||||
{ _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(
|
||||
{ _id: variant._id },
|
||||
{ $push: { options: { option_id: optionId, value: `${optionValue}` } } }
|
||||
)
|
||||
return this.productVariantModel_
|
||||
.updateOne(
|
||||
{ _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.
|
||||
*/
|
||||
async deleteOptionValue(variantId, optionId) {
|
||||
return this.productVariantModel_.updateOne(
|
||||
{ _id: variantId },
|
||||
{ $pull: { options: { option_id: optionId } } }
|
||||
)
|
||||
return this.productVariantModel_
|
||||
.updateOne(
|
||||
{ _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
|
||||
* @return {Promise} the result of the find operation
|
||||
*/
|
||||
list(selector) {
|
||||
async list(selector) {
|
||||
return this.productVariantModel_.find(selector)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,11 @@ import { BaseService } from "medusa-interfaces"
|
||||
* @implements BaseService
|
||||
*/
|
||||
class ProductService extends BaseService {
|
||||
static Events = {
|
||||
UPDATED: "product.updated",
|
||||
CREATED: "product.created",
|
||||
}
|
||||
|
||||
/** @param { productModel: (ProductModel) } */
|
||||
constructor({ productModel, eventBusService, productVariantService }) {
|
||||
super()
|
||||
@@ -86,12 +91,16 @@ class ProductService extends BaseService {
|
||||
* @param {object} product - the product to create
|
||||
* @return {Promise} resolves to the creation result.
|
||||
*/
|
||||
createDraft(product) {
|
||||
async createDraft(product) {
|
||||
return this.productModel_
|
||||
.create({
|
||||
...product,
|
||||
published: false,
|
||||
})
|
||||
.then(result => {
|
||||
this.eventBus_.emit(ProductService.Events.CREATED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
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.
|
||||
* @return {Promise} resolves to the creation result.
|
||||
*/
|
||||
publish(productId) {
|
||||
async publish(productId) {
|
||||
return this.productModel_
|
||||
.updateOne({ _id: productId }, { $set: { published: true } })
|
||||
.then(result => {
|
||||
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
@@ -183,6 +196,10 @@ class ProductService extends BaseService {
|
||||
{ $set: update },
|
||||
{ runValidators: true }
|
||||
)
|
||||
.then(result => {
|
||||
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
@@ -266,10 +283,15 @@ class ProductService extends BaseService {
|
||||
|
||||
const newVariant = await this.productVariantService_.createDraft(variant)
|
||||
|
||||
return this.productModel_.updateOne(
|
||||
{ _id: product._id },
|
||||
{ $push: { variants: newVariant._id } }
|
||||
)
|
||||
return this.productModel_
|
||||
.updateOne({ _id: product._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)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -303,7 +325,7 @@ class ProductService extends BaseService {
|
||||
"Default Value"
|
||||
)
|
||||
)
|
||||
).catch(err => {
|
||||
).catch(async err => {
|
||||
// If any of the variants failed to add the new option value we clean up
|
||||
return Promise.all(
|
||||
product.variants.map(async variantId =>
|
||||
@@ -329,7 +351,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
|
||||
return Promise.all(
|
||||
product.variants.map(async variantId =>
|
||||
@@ -363,14 +389,22 @@ class ProductService extends BaseService {
|
||||
return variant
|
||||
})
|
||||
|
||||
return this.productModel_.updateOne(
|
||||
{
|
||||
_id: productId,
|
||||
},
|
||||
{
|
||||
$set: { variants: newOrder },
|
||||
}
|
||||
)
|
||||
return this.productModel_
|
||||
.updateOne(
|
||||
{
|
||||
_id: productId,
|
||||
},
|
||||
{
|
||||
$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)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,14 +438,22 @@ class ProductService extends BaseService {
|
||||
return option
|
||||
})
|
||||
|
||||
return this.productModel_.updateOne(
|
||||
{
|
||||
_id: productId,
|
||||
},
|
||||
{
|
||||
$set: { options: newOrder },
|
||||
}
|
||||
)
|
||||
return this.productModel_
|
||||
.updateOne(
|
||||
{
|
||||
_id: productId,
|
||||
},
|
||||
{
|
||||
$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)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -448,15 +490,23 @@ class ProductService extends BaseService {
|
||||
const update = {}
|
||||
update["options.$.title"] = title
|
||||
|
||||
return this.productModel_.updateOne(
|
||||
{
|
||||
_id: productId,
|
||||
"options._id": optionId,
|
||||
},
|
||||
{
|
||||
$set: update,
|
||||
}
|
||||
)
|
||||
return this.productModel_
|
||||
.updateOne(
|
||||
{
|
||||
_id: productId,
|
||||
"options._id": optionId,
|
||||
},
|
||||
{
|
||||
$set: update,
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -504,16 +554,23 @@ class ProductService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.productModel_.updateOne(
|
||||
{ _id: productId },
|
||||
{
|
||||
$pull: {
|
||||
options: {
|
||||
_id: optionId,
|
||||
const result = await this.productModel_
|
||||
.updateOne(
|
||||
{ _id: productId },
|
||||
{
|
||||
$pull: {
|
||||
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 (product.variants) {
|
||||
@@ -538,14 +595,22 @@ class ProductService extends BaseService {
|
||||
|
||||
await this.productVariantService_.delete(variantId)
|
||||
|
||||
return this.productModel_.updateOne(
|
||||
{ _id: product._id },
|
||||
{
|
||||
$pull: {
|
||||
variants: variantId,
|
||||
},
|
||||
}
|
||||
)
|
||||
return this.productModel_
|
||||
.updateOne(
|
||||
{ _id: product._id },
|
||||
{
|
||||
$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) {
|
||||
@@ -638,6 +703,10 @@ class ProductService extends BaseService {
|
||||
const keyPath = `metadata.${key}`
|
||||
return this.productModel_
|
||||
.updateOne({ _id: validatedId }, { $set: { [keyPath]: value } })
|
||||
.then(result => {
|
||||
this.eventBus_.emit(ProductService.Events.UPDATED, result)
|
||||
return result
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
|
||||
@@ -37,7 +37,7 @@ class ShippingOptionService extends BaseService {
|
||||
*/
|
||||
validateId_(rawId) {
|
||||
const schema = Validator.objectId()
|
||||
const { value, error } = schema.validate(rawId)
|
||||
const { value, error } = schema.validate(rawId.toString())
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
@@ -246,10 +246,10 @@ class ShippingOptionService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
if (price.type === "flat_rate" && (!price.amount || price.amount < 0)) {
|
||||
if (price.type === "flat_rate" && (!price.amount || price.amount <= 0)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"Flat rate prices must have a postive amount field."
|
||||
"Flat rate prices must have be zero or have a postive amount field."
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ class ShippingProfileService extends BaseService {
|
||||
*/
|
||||
validateId_(rawId) {
|
||||
const schema = Validator.objectId()
|
||||
const { value, error } = schema.validate(rawId)
|
||||
const { value, error } = schema.validate(rawId.toString())
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
@@ -76,6 +76,27 @@ class ShippingProfileService extends BaseService {
|
||||
return profile
|
||||
}
|
||||
|
||||
async retrieveDefault() {
|
||||
return await this.profileModel_
|
||||
.findOne({ name: "default_shipping_profile" })
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a default shipping profile, if this does not already exist.
|
||||
* @return {Promise<ShippingProfile>} the shipping profile
|
||||
*/
|
||||
async createDefault() {
|
||||
const profile = await this.retrieveDefault()
|
||||
if (!profile) {
|
||||
return this.profileModel_.create({ name: "default_shipping_profile" })
|
||||
}
|
||||
|
||||
return profile
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new shipping profile.
|
||||
* @param {ShippingProfile} profile - the shipping profile to create from
|
||||
|
||||
@@ -28,7 +28,7 @@ class StoreService extends BaseService {
|
||||
*/
|
||||
validateId_(rawId) {
|
||||
const schema = Validator.objectId()
|
||||
const { value, error } = schema.validate(rawId)
|
||||
const { value, error } = schema.validate(rawId.toString())
|
||||
if (error) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_ARGUMENT,
|
||||
|
||||
@@ -9,6 +9,10 @@ import { BaseService } from "medusa-interfaces"
|
||||
* @implements BaseService
|
||||
*/
|
||||
class UserService extends BaseService {
|
||||
static Events = {
|
||||
PASSWORD_RESET: "user.password_reset",
|
||||
}
|
||||
|
||||
constructor({ userModel, eventBusService }) {
|
||||
super()
|
||||
|
||||
@@ -245,6 +249,11 @@ class UserService extends BaseService {
|
||||
const expiry = Math.floor(Date.now() / 1000) + 60 * 15
|
||||
const payload = { user_id: user._id, exp: expiry }
|
||||
const token = jwt.sign(payload, secret)
|
||||
// Notify subscribers
|
||||
this.eventBus_.emit(UserService.Events.PASSWORD_RESET, {
|
||||
email: user.email,
|
||||
token,
|
||||
})
|
||||
return token
|
||||
}
|
||||
|
||||
|
||||
@@ -4522,6 +4522,14 @@ media-typer@0.3.0:
|
||||
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
||||
|
||||
medusa-core-utils@^0.3.0:
|
||||
version "0.1.39"
|
||||
resolved "https://registry.yarnpkg.com/medusa-core-utils/-/medusa-core-utils-0.1.39.tgz#d57816c9bd43f9a92883650c1e66add1665291df"
|
||||
integrity sha512-R8+U1ile7if+nR6Cjh5exunx0ETV0OfkWUUBUpz1KmHSDv0V0CcvQqU9lcZesPFDEbu3Y2iEjsCqidVA4nG2nQ==
|
||||
dependencies:
|
||||
"@hapi/joi" "^16.1.8"
|
||||
joi-objectid "^3.0.1"
|
||||
|
||||
medusa-interfaces@^0.1.27:
|
||||
version "0.1.27"
|
||||
resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-0.1.27.tgz#e77f9a9f82a7118eac8b35c1498ef8a5cec78898"
|
||||
@@ -4529,6 +4537,13 @@ medusa-interfaces@^0.1.27:
|
||||
dependencies:
|
||||
mongoose "^5.8.0"
|
||||
|
||||
medusa-test-utils@^0.3.0:
|
||||
version "0.1.39"
|
||||
resolved "https://registry.yarnpkg.com/medusa-test-utils/-/medusa-test-utils-0.1.39.tgz#b7c166006a2fa4f02e52ab3bfafc19a3ae787f3e"
|
||||
integrity sha512-M/Br8/HYvl7x2oLnme4NxdQwoyV0XUyOWiCyvPp7q1HUTB684lhJf1MikZVrcSjsh2L1rpyi3GRbKdf4cpJWvw==
|
||||
dependencies:
|
||||
mongoose "^5.8.0"
|
||||
|
||||
memory-pager@^1.0.2:
|
||||
version "1.5.0"
|
||||
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
|
||||
|
||||
Reference in New Issue
Block a user