fix merge conflicts
This commit is contained in:
commit
283f8fbe54
151 files changed
+5526
-2508
No files matched your search
+177
-168
@@ -1,26 +1,32 @@
|
|||||||
title: Carts
|
title: Carts
|
||||||
domain: store
|
domain: store
|
||||||
routes:
|
routes:
|
||||||
- method: POST
|
|
||||||
path: /carts
|
|
||||||
- method: GET
|
- method: GET
|
||||||
path: /carts/:id
|
path: /carts/:id
|
||||||
|
- method: POST
|
||||||
|
path: /carts
|
||||||
- method: POST
|
- method: POST
|
||||||
path: /carts/:id
|
path: /carts/:id
|
||||||
|
- method: POST
|
||||||
|
path: /carts/:id/complete-cart
|
||||||
- method: POST
|
- method: POST
|
||||||
path: /carts/:id/line-items
|
path: /carts/:id/line-items
|
||||||
- method: POST
|
- method: POST
|
||||||
path: /carts/:id/line-items/:line_id
|
path: /carts/:id/line-items/:line_id
|
||||||
- method: DELETE
|
|
||||||
path: /carts/:id/line-items/:line_id
|
|
||||||
- method: POST
|
- method: POST
|
||||||
path: /carts/:id/payment-sessions
|
path: /carts/:id/payment-sessions
|
||||||
- method: DELETE
|
|
||||||
path: /carts/:id/payment-sessions/:provider_id
|
|
||||||
- method: POST
|
- 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
|
- method: POST
|
||||||
path: /carts/:id/shipping-methods
|
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
|
- method: DELETE
|
||||||
path: /carts/:id/discounts/:code
|
path: /carts/:id/discounts/:code
|
||||||
route: /carts
|
route: /carts
|
||||||
@@ -40,7 +46,7 @@ endpoints:
|
|||||||
- name: items
|
- name: items
|
||||||
type: List
|
type: List
|
||||||
required: false
|
required: false
|
||||||
description: List of objects with `variantId` and `quantity`.
|
description: List of objects with `variant_id` and `quantity`.
|
||||||
description: >
|
description: >
|
||||||
Creates a Cart within the given region and with the initial items. If no
|
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
|
`region_id` is provided the cart will be associated with the first Region
|
||||||
@@ -49,19 +55,11 @@ endpoints:
|
|||||||
- path: /:id
|
- path: /:id
|
||||||
method: GET
|
method: GET
|
||||||
title: Retrieve a Cart
|
title: Retrieve a Cart
|
||||||
params:
|
|
||||||
- name: id
|
|
||||||
type: String
|
|
||||||
description: Id of the cart.
|
|
||||||
description: >
|
description: >
|
||||||
Retrieves an existing cart.
|
Retrieves an existing cart.
|
||||||
- path: /:id
|
- path: /:id
|
||||||
method: POST
|
method: POST
|
||||||
title: Update a Cart
|
title: Update a Cart
|
||||||
params:
|
|
||||||
- name: id
|
|
||||||
type: String
|
|
||||||
description: Id of the cart.
|
|
||||||
body:
|
body:
|
||||||
- name: region_id
|
- name: region_id
|
||||||
type: String
|
type: String
|
||||||
@@ -84,17 +82,28 @@ endpoints:
|
|||||||
description: >
|
description: >
|
||||||
A list of objects with `code` that represents a discount code to be
|
A list of objects with `code` that represents a discount code to be
|
||||||
applied to the cart.
|
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: >
|
description: >
|
||||||
Updates an existing cart with the provided data. Updating the `region_id`
|
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
|
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
|
defined for the region. You can apply multiple discounts at once by
|
||||||
calling the endpoint with an array of discounts.
|
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
|
- path: /:id/line-items
|
||||||
method: POST
|
method: POST
|
||||||
title: Add line item
|
title: Add line item
|
||||||
params:
|
|
||||||
- name: id
|
|
||||||
type: String
|
|
||||||
body:
|
body:
|
||||||
- name: variant_id
|
- name: variant_id
|
||||||
type: String
|
type: String
|
||||||
@@ -114,13 +123,6 @@ endpoints:
|
|||||||
- path: /:id/line-items/:line_id
|
- path: /:id/line-items/:line_id
|
||||||
method: POST
|
method: POST
|
||||||
title: Update line item
|
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:
|
body:
|
||||||
- name: quantity
|
- name: quantity
|
||||||
type: Integer
|
type: Integer
|
||||||
@@ -132,84 +134,61 @@ endpoints:
|
|||||||
- path: /:id/line-items/:line_id
|
- path: /:id/line-items/:line_id
|
||||||
method: DELETE
|
method: DELETE
|
||||||
title: Remove line item
|
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: >
|
description: >
|
||||||
Removes a the given line item from the cart.
|
Removes a the given line item from the cart.
|
||||||
- path: /:id/discounts/:code
|
- path: /:id/discounts/:code
|
||||||
method: DELETE
|
method: DELETE
|
||||||
title: Remove discount code
|
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.
|
description: Removes a discount code from the cart.
|
||||||
- path: /:id/payment-sessions
|
- path: /:id/payment-sessions
|
||||||
method: POST
|
method: POST
|
||||||
title: Create payment sessions
|
title: Create payment sessions
|
||||||
params:
|
|
||||||
- name: id
|
|
||||||
type: String
|
|
||||||
description: Id of the cart.
|
|
||||||
description: >
|
description: >
|
||||||
Initializes the payment sessions that can be used to pay for the items of
|
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.
|
the cart. This is usually called when a customer proceeds to checkout.
|
||||||
- path: /:id/payment-sessions/:provider_id
|
- path: /:id/payment-sessions/:provider_id
|
||||||
method: DELETE
|
method: DELETE
|
||||||
title: Delete payment session
|
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: >
|
description: >
|
||||||
Deletes a payment session. Note that this will initialize a new payment
|
Deletes a payment session. Note that this will initialize a new payment
|
||||||
session with the provider. This is usually used if the payment session
|
session with the provider. This is usually used if the payment session
|
||||||
reaches a state that cannot be recovered from.
|
reaches a state that cannot be recovered from.
|
||||||
- path: /:id/payment-method
|
- path: /:id/payment-session
|
||||||
method: POST
|
method: POST
|
||||||
title: Add payment method
|
title: Set payment session on cart
|
||||||
params:
|
params:
|
||||||
- name: id
|
|
||||||
type: String
|
|
||||||
description: Id of the cart.
|
|
||||||
body:
|
|
||||||
- name: provider_id
|
- name: provider_id
|
||||||
type: String
|
type: String
|
||||||
required: true
|
required: true
|
||||||
description: >
|
description: Id of the provider (e.g. stripe).
|
||||||
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: >
|
description: >
|
||||||
Adds or updates the payment method that will be used to pay for the items
|
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
|
in the cart. The payment is not processed until the payment provider's
|
||||||
authorization functionality is called. You should consult with your
|
authorization functionality is called. You should consult with your
|
||||||
installed payment plugins to find information about how to authorize a
|
installed payment plugins to find information about how to authorize a
|
||||||
payment.
|
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
|
- path: /:id/shipping-methods
|
||||||
method: POST
|
method: POST
|
||||||
title: Add shipping method
|
title: Add shipping method
|
||||||
params:
|
|
||||||
- name: id
|
|
||||||
type: String
|
|
||||||
description: Id of the cart.
|
|
||||||
body:
|
body:
|
||||||
- name: option_id
|
- name: option_id
|
||||||
type: String
|
type: String
|
||||||
@@ -226,115 +205,145 @@ endpoints:
|
|||||||
response: |
|
response: |
|
||||||
{
|
{
|
||||||
"cart": {
|
"cart": {
|
||||||
"_id": "5f68a234d694d000217a4d64",
|
"id": "cart_1234",
|
||||||
"customer_id": "",
|
"email": null,
|
||||||
"region_id": "5f4cd57a5d1e3200214c0e4e",
|
"billing_address_id": null,
|
||||||
"email": "",
|
"billing_address": null,
|
||||||
|
"shipping_address_id": "addr_1234",
|
||||||
"shipping_address": {
|
"shipping_address": {
|
||||||
"_id": "5f68a234d694d000217a4d65",
|
"id": "addr_1234",
|
||||||
"country_code": "US"
|
"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": [
|
"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,
|
"is_giftcard": false,
|
||||||
|
"should_merge": true,
|
||||||
|
"allow_discounts": true,
|
||||||
"has_shipping": false,
|
"has_shipping": false,
|
||||||
"returned": false,
|
"unit_price": 6000,
|
||||||
"fulfilled": false,
|
"variant_id": "variant_1234",
|
||||||
"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": {
|
"variant": {
|
||||||
"_id": "5f4cf81a2ac41700211f6e63",
|
"id": "variant_1234",
|
||||||
"barcode": "",
|
"title": "30x30",
|
||||||
"image": "",
|
"product_id": "prod_1234",
|
||||||
"published": true,
|
"sku": null,
|
||||||
"inventory_quantity": 11,
|
"barcode": null,
|
||||||
|
"ean": null,
|
||||||
|
"upc": null,
|
||||||
|
"inventory_quantity": 9999,
|
||||||
"allow_backorder": false,
|
"allow_backorder": false,
|
||||||
"manage_inventory": true,
|
"manage_inventory": true,
|
||||||
"title": "1234",
|
"hs_code": null,
|
||||||
"sku": "SKU1234",
|
"origin_country": null,
|
||||||
"ean": "1111111111111",
|
"mid_code": null,
|
||||||
"options": [
|
"material": null,
|
||||||
{
|
"weight": null,
|
||||||
"_id": "5f4cf81a2ac41700211f6e64",
|
"length": null,
|
||||||
"value": "1234",
|
"height": null,
|
||||||
"option_id": "5f4cf81a2ac41700211f6e62"
|
"width": null,
|
||||||
}
|
"created_at": "2021-01-19T17:09:01.864Z",
|
||||||
],
|
"updated_at": "2021-02-02T02:43:17.718Z",
|
||||||
"prices": [
|
"deleted_at": null,
|
||||||
{
|
"metadata": null
|
||||||
"_id": "5f4cf81a2ac41700211f6e65",
|
|
||||||
"currency_code": "SEK",
|
|
||||||
"amount": 3180
|
|
||||||
},
|
},
|
||||||
{
|
"quantity": 1,
|
||||||
"_id": "5f4cf81a2ac41700211f6e66",
|
"fulfilled_quantity": null,
|
||||||
"currency_code": "EUR",
|
"returned_quantity": null,
|
||||||
"amount": 303.2
|
"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
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"_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": [],
|
"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": [],
|
"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,
|
"shipping_total": 0,
|
||||||
"discount_total": 0,
|
"discount_total": 0,
|
||||||
"tax_total": 0,
|
"tax_total": 1500,
|
||||||
"subtotal": 303.2,
|
"gift_card_total": 0,
|
||||||
"total": 303.2,
|
"subtotal": 6000,
|
||||||
"region": {
|
"total": 7500
|
||||||
"_id": "5f4cd57a5d1e3200214c0e4e",
|
|
||||||
"tax_rate": 0,
|
|
||||||
"countries": [
|
|
||||||
"US"
|
|
||||||
],
|
|
||||||
"payment_providers": [
|
|
||||||
"stripe"
|
|
||||||
],
|
|
||||||
"fulfillment_providers": [
|
|
||||||
"shiphero"
|
|
||||||
],
|
|
||||||
"name": "United States",
|
|
||||||
"currency_code": "EUR",
|
|
||||||
"tax_code": "ABR"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ routes:
|
|||||||
path: /customers/:id/reset-password-token
|
path: /customers/:id/reset-password-token
|
||||||
route: /customers
|
route: /customers
|
||||||
description: >
|
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.
|
Customers must have unique emails.
|
||||||
endpoints:
|
endpoints:
|
||||||
- path: /
|
- path: /
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
dist
|
dist/
|
||||||
node_modules
|
node_modules
|
||||||
*yarn-error.log
|
*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" }],
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -18,4 +18,5 @@ module.exports = {
|
|||||||
`.cache`,
|
`.cache`,
|
||||||
],
|
],
|
||||||
transform: { "^.+\\.[jt]s$": `<rootDir>/jest-transformer.js` },
|
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": {
|
"command": {
|
||||||
"publish": {
|
"publish": {
|
||||||
"allowBranch": "master",
|
"allowBranch": "release/*",
|
||||||
"bump": "patch",
|
"bump": "patch",
|
||||||
"conventionalCommits": true,
|
"conventionalCommits": true,
|
||||||
"message": "chore(release): Publish"
|
"message": "chore(release): Publish"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
"typeorm": "^0.2.30"
|
"typeorm": "^0.2.30"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"publish:next": "lerna publish --canary --preid next --dist-tag next",
|
||||||
"bootstrap": "lerna bootstrap",
|
"bootstrap": "lerna bootstrap",
|
||||||
"jest": "jest",
|
"jest": "jest",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-file-spaces
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-file-spaces",
|
"name": "medusa-file-spaces",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "Digital Ocean Spaces file connector for Medusa",
|
"description": "Digital Ocean Spaces file connector for Medusa",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2",
|
"medusa-test-utils": "^1.1.3",
|
||||||
"stripe": "^8.50.0"
|
"stripe": "^8.50.0"
|
||||||
},
|
},
|
||||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
|
|||||||
@@ -3,6 +3,17 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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.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",
|
"name": "medusa-fulfillment-webshipper",
|
||||||
"version": "1.1.1",
|
"version": "1.1.2",
|
||||||
"description": "Webshipper Fulfillment provider for Medusa",
|
"description": "Webshipper Fulfillment provider for Medusa",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -422,8 +422,41 @@ class WebshipperFulfillmentService extends FulfillmentService {
|
|||||||
/**
|
/**
|
||||||
* This plugin doesn't support shipment documents.
|
* This plugin doesn't support shipment documents.
|
||||||
*/
|
*/
|
||||||
async getShipmentDocuments() {
|
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 []
|
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.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
# [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
|
**Note:** Version bump only for package medusa-interfaces
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-interfaces",
|
"name": "medusa-interfaces",
|
||||||
"version": "1.1.0",
|
"version": "1.1.1",
|
||||||
"description": "Core interfaces for Medusa",
|
"description": "Core interfaces for Medusa",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ export { default as BaseModel } from "./base-model"
|
|||||||
export { default as PaymentService } from "./payment-service"
|
export { default as PaymentService } from "./payment-service"
|
||||||
export { default as FulfillmentService } from "./fulfillment-service"
|
export { default as FulfillmentService } from "./fulfillment-service"
|
||||||
export { default as FileService } from "./file-service"
|
export { default as FileService } from "./file-service"
|
||||||
|
export { default as NotificationService } from "./notification-service"
|
||||||
export { default as OauthService } from "./oauth-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.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-payment-adyen
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-payment-adyen",
|
"name": "medusa-payment-adyen",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "Adyen Payment provider for Medusa Commerce",
|
"description": "Adyen Payment provider for Medusa Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"jest": "^25.5.2",
|
"jest": "^25.5.2",
|
||||||
"medusa-test-utils": "^1.1.2"
|
"medusa-test-utils": "^1.1.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "babel src -d .",
|
"build": "babel src -d .",
|
||||||
|
|||||||
@@ -3,6 +3,25 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-payment-klarna
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-payment-klarna",
|
"name": "medusa-payment-klarna",
|
||||||
"version": "1.1.2",
|
"version": "1.1.4",
|
||||||
"description": "Klarna Payment provider for Medusa Commerce",
|
"description": "Klarna Payment provider for Medusa Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2"
|
"medusa-test-utils": "^1.1.3"
|
||||||
},
|
},
|
||||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -397,6 +397,11 @@ class KlarnaProviderService extends PaymentService {
|
|||||||
return this.klarna_
|
return this.klarna_
|
||||||
.post(`${this.klarnaOrderUrl_}/${paymentData.order_id}`, order)
|
.post(`${this.klarnaOrderUrl_}/${paymentData.order_id}`, order)
|
||||||
.then(({ data }) => data)
|
.then(({ data }) => data)
|
||||||
|
.catch(async (_) => {
|
||||||
|
return this.klarna_
|
||||||
|
.post(this.klarnaOrderUrl_, order)
|
||||||
|
.then(({ data }) => data)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return paymentData
|
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.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-payment-stripe
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-payment-stripe",
|
"name": "medusa-payment-stripe",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "Stripe Payment provider for Meduas Commerce",
|
"description": "Stripe Payment provider for Meduas Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"cross-env": "^5.2.1",
|
"cross-env": "^5.2.1",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"jest": "^25.5.2",
|
"jest": "^25.5.2",
|
||||||
"medusa-test-utils": "^1.1.2"
|
"medusa-test-utils": "^1.1.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "babel src -d . --ignore **/__tests__",
|
"build": "babel src -d . --ignore **/__tests__",
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-plugin-add-ons
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-add-ons",
|
"name": "medusa-plugin-add-ons",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "Add-on plugin for Medusa Commerce",
|
"description": "Add-on plugin for Medusa Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"jest": "^25.5.2",
|
"jest": "^25.5.2",
|
||||||
"medusa-test-utils": "^1.1.2"
|
"medusa-test-utils": "^1.1.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "babel src -d . --ignore **/__tests__",
|
"build": "babel src -d . --ignore **/__tests__",
|
||||||
|
|||||||
@@ -3,6 +3,22 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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.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",
|
"name": "medusa-plugin-brightpearl",
|
||||||
"version": "1.1.2",
|
"version": "1.1.4",
|
||||||
"description": "Brightpearl plugin for Medusa Commerce",
|
"description": "Brightpearl plugin for Medusa Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
"cross-env": "^7.0.2",
|
"cross-env": "^7.0.2",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"jest": "^25.5.2",
|
"jest": "^25.5.2",
|
||||||
"medusa-test-utils": "^1.1.2",
|
"medusa-test-utils": "^1.1.3",
|
||||||
"prettier": "^2.0.5"
|
"prettier": "^2.0.5"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -12,4 +12,5 @@ yarn.lock
|
|||||||
/services
|
/services
|
||||||
/models
|
/models
|
||||||
/subscribers
|
/subscribers
|
||||||
|
/loaders
|
||||||
|
|
||||||
@@ -3,6 +3,17 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-plugin-contentful
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-contentful",
|
"name": "medusa-plugin-contentful",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "Contentful plugin for Medusa Commerce",
|
"description": "Contentful plugin for Medusa Commerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
"contentful-management": "^5.27.1",
|
"contentful-management": "^5.27.1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2",
|
"medusa-test-utils": "^1.1.3",
|
||||||
"redis": "^3.0.2"
|
"redis": "^3.0.2"
|
||||||
},
|
},
|
||||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
"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,15 +114,14 @@ class ContentfulService extends BaseService {
|
|||||||
async createProductInContentful(product) {
|
async createProductInContentful(product) {
|
||||||
try {
|
try {
|
||||||
const p = await this.productService_.retrieve(product.id, {
|
const p = await this.productService_.retrieve(product.id, {
|
||||||
relations: ["variants", "options"],
|
relations: ["variants", "options", "tags", "type", "collection"],
|
||||||
})
|
})
|
||||||
|
|
||||||
const environment = await this.getContentfulEnvironment_()
|
const environment = await this.getContentfulEnvironment_()
|
||||||
const variantEntries = await this.getVariantEntries_(p.variants)
|
const variantEntries = await this.getVariantEntries_(p.variants)
|
||||||
const variantLinks = this.getVariantLinks_(variantEntries)
|
const variantLinks = this.getVariantLinks_(variantEntries)
|
||||||
|
|
||||||
const result = await environment.createEntryWithId("product", p.id, {
|
const fields = {
|
||||||
fields: {
|
|
||||||
title: {
|
title: {
|
||||||
"en-US": p.title,
|
"en-US": p.title,
|
||||||
},
|
},
|
||||||
@@ -135,7 +134,38 @@ class ContentfulService extends BaseService {
|
|||||||
objectId: {
|
objectId: {
|
||||||
"en-US": p.id,
|
"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")) || []
|
const ignoreIds = (await this.getIgnoreIds_("product")) || []
|
||||||
@@ -210,7 +240,7 @@ class ContentfulService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const p = await this.productService_.retrieve(product.id, {
|
const p = await this.productService_.retrieve(product.id, {
|
||||||
relations: ["options", "variants"],
|
relations: ["options", "variants", "type", "collection", "tags"],
|
||||||
})
|
})
|
||||||
|
|
||||||
const variantEntries = await this.getVariantEntries_(p.variants)
|
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
|
productEntry.fields = productEntryFields
|
||||||
|
|
||||||
const updatedEntry = await productEntry.update()
|
const updatedEntry = await productEntry.update()
|
||||||
@@ -372,6 +430,11 @@ class ContentfulService extends BaseService {
|
|||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getType(type) {
|
||||||
|
const environment = await this.getContentfulEnvironment_()
|
||||||
|
return environment.getContentType(type)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ContentfulService
|
export default ContentfulService
|
||||||
@@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-plugin-economic
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-economic",
|
"name": "medusa-plugin-economic",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-economic",
|
"name": "medusa-plugin-economic",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "E-conomic financial reporting",
|
"description": "E-conomic financial reporting",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2",
|
"medusa-test-utils": "^1.1.3",
|
||||||
"moment": "^2.27.0"
|
"moment": "^2.27.0"
|
||||||
},
|
},
|
||||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-plugin-mailchimp
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-mailchimp",
|
"name": "medusa-plugin-mailchimp",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "Mailchimp newsletter subscriptions",
|
"description": "Mailchimp newsletter subscriptions",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -41,7 +41,7 @@
|
|||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"mailchimp-api-v3": "^1.14.0",
|
"mailchimp-api-v3": "^1.14.0",
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2"
|
"medusa-test-utils": "^1.1.3"
|
||||||
},
|
},
|
||||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-plugin-permissions
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-permissions",
|
"name": "medusa-plugin-permissions",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "Role permission for Medusa core",
|
"description": "Role permission for Medusa core",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2",
|
"medusa-test-utils": "^1.1.3",
|
||||||
"mongoose": "^5.8.0"
|
"mongoose": "^5.8.0"
|
||||||
},
|
},
|
||||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
|
|||||||
@@ -3,6 +3,18 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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.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",
|
"name": "medusa-plugin-segment",
|
||||||
"version": "1.1.3",
|
"version": "1.1.4",
|
||||||
"description": "Segment Analytics",
|
"description": "Segment Analytics",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2"
|
"medusa-test-utils": "^1.1.3"
|
||||||
},
|
},
|
||||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -106,10 +106,10 @@ class SegmentService extends BaseService {
|
|||||||
let name = item.title
|
let name = item.title
|
||||||
|
|
||||||
const unit_price = item.unit_price
|
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(
|
const revenue = await this.getReportingValue(
|
||||||
order.currency_code,
|
order.currency_code,
|
||||||
line_total
|
line_total / 100
|
||||||
)
|
)
|
||||||
|
|
||||||
let sku = ""
|
let sku = ""
|
||||||
@@ -126,7 +126,7 @@ class SegmentService extends BaseService {
|
|||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
variant,
|
variant,
|
||||||
price: unit_price / 100,
|
price: line_total / 100 / item.quantity,
|
||||||
reporting_revenue: revenue,
|
reporting_revenue: revenue,
|
||||||
product_id: item.variant.product_id,
|
product_id: item.variant.product_id,
|
||||||
sku,
|
sku,
|
||||||
|
|||||||
@@ -3,6 +3,23 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-plugin-sendgrid
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-sendgrid",
|
"name": "medusa-plugin-sendgrid",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "SendGrid transactional emails",
|
"description": "SendGrid transactional emails",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2"
|
"medusa-test-utils": "^1.1.3"
|
||||||
},
|
},
|
||||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
import { BaseService } from "medusa-interfaces"
|
import { NotificationService } from "medusa-interfaces"
|
||||||
import SendGrid from "@sendgrid/mail"
|
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`
|
* @param {Object} options - options defined in `medusa-config.js`
|
||||||
* e.g.
|
* e.g.
|
||||||
@@ -15,67 +17,218 @@ class SendGridService extends BaseService {
|
|||||||
* customer_password_reset_template: 1111,
|
* customer_password_reset_template: 1111,
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
constructor({}, options) {
|
constructor(
|
||||||
|
{
|
||||||
|
storeService,
|
||||||
|
orderService,
|
||||||
|
returnService,
|
||||||
|
swapService,
|
||||||
|
lineItemService,
|
||||||
|
claimService,
|
||||||
|
fulfillmentService,
|
||||||
|
fulfillmentProviderService,
|
||||||
|
totalsService,
|
||||||
|
},
|
||||||
|
options
|
||||||
|
) {
|
||||||
super()
|
super()
|
||||||
|
|
||||||
this.options_ = options
|
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)
|
SendGrid.setApiKey(options.api_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async fetchAttachments(event, data, attachmentGenerator) {
|
||||||
* 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
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case "order.gift_card_created":
|
case "swap.created":
|
||||||
templateId = this.options_.gift_card_created_template
|
case "order.return_requested": {
|
||||||
data = {
|
let attachments = []
|
||||||
...data,
|
const { shipping_method, shipping_data } = data.return_request
|
||||||
display_value: data.giftcard.rule.value * (1 + data.tax_rate),
|
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,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
}
|
}
|
||||||
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:
|
default:
|
||||||
return
|
return []
|
||||||
}
|
}
|
||||||
try {
|
}
|
||||||
if (templateId) {
|
|
||||||
return SendGrid.send({
|
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,
|
template_id: templateId,
|
||||||
from: this.options_.from,
|
from: this.options_.from,
|
||||||
to: data.email,
|
to: data.email,
|
||||||
dynamic_template_data: data,
|
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,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
|
||||||
throw error
|
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 }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,6 +246,418 @@ class SendGridService extends BaseService {
|
|||||||
throw error
|
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
|
export default SendGridService
|
||||||
@@ -3,187 +3,26 @@ class OrderSubscriber {
|
|||||||
totalsService,
|
totalsService,
|
||||||
orderService,
|
orderService,
|
||||||
sendgridService,
|
sendgridService,
|
||||||
eventBusService,
|
notificationService,
|
||||||
fulfillmentService,
|
fulfillmentService,
|
||||||
}) {
|
}) {
|
||||||
this.orderService_ = orderService
|
this.orderService_ = orderService
|
||||||
this.totalsService_ = totalsService
|
this.totalsService_ = totalsService
|
||||||
this.sendgridService_ = sendgridService
|
this.sendgridService_ = sendgridService
|
||||||
this.eventBus_ = eventBusService
|
this.notificationService_ = notificationService
|
||||||
this.fulfillmentService_ = fulfillmentService
|
this.fulfillmentService_ = fulfillmentService
|
||||||
|
|
||||||
this.eventBus_.subscribe(
|
this.notificationService_.subscribe("order.shipment_created", "sendgrid")
|
||||||
"order.shipment_created",
|
this.notificationService_.subscribe("order.gift_card_created", "sendgrid")
|
||||||
async ({ id, fulfillment_id }) => {
|
this.notificationService_.subscribe("gift_card.created", "sendgrid")
|
||||||
const order = await this.orderService_.retrieve(id, {
|
this.notificationService_.subscribe("order.placed", "sendgrid")
|
||||||
select: [
|
this.notificationService_.subscribe("order.canceled", "sendgrid")
|
||||||
"shipping_total",
|
this.notificationService_.subscribe("customer.password_reset", "sendgrid")
|
||||||
"discount_total",
|
this.notificationService_.subscribe("claim.shipment_created", "sendgrid")
|
||||||
"tax_total",
|
this.notificationService_.subscribe("swap.shipment_created", "sendgrid")
|
||||||
"refunded_total",
|
this.notificationService_.subscribe("swap.created", "sendgrid")
|
||||||
"gift_card_total",
|
this.notificationService_.subscribe("order.items_returned", "sendgrid")
|
||||||
"subtotal",
|
this.notificationService_.subscribe("order.return_requested", "sendgrid")
|
||||||
"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)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,6 @@ class UserSubscriber {
|
|||||||
data
|
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.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-plugin-slack-notification
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-economic",
|
"name": "medusa-plugin-economic",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-slack-notification",
|
"name": "medusa-plugin-slack-notification",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "Slack notifications",
|
"description": "Slack notifications",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2",
|
"medusa-test-utils": "^1.1.3",
|
||||||
"moment": "^2.27.0"
|
"moment": "^2.27.0"
|
||||||
},
|
},
|
||||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-plugin-twilio-sms
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-twilio-sms",
|
"name": "medusa-plugin-twilio-sms",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
"@babel/plugin-transform-classes": "^7.9.5",
|
"@babel/plugin-transform-classes": "^7.9.5",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2",
|
"medusa-test-utils": "^1.1.3",
|
||||||
"twilio": "^3.49.1"
|
"twilio": "^3.49.1"
|
||||||
},
|
},
|
||||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
|
|||||||
@@ -3,6 +3,14 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package medusa-plugin-wishlist
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "medusa-plugin-wishlist",
|
"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",
|
"description": "Provides /customers/:id/wishlist to add items to a customr's wishlist",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2"
|
"medusa-test-utils": "^1.1.3"
|
||||||
},
|
},
|
||||||
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
"gitHead": "0646bd395a6056657cb0aa93c13699c4a9dbbcdd"
|
||||||
}
|
}
|
||||||
@@ -3,6 +3,17 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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.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",
|
"name": "medusa-test-utils",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"description": "Test utils for Medusa",
|
"description": "Test utils for Medusa",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ class MockRepo {
|
|||||||
softRemove,
|
softRemove,
|
||||||
find,
|
find,
|
||||||
findOne,
|
findOne,
|
||||||
|
findOneWithRelations,
|
||||||
findOneOrFail,
|
findOneOrFail,
|
||||||
save,
|
save,
|
||||||
findAndCount,
|
findAndCount,
|
||||||
@@ -19,6 +20,7 @@ class MockRepo {
|
|||||||
this.findOneOrFail_ = findOneOrFail;
|
this.findOneOrFail_ = findOneOrFail;
|
||||||
this.save_ = save;
|
this.save_ = save;
|
||||||
this.findAndCount_ = findAndCount;
|
this.findAndCount_ = findAndCount;
|
||||||
|
this.findOneWithRelations_ = findOneWithRelations;
|
||||||
}
|
}
|
||||||
|
|
||||||
setFindOne(fn) {
|
setFindOne(fn) {
|
||||||
@@ -53,6 +55,11 @@ class MockRepo {
|
|||||||
return this.findOneOrFail_(...args);
|
return this.findOneOrFail_(...args);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
findOneWithRelations = jest.fn().mockImplementation((...args) => {
|
||||||
|
if (this.findOneWithRelations_) {
|
||||||
|
return this.findOneWithRelations_(...args);
|
||||||
|
}
|
||||||
|
});
|
||||||
findOne = jest.fn().mockImplementation((...args) => {
|
findOne = jest.fn().mockImplementation((...args) => {
|
||||||
if (this.findOne_) {
|
if (this.findOne_) {
|
||||||
return this.findOne_(...args);
|
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.
|
All notable changes to this project will be documented in this file.
|
||||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
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)
|
## [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
|
**Note:** Version bump only for package @medusajs/medusa
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@medusajs/medusa",
|
"name": "@medusajs/medusa",
|
||||||
"version": "1.1.6",
|
"version": "1.1.8",
|
||||||
"description": "E-commerce for JAMstack",
|
"description": "E-commerce for JAMstack",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/plugin-transform-classes": "^7.9.5",
|
"@babel/plugin-transform-classes": "^7.9.5",
|
||||||
"@hapi/joi": "^16.1.8",
|
"@hapi/joi": "^16.1.8",
|
||||||
|
"@types/lodash": "^4.14.168",
|
||||||
"awilix": "^4.2.3",
|
"awilix": "^4.2.3",
|
||||||
"body-parser": "^1.19.0",
|
"body-parser": "^1.19.0",
|
||||||
"bull": "^3.12.1",
|
"bull": "^3.12.1",
|
||||||
@@ -67,7 +68,7 @@
|
|||||||
"joi-objectid": "^3.0.1",
|
"joi-objectid": "^3.0.1",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"medusa-core-utils": "^1.1.0",
|
"medusa-core-utils": "^1.1.0",
|
||||||
"medusa-test-utils": "^1.1.2",
|
"medusa-test-utils": "^1.1.3",
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.9.1",
|
||||||
"multer": "^1.4.2",
|
"multer": "^1.4.2",
|
||||||
"passport": "^0.4.0",
|
"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 returnRoutes from "./returns"
|
||||||
import variantRoutes from "./variants"
|
import variantRoutes from "./variants"
|
||||||
import draftOrderRoutes from "./draft-orders"
|
import draftOrderRoutes from "./draft-orders"
|
||||||
|
import collectionRoutes from "./collections"
|
||||||
|
import notificationRoutes from "./notifications"
|
||||||
|
|
||||||
const route = Router()
|
const route = Router()
|
||||||
|
|
||||||
@@ -62,6 +64,8 @@ export default (app, container, config) => {
|
|||||||
returnRoutes(route)
|
returnRoutes(route)
|
||||||
variantRoutes(route)
|
variantRoutes(route)
|
||||||
draftOrderRoutes(route)
|
draftOrderRoutes(route)
|
||||||
|
collectionRoutes(route)
|
||||||
|
notificationRoutes(route)
|
||||||
|
|
||||||
return app
|
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)
|
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.
|
* Delete metadata key / value pair.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ export default async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
const orderService = req.scope.resolve("orderService")
|
const orderService = req.scope.resolve("orderService")
|
||||||
const returnService = req.scope.resolve("returnService")
|
const returnService = req.scope.resolve("returnService")
|
||||||
|
const eventBus = req.scope.resolve("eventBusService")
|
||||||
|
|
||||||
let inProgress = true
|
let inProgress = true
|
||||||
let err = false
|
let err = false
|
||||||
@@ -98,6 +99,13 @@ export default async (req, res) => {
|
|||||||
.fulfill(createdReturn.id)
|
.fulfill(createdReturn.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await eventBus
|
||||||
|
.withTransaction(manager)
|
||||||
|
.emit("order.return_requested", {
|
||||||
|
id,
|
||||||
|
return_id: createdReturn.id,
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
recovery_point: "return_requested",
|
recovery_point: "return_requested",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe("POST /admin/products", () => {
|
|||||||
payload: {
|
payload: {
|
||||||
title: "Test Product",
|
title: "Test Product",
|
||||||
description: "Test Description",
|
description: "Test Description",
|
||||||
tags: "hi,med,dig",
|
tags: [{ id: "test", value: "test" }],
|
||||||
handle: "test-product",
|
handle: "test-product",
|
||||||
},
|
},
|
||||||
adminSession: {
|
adminSession: {
|
||||||
@@ -36,7 +36,7 @@ describe("POST /admin/products", () => {
|
|||||||
expect(ProductServiceMock.create).toHaveBeenCalledWith({
|
expect(ProductServiceMock.create).toHaveBeenCalledWith({
|
||||||
title: "Test Product",
|
title: "Test Product",
|
||||||
description: "Test Description",
|
description: "Test Description",
|
||||||
tags: "hi,med,dig",
|
tags: [{ id: "test", value: "test" }],
|
||||||
handle: "test-product",
|
handle: "test-product",
|
||||||
is_giftcard: false,
|
is_giftcard: false,
|
||||||
profile_id: IdMap.getId("default_shipping_profile"),
|
profile_id: IdMap.getId("default_shipping_profile"),
|
||||||
|
|||||||
@@ -34,11 +34,12 @@ describe("GET /admin/products/:id", () => {
|
|||||||
"title",
|
"title",
|
||||||
"subtitle",
|
"subtitle",
|
||||||
"description",
|
"description",
|
||||||
"tags",
|
|
||||||
"handle",
|
"handle",
|
||||||
"is_giftcard",
|
"is_giftcard",
|
||||||
"thumbnail",
|
"thumbnail",
|
||||||
"profile_id",
|
"profile_id",
|
||||||
|
"collection_id",
|
||||||
|
"type_id",
|
||||||
"weight",
|
"weight",
|
||||||
"length",
|
"length",
|
||||||
"height",
|
"height",
|
||||||
@@ -57,6 +58,9 @@ describe("GET /admin/products/:id", () => {
|
|||||||
"variants.options",
|
"variants.options",
|
||||||
"images",
|
"images",
|
||||||
"options",
|
"options",
|
||||||
|
"tags",
|
||||||
|
"type",
|
||||||
|
"collection",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,13 +6,28 @@ export default async (req, res) => {
|
|||||||
title: Validator.string().required(),
|
title: Validator.string().required(),
|
||||||
subtitle: Validator.string().allow(""),
|
subtitle: Validator.string().allow(""),
|
||||||
description: Validator.string().allow(""),
|
description: Validator.string().allow(""),
|
||||||
tags: Validator.string().optional(),
|
|
||||||
is_giftcard: Validator.boolean().default(false),
|
is_giftcard: Validator.boolean().default(false),
|
||||||
images: Validator.array()
|
images: Validator.array()
|
||||||
.items(Validator.string())
|
.items(Validator.string())
|
||||||
.optional(),
|
.optional(),
|
||||||
thumbnail: Validator.string().optional(),
|
thumbnail: Validator.string().optional(),
|
||||||
handle: 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({
|
options: Validator.array().items({
|
||||||
title: Validator.string().required(),
|
title: Validator.string().required(),
|
||||||
}),
|
}),
|
||||||
@@ -48,7 +63,7 @@ export default async (req, res) => {
|
|||||||
Validator.object()
|
Validator.object()
|
||||||
.keys({
|
.keys({
|
||||||
region_id: Validator.string(),
|
region_id: Validator.string(),
|
||||||
currency_code: Validator.string().required(),
|
currency_code: Validator.string(),
|
||||||
amount: Validator.number()
|
amount: Validator.number()
|
||||||
.integer()
|
.integer()
|
||||||
.required(),
|
.required(),
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ export default app => {
|
|||||||
|
|
||||||
route.post("/", middlewares.wrap(require("./create-product").default))
|
route.post("/", middlewares.wrap(require("./create-product").default))
|
||||||
route.post("/:id", middlewares.wrap(require("./update-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(
|
route.post(
|
||||||
"/:id/variants",
|
"/:id/variants",
|
||||||
@@ -52,6 +57,9 @@ export const defaultRelations = [
|
|||||||
"variants.options",
|
"variants.options",
|
||||||
"images",
|
"images",
|
||||||
"options",
|
"options",
|
||||||
|
"tags",
|
||||||
|
"type",
|
||||||
|
"collection",
|
||||||
]
|
]
|
||||||
|
|
||||||
export const defaultFields = [
|
export const defaultFields = [
|
||||||
@@ -59,11 +67,12 @@ export const defaultFields = [
|
|||||||
"title",
|
"title",
|
||||||
"subtitle",
|
"subtitle",
|
||||||
"description",
|
"description",
|
||||||
"tags",
|
|
||||||
"handle",
|
"handle",
|
||||||
"is_giftcard",
|
"is_giftcard",
|
||||||
"thumbnail",
|
"thumbnail",
|
||||||
"profile_id",
|
"profile_id",
|
||||||
|
"collection_id",
|
||||||
|
"type_id",
|
||||||
"weight",
|
"weight",
|
||||||
"length",
|
"length",
|
||||||
"height",
|
"height",
|
||||||
@@ -82,11 +91,12 @@ export const allowedFields = [
|
|||||||
"title",
|
"title",
|
||||||
"subtitle",
|
"subtitle",
|
||||||
"description",
|
"description",
|
||||||
"tags",
|
|
||||||
"handle",
|
"handle",
|
||||||
"is_giftcard",
|
"is_giftcard",
|
||||||
"thumbnail",
|
"thumbnail",
|
||||||
"profile_id",
|
"profile_id",
|
||||||
|
"collection_id",
|
||||||
|
"type_id",
|
||||||
"weight",
|
"weight",
|
||||||
"length",
|
"length",
|
||||||
"height",
|
"height",
|
||||||
@@ -105,4 +115,7 @@ export const allowedRelations = [
|
|||||||
"variants.prices",
|
"variants.prices",
|
||||||
"images",
|
"images",
|
||||||
"options",
|
"options",
|
||||||
|
"tags",
|
||||||
|
"type",
|
||||||
|
"collection",
|
||||||
]
|
]
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export default async (req, res) => {
|
||||||
|
try {
|
||||||
|
const productService = req.scope.resolve("productService")
|
||||||
|
|
||||||
|
const tags = await productService.listTagsByUsage()
|
||||||
|
|
||||||
|
res.json({ tags })
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
Loaded 100 of 151 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user