From df6eb29b28246334391de4e1add5c785f8c6d0fe Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Fri, 23 May 2025 14:51:09 -0400 Subject: [PATCH 1/4] new components --- components/easyship/.gitignore | 3 - .../create-shipment/create-shipment.mjs | 222 ++++++++++++++++++ .../actions/find-shipment/find-shipment.mjs | 26 ++ components/easyship/app/easyship.app.ts | 13 - components/easyship/easyship.app.mjs | 121 ++++++++++ components/easyship/package.json | 8 +- components/easyship/sources/common/base.mjs | 65 +++++ .../new-shipping-label-created.mjs | 19 ++ .../new-shipping-label-created/test-event.mjs | 13 + .../tracking-status-updated/test-event.mjs | 15 ++ .../tracking-status-updated.mjs | 19 ++ .../warehouse-state-updated/test-event.mjs | 10 + .../warehouse-state-updated.mjs | 19 ++ 13 files changed, 534 insertions(+), 19 deletions(-) delete mode 100644 components/easyship/.gitignore create mode 100644 components/easyship/actions/create-shipment/create-shipment.mjs create mode 100644 components/easyship/actions/find-shipment/find-shipment.mjs delete mode 100644 components/easyship/app/easyship.app.ts create mode 100644 components/easyship/easyship.app.mjs create mode 100644 components/easyship/sources/common/base.mjs create mode 100644 components/easyship/sources/new-shipping-label-created/new-shipping-label-created.mjs create mode 100644 components/easyship/sources/new-shipping-label-created/test-event.mjs create mode 100644 components/easyship/sources/tracking-status-updated/test-event.mjs create mode 100644 components/easyship/sources/tracking-status-updated/tracking-status-updated.mjs create mode 100644 components/easyship/sources/warehouse-state-updated/test-event.mjs create mode 100644 components/easyship/sources/warehouse-state-updated/warehouse-state-updated.mjs diff --git a/components/easyship/.gitignore b/components/easyship/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/easyship/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/easyship/actions/create-shipment/create-shipment.mjs b/components/easyship/actions/create-shipment/create-shipment.mjs new file mode 100644 index 0000000000000..3236122acd5eb --- /dev/null +++ b/components/easyship/actions/create-shipment/create-shipment.mjs @@ -0,0 +1,222 @@ +import easyship from "../../easyship.app.mjs"; + +export default { + key: "easyship-create-shipment", + name: "Create Shipment", + description: "Create a new shipment in Easyship. [See the docs](https://developers.easyship.com/reference/shipments_create)", + version: "0.0.1", + type: "action", + props: { + easyship, + originontactName: { + type: "string", + label: "Origin Name", + description: "The full name of a person at the origin address", + }, + originContactEmail: { + type: "string", + label: "Origin Email", + description: "Email address used to reach the person in `Origin Name`", + }, + originContactPhone: { + type: "string", + label: "Origin Phone Number", + description: "Phone number used to reach the person in `Origin Name` (may or may not be SMS-ready)", + }, + originCompanyName: { + type: "string", + label: "Origin Company Name", + description: "The company or organization at the originaddress", + }, + originLine1: { + type: "string", + label: "Origin Street Address", + description: "Street address of the origin address", + }, + originCity: { + type: "string", + label: "Origin City", + description: "City of the origin address", + }, + originState: { + type: "string", + label: "Origin State", + description: "State, Province, or other top-level administrative region of the origin address", + }, + originPostalCode: { + type: "string", + label: "Origin Postal Code", + description: "Postal code of the origin address", + }, + originCountry: { + type: "string", + label: "Origin Country (Alpha-2 Code)", + description: "ISO 3166-1 alpha-2 code of the origin country", + optional: true, + }, + destinationName: { + type: "string", + label: "Destination Name", + description: "The full name of a person at the destination address.", + }, + destinationEmail: { + type: "string", + label: "Destination Email", + description: "Email address used to reach the person at the destination address.", + }, + destinationPhoneNumber: { + type: "string", + label: "Destination Phone Number", + description: "Phone number used to reach the person at the destination address (may or may not be SMS-ready).", + }, + destinationCompanyName: { + type: "string", + label: "Destination Company Name", + description: "The company or organization at the destination address.", + optional: true, + }, + destinationStreetAddress: { + type: "string", + label: "Destination Street Address", + description: "Street address of the destination address.", + }, + destinationCity: { + type: "string", + label: "Destination City", + description: "City of the destination address.", + }, + destinationState: { + type: "string", + label: "Destination State", + description: "State, Province, or other top-level administrative region of the destination address.", + }, + destinationPostalCode: { + type: "string", + label: "Destination Postal Code", + description: "Postal code of the destination address.", + }, + destinationCountry: { + type: "string", + label: "Destination Country (Alpha-2 Code)", + description: "ISO 3166-1 alpha-2 code of the destination country.", + }, + numberOfParcels: { + type: "integer", + label: "Number of Parcels", + description: "The number of parcels to ship", + reloadProps: true, + }, + }, + async additionalProps() { + const props = {}; + if (this.numberOfParcels > 0) { + for (let i = 1; i <= this.numberOfParcels; i++) { + props[`parcelWeight${i}`] = { + type: "string", + label: `Parcel ${i} Weight (kg)`, + description: `Item actual weight in kg of parcel ${i}`, + }; + props[`parcelLength${i}`] = { + type: "string", + label: `Parcel ${i} Length (cm)`, + description: `Length of parcel ${i}`, + }; + props[`parcelWidth${i}`] = { + type: "string", + label: `Parcel ${i} Width (cm)`, + description: `Width of parcel ${i}`, + }; + props[`parcelHeight${i}`] = { + type: "string", + label: `Parcel ${i} Height (cm)`, + description: `Height of parcel ${i}`, + }; + props[`parcelDescription${i}`] = { + type: "string", + label: `Parcel ${i} Description`, + description: `Description of parcel ${i}`, + }; + props[`parcelValue${i}`] = { + type: "string", + label: `Parcel ${i} Value`, + description: `Value of parcel ${i}`, + }; + props[`parcelCurrency${i}`] = { + type: "string", + label: `Parcel ${i} Currency`, + description: `Currency of parcel ${i} value`, + }; + props[`parcelCategory${i}`] = { + type: "string", + label: `Parcel ${i} Category`, + description: `Category of parcel ${i}`, + options: await this.getCategoriesOptions(), + }; + } + } + return props; + }, + methods: { + async getCategoriesOptions() { + const categories = await this.easyship.getPaginatedResources({ + fn: this.easyship.listCategories, + resourceKey: "item_categories", + }); + return categories.map((c) => ({ + label: c.name, + value: c.slug, + })); + }, + }, + async run({ $ }) { + const parcelItems = []; + for (let i = 1; i <= this.numberOfParcels; i++) { + parcelItems.push({ + dimensions: { + length: this[`parcelLength${i}`], + width: this[`parcelWidth${i}`], + height: this[`parcelHeight${i}`], + }, + actual_weight: this[`parcelWeight${i}`], + description: this[`parcelDescription${i}`], + declared_customs_value: this[`parcelValue${i}`], + declared_currency: this[`parcelCurrency${i}`] || "USD", + category: this[`parcelCategory${i}`], + }); + } + const response = await this.easyship.createShipment({ + $, + data: { + origin_address: { + contact_name: this.originontactName, + contact_email: this.originContactEmail, + contact_phone: this.originContactPhone, + company_name: this.originCompanyName, + line_1: this.originLine1, + city: this.originCity, + state: this.originState, + postal_code: this.originPostalCode, + country_alpha2: this.originCountry, + }, + destination_address: { + contact_name: this.destinationName, + contact_email: this.destinationEmail, + contact_phone: this.destinationPhoneNumber, + company_name: this.destinationCompanyName, + line_1: this.destinationStreetAddress, + city: this.destinationCity, + state: this.destinationState, + postal_code: this.destinationPostalCode, + country_alpha2: this.destinationCountry, + }, + parcels: [ + { + items: parcelItems, + }, + ], + }, + }); + $.export("$summary", `Created shipment with ID: ${response.shipment.easyship_shipment_id}`); + return response; + }, +}; diff --git a/components/easyship/actions/find-shipment/find-shipment.mjs b/components/easyship/actions/find-shipment/find-shipment.mjs new file mode 100644 index 0000000000000..bd4e6af6dc022 --- /dev/null +++ b/components/easyship/actions/find-shipment/find-shipment.mjs @@ -0,0 +1,26 @@ +import easyship from "../../easyship.app.mjs"; + +export default { + key: "easyship-find-shipment", + name: "Find Shipment", + description: "Find a shipment by ID. [See the documentation](https://developers.easyship.com/reference/shipments_index)", + version: "0.0.1", + type: "action", + props: { + easyship, + shipmentId: { + propDefinition: [ + easyship, + "shipmentId", + ], + }, + }, + async run({ $ }) { + const response = await this.easyship.getShipment({ + $, + shipmentId: this.shipmentId, + }); + $.export("$summary", `Found shipment with ID: ${this.shipmentId}`); + return response; + }, +}; diff --git a/components/easyship/app/easyship.app.ts b/components/easyship/app/easyship.app.ts deleted file mode 100644 index 0ab3d6a959e08..0000000000000 --- a/components/easyship/app/easyship.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "easyship", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); diff --git a/components/easyship/easyship.app.mjs b/components/easyship/easyship.app.mjs new file mode 100644 index 0000000000000..2ef1fa888f1ce --- /dev/null +++ b/components/easyship/easyship.app.mjs @@ -0,0 +1,121 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "easyship", + propDefinitions: { + shipmentId: { + type: "string", + label: "Shipment ID", + description: "The ID of the shipment to find", + async options({ page }) { + const { shipments } = await this.listShipments({ + params: { + page: page + 1, + per_page: 100, + }, + }); + return shipments.map((shipment) => shipment.easyship_shipment_id ); + }, + }, + }, + methods: { + _baseUrl() { + return "https://public-api.easyship.com/2023-01"; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: { + Authorization: `Bearer ${this.$auth.api_token}`, + }, + ...opts, + }); + }, + createWebhook(opts = {}) { + return this._makeRequest({ + path: "/webhooks", + method: "POST", + ...opts, + }); + }, + activateWebhook({ + webhookId, ...opts + }) { + return this._makeRequest({ + path: `/webhooks/${webhookId}/activate`, + method: "POST", + ...opts, + }); + }, + deleteWebhook({ + webhookId, ...opts + }) { + return this._makeRequest({ + path: `/webhooks/${webhookId}`, + method: "DELETE", + ...opts, + }); + }, + getShipment({ + shipmentId, ...opts + }) { + return this._makeRequest({ + path: `/shipments/${shipmentId}`, + ...opts, + }); + }, + listCategories(opts = {}) { + return this._makeRequest({ + path: "/item_categories", + ...opts, + }); + }, + listShipments(opts = {}) { + return this._makeRequest({ + path: "/shipments", + ...opts, + }); + }, + createShipment(opts = {}) { + return this._makeRequest({ + path: "/shipments", + method: "POST", + ...opts, + }); + }, + async *paginate({ + fn, params, resourceKey, max, + }) { + params = { + ...params, + page: 1, + per_page: 100, + }; + let total, count = 0; + do { + const response = await fn({ + params, + }); + const items = response[resourceKey]; + for (const item of items) { + yield item; + if (max && count >= max) { + return; + } + } + params.page++; + total = items.length; + } while (total === params.per_page); + }, + async getPaginatedResources(opts = {}) { + const results = []; + for await (const item of this.paginate(opts)) { + results.push(item); + } + return results; + }, + }, +}; diff --git a/components/easyship/package.json b/components/easyship/package.json index c30333f01557e..cf29a81ef4f4a 100644 --- a/components/easyship/package.json +++ b/components/easyship/package.json @@ -1,16 +1,18 @@ { "name": "@pipedream/easyship", - "version": "0.0.2", + "version": "0.1.0", "description": "Pipedream Easyship Components", - "main": "dist/app/easyship.app.mjs", + "main": "easyship.app.mjs", "keywords": [ "pipedream", "easyship" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/easyship", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/easyship/sources/common/base.mjs b/components/easyship/sources/common/base.mjs new file mode 100644 index 0000000000000..a41e32b74c55d --- /dev/null +++ b/components/easyship/sources/common/base.mjs @@ -0,0 +1,65 @@ +import easyship from "../../easyship.app.mjs"; + +export default { + props: { + easyship, + db: "$.service.db", + http: { + type: "$.interface.http", + customResponse: true, + }, + }, + hooks: { + async activate() { + const { webhook } = await this.easyship.createWebhook({ + data: { + endpoint: this.http.endpoint, + event_types: [ + this.getEventType(), + ], + version: "2023-01", + }, + }); + this._setHookId(webhook.id); + await this.easyship.activateWebhook({ + webhookId: webhook.id, + }); + }, + async deactivate() { + const hookId = this._getHookId(); + if (hookId) { + await this.easyship.deleteWebhook({ + webhookId: hookId, + }); + } + }, + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + generateMeta(body) { + return { + id: body.resource_id, + summary: `New ${body.event_type} event`, + ts: Date.now(), + }; + }, + }, + async run(event) { + const { body } = event; + if (!body) { + return; + } + + this.http.respond({ + status: 200, + }); + + const meta = this.generateMeta(body); + this.$emit(body, meta); + }, +}; diff --git a/components/easyship/sources/new-shipping-label-created/new-shipping-label-created.mjs b/components/easyship/sources/new-shipping-label-created/new-shipping-label-created.mjs new file mode 100644 index 0000000000000..3138ca9d8cf2c --- /dev/null +++ b/components/easyship/sources/new-shipping-label-created/new-shipping-label-created.mjs @@ -0,0 +1,19 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "easyship-new-shipping-label-created", + name: "New Shipping Label Created (Instant)", + description: "Emit new event when a new shipping label is created", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "shipment_label_created"; + }, + }, + sampleEmit, +}; diff --git a/components/easyship/sources/new-shipping-label-created/test-event.mjs b/components/easyship/sources/new-shipping-label-created/test-event.mjs new file mode 100644 index 0000000000000..aab756a6e75ce --- /dev/null +++ b/components/easyship/sources/new-shipping-label-created/test-event.mjs @@ -0,0 +1,13 @@ +export default { + "event_type": "shipment.label.created", + "resource_type": "shipment", + "resource_id": "ESTEST00000", + "data": { + "easyship_shipment_id": "ESTEST00000", + "platform_order_number": "#100001", + "status": "success", + "label_url": "https://goeasyship.s3.amazonaws.com/ESTEST00000_label.pdf", + "tracking_number": "12345", + "tracking_page_url": "https://www.trackmyshipment.co/shipment-tracking/ESTEST00000" + } + } \ No newline at end of file diff --git a/components/easyship/sources/tracking-status-updated/test-event.mjs b/components/easyship/sources/tracking-status-updated/test-event.mjs new file mode 100644 index 0000000000000..20b00f4fd3f43 --- /dev/null +++ b/components/easyship/sources/tracking-status-updated/test-event.mjs @@ -0,0 +1,15 @@ +export default { + "event_type": "shipment.tracking.status.changed", + "resource_type": "shipment", + "resource_id": "ESTEST00000", + "data": { + "easyship_shipment_id": "ESTEST00000", + "platform_order_number": "#100001", + "origin": "US", + "destination": "CA", + "company_order_number": "#100001", + "status": "Delivered", + "tracking_number": "12345", + "tracking_page_url": "https://www.trackmyshipment.co/shipment-tracking/ESTEST00000" + } + } \ No newline at end of file diff --git a/components/easyship/sources/tracking-status-updated/tracking-status-updated.mjs b/components/easyship/sources/tracking-status-updated/tracking-status-updated.mjs new file mode 100644 index 0000000000000..0da513a446ee6 --- /dev/null +++ b/components/easyship/sources/tracking-status-updated/tracking-status-updated.mjs @@ -0,0 +1,19 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "easyship-tracking-status-updated", + name: "Tracking Status Updated (Instant)", + description: "Emit new event when a tracking status is updated", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "shipment_tracking_status_changed"; + }, + }, + sampleEmit, +}; diff --git a/components/easyship/sources/warehouse-state-updated/test-event.mjs b/components/easyship/sources/warehouse-state-updated/test-event.mjs new file mode 100644 index 0000000000000..749446b84899b --- /dev/null +++ b/components/easyship/sources/warehouse-state-updated/test-event.mjs @@ -0,0 +1,10 @@ +export default { + "event_type": "shipment.warehouse.state.updated", + "resource_type": "shipment", + "resource_id": "ESTEST00000", + "data": { + "easyship_shipment_id": "ESTEST00000", + "platform_order_number": "#100001", + "warehouse_state": "none" + } + } \ No newline at end of file diff --git a/components/easyship/sources/warehouse-state-updated/warehouse-state-updated.mjs b/components/easyship/sources/warehouse-state-updated/warehouse-state-updated.mjs new file mode 100644 index 0000000000000..12347f6ca6e12 --- /dev/null +++ b/components/easyship/sources/warehouse-state-updated/warehouse-state-updated.mjs @@ -0,0 +1,19 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "easyship-warehouse-state-updated", + name: "Warehouse State Updated (Instant)", + description: "Emit new event when a warehouse state is updated", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "shipment_warehouse_state_updated"; + }, + }, + sampleEmit, +}; From d0cfb779dac1f15f706d0b87d1c9fe9308b9bb0c Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Fri, 23 May 2025 14:51:54 -0400 Subject: [PATCH 2/4] pnpm-lock.yaml --- pnpm-lock.yaml | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8ae5fddd1f848..cb8c69dc29132 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3995,7 +3995,11 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/easyship: {} + components/easyship: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/echtpost_postcards: dependencies: @@ -6045,8 +6049,7 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/heylibby: - specifiers: {} + components/heylibby: {} components/heyreach: {} @@ -8573,11 +8576,9 @@ importers: specifier: ^3.0.3 version: 3.0.3 - components/neetocal: - specifiers: {} + components/neetocal: {} - components/neetodesk: - specifiers: {} + components/neetodesk: {} components/neetoform: {} From e41605d27d607ef75cdfeb4e759ed05bc0355159 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Fri, 23 May 2025 15:04:10 -0400 Subject: [PATCH 3/4] updates --- .../easyship/actions/create-shipment/create-shipment.mjs | 4 ++-- components/easyship/easyship.app.mjs | 2 +- components/easyship/sources/common/base.mjs | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/components/easyship/actions/create-shipment/create-shipment.mjs b/components/easyship/actions/create-shipment/create-shipment.mjs index 3236122acd5eb..ac09e7598df06 100644 --- a/components/easyship/actions/create-shipment/create-shipment.mjs +++ b/components/easyship/actions/create-shipment/create-shipment.mjs @@ -8,7 +8,7 @@ export default { type: "action", props: { easyship, - originontactName: { + originContactName: { type: "string", label: "Origin Name", description: "The full name of a person at the origin address", @@ -188,7 +188,7 @@ export default { $, data: { origin_address: { - contact_name: this.originontactName, + contact_name: this.originContactName, contact_email: this.originContactEmail, contact_phone: this.originContactPhone, company_name: this.originCompanyName, diff --git a/components/easyship/easyship.app.mjs b/components/easyship/easyship.app.mjs index 2ef1fa888f1ce..a13bddebb6f85 100644 --- a/components/easyship/easyship.app.mjs +++ b/components/easyship/easyship.app.mjs @@ -102,7 +102,7 @@ export default { const items = response[resourceKey]; for (const item of items) { yield item; - if (max && count >= max) { + if (max && ++count >= max) { return; } } diff --git a/components/easyship/sources/common/base.mjs b/components/easyship/sources/common/base.mjs index a41e32b74c55d..de02d19f4787f 100644 --- a/components/easyship/sources/common/base.mjs +++ b/components/easyship/sources/common/base.mjs @@ -50,15 +50,15 @@ export default { }, }, async run(event) { + this.http.respond({ + status: 200, + }); + const { body } = event; if (!body) { return; } - this.http.respond({ - status: 200, - }); - const meta = this.generateMeta(body); this.$emit(body, meta); }, From 45b4c34ad8b3dad8af55e15f273c2e24964d70c4 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Mon, 26 May 2025 12:21:24 -0400 Subject: [PATCH 4/4] updates --- components/easyship/easyship.app.mjs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/components/easyship/easyship.app.mjs b/components/easyship/easyship.app.mjs index a13bddebb6f85..deeb9856f18fa 100644 --- a/components/easyship/easyship.app.mjs +++ b/components/easyship/easyship.app.mjs @@ -21,7 +21,7 @@ export default { }, methods: { _baseUrl() { - return "https://public-api.easyship.com/2023-01"; + return "https://public-api.easyship.com/2024-09"; }, _makeRequest({ $ = this, path, ...opts @@ -30,6 +30,7 @@ export default { url: `${this._baseUrl()}${path}`, headers: { Authorization: `Bearer ${this.$auth.api_token}`, + Accept: "application/json", }, ...opts, });