From e95e69aaf3ccc3ffc5230313b307f789491ccef6 Mon Sep 17 00:00:00 2001 From: Jorge Cortes Date: Thu, 16 Oct 2025 16:43:50 -0500 Subject: [PATCH] [Components] Xola - new components --- .../add-event-guide/add-event-guide.mjs | 81 ++++ .../create-experience-schedule.mjs | 140 +++++++ .../delete-experience-schedule.mjs | 71 ++++ .../xola/actions/patch-event/patch-event.mjs | 76 ++++ .../remove-event-guide/remove-event-guide.mjs | 69 ++++ .../update-experience-schedule.mjs | 153 +++++++ components/xola/common/constants.mjs | 6 + components/xola/package.json | 7 +- components/xola/sources/common/webhook.mjs | 96 +++++ .../experience-deleted-instant.mjs | 28 ++ .../experience-deleted-instant/test-event.mjs | 72 ++++ .../experience-updated-instant.mjs | 28 ++ .../experience-updated-instant/test-event.mjs | 68 ++++ .../new-experience-created-instant.mjs | 27 ++ .../test-event.mjs | 68 ++++ .../new-order-created-instant.mjs | 27 ++ .../new-order-created-instant/test-event.mjs | 21 + .../order-deleted-instant.mjs | 28 ++ .../order-deleted-instant/test-event.mjs | 8 + .../order-updated-instant.mjs | 28 ++ .../order-updated-instant/test-event.mjs | 26 ++ .../user-updated-instant/test-event.mjs | 17 + .../user-updated-instant.mjs | 28 ++ components/xola/xola.app.mjs | 378 +++++++++++++++++- pnpm-lock.yaml | 6 +- 25 files changed, 1549 insertions(+), 8 deletions(-) create mode 100644 components/xola/actions/add-event-guide/add-event-guide.mjs create mode 100644 components/xola/actions/create-experience-schedule/create-experience-schedule.mjs create mode 100644 components/xola/actions/delete-experience-schedule/delete-experience-schedule.mjs create mode 100644 components/xola/actions/patch-event/patch-event.mjs create mode 100644 components/xola/actions/remove-event-guide/remove-event-guide.mjs create mode 100644 components/xola/actions/update-experience-schedule/update-experience-schedule.mjs create mode 100644 components/xola/common/constants.mjs create mode 100644 components/xola/sources/common/webhook.mjs create mode 100644 components/xola/sources/experience-deleted-instant/experience-deleted-instant.mjs create mode 100644 components/xola/sources/experience-deleted-instant/test-event.mjs create mode 100644 components/xola/sources/experience-updated-instant/experience-updated-instant.mjs create mode 100644 components/xola/sources/experience-updated-instant/test-event.mjs create mode 100644 components/xola/sources/new-experience-created-instant/new-experience-created-instant.mjs create mode 100644 components/xola/sources/new-experience-created-instant/test-event.mjs create mode 100644 components/xola/sources/new-order-created-instant/new-order-created-instant.mjs create mode 100644 components/xola/sources/new-order-created-instant/test-event.mjs create mode 100644 components/xola/sources/order-deleted-instant/order-deleted-instant.mjs create mode 100644 components/xola/sources/order-deleted-instant/test-event.mjs create mode 100644 components/xola/sources/order-updated-instant/order-updated-instant.mjs create mode 100644 components/xola/sources/order-updated-instant/test-event.mjs create mode 100644 components/xola/sources/user-updated-instant/test-event.mjs create mode 100644 components/xola/sources/user-updated-instant/user-updated-instant.mjs diff --git a/components/xola/actions/add-event-guide/add-event-guide.mjs b/components/xola/actions/add-event-guide/add-event-guide.mjs new file mode 100644 index 0000000000000..6eab85617f45c --- /dev/null +++ b/components/xola/actions/add-event-guide/add-event-guide.mjs @@ -0,0 +1,81 @@ +import app from "../../xola.app.mjs"; + +export default { + key: "xola-add-event-guide", + name: "Add Event Guide", + description: "Adds a guide to an event. [See the documentation](https://xola.github.io/xola-docs/#tag/events/operation/addEventGuide)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + sellerId: { + propDefinition: [ + app, + "sellerId", + ], + async options() { + const { + id: value, + name: label, + } = await this.getUser(); + return [ + { + label, + value, + }, + ]; + }, + }, + eventId: { + propDefinition: [ + app, + "eventId", + ({ sellerId }) => ({ + sellerId, + }), + ], + }, + guideId: { + propDefinition: [ + app, + "guideId", + ({ sellerId }) => ({ + sellerId, + }), + ], + }, + forceConfirm: { + type: "boolean", + label: "Force Confirm", + description: "Force assignment even if guide has conflicts", + optional: true, + }, + }, + async run({ $ }) { + const { + app, + eventId, + guideId, + forceConfirm, + } = this; + + const response = await app.addEventGuide({ + $, + eventId, + data: { + guide: { + id: guideId, + forceConfirm, + }, + }, + }); + + $.export("$summary", "Successfully added guide to event"); + return response; + }, +}; diff --git a/components/xola/actions/create-experience-schedule/create-experience-schedule.mjs b/components/xola/actions/create-experience-schedule/create-experience-schedule.mjs new file mode 100644 index 0000000000000..eac3314f244cd --- /dev/null +++ b/components/xola/actions/create-experience-schedule/create-experience-schedule.mjs @@ -0,0 +1,140 @@ +import app from "../../xola.app.mjs"; + +export default { + key: "xola-create-experience-schedule", + name: "Create Experience Schedule", + description: "Creates a new schedule for an experience. [See the documentation](https://xola.github.io/xola-docs/#tag/schedules/operation/createExperienceSchedule)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + sellerId: { + propDefinition: [ + app, + "sellerId", + ], + async options() { + const { + id: value, + name: label, + } = await this.getUser(); + return [ + { + label, + value, + }, + ]; + }, + }, + experienceId: { + propDefinition: [ + app, + "experienceId", + ({ sellerId }) => ({ + sellerId, + }), + ], + }, + name: { + propDefinition: [ + app, + "name", + ], + }, + type: { + propDefinition: [ + app, + "type", + ], + }, + days: { + propDefinition: [ + app, + "days", + ], + }, + departure: { + propDefinition: [ + app, + "departure", + ], + }, + times: { + propDefinition: [ + app, + "times", + ], + }, + priceDelta: { + propDefinition: [ + app, + "priceDelta", + ], + }, + repeat: { + propDefinition: [ + app, + "repeat", + ], + }, + start: { + propDefinition: [ + app, + "start", + ], + }, + end: { + propDefinition: [ + app, + "end", + ], + }, + dates: { + propDefinition: [ + app, + "dates", + ], + }, + }, + async run({ $ }) { + const { + app, + experienceId, + name, + type, + days, + times, + departure, + priceDelta, + repeat, + start, + end, + dates, + } = this; + + const response = await app.createExperienceSchedule({ + $, + experienceId, + data: { + start, + end, + dates, + type, + name, + repeat, + days, + times, + departure, + priceDelta, + }, + }); + + $.export("$summary", `Successfully created schedule for experience with ID \`${response.id}\``); + return response; + }, +}; diff --git a/components/xola/actions/delete-experience-schedule/delete-experience-schedule.mjs b/components/xola/actions/delete-experience-schedule/delete-experience-schedule.mjs new file mode 100644 index 0000000000000..ae5c3a1e2606e --- /dev/null +++ b/components/xola/actions/delete-experience-schedule/delete-experience-schedule.mjs @@ -0,0 +1,71 @@ +import app from "../../xola.app.mjs"; + +export default { + key: "xola-delete-experience-schedule", + name: "Delete Experience Schedule", + description: "Deletes a schedule from an experience. [See the documentation](https://xola.github.io/xola-docs/#tag/schedules/operation/deleteExperienceSchedule)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + sellerId: { + propDefinition: [ + app, + "sellerId", + ], + async options() { + const { + id: value, + name: label, + } = await this.getUser(); + return [ + { + label, + value, + }, + ]; + }, + }, + experienceId: { + propDefinition: [ + app, + "experienceId", + ({ sellerId }) => ({ + sellerId, + }), + ], + }, + scheduleId: { + propDefinition: [ + app, + "scheduleId", + ({ experienceId }) => ({ + experienceId, + }), + ], + }, + }, + async run({ $ }) { + const { + app, + experienceId, + scheduleId, + } = this; + + await app.deleteExperienceSchedule({ + $, + experienceId, + scheduleId, + }); + + $.export("$summary", "Successfully deleted schedule"); + return { + success: true, + }; + }, +}; diff --git a/components/xola/actions/patch-event/patch-event.mjs b/components/xola/actions/patch-event/patch-event.mjs new file mode 100644 index 0000000000000..0e1bff845351b --- /dev/null +++ b/components/xola/actions/patch-event/patch-event.mjs @@ -0,0 +1,76 @@ +import app from "../../xola.app.mjs"; + +export default { + key: "xola-patch-event", + name: "Patch Event", + description: "Partially update an event's properties. [See the documentation](https://xola.github.io/xola-docs/#tag/events/operation/patchEvent)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + sellerId: { + propDefinition: [ + app, + "sellerId", + ], + async options() { + const { + id: value, + name: label, + } = await this.getUser(); + return [ + { + label, + value, + }, + ]; + }, + }, + eventId: { + propDefinition: [ + app, + "eventId", + ({ sellerId }) => ({ + sellerId, + }), + ], + }, + max: { + type: "integer", + label: "Max", + description: "Maximum capacity override", + min: 0, + }, + manual: { + type: "boolean", + label: "Manual", + description: "Flag to reopen a closed event as manual", + optional: true, + }, + }, + async run({ $ }) { + const { + app, + eventId, + max, + manual, + } = this; + + const response = await app.patchEvent({ + $, + eventId, + data: { + max, + manual, + }, + }); + + $.export("$summary", "Successfully patched event"); + return response; + }, +}; diff --git a/components/xola/actions/remove-event-guide/remove-event-guide.mjs b/components/xola/actions/remove-event-guide/remove-event-guide.mjs new file mode 100644 index 0000000000000..6e72f752dc854 --- /dev/null +++ b/components/xola/actions/remove-event-guide/remove-event-guide.mjs @@ -0,0 +1,69 @@ +import app from "../../xola.app.mjs"; + +export default { + key: "xola-remove-event-guide", + name: "Remove Event Guide", + description: "Removes a guide from an event. [See the documentation](https://xola.github.io/xola-docs/#tag/events/operation/removeEventGuide)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + sellerId: { + propDefinition: [ + app, + "sellerId", + ], + async options() { + const { + id: value, + name: label, + } = await this.getUser(); + return [ + { + label, + value, + }, + ]; + }, + }, + eventId: { + propDefinition: [ + app, + "eventId", + ({ sellerId }) => ({ + sellerId, + }), + ], + }, + guideId: { + propDefinition: [ + app, + "guideId", + ({ sellerId }) => ({ + sellerId, + }), + ], + }, + }, + async run({ $ }) { + const { + app, + eventId, + guideId, + } = this; + + const response = await app.removeEventGuide({ + $, + eventId, + guideId, + }); + + $.export("$summary", "Successfully removed guide from event"); + return response; + }, +}; diff --git a/components/xola/actions/update-experience-schedule/update-experience-schedule.mjs b/components/xola/actions/update-experience-schedule/update-experience-schedule.mjs new file mode 100644 index 0000000000000..f5bf10e31bc27 --- /dev/null +++ b/components/xola/actions/update-experience-schedule/update-experience-schedule.mjs @@ -0,0 +1,153 @@ +import app from "../../xola.app.mjs"; + +export default { + key: "xola-update-experience-schedule", + name: "Update Experience Schedule", + description: "Updates an existing schedule for an experience. [See the documentation](https://xola.github.io/xola-docs/#tag/schedules/operation/updateExperienceSchedule)", + version: "0.0.1", + type: "action", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + sellerId: { + propDefinition: [ + app, + "sellerId", + ], + async options() { + const { + id: value, + name: label, + } = await this.getUser(); + return [ + { + label, + value, + }, + ]; + }, + }, + experienceId: { + propDefinition: [ + app, + "experienceId", + ({ sellerId }) => ({ + sellerId, + }), + ], + }, + scheduleId: { + propDefinition: [ + app, + "scheduleId", + ({ experienceId }) => ({ + experienceId, + }), + ], + }, + name: { + optional: true, + propDefinition: [ + app, + "name", + ], + }, + type: { + optional: true, + propDefinition: [ + app, + "type", + ], + }, + days: { + propDefinition: [ + app, + "days", + ], + }, + departure: { + propDefinition: [ + app, + "departure", + ], + }, + times: { + propDefinition: [ + app, + "times", + ], + }, + priceDelta: { + propDefinition: [ + app, + "priceDelta", + ], + }, + repeat: { + propDefinition: [ + app, + "repeat", + ], + }, + start: { + propDefinition: [ + app, + "start", + ], + }, + end: { + propDefinition: [ + app, + "end", + ], + }, + dates: { + propDefinition: [ + app, + "dates", + ], + }, + }, + async run({ $ }) { + const { + app, + experienceId, + scheduleId, + name, + type, + days, + times, + departure, + priceDelta, + repeat, + start, + end, + dates, + } = this; + + const response = await app.updateExperienceSchedule({ + $, + experienceId, + scheduleId, + data: { + start, + end, + dates, + type, + name, + repeat, + days, + times, + departure, + priceDelta, + }, + }); + + $.export("$summary", `Successfully updated schedule \`${scheduleId}\` for experience with ID \`${experienceId}\``); + return response; + }, +}; diff --git a/components/xola/common/constants.mjs b/components/xola/common/constants.mjs new file mode 100644 index 0000000000000..98a3700107f4f --- /dev/null +++ b/components/xola/common/constants.mjs @@ -0,0 +1,6 @@ +export default { + VERSION_PATH: "/api", + API_VERSION: "2025-07-07", + DEFAULT_LIMIT: 20, + DEFAULT_MAX: 100, +}; diff --git a/components/xola/package.json b/components/xola/package.json index 3bb32c4ed2bc6..677467f470430 100644 --- a/components/xola/package.json +++ b/components/xola/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/xola", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Xola Components", "main": "xola.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.1.0" } -} \ No newline at end of file +} diff --git a/components/xola/sources/common/webhook.mjs b/components/xola/sources/common/webhook.mjs new file mode 100644 index 0000000000000..d4c0c27dc3403 --- /dev/null +++ b/components/xola/sources/common/webhook.mjs @@ -0,0 +1,96 @@ +import { ConfigurationError } from "@pipedream/platform"; +import app from "../../xola.app.mjs"; + +export default { + props: { + app, + db: "$.service.db", + http: "$.interface.http", + userId: { + propDefinition: [ + app, + "sellerId", + ], + label: "User ID", + description: "The unique identifier of the user", + async options() { + const { + id: value, + name: label, + } = await this.getUser(); + return [ + { + label, + value, + }, + ]; + }, + }, + }, + hooks: { + async activate() { + const { + userId, + getEventName, + http: { endpoint: url }, + createWebhook, + } = this; + + const { id } = await createWebhook({ + userId, + data: { + url, + eventName: getEventName(), + }, + }); + this.setWebhookId(id); + }, + async deactivate() { + const { + userId, + getWebhookId, + deleteWebhook, + } = this; + const webhookId = getWebhookId(); + if (webhookId) { + await deleteWebhook({ + webhookId, + userId, + }); + } + }, + }, + methods: { + setWebhookId(value) { + this.db.set("webhookId", value); + }, + getWebhookId() { + return this.db.get("webhookId"); + }, + getEventName() { + throw new ConfigurationError("getEventName is not implemented"); + }, + generateMeta() { + throw new ConfigurationError("generateMeta is not implemented"); + }, + createWebhook({ + userId, ...args + }) { + return this.app.post({ + path: `/users/${userId}/hooks`, + ...args, + }); + }, + deleteWebhook({ + userId, webhookId, ...args + }) { + return this.app.delete({ + path: `/users/${userId}/hooks/${webhookId}`, + ...args, + }); + }, + }, + async run({ body }) { + this.$emit(body, this.generateMeta(body)); + }, +}; diff --git a/components/xola/sources/experience-deleted-instant/experience-deleted-instant.mjs b/components/xola/sources/experience-deleted-instant/experience-deleted-instant.mjs new file mode 100644 index 0000000000000..09b565013ad71 --- /dev/null +++ b/components/xola/sources/experience-deleted-instant/experience-deleted-instant.mjs @@ -0,0 +1,28 @@ +import common from "../common/webhook.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "xola-experience-deleted-instant", + name: "Experience Deleted (Instant)", + description: "Emit new event when an experience is deleted. [See the documentation](https://developers.xola.com/reference/webhook-introduction)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventName() { + return "experience.delete"; + }, + generateMeta(body) { + const { data } = body; + const ts = Date.now(); + return { + id: `${data.id}-${ts}`, + summary: `Experience Deleted ${data.name}`, + ts, + }; + }, + }, + sampleEmit, +}; diff --git a/components/xola/sources/experience-deleted-instant/test-event.mjs b/components/xola/sources/experience-deleted-instant/test-event.mjs new file mode 100644 index 0000000000000..5c6e86f632de5 --- /dev/null +++ b/components/xola/sources/experience-deleted-instant/test-event.mjs @@ -0,0 +1,72 @@ +export default { + "eventName": "experience.delete", + "data": { + "object": "experience", + "id": "5f7a9b8c1e2d3f4a5b6c7d8e", + "name": "Sunset Kayaking Adventure", + "desc": "Experience the beauty of our coastal waters during golden hour. Perfect for beginners and experienced paddlers alike.", + "currency": "USD", + "seller": { + "id": "5f7a9b8c1e2d3f4a5b6c7d8f" + }, + "catalog": { + "items": [], + "terms": [], + "promotions": [], + "priceTiers": [] + }, + "status": "deleted", + "medias": [], + "deletedAt": "2025-10-21T18:30:00+00:00", + "deletedBy": { + "id": "5f7a9b8c1e2d3f4a5b6c7d8f" + }, + "guestType": "normal", + "paymentMethod": "cc", + "addOns": [], + "sortDemographics": false, + "excerpt": "Experience the beauty of our coastal waters during golden hour.", + "group": { + "orderMin": 1, + "outingMin": 1, + "outingMinCutoff": 3600 + }, + "requireCCForSources": [], + "updated": "2025-10-21T18:30:00+00:00", + "category": null, + "complete": true, + "reviewed": false, + "requireAdult": false, + "earlyReturn": false, + "priceSchemes": [], + "pickupAddress": "123 Harbor View Dr, Marina Bay, CA 94102", + "isScheduled": true, + "geo": { + "lat": 37.8044, + "lng": -122.2712 + }, + "included": "All kayaking equipment, life jackets, paddles, safety briefing", + "notIncluded": "Transportation, food and beverages, gratuity", + "attachments": [], + "resources": [], + "demographics": [], + "schedules": [], + "virtualMeeting": { + "enabled": false, + "startOffset": 30, + "blockEntryOffset": 15, + "payment": "full" + }, + "customerNamePreference": "required", + "balanceDueReminder": false, + "requireGuestIdentification": true, + "duration": 1, + "eventDuration": 1440, + "allowedPrivacies": [ + "public" + ], + "balanceDueOffset": 0, + "scheduleType": "day", + "visible": true + } +}; diff --git a/components/xola/sources/experience-updated-instant/experience-updated-instant.mjs b/components/xola/sources/experience-updated-instant/experience-updated-instant.mjs new file mode 100644 index 0000000000000..5944c16f48c9a --- /dev/null +++ b/components/xola/sources/experience-updated-instant/experience-updated-instant.mjs @@ -0,0 +1,28 @@ +import common from "../common/webhook.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "xola-experience-updated-instant", + name: "Experience Updated (Instant)", + description: "Emit new event when an experience is updated. [See the documentation](https://developers.xola.com/reference/webhook-introduction)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventName() { + return "experience.update"; + }, + generateMeta(body) { + const { data } = body; + const ts = Date.now(); + return { + id: `${data.id}-${ts}`, + summary: `Experience Updated ${data.name}`, + ts, + }; + }, + }, + sampleEmit, +}; diff --git a/components/xola/sources/experience-updated-instant/test-event.mjs b/components/xola/sources/experience-updated-instant/test-event.mjs new file mode 100644 index 0000000000000..e72c232f26401 --- /dev/null +++ b/components/xola/sources/experience-updated-instant/test-event.mjs @@ -0,0 +1,68 @@ +export default { + "eventName": "experience.update", + "data": { + "object": "experience", + "id": "5f8b3c9d2e4f5a6b7c8d9e0f", + "name": "Mountain Hiking Expedition", + "desc": "Join us for an unforgettable hiking adventure through scenic mountain trails. Suitable for intermediate to advanced hikers.", + "currency": "USD", + "seller": { + "id": "5f8b3c9d2e4f5a6b7c8d9e10" + }, + "catalog": { + "items": [], + "terms": [], + "promotions": [], + "priceTiers": [] + }, + "status": "active", + "medias": [], + "guestType": "normal", + "paymentMethod": "cc", + "addOns": [], + "sortDemographics": false, + "excerpt": "Join us for an unforgettable hiking adventure through scenic mountain trails.", + "group": { + "orderMin": 1, + "outingMin": 1, + "outingMinCutoff": 3600 + }, + "requireCCForSources": [], + "updated": "2025-10-21T16:45:30+00:00", + "category": null, + "complete": true, + "reviewed": true, + "requireAdult": false, + "earlyReturn": false, + "priceSchemes": [], + "pickupAddress": "456 Mountain Trail Rd, Boulder, CO 80302", + "isScheduled": true, + "geo": { + "lat": 40.0150, + "lng": -105.2705 + }, + "included": "Professional guide, hiking poles, trail snacks, water bottles", + "notIncluded": "Personal hiking boots, lunch, insurance, transportation", + "attachments": [], + "resources": [], + "demographics": [], + "schedules": [], + "virtualMeeting": { + "enabled": false, + "startOffset": 30, + "blockEntryOffset": 15, + "payment": "full" + }, + "customerNamePreference": "required", + "balanceDueReminder": false, + "requireGuestIdentification": true, + "duration": 1, + "eventDuration": 1440, + "allowedPrivacies": [ + "public" + ], + "balanceDueOffset": 0, + "scheduleType": "day", + "visible": true + } +}; diff --git a/components/xola/sources/new-experience-created-instant/new-experience-created-instant.mjs b/components/xola/sources/new-experience-created-instant/new-experience-created-instant.mjs new file mode 100644 index 0000000000000..57c7889ca5c4f --- /dev/null +++ b/components/xola/sources/new-experience-created-instant/new-experience-created-instant.mjs @@ -0,0 +1,27 @@ +import common from "../common/webhook.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "xola-new-experience-created-instant", + name: "New Experience Created (Instant)", + description: "Emit new event when a new experience is created. [See the documentation](https://developers.xola.com/reference/webhook-introduction)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventName() { + return "experience.create"; + }, + generateMeta(body) { + const { data } = body; + return { + id: data.id, + summary: `New Experience ${data.name}`, + ts: Date.now(), + }; + }, + }, + sampleEmit, +}; diff --git a/components/xola/sources/new-experience-created-instant/test-event.mjs b/components/xola/sources/new-experience-created-instant/test-event.mjs new file mode 100644 index 0000000000000..7ea513a8c5bd6 --- /dev/null +++ b/components/xola/sources/new-experience-created-instant/test-event.mjs @@ -0,0 +1,68 @@ +export default { + "eventName": "experience.create", + "data": { + "object": "experience", + "id": "5f9c4d0e3f5a6b7c8d9e0f1a", + "name": "Wine Tasting & Vineyard Tour", + "desc": "Discover the art of winemaking with our exclusive vineyard tour and tasting experience. Sample premium wines while learning about the production process.", + "currency": "USD", + "seller": { + "id": "5f9c4d0e3f5a6b7c8d9e0f1b" + }, + "catalog": { + "items": [], + "terms": [], + "promotions": [], + "priceTiers": [] + }, + "status": "draft", + "medias": [], + "guestType": "normal", + "paymentMethod": "cc", + "addOns": [], + "sortDemographics": false, + "excerpt": "Discover the art of winemaking with our exclusive vineyard tour and tasting experience.", + "group": { + "orderMin": 1, + "outingMin": 1, + "outingMinCutoff": 3600 + }, + "requireCCForSources": [], + "updated": "2025-10-21T10:15:00+00:00", + "category": null, + "complete": true, + "reviewed": false, + "requireAdult": true, + "earlyReturn": false, + "priceSchemes": [], + "pickupAddress": "789 Vineyard Lane, Napa Valley, CA 94558", + "isScheduled": true, + "geo": { + "lat": 38.2975, + "lng": -122.2869 + }, + "included": "Guided tour, wine tasting of 5 premium wines, cheese pairing, souvenir glass", + "notIncluded": "Hotel pickup, additional wine purchases, gratuity", + "attachments": [], + "resources": [], + "demographics": [], + "schedules": [], + "virtualMeeting": { + "enabled": false, + "startOffset": 30, + "blockEntryOffset": 15, + "payment": "full" + }, + "customerNamePreference": "required", + "balanceDueReminder": false, + "requireGuestIdentification": true, + "duration": 1, + "eventDuration": 180, + "allowedPrivacies": [ + "public" + ], + "balanceDueOffset": 0, + "scheduleType": "day", + "visible": true + } +}; diff --git a/components/xola/sources/new-order-created-instant/new-order-created-instant.mjs b/components/xola/sources/new-order-created-instant/new-order-created-instant.mjs new file mode 100644 index 0000000000000..d68e7e5ee1e8c --- /dev/null +++ b/components/xola/sources/new-order-created-instant/new-order-created-instant.mjs @@ -0,0 +1,27 @@ +import common from "../common/webhook.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "xola-new-order-created-instant", + name: "New Order Created (Instant)", + description: "Emit new event when a new order is created. [See the documentation](https://developers.xola.com/reference/webhook-introduction)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventName() { + return "order.create"; + }, + generateMeta(body) { + const { data } = body; + return { + id: data.id, + summary: `New Order ${data.id}`, + ts: Date.now(), + }; + }, + }, + sampleEmit, +}; diff --git a/components/xola/sources/new-order-created-instant/test-event.mjs b/components/xola/sources/new-order-created-instant/test-event.mjs new file mode 100644 index 0000000000000..7f84aef1f813e --- /dev/null +++ b/components/xola/sources/new-order-created-instant/test-event.mjs @@ -0,0 +1,21 @@ +export default { + eventName: "order.create", + data: { + id: "507f1f77bcf86cd799439011", + status: "active", + amount: 12000, + currency: "USD", + experience: { + id: "507f1f77bcf86cd799439012", + name: "City Walking Tour", + }, + user: { + id: "507f1f77bcf86cd799439013", + name: "John Doe", + email: "john@example.com", + }, + createdAt: "2024-01-15T10:30:00Z", + updatedAt: "2024-01-15T10:30:00Z", + }, + audit: {}, +}; diff --git a/components/xola/sources/order-deleted-instant/order-deleted-instant.mjs b/components/xola/sources/order-deleted-instant/order-deleted-instant.mjs new file mode 100644 index 0000000000000..8e7ba97a5987f --- /dev/null +++ b/components/xola/sources/order-deleted-instant/order-deleted-instant.mjs @@ -0,0 +1,28 @@ +import common from "../common/webhook.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "xola-order-deleted-instant", + name: "Order Deleted (Instant)", + description: "Emit new event when an order is deleted. [See the documentation](https://developers.xola.com/reference/webhook-introduction)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventName() { + return "order.delete"; + }, + generateMeta(body) { + const { data } = body; + const ts = Date.now(); + return { + id: `${data.id}-${ts}`, + summary: `Order Deleted ${data.id}`, + ts, + }; + }, + }, + sampleEmit, +}; diff --git a/components/xola/sources/order-deleted-instant/test-event.mjs b/components/xola/sources/order-deleted-instant/test-event.mjs new file mode 100644 index 0000000000000..022571f7466ca --- /dev/null +++ b/components/xola/sources/order-deleted-instant/test-event.mjs @@ -0,0 +1,8 @@ +export default { + eventName: "order.delete", + data: { + id: "507f1f77bcf86cd799439011", + status: "canceled", + }, + audit: {}, +}; diff --git a/components/xola/sources/order-updated-instant/order-updated-instant.mjs b/components/xola/sources/order-updated-instant/order-updated-instant.mjs new file mode 100644 index 0000000000000..770bd770851c4 --- /dev/null +++ b/components/xola/sources/order-updated-instant/order-updated-instant.mjs @@ -0,0 +1,28 @@ +import common from "../common/webhook.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "xola-order-updated-instant", + name: "Order Updated (Instant)", + description: "Emit new event when an order is updated. [See the documentation](https://developers.xola.com/reference/webhook-introduction)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventName() { + return "order.update"; + }, + generateMeta(body) { + const { data } = body; + const ts = Date.now(); + return { + id: `${data.id}-${ts}`, + summary: `Order Updated ${data.id}`, + ts, + }; + }, + }, + sampleEmit, +}; diff --git a/components/xola/sources/order-updated-instant/test-event.mjs b/components/xola/sources/order-updated-instant/test-event.mjs new file mode 100644 index 0000000000000..9982edfad9aac --- /dev/null +++ b/components/xola/sources/order-updated-instant/test-event.mjs @@ -0,0 +1,26 @@ +export default { + eventName: "order.update", + data: { + id: "507f1f77bcf86cd799439011", + status: "confirmed", + amount: 12000, + currency: "USD", + experience: { + id: "507f1f77bcf86cd799439012", + name: "City Walking Tour", + }, + user: { + id: "507f1f77bcf86cd799439013", + name: "John Doe", + email: "john@example.com", + }, + createdAt: "2024-01-15T10:30:00Z", + updatedAt: "2024-01-15T14:45:00Z", + }, + audit: { + status: { + old: "active", + new: "confirmed", + }, + }, +}; diff --git a/components/xola/sources/user-updated-instant/test-event.mjs b/components/xola/sources/user-updated-instant/test-event.mjs new file mode 100644 index 0000000000000..1c9abba3edbb3 --- /dev/null +++ b/components/xola/sources/user-updated-instant/test-event.mjs @@ -0,0 +1,17 @@ +export default { + eventName: "user.update", + data: { + id: "507f1f77bcf86cd799439013", + name: "John Doe", + email: "john@example.com", + phone: "+1234567890", + country: "US", + updatedAt: "2024-01-15T16:20:00Z", + }, + audit: { + phone: { + old: null, + new: "+1234567890", + }, + }, +}; diff --git a/components/xola/sources/user-updated-instant/user-updated-instant.mjs b/components/xola/sources/user-updated-instant/user-updated-instant.mjs new file mode 100644 index 0000000000000..3e4e79b3cce3a --- /dev/null +++ b/components/xola/sources/user-updated-instant/user-updated-instant.mjs @@ -0,0 +1,28 @@ +import common from "../common/webhook.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "xola-user-updated-instant", + name: "User Updated (Instant)", + description: "Emit new event when a user is updated. [See the documentation](https://developers.xola.com/reference/webhook-introduction)", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventName() { + return "user.update"; + }, + generateMeta(body) { + const { data } = body; + const ts = Date.now(); + return { + id: `${data.id}-${ts}`, + summary: `User Updated ${data.name}`, + ts, + }; + }, + }, + sampleEmit, +}; diff --git a/components/xola/xola.app.mjs b/components/xola/xola.app.mjs index 448e77fc7256d..cc5d449b9be72 100644 --- a/components/xola/xola.app.mjs +++ b/components/xola/xola.app.mjs @@ -1,11 +1,379 @@ +import { axios } from "@pipedream/platform"; +import constants from "./common/constants.mjs"; + export default { type: "app", app: "xola", - propDefinitions: {}, + propDefinitions: { + sellerId: { + type: "string", + label: "Seller ID", + description: "The unique identifier of the seller", + async options({ page }) { + const { data } = await this.listSellers({ + params: { + limit: constants.DEFAULT_LIMIT, + offset: page * constants.DEFAULT_LIMIT, + }, + }); + return data.map(({ + id: value, name, company, + }) => ({ + label: company || name, + value, + })); + }, + }, + experienceId: { + type: "string", + label: "Experience ID", + description: "The unique identifier of the experience", + async options({ + page, sellerId, + }) { + try { + const { data } = await this.listExperiences({ + params: { + limit: constants.DEFAULT_LIMIT, + skip: page * constants.DEFAULT_LIMIT, + seller: sellerId, + }, + }); + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + + } catch (error) { + console.error(error); + return []; + } + }, + }, + eventId: { + type: "string", + label: "Event ID", + description: "The unique identifier of the event", + async options({ + page, sellerId, + }) { + const data = await this.listEvents({ + params: { + seller: sellerId, + limit: constants.DEFAULT_LIMIT, + skip: page * constants.DEFAULT_LIMIT, + }, + }); + return data.map(({ + id: value, title: label, + }) => ({ + label, + value, + })); + }, + }, + scheduleId: { + type: "string", + label: "Schedule ID", + description: "The unique identifier of the schedule", + async options({ experienceId }) { + if (!experienceId) { + return []; + } + const experience = await this.getExperience({ + experienceId, + params: { + expand: "schedules", + }, + }); + const schedules = experience?.schedules || []; + return schedules.map(({ + id: value, name, type, + }) => ({ + label: name || `${type} schedule`, + value, + })); + }, + }, + guideId: { + type: "string", + label: "Guide ID", + description: "The unique identifier of the guide", + async options({ + page, sellerId, + }) { + const { data } = await this.listGuides({ + sellerId, + params: { + limit: constants.DEFAULT_LIMIT, + skip: page * constants.DEFAULT_LIMIT, + }, + }); + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + name: { + type: "string", + label: "Name", + description: "Name for this schedule", + }, + type: { + type: "string", + label: "Type", + description: "Can be for an open (`available`) schedule or a `blackout` schedule", + options: [ + "available", + "blackout", + ], + }, + days: { + type: "string[]", + label: "Days", + description: "Days of week", + optional: true, + options: [ + { + label: "Sunday", + value: "0", + }, + { + label: "Monday", + value: "1", + }, + { + label: "Tuesday", + value: "2", + }, + { + label: "Wednesday", + value: "3", + }, + { + label: "Thursday", + value: "4", + }, + { + label: "Friday", + value: "5", + }, + { + label: "Saturday", + value: "6", + }, + ], + }, + departure: { + type: "string", + label: "Departure", + description: "Whether departure time is fixed or varies", + options: [ + "fixed", + "varies", + ], + optional: true, + }, + times: { + type: "string[]", + label: "Times", + description: "Start times in HHMM format (e.g., `900` = 9:00 AM, `1400` = 2:00 PM, `1800` = 6:00 PM).", + options: [ + { + label: "9:00 AM", + value: "900", + }, + { + label: "2:00 PM", + value: "1400", + }, + { + label: "6:00 PM", + value: "1800", + }, + ], + optional: true, + }, + priceDelta: { + type: "string", + label: "Price Delta", + description: "Price adjustment for this schedule (can be positive or negative). Only available when **Type** is `available`", + optional: true, + }, + repeat: { + type: "string", + label: "Repeat", + description: "When and how the schedule should repeat. Options are `weekly` (repeat the same schedule every week until **End**, if specified), or custom, which can be used in conjunction with **Dates** to specify individual days for the schedule to run", + optional: true, + options: [ + "weekly", + "custom", + ], + }, + start: { + type: "string", + label: "Start", + description: "Start date of the schedule in ISO 8601 format. Example: `2024-01-01T00:00:00Z`", + optional: true, + }, + end: { + type: "string", + label: "End", + description: "End date of the schedule in ISO 8601 format. Example: `2024-12-31T23:59:59Z`", + optional: true, + }, + dates: { + type: "string[]", + label: "Dates", + description: "Specific dates when this schedule applies. Only available when **Repeat** is `custom`. Format is `YYYY-MM-DD`. Cannot be combined with **End**.", + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + getUrl(path) { + const { api_url: apiUrl } = this.$auth; + return `${apiUrl}${constants.VERSION_PATH}${path}`; + }, + getHeaders(headers) { + return { + "x-api-key": this.$auth.api_key, + "x-api-version": constants.API_VERSION, + ...headers, + }; + }, + makeRequest({ + $ = this, path, headers, ...opts + }) { + return axios($, { + url: this.getUrl(path), + headers: this.getHeaders(headers), + ...opts, + }); + }, + post(args = {}) { + return this.makeRequest({ + method: "POST", + ...args, + }); + }, + put(args = {}) { + return this.makeRequest({ + method: "PUT", + ...args, + }); + }, + patch(args = {}) { + return this.makeRequest({ + method: "PATCH", + ...args, + }); + }, + delete(args = {}) { + return this.makeRequest({ + method: "DELETE", + ...args, + }); + }, + listExperiences(args = {}) { + return this.makeRequest({ + path: "/experiences", + ...args, + }); + }, + getExperience({ + experienceId, ...args + }) { + return this.makeRequest({ + path: `/experiences/${experienceId}`, + ...args, + }); + }, + listGuides({ + sellerId, ...args + } = {}) { + return this.makeRequest({ + path: `/sellers/${sellerId}/guides`, + ...args, + }); + }, + listEvents(args = {}) { + return this.makeRequest({ + path: "/events", + ...args, + }); + }, + listSellers(args = {}) { + return this.makeRequest({ + path: "/sellers", + ...args, + }); + }, + listPurchases(args = {}) { + return this.makeRequest({ + path: "/purchases", + ...args, + }); + }, + createExperienceSchedule({ + experienceId, ...args + }) { + return this.post({ + path: `/experiences/${experienceId}/schedules`, + ...args, + }); + }, + updateExperienceSchedule({ + experienceId, scheduleId, ...args + }) { + return this.put({ + path: `/experiences/${experienceId}/schedules/${scheduleId}`, + ...args, + }); + }, + deleteExperienceSchedule({ + experienceId, scheduleId, ...args + }) { + return this.delete({ + path: `/experiences/${experienceId}/schedules/${scheduleId}`, + ...args, + }); + }, + patchEvent({ + eventId, ...args + }) { + return this.patch({ + path: `/events/${eventId}`, + ...args, + }); + }, + addEventGuide({ + eventId, ...args + }) { + return this.post({ + path: `/events/${eventId}/guides`, + ...args, + }); + }, + removeEventGuide({ + eventId, guideId, ...args + }) { + return this.delete({ + path: `/events/${eventId}/guides/${guideId}`, + ...args, + }); + }, + getUser({ + userId = "me", ...args + } = {}) { + return this.makeRequest({ + path: `/users/${userId}`, + ...args, + }); }, }, -}; \ No newline at end of file +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f75421a991890..f398eb52e8c08 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16193,7 +16193,11 @@ importers: specifier: ^3.1.0 version: 3.1.0 - components/xola: {} + components/xola: + dependencies: + '@pipedream/platform': + specifier: ^3.1.0 + version: 3.1.0 components/xperiencify: dependencies: