fix merge conflicts
This commit is contained in:
+184
-175
@@ -1,26 +1,32 @@
|
||||
title: Carts
|
||||
domain: store
|
||||
routes:
|
||||
- method: POST
|
||||
path: /carts
|
||||
- method: GET
|
||||
path: /carts/:id
|
||||
- method: POST
|
||||
path: /carts
|
||||
- method: POST
|
||||
path: /carts/:id
|
||||
- method: POST
|
||||
path: /carts/:id/complete-cart
|
||||
- method: POST
|
||||
path: /carts/:id/line-items
|
||||
- method: POST
|
||||
path: /carts/:id/line-items/:line_id
|
||||
- method: DELETE
|
||||
path: /carts/:id/line-items/:line_id
|
||||
- method: POST
|
||||
path: /carts/:id/payment-sessions
|
||||
- method: DELETE
|
||||
path: /carts/:id/payment-sessions/:provider_id
|
||||
- method: POST
|
||||
path: /carts/:id/payment-method
|
||||
path: /carts/:id/payment-session/update
|
||||
- method: POST
|
||||
path: /carts/:id/payment-sessions/:provider_id/refresh
|
||||
- method: POST
|
||||
path: /carts/:id/payment-session
|
||||
- method: POST
|
||||
path: /carts/:id/shipping-methods
|
||||
- method: DELETE
|
||||
path: /carts/:id/payment-sessions/:provider_id
|
||||
- method: DELETE
|
||||
path: /carts/:id/line-items/:line_id
|
||||
- method: DELETE
|
||||
path: /carts/:id/discounts/:code
|
||||
route: /carts
|
||||
@@ -40,7 +46,7 @@ endpoints:
|
||||
- name: items
|
||||
type: List
|
||||
required: false
|
||||
description: List of objects with `variantId` and `quantity`.
|
||||
description: List of objects with `variant_id` and `quantity`.
|
||||
description: >
|
||||
Creates a Cart within the given region and with the initial items. If no
|
||||
`region_id` is provided the cart will be associated with the first Region
|
||||
@@ -49,19 +55,11 @@ endpoints:
|
||||
- path: /:id
|
||||
method: GET
|
||||
title: Retrieve a Cart
|
||||
params:
|
||||
- name: id
|
||||
type: String
|
||||
description: Id of the cart.
|
||||
description: >
|
||||
Retrieves an existing cart.
|
||||
- path: /:id
|
||||
method: POST
|
||||
title: Update a Cart
|
||||
params:
|
||||
- name: id
|
||||
type: String
|
||||
description: Id of the cart.
|
||||
body:
|
||||
- name: region_id
|
||||
type: String
|
||||
@@ -84,17 +82,28 @@ endpoints:
|
||||
description: >
|
||||
A list of objects with `code` that represents a discount code to be
|
||||
applied to the cart.
|
||||
- name: customer_id
|
||||
type: String
|
||||
description: >
|
||||
A list of objects with `code` that represents a discount code to be
|
||||
applied to the cart.
|
||||
description: >
|
||||
Updates an existing cart with the provided data. Updating the `region_id`
|
||||
will change the prices of the items in the cart to match the prices
|
||||
defined for the region. You can apply multiple discounts at once by
|
||||
calling the endpoint with an array of discounts.
|
||||
- path: /:id/complete-cart
|
||||
method: POST
|
||||
title: Complete cart
|
||||
description: >
|
||||
Completes a cart. The following steps will be performed. Payment authorization
|
||||
is attempted and if more work is required, we simply return the cart for further updates.
|
||||
If payment is authorized and order is not yet created, we make sure to do so.
|
||||
The completion of a cart can be performed idempotently with a provided header `Idempotency-Key`.
|
||||
If not provided, we will generate one for the request.
|
||||
- path: /:id/line-items
|
||||
method: POST
|
||||
title: Add line item
|
||||
params:
|
||||
- name: id
|
||||
type: String
|
||||
body:
|
||||
- name: variant_id
|
||||
type: String
|
||||
@@ -114,13 +123,6 @@ endpoints:
|
||||
- path: /:id/line-items/:line_id
|
||||
method: POST
|
||||
title: Update line item
|
||||
params:
|
||||
- name: id
|
||||
type: String
|
||||
description: Id of the cart to update.
|
||||
- name: line_id
|
||||
type: String
|
||||
description: Id of the line to update.
|
||||
body:
|
||||
- name: quantity
|
||||
type: Integer
|
||||
@@ -132,84 +134,61 @@ endpoints:
|
||||
- path: /:id/line-items/:line_id
|
||||
method: DELETE
|
||||
title: Remove line item
|
||||
params:
|
||||
- name: id
|
||||
type: String
|
||||
description: Id of the cart.
|
||||
- name: line_id
|
||||
type: String
|
||||
description: Id of the line item to remove.
|
||||
description: >
|
||||
Removes a the given line item from the cart.
|
||||
- path: /:id/discounts/:code
|
||||
method: DELETE
|
||||
title: Remove discount code
|
||||
params:
|
||||
- name: id
|
||||
type: String
|
||||
description: Id of the cart.
|
||||
- name: code
|
||||
type: String
|
||||
description: The discount code to remove
|
||||
description: Removes a discount code from the cart.
|
||||
- path: /:id/payment-sessions
|
||||
method: POST
|
||||
title: Create payment sessions
|
||||
params:
|
||||
- name: id
|
||||
type: String
|
||||
description: Id of the cart.
|
||||
description: >
|
||||
Initializes the payment sessions that can be used to pay for the items of
|
||||
the cart. This is usually called when a customer proceeds to checkout.
|
||||
- path: /:id/payment-sessions/:provider_id
|
||||
method: DELETE
|
||||
title: Delete payment session
|
||||
params:
|
||||
- name: id
|
||||
type: String
|
||||
description: Id of the cart.
|
||||
- name: provider_id
|
||||
type: String
|
||||
description: >
|
||||
Id of the provider that created the payment session to remove.
|
||||
description: >
|
||||
Deletes a payment session. Note that this will initialize a new payment
|
||||
session with the provider. This is usually used if the payment session
|
||||
reaches a state that cannot be recovered from.
|
||||
- path: /:id/payment-method
|
||||
- path: /:id/payment-session
|
||||
method: POST
|
||||
title: Add payment method
|
||||
title: Set payment session on cart
|
||||
params:
|
||||
- name: id
|
||||
type: String
|
||||
description: Id of the cart.
|
||||
body:
|
||||
- name: provider_id
|
||||
type: String
|
||||
required: true
|
||||
description: >
|
||||
Id of the provider that offers the payment method.
|
||||
- name: data
|
||||
type: Dictionary
|
||||
description: >
|
||||
Used to hold any data that the payment method may need to process the
|
||||
payment. The requirements of `data` will be different across payment
|
||||
methods, and you should look at your installed payment providers to
|
||||
find out what to send.
|
||||
description: Id of the provider (e.g. stripe).
|
||||
description: >
|
||||
Adds or updates the payment method that will be used to pay for the items
|
||||
in the cart. The payment is not processed until the payment provider's
|
||||
authorization functionality is called. You should consult with your
|
||||
installed payment plugins to find information about how to authorize a
|
||||
payment.
|
||||
- path: /:id/payment-session/update
|
||||
method: POST
|
||||
title: Update payment session on cart
|
||||
params:
|
||||
- name: session
|
||||
type: Dictionary
|
||||
required: true
|
||||
description: >
|
||||
The session object needs to have a `provider_id` (e.g. stripe) and `data`, that holds the update data for the session.
|
||||
The data object can be anything that might be needed for the payment provider to process the
|
||||
payment. The requirements of `data` will be different across payment
|
||||
methods, and you should look at your installed payment providers to
|
||||
find out what to send..
|
||||
description: >
|
||||
Updates the payment session that will be used to pay for the items
|
||||
in the cart. The payment is not processed until the payment provider's
|
||||
authorization functionality is called. You should consult with your
|
||||
installed payment plugins to find information about how to authorize a
|
||||
payment.
|
||||
- path: /:id/shipping-methods
|
||||
method: POST
|
||||
title: Add shipping method
|
||||
params:
|
||||
- name: id
|
||||
type: String
|
||||
description: Id of the cart.
|
||||
body:
|
||||
- name: option_id
|
||||
type: String
|
||||
@@ -226,115 +205,145 @@ endpoints:
|
||||
response: |
|
||||
{
|
||||
"cart": {
|
||||
"_id": "5f68a234d694d000217a4d64",
|
||||
"customer_id": "",
|
||||
"region_id": "5f4cd57a5d1e3200214c0e4e",
|
||||
"email": "",
|
||||
"id": "cart_1234",
|
||||
"email": null,
|
||||
"billing_address_id": null,
|
||||
"billing_address": null,
|
||||
"shipping_address_id": "addr_1234",
|
||||
"shipping_address": {
|
||||
"_id": "5f68a234d694d000217a4d65",
|
||||
"country_code": "US"
|
||||
"id": "addr_1234",
|
||||
"customer_id": null,
|
||||
"company": null,
|
||||
"first_name": null,
|
||||
"last_name": null,
|
||||
"address_1": null,
|
||||
"address_2": null,
|
||||
"city": null,
|
||||
"country_code": "dk",
|
||||
"province": null,
|
||||
"postal_code": null,
|
||||
"phone": null,
|
||||
"created_at": "2021-02-04T15:40:54.691Z",
|
||||
"updated_at": "2021-02-04T15:40:54.691Z",
|
||||
"deleted_at": null,
|
||||
"metadata": null
|
||||
},
|
||||
"items": [{
|
||||
"id": "item_1234",
|
||||
"cart_id": "cart_1234",
|
||||
"order_id": null,
|
||||
"swap_id": null,
|
||||
"claim_order_id": null,
|
||||
"title": "Terry Towel",
|
||||
"description": "30x30",
|
||||
"thumbnail": "",
|
||||
"is_giftcard": false,
|
||||
"should_merge": true,
|
||||
"allow_discounts": true,
|
||||
"has_shipping": false,
|
||||
"unit_price": 6000,
|
||||
"variant_id": "variant_1234",
|
||||
"variant": {
|
||||
"id": "variant_1234",
|
||||
"title": "30x30",
|
||||
"product_id": "prod_1234",
|
||||
"sku": null,
|
||||
"barcode": null,
|
||||
"ean": null,
|
||||
"upc": null,
|
||||
"inventory_quantity": 9999,
|
||||
"allow_backorder": false,
|
||||
"manage_inventory": true,
|
||||
"hs_code": null,
|
||||
"origin_country": null,
|
||||
"mid_code": null,
|
||||
"material": null,
|
||||
"weight": null,
|
||||
"length": null,
|
||||
"height": null,
|
||||
"width": null,
|
||||
"created_at": "2021-01-19T17:09:01.864Z",
|
||||
"updated_at": "2021-02-02T02:43:17.718Z",
|
||||
"deleted_at": null,
|
||||
"metadata": null
|
||||
},
|
||||
"quantity": 1,
|
||||
"fulfilled_quantity": null,
|
||||
"returned_quantity": null,
|
||||
"shipped_quantity": null,
|
||||
"created_at": "2021-02-04T15:42:12.514Z",
|
||||
"updated_at": "2021-02-04T15:42:12.514Z",
|
||||
"metadata": {}
|
||||
}],
|
||||
"region_id": "reg_1234",
|
||||
"region": {
|
||||
"id": "reg_1234",
|
||||
"name": "Denmark",
|
||||
"currency_code": "dkk",
|
||||
"tax_rate": "25",
|
||||
"tax_code": null,
|
||||
"countries": [{
|
||||
"id": 60,
|
||||
"iso_2": "dk",
|
||||
"iso_3": "dnk",
|
||||
"num_code": 208,
|
||||
"name": "DENMARK",
|
||||
"display_name": "Denmark",
|
||||
"region_id": "reg_1234"
|
||||
}],
|
||||
"payment_providers": [{
|
||||
"id": "stripe",
|
||||
"is_installed": true
|
||||
}],
|
||||
"fulfillment_providers": [{
|
||||
"id": "manual",
|
||||
"is_installed": true
|
||||
}],
|
||||
"created_at": "2021-01-19T17:09:01.864Z",
|
||||
"updated_at": "2021-01-21T15:44:05.492Z",
|
||||
"deleted_at": null,
|
||||
"metadata": null
|
||||
},
|
||||
"items": [
|
||||
{
|
||||
"is_giftcard": false,
|
||||
"has_shipping": false,
|
||||
"returned": false,
|
||||
"fulfilled": false,
|
||||
"fulfilled_quantity": 0,
|
||||
"returned_quantity": 0,
|
||||
"_id": "5f72f79c2834b400216b1a54",
|
||||
"title": "Line Item",
|
||||
"description": "240x260",
|
||||
"quantity": 1,
|
||||
"thumbnail": "https://example-thumbnail.com",
|
||||
"content": {
|
||||
"unit_price": 303.2,
|
||||
"variant": {
|
||||
"_id": "5f4cf81a2ac41700211f6e63",
|
||||
"barcode": "",
|
||||
"image": "",
|
||||
"published": true,
|
||||
"inventory_quantity": 11,
|
||||
"allow_backorder": false,
|
||||
"manage_inventory": true,
|
||||
"title": "1234",
|
||||
"sku": "SKU1234",
|
||||
"ean": "1111111111111",
|
||||
"options": [
|
||||
{
|
||||
"_id": "5f4cf81a2ac41700211f6e64",
|
||||
"value": "1234",
|
||||
"option_id": "5f4cf81a2ac41700211f6e62"
|
||||
}
|
||||
],
|
||||
"prices": [
|
||||
{
|
||||
"_id": "5f4cf81a2ac41700211f6e65",
|
||||
"currency_code": "SEK",
|
||||
"amount": 3180
|
||||
},
|
||||
{
|
||||
"_id": "5f4cf81a2ac41700211f6e66",
|
||||
"currency_code": "EUR",
|
||||
"amount": 303.2
|
||||
},
|
||||
{
|
||||
"_id": "5f4cf81a2ac41700211f6e67",
|
||||
"currency_code": "DKK",
|
||||
"amount": 2260
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"origin_country": "Portugal"
|
||||
}
|
||||
},
|
||||
"product": {
|
||||
"_id": "5f4cf81a2ac41700211f6e61",
|
||||
"description": "100% Organic",
|
||||
"tags": "",
|
||||
"is_giftcard": false,
|
||||
"images": [],
|
||||
"thumbnail": "https://example-thumbnail.com",
|
||||
"variants": [
|
||||
"5f4cf81a2ac41700211f6e63"
|
||||
],
|
||||
"published": false,
|
||||
"title": "Line Item",
|
||||
"options": [
|
||||
{
|
||||
"values": [],
|
||||
"_id": "5f4cf81a2ac41700211f6e62",
|
||||
"title": "Size"
|
||||
}
|
||||
]
|
||||
},
|
||||
"quantity": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"discounts": [],
|
||||
"payment_sessions": [],
|
||||
"gift_cards": [],
|
||||
"customer_id": null,
|
||||
"payment_sessions": [{
|
||||
"id": "ps_1234",
|
||||
"cart_id": "cart_1234",
|
||||
"provider_id": "stripe",
|
||||
"is_selected": true,
|
||||
"status": "pending",
|
||||
"data": {},
|
||||
"created_at": "2021-02-08T11:37:17.906Z",
|
||||
"updated_at": "2021-02-08T11:37:17.906Z",
|
||||
"idempotency_key": null
|
||||
}],
|
||||
"payment_id": null,
|
||||
"payment": null,
|
||||
"shipping_methods": [],
|
||||
"type": "default",
|
||||
"completed_at": null,
|
||||
"created_at": "2021-02-04T15:40:54.691Z",
|
||||
"updated_at": "2021-02-04T15:40:54.691Z",
|
||||
"deleted_at": null,
|
||||
"metadata": null,
|
||||
"idempotency_key": null,
|
||||
"payment_session": {
|
||||
"id": "ps_1234",
|
||||
"cart_id": "cart_1234",
|
||||
"provider_id": "stripe",
|
||||
"is_selected": true,
|
||||
"status": "pending",
|
||||
"data": {},
|
||||
"created_at": "2021-02-08T11:37:17.906Z",
|
||||
"updated_at": "2021-02-08T11:37:17.906Z",
|
||||
"idempotency_key": null
|
||||
},
|
||||
"shipping_total": 0,
|
||||
"discount_total": 0,
|
||||
"tax_total": 0,
|
||||
"subtotal": 303.2,
|
||||
"total": 303.2,
|
||||
"region": {
|
||||
"_id": "5f4cd57a5d1e3200214c0e4e",
|
||||
"tax_rate": 0,
|
||||
"countries": [
|
||||
"US"
|
||||
],
|
||||
"payment_providers": [
|
||||
"stripe"
|
||||
],
|
||||
"fulfillment_providers": [
|
||||
"shiphero"
|
||||
],
|
||||
"name": "United States",
|
||||
"currency_code": "EUR",
|
||||
"tax_code": "ABR"
|
||||
}
|
||||
"tax_total": 1500,
|
||||
"gift_card_total": 0,
|
||||
"subtotal": 6000,
|
||||
"total": 7500
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ routes:
|
||||
path: /customers/:id/reset-password-token
|
||||
route: /customers
|
||||
description: >
|
||||
Customers can create a login to view Order history and manage details.
|
||||
Customers can create a login to view order history and manage details.
|
||||
Customers must have unique emails.
|
||||
endpoints:
|
||||
- path: /
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
dist
|
||||
dist/
|
||||
node_modules
|
||||
*yarn-error.log
|
||||
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
const { dropDatabase } = require("pg-god");
|
||||
const path = require("path");
|
||||
|
||||
const setupServer = require("../../../helpers/setup-server");
|
||||
const { useApi } = require("../../../helpers/use-api");
|
||||
const { initDb } = require("../../../helpers/use-db");
|
||||
|
||||
const adminSeeder = require("../../helpers/admin-seeder");
|
||||
const productSeeder = require("../../helpers/product-seeder");
|
||||
|
||||
jest.setTimeout(30000);
|
||||
|
||||
describe("/admin/products", () => {
|
||||
let medusaProcess;
|
||||
let dbConnection;
|
||||
|
||||
beforeAll(async () => {
|
||||
const cwd = path.resolve(path.join(__dirname, "..", ".."));
|
||||
dbConnection = await initDb({ cwd });
|
||||
medusaProcess = await setupServer({ cwd });
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await dbConnection.close();
|
||||
await dropDatabase({ databaseName: "medusa-integration" });
|
||||
|
||||
medusaProcess.kill();
|
||||
});
|
||||
|
||||
describe("POST /admin/products", () => {
|
||||
beforeEach(async () => {
|
||||
try {
|
||||
await productSeeder(dbConnection);
|
||||
await adminSeeder(dbConnection);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
const manager = dbConnection.manager;
|
||||
await manager.query(`DELETE FROM "product_option_value"`);
|
||||
await manager.query(`DELETE FROM "product_option"`);
|
||||
await manager.query(`DELETE FROM "money_amount"`);
|
||||
await manager.query(`DELETE FROM "product_variant"`);
|
||||
await manager.query(`DELETE FROM "product"`);
|
||||
await manager.query(`DELETE FROM "product_collection"`);
|
||||
await manager.query(`DELETE FROM "product_tag"`);
|
||||
await manager.query(`DELETE FROM "product_type"`);
|
||||
await manager.query(
|
||||
`UPDATE "country" SET region_id=NULL WHERE iso_2 = 'us'`
|
||||
);
|
||||
await manager.query(`DELETE FROM "region"`);
|
||||
await manager.query(`DELETE FROM "user"`);
|
||||
});
|
||||
|
||||
it("creates a product", async () => {
|
||||
const api = useApi();
|
||||
|
||||
const payload = {
|
||||
title: "Test product",
|
||||
description: "test-product-description",
|
||||
type: { value: "test-type" },
|
||||
collection_id: "test-collection",
|
||||
tags: [{ value: "123" }, { value: "456" }],
|
||||
options: [{ title: "size" }, { title: "color" }],
|
||||
variants: [
|
||||
{
|
||||
title: "Test variant",
|
||||
inventory_quantity: 10,
|
||||
prices: [{ currency_code: "usd", amount: 100 }],
|
||||
options: [{ value: "large" }, { value: "green" }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const response = await api
|
||||
.post("/admin/products", payload, {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
expect(response.data.product).toEqual(
|
||||
expect.objectContaining({
|
||||
title: "Test product",
|
||||
handle: "test-product",
|
||||
tags: [
|
||||
expect.objectContaining({
|
||||
value: "123",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "456",
|
||||
}),
|
||||
],
|
||||
type: expect.objectContaining({
|
||||
value: "test-type",
|
||||
}),
|
||||
collection: expect.objectContaining({
|
||||
id: "test-collection",
|
||||
title: "Test collection",
|
||||
}),
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
title: "size",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
title: "color",
|
||||
}),
|
||||
],
|
||||
variants: [
|
||||
expect.objectContaining({
|
||||
title: "Test variant",
|
||||
prices: [
|
||||
expect.objectContaining({
|
||||
currency_code: "usd",
|
||||
amount: 100,
|
||||
}),
|
||||
],
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
value: "large",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
value: "green",
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("updates a product (update tags, delete collection, delete type)", async () => {
|
||||
const api = useApi();
|
||||
|
||||
const payload = {
|
||||
collection_id: null,
|
||||
type: null,
|
||||
tags: [{ value: "123" }],
|
||||
};
|
||||
|
||||
const response = await api
|
||||
.post("/admin/products/test-product", payload, {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
expect(response.data.product).toEqual(
|
||||
expect.objectContaining({
|
||||
tags: [
|
||||
expect.objectContaining({
|
||||
value: "123",
|
||||
}),
|
||||
],
|
||||
type: null,
|
||||
collection: null,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it("add option", async () => {
|
||||
const api = useApi();
|
||||
|
||||
const payload = {
|
||||
title: "should_add",
|
||||
};
|
||||
|
||||
const response = await api
|
||||
.post("/admin/products/test-product/options", payload, {
|
||||
headers: {
|
||||
Authorization: "Bearer test_token",
|
||||
},
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
expect(response.data.product).toEqual(
|
||||
expect.objectContaining({
|
||||
options: [
|
||||
expect.objectContaining({
|
||||
title: "should_add",
|
||||
product_id: "test-product",
|
||||
}),
|
||||
],
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,68 @@
|
||||
const {
|
||||
ProductCollection,
|
||||
ProductTag,
|
||||
ProductType,
|
||||
Region,
|
||||
Product,
|
||||
ShippingProfile,
|
||||
ProductVariant,
|
||||
} = require("@medusajs/medusa");
|
||||
|
||||
module.exports = async (connection, data = {}) => {
|
||||
const manager = connection.manager;
|
||||
|
||||
const defaultProfile = await manager.findOne(ShippingProfile, {
|
||||
type: "default",
|
||||
});
|
||||
|
||||
const coll = manager.create(ProductCollection, {
|
||||
id: "test-collection",
|
||||
title: "Test collection",
|
||||
});
|
||||
|
||||
await manager.save(coll);
|
||||
|
||||
const tag = manager.create(ProductTag, {
|
||||
id: "tag1",
|
||||
value: "123",
|
||||
});
|
||||
|
||||
await manager.save(tag);
|
||||
|
||||
const type = manager.create(ProductType, {
|
||||
id: "test-type",
|
||||
value: "test-type",
|
||||
});
|
||||
|
||||
await manager.save(type);
|
||||
|
||||
await manager.insert(Region, {
|
||||
id: "test-region",
|
||||
name: "Test Region",
|
||||
currency_code: "usd",
|
||||
tax_rate: 0,
|
||||
});
|
||||
|
||||
await manager.insert(Product, {
|
||||
id: "test-product",
|
||||
title: "Test product",
|
||||
profile_id: defaultProfile.id,
|
||||
description: "test-product-description",
|
||||
collection_id: "test-collection",
|
||||
type: { id: "test-type", value: "test-type" },
|
||||
tags: [
|
||||
{ id: "tag1", value: "123" },
|
||||
{ tag: "tag2", value: "456" },
|
||||
],
|
||||
options: [{ id: "test-option", title: "Default value" }],
|
||||
});
|
||||
|
||||
await manager.insert(ProductVariant, {
|
||||
id: "test-variant",
|
||||
inventory_quantity: 10,
|
||||
title: "Test variant",
|
||||
product_id: "test-product",
|
||||
prices: [{ id: "test-price", currency_code: "usd", amount: 100 }],
|
||||
options: [{ id: "test-variant-option", value: "Default variant" }],
|
||||
});
|
||||
};
|
||||
@@ -16,4 +16,4 @@
|
||||
"@babel/node": "^7.12.10",
|
||||
"babel-preset-medusa-package": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,4 +18,5 @@ module.exports = {
|
||||
`.cache`,
|
||||
],
|
||||
transform: { "^.+\\.[jt]s$": `<rootDir>/jest-transformer.js` },
|
||||
setupFilesAfterEnv: ["<rootDir>/integration-tests/setup.js"],
|
||||
};
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
const { dropDatabase } = require("pg-god");
|
||||
|
||||
afterAll(() => {
|
||||
dropDatabase({ databaseName: "medusa-integration" });
|
||||
});
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
],
|
||||
"command": {
|
||||
"publish": {
|
||||
"allowBranch": "master",
|
||||
"allowBranch": "release/*",
|
||||
"bump": "patch",
|
||||
"conventionalCommits": true,
|
||||
"message": "chore(release): Publish"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"typeorm": "^0.2.30"
|
||||
},
|
||||
"scripts": {
|
||||
"publish:next": "lerna publish --canary --preid next --dist-tag next",
|
||||
"bootstrap": "lerna bootstrap",
|
||||
"jest": "jest",
|
||||
"test": "jest",
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-file-spaces@1.1.2...medusa-file-spaces@1.1.3) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-file-spaces
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-file-spaces@1.1.1...medusa-file-spaces@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-file-spaces
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-file-spaces",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Digital Ocean Spaces file connector for Medusa",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -41,7 +41,7 @@
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2",
|
||||
"medusa-test-utils": "^1.1.3",
|
||||
"stripe": "^8.50.0"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-fulfillment-webshipper@1.1.1...medusa-fulfillment-webshipper@1.1.2) (2021-02-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* notifications ([#172](https://github.com/medusajs/medusa/issues/172)) ([7308946](https://github.com/medusajs/medusa/commit/7308946e567ed4e63e1ed3d9d31b30c4f1a73f0d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.1](https://github.com/medusajs/medusa/compare/medusa-fulfillment-webshipper@1.1.0...medusa-fulfillment-webshipper@1.1.1) (2021-02-03)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-fulfillment-webshipper",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.2",
|
||||
"description": "Webshipper Fulfillment provider for Medusa",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -422,8 +422,41 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
||||
/**
|
||||
* This plugin doesn't support shipment documents.
|
||||
*/
|
||||
async getShipmentDocuments() {
|
||||
return []
|
||||
async retrieveDocuments(fulfillmentData, documentType) {
|
||||
switch (documentType) {
|
||||
case "label":
|
||||
const labelRelation = fulfillmentData?.relationships?.labels
|
||||
if (labelRelation) {
|
||||
const docs = await this.retrieveRelationship(labelRelation)
|
||||
.then(({ data }) => data)
|
||||
.catch((_) => [])
|
||||
|
||||
return docs.map((d) => ({
|
||||
name: d.attributes.document_type,
|
||||
base_64: d.attributes.base64,
|
||||
type: "application/pdf",
|
||||
}))
|
||||
}
|
||||
return []
|
||||
|
||||
case "invoice":
|
||||
const docRelation = fulfillmentData?.relationships?.documents
|
||||
if (docRelation) {
|
||||
const docs = await this.retrieveRelationship(docRelation)
|
||||
.then(({ data }) => data)
|
||||
.catch((_) => [])
|
||||
|
||||
return docs.map((d) => ({
|
||||
name: d.attributes.document_type,
|
||||
base_64: d.attributes.base64,
|
||||
type: "application/pdf",
|
||||
}))
|
||||
}
|
||||
return []
|
||||
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.1](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.1.0...medusa-interfaces@1.1.1) (2021-02-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* notifications ([#172](https://github.com/medusajs/medusa/issues/172)) ([7308946](https://github.com/medusajs/medusa/commit/7308946e567ed4e63e1ed3d9d31b30c4f1a73f0d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [1.1.0](https://github.com/medusajs/medusa/compare/medusa-interfaces@1.0.14...medusa-interfaces@1.1.0) (2021-01-26)
|
||||
|
||||
**Note:** Version bump only for package medusa-interfaces
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-interfaces",
|
||||
"version": "1.1.0",
|
||||
"version": "1.1.1",
|
||||
"description": "Core interfaces for Medusa",
|
||||
"main": "dist/index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -3,4 +3,5 @@ export { default as BaseModel } from "./base-model"
|
||||
export { default as PaymentService } from "./payment-service"
|
||||
export { default as FulfillmentService } from "./fulfillment-service"
|
||||
export { default as FileService } from "./file-service"
|
||||
export { default as NotificationService } from "./notification-service"
|
||||
export { default as OauthService } from "./oauth-service"
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import BaseService from "./base-service"
|
||||
|
||||
/**
|
||||
* Interface for Notification Providers
|
||||
* @interface
|
||||
*/
|
||||
class BaseNotificationService extends BaseService {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
getIdentifier() {
|
||||
return this.constructor.identifier
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to retrieve documents related to a shipment.
|
||||
*/
|
||||
sendNotification(event, data) {
|
||||
throw new Error("Must be overridden by child")
|
||||
}
|
||||
|
||||
resendNotification(notification, config = {}) {
|
||||
throw new Error("Must be overridden by child")
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseNotificationService
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-payment-adyen@1.1.2...medusa-payment-adyen@1.1.3) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-payment-adyen
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-payment-adyen@1.1.1...medusa-payment-adyen@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-payment-adyen
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-payment-adyen",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Adyen Payment provider for Medusa Commerce",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -24,7 +24,7 @@
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^6.8.0",
|
||||
"jest": "^25.5.2",
|
||||
"medusa-test-utils": "^1.1.2"
|
||||
"medusa-test-utils": "^1.1.3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel src -d .",
|
||||
|
||||
@@ -3,6 +3,25 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.4](https://github.com/medusajs/medusa/compare/medusa-payment-klarna@1.1.3...medusa-payment-klarna@1.1.4) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-payment-klarna
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-payment-klarna@1.1.2...medusa-payment-klarna@1.1.3) (2021-02-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* expired klarna orders ([5ef13d4](https://github.com/medusajs/medusa/commit/5ef13d49c0a2f6d82c5c2342ad800749e41d46fb))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-payment-klarna@1.1.1...medusa-payment-klarna@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-payment-klarna
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-payment-klarna",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.4",
|
||||
"description": "Klarna Payment provider for Medusa Commerce",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -41,7 +41,7 @@
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2"
|
||||
"medusa-test-utils": "^1.1.3"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
}
|
||||
|
||||
@@ -397,6 +397,11 @@ class KlarnaProviderService extends PaymentService {
|
||||
return this.klarna_
|
||||
.post(`${this.klarnaOrderUrl_}/${paymentData.order_id}`, order)
|
||||
.then(({ data }) => data)
|
||||
.catch(async (_) => {
|
||||
return this.klarna_
|
||||
.post(this.klarnaOrderUrl_, order)
|
||||
.then(({ data }) => data)
|
||||
})
|
||||
}
|
||||
|
||||
return paymentData
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-optional-chaining",
|
||||
"@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,16 @@
|
||||
/lib
|
||||
node_modules
|
||||
.DS_store
|
||||
.env*
|
||||
/*.js
|
||||
!index.js
|
||||
yarn.lock
|
||||
|
||||
/dist
|
||||
|
||||
/api
|
||||
/services
|
||||
/models
|
||||
/subscribers
|
||||
/loaders
|
||||
/__mocks__
|
||||
@@ -0,0 +1,13 @@
|
||||
/lib
|
||||
node_modules
|
||||
.DS_store
|
||||
.env*
|
||||
/*.js
|
||||
!index.js
|
||||
yarn.lock
|
||||
src
|
||||
.gitignore
|
||||
.eslintrc
|
||||
.babelrc
|
||||
.prettierrc
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"semi": false,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "es5"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.0.3](https://github.com/medusajs/medusa/compare/medusa-payment-paypal@1.0.2...medusa-payment-paypal@1.0.3) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-payment-paypal
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.0.2](https://github.com/medusajs/medusa/compare/medusa-payment-paypal@1.0.1...medusa-payment-paypal@1.0.2) (2021-02-09)
|
||||
|
||||
**Note:** Version bump only for package medusa-payment-paypal
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 1.0.1 (2021-02-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds paypal ([#168](https://github.com/medusajs/medusa/issues/168)) ([#169](https://github.com/medusajs/medusa/issues/169)) ([427ae25](https://github.com/medusajs/medusa/commit/427ae25016bb3a22ebc05aa7b18017132846567c))
|
||||
@@ -0,0 +1,11 @@
|
||||
# medusa-payment-paypal
|
||||
|
||||
## Options
|
||||
|
||||
```
|
||||
sandbox: [default: false],
|
||||
client_id: "CLIENT_ID", REQUIRED
|
||||
client_secret: "CLIENT_SECRET", REQUIRED
|
||||
auth_webhook_id: REQUIRED for webhook to work
|
||||
```
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
// noop
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"name": "medusa-payment-paypal",
|
||||
"version": "1.0.3",
|
||||
"description": "Paypal Payment provider for Meduas Commerce",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/medusajs/medusa",
|
||||
"directory": "packages/medusa-payment-paypal"
|
||||
},
|
||||
"author": "Sebastian Rindom",
|
||||
"license": "MIT",
|
||||
"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-proposal-optional-chaining": "^7.12.7",
|
||||
"@babel/plugin-transform-classes": "^7.9.5",
|
||||
"@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",
|
||||
"medusa-test-utils": "^1.1.3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel src -d . --ignore **/__tests__",
|
||||
"prepare": "cross-env NODE_ENV=production npm run build",
|
||||
"watch": "babel -w src --out-dir . --ignore **/__tests__",
|
||||
"test": "jest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"medusa-interfaces": "1.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"@paypal/checkout-server-sdk": "^1.0.2",
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^1.1.0"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
}
|
||||
@@ -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,13 @@
|
||||
import { Router } from "express"
|
||||
import bodyParser from "body-parser"
|
||||
import middlewares from "../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default (app) => {
|
||||
app.use("/paypal", route)
|
||||
|
||||
route.use(bodyParser.json())
|
||||
route.post("/hooks", middlewares.wrap(require("./paypal").default))
|
||||
return app
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
export default async (req, res) => {
|
||||
const auth_algo = req.headers["paypal-auth-algo"]
|
||||
const cert_url = req.headers["paypal-cert-url"]
|
||||
const transmission_id = req.headers["paypal-transmission-id"]
|
||||
const transmission_sig = req.headers["paypal-transmission-sig"]
|
||||
const transmission_time = req.headers["paypal-transmission-time"]
|
||||
|
||||
const paypalService = req.scope.resolve("paypalProviderService")
|
||||
|
||||
try {
|
||||
await paypalService.verifyWebhook({
|
||||
auth_algo,
|
||||
cert_url,
|
||||
transmission_id,
|
||||
transmission_sig,
|
||||
transmission_time,
|
||||
webhook_event: req.body,
|
||||
})
|
||||
} catch (err) {
|
||||
res.sendStatus(401)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const authId = req.body.resource.id
|
||||
const auth = await paypalService.retrieveAuthorization(authId)
|
||||
|
||||
const order = await paypalService.retrieveOrderFromAuth(auth)
|
||||
|
||||
const purchaseUnit = order.purchase_units[0]
|
||||
const cartId = purchaseUnit.custom_id
|
||||
|
||||
const manager = req.scope.resolve("manager")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
|
||||
await manager.transaction(async (m) => {
|
||||
const order = await orderService
|
||||
.withTransaction(m)
|
||||
.retrieveByCartId(cartId)
|
||||
.catch((_) => undefined)
|
||||
|
||||
if (!order) {
|
||||
await cartService.withTransaction(m).setPaymentSession(cartId, "paypal")
|
||||
await cartService.withTransaction(m).authorizePayment(cartId)
|
||||
await orderService.withTransaction(m).createFromCart(cartId)
|
||||
}
|
||||
})
|
||||
|
||||
res.sendStatus(200)
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
res.sendStatus(409)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
import _ from "lodash"
|
||||
import PayPal from "@paypal/checkout-server-sdk"
|
||||
import { PaymentService } from "medusa-interfaces"
|
||||
|
||||
class PayPalProviderService extends PaymentService {
|
||||
static identifier = "paypal"
|
||||
|
||||
constructor({ customerService, totalsService, regionService }, options) {
|
||||
super()
|
||||
|
||||
/**
|
||||
* Required PayPal options:
|
||||
* {
|
||||
* sandbox: [default: false],
|
||||
* client_id: "CLIENT_ID", REQUIRED
|
||||
* client_secret: "CLIENT_SECRET", REQUIRED
|
||||
* auth_webhook_id: REQUIRED for webhook to work
|
||||
* }
|
||||
*/
|
||||
this.options_ = options
|
||||
|
||||
let environment
|
||||
if (this.options_.sandbox) {
|
||||
environment = new PayPal.core.SandboxEnvironment(
|
||||
options.client_id,
|
||||
options.client_secret
|
||||
)
|
||||
} else {
|
||||
environment = new PayPal.core.LiveEnvironment(
|
||||
options.client_id,
|
||||
options.client_secret
|
||||
)
|
||||
}
|
||||
|
||||
/** @private @const {PayPalHttpClient} */
|
||||
this.paypal_ = new PayPal.core.PayPalHttpClient(environment)
|
||||
|
||||
/** @private @const {CustomerService} */
|
||||
this.customerService_ = customerService
|
||||
|
||||
/** @private @const {RegionService} */
|
||||
this.regionService_ = regionService
|
||||
|
||||
/** @private @const {TotalsService} */
|
||||
this.totalsService_ = totalsService
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches an open PayPal order and maps its status to Medusa payment
|
||||
* statuses.
|
||||
* @param {object} paymentData - the data stored with the payment
|
||||
* @returns {Promise<string>} the status of the order
|
||||
*/
|
||||
async getStatus(paymentData) {
|
||||
const order = await this.retrievePayment(paymentData)
|
||||
|
||||
let status = "pending"
|
||||
|
||||
switch (order.status) {
|
||||
case "CREATED":
|
||||
return "pending"
|
||||
case "COMPLETED":
|
||||
return "authorized"
|
||||
case "SAVED":
|
||||
case "APPROVED":
|
||||
case "PAYER_ACTION_REQUIRED":
|
||||
return "requires_more"
|
||||
case "VOIDED":
|
||||
return "canceled"
|
||||
// return "captured"
|
||||
default:
|
||||
return status
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Not supported
|
||||
*/
|
||||
async retrieveSavedMethods(customer) {
|
||||
return Promise.resolve([])
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PayPal order, with an Authorize intent. The plugin doesn't
|
||||
* support shipping details at the moment.
|
||||
* Reference docs: https://developer.paypal.com/docs/api/orders/v2/
|
||||
* @param {object} cart - cart to create a payment for
|
||||
* @returns {object} the data to be stored with the payment session.
|
||||
*/
|
||||
async createPayment(cart) {
|
||||
const { region_id } = cart
|
||||
const { currency_code } = await this.regionService_.retrieve(region_id)
|
||||
|
||||
const amount = await this.totalsService_.getTotal(cart)
|
||||
|
||||
const request = new PayPal.orders.OrdersCreateRequest()
|
||||
request.requestBody({
|
||||
intent: "AUTHORIZE",
|
||||
application_context: {
|
||||
shipping_preference: "NO_SHIPPING",
|
||||
},
|
||||
purchase_units: [
|
||||
{
|
||||
custom_id: cart.id,
|
||||
amount: {
|
||||
currency_code: currency_code.toUpperCase(),
|
||||
value: (amount / 100).toFixed(2),
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const res = await this.paypal_.execute(request)
|
||||
|
||||
return { id: res.result.id }
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a PayPal order.
|
||||
* @param {object} data - the data stored with the payment
|
||||
* @returns {Promise<object>} PayPal order
|
||||
*/
|
||||
async retrievePayment(data) {
|
||||
try {
|
||||
const request = new PayPal.orders.OrdersGetRequest(data.id)
|
||||
const res = await this.paypal_.execute(request)
|
||||
return res.result
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the payment data from a payment session
|
||||
* @param {object} session - the session to fetch payment data for.
|
||||
* @returns {Promise<object>} the PayPal order object
|
||||
*/
|
||||
async getPaymentData(session) {
|
||||
try {
|
||||
return this.retrievePayment(session.data)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method does not call the PayPal authorize function, but fetches the
|
||||
* status of the payment as it is expected to have been authorized client side.
|
||||
* @param {object} session - payment session
|
||||
* @param {object} context - properties relevant to current context
|
||||
* @returns {Promise<{ status: string, data: object }>} result with data and status
|
||||
*/
|
||||
async authorizePayment(session, context = {}) {
|
||||
const stat = await this.getStatus(session.data)
|
||||
|
||||
try {
|
||||
return { data: session.data, status: stat }
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the data stored with the payment session.
|
||||
* @param {object} data - the currently stored data.
|
||||
* @param {object} update - the update data to store.
|
||||
* @returns {object} the merged data of the two arguments.
|
||||
*/
|
||||
async updatePaymentData(data, update) {
|
||||
try {
|
||||
return {
|
||||
...data,
|
||||
...update.data,
|
||||
}
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the PayPal order.
|
||||
* @param {object} sessionData - payment session data.
|
||||
* @param {object} cart - the cart to update by.
|
||||
* @returns {object} the resulting order object.
|
||||
*/
|
||||
async updatePayment(sessionData, cart) {
|
||||
try {
|
||||
const { region_id } = cart
|
||||
const { currency_code } = await this.regionService_.retrieve(region_id)
|
||||
|
||||
const request = new PayPal.orders.OrdersPatchRequest(sessionData.id)
|
||||
request.requestBody([
|
||||
{
|
||||
op: "replace",
|
||||
path: "/purchase_units/@reference_id=='default'",
|
||||
value: {
|
||||
amount: {
|
||||
currency_code: currency_code.toUpperCase(),
|
||||
value: (cart.total / 100).toFixed(),
|
||||
},
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
await this.paypal_.execute(request)
|
||||
|
||||
return sessionData
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Not suported
|
||||
*/
|
||||
async deletePayment(payment) {
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures a previously authorized order.
|
||||
* @param {object} payment - the payment to capture
|
||||
* @returns {object} the PayPal order
|
||||
*/
|
||||
async capturePayment(payment) {
|
||||
const { purchase_units } = payment.data
|
||||
|
||||
const id = purchase_units[0].payments.authorizations[0].id
|
||||
|
||||
try {
|
||||
const request = new PayPal.payments.AuthorizationsCaptureRequest(id)
|
||||
await this.paypal_.execute(request)
|
||||
return this.retrievePayment(payment.data)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refunds a given amount.
|
||||
* @param {object} payment - payment to refund
|
||||
* @param {number} amountToRefund - amount to refund
|
||||
* @returns {string} the resulting PayPal order
|
||||
*/
|
||||
async refundPayment(payment, amountToRefund) {
|
||||
const { purchase_units } = payment.data
|
||||
|
||||
try {
|
||||
const payments = purchase_units[0].payments
|
||||
if (!(payments && payments.captures.length)) {
|
||||
throw new Error("Order not yet captured")
|
||||
}
|
||||
|
||||
const payId = payments.captures[0].id
|
||||
const request = new PayPal.payments.CapturesRefundRequest(payId)
|
||||
|
||||
request.requestBody({
|
||||
amount: {
|
||||
currency_code: payment.currency_code.toUpperCase(),
|
||||
value: (amountToRefund / 100).toFixed(),
|
||||
},
|
||||
})
|
||||
|
||||
await this.paypal_.execute(request)
|
||||
|
||||
return this.retrievePayment(payment.id)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels payment for Stripe payment intent.
|
||||
* @param {object} paymentData - payment method data from cart
|
||||
* @returns {object} canceled payment intent
|
||||
*/
|
||||
async cancelPayment(payment) {
|
||||
try {
|
||||
const { purchase_units } = payment.data
|
||||
if (payment.captured_at) {
|
||||
const payments = purchase_units[0].payments
|
||||
|
||||
const payId = payments.captures[0].id
|
||||
const request = new PayPal.payments.CapturesRefundRequest(payId)
|
||||
await this.paypal_.execute(request)
|
||||
} else {
|
||||
const id = purchase_units[0].payments.authorizations[0].id
|
||||
const request = new PayPal.payments.AuthorizationsVoidRequest(id)
|
||||
await this.paypal_.execute(request)
|
||||
}
|
||||
|
||||
return this.retrievePayment(payment.data)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a PayPal authorization object the method will find the order that
|
||||
* created the authorization, by following the HATEOAS link to the order.
|
||||
* @param {object} auth - the authorization object.
|
||||
* @returns {Promise<object>} the PayPal order object
|
||||
*/
|
||||
async retrieveOrderFromAuth(auth) {
|
||||
const link = auth.links.find((l) => l.rel === "up")
|
||||
const parts = link.href.split("/")
|
||||
const orderId = parts[parts.length - 1]
|
||||
const orderReq = new PayPal.orders.OrdersGetRequest(orderId)
|
||||
const res = await this.paypal_.execute(orderReq)
|
||||
if (res.result) {
|
||||
return res.result
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a PayPal authorization.
|
||||
* @param {string} id - the id of the authorization.
|
||||
* @returns {Promise<object>} the authorization.
|
||||
*/
|
||||
async retrieveAuthorization(id) {
|
||||
const authReq = new PayPal.payments.AuthorizationsGetRequest(id)
|
||||
const res = await this.paypal_.execute(authReq)
|
||||
if (res.result) {
|
||||
return res.result
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a webhook is verified.
|
||||
* @param {object} data - the verficiation data.
|
||||
* @returns {Promise<object>} the response of the verification request.
|
||||
*/
|
||||
async verifyWebhook(data) {
|
||||
const verifyReq = {
|
||||
verb: "POST",
|
||||
path: "/v1/notifications/verify-webhook-signature",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: {
|
||||
webhook_id: this.options_.auth_webhook_id,
|
||||
...data,
|
||||
},
|
||||
}
|
||||
|
||||
return this.paypal_.execute(verifyReq)
|
||||
}
|
||||
|
||||
/**
|
||||
* Upserts a webhook that listens for order authorizations.
|
||||
*/
|
||||
async ensureWebhooks() {
|
||||
if (!this.options_.backend_url) {
|
||||
return
|
||||
}
|
||||
|
||||
const webhookReq = {
|
||||
verb: "GET",
|
||||
path: "/v1/notifications/webhooks",
|
||||
}
|
||||
const webhookRes = await this.paypal_.execute(webhookReq)
|
||||
|
||||
console.log(webhookRes.result.webhooks)
|
||||
let found
|
||||
if (webhookRes.result && webhookRes.result.webhooks) {
|
||||
found = webhookRes.result.webhooks.find((w) => {
|
||||
const notificationType = w.event_types.find(
|
||||
(e) => e.name === "PAYMENT.AUTHORIZATION.CREATED"
|
||||
)
|
||||
return (
|
||||
!!notificationType &&
|
||||
w.url === `${this.options_.backend_url}/paypal/hooks`
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
const whCreateReq = {
|
||||
verb: "POST",
|
||||
path: "/v1/notifications/webhooks",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: {
|
||||
id: "medusa-auth-notification",
|
||||
url: `${this.options_.backend_url}/paypal/hooks`,
|
||||
event_types: [
|
||||
{
|
||||
name: "PAYMENT.AUTHORIZATION.CREATED",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
await this.paypal_.execute(whCreateReq)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default PayPalProviderService
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-payment-stripe@1.1.2...medusa-payment-stripe@1.1.3) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-payment-stripe
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-payment-stripe@1.1.1...medusa-payment-stripe@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-payment-stripe
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-payment-stripe",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Stripe Payment provider for Meduas Commerce",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -26,7 +26,7 @@
|
||||
"cross-env": "^5.2.1",
|
||||
"eslint": "^6.8.0",
|
||||
"jest": "^25.5.2",
|
||||
"medusa-test-utils": "^1.1.2"
|
||||
"medusa-test-utils": "^1.1.3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel src -d . --ignore **/__tests__",
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.2...medusa-plugin-add-ons@1.1.3) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-add-ons
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-plugin-add-ons@1.1.1...medusa-plugin-add-ons@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-add-ons
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-add-ons",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Add-on plugin for Medusa Commerce",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -25,7 +25,7 @@
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^6.8.0",
|
||||
"jest": "^25.5.2",
|
||||
"medusa-test-utils": "^1.1.2"
|
||||
"medusa-test-utils": "^1.1.3"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "babel src -d . --ignore **/__tests__",
|
||||
|
||||
@@ -3,6 +3,22 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.4](https://github.com/medusajs/medusa/compare/medusa-plugin-brightpearl@1.1.3...medusa-plugin-brightpearl@1.1.4) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-brightpearl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-brightpearl@1.1.2...medusa-plugin-brightpearl@1.1.3) (2021-02-08)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-brightpearl
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-plugin-brightpearl@1.1.1...medusa-plugin-brightpearl@1.1.2) (2021-02-03)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-brightpearl",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.4",
|
||||
"description": "Brightpearl plugin for Medusa Commerce",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -27,7 +27,7 @@
|
||||
"cross-env": "^7.0.2",
|
||||
"eslint": "^6.8.0",
|
||||
"jest": "^25.5.2",
|
||||
"medusa-test-utils": "^1.1.2",
|
||||
"medusa-test-utils": "^1.1.3",
|
||||
"prettier": "^2.0.5"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -12,4 +12,5 @@ yarn.lock
|
||||
/services
|
||||
/models
|
||||
/subscribers
|
||||
/loaders
|
||||
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-contentful@1.1.2...medusa-plugin-contentful@1.1.3) (2021-02-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **medusa:** Product category, type and tags ([c4d1203](https://github.com/medusajs/medusa/commit/c4d1203155b7cc03e8892f0409efec83e030063e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-plugin-contentful@1.1.1...medusa-plugin-contentful@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-contentful
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-contentful",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Contentful plugin for Medusa Commerce",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -40,7 +40,7 @@
|
||||
"contentful-management": "^5.27.1",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2",
|
||||
"medusa-test-utils": "^1.1.3",
|
||||
"redis": "^3.0.2"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
const checkContentTypes = async (container) => {
|
||||
const contentfulService = container.resolve("contentfulService")
|
||||
|
||||
let product
|
||||
let variant
|
||||
|
||||
try {
|
||||
product = await contentfulService.getType("product")
|
||||
variant = await contentfulService.getType("productVariant")
|
||||
} catch (error) {
|
||||
if (!product) {
|
||||
throw Error("Content type: `product` is missing in Contentful")
|
||||
}
|
||||
if (!variant) {
|
||||
throw Error("Content type: `productVariant` is missing in Contentful")
|
||||
}
|
||||
}
|
||||
|
||||
if (product && product.fields) {
|
||||
const productFields = product.fields
|
||||
|
||||
const keys = Object.values(productFields).map((f) => f.id)
|
||||
if (!requiredProductFields.every((f) => keys.includes(f))) {
|
||||
throw Error(
|
||||
`Contentful: Content type ${`product`} is missing some required key(s). Required: ${requiredProductFields.join(
|
||||
", "
|
||||
)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (variant && variant.fields) {
|
||||
const variantFields = variant.fields
|
||||
|
||||
const keys = Object.values(variantFields).map((f) => f.id)
|
||||
if (!requiredVariantFields.every((f) => keys.includes(f))) {
|
||||
throw Error(
|
||||
`Contentful: Content type ${`productVariant`} is missing some required key(s). Required: ${requiredVariantFields.join(
|
||||
", "
|
||||
)}`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const requiredProductFields = [
|
||||
"title",
|
||||
"variants",
|
||||
"options",
|
||||
"objectId",
|
||||
"type",
|
||||
"collection",
|
||||
"tags",
|
||||
"handle",
|
||||
]
|
||||
|
||||
const requiredVariantFields = ["title", "sku", "prices", "options", "objectId"]
|
||||
|
||||
export default checkContentTypes
|
||||
@@ -114,28 +114,58 @@ class ContentfulService extends BaseService {
|
||||
async createProductInContentful(product) {
|
||||
try {
|
||||
const p = await this.productService_.retrieve(product.id, {
|
||||
relations: ["variants", "options"],
|
||||
relations: ["variants", "options", "tags", "type", "collection"],
|
||||
})
|
||||
|
||||
const environment = await this.getContentfulEnvironment_()
|
||||
const variantEntries = await this.getVariantEntries_(p.variants)
|
||||
const variantLinks = this.getVariantLinks_(variantEntries)
|
||||
|
||||
const result = await environment.createEntryWithId("product", p.id, {
|
||||
fields: {
|
||||
title: {
|
||||
"en-US": p.title,
|
||||
},
|
||||
variants: {
|
||||
"en-US": variantLinks,
|
||||
},
|
||||
options: {
|
||||
"en-US": p.options,
|
||||
},
|
||||
objectId: {
|
||||
"en-US": p.id,
|
||||
},
|
||||
const fields = {
|
||||
title: {
|
||||
"en-US": p.title,
|
||||
},
|
||||
variants: {
|
||||
"en-US": variantLinks,
|
||||
},
|
||||
options: {
|
||||
"en-US": p.options,
|
||||
},
|
||||
objectId: {
|
||||
"en-US": p.id,
|
||||
},
|
||||
}
|
||||
|
||||
if (p.type) {
|
||||
const type = {
|
||||
"en-US": p.type.value,
|
||||
}
|
||||
fields.type = type
|
||||
}
|
||||
|
||||
if (p.collection) {
|
||||
const collection = {
|
||||
"en-US": p.collection.title,
|
||||
}
|
||||
fields.collection = collection
|
||||
}
|
||||
|
||||
if (p.tags) {
|
||||
const tags = {
|
||||
"en-US": p.tags,
|
||||
}
|
||||
fields.tags = tags
|
||||
}
|
||||
|
||||
if (p.handle) {
|
||||
const handle = {
|
||||
"en-US": p.handle,
|
||||
}
|
||||
fields.handle = handle
|
||||
}
|
||||
|
||||
const result = await environment.createEntryWithId("product", p.id, {
|
||||
fields,
|
||||
})
|
||||
|
||||
const ignoreIds = (await this.getIgnoreIds_("product")) || []
|
||||
@@ -210,7 +240,7 @@ class ContentfulService extends BaseService {
|
||||
}
|
||||
|
||||
const p = await this.productService_.retrieve(product.id, {
|
||||
relations: ["options", "variants"],
|
||||
relations: ["options", "variants", "type", "collection", "tags"],
|
||||
})
|
||||
|
||||
const variantEntries = await this.getVariantEntries_(p.variants)
|
||||
@@ -232,6 +262,34 @@ class ContentfulService extends BaseService {
|
||||
},
|
||||
}
|
||||
|
||||
if (p.type) {
|
||||
const type = {
|
||||
"en-US": p.type.value,
|
||||
}
|
||||
productEntryFields.type = type
|
||||
}
|
||||
|
||||
if (p.collection) {
|
||||
const collection = {
|
||||
"en-US": p.collection.title,
|
||||
}
|
||||
productEntryFields.collection = collection
|
||||
}
|
||||
|
||||
if (p.tags) {
|
||||
const tags = {
|
||||
"en-US": p.tags,
|
||||
}
|
||||
productEntryFields.tags = tags
|
||||
}
|
||||
|
||||
if (p.handle) {
|
||||
const handle = {
|
||||
"en-US": p.handle,
|
||||
}
|
||||
productEntryFields.handle = handle
|
||||
}
|
||||
|
||||
productEntry.fields = productEntryFields
|
||||
|
||||
const updatedEntry = await productEntry.update()
|
||||
@@ -372,6 +430,11 @@ class ContentfulService extends BaseService {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async getType(type) {
|
||||
const environment = await this.getContentfulEnvironment_()
|
||||
return environment.getContentType(type)
|
||||
}
|
||||
}
|
||||
|
||||
export default ContentfulService
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-economic@1.1.2...medusa-plugin-economic@1.1.3) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-economic
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-plugin-economic@1.1.1...medusa-plugin-economic@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-economic
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-economic",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-economic",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "E-conomic financial reporting",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -40,7 +40,7 @@
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2",
|
||||
"medusa-test-utils": "^1.1.3",
|
||||
"moment": "^2.27.0"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-mailchimp@1.1.2...medusa-plugin-mailchimp@1.1.3) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-mailchimp
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-plugin-mailchimp@1.1.1...medusa-plugin-mailchimp@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-mailchimp
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-mailchimp",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Mailchimp newsletter subscriptions",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -41,7 +41,7 @@
|
||||
"express": "^4.17.1",
|
||||
"mailchimp-api-v3": "^1.14.0",
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2"
|
||||
"medusa-test-utils": "^1.1.3"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-permissions@1.1.2...medusa-plugin-permissions@1.1.3) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-permissions
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-plugin-permissions@1.1.1...medusa-plugin-permissions@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-permissions
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-permissions",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Role permission for Medusa core",
|
||||
"main": "dist/index.js",
|
||||
"repository": {
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2",
|
||||
"medusa-test-utils": "^1.1.3",
|
||||
"mongoose": "^5.8.0"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.4](https://github.com/medusajs/medusa/compare/medusa-plugin-segment@1.1.3...medusa-plugin-segment@1.1.4) (2021-02-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* item reporting revenue discounted ([6e6669d](https://github.com/medusajs/medusa/commit/6e6669dee8cb3f7e044a4001fa1e183bb61766dd))
|
||||
* price after discounts ([0ed6b4a](https://github.com/medusajs/medusa/commit/0ed6b4a6a09d1d2ef5c9d6a199db1e01234bc3c2))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-segment@1.1.2...medusa-plugin-segment@1.1.3) (2021-02-03)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-segment",
|
||||
"version": "1.1.3",
|
||||
"version": "1.1.4",
|
||||
"description": "Segment Analytics",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -40,7 +40,7 @@
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2"
|
||||
"medusa-test-utils": "^1.1.3"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
}
|
||||
|
||||
@@ -106,10 +106,10 @@ class SegmentService extends BaseService {
|
||||
let name = item.title
|
||||
|
||||
const unit_price = item.unit_price
|
||||
const line_total = (unit_price * item.quantity) / 100
|
||||
const line_total = this.totalsService_.getLineItemRefund(order, item)
|
||||
const revenue = await this.getReportingValue(
|
||||
order.currency_code,
|
||||
line_total
|
||||
line_total / 100
|
||||
)
|
||||
|
||||
let sku = ""
|
||||
@@ -126,7 +126,7 @@ class SegmentService extends BaseService {
|
||||
return {
|
||||
name,
|
||||
variant,
|
||||
price: unit_price / 100,
|
||||
price: line_total / 100 / item.quantity,
|
||||
reporting_revenue: revenue,
|
||||
product_id: item.variant.product_id,
|
||||
sku,
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-sendgrid@1.1.2...medusa-plugin-sendgrid@1.1.3) (2021-02-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* attachment generator for invoices ([0cc2ccd](https://github.com/medusajs/medusa/commit/0cc2ccd3b2100960ce7d3422e20ce89ca96704a0))
|
||||
* handles normalizeThumb without thumbnail ([93f6812](https://github.com/medusajs/medusa/commit/93f68126bfb27f694211b6f890ce0370c42fa08a))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* notifications ([#172](https://github.com/medusajs/medusa/issues/172)) ([7308946](https://github.com/medusajs/medusa/commit/7308946e567ed4e63e1ed3d9d31b30c4f1a73f0d))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-plugin-sendgrid@1.1.1...medusa-plugin-sendgrid@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-sendgrid
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-sendgrid",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "SendGrid transactional emails",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -40,7 +40,7 @@
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2"
|
||||
"medusa-test-utils": "^1.1.3"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { BaseService } from "medusa-interfaces"
|
||||
import { NotificationService } from "medusa-interfaces"
|
||||
import SendGrid from "@sendgrid/mail"
|
||||
|
||||
class SendGridService extends BaseService {
|
||||
class SendGridService extends NotificationService {
|
||||
static identifier = "sendgrid"
|
||||
|
||||
/**
|
||||
* @param {Object} options - options defined in `medusa-config.js`
|
||||
* e.g.
|
||||
@@ -15,69 +17,220 @@ class SendGridService extends BaseService {
|
||||
* customer_password_reset_template: 1111,
|
||||
* }
|
||||
*/
|
||||
constructor({}, options) {
|
||||
constructor(
|
||||
{
|
||||
storeService,
|
||||
orderService,
|
||||
returnService,
|
||||
swapService,
|
||||
lineItemService,
|
||||
claimService,
|
||||
fulfillmentService,
|
||||
fulfillmentProviderService,
|
||||
totalsService,
|
||||
},
|
||||
options
|
||||
) {
|
||||
super()
|
||||
|
||||
this.options_ = options
|
||||
|
||||
this.fulfillmentProviderService_ = fulfillmentProviderService
|
||||
this.storeService_ = storeService
|
||||
this.lineItemService_ = lineItemService
|
||||
this.orderService_ = orderService
|
||||
this.claimService_ = claimService
|
||||
this.returnService_ = returnService
|
||||
this.swapService_ = swapService
|
||||
this.fulfillmentService_ = fulfillmentService
|
||||
this.totalsService_ = totalsService
|
||||
|
||||
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, data) {
|
||||
let templateId
|
||||
async fetchAttachments(event, data, attachmentGenerator) {
|
||||
switch (event) {
|
||||
case "order.gift_card_created":
|
||||
templateId = this.options_.gift_card_created_template
|
||||
data = {
|
||||
...data,
|
||||
display_value: data.giftcard.rule.value * (1 + data.tax_rate),
|
||||
case "swap.created":
|
||||
case "order.return_requested": {
|
||||
let attachments = []
|
||||
const { shipping_method, shipping_data } = data.return_request
|
||||
if (shipping_method) {
|
||||
const provider = shipping_method.shipping_option.provider_id
|
||||
|
||||
const lbl = await this.fulfillmentProviderService_.retrieveDocuments(
|
||||
provider,
|
||||
shipping_data,
|
||||
"label"
|
||||
)
|
||||
|
||||
attachments = attachments.concat(
|
||||
lbl.map((d) => ({
|
||||
name: "return-label",
|
||||
base64: d.base_64,
|
||||
type: d.type,
|
||||
}))
|
||||
)
|
||||
}
|
||||
break
|
||||
case "order.placed":
|
||||
templateId = this.options_.order_placed_template
|
||||
break
|
||||
case "order.updated":
|
||||
templateId = this.options_.order_updated_template
|
||||
break
|
||||
case "order.shipment_created":
|
||||
templateId = this.options_.order_shipped_template
|
||||
break
|
||||
case "order.cancelled":
|
||||
templateId = this.options_.order_cancelled_template
|
||||
break
|
||||
case "order.completed":
|
||||
templateId = this.options_.order_completed_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 {
|
||||
if (templateId) {
|
||||
return SendGrid.send({
|
||||
template_id: templateId,
|
||||
from: this.options_.from,
|
||||
to: data.email,
|
||||
dynamic_template_data: data,
|
||||
})
|
||||
|
||||
if (attachmentGenerator && attachmentGenerator.createReturnInvoice) {
|
||||
const base64 = await attachmentGenerator.createReturnInvoice(
|
||||
data.order,
|
||||
data.return_request.items
|
||||
)
|
||||
attachments.push({
|
||||
name: "invoice",
|
||||
base64,
|
||||
type: "application/pdf",
|
||||
})
|
||||
}
|
||||
|
||||
return attachments
|
||||
}
|
||||
} catch (error) {
|
||||
throw error
|
||||
default:
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
async fetchData(event, eventData, attachmentGenerator) {
|
||||
switch (event) {
|
||||
case "order.return_requested":
|
||||
return this.returnRequestedData(eventData, attachmentGenerator)
|
||||
case "swap.shipment_created":
|
||||
return this.swapShipmentCreatedData(eventData, attachmentGenerator)
|
||||
case "claim.shipment_created":
|
||||
return this.claimShipmentCreatedData(eventData, attachmentGenerator)
|
||||
case "order.items_returned":
|
||||
return this.itemsReturnedData(eventData, attachmentGenerator)
|
||||
case "order.swap_received":
|
||||
return this.swapReceivedData(eventData, attachmentGenerator)
|
||||
case "swap.created":
|
||||
return this.swapCreatedData(eventData, attachmentGenerator)
|
||||
case "gift_card.created":
|
||||
return this.gcCreatedData(eventData, attachmentGenerator)
|
||||
case "order.gift_card_created":
|
||||
return this.gcCreatedData(eventData, attachmentGenerator)
|
||||
case "order.placed":
|
||||
return this.orderPlacedData(eventData, attachmentGenerator)
|
||||
case "order.shipment_created":
|
||||
return this.orderShipmentCreatedData(eventData, attachmentGenerator)
|
||||
case "order.canceled":
|
||||
return this.orderCanceledData(eventData, attachmentGenerator)
|
||||
case "user.password_reset":
|
||||
return this.userPasswordResetData(eventData, attachmentGenerator)
|
||||
case "customer.password_reset":
|
||||
return this.customerPasswordResetData(eventData, attachmentGenerator)
|
||||
default:
|
||||
return {}
|
||||
}
|
||||
}
|
||||
|
||||
getTemplateId(event) {
|
||||
switch (event) {
|
||||
case "order.return_requested":
|
||||
return this.options_.order_return_requested_template
|
||||
case "swap.shipment_created":
|
||||
return this.options_.swap_shipment_created_template
|
||||
case "claim.shipment_created":
|
||||
return this.options_.claim_shipment_created_template
|
||||
case "order.items_returned":
|
||||
return this.options_.order_items_returned_template
|
||||
case "order.swap_received":
|
||||
return this.options_.order_swap_received_template
|
||||
case "swap.created":
|
||||
return this.options_.swap_created_template
|
||||
case "gift_card.created":
|
||||
return this.options_.gift_card_created_template
|
||||
case "order.gift_card_created":
|
||||
return this.options_.gift_card_created_template
|
||||
case "order.placed":
|
||||
return this.options_.order_placed_template
|
||||
case "order.shipment_created":
|
||||
return this.options_.order_shipped_template
|
||||
case "order.canceled":
|
||||
return this.options_.order_canceled_template
|
||||
case "user.password_reset":
|
||||
return this.options_.user_password_reset_template
|
||||
case "customer.password_reset":
|
||||
return this.options_.customer_password_reset_template
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
async sendNotification(event, eventData, attachmentGenerator) {
|
||||
let templateId = this.getTemplateId(event)
|
||||
|
||||
if (!templateId) {
|
||||
return false
|
||||
}
|
||||
|
||||
const data = await this.fetchData(event, eventData, attachmentGenerator)
|
||||
const attachments = await this.fetchAttachments(
|
||||
event,
|
||||
data,
|
||||
attachmentGenerator
|
||||
)
|
||||
|
||||
const sendOptions = {
|
||||
template_id: templateId,
|
||||
from: this.options_.from,
|
||||
to: data.email,
|
||||
dynamic_template_data: data,
|
||||
has_attachments: attachments?.length,
|
||||
}
|
||||
|
||||
if (attachments?.length) {
|
||||
sendOptions.has_attachments = true
|
||||
sendOptions.attachments = attachments.map((a) => {
|
||||
return {
|
||||
content: a.base64,
|
||||
filename: a.name,
|
||||
type: a.type,
|
||||
disposition: "attachment",
|
||||
contentId: a.name,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const status = await SendGrid.send(sendOptions)
|
||||
.then(() => "sent")
|
||||
.catch(() => "failed")
|
||||
|
||||
// We don't want heavy docs stored in DB
|
||||
delete sendOptions.attachments
|
||||
|
||||
return { to: data.email, status, data: sendOptions }
|
||||
}
|
||||
|
||||
async resendNotification(notification, config, attachmentGenerator) {
|
||||
const sendOptions = {
|
||||
...notification.data,
|
||||
to: config.to || notification.to,
|
||||
}
|
||||
|
||||
const attachs = await this.fetchAttachments(
|
||||
notification.event_name,
|
||||
notification.data.dynamic_template_data,
|
||||
attachmentGenerator
|
||||
)
|
||||
|
||||
sendOptions.attachments = attachs.map((a) => {
|
||||
return {
|
||||
content: a.base64,
|
||||
filename: a.name,
|
||||
type: a.type,
|
||||
disposition: "attachment",
|
||||
contentId: a.name,
|
||||
}
|
||||
})
|
||||
|
||||
const status = await SendGrid.send(sendOptions)
|
||||
.then(() => "sent")
|
||||
.catch(() => "failed")
|
||||
|
||||
return { to: sendOptions.to, status, data: sendOptions }
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an email using SendGrid.
|
||||
* @param {string} templateId - id of template in SendGrid
|
||||
@@ -93,6 +246,418 @@ class SendGridService extends BaseService {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async orderShipmentCreatedData({ id, fulfillment_id }, attachmentGenerator) {
|
||||
const order = await this.orderService_.retrieve(id, {
|
||||
select: [
|
||||
"shipping_total",
|
||||
"discount_total",
|
||||
"tax_total",
|
||||
"refunded_total",
|
||||
"gift_card_total",
|
||||
"subtotal",
|
||||
"total",
|
||||
"refundable_amount",
|
||||
],
|
||||
relations: [
|
||||
"customer",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"discounts",
|
||||
"shipping_methods",
|
||||
"shipping_methods.shipping_option",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"returns",
|
||||
"gift_cards",
|
||||
"gift_card_transactions",
|
||||
],
|
||||
})
|
||||
|
||||
const shipment = await this.fulfillmentService_.retrieve(fulfillment_id, {
|
||||
relations: ["items"],
|
||||
})
|
||||
|
||||
return {
|
||||
order,
|
||||
date: shipment.shipped_at.toDateString(),
|
||||
email: order.email,
|
||||
fulfillment: shipment,
|
||||
tracking_number: shipment.tracking_numbers.join(", "),
|
||||
}
|
||||
}
|
||||
|
||||
async orderPlacedData({ id }) {
|
||||
const order = await this.orderService_.retrieve(id, {
|
||||
select: [
|
||||
"shipping_total",
|
||||
"discount_total",
|
||||
"tax_total",
|
||||
"refunded_total",
|
||||
"gift_card_total",
|
||||
"subtotal",
|
||||
"total",
|
||||
],
|
||||
relations: [
|
||||
"customer",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"discounts",
|
||||
"shipping_methods",
|
||||
"shipping_methods.shipping_option",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"returns",
|
||||
"gift_cards",
|
||||
"gift_card_transactions",
|
||||
],
|
||||
})
|
||||
|
||||
const {
|
||||
subtotal,
|
||||
tax_total,
|
||||
discount_total,
|
||||
shipping_total,
|
||||
gift_card_total,
|
||||
total,
|
||||
} = order
|
||||
|
||||
const taxRate = order.tax_rate / 100
|
||||
const currencyCode = order.currency_code.toUpperCase()
|
||||
|
||||
const items = this.processItems_(order.items, taxRate, currencyCode)
|
||||
|
||||
let discounts = []
|
||||
if (order.discounts) {
|
||||
discounts = order.discounts.map((discount) => {
|
||||
return {
|
||||
is_giftcard: false,
|
||||
code: discount.code,
|
||||
descriptor: `${discount.rule.value}${
|
||||
discount.rule.type === "percentage" ? "%" : ` ${currencyCode}`
|
||||
}`,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let giftCards = []
|
||||
if (order.gift_cards) {
|
||||
giftCards = order.gift_cards.map((gc) => {
|
||||
return {
|
||||
is_giftcard: true,
|
||||
code: gc.code,
|
||||
descriptor: `${gc.value} ${currencyCode}`,
|
||||
}
|
||||
})
|
||||
|
||||
discounts.concat(giftCards)
|
||||
}
|
||||
|
||||
return {
|
||||
...order,
|
||||
has_discounts: order.discounts.length,
|
||||
has_gift_cards: order.gift_cards.length,
|
||||
date: order.created_at.toDateString(),
|
||||
items,
|
||||
discounts,
|
||||
subtotal: `${this.humanPrice_(subtotal * (1 + taxRate))} ${currencyCode}`,
|
||||
gift_card_total: `${this.humanPrice_(
|
||||
gift_card_total * (1 + taxRate)
|
||||
)} ${currencyCode}`,
|
||||
tax_total: `${this.humanPrice_(tax_total)} ${currencyCode}`,
|
||||
discount_total: `${this.humanPrice_(
|
||||
discount_total * (1 + taxRate)
|
||||
)} ${currencyCode}`,
|
||||
shipping_total: `${this.humanPrice_(
|
||||
shipping_total * (1 + taxRate)
|
||||
)} ${currencyCode}`,
|
||||
total: `${this.humanPrice_(total)} ${currencyCode}`,
|
||||
}
|
||||
}
|
||||
|
||||
async gcCreatedData({ id }) {
|
||||
const giftCard = await this.giftCardService_.retrieve(id, {
|
||||
relations: ["region", "order"],
|
||||
})
|
||||
|
||||
const taxRate = giftCard.region.tax_rate / 100
|
||||
|
||||
return {
|
||||
...giftCard,
|
||||
email: giftCard.order.email,
|
||||
display_value: giftCard.value * (1 + taxRate),
|
||||
}
|
||||
}
|
||||
|
||||
async returnRequestedData({ id, return_id }) {
|
||||
// Fetch the return request
|
||||
const returnRequest = await this.returnService_.retrieve(return_id, {
|
||||
relations: [
|
||||
"items",
|
||||
"items.item",
|
||||
"items.item.variant",
|
||||
"items.item.variant.product",
|
||||
"shipping_method",
|
||||
"shipping_method.shipping_option",
|
||||
],
|
||||
})
|
||||
|
||||
const items = await this.lineItemService_.list({
|
||||
id: returnRequest.items.map(({ item_id }) => item_id),
|
||||
})
|
||||
|
||||
returnRequest.items = returnRequest.items.map((item) => {
|
||||
const found = items.find((i) => i.id === item.item_id)
|
||||
return {
|
||||
...item,
|
||||
item: found,
|
||||
}
|
||||
})
|
||||
|
||||
// Fetch the order
|
||||
const order = await this.orderService_.retrieve(id, {
|
||||
select: ["total"],
|
||||
relations: ["items", "discounts", "shipping_address", "returns"],
|
||||
})
|
||||
|
||||
// Calculate which items are in the return
|
||||
const returnItems = returnRequest.items.map((i) => {
|
||||
const found = order.items.find((oi) => oi.id === i.item_id)
|
||||
return {
|
||||
...found,
|
||||
quantity: i.quantity,
|
||||
}
|
||||
})
|
||||
|
||||
const taxRate = order.tax_rate / 100
|
||||
const currencyCode = order.currency_code.toUpperCase()
|
||||
|
||||
// Get total of the returned products
|
||||
const item_subtotal = this.totalsService_.getRefundTotal(order, returnItems)
|
||||
|
||||
// If the return has a shipping method get the price and any attachments
|
||||
let shippingTotal = 0
|
||||
if (returnRequest.shipping_method) {
|
||||
shippingTotal = returnRequest.shipping_method.price * (1 + taxRate)
|
||||
}
|
||||
|
||||
return {
|
||||
has_shipping: !!returnRequest.shipping_method,
|
||||
email: order.email,
|
||||
items: this.processItems_(returnItems, taxRate, currencyCode),
|
||||
subtotal: `${this.humanPrice_(item_subtotal)} ${currencyCode}`,
|
||||
shipping_total: `${this.humanPrice_(shippingTotal)} ${currencyCode}`,
|
||||
refund_amount: `${this.humanPrice_(
|
||||
returnRequest.refund_amount
|
||||
)} ${currencyCode}`,
|
||||
return_request: {
|
||||
...returnRequest,
|
||||
refund_amount: `${this.humanPrice_(
|
||||
returnRequest.refund_amount
|
||||
)} ${currencyCode}`,
|
||||
},
|
||||
order,
|
||||
date: returnRequest.updated_at.toDateString(),
|
||||
}
|
||||
}
|
||||
|
||||
async swapCreatedData({ id }) {
|
||||
const store = await this.storeService_.retrieve()
|
||||
const swap = await this.swapService_.retrieve(id, {
|
||||
relations: [
|
||||
"additional_items",
|
||||
"return_order",
|
||||
"return_order.items",
|
||||
"return_order.items.item",
|
||||
"return_order.shipping_method",
|
||||
"return_order.shipping_method.shipping_option",
|
||||
],
|
||||
})
|
||||
|
||||
const returnRequest = swap.return_order
|
||||
|
||||
const items = await this.lineItemService_.list({
|
||||
id: returnRequest.items.map(({ item_id }) => item_id),
|
||||
})
|
||||
|
||||
returnRequest.items = returnRequest.items.map((item) => {
|
||||
const found = items.find((i) => i.id === item.item_id)
|
||||
return {
|
||||
...item,
|
||||
item: found,
|
||||
}
|
||||
})
|
||||
|
||||
const swapLink = store.swap_link_template.replace(
|
||||
/\{cart_id\}/,
|
||||
swap.cart_id
|
||||
)
|
||||
|
||||
const order = await this.orderService_.retrieve(swap.order_id, {
|
||||
select: ["total"],
|
||||
relations: ["items", "discounts", "shipping_address"],
|
||||
})
|
||||
|
||||
const taxRate = order.tax_rate / 100
|
||||
const currencyCode = order.currency_code.toUpperCase()
|
||||
|
||||
const returnItems = this.processItems_(
|
||||
swap.return_order.items.map((i) => {
|
||||
const found = order.items.find((oi) => oi.id === i.item_id)
|
||||
return {
|
||||
...found,
|
||||
quantity: i.quantity,
|
||||
}
|
||||
}),
|
||||
taxRate,
|
||||
currencyCode
|
||||
)
|
||||
|
||||
const returnTotal = this.totalsService_.getRefundTotal(order, returnItems)
|
||||
|
||||
const constructedOrder = {
|
||||
...order,
|
||||
shipping_methods: [],
|
||||
items: swap.additional_items,
|
||||
}
|
||||
|
||||
const additionalTotal = this.totalsService_.getTotal(constructedOrder)
|
||||
|
||||
const refundAmount = swap.return_order.refund_amount
|
||||
|
||||
return {
|
||||
swap,
|
||||
order,
|
||||
return_request: returnRequest,
|
||||
date: swap.updated_at.toDateString(),
|
||||
swap_link: swapLink,
|
||||
email: order.email,
|
||||
items: this.processItems_(swap.additional_items, taxRate, currencyCode),
|
||||
return_items: returnItems,
|
||||
return_total: `${this.humanPrice_(returnTotal)} ${currencyCode}`,
|
||||
refund_amount: `${this.humanPrice_(refundAmount)} ${currencyCode}`,
|
||||
additional_total: `${this.humanPrice_(additionalTotal)} ${currencyCode}`,
|
||||
}
|
||||
}
|
||||
|
||||
async itemsReturnedData(data) {
|
||||
return this.returnRequestedData(data)
|
||||
}
|
||||
|
||||
async swapShipmentCreatedData({ id, fulfillment_id }) {
|
||||
const swap = await this.swapService_.retrieve(id, {
|
||||
relations: [
|
||||
"shipping_address",
|
||||
"shipping_methods",
|
||||
"additional_items",
|
||||
"return_order",
|
||||
"return_order.items",
|
||||
],
|
||||
})
|
||||
|
||||
const order = await this.orderService_.retrieve(swap.order_id, {
|
||||
relations: ["items", "discounts"],
|
||||
})
|
||||
|
||||
const taxRate = order.tax_rate / 100
|
||||
const currencyCode = order.currency_code.toUpperCase()
|
||||
|
||||
const returnItems = this.processItems_(
|
||||
swap.return_order.items.map((i) => {
|
||||
const found = order.items.find((oi) => oi.id === i.item_id)
|
||||
return {
|
||||
...found,
|
||||
quantity: i.quantity,
|
||||
}
|
||||
}),
|
||||
taxRate,
|
||||
currencyCode
|
||||
)
|
||||
|
||||
const returnTotal = this.totalsService_.getRefundTotal(order, returnItems)
|
||||
|
||||
const constructedOrder = {
|
||||
...order,
|
||||
shipping_methods: swap.shipping_methods,
|
||||
items: swap.additional_items,
|
||||
}
|
||||
|
||||
const additionalTotal = this.totalsService_.getTotal(constructedOrder)
|
||||
|
||||
const refundAmount = swap.return_order.refund_amount
|
||||
|
||||
const shipment = await this.fulfillmentService_.retrieve(fulfillment_id)
|
||||
|
||||
return {
|
||||
swap,
|
||||
order,
|
||||
items: this.processItems_(swap.additional_items, taxRate, currencyCode),
|
||||
date: swap.updated_at.toDateString(),
|
||||
email: order.email,
|
||||
tax_amount: `${this.humanPrice_(
|
||||
swap.difference_due * taxRate
|
||||
)} ${currencyCode}`,
|
||||
paid_total: `${this.humanPrice_(swap.difference_due)} ${currencyCode}`,
|
||||
return_total: `${this.humanPrice_(returnTotal)} ${currencyCode}`,
|
||||
refund_amount: `${this.humanPrice_(refundAmount)} ${currencyCode}`,
|
||||
additional_total: `${this.humanPrice_(additionalTotal)} ${currencyCode}`,
|
||||
fulfillment: shipment,
|
||||
tracking_number: shipment.tracking_numbers.join(", "),
|
||||
}
|
||||
}
|
||||
|
||||
async claimShipmentCreatedData({ id, fulfillment_id }) {
|
||||
const claim = await this.claimService_.retrieve(id, {
|
||||
relations: ["order", "order.items", "order.shipping_address"],
|
||||
})
|
||||
|
||||
const shipment = await this.fulfillmentService_.retrieve(fulfillment_id)
|
||||
|
||||
return {
|
||||
email: claim.order.email,
|
||||
claim,
|
||||
order: claim.order,
|
||||
fulfillment: shipment,
|
||||
tracking_number: shipment.tracking_numbers.join(", "),
|
||||
}
|
||||
}
|
||||
|
||||
userPasswordResetData(data) {
|
||||
return data
|
||||
}
|
||||
|
||||
customerPasswordResetData(data) {
|
||||
return data
|
||||
}
|
||||
|
||||
processItems_(items, taxRate, currencyCode) {
|
||||
return items.map((i) => {
|
||||
return {
|
||||
...i,
|
||||
thumbnail: this.normalizeThumbUrl_(i.thumbnail),
|
||||
price: `${this.humanPrice_(
|
||||
i.unit_price * (1 + taxRate)
|
||||
)} ${currencyCode}`,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
humanPrice_(amount) {
|
||||
return amount ? (amount / 100).toFixed(2) : "0.00"
|
||||
}
|
||||
|
||||
normalizeThumbUrl_(url) {
|
||||
if (!url) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (url.startsWith("http")) {
|
||||
return url
|
||||
} else if (url.startsWith("//")) {
|
||||
return `https:${url}`
|
||||
}
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
export default SendGridService
|
||||
|
||||
@@ -3,187 +3,26 @@ class OrderSubscriber {
|
||||
totalsService,
|
||||
orderService,
|
||||
sendgridService,
|
||||
eventBusService,
|
||||
notificationService,
|
||||
fulfillmentService,
|
||||
}) {
|
||||
this.orderService_ = orderService
|
||||
this.totalsService_ = totalsService
|
||||
this.sendgridService_ = sendgridService
|
||||
this.eventBus_ = eventBusService
|
||||
this.notificationService_ = notificationService
|
||||
this.fulfillmentService_ = fulfillmentService
|
||||
|
||||
this.eventBus_.subscribe(
|
||||
"order.shipment_created",
|
||||
async ({ id, fulfillment_id }) => {
|
||||
const order = await this.orderService_.retrieve(id, {
|
||||
select: [
|
||||
"shipping_total",
|
||||
"discount_total",
|
||||
"tax_total",
|
||||
"refunded_total",
|
||||
"gift_card_total",
|
||||
"subtotal",
|
||||
"total",
|
||||
"refundable_amount",
|
||||
],
|
||||
relations: [
|
||||
"customer",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"discounts",
|
||||
"shipping_methods",
|
||||
"shipping_methods.shipping_option",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"returns",
|
||||
"gift_cards",
|
||||
"gift_card_transactions",
|
||||
"swaps",
|
||||
"swaps.return_order",
|
||||
"swaps.payment",
|
||||
"swaps.shipping_methods",
|
||||
"swaps.shipping_address",
|
||||
"swaps.additional_items",
|
||||
"swaps.fulfillments",
|
||||
],
|
||||
})
|
||||
|
||||
const shipment = await this.fulfillmentService_.retrieve(fulfillment_id)
|
||||
|
||||
const data = {
|
||||
...order,
|
||||
tracking_number: shipment.tracking_numbers.join(", "),
|
||||
}
|
||||
|
||||
await this.sendgridService_.transactionalEmail(
|
||||
"order.shipment_created",
|
||||
data
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
this.eventBus_.subscribe("order.gift_card_created", async (order) => {
|
||||
await this.sendgridService_.transactionalEmail(
|
||||
"order.gift_card_created",
|
||||
order
|
||||
)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("order.placed", async (orderObj) => {
|
||||
try {
|
||||
const order = await this.orderService_.retrieve(orderObj.id, {
|
||||
select: [
|
||||
"shipping_total",
|
||||
"discount_total",
|
||||
"tax_total",
|
||||
"refunded_total",
|
||||
"gift_card_total",
|
||||
"subtotal",
|
||||
"total",
|
||||
],
|
||||
relations: [
|
||||
"customer",
|
||||
"billing_address",
|
||||
"shipping_address",
|
||||
"discounts",
|
||||
"shipping_methods",
|
||||
"shipping_methods.shipping_option",
|
||||
"payments",
|
||||
"fulfillments",
|
||||
"returns",
|
||||
"gift_cards",
|
||||
"gift_card_transactions",
|
||||
"swaps",
|
||||
"swaps.return_order",
|
||||
"swaps.payment",
|
||||
"swaps.shipping_methods",
|
||||
"swaps.shipping_address",
|
||||
"swaps.additional_items",
|
||||
"swaps.fulfillments",
|
||||
],
|
||||
})
|
||||
|
||||
const {
|
||||
subtotal,
|
||||
tax_total,
|
||||
discount_total,
|
||||
shipping_total,
|
||||
total,
|
||||
} = order
|
||||
|
||||
const taxRate = order.tax_rate / 100
|
||||
const currencyCode = order.currency_code.toUpperCase()
|
||||
|
||||
const items = order.items.map((i) => {
|
||||
return {
|
||||
...i,
|
||||
price: `${((i.unit_price / 100) * (1 + taxRate)).toFixed(
|
||||
2
|
||||
)} ${currencyCode}`,
|
||||
}
|
||||
})
|
||||
|
||||
let discounts = []
|
||||
if (order.discounts) {
|
||||
discounts = order.discounts.map((discount) => {
|
||||
return {
|
||||
is_giftcard: false,
|
||||
code: discount.code,
|
||||
descriptor: `${discount.rule.value}${
|
||||
discount.rule.type === "percentage" ? "%" : ` ${currencyCode}`
|
||||
}`,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let giftCards = []
|
||||
if (order.gift_cards) {
|
||||
giftCards = order.gift_cards.map((gc) => {
|
||||
return {
|
||||
is_giftcard: true,
|
||||
code: gc.code,
|
||||
descriptor: `${gc.value} ${currencyCode}`,
|
||||
}
|
||||
})
|
||||
|
||||
discounts.concat(giftCards)
|
||||
}
|
||||
|
||||
const data = {
|
||||
...order,
|
||||
date: order.created_at.toDateString(),
|
||||
items,
|
||||
discounts,
|
||||
subtotal: `${((subtotal / 100) * (1 + taxRate)).toFixed(
|
||||
2
|
||||
)} ${currencyCode}`,
|
||||
tax_total: `${(tax_total / 100).toFixed(2)} ${currencyCode}`,
|
||||
discount_total: `${((discount_total / 100) * (1 + taxRate)).toFixed(
|
||||
2
|
||||
)} ${currencyCode}`,
|
||||
shipping_total: `${((shipping_total / 100) * (1 + taxRate)).toFixed(
|
||||
2
|
||||
)} ${currencyCode}`,
|
||||
total: `${(total / 100).toFixed(2)} ${currencyCode}`,
|
||||
}
|
||||
|
||||
await this.sendgridService_.transactionalEmail("order.placed", data)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("order.cancelled", async (order) => {
|
||||
await this.sendgridService_.transactionalEmail("order.cancelled", order)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("order.completed", async (order) => {
|
||||
await this.sendgridService_.transactionalEmail("order.completed", order)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("order.updated", async (order) => {
|
||||
await this.sendgridService_.transactionalEmail("order.updated", order)
|
||||
})
|
||||
this.notificationService_.subscribe("order.shipment_created", "sendgrid")
|
||||
this.notificationService_.subscribe("order.gift_card_created", "sendgrid")
|
||||
this.notificationService_.subscribe("gift_card.created", "sendgrid")
|
||||
this.notificationService_.subscribe("order.placed", "sendgrid")
|
||||
this.notificationService_.subscribe("order.canceled", "sendgrid")
|
||||
this.notificationService_.subscribe("customer.password_reset", "sendgrid")
|
||||
this.notificationService_.subscribe("claim.shipment_created", "sendgrid")
|
||||
this.notificationService_.subscribe("swap.shipment_created", "sendgrid")
|
||||
this.notificationService_.subscribe("swap.created", "sendgrid")
|
||||
this.notificationService_.subscribe("order.items_returned", "sendgrid")
|
||||
this.notificationService_.subscribe("order.return_requested", "sendgrid")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,13 +10,6 @@ class UserSubscriber {
|
||||
data
|
||||
)
|
||||
})
|
||||
|
||||
this.eventBus_.subscribe("customer.password_reset", async (data) => {
|
||||
await this.sendgridService_.transactionalEmail(
|
||||
"customer.password_reset",
|
||||
data
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-slack-notification@1.1.2...medusa-plugin-slack-notification@1.1.3) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-slack-notification
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-plugin-slack-notification@1.1.1...medusa-plugin-slack-notification@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-slack-notification
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-economic",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-slack-notification",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Slack notifications",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -40,7 +40,7 @@
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2",
|
||||
"medusa-test-utils": "^1.1.3",
|
||||
"moment": "^2.27.0"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-twilio-sms@1.1.2...medusa-plugin-twilio-sms@1.1.3) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-twilio-sms
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-plugin-twilio-sms@1.1.1...medusa-plugin-twilio-sms@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-twilio-sms
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-twilio-sms",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -36,7 +36,7 @@
|
||||
"@babel/plugin-transform-classes": "^7.9.5",
|
||||
"body-parser": "^1.19.0",
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2",
|
||||
"medusa-test-utils": "^1.1.3",
|
||||
"twilio": "^3.49.1"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-plugin-wishlist@1.1.2...medusa-plugin-wishlist@1.1.3) (2021-02-17)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-wishlist
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-plugin-wishlist@1.1.1...medusa-plugin-wishlist@1.1.2) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package medusa-plugin-wishlist
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-plugin-wishlist",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Provides /customers/:id/wishlist to add items to a customr's wishlist",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -38,7 +38,7 @@
|
||||
"body-parser": "^1.19.0",
|
||||
"express": "^4.17.1",
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2"
|
||||
"medusa-test-utils": "^1.1.3"
|
||||
},
|
||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||
}
|
||||
|
||||
@@ -3,6 +3,17 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.3](https://github.com/medusajs/medusa/compare/medusa-test-utils@1.1.2...medusa-test-utils@1.1.3) (2021-02-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use parallel relation fetching ([#173](https://github.com/medusajs/medusa/issues/173)) ([46006e4](https://github.com/medusajs/medusa/commit/46006e4b0647bada1dc2cb417766e22f65bad23e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.2](https://github.com/medusajs/medusa/compare/medusa-test-utils@1.1.1...medusa-test-utils@1.1.2) (2021-02-03)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "medusa-test-utils",
|
||||
"version": "1.1.2",
|
||||
"version": "1.1.3",
|
||||
"description": "Test utils for Medusa",
|
||||
"main": "dist/index.js",
|
||||
"repository": {
|
||||
|
||||
@@ -6,6 +6,7 @@ class MockRepo {
|
||||
softRemove,
|
||||
find,
|
||||
findOne,
|
||||
findOneWithRelations,
|
||||
findOneOrFail,
|
||||
save,
|
||||
findAndCount,
|
||||
@@ -19,6 +20,7 @@ class MockRepo {
|
||||
this.findOneOrFail_ = findOneOrFail;
|
||||
this.save_ = save;
|
||||
this.findAndCount_ = findAndCount;
|
||||
this.findOneWithRelations_ = findOneWithRelations;
|
||||
}
|
||||
|
||||
setFindOne(fn) {
|
||||
@@ -53,6 +55,11 @@ class MockRepo {
|
||||
return this.findOneOrFail_(...args);
|
||||
}
|
||||
});
|
||||
findOneWithRelations = jest.fn().mockImplementation((...args) => {
|
||||
if (this.findOneWithRelations_) {
|
||||
return this.findOneWithRelations_(...args);
|
||||
}
|
||||
});
|
||||
findOne = jest.fn().mockImplementation((...args) => {
|
||||
if (this.findOne_) {
|
||||
return this.findOne_(...args);
|
||||
|
||||
+1105
-1227
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,35 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.1.8](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.7...@medusajs/medusa@1.1.8) (2021-02-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* test ([4229e24](https://github.com/medusajs/medusa/commit/4229e241d03986039d283d98e034eaaefb50e04d))
|
||||
* use parallel relation fetching ([#173](https://github.com/medusajs/medusa/issues/173)) ([46006e4](https://github.com/medusajs/medusa/commit/46006e4b0647bada1dc2cb417766e22f65bad23e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* notifications ([#172](https://github.com/medusajs/medusa/issues/172)) ([7308946](https://github.com/medusajs/medusa/commit/7308946e567ed4e63e1ed3d9d31b30c4f1a73f0d))
|
||||
* **medusa:** Product category, type and tags ([c4d1203](https://github.com/medusajs/medusa/commit/c4d1203155b7cc03e8892f0409efec83e030063e))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.7](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.6...@medusajs/medusa@1.1.7) (2021-02-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* adds paypal ([#168](https://github.com/medusajs/medusa/issues/168)) ([#169](https://github.com/medusajs/medusa/issues/169)) ([427ae25](https://github.com/medusajs/medusa/commit/427ae25016bb3a22ebc05aa7b18017132846567c))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [1.1.6](https://github.com/medusajs/medusa/compare/@medusajs/medusa@1.1.5...@medusajs/medusa@1.1.6) (2021-02-03)
|
||||
|
||||
**Note:** Version bump only for package @medusajs/medusa
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@medusajs/medusa",
|
||||
"version": "1.1.6",
|
||||
"version": "1.1.8",
|
||||
"description": "E-commerce for JAMstack",
|
||||
"main": "dist/index.js",
|
||||
"repository": {
|
||||
@@ -49,6 +49,7 @@
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-classes": "^7.9.5",
|
||||
"@hapi/joi": "^16.1.8",
|
||||
"@types/lodash": "^4.14.168",
|
||||
"awilix": "^4.2.3",
|
||||
"body-parser": "^1.19.0",
|
||||
"bull": "^3.12.1",
|
||||
@@ -67,7 +68,7 @@
|
||||
"joi-objectid": "^3.0.1",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"medusa-core-utils": "^1.1.0",
|
||||
"medusa-test-utils": "^1.1.2",
|
||||
"medusa-test-utils": "^1.1.3",
|
||||
"morgan": "^1.9.1",
|
||||
"multer": "^1.4.2",
|
||||
"passport": "^0.4.0",
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductCollectionServiceMock } from "../../../../../services/__mocks__/product-collection"
|
||||
|
||||
describe("POST /admin/collections", () => {
|
||||
describe("successful creation", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("POST", "/admin/collections", {
|
||||
payload: {
|
||||
title: "Suits",
|
||||
handle: "suits",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("returns created product collection", () => {
|
||||
expect(subject.body.collection.id).toEqual(IdMap.getId("col"))
|
||||
})
|
||||
|
||||
it("calls production collection service create", () => {
|
||||
expect(ProductCollectionServiceMock.create).toHaveBeenCalledTimes(1)
|
||||
expect(ProductCollectionServiceMock.create).toHaveBeenCalledWith({
|
||||
title: "Suits",
|
||||
handle: "suits",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("invalid data returns error details", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("POST", "/admin/collections", {
|
||||
payload: {
|
||||
handle: "no-title-collection",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("returns 400", () => {
|
||||
expect(subject.status).toEqual(400)
|
||||
})
|
||||
|
||||
it("returns error details", () => {
|
||||
expect(subject.body.name).toEqual("invalid_data")
|
||||
expect(subject.body.message[0].message).toEqual(`"title" is required`)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,42 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductCollectionServiceMock } from "../../../../../services/__mocks__/product-collection"
|
||||
|
||||
describe("DELETE /admin/collections/:id", () => {
|
||||
describe("successful removes collection", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"DELETE",
|
||||
`/admin/collections/${IdMap.getId("collection")}`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls product collection service delete", () => {
|
||||
expect(ProductCollectionServiceMock.delete).toHaveBeenCalledTimes(1)
|
||||
expect(ProductCollectionServiceMock.delete).toHaveBeenCalledWith(
|
||||
IdMap.getId("collection")
|
||||
)
|
||||
})
|
||||
|
||||
it("returns delete result", () => {
|
||||
expect(subject.body).toEqual({
|
||||
id: IdMap.getId("collection"),
|
||||
object: "product-collection",
|
||||
deleted: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,37 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductCollectionServiceMock } from "../../../../../services/__mocks__/product-collection"
|
||||
|
||||
describe("GET /admin/categories/:id", () => {
|
||||
describe("get collection by id successfully", () => {
|
||||
let subject
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"GET",
|
||||
`/admin/collections/${IdMap.getId("col")}`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls retrieve from product collection service", () => {
|
||||
expect(ProductCollectionServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(ProductCollectionServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("col")
|
||||
)
|
||||
})
|
||||
|
||||
it("returns variant decorated", () => {
|
||||
expect(subject.body.collection.id).toEqual(IdMap.getId("col"))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,28 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductCollectionServiceMock } from "../../../../../services/__mocks__/product-collection"
|
||||
|
||||
describe("GET /admin/collections", () => {
|
||||
describe("successful retrieval", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
jest.clearAllMocks()
|
||||
subject = await request("GET", `/admin/collections`, {
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls product collection service list", () => {
|
||||
expect(ProductCollectionServiceMock.list).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,44 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductCollectionServiceMock } from "../../../../../services/__mocks__/product-collection"
|
||||
|
||||
describe("POST /admin/collections/:id", () => {
|
||||
describe("successful update", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/collections/${IdMap.getId("col")}`,
|
||||
{
|
||||
payload: {
|
||||
title: "Suits and vests",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("returns updated product collection", () => {
|
||||
expect(subject.body.collection.id).toEqual(IdMap.getId("col"))
|
||||
})
|
||||
|
||||
it("product collection service update", () => {
|
||||
expect(ProductCollectionServiceMock.update).toHaveBeenCalledTimes(1)
|
||||
expect(ProductCollectionServiceMock.update).toHaveBeenCalledWith(
|
||||
IdMap.getId("col"),
|
||||
{
|
||||
title: "Suits and vests",
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,29 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().required(),
|
||||
handle: Validator.string()
|
||||
.optional()
|
||||
.allow(""),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const productCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
const created = await productCollectionService.create(value)
|
||||
const collection = await productCollectionService.retrieve(created.id)
|
||||
|
||||
res.status(200).json({ collection })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const productCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
await productCollectionService.delete(id)
|
||||
|
||||
res.json({
|
||||
id,
|
||||
object: "product-collection",
|
||||
deleted: true,
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
try {
|
||||
const productCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
const collection = await productCollectionService.retrieve(id)
|
||||
res.status(200).json({ collection })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Router } from "express"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
app.use("/collections", route)
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-collection").default))
|
||||
route.post("/:id", middlewares.wrap(require("./update-collection").default))
|
||||
|
||||
route.delete("/:id", middlewares.wrap(require("./delete-collection").default))
|
||||
|
||||
route.get("/:id", middlewares.wrap(require("./get-collection").default))
|
||||
route.get("/", middlewares.wrap(require("./list-collections").default))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultFields = ["id", "title", "handle"]
|
||||
export const defaultRelations = ["products"]
|
||||
@@ -0,0 +1,30 @@
|
||||
import { defaultFields, defaultRelations } from "."
|
||||
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const selector = {}
|
||||
|
||||
const limit = parseInt(req.query.limit) || 10
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
const productCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
const listConfig = {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
}
|
||||
|
||||
const collections = await productCollectionService.list(
|
||||
selector,
|
||||
listConfig
|
||||
)
|
||||
|
||||
res.status(200).json({ collections })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().optional(),
|
||||
handle: Validator.string().optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const productCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
const updated = await productCollectionService.update(id, value)
|
||||
const collection = await productCollectionService.retrieve(updated.id)
|
||||
|
||||
res.status(200).json({ collection })
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ import swapRoutes from "./swaps"
|
||||
import returnRoutes from "./returns"
|
||||
import variantRoutes from "./variants"
|
||||
import draftOrderRoutes from "./draft-orders"
|
||||
import collectionRoutes from "./collections"
|
||||
import notificationRoutes from "./notifications"
|
||||
|
||||
const route = Router()
|
||||
|
||||
@@ -62,6 +64,8 @@ export default (app, container, config) => {
|
||||
returnRoutes(route)
|
||||
variantRoutes(route)
|
||||
draftOrderRoutes(route)
|
||||
collectionRoutes(route)
|
||||
notificationRoutes(route)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { Router } from "express"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
app.use("/notifications", route)
|
||||
|
||||
/**
|
||||
* List notifications
|
||||
*/
|
||||
route.get("/", middlewares.wrap(require("./list-notifications").default))
|
||||
|
||||
/**
|
||||
* Resend a notification
|
||||
*/
|
||||
route.post(
|
||||
"/:id/resend",
|
||||
middlewares.wrap(require("./resend-notification").default)
|
||||
)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
export const defaultRelations = ["resends"]
|
||||
export const allowedRelations = ["resends"]
|
||||
|
||||
export const defaultFields = [
|
||||
"id",
|
||||
"resource_type",
|
||||
"resource_id",
|
||||
"event_name",
|
||||
"to",
|
||||
"provider_id",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
|
||||
export const allowedFields = [
|
||||
"id",
|
||||
"resource_type",
|
||||
"resource_id",
|
||||
"provider_id",
|
||||
"event_name",
|
||||
"to",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
]
|
||||
@@ -0,0 +1,64 @@
|
||||
import _ from "lodash"
|
||||
import { defaultRelations, defaultFields } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const notificationService = req.scope.resolve("notificationService")
|
||||
|
||||
const limit = parseInt(req.query.limit) || 50
|
||||
const offset = parseInt(req.query.offset) || 0
|
||||
|
||||
let selector = {}
|
||||
|
||||
let includeFields = []
|
||||
if ("fields" in req.query) {
|
||||
includeFields = req.query.fields.split(",")
|
||||
}
|
||||
|
||||
let expandFields = []
|
||||
if ("expand" in req.query) {
|
||||
expandFields = req.query.expand.split(",")
|
||||
}
|
||||
|
||||
if ("event_name" in req.query) {
|
||||
const values = req.query.event_name.split(",")
|
||||
selector.event_name = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if ("resource_type" in req.query) {
|
||||
const values = req.query.resource_type.split(",")
|
||||
selector.resource_type = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if ("resource_id" in req.query) {
|
||||
const values = req.query.resource_id.split(",")
|
||||
selector.resource_id = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if ("to" in req.query) {
|
||||
const values = req.query.to.split(",")
|
||||
selector.to = values.length > 1 ? values : values[0]
|
||||
}
|
||||
|
||||
if (!("include_resends" in req.query)) {
|
||||
selector.parent_id = null
|
||||
}
|
||||
|
||||
const listConfig = {
|
||||
select: includeFields.length ? includeFields : defaultFields,
|
||||
relations: expandFields.length ? expandFields : defaultRelations,
|
||||
skip: offset,
|
||||
take: limit,
|
||||
order: { created_at: "DESC" },
|
||||
}
|
||||
|
||||
const notifications = await notificationService.list(selector, listConfig)
|
||||
|
||||
const fields = [...listConfig.select, ...listConfig.relations]
|
||||
const data = notifications.map(o => _.pick(o, fields))
|
||||
|
||||
res.json({ notifications: data, offset, limit })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
to: Validator.string().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const notificationService = req.scope.resolve("notificationService")
|
||||
|
||||
const config = {}
|
||||
|
||||
if (value.to) {
|
||||
config.to = value.to
|
||||
}
|
||||
|
||||
await notificationService.resend(id, config)
|
||||
|
||||
const notification = await notificationService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ notification })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
import { defaultFields, defaultRelations } from "./"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, claim_id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
fulfillment_id: Validator.string().required(),
|
||||
tracking_numbers: Validator.array()
|
||||
.items(Validator.string())
|
||||
.optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const claimService = req.scope.resolve("claimService")
|
||||
|
||||
await claimService.createShipment(
|
||||
claim_id,
|
||||
value.fulfillment_id,
|
||||
value.tracking_numbers
|
||||
)
|
||||
|
||||
const order = await orderService.retrieve(id, {
|
||||
select: defaultFields,
|
||||
relations: defaultRelations,
|
||||
})
|
||||
|
||||
res.json({ order })
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -161,6 +161,14 @@ export default app => {
|
||||
middlewares.wrap(require("./fulfill-claim").default)
|
||||
)
|
||||
|
||||
/**
|
||||
* Creates claim fulfillment
|
||||
*/
|
||||
route.post(
|
||||
"/:id/claims/:claim_id/shipments",
|
||||
middlewares.wrap(require("./create-claim-shipment").default)
|
||||
)
|
||||
|
||||
/**
|
||||
* Delete metadata key / value pair.
|
||||
*/
|
||||
|
||||
@@ -53,6 +53,7 @@ export default async (req, res) => {
|
||||
try {
|
||||
const orderService = req.scope.resolve("orderService")
|
||||
const returnService = req.scope.resolve("returnService")
|
||||
const eventBus = req.scope.resolve("eventBusService")
|
||||
|
||||
let inProgress = true
|
||||
let err = false
|
||||
@@ -98,6 +99,13 @@ export default async (req, res) => {
|
||||
.fulfill(createdReturn.id)
|
||||
}
|
||||
|
||||
await eventBus
|
||||
.withTransaction(manager)
|
||||
.emit("order.return_requested", {
|
||||
id,
|
||||
return_id: createdReturn.id,
|
||||
})
|
||||
|
||||
return {
|
||||
recovery_point: "return_requested",
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ describe("POST /admin/products", () => {
|
||||
payload: {
|
||||
title: "Test Product",
|
||||
description: "Test Description",
|
||||
tags: "hi,med,dig",
|
||||
tags: [{ id: "test", value: "test" }],
|
||||
handle: "test-product",
|
||||
},
|
||||
adminSession: {
|
||||
@@ -36,7 +36,7 @@ describe("POST /admin/products", () => {
|
||||
expect(ProductServiceMock.create).toHaveBeenCalledWith({
|
||||
title: "Test Product",
|
||||
description: "Test Description",
|
||||
tags: "hi,med,dig",
|
||||
tags: [{ id: "test", value: "test" }],
|
||||
handle: "test-product",
|
||||
is_giftcard: false,
|
||||
profile_id: IdMap.getId("default_shipping_profile"),
|
||||
|
||||
@@ -34,11 +34,12 @@ describe("GET /admin/products/:id", () => {
|
||||
"title",
|
||||
"subtitle",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"is_giftcard",
|
||||
"thumbnail",
|
||||
"profile_id",
|
||||
"collection_id",
|
||||
"type_id",
|
||||
"weight",
|
||||
"length",
|
||||
"height",
|
||||
@@ -57,6 +58,9 @@ describe("GET /admin/products/:id", () => {
|
||||
"variants.options",
|
||||
"images",
|
||||
"options",
|
||||
"tags",
|
||||
"type",
|
||||
"collection",
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
@@ -6,13 +6,28 @@ export default async (req, res) => {
|
||||
title: Validator.string().required(),
|
||||
subtitle: Validator.string().allow(""),
|
||||
description: Validator.string().allow(""),
|
||||
tags: Validator.string().optional(),
|
||||
is_giftcard: Validator.boolean().default(false),
|
||||
images: Validator.array()
|
||||
.items(Validator.string())
|
||||
.optional(),
|
||||
thumbnail: Validator.string().optional(),
|
||||
handle: Validator.string().optional(),
|
||||
type: Validator.object()
|
||||
.keys({
|
||||
id: Validator.string().optional(),
|
||||
value: Validator.string().required(),
|
||||
})
|
||||
.allow(null)
|
||||
.optional(),
|
||||
collection_id: Validator.string()
|
||||
.allow(null)
|
||||
.optional(),
|
||||
tags: Validator.array()
|
||||
.items({
|
||||
id: Validator.string().optional(),
|
||||
value: Validator.string().required(),
|
||||
})
|
||||
.optional(),
|
||||
options: Validator.array().items({
|
||||
title: Validator.string().required(),
|
||||
}),
|
||||
@@ -48,7 +63,7 @@ export default async (req, res) => {
|
||||
Validator.object()
|
||||
.keys({
|
||||
region_id: Validator.string(),
|
||||
currency_code: Validator.string().required(),
|
||||
currency_code: Validator.string(),
|
||||
amount: Validator.number()
|
||||
.integer()
|
||||
.required(),
|
||||
|
||||
@@ -8,6 +8,11 @@ export default app => {
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-product").default))
|
||||
route.post("/:id", middlewares.wrap(require("./update-product").default))
|
||||
route.get("/types", middlewares.wrap(require("./list-types").default))
|
||||
route.get(
|
||||
"/tag-usage",
|
||||
middlewares.wrap(require("./list-tag-usage-count").default)
|
||||
)
|
||||
|
||||
route.post(
|
||||
"/:id/variants",
|
||||
@@ -52,6 +57,9 @@ export const defaultRelations = [
|
||||
"variants.options",
|
||||
"images",
|
||||
"options",
|
||||
"tags",
|
||||
"type",
|
||||
"collection",
|
||||
]
|
||||
|
||||
export const defaultFields = [
|
||||
@@ -59,11 +67,12 @@ export const defaultFields = [
|
||||
"title",
|
||||
"subtitle",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"is_giftcard",
|
||||
"thumbnail",
|
||||
"profile_id",
|
||||
"collection_id",
|
||||
"type_id",
|
||||
"weight",
|
||||
"length",
|
||||
"height",
|
||||
@@ -82,11 +91,12 @@ export const allowedFields = [
|
||||
"title",
|
||||
"subtitle",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"is_giftcard",
|
||||
"thumbnail",
|
||||
"profile_id",
|
||||
"collection_id",
|
||||
"type_id",
|
||||
"weight",
|
||||
"length",
|
||||
"height",
|
||||
@@ -105,4 +115,7 @@ export const allowedRelations = [
|
||||
"variants.prices",
|
||||
"images",
|
||||
"options",
|
||||
"tags",
|
||||
"type",
|
||||
"collection",
|
||||
]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user