From 5cfb662ec05fe53166cb6150d7eb92217999d447 Mon Sep 17 00:00:00 2001 From: Sebastian Rindom Date: Thu, 18 Jan 2024 10:38:33 +0100 Subject: [PATCH] fix: migrate segment subscribers + typescript support (#5904) Fixes #6068. **What** - Uses new Subscriber API. - Adds support for typescript files in Segment plugin. - Adds a peerDependency on @medusajs/medusa for the version that introduced new Subscriber API. --- packages/medusa-plugin-segment/.babelrc | 13 - packages/medusa-plugin-segment/package.json | 23 +- .../src/services/segment.js | 6 +- .../src/subscribers/claim-created.ts | 46 +++ .../src/subscribers/order-canceled.ts | 21 ++ .../src/subscribers/order-items-returned.ts | 82 +++++ .../src/subscribers/order-placed.ts | 87 +++++ .../src/subscribers/order-shipment-created.ts | 60 ++++ .../src/subscribers/order.js | 338 ------------------ .../src/subscribers/swap-created.ts | 24 ++ .../src/subscribers/swap-payment-completed.ts | 36 ++ .../src/subscribers/swap-shipment-created.ts | 41 +++ .../src/subscribers/swap.js | 149 -------- .../src/utils/gather-swap-report.ts | 71 ++++ .../medusa-plugin-segment/tsconfig.admin.json | 8 + packages/medusa-plugin-segment/tsconfig.json | 35 ++ .../tsconfig.server.json | 7 + .../medusa-plugin-segment/tsconfig.spec.json | 5 + yarn.lock | 15 +- 19 files changed, 536 insertions(+), 531 deletions(-) delete mode 100644 packages/medusa-plugin-segment/.babelrc create mode 100644 packages/medusa-plugin-segment/src/subscribers/claim-created.ts create mode 100644 packages/medusa-plugin-segment/src/subscribers/order-canceled.ts create mode 100644 packages/medusa-plugin-segment/src/subscribers/order-items-returned.ts create mode 100644 packages/medusa-plugin-segment/src/subscribers/order-placed.ts create mode 100644 packages/medusa-plugin-segment/src/subscribers/order-shipment-created.ts delete mode 100644 packages/medusa-plugin-segment/src/subscribers/order.js create mode 100644 packages/medusa-plugin-segment/src/subscribers/swap-created.ts create mode 100644 packages/medusa-plugin-segment/src/subscribers/swap-payment-completed.ts create mode 100644 packages/medusa-plugin-segment/src/subscribers/swap-shipment-created.ts delete mode 100644 packages/medusa-plugin-segment/src/subscribers/swap.js create mode 100644 packages/medusa-plugin-segment/src/utils/gather-swap-report.ts create mode 100644 packages/medusa-plugin-segment/tsconfig.admin.json create mode 100644 packages/medusa-plugin-segment/tsconfig.json create mode 100644 packages/medusa-plugin-segment/tsconfig.server.json create mode 100644 packages/medusa-plugin-segment/tsconfig.spec.json diff --git a/packages/medusa-plugin-segment/.babelrc b/packages/medusa-plugin-segment/.babelrc deleted file mode 100644 index 4d2dfe8f09..0000000000 --- a/packages/medusa-plugin-segment/.babelrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "plugins": [ - "@babel/plugin-proposal-class-properties", - "@babel/plugin-transform-instanceof", - "@babel/plugin-transform-classes" - ], - "presets": ["@babel/preset-env"], - "env": { - "test": { - "plugins": ["@babel/plugin-transform-runtime"] - } - } -} diff --git a/packages/medusa-plugin-segment/package.json b/packages/medusa-plugin-segment/package.json index 4598e08d10..9b1d5b63dc 100644 --- a/packages/medusa-plugin-segment/package.json +++ b/packages/medusa-plugin-segment/package.json @@ -14,36 +14,27 @@ "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-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", "cross-env": "^5.2.1", "eslint": "^6.8.0", "jest": "^25.5.4", "medusa-interfaces": "^1.3.7", - "medusa-test-utils": "^1.1.40" + "medusa-test-utils": "^1.1.40", + "rimraf": "^5.0.1", + "typescript": "^4.9.5" }, "scripts": { - "prepare": "cross-env NODE_ENV=production yarn run build", + "prepublishOnly": "cross-env NODE_ENV=production tsc --build", "test": "jest --passWithNoTests src", - "build": "babel src --out-dir . --ignore '**/__tests__','**/__mocks__'", - "watch": "babel -w src --out-dir . --ignore '**/__tests__','**/__mocks__'" + "build": "rimraf dist && tsc -p ./tsconfig.server.json", + "watch": "tsc --watch" }, "peerDependencies": { + "@medusajs/medusa": ">= 1.18.0 < 2", "medusa-interfaces": "^1.3.7" }, "dependencies": { "analytics-node": "^3.4.0-beta.1", "axios": "^0.19.2", - "body-parser": "^1.19.0", - "express": "^4.17.1", "medusa-core-utils": "^1.2.0" }, "gitHead": "cd1f5afa5aa8c0b15ea957008ee19f1d695cbd2e", diff --git a/packages/medusa-plugin-segment/src/services/segment.js b/packages/medusa-plugin-segment/src/services/segment.js index caaae460e4..9fa275de4f 100644 --- a/packages/medusa-plugin-segment/src/services/segment.js +++ b/packages/medusa-plugin-segment/src/services/segment.js @@ -78,10 +78,10 @@ class SegmentService extends BaseService { order_id: order.id, email: order.email, region_id: order.region_id, - payment_provider: order.payments.map((p) => p.provider_id).join(","), + payment_provider: order.payments?.map((p) => p.provider_id).join(","), shipping_methods: order.shipping_methods, - shipping_country: order.shipping_address.country_code, - shipping_city: order.shipping_address.city, + shipping_country: order.shipping_address?.country_code, + shipping_city: order.shipping_address?.city, reporting_total: await this.getReportingValue(order.currency_code, total), reporting_subtotal: await this.getReportingValue( order.currency_code, diff --git a/packages/medusa-plugin-segment/src/subscribers/claim-created.ts b/packages/medusa-plugin-segment/src/subscribers/claim-created.ts new file mode 100644 index 0000000000..01101ffc06 --- /dev/null +++ b/packages/medusa-plugin-segment/src/subscribers/claim-created.ts @@ -0,0 +1,46 @@ +export default async function handler({ data: { id }, container }) { + const segmentService = container.resolve("segmentService") + const claimService = container.resolve("claimService") + + const claim = await claimService.retrieve(id, { + relations: [ + "order", + "claim_items", + "claim_items.item", + "claim_items.tags", + "claim_items.variant", + ], + }) + + for (const ci of claim.claim_items) { + const price = ci.item.unit_price / 100 + const reporting_price = await segmentService.getReportingValue( + claim.order.currency_code, + price + ) + const event = { + event: "Item Claimed", + userId: claim.order.customer_id, + timestamp: claim.created_at, + properties: { + price, + reporting_price, + order_id: claim.order_id, + claim_id: claim.id, + claim_item_id: ci.id, + type: claim.type, + quantity: ci.quantity, + variant: ci.variant.sku, + product_id: ci.variant.product_id, + reason: ci.reason, + note: ci.note, + tags: ci.tags.map((t) => ({ id: t.id, value: t.value })), + }, + } + await segmentService.track(event) + } +} + +export const config = { + event: "claim.created", +} diff --git a/packages/medusa-plugin-segment/src/subscribers/order-canceled.ts b/packages/medusa-plugin-segment/src/subscribers/order-canceled.ts new file mode 100644 index 0000000000..daaa233351 --- /dev/null +++ b/packages/medusa-plugin-segment/src/subscribers/order-canceled.ts @@ -0,0 +1,21 @@ +export default async function handler({ data: { id }, container }) { + const orderService = container.resolve("orderService") + const segmentService = container.resolve("segmentService") + + const order = await orderService.retrieveWithTotals(id) + + const date = new Date() + const orderData = await segmentService.buildOrder(order) + const orderEvent = { + event: "Order Cancelled", + userId: order.customer_id, + properties: orderData, + timestamp: date, + } + + segmentService.track(orderEvent) +} + +export const config = { + event: "order.canceled", +} diff --git a/packages/medusa-plugin-segment/src/subscribers/order-items-returned.ts b/packages/medusa-plugin-segment/src/subscribers/order-items-returned.ts new file mode 100644 index 0000000000..07012730ea --- /dev/null +++ b/packages/medusa-plugin-segment/src/subscribers/order-items-returned.ts @@ -0,0 +1,82 @@ +export default async function handler({ data: { id, return_id }, container }) { + const orderService = container.resolve("orderService") + const returnService = container.resolve("returnService") + const segmentService = container.resolve("segmentService") + + const order = await orderService.retrieveWithTotals(id, { + relations: [ + "customer", + "billing_address", + "shipping_address", + "discounts", + "discounts.rule", + "shipping_methods", + "shipping_methods.shipping_option", + "payments", + "fulfillments", + "returns", + "items", + "gift_cards", + "gift_card_transactions", + "swaps", + "swaps.return_order", + "swaps.payment", + "swaps.shipping_methods", + "swaps.shipping_methods.shipping_option", + "swaps.shipping_address", + "swaps.additional_items", + "swaps.fulfillments", + ], + }) + + const ret = await returnService.retrieve(return_id, { + relations: ["items", "items.reason"], + }) + + const shipping: object[] = [] + if (ret.shipping_method && ret.shipping_method.price) { + shipping.push({ + ...ret.shipping_method, + price: -1 * (ret.shipping_method.price / 100), + }) + } + + let merged = [...order.items] + + // merge items from order with items from order swaps + if (order.swaps && order.swaps.length) { + for (const s of order.swaps) { + merged = [...merged, ...s.additional_items] + } + } + + const toBuildFrom = { + ...order, + shipping_methods: shipping, + items: ret.items.map((i) => { + const li = merged.find((l) => l.id === i.item_id) + if (i.reason) { + li.reason = i.reason + } + + if (i.note) { + li.note = i.note + } + return li + }), + } + + const orderData = await segmentService.buildOrder(toBuildFrom) + const orderEvent = { + event: "Order Refunded", + userId: order.customer_id, + properties: orderData, + timestamp: new Date(), + } + + segmentService.track(orderEvent) +} + +export const config = { + event: "order.items-returned", +} diff --git a/packages/medusa-plugin-segment/src/subscribers/order-placed.ts b/packages/medusa-plugin-segment/src/subscribers/order-placed.ts new file mode 100644 index 0000000000..044d4637ed --- /dev/null +++ b/packages/medusa-plugin-segment/src/subscribers/order-placed.ts @@ -0,0 +1,87 @@ +export default async function handler({ data: { id }, container }) { + const orderService = container.resolve("orderService") + const cartService = container.resolve("cartService") + const segmentService = container.resolve("segmentService") + const order = await orderService.retrieveWithTotals(id, { + relations: [ + "customer", + "billing_address", + "shipping_address", + "discounts", + "discounts.rule", + "shipping_methods", + "shipping_methods.shipping_option", + "payments", + "fulfillments", + "items", + "returns", + "gift_cards", + "gift_card_transactions", + "swaps", + "swaps.return_order", + "swaps.payment", + "swaps.shipping_methods", + "swaps.shipping_methods.shipping_option", + "swaps.shipping_address", + "swaps.additional_items", + "swaps.fulfillments", + ], + }) + + const eventContext: Record = {} + const integrations: Record = {} + + if (order.cart_id) { + try { + const cart = await cartService.retrieve(order.cart_id, { + select: ["context"], + }) + + if (cart.context) { + if (cart.context.ip) { + eventContext.ip = cart.context.ip + } + + if (cart.context.user_agent) { + eventContext.user_agent = cart.context.user_agent + } + + if (segmentService.options_ && segmentService.options_.use_ga_id) { + if (cart.context.ga_id) { + integrations["Google Analytics"] = { + clientId: cart.context.ga_id, + } + } + } + } + } catch (err) { + console.log(err) + console.warn("Failed to gather context for order") + } + } + + const orderData = await segmentService.buildOrder(order) + const orderEvent = { + event: "Order Completed", + userId: order.customer_id, + properties: orderData, + timestamp: order.created_at, + context: eventContext, + integrations, + } + + segmentService.identify({ + userId: order.customer_id, + traits: { + email: order.email, + firstName: order.shipping_address.first_name, + lastName: order.shipping_address.last_name, + }, + }) + + segmentService.track(orderEvent) +} + +export const config = { + event: "order.placed", +} diff --git a/packages/medusa-plugin-segment/src/subscribers/order-shipment-created.ts b/packages/medusa-plugin-segment/src/subscribers/order-shipment-created.ts new file mode 100644 index 0000000000..1428acdff4 --- /dev/null +++ b/packages/medusa-plugin-segment/src/subscribers/order-shipment-created.ts @@ -0,0 +1,60 @@ +export default async function handler({ + data: { id, fulfillment_id }, + container, +}) { + const orderService = container.resolve("orderService") + const fulfillmentService = container.resolve("fulfillmentService") + const segmentService = container.resolve("segmentService") + + const order = await orderService.retrieveWithTotals(id, { + relations: [ + "customer", + "billing_address", + "shipping_address", + "discounts", + "discounts.rule", + "shipping_methods", + "shipping_methods.shipping_option", + "payments", + "fulfillments", + "returns", + "items", + "gift_cards", + "gift_card_transactions", + "swaps", + "swaps.return_order", + "swaps.payment", + "swaps.shipping_methods", + "swaps.shipping_methods.shipping_option", + "swaps.shipping_address", + "swaps.additional_items", + "swaps.fulfillments", + ], + }) + + const fulfillment = await fulfillmentService.retrieve(fulfillment_id, { + relations: ["items"], + }) + + const toBuildFrom = { + ...order, + provider_id: fulfillment.provider, + items: fulfillment.items.map((i) => + order.items.find((l) => l.id === i.item_id) + ), + } + + const orderData = await segmentService.buildOrder(toBuildFrom) + const orderEvent = { + event: "Order Shipped", + userId: order.customer_id, + properties: orderData, + timestamp: fulfillment.shipped_at, + } + + segmentService.track(orderEvent) +} + +export const config = { + event: "order.shipment_created", +} diff --git a/packages/medusa-plugin-segment/src/subscribers/order.js b/packages/medusa-plugin-segment/src/subscribers/order.js deleted file mode 100644 index 3c13b334a5..0000000000 --- a/packages/medusa-plugin-segment/src/subscribers/order.js +++ /dev/null @@ -1,338 +0,0 @@ -class OrderSubscriber { - constructor({ - segmentService, - eventBusService, - orderService, - cartService, - claimService, - returnService, - fulfillmentService, - }) { - this.orderService_ = orderService - - this.cartService_ = cartService - - this.returnService_ = returnService - - this.claimService_ = claimService - - this.fulfillmentService_ = fulfillmentService - - // Swaps - // swap.created - // swap.received - // swap.shipment_created - // swap.payment_completed - // swap.payment_captured - // swap.refund_processed - - eventBusService.subscribe( - "order.shipment_created", - async ({ id, fulfillment_id }) => { - const order = await this.orderService_.retrieve(id, { - select: [ - "shipping_total", - "discount_total", - "tax_total", - "refunded_total", - "gift_card_total", - "subtotal", - "total", - ], - relations: [ - "customer", - "billing_address", - "shipping_address", - "discounts", - "discounts.rule", - "shipping_methods", - "shipping_methods.shipping_option", - "payments", - "fulfillments", - "returns", - "items", - "gift_cards", - "gift_card_transactions", - "swaps", - "swaps.return_order", - "swaps.payment", - "swaps.shipping_methods", - "swaps.shipping_methods.shipping_option", - "swaps.shipping_address", - "swaps.additional_items", - "swaps.fulfillments", - ], - }) - - const fulfillment = await this.fulfillmentService_.retrieve( - fulfillment_id, - { - relations: ["items"], - } - ) - - const toBuildFrom = { - ...order, - provider_id: fulfillment.provider, - items: fulfillment.items.map((i) => - order.items.find((l) => l.id === i.item_id) - ), - } - - const orderData = await segmentService.buildOrder(toBuildFrom) - const orderEvent = { - event: "Order Shipped", - userId: order.customer_id, - properties: orderData, - timestamp: fulfillment.shipped_at, - } - - segmentService.track(orderEvent) - } - ) - - eventBusService.subscribe("claim.created", async ({ id }) => { - const claim = await this.claimService_.retrieve(id, { - relations: [ - "order", - "claim_items", - "claim_items.item", - "claim_items.tags", - "claim_items.variant", - ], - }) - - for (const ci of claim.claim_items) { - const price = ci.item.unit_price / 100 - const reporting_price = await segmentService.getReportingValue( - claim.order.currency_code, - price - ) - const event = { - event: "Item Claimed", - userId: claim.order.customer_id, - timestamp: claim.created_at, - properties: { - price, - reporting_price, - order_id: claim.order_id, - claim_id: claim.id, - claim_item_id: ci.id, - type: claim.type, - quantity: ci.quantity, - variant: ci.variant.sku, - product_id: ci.variant.product_id, - reason: ci.reason, - note: ci.note, - tags: ci.tags.map((t) => ({ id: t.id, value: t.value })), - }, - } - await segmentService.track(event) - } - }) - - eventBusService.subscribe( - "order.items_returned", - async ({ id, return_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", - "discounts.rule", - "shipping_methods", - "shipping_methods.shipping_option", - "payments", - "fulfillments", - "returns", - "items", - "gift_cards", - "gift_card_transactions", - "swaps", - "swaps.return_order", - "swaps.payment", - "swaps.shipping_methods", - "swaps.shipping_methods.shipping_option", - "swaps.shipping_address", - "swaps.additional_items", - "swaps.fulfillments", - ], - }) - - const ret = await this.returnService_.retrieve(return_id, { - relations: ["items", "items.reason"], - }) - - const shipping = [] - if (ret.shipping_method && ret.shipping_method.price) { - shipping.push({ - ...ret.shipping_method, - price: -1 * (ret.shipping_method.price / 100), - }) - } - - let merged = [...order.items] - - // merge items from order with items from order swaps - if (order.swaps && order.swaps.length) { - for (const s of order.swaps) { - merged = [...merged, ...s.additional_items] - } - } - - const toBuildFrom = { - ...order, - shipping_methods: shipping, - items: ret.items.map((i) => { - const li = merged.find((l) => l.id === i.item_id) - if (i.reason) { - li.reason = i.reason - } - - if (i.note) { - li.note = i.note - } - return li - }), - } - - const orderData = await segmentService.buildOrder(toBuildFrom) - const orderEvent = { - event: "Order Refunded", - userId: order.customer_id, - properties: orderData, - timestamp: new Date(), - } - - segmentService.track(orderEvent) - } - ) - - eventBusService.subscribe("order.canceled", async ({ id }) => { - const order = await this.orderService_.retrieve(id, { - select: [ - "shipping_total", - "discount_total", - "tax_total", - "refunded_total", - "gift_card_total", - "subtotal", - "total", - ], - }) - - const date = new Date() - const orderData = await segmentService.buildOrder(order) - const orderEvent = { - event: "Order Cancelled", - userId: order.customer_id, - properties: orderData, - timestamp: date, - } - - segmentService.track(orderEvent) - }) - - eventBusService.subscribe("order.placed", async ({ 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", - "discounts.rule", - "shipping_methods", - "shipping_methods.shipping_option", - "payments", - "fulfillments", - "items", - "returns", - "gift_cards", - "gift_card_transactions", - "swaps", - "swaps.return_order", - "swaps.payment", - "swaps.shipping_methods", - "swaps.shipping_methods.shipping_option", - "swaps.shipping_address", - "swaps.additional_items", - "swaps.fulfillments", - ], - }) - - const eventContext = {} - const integrations = {} - - if (order.cart_id) { - try { - const cart = await this.cartService_.retrieve(order.cart_id, { - select: ["context"], - }) - - if (cart.context) { - if (cart.context.ip) { - eventContext.ip = cart.context.ip - } - - if (cart.context.user_agent) { - eventContext.user_agent = cart.context.user_agent - } - - if (segmentService.options_ && segmentService.options_.use_ga_id) { - if (cart.context.ga_id) { - integrations["Google Analytics"] = { - clientId: cart.context.ga_id, - } - } - } - } - } catch (err) { - console.log(err) - console.warn("Failed to gather context for order") - } - } - - const orderData = await segmentService.buildOrder(order) - const orderEvent = { - event: "Order Completed", - userId: order.customer_id, - properties: orderData, - timestamp: order.created_at, - context: eventContext, - integrations, - } - - segmentService.identify({ - userId: order.customer_id, - traits: { - email: order.email, - firstName: order.shipping_address.first_name, - lastName: order.shipping_address.last_name, - }, - }) - - segmentService.track(orderEvent) - }) - } -} - -export default OrderSubscriber diff --git a/packages/medusa-plugin-segment/src/subscribers/swap-created.ts b/packages/medusa-plugin-segment/src/subscribers/swap-created.ts new file mode 100644 index 0000000000..ca2be09343 --- /dev/null +++ b/packages/medusa-plugin-segment/src/subscribers/swap-created.ts @@ -0,0 +1,24 @@ +import { gatherSwapReport } from "../utils/gather-swap-report" + +export default async function handler({ data: { id }, container }) { + const swapService = container.resolve("swapService") + const segmentService = container.resolve("segmentService") + const lineItemService = container.resolve("lineItemService") + + const [swap, swapReport] = await gatherSwapReport(id, { + swapService, + segmentService, + lineItemService, + }) + + return await segmentService.track({ + event: "Swap Created", + userId: swap.order.customer_id, + timestamp: swap.created_at, + properties: swapReport, + }) +} + +export const config = { + event: "swap.created", +} diff --git a/packages/medusa-plugin-segment/src/subscribers/swap-payment-completed.ts b/packages/medusa-plugin-segment/src/subscribers/swap-payment-completed.ts new file mode 100644 index 0000000000..c06ddecfa6 --- /dev/null +++ b/packages/medusa-plugin-segment/src/subscribers/swap-payment-completed.ts @@ -0,0 +1,36 @@ +import { humanizeAmount } from "medusa-core-utils" +import { gatherSwapReport } from "../utils/gather-swap-report" + +export default async function handler({ data: { id }, container }) { + const swapService = container.resolve("swapService") + const segmentService = container.resolve("segmentService") + const lineItemService = container.resolve("lineItemService") + + const [swap, swapReport] = await gatherSwapReport(id, { + swapService, + segmentService, + lineItemService, + }) + + const currency = swapReport.currency + const total = humanizeAmount(swap.difference_due, currency) + const reporting_total = await segmentService.getReportingValue( + currency, + total + ) + + return await segmentService.track({ + event: "Swap Confirmed", + userId: swap.order.customer_id, + timestamp: swap.confirmed_at, + properties: { + reporting_total, + total, + ...swapReport, + }, + }) +} + +export const config = { + event: "swap.payment_completed", +} diff --git a/packages/medusa-plugin-segment/src/subscribers/swap-shipment-created.ts b/packages/medusa-plugin-segment/src/subscribers/swap-shipment-created.ts new file mode 100644 index 0000000000..06b2a6957a --- /dev/null +++ b/packages/medusa-plugin-segment/src/subscribers/swap-shipment-created.ts @@ -0,0 +1,41 @@ +import { humanizeAmount } from "medusa-core-utils" +import { gatherSwapReport } from "../utils/gather-swap-report" + +export default async function handler({ + data: { id, fulfillment_id }, + container, +}) { + const segmentService = container.resolve("segmentService") + const fulfillmentService = container.resolve("fulfillmentService") + const swapService = container.resolve("swapService") + const lineItemService = container.resolve("lineItemService") + + const [swap, swapReport] = await gatherSwapReport(id, { + swapService, + segmentService, + lineItemService, + }) + const fulfillment = await fulfillmentService.retrieve(fulfillment_id) + + const currency = swapReport.currency + const total = humanizeAmount(swap.difference_due, currency) + const reporting_total = await segmentService.getReportingValue( + currency, + total + ) + + return await segmentService.track({ + event: "Swap Shipped", + userId: swap.order.customer_id, + timestamp: fulfillment.shipped_at, + properties: { + reporting_total, + total, + ...swapReport, + }, + }) +} + +export const config = { + event: "swap.shipment_created", +} diff --git a/packages/medusa-plugin-segment/src/subscribers/swap.js b/packages/medusa-plugin-segment/src/subscribers/swap.js deleted file mode 100644 index 30c83d0b91..0000000000 --- a/packages/medusa-plugin-segment/src/subscribers/swap.js +++ /dev/null @@ -1,149 +0,0 @@ -import { humanizeAmount } from "medusa-core-utils" - -class OrderSubscriber { - constructor({ - segmentService, - eventBusService, - swapService, - lineItemService, - fulfillmentService, - }) { - this.fulfillmentService_ = fulfillmentService - - this.lineItemService_ = lineItemService - - this.swapService_ = swapService - - this.segmentService_ = segmentService - - eventBusService.subscribe( - "swap.shipment_created", - async ({ id, fulfillment_id }) => { - const [swap, swapReport] = await this.gatherSwapReport(id) - const fulfillment = await this.fulfillmentService_.retrieve( - fulfillment_id - ) - - const currency = swapReport.currency - const total = humanizeAmount(swap.difference_due, currency) - const reporting_total = await this.segmentService_.getReportingValue( - currency, - total - ) - - return await segmentService.track({ - event: "Swap Shipped", - userId: swap.order.customer_id, - timestamp: fulfillment.shipped_at, - properties: { - reporting_total, - total, - ...swapReport, - }, - }) - } - ) - - eventBusService.subscribe("swap.payment_completed", async ({ id }) => { - const [swap, swapReport] = await this.gatherSwapReport(id) - - const currency = swapReport.currency - const total = humanizeAmount(swap.difference_due, currency) - const reporting_total = await this.segmentService_.getReportingValue( - currency, - total - ) - - return await segmentService.track({ - event: "Swap Confirmed", - userId: swap.order.customer_id, - timestamp: swap.confirmed_at, - properties: { - reporting_total, - total, - ...swapReport, - }, - }) - }) - - eventBusService.subscribe("swap.created", async ({ id }) => { - const [swap, swapReport] = await this.gatherSwapReport(id) - - return await segmentService.track({ - event: "Swap Created", - userId: swap.order.customer_id, - timestamp: swap.created_at, - properties: swapReport, - }) - }) - } - - async gatherSwapReport(id) { - const swap = await this.swapService_.retrieve(id, { - relations: [ - "order", - "additional_items", - "additional_items.variant", - "return_order", - "return_order.items", - "return_order.shipping_method", - ], - }) - - const currency = swap.order.currency_code - - const newItems = await Promise.all( - swap.additional_items.map(async (i) => { - const price = humanizeAmount(i.unit_price, currency) - const reporting_price = await this.segmentService_.getReportingValue( - currency, - price - ) - - return { - name: i.title, - product_id: i.variant.product_id, - variant: i.variant.sku, - quantity: i.quantity, - price, - reporting_price, - } - }) - ) - - const returnItems = await Promise.all( - swap.return_order.items.map(async (ri) => { - const i = await this.lineItemService_.retrieve(ri.item_id, { - relations: ["variant"], - }) - const price = humanizeAmount(i.unit_price, currency) - const reporting_price = await this.segmentService_.getReportingValue( - currency, - price - ) - - return { - name: i.title, - product_id: i.variant.product_id, - variant: i.variant.sku, - quantity: ri.quantity, - price, - reporting_price, - } - }) - ) - - return [ - swap, - { - swap_id: swap.id, - order_id: swap.order_id, - new_items: newItems, - return_items: returnItems, - currency, - }, - ] - } -} - -export default OrderSubscriber diff --git a/packages/medusa-plugin-segment/src/utils/gather-swap-report.ts b/packages/medusa-plugin-segment/src/utils/gather-swap-report.ts new file mode 100644 index 0000000000..3055b8e33a --- /dev/null +++ b/packages/medusa-plugin-segment/src/utils/gather-swap-report.ts @@ -0,0 +1,71 @@ +import { humanizeAmount } from "medusa-core-utils" + +export async function gatherSwapReport( + id, + { swapService, segmentService, lineItemService } +) { + const swap = await swapService.retrieve(id, { + relations: [ + "order", + "additional_items", + "additional_items.variant", + "return_order", + "return_order.items", + "return_order.shipping_method", + ], + }) + + const currency = swap.order.currency_code + + const newItems = await Promise.all( + swap.additional_items.map(async (i) => { + const price = humanizeAmount(i.unit_price, currency) + const reporting_price = await segmentService.getReportingValue( + currency, + price + ) + + return { + name: i.title, + product_id: i.variant.product_id, + variant: i.variant.sku, + quantity: i.quantity, + price, + reporting_price, + } + }) + ) + + const returnItems = await Promise.all( + swap.return_order.items.map(async (ri) => { + const i = await lineItemService.retrieve(ri.item_id, { + relations: ["variant"], + }) + const price = humanizeAmount(i.unit_price, currency) + const reporting_price = await segmentService.getReportingValue( + currency, + price + ) + + return { + name: i.title, + product_id: i.variant.product_id, + variant: i.variant.sku, + quantity: ri.quantity, + price, + reporting_price, + } + }) + ) + + return [ + swap, + { + swap_id: swap.id, + order_id: swap.order_id, + new_items: newItems, + return_items: returnItems, + currency, + }, + ] +} diff --git a/packages/medusa-plugin-segment/tsconfig.admin.json b/packages/medusa-plugin-segment/tsconfig.admin.json new file mode 100644 index 0000000000..b109ee6f17 --- /dev/null +++ b/packages/medusa-plugin-segment/tsconfig.admin.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "esnext" + }, + "include": ["src/admin"], + "exclude": ["**/*.spec.js"] +} diff --git a/packages/medusa-plugin-segment/tsconfig.json b/packages/medusa-plugin-segment/tsconfig.json new file mode 100644 index 0000000000..869a718e13 --- /dev/null +++ b/packages/medusa-plugin-segment/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "lib": [ + "es5", + "es6", + "es2019" + ], + "target": "es5", + "jsx": "react-jsx" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, + "outDir": "./dist", + "esModuleInterop": true, + "declaration": true, + "module": "commonjs", + "moduleResolution": "node", + "emitDecoratorMetadata": true, + "experimentalDecorators": true, + "noImplicitReturns": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "noImplicitThis": true, + "allowJs": true, + "skipLibCheck": true, + "downlevelIteration": true // to use ES5 specific tooling + }, + "include": ["src"], + "exclude": [ + "dist", + "build", + "src/**/__tests__", + "src/**/__mocks__", + "src/**/__fixtures__", + "node_modules", + ".eslintrc.js" + ] +} diff --git a/packages/medusa-plugin-segment/tsconfig.server.json b/packages/medusa-plugin-segment/tsconfig.server.json new file mode 100644 index 0000000000..0a84ca8c0c --- /dev/null +++ b/packages/medusa-plugin-segment/tsconfig.server.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "inlineSourceMap": true /* Emit a single file with source maps instead of having a separate file. */ + }, + "exclude": ["src/admin", "**/*.spec.js"] +} diff --git a/packages/medusa-plugin-segment/tsconfig.spec.json b/packages/medusa-plugin-segment/tsconfig.spec.json new file mode 100644 index 0000000000..9b62409191 --- /dev/null +++ b/packages/medusa-plugin-segment/tsconfig.spec.json @@ -0,0 +1,5 @@ +{ + "extends": "./tsconfig.json", + "include": ["src"], + "exclude": ["node_modules"] +} diff --git a/yarn.lock b/yarn.lock index e94514da4a..a4ac711383 100644 --- a/yarn.lock +++ b/yarn.lock @@ -37380,27 +37380,18 @@ __metadata: version: 0.0.0-use.local resolution: "medusa-plugin-segment@workspace:packages/medusa-plugin-segment" dependencies: - "@babel/cli": ^7.7.5 - "@babel/core": ^7.7.5 - "@babel/node": ^7.7.4 - "@babel/plugin-proposal-class-properties": ^7.7.4 - "@babel/plugin-transform-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 analytics-node: ^3.4.0-beta.1 axios: ^0.19.2 - body-parser: ^1.19.0 cross-env: ^5.2.1 eslint: ^6.8.0 - express: ^4.17.1 jest: ^25.5.4 medusa-core-utils: ^1.2.0 medusa-interfaces: ^1.3.7 medusa-test-utils: ^1.1.40 + rimraf: ^5.0.1 + typescript: ^4.9.5 peerDependencies: + "@medusajs/medusa": ">= 1.18.0 < 2" medusa-interfaces: ^1.3.7 languageName: unknown linkType: soft