From 8a4945f8af2d208a491ca502f9583865c8427878 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Thu, 28 Aug 2025 19:58:00 -0400 Subject: [PATCH 1/5] wip --- .../actions/list-bookings/list-bookings.mjs | 90 +++++++++++ .../search-contacts/search-contacts.mjs | 49 ++++++ .../booking_experts/booking_experts.app.mjs | 152 +++++++++++++++++- components/booking_experts/package.json | 7 +- .../booking-updated/booking-updated.mjs | 44 +++++ .../sources/common/base-polling.mjs | 94 +++++++++++ .../new-booking-created.mjs | 41 +++++ 7 files changed, 470 insertions(+), 7 deletions(-) create mode 100644 components/booking_experts/actions/list-bookings/list-bookings.mjs create mode 100644 components/booking_experts/actions/search-contacts/search-contacts.mjs create mode 100644 components/booking_experts/sources/booking-updated/booking-updated.mjs create mode 100644 components/booking_experts/sources/common/base-polling.mjs create mode 100644 components/booking_experts/sources/new-booking-created/new-booking-created.mjs diff --git a/components/booking_experts/actions/list-bookings/list-bookings.mjs b/components/booking_experts/actions/list-bookings/list-bookings.mjs new file mode 100644 index 0000000000000..00a46fe92bcce --- /dev/null +++ b/components/booking_experts/actions/list-bookings/list-bookings.mjs @@ -0,0 +1,90 @@ +import bookingExperts from "../../booking_experts.app.mjs"; + +export default { + key: "booking_experts-list-bookings", + name: "List Bookings", + description: "Returns a list of bookings for an administration. [See the documentation](https://developers.bookingexperts.com/reference/administration-bookings-index)", + version: "0.0.1", + type: "action", + props: { + bookingExperts, + administrationId: { + propDefinition: [ + bookingExperts, + "administrationId", + ], + }, + ownerId: { + propDefinition: [ + bookingExperts, + "ownerId", + (c) => ({ + administrationId: c.administrationId, + }), + ], + description: "Filter by owner", + }, + channelId: { + propDefinition: [ + bookingExperts, + "channelId", + (c) => ({ + administrationId: c.administrationId, + }), + ], + description: "Filter by channel", + }, + reservationId: { + propDefinition: [ + bookingExperts, + "reservationId", + (c) => ({ + administrationId: c.administrationId, + }), + ], + description: "Filter by reservation", + }, + createdAt: { + type: "string", + label: "Created At", + description: "Filter by created at date. Example: `2025-08-28`", + optional: true, + }, + updatedAt: { + type: "string", + label: "Updated At", + description: "Filter by updated at date. Example: `2025-08-28`", + optional: true, + }, + page: { + type: "integer", + label: "Page", + description: "Page number", + optional: true, + }, + perPage: { + type: "integer", + label: "Per Page", + description: "Number of items per page", + max: 100, + optional: true, + }, + }, + async run({ $ }) { + const { data } = await this.bookingExperts.listBookings({ + $, + administrationId: this.administrationId, + params: { + "filter[owner]": this.ownerId, + "filter[channel]": this.channelId, + "filter[reservations]": this.reservationId, + "filter[created_at]": this.createdAt, + "filter[updated_at]": this.updatedAt, + "page[number]": this.page, + "page[size]": this.perPage, + }, + }); + $.export("$summary", `Found ${data.length} bookings`); + return data; + }, +}; diff --git a/components/booking_experts/actions/search-contacts/search-contacts.mjs b/components/booking_experts/actions/search-contacts/search-contacts.mjs new file mode 100644 index 0000000000000..965364e829a4d --- /dev/null +++ b/components/booking_experts/actions/search-contacts/search-contacts.mjs @@ -0,0 +1,49 @@ +import bookingExperts from "../../booking_experts.app.mjs"; +import { ConfigurationError } from "@pipedream/platform"; + +export default { + key: "booking_experts-search-contacts", + name: "Search Contacts", + description: "Search for contacts by name or email. [See the documentation](https://developers.bookingexperts.com/reference/contact-search-first)", + version: "0.0.1", + type: "action", + props: { + bookingExperts, + email: { + type: "string", + label: "Email", + description: "The email of the contact to search for", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "The phone number of the contact to search for", + optional: true, + }, + }, + async run({ $ }) { + if (!this.email && !this.phone) { + throw new ConfigurationError("Either email or phone must be provided"); + } + + try { + const { data } = await this.bookingExperts.searchContacts({ + params: { + email: this.email, + phone: this.phone, + }, + }); + if (data?.id) { + $.export("$summary", "Found contact matching criteria"); + } + return data; + } catch (error) { + if (error.response?.status === 404) { + $.export("$summary", "No contact found matching criteria"); + return; + } + throw error; + } + }, +}; diff --git a/components/booking_experts/booking_experts.app.mjs b/components/booking_experts/booking_experts.app.mjs index 24507a0654538..53ec4352cb362 100644 --- a/components/booking_experts/booking_experts.app.mjs +++ b/components/booking_experts/booking_experts.app.mjs @@ -1,11 +1,153 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "booking_experts", - propDefinitions: {}, + propDefinitions: { + administrationId: { + type: "string", + label: "Administration ID", + description: "The ID of the administration", + async options({ page }) { + const { data } = await this.listAdministrations({ + params: { + "page[number]": page + 1, + }, + }); + return data?.map(({ + id, attributes, + }) => ({ + label: attributes.name, + value: id, + })) || []; + }, + }, + ownerId: { + type: "string", + label: "Owner ID", + description: "The ID of an owner", + optional: true, + async options({ + page, administrationId, + }) { + const { data } = await this.listOwners({ + administrationId, + params: { + "page[number]": page + 1, + }, + }); + return data?.map(({ + id, attributes, + }) => ({ + label: attributes.first_name + " " + attributes.last_name, + value: id, + })) || []; + }, + }, + channelId: { + type: "string", + label: "Channel ID", + description: "The ID of a channel", + optional: true, + async options({ + page, administrationId, + }) { + const { data } = await this.listChannels({ + administrationId, + params: { + "page[number]": page + 1, + }, + }); + return data?.map(({ + id, attributes, + }) => ({ + label: attributes.name, + value: id, + })) || []; + }, + }, + reservationId: { + type: "string", + label: "Reservation ID", + description: "The ID of a reservation", + optional: true, + async options({ + page, administrationId, + }) { + const { data } = await this.listReservations({ + administrationId, + params: { + "page[number]": page + 1, + }, + }); + return data?.map(({ + id, attributes, + }) => ({ + label: `${attributes.start_date} - ${attributes.end_date}`, + value: id, + })) || []; + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.bookingexperts.com/v3"; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: `${this._baseUrl()}${path}`, + headers: { + "x-api-key": `${this.$auth.api_key}`, + "accept": "application/vnd.api+json", + }, + ...opts, + }); + }, + listAdministrations(opts = {}) { + return this._makeRequest({ + path: "/administrations", + ...opts, + }); + }, + listBookings({ + administrationId, ...opts + }) { + return this._makeRequest({ + path: `/administrations/${administrationId}/bookings`, + ...opts, + }); + }, + listOwners({ + administrationId, ...opts + }) { + return this._makeRequest({ + path: `/administrations/${administrationId}/owners`, + ...opts, + }); + }, + listChannels({ + administrationId, ...opts + }) { + return this._makeRequest({ + path: `/administrations/${administrationId}/channels`, + ...opts, + }); + }, + listReservations({ + administrationId, ...opts + }) { + return this._makeRequest({ + path: `/administrations/${administrationId}/reservations`, + ...opts, + }); + }, + searchContacts(opts = {}) { + return this._makeRequest({ + path: "/contacts/search/first", + ...opts, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/booking_experts/package.json b/components/booking_experts/package.json index fe8c7d35e3779..b71162e0e3a8c 100644 --- a/components/booking_experts/package.json +++ b/components/booking_experts/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/booking_experts", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Booking Experts Components", "main": "booking_experts.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/booking_experts/sources/booking-updated/booking-updated.mjs b/components/booking_experts/sources/booking-updated/booking-updated.mjs new file mode 100644 index 0000000000000..0fe880729c969 --- /dev/null +++ b/components/booking_experts/sources/booking-updated/booking-updated.mjs @@ -0,0 +1,44 @@ +import common from "../common/base-polling.mjs"; + +export default { + ...common, + key: "booking_experts-booking-updated", + name: "Booking Updated", + description: "Emit new event for each booking updated. [See the documentation](https://developers.bookingexperts.com/reference/administration-bookings-index)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + administrationId: { + propDefinition: [ + common.props.bookingExperts, + "administrationId", + ], + }, + }, + methods: { + ...common.methods, + getResourceFn() { + return this.bookingExperts.listBookings; + }, + getArgs() { + return { + administrationId: this.administrationId, + params: { + sort: "-updated_at", + }, + }; + }, + getTsField() { + return "updated_at"; + }, + generateMeta(booking) { + return { + id: booking.id, + summary: `Booking updated: ${booking.id}`, + ts: Date.parse(booking.attributes.updated_at), + }; + }, + }, +}; diff --git a/components/booking_experts/sources/common/base-polling.mjs b/components/booking_experts/sources/common/base-polling.mjs new file mode 100644 index 0000000000000..fcd7732e81547 --- /dev/null +++ b/components/booking_experts/sources/common/base-polling.mjs @@ -0,0 +1,94 @@ +import bookingExperts from "../../booking_experts.app.mjs"; +import { + DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, ConfigurationError, +} from "@pipedream/platform"; + +export default { + props: { + bookingExperts, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(ts) { + this.db.set("lastTs", ts); + }, + getArgs() { + return {}; + }, + getTsField() { + return "created_at"; + }, + async processEvent(limit) { + const lastTs = this._getLastTs(); + const resourceFn = this.getResourceFn(); + let args = this.getArgs(); + const tsField = this.getTsField(); + + const items = []; + let total, hasMore, count = 0; + args = { + ...args, + params: { + ...args?.params, + "page[number]": 1, + "page[size]": 100, + }, + }; + do { + const { data } = await resourceFn(args); + total = data?.length; + if (!total) { + break; + } + for (const item of data) { + const ts = Date.parse(item.attributes[tsField]); + if (ts > lastTs) { + items.push(item); + if (limit && ++count >= limit) { + hasMore = false; + break; + } + } else { + hasMore = false; + break; + } + } + args.params["page[number]"]++; + } while (hasMore && total === args.params["page[size]"]); + + if (!items.length) { + return; + } + + this._setLastTs(Date.parse(items[0].attributes[tsField])); + + items.forEach((item) => { + const meta = this.generateMeta(item); + this.$emit(item, meta); + }); + }, + getResourceFn() { + throw new ConfigurationError("getResourceFn is not implemented"); + }, + generateMeta() { + throw new ConfigurationError("generateMeta is not implemented"); + }, + }, + hooks: { + async deploy() { + await this.processEvent(25); + }, + }, + async run() { + await this.processEvent(); + }, +}; diff --git a/components/booking_experts/sources/new-booking-created/new-booking-created.mjs b/components/booking_experts/sources/new-booking-created/new-booking-created.mjs new file mode 100644 index 0000000000000..41b124bfbf2a7 --- /dev/null +++ b/components/booking_experts/sources/new-booking-created/new-booking-created.mjs @@ -0,0 +1,41 @@ +import common from "../common/base-polling.mjs"; + +export default { + ...common, + key: "booking_experts-new-booking-created", + name: "New Booking Created", + description: "Emit new event for each new booking created. [See the documentation](https://developers.bookingexperts.com/reference/administration-bookings-index)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + administrationId: { + propDefinition: [ + common.props.bookingExperts, + "administrationId", + ], + }, + }, + methods: { + ...common.methods, + getResourceFn() { + return this.bookingExperts.listBookings; + }, + getArgs() { + return { + administrationId: this.administrationId, + params: { + sort: "-created_at", + }, + }; + }, + generateMeta(booking) { + return { + id: booking.id, + summary: `New booking created: ${booking.id}`, + ts: Date.parse(booking.attributes.created_at), + }; + }, + }, +}; From 420c33b60fb256e9c3361591361f134874815eac Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Fri, 29 Aug 2025 12:46:29 -0400 Subject: [PATCH 2/5] new components --- .../add-guest-to-reservation.mjs | 96 +++++++++++++ .../create-agenda-period.mjs | 96 +++++++++++++ .../get-complex-prices/get-complex-prices.mjs | 52 +++++++ .../actions/list-bookings/list-bookings.mjs | 28 ++-- .../list-inventory-objects.mjs | 57 ++++++++ .../booking_experts/booking_experts.app.mjs | 129 +++++++++++++++++- .../inventory-object-updated.mjs | 44 ++++++ .../new-inventory-object-created.mjs | 41 ++++++ 8 files changed, 522 insertions(+), 21 deletions(-) create mode 100644 components/booking_experts/actions/add-guest-to-reservation/add-guest-to-reservation.mjs create mode 100644 components/booking_experts/actions/create-agenda-period/create-agenda-period.mjs create mode 100644 components/booking_experts/actions/get-complex-prices/get-complex-prices.mjs create mode 100644 components/booking_experts/actions/list-inventory-objects/list-inventory-objects.mjs create mode 100644 components/booking_experts/sources/inventory-object-updated/inventory-object-updated.mjs create mode 100644 components/booking_experts/sources/new-inventory-object-created/new-inventory-object-created.mjs diff --git a/components/booking_experts/actions/add-guest-to-reservation/add-guest-to-reservation.mjs b/components/booking_experts/actions/add-guest-to-reservation/add-guest-to-reservation.mjs new file mode 100644 index 0000000000000..7cb35cb62447f --- /dev/null +++ b/components/booking_experts/actions/add-guest-to-reservation/add-guest-to-reservation.mjs @@ -0,0 +1,96 @@ +import bookingExperts from "../../booking_experts.app.mjs"; + +export default { + key: "booking_experts-add-guest-to-reservation", + name: "Add Guest to Reservation", + description: "Add a guest to a reservation.. [See the documentation](https://developers.bookingexperts.com/reference/administration-reservation-guests-create)", + version: "0.0.1", + type: "action", + props: { + bookingExperts, + administrationId: { + propDefinition: [ + bookingExperts, + "administrationId", + ], + }, + reservationId: { + propDefinition: [ + bookingExperts, + "reservationId", + (c) => ({ + administrationId: c.administrationId, + }), + ], + }, + firstName: { + type: "string", + label: "First Name", + description: "The first name of the guest", + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of the guest", + }, + email: { + type: "string", + label: "Email", + description: "The email of the guest", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "The phone number of the guest", + optional: true, + }, + address: { + type: "string", + label: "Address", + description: "The street address of the guest", + optional: true, + }, + city: { + type: "string", + label: "City", + description: "The city of the guest", + optional: true, + }, + postalCode: { + type: "string", + label: "Postal Code", + description: "The postal code of the guest", + optional: true, + }, + countryCode: { + type: "string", + label: "Country Code", + description: "The country code of the guest", + optional: true, + }, + }, + async run({ $ }) { + const { data } = await this.bookingExperts.addGuestToReservation({ + administrationId: this.administrationId, + reservationId: this.reservationId, + data: { + data: { + type: "guest", + attributes: { + first_name: this.firstName, + last_name: this.lastName, + email: this.email, + phone: this.phone, + address: this.address, + city: this.city, + postal_code: this.postalCode, + country_code: this.countryCode, + }, + }, + }, + }); + $.export("$summary", "Guest added to reservation"); + return data; + }, +}; diff --git a/components/booking_experts/actions/create-agenda-period/create-agenda-period.mjs b/components/booking_experts/actions/create-agenda-period/create-agenda-period.mjs new file mode 100644 index 0000000000000..59e52caf59dfc --- /dev/null +++ b/components/booking_experts/actions/create-agenda-period/create-agenda-period.mjs @@ -0,0 +1,96 @@ +import bookingExperts from "../../booking_experts.app.mjs"; + +export default { + key: "booking_experts-create-agenda-period", + name: "Create Agenda Period", + description: "Creates a new agenda period. [See the documentation](https://developers.bookingexperts.com/reference/administration-maintenance-agenda-periods-create)", + version: "0.0.1", + type: "action", + props: { + bookingExperts, + administrationId: { + propDefinition: [ + bookingExperts, + "administrationId", + ], + }, + type: { + type: "string", + label: "Type", + description: "The type of agenda period to create", + options: [ + "maintenance_agenda_periods", + "external_blocked_agenda_periods", + ], + }, + label: { + type: "string", + label: "Label", + description: "The label of the agenda period", + }, + startDate: { + type: "string", + label: "Start Date", + description: "The start date of the agenda period. Example: `2025-08-28`", + }, + endDate: { + type: "string", + label: "End Date", + description: "The end date of the agenda period", + }, + inventoryObjectId: { + propDefinition: [ + bookingExperts, + "inventoryObjectId", + (c) => ({ + administrationId: c.administrationId, + }), + ], + }, + rentableId: { + propDefinition: [ + bookingExperts, + "rentableId", + (c) => ({ + administrationId: c.administrationId, + inventoryObjectId: c.inventoryObjectId, + }), + ], + }, + }, + async run({ $ }) { + const { data } = await this.bookingExperts.createAgendaPeriod({ + $, + administrationId: this.administrationId, + type: this.type, + data: { + data: { + type: "agenda_period", + attributes: { + label: this.label, + start_date: this.startDate, + end_date: this.endDate, + }, + relationships: { + inventory_object: this.inventoryObjectId + ? { + data: { + type: "inventory_object", + id: this.inventoryObjectId, + }, + } + : undefined, + rentable: { + data: { + type: "rentable", + id: this.rentableId, + }, + }, + }, + }, + }, + }); + $.export("$summary", "Agenda period created"); + return data; + }, +}; diff --git a/components/booking_experts/actions/get-complex-prices/get-complex-prices.mjs b/components/booking_experts/actions/get-complex-prices/get-complex-prices.mjs new file mode 100644 index 0000000000000..be82e7797c0c9 --- /dev/null +++ b/components/booking_experts/actions/get-complex-prices/get-complex-prices.mjs @@ -0,0 +1,52 @@ +import bookingExperts from "../../booking_experts.app.mjs"; + +export default { + key: "booking_experts-get-complex-prices", + name: "Get Complex Prices", + description: "Returns all complex prices of a master price list. [See the documentation](https://developers.bookingexperts.com/reference/administration-masterpricelist-complexprices-index)", + version: "0.0.1", + type: "action", + props: { + bookingExperts, + administrationId: { + propDefinition: [ + bookingExperts, + "administrationId", + ], + }, + masterPriceListId: { + propDefinition: [ + bookingExperts, + "masterPriceListId", + (c) => ({ + administrationId: c.administrationId, + }), + ], + }, + page: { + propDefinition: [ + bookingExperts, + "page", + ], + }, + perPage: { + propDefinition: [ + bookingExperts, + "perPage", + ], + }, + }, + async run({ $ }) { + const { data } = await this.bookingExperts.getComplexPrices({ + $, + administrationId: this.administrationId, + masterPriceListId: this.masterPriceListId, + params: { + "page[number]": this.page, + "page[size]": this.perPage, + }, + }); + $.export("$summary", `Found ${data.length} complex prices`); + return data; + }, +}; diff --git a/components/booking_experts/actions/list-bookings/list-bookings.mjs b/components/booking_experts/actions/list-bookings/list-bookings.mjs index 00a46fe92bcce..6a980532da596 100644 --- a/components/booking_experts/actions/list-bookings/list-bookings.mjs +++ b/components/booking_experts/actions/list-bookings/list-bookings.mjs @@ -43,31 +43,19 @@ export default { }), ], description: "Filter by reservation", - }, - createdAt: { - type: "string", - label: "Created At", - description: "Filter by created at date. Example: `2025-08-28`", - optional: true, - }, - updatedAt: { - type: "string", - label: "Updated At", - description: "Filter by updated at date. Example: `2025-08-28`", optional: true, }, page: { - type: "integer", - label: "Page", - description: "Page number", - optional: true, + propDefinition: [ + bookingExperts, + "page", + ], }, perPage: { - type: "integer", - label: "Per Page", - description: "Number of items per page", - max: 100, - optional: true, + propDefinition: [ + bookingExperts, + "perPage", + ], }, }, async run({ $ }) { diff --git a/components/booking_experts/actions/list-inventory-objects/list-inventory-objects.mjs b/components/booking_experts/actions/list-inventory-objects/list-inventory-objects.mjs new file mode 100644 index 0000000000000..3236f5878b687 --- /dev/null +++ b/components/booking_experts/actions/list-inventory-objects/list-inventory-objects.mjs @@ -0,0 +1,57 @@ +import bookingExperts from "../../booking_experts.app.mjs"; + +export default { + key: "booking_experts-list-inventory-objects", + name: "List Inventory Objects", + description: "Returns inventory objects of the administration. [See the documentation](https://developers.bookingexperts.com/reference/administration-inventoryobjects-index)", + version: "0.0.1", + type: "action", + props: { + bookingExperts, + administrationId: { + propDefinition: [ + bookingExperts, + "administrationId", + ], + }, + name: { + type: "string", + label: "Name", + description: "Filter by name", + optional: true, + }, + labels: { + type: "string[]", + label: "Labels", + description: "Filter by labels", + optional: true, + }, + page: { + propDefinition: [ + bookingExperts, + "page", + ], + }, + perPage: { + propDefinition: [ + bookingExperts, + "perPage", + ], + }, + }, + async run({ $ }) { + const { data } = await this.bookingExperts.listInventoryObjects({ + administrationId: this.administrationId, + params: { + "filter[name]": this.name, + "filter[labels]": this.labels + ? this.labels.join(",") + : undefined, + "page[number]": this.page, + "page[size]": this.perPage, + }, + }); + $.export("$summary", `Found ${data.length} inventory objects`); + return data; + }, +}; diff --git a/components/booking_experts/booking_experts.app.mjs b/components/booking_experts/booking_experts.app.mjs index 53ec4352cb362..8ac750fa35e20 100644 --- a/components/booking_experts/booking_experts.app.mjs +++ b/components/booking_experts/booking_experts.app.mjs @@ -70,7 +70,6 @@ export default { type: "string", label: "Reservation ID", description: "The ID of a reservation", - optional: true, async options({ page, administrationId, }) { @@ -88,6 +87,84 @@ export default { })) || []; }, }, + masterPriceListId: { + type: "string", + label: "Master Price List ID", + description: "The ID of a master price list", + async options({ + page, administrationId, + }) { + const { data } = await this.listMasterPriceLists({ + administrationId, + params: { + "page[number]": page + 1, + }, + }); + return data?.map(({ + id, attributes, + }) => ({ + label: attributes.name, + value: id, + })) || []; + }, + }, + inventoryObjectId: { + type: "string", + label: "Inventory Object ID", + description: "The ID of an inventory object", + optional: true, + async options({ + page, administrationId, + }) { + const { data } = await this.listInventoryObjects({ + administrationId, + params: { + "page[number]": page + 1, + }, + }); + return data?.map(({ + id, attributes, + }) => ({ + label: attributes.name_with_type, + value: id, + })) || []; + }, + }, + rentableId: { + type: "string", + label: "Rentable ID", + description: "The ID of a rentable", + async options({ + page, administrationId, inventoryObjectId, + }) { + const { data } = await this.listRentables({ + administrationId, + params: { + "page[number]": page + 1, + "filter[inventory_object]": inventoryObjectId, + }, + }); + return data?.map(({ + id, attributes, + }) => ({ + label: attributes.name, + value: id, + })) || []; + }, + }, + page: { + type: "integer", + label: "Page", + description: "Page number", + optional: true, + }, + perPage: { + type: "integer", + label: "Per Page", + description: "Number of items per page", + max: 100, + optional: true, + }, }, methods: { _baseUrl() { @@ -105,6 +182,14 @@ export default { ...opts, }); }, + getComplexPrices({ + administrationId, masterPriceListId, ...opts + }) { + return this._makeRequest({ + path: `/administrations/${administrationId}/master_price_lists/${masterPriceListId}/complex_prices`, + ...opts, + }); + }, listAdministrations(opts = {}) { return this._makeRequest({ path: "/administrations", @@ -143,11 +228,53 @@ export default { ...opts, }); }, + listMasterPriceLists({ + administrationId, ...opts + }) { + return this._makeRequest({ + path: `/administrations/${administrationId}/master_price_lists`, + ...opts, + }); + }, + listInventoryObjects({ + administrationId, ...opts + }) { + return this._makeRequest({ + path: `/administrations/${administrationId}/inventory_objects`, + ...opts, + }); + }, + listRentables({ + administrationId, ...opts + }) { + return this._makeRequest({ + path: `/administrations/${administrationId}/rentables`, + ...opts, + }); + }, searchContacts(opts = {}) { return this._makeRequest({ path: "/contacts/search/first", ...opts, }); }, + createAgendaPeriod({ + administrationId, type, ...opts + }) { + return this._makeRequest({ + path: `/administrations/${administrationId}/${type}`, + method: "POST", + ...opts, + }); + }, + addGuestToReservation({ + administrationId, reservationId, ...opts + }) { + return this._makeRequest({ + path: `/administrations/${administrationId}/reservations/${reservationId}/guests`, + method: "POST", + ...opts, + }); + }, }, }; diff --git a/components/booking_experts/sources/inventory-object-updated/inventory-object-updated.mjs b/components/booking_experts/sources/inventory-object-updated/inventory-object-updated.mjs new file mode 100644 index 0000000000000..27f12221f6a62 --- /dev/null +++ b/components/booking_experts/sources/inventory-object-updated/inventory-object-updated.mjs @@ -0,0 +1,44 @@ +import common from "../common/base-polling.mjs"; + +export default { + ...common, + key: "booking_experts-inventory-object-updated", + name: "Inventory Object Updated", + description: "Emit new event when an inventory object is updated. [See the documentation](https://developers.bookingexperts.com/reference/administration-inventoryobjects-index)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + administrationId: { + propDefinition: [ + common.props.bookingExperts, + "administrationId", + ], + }, + }, + methods: { + ...common.methods, + getResourceFn() { + return this.bookingExperts.listInventoryObjects; + }, + getArgs() { + return { + administrationId: this.administrationId, + params: { + sort: "-updated_at", + }, + }; + }, + getTsField() { + return "updated_at"; + }, + generateMeta(inventoryObject) { + return { + id: inventoryObject.id, + summary: `Inventory object updated: ${inventoryObject.id}`, + ts: Date.parse(inventoryObject.attributes.updated_at), + }; + }, + }, +}; diff --git a/components/booking_experts/sources/new-inventory-object-created/new-inventory-object-created.mjs b/components/booking_experts/sources/new-inventory-object-created/new-inventory-object-created.mjs new file mode 100644 index 0000000000000..32f2690021052 --- /dev/null +++ b/components/booking_experts/sources/new-inventory-object-created/new-inventory-object-created.mjs @@ -0,0 +1,41 @@ +import common from "../common/base-polling.mjs"; + +export default { + ...common, + key: "booking_experts-new-inventory-object-created", + name: "New Inventory Object Created", + description: "Emit new event when a new inventory object is created. [See the documentation](https://developers.bookingexperts.com/reference/administration-inventoryobjects-index)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + ...common.props, + administrationId: { + propDefinition: [ + common.props.bookingExperts, + "administrationId", + ], + }, + }, + methods: { + ...common.methods, + getResourceFn() { + return this.bookingExperts.listInventoryObjects; + }, + getArgs() { + return { + administrationId: this.administrationId, + params: { + sort: "-created_at", + }, + }; + }, + generateMeta(inventoryObject) { + return { + id: inventoryObject.id, + summary: `New inventory object created: ${inventoryObject.id}`, + ts: Date.parse(inventoryObject.attributes.created_at), + }; + }, + }, +}; From c16cb9773bc7d4c904b8a819b32b20357b4b8a09 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Fri, 29 Aug 2025 12:47:11 -0400 Subject: [PATCH 3/5] pnpm-lock.yaml --- pnpm-lock.yaml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3b944f6d5eb3..216e540f219be 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1789,7 +1789,11 @@ importers: components/bolt_iot: {} - components/booking_experts: {} + components/booking_experts: + dependencies: + '@pipedream/platform': + specifier: ^3.1.0 + version: 3.1.0 components/bookingmood: {} @@ -10938,8 +10942,7 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/postbin: - specifiers: {} + components/postbin: {} components/postgresql: dependencies: @@ -14995,8 +14998,7 @@ importers: components/upollo: {} - components/uproc: - specifiers: {} + components/uproc: {} components/upstash_redis: dependencies: From ba11be386a3a7d0e820937fbb1d816acdb998d36 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Fri, 29 Aug 2025 12:52:51 -0400 Subject: [PATCH 4/5] pnpm-lock.yaml --- pnpm-lock.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3edf7da596f8e..509b9f72fde61 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -439,8 +439,7 @@ importers: specifier: ^3.0.0 version: 3.0.3 - components/afosto: - specifiers: {} + components/afosto: {} components/aftership: dependencies: From 74d44788e38197d73701ce58b102e908f56e1a10 Mon Sep 17 00:00:00 2001 From: Michelle Bergeron Date: Fri, 29 Aug 2025 13:09:08 -0400 Subject: [PATCH 5/5] update description --- .../booking_experts/actions/search-contacts/search-contacts.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/booking_experts/actions/search-contacts/search-contacts.mjs b/components/booking_experts/actions/search-contacts/search-contacts.mjs index 965364e829a4d..c5e9402d2b3dc 100644 --- a/components/booking_experts/actions/search-contacts/search-contacts.mjs +++ b/components/booking_experts/actions/search-contacts/search-contacts.mjs @@ -4,7 +4,7 @@ import { ConfigurationError } from "@pipedream/platform"; export default { key: "booking_experts-search-contacts", name: "Search Contacts", - description: "Search for contacts by name or email. [See the documentation](https://developers.bookingexperts.com/reference/contact-search-first)", + description: "Search for contacts by email or phone. [See the documentation](https://developers.bookingexperts.com/reference/contact-search-first)", version: "0.0.1", type: "action", props: {