-
Notifications
You must be signed in to change notification settings - Fork 5.5k
17440 lightspeed #18396
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
17440 lightspeed #18396
Conversation
…nt sources - Added actions for finding customers, invoices, orders, products, and shipments. - Introduced event sources for customer creation, updates, invoice updates, order creation, updates, and shipping events. - Updated app properties to include customer and order identifiers for better data retrieval. - Version bump to 0.1.0 and added dependencies for improved functionality.
The latest updates on your projects. Learn more about Vercel for GitHub. 2 Skipped Deployments
|
WalkthroughAdds a Lightspeed ECom C-Series integration: expands the app with HTTP helpers, endpoint wrappers, and a paginate generator; adds many actions (find/get) using pagination; introduces a webhook-based common source and multiple instant event sources with test fixtures; updates package version and dependencies. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Action as Find Action
participant App as Lightspeed App
participant API as Lightspeed ECom API
User->>Action: Invoke action with filters
Action->>App: paginate(fn=listX, params, dataField)
loop For each page
App->>API: GET /{resource}.json?query
API-->>App: { dataField: [items], pagination }
App-->>Action: yield items
end
Action-->>User: return accumulated items + $summary
sequenceDiagram
autonumber
participant Src as Source (common base)
participant App as Lightspeed App
participant API as Lightspeed ECom API
participant Hook as HTTP endpoint
participant PD as Emitter
rect rgba(220,235,255,0.25)
note over Src,App: Activation
Src->>App: createHook({ itemGroup, itemAction, address })
App->>API: POST /hooks.json
API-->>App: { id, ... }
App-->>Src: webhook id
Src->>Src: store webhookId in DB
end
rect rgba(220,255,220,0.25)
note over API,Hook: Event delivery
API-->>Hook: POST webhook body
Hook->>Src: run({ body })
Src->>Hook: http.respond(200)
Src->>PD: emit(body, meta)
end
rect rgba(255,235,200,0.25)
note over Src,App: Deactivation
Src->>Src: load webhookId from DB
Src->>App: deleteHook(webhookId)
App->>API: DELETE /hooks/{id}.json
API-->>App: 204
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
🧹 Nitpick comments (43)
components/lightspeed_ecom_c_series/sources/product-deleted/test-event.mjs (1)
1-1
: Empty sample payload — please provide a minimal, realistic shape or add rationaleAn empty object makes downstream examples / UI previews unhelpful. If Lightspeed sends no body on delete, add a comment stating that. Otherwise, include the minimal fields (e.g., product id).
Proposed minimal fixture (verify against actual webhook shape before committing):
-export default {} +export default { + product: { + id: 123 + } +}components/lightspeed_ecom_c_series/sources/invoice-updated/test-event.mjs (1)
1-52
: Prefer HTTPS in sample linksAll “link” values use http://. Use https:// to avoid shipping insecure URLs in examples and quiet security scanners. Apply this across all test-event fixtures.
Example (repeat for all “link” fields in this file):
- "link":"http://api.shoplightspeed.com/en/orders/123.json" + "link":"https://api.shoplightspeed.com/en/orders/123.json"components/lightspeed_ecom_c_series/sources/order-updated/test-event.mjs (2)
10-11
: Use reserved placeholders for IP/emailSwap the real-looking IP and non-reserved email domain for doc-safe placeholders.
- "remoteIp":"35.245.159.47", + "remoteIp":"203.0.113.10", ... - "email":"customer@email.com", + "email":"customer@example.com",Also applies to: 22-22
7-7
: Order vs. shipment status consistencyOrder status is "completed_shipped" while shipmentStatus is "not_shipped". If intentional for edge-case coverage, add a brief comment; otherwise, align them.
Also applies to: 93-94
components/lightspeed_ecom_c_series/sources/customer-updated/test-event.mjs (2)
15-15
: Use reserved example email domainReplace with example.com to avoid referencing a real domain.
- "email":"customer@email.com", + "email":"customer@example.com",
59-63
: HTTPS links in sample dataStandardize “link” fields to https://.
Example:
- "link":"http:\/\/api.shoplightspeed.com\/en\/groups\/customers.json?customer=123" + "link":"https:\/\/api.shoplightspeed.com\/en\/groups\/customers.json?customer=123"Also applies to: 66-70, 101-106, 109-113
components/lightspeed_ecom_c_series/sources/new-product-created/test-event.mjs (1)
19-23
: Switch test links to HTTPSSame HTTPS concern as other fixtures.
Representative change:
- "link":"http:\/\/api.shoplightspeed.com\/en\/brands\/123.json" + "link":"https:\/\/api.shoplightspeed.com\/en\/brands\/123.json"Also applies to: 26-30, 36-40, 43-47, 50-54, 58-62, 66-70, 73-77, 80-84
components/lightspeed_ecom_c_series/sources/new-order-created/test-event.mjs (2)
10-10
: Use reserved placeholders for IP/email- "remoteIp":"35.245.159.47", + "remoteIp":"203.0.113.11", ... - "email":"customer@email.com", + "email":"customer@example.com",Also applies to: 22-22
615-619
: HTTPS for linksUpdate sample links to https:// across this file.
- "link":"http:\/\/api.shoplightspeed.com\/en\/invoices\/123.json" + "link":"https:\/\/api.shoplightspeed.com\/en\/invoices\/123.json"Also applies to: 640-643, 678-681
components/lightspeed_ecom_c_series/sources/new-customer-created/test-event.mjs (2)
15-15
: Use reserved example email domain- "email":"customer@email.com", + "email":"customer@example.com",
59-63
: HTTPS links in sample dataSame as other fixtures.
- "link":"http:\/\/api.shoplightspeed.com\/en\/groups\/customers.json?customer=123" + "link":"https:\/\/api.shoplightspeed.com\/en\/groups\/customers.json?customer=123"Also applies to: 66-70, 101-106, 109-113
components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs (11)
145-149
: Off‑by‑one in brands options pagination.Other option builders use
page + 1
(Lightspeed pagination is 1‑based). Here it passespage
directly, likely skipping page 1.- params: { - page, - }, + params: { + page: page + 1, + },
204-211
: Base URL may be incorrect for common cluster values (eu1/us1).Official guidance maps clusters to fixed API hosts (eu1 → api.webshopapp.com, us1 → api.shoplightspeed.com). Building
https://api.${cluster}.com
only works ifcluster
is alreadywebshopapp
orshoplightspeed
. Add mapping or validate input.References: elastic.io Lightspeed eCom component docs show the mapping. (docs.elastic.io)
Suggested adjustment:
- _baseUrl() { - return `https://api.${this.$auth.cluster}.com/${this.$auth.lang}`; - }, + _baseUrl() { + const c = (this.$auth.cluster || "").toLowerCase(); + const host = + c === "eu1" || c === "eu" ? "api.webshopapp.com" : + c === "us1" || c === "us" || c === "noam" ? "api.shoplightspeed.com" : + c.includes(".") ? c : `api.${c}.com`; + return `https://${host}/${this.$auth.lang}`; + },If you intend
cluster
to be the host fragment (e.g.,webshopapp
/shoplightspeed
), please document that in the auth UI.
18-23
: Label building: guard against missing names.Customers without
firstname
/lastname
render labels with extra spaces. Minor, but easy to harden.- label: `${firstname} ${middlename && `${middlename} ` || ""}${lastname} ${email && `(${email})` || ""}`, + label: `${[firstname, middlename, lastname].filter(Boolean).join(" ")}${email ? ` (${email})` : ""}`,
37-42
: Same label hardening suggestion for customer email options.- label: `${firstname} ${middlename && `${middlename} ` || ""}${lastname} ${value && `(${value})` || ""}`, + label: `${[firstname, middlename, lastname].filter(Boolean).join(" ")}${value ? ` (${value})` : ""}`,
56-61
: Order options: stabilize when email missing.Minor parity with the customer label fix.
- label: `Order #${number} ${email && `(${email})`}`, + label: `Order #${number}${email ? ` (${email})` : ""}`,
75-81
: Order ID options: same label nit.- label: `Order #${number} ${email && `(${email})`}`, + label: `Order #${number}${email ? ` (${email})` : ""}`,
94-99
: Shipment number label nit.- label: `Shipment #${number} ${status && `(${status})`}`, + label: `Shipment #${number}${status ? ` (${status})` : ""}`,
113-118
: Shipment ID label nit.- label: `Shipment #${number} ${status && `(${status})`}`, + label: `Shipment #${number}${status ? ` (${status})` : ""}`,
132-137
: Invoice number label nit.- label: `Invoice #${number} ${status && `(${status})`}`, + label: `Invoice #${number}${status ? ` (${status})` : ""}`,
178-201
: Date prop descriptions reference “products” only, but these props are reused for orders, invoices, shipments.Make the copy generic to avoid confusion in other actions.
- description: "Show products created after date. **Format: `YYYY-MM-DD HH:MM:SS`**", + description: "Show records created after date. **Format: `YYYY-MM-DD HH:MM:SS`**", @@ - description: "Show products created before date. **Format: `YYYY-MM-DD HH:MM:SS`**", + description: "Show records created before date. **Format: `YYYY-MM-DD HH:MM:SS`**", @@ - description: "Show products last updated after date. **Format: `YYYY-MM-DD HH:MM:SS`**", + description: "Show records last updated after date. **Format: `YYYY-MM-DD HH:MM:SS`**", @@ - description: "Show products last updated before date. **Format: `YYYY-MM-DD HH:MM:SS`**", + description: "Show records last updated before date. **Format: `YYYY-MM-DD HH:MM:SS`**",
10-11
: Minor copy nits.
- “customerid” → “customer ID”
- “Retrieve all customers from a specific customer…” → “Retrieve customers by email.”
- description: "Retrieve all orders from a specific customer based on the customerid", + description: "Retrieve all orders from a specific customer based on the customer ID", @@ - description: "Retrieve all customers from a specific customer based on the customer email", + description: "Retrieve customers by email",Also applies to: 29-30
components/lightspeed_ecom_c_series/actions/get-order-products/get-order-products.mjs (1)
6-6
: Tweak description grammar and link text.“Get an order products by ID” → “Get order products for an order ID.”
- description: "Get an order products by ID. [See the documentation](https://developers.lightspeedhq.com/ecom/endpoints/orderproduct/#get-all-order-products)", + description: "Get order products for an order ID. [See the documentation](https://developers.lightspeedhq.com/ecom/endpoints/orderproduct/#get-all-order-products)",components/lightspeed_ecom_c_series/actions/find-order/find-order.mjs (2)
5-7
: Description doesn’t match behavior (filters vs “by ID”).This action searches with multiple filters, not strictly “by ID”. Reword to avoid confusion.
- description: "Find an order by ID. [See the documentation](https://developers.lightspeedhq.com/ecom/endpoints/order/#get-all-orders)", + description: "Find orders by filters. [See the documentation](https://developers.lightspeedhq.com/ecom/endpoints/order/#get-all-orders)",
62-76
: Optional: add a max results cap to avoid unintentionally paging the entire dataset.Expose
limit
(mapped topage_size
) and/ormaxResults
passed topaginate
.- const response = this.app.paginate({ + const response = this.app.paginate({ fn: this.app.listOrder, $, params: { customer: this.customerId, number: this.orderNumber, since_id: this.sinceId, created_at_min: this.createdAtMin, created_at_max: this.createdAtMax, updated_at_min: this.updatedAtMin, updated_at_max: this.updatedAtMax, }, dataField: "orders", + // maxResults: 500, // uncomment to cap results });components/lightspeed_ecom_c_series/actions/find-shipment/find-shipment.mjs (3)
5-7
: Description doesn’t match behaviorThis action lists/filters shipments; it’s not “by ID.” Align name/description with actual filters.
- name: "Find Shipment", - description: "Find a shipment by ID. [See the documentation](https://developers.lightspeedhq.com/ecom/endpoints/shipment/#get-all-shipments)", + name: "Find Shipments", + description: "Find shipments using filters (customer, order, number, date range). See docs: Get all shipments.",
16-26
: Property copy edits
- “customerid” → “customer ID”.
- Clarify order/shipment number behavior.
- description: "Retrieve all shipments from a specific customer based on the customerid", + description: "Retrieve shipments for a specific customer ID", ... - description: "Retrieve a shipment based on the order number", + description: "Retrieve shipments for an order number",
73-82
: Avoid sending undefined filtersFilter out empty/undefined params to prevent noisy requests.
- params: { - customer: this.customerId, - order: this.orderNumber, - number: this.shipmentNumber, - since_id: this.sinceId, - created_at_min: this.createdAtMin, - created_at_max: this.createdAtMax, - updated_at_min: this.updatedAtMin, - updated_at_max: this.updatedAtMax, - }, + params: Object.fromEntries(Object.entries({ + customer: this.customerId, + order: this.orderNumber, + number: this.shipmentNumber, + since_id: this.sinceId, + created_at_min: this.createdAtMin, + created_at_max: this.createdAtMax, + updated_at_min: this.updatedAtMin, + updated_at_max: this.updatedAtMax, + }).filter(([, v]) => v != null && v !== "")),components/lightspeed_ecom_c_series/actions/find-customers/find-customers.mjs (3)
5-7
: Description mismatchYou’re finding customers via filters (email, dates), not “by ID.” Align wording.
- description: "Find a customer by ID. [See the documentation](https://developers.lightspeedhq.com/ecom/endpoints/customer/#get-all-customers)", + description: "Find customers using filters (email, date range). See docs: Get all customers.",
16-18
: Clarify prop phrasingCurrent text is awkward.
- description: "Retrieve all customers from a specific customer based on the customer email", + description: "Filter customers by email address",
54-67
: Param sanitization (optional)Consider filtering out undefined/empty values for cleaner requests.
- params: { - email: this.customerEmail, - since_id: this.sinceId, - created_at_min: this.createdAtMin, - created_at_max: this.createdAtMax, - updated_at_min: this.updatedAtMin, - updated_at_max: this.updatedAtMax, - }, + params: Object.fromEntries(Object.entries({ + email: this.customerEmail, + since_id: this.sinceId, + created_at_min: this.createdAtMin, + created_at_max: this.createdAtMax, + updated_at_min: this.updatedAtMin, + updated_at_max: this.updatedAtMax, + }).filter(([, v]) => v != null && v !== "")),components/lightspeed_ecom_c_series/actions/find-product/find-product.mjs (2)
5-7
: Fix grammar and scopeUse plural and remove “by ID” since this lists/filters.
- name: "Find Product", - description: "Find an product by ID. [See the documentation](https://developers.lightspeedhq.com/ecom/endpoints/product/#get-all-products)", + name: "Find Products", + description: "Find products using filters (brand, date range). See docs: Get all products.",
50-62
: Param sanitization (optional)Keep requests clean by omitting undefined/empty values.
- params: { - brand: this.brandId, - since_id: this.sinceId, - created_at_min: this.createdAtMin, - created_at_max: this.createdAtMax, - updated_at_min: this.updatedAtMin, - updated_at_max: this.updatedAtMax, - }, + params: Object.fromEntries(Object.entries({ + brand: this.brandId, + since_id: this.sinceId, + created_at_min: this.createdAtMin, + created_at_max: this.createdAtMax, + updated_at_min: this.updatedAtMin, + updated_at_max: this.updatedAtMax, + }).filter(([, v]) => v != null && v !== "")),components/lightspeed_ecom_c_series/actions/find-invoice/find-invoice.mjs (3)
5-7
: Description mismatchThis action lists/filters invoices; not “by ID.”
- description: "Find an invoice by ID. [See the documentation](https://developers.lightspeedhq.com/ecom/endpoints/invoice/#get-all-invoices)", + description: "Find invoices using filters (customer, order, number, date range). See docs: Get all invoices.",
16-26
: Copy and semantics fixes
- “customerid” → “customer ID”.
- Order filter prop describes fetching an order; should reference invoices for an order number.
- description: "Retrieve all invoices from a specific customer based on the customerid", + description: "Retrieve invoices for a specific customer ID", ... - description: "Retrieve an order based on the order number", + description: "Retrieve invoices for an order number",
74-83
: Omit undefined filters (optional)Mirror other comments: filter out empty values before calling.
- params: { - customer: this.customerId, - number: this.invoiceNumber, - order: this.orderNumber, - since_id: this.sinceId, - created_at_min: this.createdAtMin, - created_at_max: this.createdAtMax, - updated_at_min: this.updatedAtMin, - updated_at_max: this.updatedAtMax, - }, + params: Object.fromEntries(Object.entries({ + customer: this.customerId, + number: this.invoiceNumber, + order: this.orderNumber, + since_id: this.sinceId, + created_at_min: this.createdAtMin, + created_at_max: this.createdAtMax, + updated_at_min: this.updatedAtMin, + updated_at_max: this.updatedAtMax, + }).filter(([, v]) => v != null && v !== "")),components/lightspeed_ecom_c_series/sources/order-updated/order-updated.mjs (2)
6-8
: Key prefix inconsistent with appOther modules use
lightspeed_ecom_c_series-...
. Align for discoverability and to avoid collisions.- key: "indiefunnels-order-updated", + key: "lightspeed_ecom_c_series-order-updated",
8-9
: Grammar fix“a order” → “an order”.
- description: "Emit new event when a order is updated.", + description: "Emit new event when an order is updated.",components/lightspeed_ecom_c_series/sources/product-deleted/product-deleted.mjs (2)
6-8
: Key prefix inconsistent with appUse the same
lightspeed_ecom_c_series-
prefix used elsewhere.- key: "indiefunnels-product-deleted", + key: "lightspeed_ecom_c_series-product-deleted",
20-26
: Potential null deref with empty sample
generateMeta
assumesbody.product.id
anddeletedAt
. The referenced test-event is empty in this PR; sample display may break.I can supply a minimal
test-event.mjs
:export default { product: { id: 123, deletedAt: "2025-09-17T20:25:55+00:00", }, };components/lightspeed_ecom_c_series/sources/order-shipped/test-event.mjs (1)
7-7
: Align sample payload with “shipped” semanticsThe order status shows completed_shipped, but shipmentStatus and shipments[].status are “not_shipped”. Align for clarity.
- "shipmentStatus":"not_shipped", + "shipmentStatus":"shipped",- "status":"not_shipped", + "status":"shipped",Also applies to: 339-339, 93-94
components/lightspeed_ecom_c_series/sources/new-customer-created/new-customer-created.mjs (1)
8-8
: Grammar nit: “a customer”.Change “an customer” → “a customer”.
Apply this diff:
- description: "Emit new event when an customer is created.", + description: "Emit new event when a customer is created.",components/lightspeed_ecom_c_series/sources/common/base.mjs (1)
35-38
: Guard deactivation when no webhookId is stored.Avoid calling
deleteHook(undefined)
on first deploys or if state was cleared.Apply this diff:
async deactivate() { const webhookId = this._getWebhookId(); - await this.app.deleteHook(webhookId); + if (!webhookId) return; + await this.app.deleteHook(webhookId); },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (32)
components/lightspeed_ecom_c_series/actions/find-customers/find-customers.mjs
(1 hunks)components/lightspeed_ecom_c_series/actions/find-invoice/find-invoice.mjs
(1 hunks)components/lightspeed_ecom_c_series/actions/find-order/find-order.mjs
(1 hunks)components/lightspeed_ecom_c_series/actions/find-product/find-product.mjs
(1 hunks)components/lightspeed_ecom_c_series/actions/find-shipment/find-shipment.mjs
(1 hunks)components/lightspeed_ecom_c_series/actions/get-order-products/get-order-products.mjs
(1 hunks)components/lightspeed_ecom_c_series/actions/get-order/get-order.mjs
(1 hunks)components/lightspeed_ecom_c_series/actions/get-product/get-product.mjs
(1 hunks)components/lightspeed_ecom_c_series/actions/get-shipment/get-shipment.mjs
(1 hunks)components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs
(1 hunks)components/lightspeed_ecom_c_series/package.json
(2 hunks)components/lightspeed_ecom_c_series/sources/common/base.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/customer-updated/customer-updated.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/customer-updated/test-event.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/invoice-updated/invoice-updated.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/invoice-updated/test-event.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/new-customer-created/new-customer-created.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/new-customer-created/test-event.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/new-invoice-created/new-invoice-created.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/new-invoice-created/test-event.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/new-order-created/new-order-created.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/new-order-created/test-event.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/new-product-created/new-product-created.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/new-product-created/test-event.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/order-shipped/order-shipped.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/order-shipped/test-event.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/order-updated/order-updated.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/order-updated/test-event.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/product-deleted/product-deleted.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/product-deleted/test-event.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/product-updated/product-updated.mjs
(1 hunks)components/lightspeed_ecom_c_series/sources/product-updated/test-event.mjs
(1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2024-12-12T19:23:09.039Z
Learnt from: jcortes
PR: PipedreamHQ/pipedream#14935
File: components/sailpoint/package.json:15-18
Timestamp: 2024-12-12T19:23:09.039Z
Learning: When developing Pipedream components, do not add built-in Node.js modules like `fs` to `package.json` dependencies, as they are native modules provided by the Node.js runtime.
Applied to files:
components/lightspeed_ecom_c_series/package.json
📚 Learning: 2024-10-08T15:33:38.240Z
Learnt from: GTFalcao
PR: PipedreamHQ/pipedream#12697
File: components/salesforce_rest_api/sources/common-webhook-methods.mjs:1-71
Timestamp: 2024-10-08T15:33:38.240Z
Learning: The `common-webhook-methods.mjs` object is designed to be extended, similar to an abstract class, and intentionally does not implement certain methods like `generateWebhookMeta` and `getEventType` to enforce implementation in subclasses.
Applied to files:
components/lightspeed_ecom_c_series/sources/common/base.mjs
🧬 Code graph analysis (10)
components/lightspeed_ecom_c_series/actions/find-shipment/find-shipment.mjs (6)
components/lightspeed_ecom_c_series/actions/find-customers/find-customers.mjs (1)
response
(55-67)components/lightspeed_ecom_c_series/actions/find-invoice/find-invoice.mjs (1)
response
(71-85)components/lightspeed_ecom_c_series/actions/find-order/find-order.mjs (1)
response
(63-76)components/lightspeed_ecom_c_series/actions/find-product/find-product.mjs (1)
response
(50-62)components/lightspeed_ecom_c_series/actions/get-order-products/get-order-products.mjs (1)
response
(19-24)components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs (1)
response
(312-315)
components/lightspeed_ecom_c_series/actions/find-customers/find-customers.mjs (6)
components/lightspeed_ecom_c_series/actions/find-invoice/find-invoice.mjs (1)
response
(71-85)components/lightspeed_ecom_c_series/actions/find-order/find-order.mjs (1)
response
(63-76)components/lightspeed_ecom_c_series/actions/find-product/find-product.mjs (1)
response
(50-62)components/lightspeed_ecom_c_series/actions/find-shipment/find-shipment.mjs (1)
response
(70-84)components/lightspeed_ecom_c_series/actions/get-order-products/get-order-products.mjs (1)
response
(19-24)components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs (1)
response
(312-315)
components/lightspeed_ecom_c_series/actions/get-product/get-product.mjs (3)
components/lightspeed_ecom_c_series/actions/get-order/get-order.mjs (1)
response
(19-22)components/lightspeed_ecom_c_series/actions/get-shipment/get-shipment.mjs (1)
response
(19-22)components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs (1)
response
(312-315)
components/lightspeed_ecom_c_series/actions/get-shipment/get-shipment.mjs (3)
components/lightspeed_ecom_c_series/actions/get-order/get-order.mjs (1)
response
(19-22)components/lightspeed_ecom_c_series/actions/get-product/get-product.mjs (1)
response
(19-22)components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs (1)
response
(312-315)
components/lightspeed_ecom_c_series/actions/get-order-products/get-order-products.mjs (4)
components/lightspeed_ecom_c_series/actions/find-customers/find-customers.mjs (1)
response
(55-67)components/lightspeed_ecom_c_series/actions/find-product/find-product.mjs (2)
response
(50-62)products
(64-64)components/lightspeed_ecom_c_series/actions/get-order/get-order.mjs (1)
response
(19-22)components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs (1)
response
(312-315)
components/lightspeed_ecom_c_series/actions/find-invoice/find-invoice.mjs (5)
components/lightspeed_ecom_c_series/actions/find-customers/find-customers.mjs (1)
response
(55-67)components/lightspeed_ecom_c_series/actions/find-order/find-order.mjs (1)
response
(63-76)components/lightspeed_ecom_c_series/actions/find-product/find-product.mjs (1)
response
(50-62)components/lightspeed_ecom_c_series/actions/find-shipment/find-shipment.mjs (1)
response
(70-84)components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs (1)
response
(312-315)
components/lightspeed_ecom_c_series/actions/find-order/find-order.mjs (6)
components/lightspeed_ecom_c_series/actions/find-customers/find-customers.mjs (1)
response
(55-67)components/lightspeed_ecom_c_series/actions/find-invoice/find-invoice.mjs (1)
response
(71-85)components/lightspeed_ecom_c_series/actions/find-product/find-product.mjs (1)
response
(50-62)components/lightspeed_ecom_c_series/actions/find-shipment/find-shipment.mjs (1)
response
(70-84)components/lightspeed_ecom_c_series/actions/get-order-products/get-order-products.mjs (1)
response
(19-24)components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs (1)
response
(312-315)
components/lightspeed_ecom_c_series/actions/find-product/find-product.mjs (6)
components/lightspeed_ecom_c_series/actions/find-customers/find-customers.mjs (1)
response
(55-67)components/lightspeed_ecom_c_series/actions/find-invoice/find-invoice.mjs (1)
response
(71-85)components/lightspeed_ecom_c_series/actions/find-order/find-order.mjs (1)
response
(63-76)components/lightspeed_ecom_c_series/actions/find-shipment/find-shipment.mjs (1)
response
(70-84)components/lightspeed_ecom_c_series/actions/get-order-products/get-order-products.mjs (2)
response
(19-24)products
(26-26)components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs (1)
response
(312-315)
components/lightspeed_ecom_c_series/actions/get-order/get-order.mjs (4)
components/lightspeed_ecom_c_series/actions/get-order-products/get-order-products.mjs (1)
response
(19-24)components/lightspeed_ecom_c_series/actions/get-product/get-product.mjs (1)
response
(19-22)components/lightspeed_ecom_c_series/actions/get-shipment/get-shipment.mjs (1)
response
(19-22)components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs (1)
response
(312-315)
components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs (10)
components/lightspeed_ecom_c_series/actions/find-customers/find-customers.mjs (2)
customers
(69-69)response
(55-67)components/lightspeed_ecom_c_series/actions/find-order/find-order.mjs (2)
orders
(78-78)response
(63-76)components/lightspeed_ecom_c_series/actions/find-shipment/find-shipment.mjs (2)
shipments
(86-86)response
(70-84)components/lightspeed_ecom_c_series/actions/find-invoice/find-invoice.mjs (2)
invoices
(87-87)response
(71-85)components/lightspeed_ecom_c_series/actions/find-product/find-product.mjs (2)
products
(64-64)response
(50-62)components/lightspeed_ecom_c_series/actions/get-order-products/get-order-products.mjs (2)
products
(26-26)response
(19-24)components/lightspeed_ecom_c_series/actions/get-order/get-order.mjs (1)
response
(19-22)components/lightspeed_ecom_c_series/actions/get-product/get-product.mjs (1)
response
(19-22)components/lightspeed_ecom_c_series/actions/get-shipment/get-shipment.mjs (1)
response
(19-22)components/lightspeed_ecom_c_series/sources/common/base.mjs (1)
response
(22-32)
🔇 Additional comments (24)
components/lightspeed_ecom_c_series/package.json (2)
3-3
: Semver bump looks appropriateBumping to 0.1.0 for new sources/actions reads right. LGTM.
15-17
: Confirm minimal required @pipedream/platform version and align across repoScan shows inconsistent versions across components (range: ^0.9.0 → ^3.1.0; also bare 1.x/2.x/3.x entries and one "null" — likely a malformed package.json).
- Determine the minimum @pipedream/platform required by this component; bump the floor if it uses APIs newer than the chosen baseline.
- Pick a repo-wide target (e.g., standardize to ^3.1.0 if needed) and update all components/**/package.json to match, then run CI/tests.
- Investigate and fix the package.json(s) that produced "null" in the scan.
components/lightspeed_ecom_c_series/actions/get-product/get-product.mjs (1)
3-26
: LGTM.Action shape matches the app helper and returns the API payload with a concise summary. No blockers.
components/lightspeed_ecom_c_series/actions/get-order-products/get-order-products.mjs (2)
31-33
: Summary string has an extra “order”.Reads “Successfully retrieved order 5 product(s)…”. Drop the first “order”.
[raise_nitpick_refactor]
- $.export("$summary", `Successfully retrieved order ${products.length} product${products.length === 1 + $.export("$summary", `Successfully retrieved ${products.length} product${products.length === 1 ? "" - : "s"} for order with ID: ${this.orderId}`); + : "s"} for order ID: ${this.orderId}`);
19-24
: ConfirmdataField
name returned by/orders/{id}/products.json
.Some implementations return
orderproducts
(lowercase) instead oforderProducts
. If the field name doesn’t match, pagination will yield nothing.Run this quick grep to see what field name we use elsewhere and avoid a mismatch:
If the API returns
orderproducts
, updatedataField
accordingly.components/lightspeed_ecom_c_series/actions/find-order/find-order.mjs (1)
16-17
: Copy nit: “customerid”.[raise_nitpick_refactor]
- description: "Retrieve all orders from a specific customer based on the customerid", + description: "Retrieve all orders from a specific customer based on the customer ID",components/lightspeed_ecom_c_series/actions/get-order/get-order.mjs (1)
3-26
: LGTM.Straightforward wrapper over
getOrder
with a clear summary.components/lightspeed_ecom_c_series/actions/find-shipment/find-shipment.mjs (2)
91-94
: Good UX summaryPluralization handled correctly.
69-84
: Verify whether theorder
filter expects the internal order ID (not the visible order number)
You map orderNumber →order
in components/lightspeed_ecom_c_series/actions/find-shipment/find-shipment.mjs; confirm the C‑Series GET /shipments.jsonorder
query parameter expects the internal order ID (document ID/GUID) rather than the human‑readable order number — if it expects the ID, send the ID instead (or use the documented parameter name); if it expects the order number, no change needed.components/lightspeed_ecom_c_series/actions/find-customers/find-customers.mjs (1)
74-77
: Nice summaryPluralization is correct.
components/lightspeed_ecom_c_series/actions/get-shipment/get-shipment.mjs (1)
18-26
: LGTMDirect pass-through to app client with a clear summary. Matches get‑order/get‑product patterns.
components/lightspeed_ecom_c_series/actions/find-product/find-product.mjs (1)
69-72
: Good summaryPluralization handled.
components/lightspeed_ecom_c_series/actions/find-invoice/find-invoice.mjs (2)
92-95
: Nice summaryPluralization handled.
70-85
: Confirm whetherorder
query parameter requires an order ID (not order number)No official Lightspeed C‑Series docs found for GET /invoices.json; unclear if
order
expects an order ID or an order number — mapping orderNumber →order
may be incorrect.File: components/lightspeed_ecom_c_series/actions/find-invoice/find-invoice.mjs (lines 70–85)
Verify by testing GET /invoices.json against a store (compare queries using order number vs order ID) or confirm with Lightspeed support; if
order
requires an ID, send the order ID (or use the correct parameter) instead of the order number.components/lightspeed_ecom_c_series/sources/order-updated/order-updated.mjs (1)
20-26
: Meta generation looks goodId/summary/timestamp are sensible. Assumes
body.order.updatedAt
exists.Please confirm the webhook payload contains
order.updatedAt
in UTC ISO 8601.components/lightspeed_ecom_c_series/sources/product-updated/test-event.mjs (1)
1-90
: Fixture is rich and representativeGood coverage of nested product relations; useful for UI/testing.
Confirm these fields mirror an actual eCom product payload (C‑Series), especially
visibility
,brand.resource
, andvariants.resource
shapes.components/lightspeed_ecom_c_series/sources/new-order-created/new-order-created.mjs (1)
20-25
: LGTM on meta fieldsUsing order.id and createdAt for created events is appropriate.
components/lightspeed_ecom_c_series/sources/new-product-created/new-product-created.mjs (1)
4-29
: Pattern matches base correctlyGroup/action/meta look good; created events safely dedupe on product.id.
components/lightspeed_ecom_c_series/sources/new-invoice-created/new-invoice-created.mjs (1)
4-29
: Solid created-event sourceMeta/id/ts mapping is consistent with created semantics.
components/lightspeed_ecom_c_series/sources/new-invoice-created/test-event.mjs (1)
1-52
: Fixture looks goodStructure matches source expectations; timestamps are ISO8601 with offset.
components/lightspeed_ecom_c_series/sources/new-customer-created/new-customer-created.mjs (2)
4-12
: Good use of common base and source metadata.Structure, spread of common, and dedupe mode are consistent with other Pipedream sources.
20-26
: Use snake_case "created_at" (not createdAt) in generateMetaLightspeed eCom C‑Series customer payloads use "id" and "created_at" (snake_case). Update components/lightspeed_ecom_c_series/sources/new-customer-created/new-customer-created.mjs (lines 20–26) to read body.customer.created_at for ts parsing instead of body.customer.createdAt.
⛔ Skipped due to learnings
Learnt from: GTFalcao PR: PipedreamHQ/pipedream#17538 File: components/aircall/sources/new-sms/new-sms.mjs:19-25 Timestamp: 2025-07-09T18:07:12.426Z Learning: In Aircall API webhook payloads, the `created_at` field is returned as an ISO 8601 string format (e.g., "2020-02-18T20:52:22.000Z"), not as milliseconds since epoch. For Pipedream components, this needs to be converted to milliseconds using `Date.parse()` before assigning to the `ts` field in `generateMeta()`.
components/lightspeed_ecom_c_series/sources/common/base.mjs (1)
1-11
: Base scaffold looks solid.Props wiring for
app
,db
, and custom HTTP interface aligns with Pipedream patterns.components/lightspeed_ecom_c_series/sources/product-updated/product-updated.mjs (1)
4-12
: Structure LGTM.Extends the common base correctly; item group/action match the event semantics.
async *paginate({ | ||
fn, params = {}, dataField, maxResults = null, ...opts | ||
}) { | ||
let hasMore = false; | ||
let count = 0; | ||
let page = 0; | ||
|
||
do { | ||
params.page = ++page; | ||
const response = await fn({ | ||
params, | ||
...opts, | ||
}); | ||
for (const d of response[dataField]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Paginator calls unbound methods (breaks all “find” actions and Get Order Products).
fn
is passed as a bare method reference (e.g., this.app.listOrder
) and invoked without binding, so this
inside the method is undefined
and _makeRequest
will throw. Bind this
when invoking.
Apply this diff:
- const response = await fn({
+ const response = await fn.call(this, {
params,
...opts,
});
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
async *paginate({ | |
fn, params = {}, dataField, maxResults = null, ...opts | |
}) { | |
let hasMore = false; | |
let count = 0; | |
let page = 0; | |
do { | |
params.page = ++page; | |
const response = await fn({ | |
params, | |
...opts, | |
}); | |
for (const d of response[dataField]) { | |
async *paginate({ | |
fn, params = {}, dataField, maxResults = null, ...opts | |
}) { | |
let hasMore = false; | |
let count = 0; | |
let page = 0; | |
do { | |
params.page = ++page; | |
const response = await fn.call(this, { | |
params, | |
...opts, | |
}); | |
for (const d of response[dataField]) { |
🤖 Prompt for AI Agents
In components/lightspeed_ecom_c_series/lightspeed_ecom_c_series.app.mjs around
lines 303–316, the paginator invokes the passed-in function `fn` without binding
its receiver, so methods like `this.app.listOrder` lose their `this` and fail;
change the invocation to bind the current instance when calling `fn` (for
example, replace the direct await fn({ params, ...opts }) with a bound call such
as await fn.call(this, { params, ...opts }) or use Reflect.apply(fn, this, [{
params, ...opts }]) so the method keeps the correct `this` context).
methods: { | ||
_setWebhookId(id) { | ||
this.db.set("webhookId", id); | ||
}, | ||
_getWebhookId() { | ||
return this.db.get("webhookId"); | ||
}, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add a shared getSummary helper.
Downstream sources call this.getSummary(...)
. Provide a simple default here to avoid crashes and unify summaries.
Apply this diff:
methods: {
+ getSummary(summary) {
+ // Keep summaries concise; trim to a safe length
+ return String(summary ?? "").slice(0, 255);
+ },
_setWebhookId(id) {
this.db.set("webhookId", id);
},
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
methods: { | |
_setWebhookId(id) { | |
this.db.set("webhookId", id); | |
}, | |
_getWebhookId() { | |
return this.db.get("webhookId"); | |
}, | |
}, | |
methods: { | |
getSummary(summary) { | |
// Keep summaries concise; trim to a safe length | |
return String(summary ?? "").slice(0, 255); | |
}, | |
_setWebhookId(id) { | |
this.db.set("webhookId", id); | |
}, | |
_getWebhookId() { | |
return this.db.get("webhookId"); | |
}, | |
}, |
🤖 Prompt for AI Agents
In components/lightspeed_ecom_c_series/sources/common/base.mjs around lines 12
to 19, there is no default getSummary method which causes downstream sources
calling this.getSummary(...) to crash; add a shared getSummary helper in the
methods object that accepts (item, options) and returns a simple normalized
summary (e.g., a short object or string built from item fields like id, name,
and a fallback to JSON.stringify(item) when fields are missing) so downstream
callers have a safe, consistent default implementation.
async run({ body }) { | ||
this.http.respond({ | ||
status: 200, | ||
}); | ||
|
||
this.$emit(body, this.generateMeta(body)); | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
No webhook authenticity verification (security).
Responding 200 and emitting without verifying origin invites spoofed events. If Lightspeed signs webhooks, verify before emitting.
Apply this illustrative diff (uses optional verifyWebhook
if implemented in the app; adjust per docs):
- async run({ body }) {
- this.http.respond({
- status: 200,
- });
-
- this.$emit(body, this.generateMeta(body));
- },
+ async run(event) {
+ const { headers, body } = event;
+ if (this.app.verifyWebhook && !this.app.verifyWebhook(headers, body)) {
+ this.http.respond({ status: 401 });
+ return;
+ }
+ this.http.respond({ status: 200 });
+ this.$emit(body, this.generateMeta(body));
+ },
Please confirm webhook signing details:
🌐 Web query:
Does Lightspeed eCom C-Series sign webhook requests? If yes, what header carries the signature and how is it computed/verified?
💡 Result:
Short answer: Yes. Lightspeed signs webhook requests using an HMAC. The signature header and verification method used across Lightspeed products are:
- Header: X-Signature. Example header format: signature=,algorithm=HMAC-SHA256. [1]
- How to verify: compute an HMAC-SHA256 of the raw request body using your application's client_secret (or webhook signature token where applicable) as the key, and compare the resulting digest to the signature value in X-Signature (use exact-match; do not reformat the payload). The algorithm field in the header indicates the HMAC algorithm (currently HMAC-SHA256). [1][2]
Sources: Lightspeed webhook docs and HMAC tips for Lightspeed products. [1][2]
References
[1] Lightspeed Webhooks — verification / X-Signature header. (x-series-api.lightspeedhq.com)
[2] HMAC tips for Lightspeed webhooks (note about using raw payload and SHA256). (o-series-support.lightspeedhq.com)
Citations:
- 1: https://x-series-api.lightspeedhq.com/docs/webhooks?utm_source=openai
- 2: https://o-series-support.lightspeedhq.com/hc/en-us/articles/31329267751707-HMAC-Tips-for-Webhooks?utm_source=openai
Verify Lightspeed webhook signature (X-Signature) before emitting
Lightspeed signs webhooks with HMAC-SHA256 in the X-Signature header (format: signature=,algorithm=HMAC-SHA256). Compute HMAC-SHA256 over the raw request body with your client_secret/webhook token and use a constant-time compare — return 401 and do NOT emit on mismatch.
File: components/lightspeed_ecom_c_series/sources/common/base.mjs — run method (lines 40–46)
- async run({ body }) {
- this.http.respond({
- status: 200,
- });
-
- this.$emit(body, this.generateMeta(body));
- },
+ async run(event) {
+ const { headers, body, rawBody } = event;
+ // Prefer an app-provided verifier; otherwise verify X-Signature (HMAC-SHA256) over rawBody.
+ if (this.app.verifyWebhook && !this.app.verifyWebhook(headers, rawBody ?? body)) {
+ this.http.respond({ status: 401 });
+ return;
+ }
+ this.http.respond({ status: 200 });
+ this.$emit(body, this.generateMeta(body));
+ },
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In components/lightspeed_ecom_c_series/sources/common/base.mjs around lines 40
to 46, the run method currently accepts and emits the webhook payload without
validating the X-Signature header; compute the HMAC-SHA256 of the raw request
body using the configured client_secret/webhook token, parse the X-Signature
header (format: signature=<hex-or-base64>,algorithm=HMAC-SHA256), perform a
constant-time comparison (e.g., crypto.timingSafeEqual) between the computed
signature and the header value, and if the signature does not match respond with
status 401 and do NOT call this.$emit; ensure you use the raw request body bytes
(not a parsed object) and handle missing/invalid signature header by returning
401 as well.
...common, | ||
key: "indiefunnels-customer-updated", | ||
name: "Customer Updated (Instant)", | ||
description: "Emit new event when an customer is updated.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix grammar in user-facing description
"an customer" → "a customer".
- description: "Emit new event when an customer is updated.",
+ description: "Emit new event when a customer is updated.",
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
description: "Emit new event when an customer is updated.", | |
description: "Emit new event when a customer is updated.", |
🤖 Prompt for AI Agents
In
components/lightspeed_ecom_c_series/sources/customer-updated/customer-updated.mjs
around line 8, the description string reads "Emit new event when an customer is
updated."; change the article "an" to "a" so it reads "Emit new event when a
customer is updated." and save the file.
generateMeta(body) { | ||
return { | ||
id: body.customer.id, | ||
summary: this.getSummary(`Customer with ID ${body.customer.id} updated`), | ||
ts: Date.parse(body.customer.updatedAt), | ||
}; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid event loss: include updatedAt in meta.id for 'unique' dedupe
Same rationale as invoices: multiple updates to one customer should not be collapsed.
- generateMeta(body) {
- return {
- id: body.customer.id,
- summary: this.getSummary(`Customer with ID ${body.customer.id} updated`),
- ts: Date.parse(body.customer.updatedAt),
- };
- },
+ generateMeta(body) {
+ const ts = Date.parse(body.customer?.updatedAt);
+ const safeTs = Number.isFinite(ts) ? ts : Date.now();
+ return {
+ id: `${body.customer?.id}:${safeTs}`,
+ summary: this.getSummary(`Customer with ID ${body.customer?.id} updated`),
+ ts: safeTs,
+ };
+ },
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
generateMeta(body) { | |
return { | |
id: body.customer.id, | |
summary: this.getSummary(`Customer with ID ${body.customer.id} updated`), | |
ts: Date.parse(body.customer.updatedAt), | |
}; | |
}, | |
generateMeta(body) { | |
const ts = Date.parse(body.customer?.updatedAt); | |
const safeTs = Number.isFinite(ts) ? ts : Date.now(); | |
return { | |
id: `${body.customer?.id}:${safeTs}`, | |
summary: this.getSummary(`Customer with ID ${body.customer?.id} updated`), | |
ts: safeTs, | |
}; | |
}, |
🤖 Prompt for AI Agents
In
components/lightspeed_ecom_c_series/sources/customer-updated/customer-updated.mjs
around lines 20 to 26, the generated meta.id uses only body.customer.id which
can collapse multiple updates for the same customer; change meta.id to include
the updatedAt value (e.g., append the ISO timestamp or its numeric timestamp) so
each update produces a unique id, ensure ts remains
Date.parse(body.customer.updatedAt) and keep summary unchanged.
"resource":{ | ||
"id":13353882, | ||
"url":"customers\/123", | ||
"link":"http:\/\/api.shoplightspeed.com\/en\/customers\/123.json", | ||
"embedded":{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Customer resource id/url mismatch
Same mismatch here: resource.id 13353882 but URL points to /customers/123.
- "id":13353882,
- "url":"customers\/123",
- "link":"http:\/\/api.shoplightspeed.com\/en\/customers\/123.json",
+ "id":13353882,
+ "url":"customers\/13353882",
+ "link":"https:\/\/api.shoplightspeed.com\/en\/customers\/13353882.json",
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
"resource":{ | |
"id":13353882, | |
"url":"customers\/123", | |
"link":"http:\/\/api.shoplightspeed.com\/en\/customers\/123.json", | |
"embedded":{ | |
"resource":{ | |
"id":13353882, | |
"url":"customers\/13353882", | |
"link":"https:\/\/api.shoplightspeed.com\/en\/customers\/13353882.json", | |
"embedded":{ |
🤖 Prompt for AI Agents
In components/lightspeed_ecom_c_series/sources/new-order-created/test-event.mjs
around lines 144 to 148, the resource object has id: 13353882 while the url
points to /customers/123, causing a mismatch; fix by making the id and URL
consistent — either update resource.id to 123 to match "customers/123" or update
the URL/path to use 13353882 (and update any linked fields like link
accordingly) so the id, url, and link all refer to the same customer identifier.
...common, | ||
key: "indiefunnels-order-shipped", | ||
name: "Order Shipped (Instant)", | ||
description: "Emit new event when a order is shipped.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix grammar in user-facing description
"a order" → "an order".
- description: "Emit new event when a order is shipped.",
+ description: "Emit new event when an order is shipped.",
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
description: "Emit new event when a order is shipped.", | |
description: "Emit new event when an order is shipped.", |
🤖 Prompt for AI Agents
In components/lightspeed_ecom_c_series/sources/order-shipped/order-shipped.mjs
around line 8, the user-facing description uses incorrect grammar ("a order is
shipped"); update the string to use "an order" instead, i.e. change the
description to "Emit new event when an order is shipped." so the message reads
correctly for end users.
generateMeta(body) { | ||
return { | ||
id: body.order.id, | ||
summary: this.getSummary(`Order with ID ${body.order.id} shipped`), | ||
ts: Date.parse(body.order.updatedAt), | ||
}; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
Prevent dedupe collisions for shipped events
If multiple shipment events occur per order, id=order.id will collapse them. Include a time (or shipment) component.
- generateMeta(body) {
- return {
- id: body.order.id,
- summary: this.getSummary(`Order with ID ${body.order.id} shipped`),
- ts: Date.parse(body.order.updatedAt),
- };
- },
+ generateMeta(body) {
+ const ts = Date.parse(body.order?.updatedAt);
+ const safeTs = Number.isFinite(ts) ? ts : Date.now();
+ return {
+ id: `${body.order?.id}:${safeTs}`,
+ summary: this.getSummary(`Order with ID ${body.order?.id} shipped`),
+ ts: safeTs,
+ };
+ },
If the webhook payload includes a shipment id, prefer ${body.shipment.id}
(or ${body.order.id}:${body.shipment.id}
) as the id.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Searching for order-shipped file..."
file=$(rg --hidden --glob '!node_modules' --files | rg 'order-shipped.mjs' || true)
if [[ -n "$file" ]]; then
echo "Found: $file"
echo "---- begin file ----"
sed -n '1,240p' "$file" || true
echo "---- end file ----"
else
echo "order-shipped.mjs not found by name search. Listing files under components/lightspeed_ecom_c_series/sources if present..."
rg -n --hidden --glob 'components/lightspeed_ecom_c_series/**' --files || true
fi
echo
echo "Searching repository for relevant symbols: generateMeta, shipment, shipment.id, order.id, updatedAt..."
rg -n --hidden -S "generateMeta" -g '!node_modules' -C3 || true
rg -n --hidden -S '\bshipment\b' -g '!node_modules' -C3 || true
rg -n --hidden -S '\bshipment\.id\b' -g '!node_modules' -C3 || true
rg -n --hidden -S '\border\.id\b' -g '!node_modules' -C3 || true
rg -n --hidden -S 'updatedAt' -g '!node_modules' -C3 || true
Length of output: 3442
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "---- order-shipped.mjs ----"
sed -n '1,240p' components/lightspeed_ecom_c_series/sources/order-shipped/order-shipped.mjs || true
echo
echo "---- test-event.mjs ----"
sed -n '1,240p' components/lightspeed_ecom_c_series/sources/order-shipped/test-event.mjs || true
echo
echo "---- search for 'shipment' and 'generateMeta' in that component ----"
rg -n --hidden -S "shipment" components/lightspeed_ecom_c_series -C3 || true
rg -n --hidden -S "generateMeta" components/lightspeed_ecom_c_series -C3 || true
Length of output: 91660
Use a shipment identifier in the dedupe key for order-shipped events
order.id alone collapses multiple shipments for the same order — prefer a shipment id when present, otherwise fall back to order id + timestamp.
File: components/lightspeed_ecom_c_series/sources/order-shipped/order-shipped.mjs — replace generateMeta with:
generateMeta(body) {
const order = body?.order ?? {};
const ts = Date.parse(order.updatedAt);
const safeTs = Number.isFinite(ts) ? ts : Date.now();
const shipmentId =
order.shipmentData?.shipment_id ??
order.shipmentId ??
order.shipment?.resource?.id ??
body.shipment?.id ??
null;
return {
id: shipmentId ? `${order.id}:${shipmentId}` : `${order.id}:${safeTs}`,
summary: this.getSummary(`Order with ID ${order.id} shipped`),
ts: safeTs,
};
},
If the incoming webhook provides a specific shipment resource id (e.g. shipmentData.shipment_id or shipment.resource.id), that should be used as the primary dedupe key.
"resource":{ | ||
"id":13353882, | ||
"url":"customers\/123", | ||
"link":"http:\/\/api.shoplightspeed.com\/en\/customers\/123.json", | ||
"embedded":{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Customer resource id/url mismatch
resource.id is 13353882 but url/link point to customers/123. This can break resolvers relying on URL ids.
- "id":13353882,
- "url":"customers\/123",
- "link":"http:\/\/api.shoplightspeed.com\/en\/customers\/123.json",
+ "id":13353882,
+ "url":"customers\/13353882",
+ "link":"https:\/\/api.shoplightspeed.com\/en\/customers\/13353882.json",
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
"resource":{ | |
"id":13353882, | |
"url":"customers\/123", | |
"link":"http:\/\/api.shoplightspeed.com\/en\/customers\/123.json", | |
"embedded":{ | |
"resource":{ | |
"id":13353882, | |
"url":"customers\/13353882", | |
"link":"https:\/\/api.shoplightspeed.com\/en\/customers\/13353882.json", | |
"embedded":{ |
🤖 Prompt for AI Agents
In components/lightspeed_ecom_c_series/sources/order-updated/test-event.mjs
around lines 144 to 148, the resource.id (13353882) does not match the id
embedded in resource.url/resource.link (customers/123), which can break
resolvers; fix by making the URL and link consistent with the resource.id (e.g.,
change "customers/123" and the link to use "customers/13353882"), or
alternatively change resource.id to 123 if the URLs are canonical—ensure id,
url, and link all reference the same customer id.
generateMeta(body) { | ||
return { | ||
id: body.product.id, | ||
summary: this.getSummary(`Product with ID ${body.product.id} updated`), | ||
ts: Date.parse(body.product.updatedAt), | ||
}; | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dedupe bug: meta.id must be per-update, not per-product.
Using product.id
with dedupe: "unique"
will collapse multiple updates to the same product.
Apply this diff to make IDs event-specific and to harden parsing:
- return {
- id: body.product.id,
- summary: this.getSummary(`Product with ID ${body.product.id} updated`),
- ts: Date.parse(body.product.updatedAt),
- };
+ const idPart = body?.product?.id;
+ const ts = Date.parse(body?.product?.updatedAt) || Date.now();
+ return {
+ id: `${idPart}-${ts}`,
+ summary: this.getSummary
+ ? this.getSummary(`Product with ID ${idPart} updated`)
+ : `Product with ID ${idPart} updated`,
+ ts,
+ };
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
generateMeta(body) { | |
return { | |
id: body.product.id, | |
summary: this.getSummary(`Product with ID ${body.product.id} updated`), | |
ts: Date.parse(body.product.updatedAt), | |
}; | |
}, | |
generateMeta(body) { | |
const idPart = body?.product?.id; | |
const ts = Date.parse(body?.product?.updatedAt) || Date.now(); | |
return { | |
id: `${idPart}-${ts}`, | |
summary: this.getSummary | |
? this.getSummary(`Product with ID ${idPart} updated`) | |
: `Product with ID ${idPart} updated`, | |
ts, | |
}; | |
}, |
🤖 Prompt for AI Agents
In
components/lightspeed_ecom_c_series/sources/product-updated/product-updated.mjs
around lines 20-26, the generateMeta function currently uses product.id which
collapses multiple updates when dedupe: "unique" is used; change meta.id to be
event-specific (e.g., combine product.id with the update timestamp or an event
identifier such as `${body.product.id}:${Date.parse(body.product.updatedAt) ||
Date.now()}`) and harden parsing by checking body, body.product, and
body.product.updatedAt exist, using Date.parse(updatedAt) with a fallback to
Date.now() (or new Date().toISOString()) for ts, and ensure id uses the same
normalized timestamp string so each update yields a unique id.
🧩 Analysis chain
Confirm payload field names.
Ensure the webhook delivers product.id
and product.updatedAt
as used here.
🌐 Web query:
For Lightspeed eCom C-Series product update webhooks: exact field names for product ID and last updated timestamp? `updatedAt` vs `updated_at`?
💡 Result:
Product update webhook (C‑Series) uses:
- product ID: id (string UUID). [1]
- last‑updated timestamp: updated_at (timestamp string). [1][2]
References: Lightspeed webhook example payloads and webhook docs. [1] (x-series-api.lightspeedhq.com)
Citations:
Use product.updated_at (snake_case) instead of product.updatedAt
Webhook payload uses product.id and product.updated_at — update Date.parse(body.product.updatedAt) to Date.parse(body.product.updated_at) (or normalize incoming keys) in components/lightspeed_ecom_c_series/sources/product-updated/product-updated.mjs (lines 20–26) to avoid NaN ts.
🤖 Prompt for AI Agents
In
components/lightspeed_ecom_c_series/sources/product-updated/product-updated.mjs
around lines 20–26, the code uses body.product.updatedAt which doesn't exist in
the webhook payload (the payload uses snake_case updated_at), causing Date.parse
to return NaN; change the ts computation to use body.product.updated_at (or
implement a small normalization/fallback such as
Date.parse(body.product.updated_at || body.product.updatedAt)) so ts is parsed
from the correct field.
Resolves #17440
Summary by CodeRabbit
New Features
Chores