Merge branch 'integration/dummy-project' of github.com:medusajs/medusa into integration/dummy-project
This commit is contained in:
commit
e9c80e80a4
67 files changed
+13988
-178
No files matched your search
Generated
+6951
File diff suppressed because it is too large.
Load diff
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@babel/plugin-proposal-class-properties",
|
"@babel/plugin-proposal-class-properties",
|
||||||
"@babel/plugin-transform-instanceof"
|
"@babel/plugin-transform-instanceof",
|
||||||
|
"@babel/plugin-transform-classes"
|
||||||
],
|
],
|
||||||
"presets": ["@babel/preset-env"],
|
"presets": ["@babel/preset-env"],
|
||||||
"env": {
|
"env": {
|
||||||
@@ -9,4 +10,4 @@
|
|||||||
"plugins": ["@babel/plugin-transform-runtime"]
|
"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",
|
"name": "medusa-fulfillment-manual",
|
||||||
"version": "0.1.27-alpha.0",
|
"version": "1.0",
|
||||||
"description": "A manual fulfillment provider for Medusa",
|
"description": "A manual fulfillment provider for Medusa",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
"jest": "^25.5.2"
|
"jest": "^25.5.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "babel src --out-dir . --ignore **/__tests__",
|
"build": "babel src -d dist",
|
||||||
"prepare": "cross-env NODE_ENV=production npm run build",
|
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||||
"watch": "babel -w src --out-dir . --ignore **/__tests__"
|
"watch": "babel -w src --out-dir . --ignore **/__tests__"
|
||||||
},
|
},
|
||||||
@@ -32,7 +32,6 @@
|
|||||||
"@babel/plugin-transform-instanceof": "^7.8.3",
|
"@babel/plugin-transform-instanceof": "^7.8.3",
|
||||||
"@babel/runtime": "^7.7.6",
|
"@babel/runtime": "^7.7.6",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^0.3.0",
|
"medusa-core-utils": "^0.3.0"
|
||||||
"medusa-interfaces": "^0.1.27-alpha.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { FulfillmentService } from "medusa-interfaces"
|
import { FulfillmentService } from "medusa-interfaces"
|
||||||
|
|
||||||
class ManualFulfillmentService extends FulfillmentService {
|
class ManualFulfillmentService extends FulfillmentService {
|
||||||
static identifier = "manual"
|
static identifier = "manual"
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -8,9 +8,11 @@ class ManualFulfillmentService extends FulfillmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getFulfillmentOptions() {
|
getFulfillmentOptions() {
|
||||||
return [{
|
return [
|
||||||
id: "manual-fulfillment"
|
{
|
||||||
}]
|
id: "manual-fulfillment",
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
validateFulfillmentData(data, cart) {
|
validateFulfillmentData(data, cart) {
|
||||||
@@ -18,11 +20,7 @@ class ManualFulfillmentService extends FulfillmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validateOption(data) {
|
validateOption(data) {
|
||||||
if (data.id === "manual-fulfillment") {
|
return true
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
canCalculate() {
|
canCalculate() {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@
|
|||||||
"medusa-test-utils": "^0.3.0"
|
"medusa-test-utils": "^0.3.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "babel src -d dist",
|
"build": "babel src -d .",
|
||||||
"prepare": "cross-env NODE_ENV=production npm run build",
|
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||||
"watch": "babel -w src --out-dir . --ignore **/__tests__",
|
"watch": "babel -w src --out-dir . --ignore **/__tests__",
|
||||||
"test": "jest"
|
"test": "jest"
|
||||||
@@ -42,4 +42,4 @@
|
|||||||
"body-parser": "^1.19.0"
|
"body-parser": "^1.19.0"
|
||||||
},
|
},
|
||||||
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
|
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
let event
|
let event
|
||||||
try {
|
try {
|
||||||
const stripeProviderService = req.resolve("pp_stripe")
|
const stripeProviderService = req.scope.resolve("pp_stripe")
|
||||||
event = stripeProviderService.constructWebhookEvent(req.body, signature)
|
event = stripeProviderService.constructWebhookEvent(req.body, signature)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
res.status(400).send(`Webhook Error: ${err.message}`)
|
res.status(400).send(`Webhook Error: ${err.message}`)
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ class StripeProviderService extends PaymentService {
|
|||||||
const paymentIntent = await this.stripe_.paymentIntents.create({
|
const paymentIntent = await this.stripe_.paymentIntents.create({
|
||||||
customer: stripeCustomerId,
|
customer: stripeCustomerId,
|
||||||
amount: amount * 100, // Stripe amount is in cents
|
amount: amount * 100, // Stripe amount is in cents
|
||||||
currency: currency_code
|
currency: currency_code,
|
||||||
})
|
})
|
||||||
|
|
||||||
return paymentIntent
|
return paymentIntent
|
||||||
@@ -134,7 +134,7 @@ class StripeProviderService extends PaymentService {
|
|||||||
const { id } = data
|
const { id } = data
|
||||||
const amount = this.totalsService_.getTotal(cart)
|
const amount = this.totalsService_.getTotal(cart)
|
||||||
return this.stripe_.paymentIntents.update(id, {
|
return this.stripe_.paymentIntents.update(id, {
|
||||||
amount
|
amount,
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw 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"
|
"winston": "^3.2.1"
|
||||||
},
|
},
|
||||||
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
|
"gitHead": "35e0930650d5f4aedf2610749cd131ae8b7e17cc"
|
||||||
}
|
}
|
||||||
@@ -43,6 +43,8 @@ export default async (req, res) => {
|
|||||||
delete value.variants
|
delete value.variants
|
||||||
|
|
||||||
const productService = req.scope.resolve("productService")
|
const productService = req.scope.resolve("productService")
|
||||||
|
const shippingProfileService = req.scope.resolve("shippingProfileService")
|
||||||
|
|
||||||
let newProduct = await productService.createDraft(value)
|
let newProduct = await productService.createDraft(value)
|
||||||
|
|
||||||
if (variants) {
|
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, [
|
newProduct = await productService.decorate(newProduct, [
|
||||||
"title",
|
"title",
|
||||||
"description",
|
"description",
|
||||||
|
|||||||
+5
@@ -12,6 +12,8 @@ describe("POST /admin/shipping-options", () => {
|
|||||||
name: "Test option",
|
name: "Test option",
|
||||||
region_id: "testregion",
|
region_id: "testregion",
|
||||||
provider_id: "test_provider",
|
provider_id: "test_provider",
|
||||||
|
data: { id: "test" },
|
||||||
|
profile_id: "test",
|
||||||
price: {
|
price: {
|
||||||
type: "flat_rate",
|
type: "flat_rate",
|
||||||
amount: 100,
|
amount: 100,
|
||||||
@@ -32,6 +34,7 @@ describe("POST /admin/shipping-options", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("returns 200", () => {
|
it("returns 200", () => {
|
||||||
|
console.log(subject)
|
||||||
expect(subject.status).toEqual(200)
|
expect(subject.status).toEqual(200)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -41,6 +44,8 @@ describe("POST /admin/shipping-options", () => {
|
|||||||
name: "Test option",
|
name: "Test option",
|
||||||
region_id: "testregion",
|
region_id: "testregion",
|
||||||
provider_id: "test_provider",
|
provider_id: "test_provider",
|
||||||
|
data: { id: "test" },
|
||||||
|
profile_id: "test",
|
||||||
price: {
|
price: {
|
||||||
type: "flat_rate",
|
type: "flat_rate",
|
||||||
amount: 100,
|
amount: 100,
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ export default async (req, res) => {
|
|||||||
name: Validator.string().required(),
|
name: Validator.string().required(),
|
||||||
region_id: Validator.string().required(),
|
region_id: Validator.string().required(),
|
||||||
provider_id: Validator.string().required(),
|
provider_id: Validator.string().required(),
|
||||||
data: Validator.object(),
|
profile_id: Validator.string().required(),
|
||||||
price: Validator.object().keys({
|
data: Validator.object().required(),
|
||||||
type: Validator.string().required(),
|
price: Validator.object()
|
||||||
amount: Validator.number().optional(),
|
.keys({
|
||||||
}),
|
type: Validator.string().required(),
|
||||||
|
amount: Validator.number().optional(),
|
||||||
|
})
|
||||||
|
.required(),
|
||||||
requirements: Validator.array()
|
requirements: Validator.array()
|
||||||
.items(
|
.items(
|
||||||
Validator.object({
|
Validator.object({
|
||||||
@@ -27,10 +30,17 @@ export default async (req, res) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const optionService = req.scope.resolve("shippingOptionService")
|
const optionService = req.scope.resolve("shippingOptionService")
|
||||||
|
const shippingProfileService = req.scope.resolve("shippingProfileService")
|
||||||
|
|
||||||
const data = await optionService.create(value)
|
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 })
|
res.status(200).json({ shipping_option: data })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
throw err
|
throw err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,7 @@ export default async (req, res) => {
|
|||||||
discounts: Validator.array().items({
|
discounts: Validator.array().items({
|
||||||
code: Validator.string(),
|
code: Validator.string(),
|
||||||
}),
|
}),
|
||||||
|
customer_id: Validator.string(),
|
||||||
})
|
})
|
||||||
|
|
||||||
const { value, error } = schema.validate(req.body)
|
const { value, error } = schema.validate(req.body)
|
||||||
@@ -27,6 +28,10 @@ export default async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
if (value.customer_id) {
|
||||||
|
await cartService.updateCustomerId(id, value.customer_id)
|
||||||
|
}
|
||||||
|
|
||||||
if (value.region_id) {
|
if (value.region_id) {
|
||||||
await cartService.setRegion(id, value.region_id)
|
await cartService.setRegion(id, value.region_id)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export default async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const customerService = req.scope.resolve("customerService")
|
const customerService = req.scope.resolve("customerService")
|
||||||
const customer = await customerService.create(value)
|
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 })
|
res.status(201).json({ customer: data })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw 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 servicesLoader from "./services"
|
||||||
import passportLoader from "./passport"
|
import passportLoader from "./passport"
|
||||||
import pluginsLoader from "./plugins"
|
import pluginsLoader from "./plugins"
|
||||||
import storeLoader from "./store"
|
import defaultsLoader from "./defaults"
|
||||||
import Logger from "./logger"
|
import Logger from "./logger"
|
||||||
|
|
||||||
export default async ({ directory: rootDirectory, expressApp }) => {
|
export default async ({ directory: rootDirectory, expressApp }) => {
|
||||||
@@ -58,8 +58,8 @@ export default async ({ directory: rootDirectory, expressApp }) => {
|
|||||||
await apiLoader({ container, rootDirectory, app: expressApp })
|
await apiLoader({ container, rootDirectory, app: expressApp })
|
||||||
Logger.info("API initialized")
|
Logger.info("API initialized")
|
||||||
|
|
||||||
await storeLoader({ container })
|
await defaultsLoader({ container })
|
||||||
Logger.info("Store initialized")
|
Logger.info("Defaults initialized")
|
||||||
|
|
||||||
return { container, dbConnection, app: expressApp }
|
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 = {
|
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: [] },
|
||||||
sku: { type: String, unique: true, sparse: true },
|
sku: { type: String, unique: true, sparse: true },
|
||||||
ean: { type: String, unique: true, sparse: true },
|
ean: { type: String, unique: true, sparse: true },
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ export const ShippingProfileServiceMock = {
|
|||||||
}
|
}
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}),
|
}),
|
||||||
|
retrieveDefault: jest.fn().mockImplementation(data => {
|
||||||
|
return Promise.resolve({ _id: IdMap.getId("default_shipping_profile") })
|
||||||
|
}),
|
||||||
list: jest.fn().mockImplementation(selector => {
|
list: jest.fn().mockImplementation(selector => {
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
return Promise.resolve([])
|
return Promise.resolve([])
|
||||||
|
|||||||
@@ -5,11 +5,13 @@ import { PaymentProviderServiceMock } from "../__mocks__/payment-provider"
|
|||||||
import { FulfillmentProviderServiceMock } from "../__mocks__/fulfillment-provider"
|
import { FulfillmentProviderServiceMock } from "../__mocks__/fulfillment-provider"
|
||||||
import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile"
|
import { ShippingProfileServiceMock } from "../__mocks__/shipping-profile"
|
||||||
import { TotalsServiceMock } from "../__mocks__/totals"
|
import { TotalsServiceMock } from "../__mocks__/totals"
|
||||||
|
import { EventBusServiceMock } from "../__mocks__/event-bus"
|
||||||
|
|
||||||
describe("OrderService", () => {
|
describe("OrderService", () => {
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
const orderService = new OrderService({
|
const orderService = new OrderService({
|
||||||
orderModel: OrderModelMock,
|
orderModel: OrderModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -54,6 +56,7 @@ describe("OrderService", () => {
|
|||||||
describe("update", () => {
|
describe("update", () => {
|
||||||
const orderService = new OrderService({
|
const orderService = new OrderService({
|
||||||
orderModel: OrderModelMock,
|
orderModel: OrderModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -182,6 +185,7 @@ describe("OrderService", () => {
|
|||||||
describe("cancel", () => {
|
describe("cancel", () => {
|
||||||
const orderService = new OrderService({
|
const orderService = new OrderService({
|
||||||
orderModel: OrderModelMock,
|
orderModel: OrderModelMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -252,6 +256,7 @@ describe("OrderService", () => {
|
|||||||
paymentProviderService: PaymentProviderServiceMock,
|
paymentProviderService: PaymentProviderServiceMock,
|
||||||
fulfillmentProviderService: FulfillmentProviderServiceMock,
|
fulfillmentProviderService: FulfillmentProviderServiceMock,
|
||||||
shippingProfileService: ShippingProfileServiceMock,
|
shippingProfileService: ShippingProfileServiceMock,
|
||||||
|
eventBusService: EventBusServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
|||||||
@@ -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(() => {
|
||||||
|
|||||||
@@ -370,6 +370,40 @@ class CartService extends BaseService {
|
|||||||
return result
|
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
|
* Sets the email of a cart
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import { BaseService } from "medusa-interfaces"
|
|||||||
* @implements BaseService
|
* @implements BaseService
|
||||||
*/
|
*/
|
||||||
class CustomerService extends BaseService {
|
class CustomerService extends BaseService {
|
||||||
|
static Events = {
|
||||||
|
PASSWORD_RESET: "customer.password_reset",
|
||||||
|
}
|
||||||
|
|
||||||
constructor({ customerModel, eventBusService }) {
|
constructor({ customerModel, eventBusService }) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
@@ -26,7 +30,7 @@ class CustomerService extends BaseService {
|
|||||||
*/
|
*/
|
||||||
validateId_(rawId) {
|
validateId_(rawId) {
|
||||||
const schema = Validator.objectId()
|
const schema = Validator.objectId()
|
||||||
const { value, error } = schema.validate(rawId)
|
const { value, error } = schema.validate(rawId.toString())
|
||||||
if (error) {
|
if (error) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_ARGUMENT,
|
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 expiry = Math.floor(Date.now() / 1000) + 60 * 15 // 15 minutes ahead
|
||||||
const payload = { customer_id: customer._id, exp: expiry }
|
const payload = { customer_id: customer._id, exp: expiry }
|
||||||
const token = jwt.sign(payload, secret)
|
const token = jwt.sign(payload, secret)
|
||||||
|
// Notify subscribers
|
||||||
// TODO: Call event layer to ensure that there is an email service that
|
this.eventBus_.emit(CustomerService.Events.PASSWORD_RESET, {
|
||||||
// sends the token.
|
email: customer.email,
|
||||||
|
token,
|
||||||
|
})
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ class DiscountService extends BaseService {
|
|||||||
*/
|
*/
|
||||||
validateId_(rawId) {
|
validateId_(rawId) {
|
||||||
const schema = Validator.objectId()
|
const schema = Validator.objectId()
|
||||||
const { value, error } = schema.validate(rawId)
|
const { value, error } = schema.validate(rawId.toString())
|
||||||
if (error) {
|
if (error) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_ARGUMENT,
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class FulfillmentProviderService {
|
|||||||
*/
|
*/
|
||||||
retrieveProvider(provider_id) {
|
retrieveProvider(provider_id) {
|
||||||
try {
|
try {
|
||||||
return this.container_.resolve(`fp_${provider_id}`)
|
return this.container_[`fp_${provider_id}`]
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.NOT_FOUND,
|
MedusaError.Types.NOT_FOUND,
|
||||||
|
|||||||
@@ -3,6 +3,12 @@ import { Validator, MedusaError } from "medusa-core-utils"
|
|||||||
import { BaseService } from "medusa-interfaces"
|
import { BaseService } from "medusa-interfaces"
|
||||||
|
|
||||||
class OrderService extends BaseService {
|
class OrderService extends BaseService {
|
||||||
|
static Events = {
|
||||||
|
PLACED: "order.placed",
|
||||||
|
UPDATED: "order.updated",
|
||||||
|
CANCELLED: "order.cancelled",
|
||||||
|
}
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
orderModel,
|
orderModel,
|
||||||
paymentProviderService,
|
paymentProviderService,
|
||||||
@@ -43,7 +49,7 @@ class OrderService extends BaseService {
|
|||||||
*/
|
*/
|
||||||
validateId_(rawId) {
|
validateId_(rawId) {
|
||||||
const schema = Validator.objectId()
|
const schema = Validator.objectId()
|
||||||
const { value, error } = schema.validate(rawId)
|
const { value, error } = schema.validate(rawId.toString())
|
||||||
if (error) {
|
if (error) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_ARGUMENT,
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
@@ -139,15 +145,30 @@ class OrderService extends BaseService {
|
|||||||
return order
|
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
|
* Creates an order
|
||||||
* @param {object} order - the order to create
|
* @param {object} order - the order to create
|
||||||
* @return {Promise} resolves to the creation result.
|
* @return {Promise} resolves to the creation result.
|
||||||
*/
|
*/
|
||||||
async create(order) {
|
async create(order) {
|
||||||
return this.orderModel_.create(order).catch(err => {
|
return this.orderModel_
|
||||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
.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 },
|
{ $set: updateFields },
|
||||||
{ runValidators: true }
|
{ runValidators: true }
|
||||||
)
|
)
|
||||||
|
.then(result => {
|
||||||
|
// Notify subscribers
|
||||||
|
this.eventBus_.emit(OrderService.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)
|
||||||
})
|
})
|
||||||
@@ -248,14 +274,23 @@ class OrderService extends BaseService {
|
|||||||
|
|
||||||
// TODO: cancel payment method
|
// TODO: cancel payment method
|
||||||
|
|
||||||
return this.orderModel_.updateOne(
|
return this.orderModel_
|
||||||
{
|
.updateOne(
|
||||||
_id: orderId,
|
{
|
||||||
},
|
_id: orderId,
|
||||||
{
|
},
|
||||||
$set: { status: "cancelled" },
|
{
|
||||||
}
|
$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(
|
return this.orderModel_
|
||||||
{
|
.updateOne(
|
||||||
_id: orderId,
|
{
|
||||||
},
|
_id: orderId,
|
||||||
{
|
},
|
||||||
$set: updateFields,
|
{
|
||||||
}
|
$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
|
* @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)
|
||||||
})
|
})
|
||||||
@@ -183,6 +196,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)
|
||||||
})
|
})
|
||||||
@@ -266,10 +283,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)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -303,7 +325,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 =>
|
||||||
@@ -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
|
// 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 =>
|
||||||
@@ -363,14 +389,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)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -404,14 +438,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)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -448,15 +490,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)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -504,16 +554,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) {
|
||||||
@@ -538,14 +595,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) {
|
||||||
@@ -638,6 +703,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)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class ShippingOptionService extends BaseService {
|
|||||||
*/
|
*/
|
||||||
validateId_(rawId) {
|
validateId_(rawId) {
|
||||||
const schema = Validator.objectId()
|
const schema = Validator.objectId()
|
||||||
const { value, error } = schema.validate(rawId)
|
const { value, error } = schema.validate(rawId.toString())
|
||||||
if (error) {
|
if (error) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_ARGUMENT,
|
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(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
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) {
|
validateId_(rawId) {
|
||||||
const schema = Validator.objectId()
|
const schema = Validator.objectId()
|
||||||
const { value, error } = schema.validate(rawId)
|
const { value, error } = schema.validate(rawId.toString())
|
||||||
if (error) {
|
if (error) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_ARGUMENT,
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
@@ -76,6 +76,27 @@ class ShippingProfileService extends BaseService {
|
|||||||
return profile
|
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.
|
* Creates a new shipping profile.
|
||||||
* @param {ShippingProfile} profile - the shipping profile to create from
|
* @param {ShippingProfile} profile - the shipping profile to create from
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ class StoreService extends BaseService {
|
|||||||
*/
|
*/
|
||||||
validateId_(rawId) {
|
validateId_(rawId) {
|
||||||
const schema = Validator.objectId()
|
const schema = Validator.objectId()
|
||||||
const { value, error } = schema.validate(rawId)
|
const { value, error } = schema.validate(rawId.toString())
|
||||||
if (error) {
|
if (error) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_ARGUMENT,
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
|
|||||||
@@ -9,6 +9,10 @@ import { BaseService } from "medusa-interfaces"
|
|||||||
* @implements BaseService
|
* @implements BaseService
|
||||||
*/
|
*/
|
||||||
class UserService extends BaseService {
|
class UserService extends BaseService {
|
||||||
|
static Events = {
|
||||||
|
PASSWORD_RESET: "user.password_reset",
|
||||||
|
}
|
||||||
|
|
||||||
constructor({ userModel, eventBusService }) {
|
constructor({ userModel, eventBusService }) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
@@ -245,6 +249,11 @@ class UserService extends BaseService {
|
|||||||
const expiry = Math.floor(Date.now() / 1000) + 60 * 15
|
const expiry = Math.floor(Date.now() / 1000) + 60 * 15
|
||||||
const payload = { user_id: user._id, exp: expiry }
|
const payload = { user_id: user._id, exp: expiry }
|
||||||
const token = jwt.sign(payload, secret)
|
const token = jwt.sign(payload, secret)
|
||||||
|
// Notify subscribers
|
||||||
|
this.eventBus_.emit(UserService.Events.PASSWORD_RESET, {
|
||||||
|
email: user.email,
|
||||||
|
token,
|
||||||
|
})
|
||||||
return 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"
|
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
|
||||||
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
|
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:
|
medusa-interfaces@^0.1.27:
|
||||||
version "0.1.27"
|
version "0.1.27"
|
||||||
resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-0.1.27.tgz#e77f9a9f82a7118eac8b35c1498ef8a5cec78898"
|
resolved "https://registry.yarnpkg.com/medusa-interfaces/-/medusa-interfaces-0.1.27.tgz#e77f9a9f82a7118eac8b35c1498ef8a5cec78898"
|
||||||
@@ -4529,6 +4537,13 @@ medusa-interfaces@^0.1.27:
|
|||||||
dependencies:
|
dependencies:
|
||||||
mongoose "^5.8.0"
|
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:
|
memory-pager@^1.0.2:
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
|
resolved "https://registry.yarnpkg.com/memory-pager/-/memory-pager-1.5.0.tgz#d8751655d22d384682741c972f2c3d6dfa3e66b5"
|
||||||
|
|||||||
Reference in new issue
Block a user