From 07cb2a1e1c586aaa9934c4faf5912f9ae1e10140 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Fri, 1 Nov 2024 14:34:26 -0300 Subject: [PATCH] tave init --- .../actions/create-contact/create-contact.mjs | 23 +++ .../tave/actions/create-job/create-job.mjs | 48 +++++ .../actions/find-contact/find-contact.mjs | 24 +++ components/tave/package.json | 2 +- .../new-contact-instant.mjs | 64 +++++++ .../new-order-booked-instant.mjs | 94 ++++++++++ .../new-payment-instant.mjs | 67 +++++++ components/tave/tave.app.mjs | 172 +++++++++++++++++- 8 files changed, 491 insertions(+), 3 deletions(-) create mode 100644 components/tave/actions/create-contact/create-contact.mjs create mode 100644 components/tave/actions/create-job/create-job.mjs create mode 100644 components/tave/actions/find-contact/find-contact.mjs create mode 100644 components/tave/sources/new-contact-instant/new-contact-instant.mjs create mode 100644 components/tave/sources/new-order-booked-instant/new-order-booked-instant.mjs create mode 100644 components/tave/sources/new-payment-instant/new-payment-instant.mjs diff --git a/components/tave/actions/create-contact/create-contact.mjs b/components/tave/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..ee06432b751d3 --- /dev/null +++ b/components/tave/actions/create-contact/create-contact.mjs @@ -0,0 +1,23 @@ +import tave from "../../tave.app.mjs"; + +export default { + key: "tave-create-contact", + name: "Create Contact", + description: "Creates a new contact in the Tave system. [See the documentation](https://tave.io/v2)", + version: "0.0.1", + type: "action", + props: { + tave, + contact: { + propDefinition: [ + tave, + "contact", + ], + }, + }, + async run({ $ }) { + const response = await this.tave.createContact(this.contact); + $.export("$summary", `Successfully created contact with name: ${response.name}`); + return response; + }, +}; diff --git a/components/tave/actions/create-job/create-job.mjs b/components/tave/actions/create-job/create-job.mjs new file mode 100644 index 0000000000000..3acea27164204 --- /dev/null +++ b/components/tave/actions/create-job/create-job.mjs @@ -0,0 +1,48 @@ +import tave from "../../tave.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "tave-create-job", + name: "Create Job", + description: "Creates a new job in the Táve system with associated items typically linked with a job. [See the documentation](https://tave.io/v2)", + version: "0.0.{{ts}}", + type: "action", + props: { + tave, + jobDetails: { + propDefinition: [ + tave, + "jobDetails", + ], + }, + additionalItems: { + type: "string", + label: "Additional Items", + description: "Additional items for the job", + optional: true, + }, + notes: { + type: "string", + label: "Notes", + description: "Additional notes for the job", + optional: true, + }, + instructions: { + type: "string", + label: "Instructions", + description: "Additional instructions for the job", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.tave.createJob({ + ...this.jobDetails, + additionalItems: this.additionalItems, + notes: this.notes, + instructions: this.instructions, + }); + + $.export("$summary", `Successfully created job with specifics: ${this.jobDetails.specifics}`); + return response; + }, +}; diff --git a/components/tave/actions/find-contact/find-contact.mjs b/components/tave/actions/find-contact/find-contact.mjs new file mode 100644 index 0000000000000..e88bfbe4c1a36 --- /dev/null +++ b/components/tave/actions/find-contact/find-contact.mjs @@ -0,0 +1,24 @@ +import tave from "../../tave.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "tave-find-contact", + name: "Find Contact", + description: "Searches for an existing contact in the Tave system. [See the documentation](https://tave.io/v2)", + version: "0.0.{{ts}}", + type: "action", + props: { + tave, + searchContact: { + propDefinition: [ + tave, + "searchContact", + ], + }, + }, + async run({ $ }) { + const response = await this.tave.searchContact(this.searchContact); + $.export("$summary", `Successfully found contact(s) based on the query: ${this.searchContact}`); + return response; + }, +}; diff --git a/components/tave/package.json b/components/tave/package.json index ca37060197df3..27046ab996466 100644 --- a/components/tave/package.json +++ b/components/tave/package.json @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/tave/sources/new-contact-instant/new-contact-instant.mjs b/components/tave/sources/new-contact-instant/new-contact-instant.mjs new file mode 100644 index 0000000000000..590355c9ad621 --- /dev/null +++ b/components/tave/sources/new-contact-instant/new-contact-instant.mjs @@ -0,0 +1,64 @@ +import tave from "../../tave.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "tave-new-contact-instant", + name: "New Contact Created", + description: "Emit new event when a contact is created. [See the documentation](https://tave.io/v2)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + tave, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: "$.service.db", + clientId: { + propDefinition: [ + tave, + "clientId", + ], + }, + }, + methods: { + async _emitWebhookEvent(contact) { + this.$emit(contact, { + id: contact.id, + summary: `New contact created: ${contact.name}`, + ts: contact.createdAt + ? Date.parse(contact.createdAt) + : Date.now(), + }); + }, + _getWebhookId() { + return this.db.get("webhookId"); + }, + _setWebhookId(id) { + this.db.set("webhookId", id); + }, + }, + hooks: { + async deploy() { + const events = await this.tave.emitNewContactEvent(this.clientId); + for (const event of events) { + this._emitWebhookEvent(event); + } + }, + async activate() { + const hookId = await this.tave.emitNewContactEvent(this.clientId); + this._setWebhookId(hookId); + }, + async deactivate() { + const hookId = this._getWebhookId(); + if (hookId) { + await this.tave.emitNewContactEvent(this.clientId); // Assuming the method handles the deletion + } + }, + }, + async run(event) { + const contact = event.body; + this._emitWebhookEvent(contact); + }, +}; diff --git a/components/tave/sources/new-order-booked-instant/new-order-booked-instant.mjs b/components/tave/sources/new-order-booked-instant/new-order-booked-instant.mjs new file mode 100644 index 0000000000000..3dfa7d5daec90 --- /dev/null +++ b/components/tave/sources/new-order-booked-instant/new-order-booked-instant.mjs @@ -0,0 +1,94 @@ +import tave from "../../tave.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "tave-new-order-booked-instant", + name: "New Order Booked", + description: "Emit new event when an order is booked, including manually booked orders in manager and electronic bookings in client access. [See the documentation](https://tave.io/v2)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + tave, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + orderId: { + propDefinition: [ + tave, + "orderId", + ], + }, + managerId: { + propDefinition: [ + tave, + "managerId", + ], + }, + clientId: { + propDefinition: [ + tave, + "clientId", + ], + }, + }, + methods: { + _getWebhookId() { + return this.db.get("webhookId"); + }, + _setWebhookId(id) { + this.db.set("webhookId", id); + }, + }, + hooks: { + async deploy() { + const events = await this.tave.emitNewOrderEvent(this.orderId, this.managerId, this.clientId); + for (const event of events.slice(-50)) { + this.$emit(event, { + id: event.id, + summary: `New order booked with ID ${event.id}`, + ts: Date.parse(event.createdAt), + }); + } + }, + async activate() { + const hookId = await this.tave.emitNewOrderEvent(this.orderId, this.managerId, this.clientId); + this._setWebhookId(hookId); + }, + async deactivate() { + const id = this._getWebhookId(); + if (id) { + await this.tave._makeRequest({ + method: "DELETE", + path: `/webhooks/${id}`, + }); + } + }, + }, + async run(event) { + const { body } = event; + const expectedSignature = {}; // Add logic to compute expected signature + const webhookSignature = event.headers["x-webhook-signature"]; + + if (webhookSignature !== expectedSignature) { + this.http.respond({ + status: 401, + body: "Unauthorized", + }); + return; + } + + this.http.respond({ + status: 200, + body: "OK", + }); + + this.$emit(body, { + id: body.orderId, + summary: `Order booked: ${body.orderId}`, + ts: Date.parse(body.createdAt), + }); + }, +}; diff --git a/components/tave/sources/new-payment-instant/new-payment-instant.mjs b/components/tave/sources/new-payment-instant/new-payment-instant.mjs new file mode 100644 index 0000000000000..c601c1757c369 --- /dev/null +++ b/components/tave/sources/new-payment-instant/new-payment-instant.mjs @@ -0,0 +1,67 @@ +import tave from "../../tave.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "tave-new-payment-instant", + name: "New Payment Created", + description: "Emit new event when a new payment is created. [See the documentation](https://tave.io/v2/docs)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + tave, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: "$.service.db", + paymentId: { + propDefinition: [ + tave, + "paymentId", + ], + }, + orderId: { + propDefinition: [ + tave, + "orderId", + ], + optional: true, + }, + }, + methods: { + _getWebhookId() { + return this.db.get("webhookId"); + }, + _setWebhookId(id) { + this.db.set("webhookId", id); + }, + }, + hooks: { + async deploy() { + const events = await this.tave.emitNewPaymentEvent(this.paymentId, this.orderId); + for (const event of events) { + this.$emit(event, { + id: event.id, + summary: `Historical payment event for payment ID: ${event.id}`, + ts: Date.now(), + }); + } + }, + async activate() { + const hookId = await this.tave.emitNewPaymentEvent(this.paymentId, this.orderId); + this._setWebhookId(hookId); + }, + async deactivate() { + const hookId = this._getWebhookId(); + // Implement the logic to delete the webhook, if supported by the API + }, + }, + async run(event) { + this.$emit(event.body, { + id: event.body.paymentId, + summary: `New payment created with ID: ${event.body.paymentId}`, + ts: Date.now(), + }); + }, +}; diff --git a/components/tave/tave.app.mjs b/components/tave/tave.app.mjs index f026933114279..3c99f7183989d 100644 --- a/components/tave/tave.app.mjs +++ b/components/tave/tave.app.mjs @@ -1,9 +1,177 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "tave", - propDefinitions: {}, + propDefinitions: { + clientId: { + type: "string", + label: "Client ID", + description: "ID of the client that is required.", + }, + orderId: { + type: "string", + label: "Order ID", + description: "ID of the order that is required.", + }, + managerId: { + type: "string", + label: "Manager ID", + description: "Optional manager ID.", + optional: true, + }, + paymentId: { + type: "string", + label: "Payment ID", + description: "ID of the payment that is required.", + }, + contact: { + type: "object", + label: "Contact Details", + description: "Includes required fields like kind and details (name, email) and optional fields (phone, address).", + properties: { + kind: { + type: "string", + label: "Kind", + }, + name: { + type: "string", + label: "Name", + }, + email: { + type: "string", + label: "Email", + }, + phone: { + type: "string", + label: "Phone", + optional: true, + }, + address: { + type: "string", + label: "Address", + optional: true, + }, + }, + }, + searchContact: { + type: "string", + label: "Contact Name or Email", + description: "The name or email of the contact to search for.", + }, + jobDetails: { + type: "object", + label: "Job Details", + description: "Includes required client details, job specifics and optional additional items or notes.", + properties: { + clientDetails: { + type: "object", + label: "Client Details", + }, + specifics: { + type: "string", + label: "Job Specifics", + }, + additionalItems: { + type: "string", + label: "Additional Items", + optional: true, + }, + notes: { + type: "string", + label: "Notes", + optional: true, + }, + instructions: { + type: "string", + label: "Instructions", + optional: true, + }, + }, + }, + }, methods: { - // this.$auth contains connected account data + _baseUrl() { + return "https://tave.io/v2"; + }, + async _makeRequest(opts = {}) { + const { + $ = this, method = "GET", path = "/", headers, ...otherOpts + } = opts; + return axios($, { + ...otherOpts, + method, + url: this._baseUrl() + path, + headers: { + ...headers, + Authorization: `Bearer ${this.$auth.oauth_access_token}`, + }, + }); + }, + async emitNewContactEvent(clientId) { + return this._makeRequest({ + path: `/contacts/${clientId}/created`, + method: "POST", + }); + }, + async emitNewOrderEvent(orderId, managerId = null, clientId = null) { + return this._makeRequest({ + path: `/orders/${orderId}/booked`, + method: "POST", + data: { + manager_id: managerId, + client_id: clientId, + }, + }); + }, + async emitNewPaymentEvent(paymentId, orderId = null) { + return this._makeRequest({ + path: `/payments/${paymentId}/created`, + method: "POST", + data: { + order_id: orderId, + }, + }); + }, + async createContact({ + kind, name, email, phone, address, + }) { + return this._makeRequest({ + path: "/contacts", + method: "POST", + data: { + kind, + name, + email, + phone, + address, + }, + }); + }, + async searchContact(contactIdentifier) { + return this._makeRequest({ + path: "/contacts/search", + method: "GET", + params: { + query: contactIdentifier, + }, + }); + }, + async createJob({ + clientDetails, specifics, additionalItems, notes, instructions, + }) { + return this._makeRequest({ + path: "/jobs", + method: "POST", + data: { + clientDetails, + specifics, + additionalItems, + notes, + instructions, + }, + }); + }, authKeys() { console.log(Object.keys(this.$auth)); },