Merge master
This commit is contained in:
commit
48310ff384
29 files changed
+1024
-24
No files matched your search
@@ -0,0 +1,193 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports["default"] = exports.CartServiceMock = exports.carts = void 0;
|
||||||
|
|
||||||
|
var _medusaTestUtils = require("medusa-test-utils");
|
||||||
|
|
||||||
|
var carts = {
|
||||||
|
emptyCart: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("emptyCart"),
|
||||||
|
items: [],
|
||||||
|
region_id: _medusaTestUtils.IdMap.getId("testRegion"),
|
||||||
|
shipping_options: [{
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
||||||
|
profile_id: "default_profile",
|
||||||
|
data: {
|
||||||
|
some_data: "yes"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
frCart: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("fr-cart"),
|
||||||
|
email: "lebron@james.com",
|
||||||
|
title: "test",
|
||||||
|
region_id: _medusaTestUtils.IdMap.getId("region-france"),
|
||||||
|
items: [{
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("line"),
|
||||||
|
title: "merge line",
|
||||||
|
description: "This is a new line",
|
||||||
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
|
content: [{
|
||||||
|
unit_price: 8,
|
||||||
|
variant: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("eur-8-us-10")
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("product")
|
||||||
|
},
|
||||||
|
quantity: 1
|
||||||
|
}, {
|
||||||
|
unit_price: 10,
|
||||||
|
variant: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("eur-10-us-12")
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("product")
|
||||||
|
},
|
||||||
|
quantity: 1
|
||||||
|
}],
|
||||||
|
quantity: 10
|
||||||
|
}, {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("existingLine"),
|
||||||
|
title: "merge line",
|
||||||
|
description: "This is a new line",
|
||||||
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
|
content: {
|
||||||
|
unit_price: 10,
|
||||||
|
variant: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("eur-10-us-12")
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("product")
|
||||||
|
},
|
||||||
|
quantity: 1
|
||||||
|
},
|
||||||
|
quantity: 10
|
||||||
|
}],
|
||||||
|
shipping_methods: [{
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
||||||
|
profile_id: "default_profile"
|
||||||
|
}],
|
||||||
|
shipping_options: [{
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
||||||
|
profile_id: "default_profile"
|
||||||
|
}],
|
||||||
|
payment_sessions: [{
|
||||||
|
provider_id: "stripe",
|
||||||
|
data: {
|
||||||
|
id: "pi_123456789",
|
||||||
|
customer: _medusaTestUtils.IdMap.getId("not-lebron")
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
payment_method: {
|
||||||
|
provider_id: "stripe",
|
||||||
|
data: {
|
||||||
|
id: "pi_123456789",
|
||||||
|
customer: _medusaTestUtils.IdMap.getId("not-lebron")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shipping_address: {},
|
||||||
|
billing_address: {},
|
||||||
|
discounts: [],
|
||||||
|
customer_id: _medusaTestUtils.IdMap.getId("lebron")
|
||||||
|
},
|
||||||
|
frCartNoStripeCustomer: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("fr-cart-no-customer"),
|
||||||
|
title: "test",
|
||||||
|
region_id: _medusaTestUtils.IdMap.getId("region-france"),
|
||||||
|
items: [{
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("line"),
|
||||||
|
title: "merge line",
|
||||||
|
description: "This is a new line",
|
||||||
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
|
content: [{
|
||||||
|
unit_price: 8,
|
||||||
|
variant: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("eur-8-us-10")
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("product")
|
||||||
|
},
|
||||||
|
quantity: 1
|
||||||
|
}, {
|
||||||
|
unit_price: 10,
|
||||||
|
variant: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("eur-10-us-12")
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("product")
|
||||||
|
},
|
||||||
|
quantity: 1
|
||||||
|
}],
|
||||||
|
quantity: 10
|
||||||
|
}, {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("existingLine"),
|
||||||
|
title: "merge line",
|
||||||
|
description: "This is a new line",
|
||||||
|
thumbnail: "test-img-yeah.com/thumb",
|
||||||
|
content: {
|
||||||
|
unit_price: 10,
|
||||||
|
variant: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("eur-10-us-12")
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("product")
|
||||||
|
},
|
||||||
|
quantity: 1
|
||||||
|
},
|
||||||
|
quantity: 10
|
||||||
|
}],
|
||||||
|
shipping_methods: [{
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
||||||
|
profile_id: "default_profile"
|
||||||
|
}],
|
||||||
|
shipping_options: [{
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("freeShipping"),
|
||||||
|
profile_id: "default_profile"
|
||||||
|
}],
|
||||||
|
payment_sessions: [{
|
||||||
|
provider_id: "stripe",
|
||||||
|
data: {
|
||||||
|
id: "pi_123456789",
|
||||||
|
customer: _medusaTestUtils.IdMap.getId("not-lebron")
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
payment_method: {
|
||||||
|
provider_id: "stripe",
|
||||||
|
data: {
|
||||||
|
id: "pi_123456789",
|
||||||
|
customer: _medusaTestUtils.IdMap.getId("not-lebron")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
shipping_address: {},
|
||||||
|
billing_address: {},
|
||||||
|
discounts: [],
|
||||||
|
customer_id: _medusaTestUtils.IdMap.getId("vvd")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.carts = carts;
|
||||||
|
var CartServiceMock = {
|
||||||
|
retrieve: jest.fn().mockImplementation(function (cartId) {
|
||||||
|
if (cartId === _medusaTestUtils.IdMap.getId("fr-cart")) {
|
||||||
|
return Promise.resolve(carts.frCart);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cartId === _medusaTestUtils.IdMap.getId("emptyCart")) {
|
||||||
|
return Promise.resolve(carts.emptyCart);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}),
|
||||||
|
updatePaymentSession: jest.fn().mockImplementation(function (cartId, stripe, paymentIntent) {
|
||||||
|
return Promise.resolve();
|
||||||
|
})
|
||||||
|
};
|
||||||
|
exports.CartServiceMock = CartServiceMock;
|
||||||
|
var mock = jest.fn().mockImplementation(function () {
|
||||||
|
return CartServiceMock;
|
||||||
|
});
|
||||||
|
var _default = mock;
|
||||||
|
exports["default"] = _default;
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports["default"] = exports.CustomerServiceMock = void 0;
|
||||||
|
|
||||||
|
var _medusaTestUtils = require("medusa-test-utils");
|
||||||
|
|
||||||
|
var CustomerServiceMock = {
|
||||||
|
retrieve: jest.fn().mockImplementation(function (id) {
|
||||||
|
if (id === _medusaTestUtils.IdMap.getId("lebron")) {
|
||||||
|
return Promise.resolve({
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("lebron"),
|
||||||
|
first_name: "LeBron",
|
||||||
|
last_name: "James",
|
||||||
|
email: "lebron@james.com",
|
||||||
|
password_hash: "1234",
|
||||||
|
metadata: {
|
||||||
|
stripe_id: "cus_123456789_new"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id === _medusaTestUtils.IdMap.getId("vvd")) {
|
||||||
|
return Promise.resolve({
|
||||||
|
_id: _medusaTestUtils.IdMap.getId("vvd"),
|
||||||
|
first_name: "Virgil",
|
||||||
|
last_name: "Van Dijk",
|
||||||
|
email: "virg@vvd.com",
|
||||||
|
password_hash: "1234",
|
||||||
|
metadata: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(undefined);
|
||||||
|
}),
|
||||||
|
setMetadata: jest.fn().mockReturnValue(Promise.resolve())
|
||||||
|
};
|
||||||
|
exports.CustomerServiceMock = CustomerServiceMock;
|
||||||
|
var mock = jest.fn().mockImplementation(function () {
|
||||||
|
return CustomerServiceMock;
|
||||||
|
});
|
||||||
|
var _default = mock;
|
||||||
|
exports["default"] = _default;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports["default"] = exports.EventBusServiceMock = void 0;
|
||||||
|
var EventBusServiceMock = {
|
||||||
|
emit: jest.fn(),
|
||||||
|
subscribe: jest.fn()
|
||||||
|
};
|
||||||
|
exports.EventBusServiceMock = EventBusServiceMock;
|
||||||
|
var mock = jest.fn().mockImplementation(function () {
|
||||||
|
return EventBusServiceMock;
|
||||||
|
});
|
||||||
|
var _default = mock;
|
||||||
|
exports["default"] = _default;
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports["default"] = exports.StripeMock = void 0;
|
||||||
|
var StripeMock = {
|
||||||
|
customers: {
|
||||||
|
create: jest.fn().mockImplementation(function (data) {
|
||||||
|
if (data.email === "virg@vvd.com") {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "cus_vvd",
|
||||||
|
email: "virg@vvd.com"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.email === "lebron@james.com") {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "cus_lebron",
|
||||||
|
email: "lebron@james.com"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
paymentIntents: {
|
||||||
|
create: jest.fn().mockImplementation(function (data) {
|
||||||
|
if (data.customer === "cus_123456789_new") {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "pi_lebron",
|
||||||
|
amount: 100,
|
||||||
|
customer: "cus_123456789_new"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.customer === "cus_lebron") {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "pi_lebron",
|
||||||
|
amount: 100,
|
||||||
|
customer: "cus_lebron"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
retrieve: jest.fn().mockImplementation(function (data) {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "pi_lebron",
|
||||||
|
customer: "cus_lebron"
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
update: jest.fn().mockImplementation(function (pi, data) {
|
||||||
|
if (data.customer === "cus_lebron_2") {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "pi_lebron",
|
||||||
|
customer: "cus_lebron_2",
|
||||||
|
amount: 1000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "pi_lebron",
|
||||||
|
customer: "cus_lebron",
|
||||||
|
amount: 1000
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
capture: jest.fn().mockImplementation(function (data) {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "pi_lebron",
|
||||||
|
customer: "cus_lebron",
|
||||||
|
amount: 1000,
|
||||||
|
status: "succeeded"
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
cancel: jest.fn().mockImplementation(function (data) {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "pi_lebron",
|
||||||
|
customer: "cus_lebron",
|
||||||
|
status: "cancelled"
|
||||||
|
});
|
||||||
|
})
|
||||||
|
},
|
||||||
|
refunds: {
|
||||||
|
create: jest.fn().mockImplementation(function (data) {
|
||||||
|
return Promise.resolve({
|
||||||
|
id: "re_123",
|
||||||
|
payment_intent: "pi_lebron",
|
||||||
|
amount: 1000,
|
||||||
|
status: "succeeded"
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exports.StripeMock = StripeMock;
|
||||||
|
var stripe = jest.fn(function () {
|
||||||
|
return StripeMock;
|
||||||
|
});
|
||||||
|
var _default = stripe;
|
||||||
|
exports["default"] = _default;
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Object.defineProperty(exports, "__esModule", {
|
||||||
|
value: true
|
||||||
|
});
|
||||||
|
exports["default"] = exports.TotalsServiceMock = void 0;
|
||||||
|
var TotalsServiceMock = {
|
||||||
|
getTotal: jest.fn()
|
||||||
|
};
|
||||||
|
exports.TotalsServiceMock = TotalsServiceMock;
|
||||||
|
var mock = jest.fn().mockImplementation(function () {
|
||||||
|
return TotalsServiceMock;
|
||||||
|
});
|
||||||
|
var _default = mock;
|
||||||
|
exports["default"] = _default;
|
||||||
@@ -4,11 +4,11 @@ import store from "./routes/store"
|
|||||||
import errorHandler from "./middlewares/error-handler"
|
import errorHandler from "./middlewares/error-handler"
|
||||||
|
|
||||||
// guaranteed to get dependencies
|
// guaranteed to get dependencies
|
||||||
export default container => {
|
export default (container, config) => {
|
||||||
const app = Router()
|
const app = Router()
|
||||||
|
|
||||||
admin(app, container)
|
admin(app, container, config)
|
||||||
store(app, container)
|
store(app, container, config)
|
||||||
|
|
||||||
app.use(errorHandler())
|
app.use(errorHandler())
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { Router } from "express"
|
import { Router } from "express"
|
||||||
|
import cors from "cors"
|
||||||
|
|
||||||
import middlewares from "../../middlewares"
|
import middlewares from "../../middlewares"
|
||||||
import authRoutes from "./auth"
|
import authRoutes from "./auth"
|
||||||
import productRoutes from "./products"
|
import productRoutes from "./products"
|
||||||
@@ -8,17 +10,25 @@ import shippingOptionRoutes from "./shipping-options"
|
|||||||
import shippingProfileRoutes from "./shipping-profiles"
|
import shippingProfileRoutes from "./shipping-profiles"
|
||||||
import discountRoutes from "./discounts"
|
import discountRoutes from "./discounts"
|
||||||
import orderRoutes from "./orders"
|
import orderRoutes from "./orders"
|
||||||
|
import storeRoutes from "./store"
|
||||||
|
|
||||||
const route = Router()
|
const route = Router()
|
||||||
|
|
||||||
export default (app, container) => {
|
export default (app, container, config) => {
|
||||||
const middlewareService = container.resolve("middlewareService")
|
|
||||||
|
|
||||||
app.use("/admin", route)
|
app.use("/admin", route)
|
||||||
|
|
||||||
|
const adminCors = config.admin_cors || ""
|
||||||
|
route.use(
|
||||||
|
cors({
|
||||||
|
origin: adminCors.split(","),
|
||||||
|
credentials: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
// Unauthenticated routes
|
// Unauthenticated routes
|
||||||
authRoutes(route)
|
authRoutes(route)
|
||||||
|
|
||||||
|
const middlewareService = container.resolve("middlewareService")
|
||||||
// Calls all middleware that has been registered to run before authentication.
|
// Calls all middleware that has been registered to run before authentication.
|
||||||
middlewareService.usePreAuthentication(app)
|
middlewareService.usePreAuthentication(app)
|
||||||
|
|
||||||
@@ -35,6 +45,7 @@ export default (app, container) => {
|
|||||||
shippingProfileRoutes(route)
|
shippingProfileRoutes(route)
|
||||||
discountRoutes(route)
|
discountRoutes(route)
|
||||||
orderRoutes(route)
|
orderRoutes(route)
|
||||||
|
storeRoutes(route)
|
||||||
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { IdMap } from "medusa-test-utils"
|
||||||
|
import { request } from "../../../../../helpers/test-request"
|
||||||
|
import { StoreServiceMock } from "../../../../../services/__mocks__/store"
|
||||||
|
|
||||||
|
describe("POST /admin/store/currencies/:currency_code", () => {
|
||||||
|
describe("successful addition", () => {
|
||||||
|
let subject
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
subject = await request("POST", `/admin/store/currencies/dkk`, {
|
||||||
|
adminSession: {
|
||||||
|
jwt: {
|
||||||
|
userId: IdMap.getId("admin_user"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns 200", () => {
|
||||||
|
expect(subject.status).toEqual(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls service retrieve", () => {
|
||||||
|
expect(StoreServiceMock.addCurrency).toHaveBeenCalledTimes(1)
|
||||||
|
expect(StoreServiceMock.addCurrency).toHaveBeenCalledWith("dkk")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { IdMap } from "medusa-test-utils"
|
||||||
|
import { request } from "../../../../../helpers/test-request"
|
||||||
|
import { StoreServiceMock } from "../../../../../services/__mocks__/store"
|
||||||
|
|
||||||
|
describe("GET /admin/store", () => {
|
||||||
|
describe("successful addition", () => {
|
||||||
|
let subject
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
subject = await request("GET", `/admin/store`, {
|
||||||
|
adminSession: {
|
||||||
|
jwt: {
|
||||||
|
userId: IdMap.getId("admin_user"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns 200", () => {
|
||||||
|
expect(subject.status).toEqual(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls service retrieve", () => {
|
||||||
|
expect(StoreServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||||
|
expect(StoreServiceMock.retrieve).toHaveBeenCalledWith()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { IdMap } from "medusa-test-utils"
|
||||||
|
import { request } from "../../../../../helpers/test-request"
|
||||||
|
import { StoreServiceMock } from "../../../../../services/__mocks__/store"
|
||||||
|
|
||||||
|
describe("DELETE /admin/store/currencies/:currency_code", () => {
|
||||||
|
describe("successful addition", () => {
|
||||||
|
let subject
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
subject = await request("DELETE", `/admin/store/currencies/dkk`, {
|
||||||
|
adminSession: {
|
||||||
|
jwt: {
|
||||||
|
userId: IdMap.getId("admin_user"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns 200", () => {
|
||||||
|
expect(subject.status).toEqual(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls service retrieve", () => {
|
||||||
|
expect(StoreServiceMock.removeCurrency).toHaveBeenCalledTimes(1)
|
||||||
|
expect(StoreServiceMock.removeCurrency).toHaveBeenCalledWith("dkk")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import { IdMap } from "medusa-test-utils"
|
||||||
|
import { request } from "../../../../../helpers/test-request"
|
||||||
|
import { StoreServiceMock } from "../../../../../services/__mocks__/store"
|
||||||
|
|
||||||
|
describe("POST /admin/store", () => {
|
||||||
|
describe("successful creation", () => {
|
||||||
|
let subject
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
subject = await request("POST", "/admin/store", {
|
||||||
|
payload: {
|
||||||
|
name: "New Name",
|
||||||
|
},
|
||||||
|
adminSession: {
|
||||||
|
jwt: {
|
||||||
|
userId: IdMap.getId("admin_user"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns 200", () => {
|
||||||
|
expect(subject.status).toEqual(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls service update", () => {
|
||||||
|
expect(StoreServiceMock.update).toHaveBeenCalledTimes(1)
|
||||||
|
expect(StoreServiceMock.update).toHaveBeenCalledWith({
|
||||||
|
name: "New Name",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("successful creation", () => {
|
||||||
|
let subject
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
subject = await request("POST", "/admin/store", {
|
||||||
|
payload: {
|
||||||
|
currencies: ["DKK", "USD"],
|
||||||
|
},
|
||||||
|
adminSession: {
|
||||||
|
jwt: {
|
||||||
|
userId: IdMap.getId("admin_user"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns 200", () => {
|
||||||
|
expect(subject.status).toEqual(200)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("calls service update", () => {
|
||||||
|
expect(StoreServiceMock.update).toHaveBeenCalledTimes(1)
|
||||||
|
expect(StoreServiceMock.update).toHaveBeenCalledWith({
|
||||||
|
currencies: ["DKK", "USD"],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export default async (req, res) => {
|
||||||
|
const { currency_code } = req.params
|
||||||
|
|
||||||
|
try {
|
||||||
|
const storeService = req.scope.resolve("storeService")
|
||||||
|
const data = await storeService.addCurrency(currency_code)
|
||||||
|
res.status(200).json({ store: data })
|
||||||
|
} catch (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export default async (req, res) => {
|
||||||
|
try {
|
||||||
|
const storeService = req.scope.resolve("storeService")
|
||||||
|
const data = await storeService.retrieve()
|
||||||
|
res.status(200).json({ store: data })
|
||||||
|
} catch (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { Router } from "express"
|
||||||
|
import middlewares from "../../../middlewares"
|
||||||
|
|
||||||
|
const route = Router()
|
||||||
|
|
||||||
|
export default app => {
|
||||||
|
app.use("/store", route)
|
||||||
|
|
||||||
|
route.get("/", middlewares.wrap(require("./get-store").default))
|
||||||
|
route.post("/", middlewares.wrap(require("./update-store").default))
|
||||||
|
route.post(
|
||||||
|
"/currencies/:currency_code",
|
||||||
|
middlewares.wrap(require("./add-currency").default)
|
||||||
|
)
|
||||||
|
route.delete(
|
||||||
|
"/currencies/:currency_code",
|
||||||
|
middlewares.wrap(require("./remove-currency").default)
|
||||||
|
)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export default async (req, res) => {
|
||||||
|
const { currency_code } = req.params
|
||||||
|
|
||||||
|
try {
|
||||||
|
const storeService = req.scope.resolve("storeService")
|
||||||
|
const data = await storeService.removeCurrency(currency_code)
|
||||||
|
res.status(200).json({ store: data })
|
||||||
|
} catch (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { MedusaError, Validator } from "medusa-core-utils"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const schema = Validator.object().keys({
|
||||||
|
name: Validator.string(),
|
||||||
|
currencies: Validator.array().items(Validator.string()),
|
||||||
|
})
|
||||||
|
|
||||||
|
const { value, error } = schema.validate(req.body)
|
||||||
|
if (error) {
|
||||||
|
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const storeService = req.scope.resolve("storeService")
|
||||||
|
const data = await storeService.update(value)
|
||||||
|
res.status(200).json({ store: data })
|
||||||
|
} catch (err) {
|
||||||
|
throw err
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Router } from "express"
|
import { Router } from "express"
|
||||||
|
import cors from "cors"
|
||||||
|
|
||||||
import productRoutes from "./products"
|
import productRoutes from "./products"
|
||||||
import cartRoutes from "./carts"
|
import cartRoutes from "./carts"
|
||||||
@@ -9,9 +10,17 @@ import regionRoutes from "./regions"
|
|||||||
|
|
||||||
const route = Router()
|
const route = Router()
|
||||||
|
|
||||||
export default app => {
|
export default (app, container, config) => {
|
||||||
app.use("/store", route)
|
app.use("/store", route)
|
||||||
|
|
||||||
|
const storeCors = config.store_cors || ""
|
||||||
|
route.use(
|
||||||
|
cors({
|
||||||
|
origin: storeCors.split(","),
|
||||||
|
credentials: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
customerRoutes(route)
|
customerRoutes(route)
|
||||||
productRoutes(route)
|
productRoutes(route)
|
||||||
orderRoutes(route)
|
orderRoutes(route)
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
import { getConfigFile, createRequireFromPath } from "medusa-core-utils"
|
||||||
|
|
||||||
import routes from "../api"
|
import routes from "../api"
|
||||||
|
|
||||||
export default async ({ app, rootDirectory, container }) => {
|
export default async ({ app, rootDirectory, container }) => {
|
||||||
const { configModule } = getConfigFile(rootDirectory, `medusa-config`)
|
const { configModule } = getConfigFile(rootDirectory, `medusa-config`)
|
||||||
const config = (configModule && configModule.projectConfig) || {}
|
const config = (configModule && configModule.projectConfig) || {}
|
||||||
|
app.use("/", routes(container, config))
|
||||||
app.use("/", routes(container))
|
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
@@ -2,15 +2,12 @@ import express from "express"
|
|||||||
import bodyParser from "body-parser"
|
import bodyParser from "body-parser"
|
||||||
import session from "client-sessions"
|
import session from "client-sessions"
|
||||||
import cookieParser from "cookie-parser"
|
import cookieParser from "cookie-parser"
|
||||||
import cors from "cors"
|
|
||||||
import morgan from "morgan"
|
import morgan from "morgan"
|
||||||
|
|
||||||
import config from "../config"
|
import config from "../config"
|
||||||
|
|
||||||
export default async ({ app }) => {
|
export default async ({ app }) => {
|
||||||
app.enable("trust proxy")
|
app.enable("trust proxy")
|
||||||
|
|
||||||
app.use(cors())
|
|
||||||
app.use(
|
app.use(
|
||||||
morgan("combined", {
|
morgan("combined", {
|
||||||
skip: () => process.env.NODE_ENV === "test",
|
skip: () => process.env.NODE_ENV === "test",
|
||||||
|
|||||||
@@ -6,6 +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 Logger from "./logger"
|
import Logger from "./logger"
|
||||||
|
|
||||||
export default async ({ directory: rootDirectory, expressApp }) => {
|
export default async ({ directory: rootDirectory, expressApp }) => {
|
||||||
@@ -54,9 +55,12 @@ export default async ({ directory: rootDirectory, expressApp }) => {
|
|||||||
await pluginsLoader({ container, rootDirectory, app: expressApp })
|
await pluginsLoader({ container, rootDirectory, app: expressApp })
|
||||||
Logger.info("Plugins Intialized")
|
Logger.info("Plugins Intialized")
|
||||||
|
|
||||||
await apiLoader({ container, app: expressApp })
|
await apiLoader({ container, rootDirectory, app: expressApp })
|
||||||
Logger.info("API initialized")
|
Logger.info("API initialized")
|
||||||
|
|
||||||
|
await storeLoader({ container })
|
||||||
|
Logger.info("Store initialized")
|
||||||
|
|
||||||
return { container, dbConnection, app: expressApp }
|
return { container, dbConnection, app: expressApp }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { getConfigFile, createRequireFromPath } from "medusa-core-utils"
|
|||||||
import _ from "lodash"
|
import _ from "lodash"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import fs from "fs"
|
import fs from "fs"
|
||||||
import { asFunction } from "awilix"
|
import { asFunction, aliasTo } from "awilix"
|
||||||
import { sync as existsSync } from "fs-exists-cached"
|
import { sync as existsSync } from "fs-exists-cached"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -112,6 +112,7 @@ function registerServices(pluginDetails, container) {
|
|||||||
const files = glob.sync(`${pluginDetails.resolve}/services/[!__]*`, {})
|
const files = glob.sync(`${pluginDetails.resolve}/services/[!__]*`, {})
|
||||||
files.forEach(fn => {
|
files.forEach(fn => {
|
||||||
const loaded = require(fn).default
|
const loaded = require(fn).default
|
||||||
|
const name = formatRegistrationName(fn)
|
||||||
|
|
||||||
if (!(loaded.prototype instanceof BaseService)) {
|
if (!(loaded.prototype instanceof BaseService)) {
|
||||||
const logger = container.resolve("logger")
|
const logger = container.resolve("logger")
|
||||||
@@ -130,9 +131,8 @@ function registerServices(pluginDetails, container) {
|
|||||||
// Add the service directly to the container in order to make simple
|
// Add the service directly to the container in order to make simple
|
||||||
// resolution if we already know which payment provider we need to use
|
// resolution if we already know which payment provider we need to use
|
||||||
container.register({
|
container.register({
|
||||||
[`pp_${loaded.identifier}`]: asFunction(
|
[name]: asFunction(cradle => new loaded(cradle, pluginDetails.options)),
|
||||||
cradle => new loaded(cradle, pluginDetails.options)
|
[`pp_${loaded.identifier}`]: aliasTo(name),
|
||||||
),
|
|
||||||
})
|
})
|
||||||
} else if (loaded.prototype instanceof FulfillmentService) {
|
} else if (loaded.prototype instanceof FulfillmentService) {
|
||||||
// Register our payment providers to paymentProviders
|
// Register our payment providers to paymentProviders
|
||||||
@@ -144,9 +144,12 @@ function registerServices(pluginDetails, container) {
|
|||||||
// Add the service directly to the container in order to make simple
|
// Add the service directly to the container in order to make simple
|
||||||
// resolution if we already know which payment provider we need to use
|
// resolution if we already know which payment provider we need to use
|
||||||
container.register({
|
container.register({
|
||||||
[`fp_${loaded.identifier}`]: asFunction(
|
[name]: asFunction(cradle => new loaded(cradle, pluginDetails.options)),
|
||||||
cradle => new loaded(cradle, pluginDetails.options)
|
[`fp_${loaded.identifier}`]: aliasTo(name),
|
||||||
),
|
})
|
||||||
|
} else {
|
||||||
|
container.register({
|
||||||
|
[name]: asFunction(cradle => new loaded(cradle, pluginDetails.options)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* Registers all services in the services directory
|
||||||
|
*/
|
||||||
|
export default ({ container }) => {
|
||||||
|
const storeService = container.resolve("storeService")
|
||||||
|
return storeService.create()
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { IdMap } from "medusa-test-utils"
|
||||||
|
|
||||||
|
export const store = {
|
||||||
|
_id: IdMap.getId("store"),
|
||||||
|
name: "test store",
|
||||||
|
currencies: ["DKK"],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StoreModelMock = {
|
||||||
|
create: jest.fn().mockReturnValue(Promise.resolve()),
|
||||||
|
updateOne: jest.fn().mockImplementation((query, update) => {
|
||||||
|
return Promise.resolve()
|
||||||
|
}),
|
||||||
|
findOne: jest.fn().mockImplementation(query => {
|
||||||
|
return Promise.resolve(store)
|
||||||
|
}),
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import mongoose from "mongoose"
|
||||||
|
import { BaseModel } from "medusa-interfaces"
|
||||||
|
|
||||||
|
class StoreModel extends BaseModel {
|
||||||
|
static modelName = "Store"
|
||||||
|
static schema = {
|
||||||
|
name: { type: String, required: true, default: "Medusa Store" },
|
||||||
|
currencies: { type: [String], default: [] },
|
||||||
|
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StoreModel
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { IdMap } from "medusa-test-utils"
|
||||||
|
|
||||||
|
export const store = {
|
||||||
|
_id: IdMap.getId("store"),
|
||||||
|
name: "Test store",
|
||||||
|
currencies: ["DKK", "SEK", "GBP"],
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StoreServiceMock = {
|
||||||
|
addCurrency: jest.fn().mockImplementation(data => {
|
||||||
|
return Promise.resolve()
|
||||||
|
}),
|
||||||
|
removeCurrency: jest.fn().mockImplementation(data => {
|
||||||
|
return Promise.resolve()
|
||||||
|
}),
|
||||||
|
update: jest.fn().mockImplementation(data => {
|
||||||
|
return Promise.resolve()
|
||||||
|
}),
|
||||||
|
retrieve: jest.fn().mockImplementation(data => {
|
||||||
|
return Promise.resolve(store)
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
const mock = jest.fn().mockImplementation(() => {
|
||||||
|
return StoreServiceMock
|
||||||
|
})
|
||||||
|
|
||||||
|
export default mock
|
||||||
@@ -4,6 +4,7 @@ import RegionService from "../region"
|
|||||||
import { RegionModelMock } from "../../models/__mocks__/region"
|
import { RegionModelMock } from "../../models/__mocks__/region"
|
||||||
import { PaymentProviderServiceMock } from "../__mocks__/payment-provider"
|
import { PaymentProviderServiceMock } from "../__mocks__/payment-provider"
|
||||||
import { FulfillmentProviderServiceMock } from "../__mocks__/fulfillment-provider"
|
import { FulfillmentProviderServiceMock } from "../__mocks__/fulfillment-provider"
|
||||||
|
import { StoreServiceMock } from "../__mocks__/store"
|
||||||
|
|
||||||
describe("RegionService", () => {
|
describe("RegionService", () => {
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
@@ -14,6 +15,7 @@ describe("RegionService", () => {
|
|||||||
it("successfully creates a new region", async () => {
|
it("successfully creates a new region", async () => {
|
||||||
const regionService = new RegionService({
|
const regionService = new RegionService({
|
||||||
regionModel: RegionModelMock,
|
regionModel: RegionModelMock,
|
||||||
|
storeService: StoreServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
await regionService.create({
|
await regionService.create({
|
||||||
@@ -37,6 +39,7 @@ describe("RegionService", () => {
|
|||||||
regionModel: RegionModelMock,
|
regionModel: RegionModelMock,
|
||||||
paymentProviderService: PaymentProviderServiceMock,
|
paymentProviderService: PaymentProviderServiceMock,
|
||||||
fulfillmentProviderService: FulfillmentProviderServiceMock,
|
fulfillmentProviderService: FulfillmentProviderServiceMock,
|
||||||
|
storeService: StoreServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
await regionService.create({
|
await regionService.create({
|
||||||
@@ -103,6 +106,7 @@ describe("RegionService", () => {
|
|||||||
regionModel: RegionModelMock,
|
regionModel: RegionModelMock,
|
||||||
paymentProviderService: PaymentProviderServiceMock,
|
paymentProviderService: PaymentProviderServiceMock,
|
||||||
fulfillmentProviderService: FulfillmentProviderServiceMock,
|
fulfillmentProviderService: FulfillmentProviderServiceMock,
|
||||||
|
storeService: StoreServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
@@ -195,6 +199,7 @@ describe("RegionService", () => {
|
|||||||
regionModel: RegionModelMock,
|
regionModel: RegionModelMock,
|
||||||
paymentProviderService: PaymentProviderServiceMock,
|
paymentProviderService: PaymentProviderServiceMock,
|
||||||
fulfillmentProviderService: FulfillmentProviderServiceMock,
|
fulfillmentProviderService: FulfillmentProviderServiceMock,
|
||||||
|
storeService: StoreServiceMock,
|
||||||
})
|
})
|
||||||
|
|
||||||
await regionService.update(IdMap.getId("region-se"), {
|
await regionService.update(IdMap.getId("region-se"), {
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
import StoreService from "../store"
|
||||||
|
import { StoreModelMock } from "../../models/__mocks__/store"
|
||||||
|
import { IdMap } from "medusa-test-utils"
|
||||||
|
|
||||||
|
describe("StoreService", () => {
|
||||||
|
describe("retrieve", () => {
|
||||||
|
const storeService = new StoreService({
|
||||||
|
storeModel: StoreModelMock,
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("retrieves store", async () => {
|
||||||
|
await storeService.retrieve()
|
||||||
|
|
||||||
|
expect(StoreModelMock.findOne).toHaveBeenCalledTimes(1)
|
||||||
|
expect(StoreModelMock.findOne).toHaveBeenCalledWith()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("update", () => {
|
||||||
|
const storeService = new StoreService({
|
||||||
|
storeModel: StoreModelMock,
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("retrieves store", async () => {
|
||||||
|
await storeService.update({
|
||||||
|
name: "New Name",
|
||||||
|
currencies: ["DKK", "sek", "uSd"],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(StoreModelMock.findOne).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
expect(StoreModelMock.updateOne).toHaveBeenCalledTimes(1)
|
||||||
|
expect(StoreModelMock.updateOne).toHaveBeenCalledWith(
|
||||||
|
{ _id: IdMap.getId("store") },
|
||||||
|
{
|
||||||
|
$set: {
|
||||||
|
name: "New Name",
|
||||||
|
currencies: ["DKK", "SEK", "USD"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ runValidators: true }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("fails if currency not ok", async () => {
|
||||||
|
await expect(
|
||||||
|
storeService.update({
|
||||||
|
currencies: ["notacurrence"],
|
||||||
|
})
|
||||||
|
).rejects.toThrow("Invalid currency NOTACURRENCE")
|
||||||
|
|
||||||
|
expect(StoreModelMock.findOne).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("addCurrency", () => {
|
||||||
|
const storeService = new StoreService({
|
||||||
|
storeModel: StoreModelMock,
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("retrieves store", async () => {
|
||||||
|
await storeService.addCurrency("sek")
|
||||||
|
|
||||||
|
expect(StoreModelMock.findOne).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
expect(StoreModelMock.updateOne).toHaveBeenCalledTimes(1)
|
||||||
|
expect(StoreModelMock.updateOne).toHaveBeenCalledWith(
|
||||||
|
{ _id: IdMap.getId("store") },
|
||||||
|
{
|
||||||
|
$push: { currencies: "SEK" },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("fails if currency not ok", async () => {
|
||||||
|
await expect(storeService.addCurrency("notacurrence")).rejects.toThrow(
|
||||||
|
"Invalid currency NOTACURRENCE"
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(StoreModelMock.findOne).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("fails if currency already existis", async () => {
|
||||||
|
await expect(storeService.addCurrency("DKK")).rejects.toThrow(
|
||||||
|
"Currency already added"
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(StoreModelMock.findOne).toHaveBeenCalledTimes(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("removeCurrency", () => {
|
||||||
|
const storeService = new StoreService({
|
||||||
|
storeModel: StoreModelMock,
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("retrieves store", async () => {
|
||||||
|
await storeService.removeCurrency("sek")
|
||||||
|
|
||||||
|
expect(StoreModelMock.findOne).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
expect(StoreModelMock.updateOne).toHaveBeenCalledTimes(1)
|
||||||
|
expect(StoreModelMock.updateOne).toHaveBeenCalledWith(
|
||||||
|
{ _id: IdMap.getId("store") },
|
||||||
|
{
|
||||||
|
$pull: { currencies: "SEK" },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -2,7 +2,6 @@ import _ from "lodash"
|
|||||||
import { Validator, MedusaError } from "medusa-core-utils"
|
import { Validator, MedusaError } from "medusa-core-utils"
|
||||||
import { BaseService } from "medusa-interfaces"
|
import { BaseService } from "medusa-interfaces"
|
||||||
import { countries } from "../utils/countries"
|
import { countries } from "../utils/countries"
|
||||||
import { currencies } from "../utils/currencies"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides layer to manipulate regions.
|
* Provides layer to manipulate regions.
|
||||||
@@ -11,6 +10,7 @@ import { currencies } from "../utils/currencies"
|
|||||||
class RegionService extends BaseService {
|
class RegionService extends BaseService {
|
||||||
constructor({
|
constructor({
|
||||||
regionModel,
|
regionModel,
|
||||||
|
storeService,
|
||||||
paymentProviderService,
|
paymentProviderService,
|
||||||
fulfillmentProviderService,
|
fulfillmentProviderService,
|
||||||
}) {
|
}) {
|
||||||
@@ -19,6 +19,9 @@ class RegionService extends BaseService {
|
|||||||
/** @private @const {RegionModel} */
|
/** @private @const {RegionModel} */
|
||||||
this.regionModel_ = regionModel
|
this.regionModel_ = regionModel
|
||||||
|
|
||||||
|
/** @private @const {StoreService} */
|
||||||
|
this.storeService_ = storeService
|
||||||
|
|
||||||
/** @private @const {PaymentProviderService} */
|
/** @private @const {PaymentProviderService} */
|
||||||
this.paymentProviderService_ = paymentProviderService
|
this.paymentProviderService_ = paymentProviderService
|
||||||
|
|
||||||
@@ -69,7 +72,7 @@ class RegionService extends BaseService {
|
|||||||
|
|
||||||
if (region.currency_code) {
|
if (region.currency_code) {
|
||||||
region.currency_code = region.currency_code.toUpperCase()
|
region.currency_code = region.currency_code.toUpperCase()
|
||||||
this.validateCurrency_(region.currency_code)
|
await this.validateCurrency_(region.currency_code)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (region.countries) {
|
if (region.countries) {
|
||||||
@@ -123,8 +126,10 @@ class RegionService extends BaseService {
|
|||||||
* Validates a currency code. Will throw if the currency code doesn't exist.
|
* Validates a currency code. Will throw if the currency code doesn't exist.
|
||||||
* @param {string} currencyCode - an ISO currency code
|
* @param {string} currencyCode - an ISO currency code
|
||||||
*/
|
*/
|
||||||
validateCurrency_(currencyCode) {
|
async validateCurrency_(currencyCode) {
|
||||||
if (!currencies[currencyCode]) {
|
const store = await this.storeService_.retrieve()
|
||||||
|
|
||||||
|
if (!store.currencies.includes(currencyCode.toUpperCase())) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
MedusaError.Types.INVALID_DATA,
|
MedusaError.Types.INVALID_DATA,
|
||||||
"Invalid currency code"
|
"Invalid currency code"
|
||||||
|
|||||||
@@ -0,0 +1,188 @@
|
|||||||
|
import mongoose from "mongoose"
|
||||||
|
import bcrypt from "bcrypt"
|
||||||
|
import _ from "lodash"
|
||||||
|
import { Validator, MedusaError } from "medusa-core-utils"
|
||||||
|
import { BaseService } from "medusa-interfaces"
|
||||||
|
|
||||||
|
import { currencies } from "../utils/currencies"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides layer to manipulate store settings.
|
||||||
|
* @implements BaseService
|
||||||
|
*/
|
||||||
|
class StoreService extends BaseService {
|
||||||
|
constructor({ storeModel, eventBusService }) {
|
||||||
|
super()
|
||||||
|
|
||||||
|
/** @private @const {storeModel} */
|
||||||
|
this.storeModel_ = storeModel
|
||||||
|
|
||||||
|
/** @private @const {EventBus} */
|
||||||
|
this.eventBus_ = eventBusService
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to validate customer ids. Throws an error if the cast fails
|
||||||
|
* @param {string} rawId - the raw customer id to validate.
|
||||||
|
* @return {string} the validated id
|
||||||
|
*/
|
||||||
|
validateId_(rawId) {
|
||||||
|
const schema = Validator.objectId()
|
||||||
|
const { value, error } = schema.validate(rawId)
|
||||||
|
if (error) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
|
"The customerId could not be casted to an ObjectId"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a store if it doesn't already exist.
|
||||||
|
* @return {Promise<Store>} the store.
|
||||||
|
*/
|
||||||
|
async create() {
|
||||||
|
const store = await this.retrieve()
|
||||||
|
if (!store) {
|
||||||
|
return this.storeModel_.create({})
|
||||||
|
}
|
||||||
|
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the store settings. There is always a maximum of one store.
|
||||||
|
* @return {Promise<Store>} the customer document.
|
||||||
|
*/
|
||||||
|
retrieve() {
|
||||||
|
return this.storeModel_.findOne().catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a customer. Metadata updates and address updates should
|
||||||
|
* use dedicated methods, e.g. `setMetadata`, etc. The function
|
||||||
|
* will throw errors if metadata updates and address updates are attempted.
|
||||||
|
* @param {string} variantId - the id of the variant. Must be a string that
|
||||||
|
* can be casted to an ObjectId
|
||||||
|
* @param {object} update - an object with the update values.
|
||||||
|
* @return {Promise} resolves to the update result.
|
||||||
|
*/
|
||||||
|
async update(update) {
|
||||||
|
const store = await this.retrieve()
|
||||||
|
|
||||||
|
if (update.metadata) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
"Use setMetadata to update metadata fields"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (update.currencies) {
|
||||||
|
update.currencies = update.currencies.map(c => c.toUpperCase())
|
||||||
|
update.currencies.forEach(c => {
|
||||||
|
if (!currencies[c]) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Invalid currency ${c}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.storeModel_
|
||||||
|
.updateOne({ _id: store._id }, { $set: update }, { runValidators: true })
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a currency to the store
|
||||||
|
* @param {string} code - 3 character ISO currency code
|
||||||
|
* @return {Promise} result after update
|
||||||
|
*/
|
||||||
|
async addCurrency(code) {
|
||||||
|
code = code.toUpperCase()
|
||||||
|
const store = await this.retrieve()
|
||||||
|
|
||||||
|
if (!currencies[code]) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Invalid currency ${code}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (store.currencies.includes(code)) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Currency already added`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.storeModel_.updateOne(
|
||||||
|
{
|
||||||
|
_id: store._id,
|
||||||
|
},
|
||||||
|
{ $push: { currencies: code } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a currency from the store
|
||||||
|
* @param {string} code - 3 character ISO currency code
|
||||||
|
* @return {Promise} result after update
|
||||||
|
*/
|
||||||
|
async removeCurrency(code) {
|
||||||
|
const store = await this.retrieve()
|
||||||
|
code = code.toUpperCase()
|
||||||
|
return this.storeModel_.updateOne(
|
||||||
|
{
|
||||||
|
_id: store._id,
|
||||||
|
},
|
||||||
|
{ $pull: { currencies: code } }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decorates a store object.
|
||||||
|
* @param {Store} store - the store to decorate.
|
||||||
|
* @param {string[]} fields - the fields to include.
|
||||||
|
* @param {string[]} expandFields - fields to expand.
|
||||||
|
* @return {Store} return the decorated Store.
|
||||||
|
*/
|
||||||
|
async decorate(store, fields, expandFields = []) {
|
||||||
|
return store
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dedicated method to set metadata for a store.
|
||||||
|
* To ensure that plugins does not overwrite each
|
||||||
|
* others metadata fields, setMetadata is provided.
|
||||||
|
* @param {string} customerId - the customer to apply metadata to.
|
||||||
|
* @param {string} key - key for metadata field
|
||||||
|
* @param {string} value - value for metadata field.
|
||||||
|
* @return {Promise} resolves to the updated result.
|
||||||
|
*/
|
||||||
|
async setMetadata(key, value) {
|
||||||
|
const store = await this.retrieve()
|
||||||
|
if (typeof key !== "string") {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_ARGUMENT,
|
||||||
|
"Key type is invalid. Metadata keys must be strings"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyPath = `metadata.${key}`
|
||||||
|
return this.storeModel_
|
||||||
|
.updateOne({ _id: store._id }, { $set: { [keyPath]: value } })
|
||||||
|
.catch(err => {
|
||||||
|
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StoreService
|
||||||
Reference in new issue
Block a user