From 9d34e3b89d54319e64a89231fbda2951c5557167 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Fri, 19 Jul 2024 11:53:31 -0300 Subject: [PATCH 1/4] specific init --- .../create-conversation.mjs | 34 +++++++ .../actions/find-company/find-company.mjs | 26 +++++ .../update-create-contact.mjs | 34 +++++++ components/specific/package.json | 2 +- .../new-contact-instant.mjs | 50 ++++++++++ .../new-conversation-instant.mjs | 41 ++++++++ components/specific/specific.app.mjs | 97 ++++++++++++++++++- 7 files changed, 278 insertions(+), 6 deletions(-) create mode 100644 components/specific/actions/create-conversation/create-conversation.mjs create mode 100644 components/specific/actions/find-company/find-company.mjs create mode 100644 components/specific/actions/update-create-contact/update-create-contact.mjs create mode 100644 components/specific/sources/new-contact-instant/new-contact-instant.mjs create mode 100644 components/specific/sources/new-conversation-instant/new-conversation-instant.mjs diff --git a/components/specific/actions/create-conversation/create-conversation.mjs b/components/specific/actions/create-conversation/create-conversation.mjs new file mode 100644 index 0000000000000..0064c8e05204b --- /dev/null +++ b/components/specific/actions/create-conversation/create-conversation.mjs @@ -0,0 +1,34 @@ +import specific from "../../specific.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "specific-create-conversation", + name: "Create Conversation", + description: "Initializes a fresh chat thread. Requires 'user-id' prop to identify the conversation's owner. Optional 'title' prop can provide a conversation's subject line. [See the documentation](https://public-api.specific.app/docs/introduction/welcome)", + version: "0.0.{{ts}}", + type: "action", + props: { + specific, + userId: { + propDefinition: [ + specific, + "userId", + ], + }, + title: { + propDefinition: [ + specific, + "title", + ], + }, + }, + async run({ $ }) { + const response = await this.specific.initializeChatThread({ + userId: this.userId, + title: this.title, + }); + + $.export("$summary", `Successfully created conversation for user ID: ${this.userId}`); + return response; + }, +}; diff --git a/components/specific/actions/find-company/find-company.mjs b/components/specific/actions/find-company/find-company.mjs new file mode 100644 index 0000000000000..71a9b063029b6 --- /dev/null +++ b/components/specific/actions/find-company/find-company.mjs @@ -0,0 +1,26 @@ +import specific from "../../specific.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "specific-find-company", + name: "Find Company", + description: "Retrieve details of a specified company. [See the documentation](https://public-api.specific.app/docs/introduction/welcome)", + version: "0.0.{{ts}}", + type: "action", + props: { + specific, + companyId: { + propDefinition: [ + specific, + "companyId", + ], + }, + }, + async run({ $ }) { + const response = await this.specific.retrieveCompanyDetails({ + companyId: this.companyId, + }); + $.export("$summary", `Successfully retrieved details for company ID: ${this.companyId}`); + return response; + }, +}; diff --git a/components/specific/actions/update-create-contact/update-create-contact.mjs b/components/specific/actions/update-create-contact/update-create-contact.mjs new file mode 100644 index 0000000000000..6c38875f29f99 --- /dev/null +++ b/components/specific/actions/update-create-contact/update-create-contact.mjs @@ -0,0 +1,34 @@ +import specific from "../../specific.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "specific-update-create-contact", + name: "Update or Create Contact", + description: "Modify an existing contact's details or create a new one if the specified contact does not exist. [See the documentation](https://public-api.specific.app/docs/types/contact)", + version: "0.0.{{ts}}", + type: "action", + props: { + specific, + contactId: { + propDefinition: [ + specific, + "contactId", + ], + }, + contactInfo: { + propDefinition: [ + specific, + "contactInfo", + ], + }, + }, + async run({ $ }) { + const response = await this.specific.modifyOrCreateContact({ + contactId: this.contactId, + contactInfo: this.contactInfo, + }); + + $.export("$summary", `Successfully updated or created contact with ID ${this.contactId}`); + return response; + }, +}; diff --git a/components/specific/package.json b/components/specific/package.json index 809e213b677d9..42b1103e6b620 100644 --- a/components/specific/package.json +++ b/components/specific/package.json @@ -12,4 +12,4 @@ "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/components/specific/sources/new-contact-instant/new-contact-instant.mjs b/components/specific/sources/new-contact-instant/new-contact-instant.mjs new file mode 100644 index 0000000000000..fcf8f179a6366 --- /dev/null +++ b/components/specific/sources/new-contact-instant/new-contact-instant.mjs @@ -0,0 +1,50 @@ +import specific from "../../specific.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "specific-new-contact-instant", + name: "New Contact Created", + description: "Emit new event whenever a new contact is created. [See the documentation](https://public-api.specific.app/docs/introduction/welcome)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + specific: { + type: "app", + app: "specific", + }, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: "$.service.db", + }, + hooks: { + async deploy() { + // Emit historical events + const contacts = await this.specific.emitNewContactCreated(); + for (const contact of contacts) { + this.$emit(contact, { + id: contact.id, + summary: `New contact: ${contact.name}`, + ts: Date.parse(contact.createdAt), + }); + } + }, + async activate() { + const webhookId = await this.specific.emitNewContactCreated(); + this.db.set("webhookId", webhookId); + }, + async deactivate() { + const webhookId = this.db.get("webhookId"); + await this.specific.emitNewContactCreated(webhookId); + }, + }, + async run(event) { + this.$emit(event.body, { + id: event.body.id, + summary: `New contact created: ${event.body.name}`, + ts: Date.parse(event.body.createdAt), + }); + }, +}; diff --git a/components/specific/sources/new-conversation-instant/new-conversation-instant.mjs b/components/specific/sources/new-conversation-instant/new-conversation-instant.mjs new file mode 100644 index 0000000000000..7d5c38766db48 --- /dev/null +++ b/components/specific/sources/new-conversation-instant/new-conversation-instant.mjs @@ -0,0 +1,41 @@ +import specific from "../../specific.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "specific-new-conversation-instant", + name: "New Conversation Instant", + description: "Emit new event whenever a new conversation is initiated. [See the documentation](https://public-api.specific.app/docs/introduction/welcome)", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + specific: { + type: "app", + app: "specific", + }, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: "$.service.db", + }, + hooks: { + async deploy() { + // No historical data to fetch + }, + async activate() { + // No activation steps needed + }, + async deactivate() { + // No deactivation steps needed + }, + }, + async run(event) { + const response = await this.specific.emitNewConversationInitiated(); + this.$emit(response, { + id: response.id, + summary: `New conversation initiated: ${response.id}`, + ts: Date.now(), + }); + }, +}; diff --git a/components/specific/specific.app.mjs b/components/specific/specific.app.mjs index 44b7e3f4254b7..ebe970c531a6f 100644 --- a/components/specific/specific.app.mjs +++ b/components/specific/specific.app.mjs @@ -1,11 +1,98 @@ +import { axios } from "@pipedream/platform"; + export default { type: "app", app: "specific", - propDefinitions: {}, + propDefinitions: { + userId: { + type: "string", + label: "User ID", + description: "The ID of the user to identify the conversation's owner", + }, + title: { + type: "string", + label: "Title", + description: "An optional title for the conversation", + optional: true, + }, + contactId: { + type: "string", + label: "Contact ID", + description: "The ID of the contact to be modified or created", + }, + contactInfo: { + type: "object", + label: "Contact Info", + description: "The updated details of the contact", + }, + companyId: { + type: "string", + label: "Company ID", + description: "The ID of the company to retrieve details for", + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://public-api.specific.app"; + }, + 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 emitNewContactCreated() { + return this._makeRequest({ + method: "POST", + path: "/contacts", + data: {}, // No props required + }); + }, + async emitNewConversationInitiated() { + return this._makeRequest({ + method: "POST", + path: "/conversations", + data: {}, // No props required + }); + }, + async initializeChatThread({ + userId, title, + }) { + return this._makeRequest({ + method: "POST", + path: "/conversations", + data: { + userId, + title, + }, + }); + }, + async modifyOrCreateContact({ + contactId, contactInfo, + }) { + return this._makeRequest({ + method: "PUT", + path: `/contacts/${contactId}`, + data: contactInfo, + }); + }, + async retrieveCompanyDetails({ companyId }) { + return this._makeRequest({ + method: "GET", + path: `/companies/${companyId}`, + }); }, }, -}; \ No newline at end of file +}; From 3440a0b7ac768d3c3100de0f51f4c1ce69069998 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 22 Jul 2024 12:29:34 -0300 Subject: [PATCH 2/4] [Components] specific #12906 Sources - New Contact (Instant) - New Conversation (Instant) Actions - Create Conversation - Update Create Contact - Find Company --- .../create-conversation.mjs | 154 +++++++++++++-- .../actions/find-company/find-company.mjs | 17 +- .../update-create-contact.mjs | 97 +++++++++- components/specific/common/utils.mjs | 8 + components/specific/package.json | 6 +- components/specific/sources/common/base.mjs | 82 ++++++++ .../new-contact-instant.mjs | 54 ++---- .../new-contact-instant/test-event.mjs | 7 + .../new-conversation-instant.mjs | 43 ++--- .../new-conversation-instant/test-event.mjs | 24 +++ components/specific/specific.app.mjs | 180 ++++++++++++------ 11 files changed, 517 insertions(+), 155 deletions(-) create mode 100644 components/specific/common/utils.mjs create mode 100644 components/specific/sources/common/base.mjs create mode 100644 components/specific/sources/new-contact-instant/test-event.mjs create mode 100644 components/specific/sources/new-conversation-instant/test-event.mjs diff --git a/components/specific/actions/create-conversation/create-conversation.mjs b/components/specific/actions/create-conversation/create-conversation.mjs index 0064c8e05204b..b67484a01a796 100644 --- a/components/specific/actions/create-conversation/create-conversation.mjs +++ b/components/specific/actions/create-conversation/create-conversation.mjs @@ -1,34 +1,166 @@ import specific from "../../specific.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "specific-create-conversation", name: "Create Conversation", - description: "Initializes a fresh chat thread. Requires 'user-id' prop to identify the conversation's owner. Optional 'title' prop can provide a conversation's subject line. [See the documentation](https://public-api.specific.app/docs/introduction/welcome)", - version: "0.0.{{ts}}", + description: "Create a new conversation. [See the documentation](https://public-api.specific.app/docs/mutations/createConversation)", + version: "0.0.1", type: "action", props: { specific, - userId: { + content: { + type: "string", + label: "Content", + description: "Conversation content as String or ProseMirror document.", + reloadProps: true, + }, + insertedAt: { + propDefinition: [ + specific, + "insertedAt", + ], + optional: true, + }, + assignee: { + type: "string", + label: "Assignee", + description: "The user's email.", + optional: true, + }, + sourceId: { + propDefinition: [ + specific, + "sourceId", + ], + optional: true, + }, + companyId: { propDefinition: [ specific, - "userId", + "companyId", ], + optional: true, }, - title: { + contactId: { propDefinition: [ specific, - "title", + "contactId", ], + optional: true, }, + sourceUrl: { + type: "string", + label: "Source URL", + description: "Source url where the conversation was gathered.", + optional: true, + }, + }, + async additionalProps() { + const props = {}; + if (this.content) { + const { data: { customFields } } = await this.specific.query({ + model: "customFields", + where: "{type: {equals: conversation }}", + fields: "name", + }); + for (const { name } of customFields) { + props[`customField-${name}`] = { + type: "string", + label: name, + description: `Custom Field: ${name}`, + optional: true, + }; + } + } + return props; }, async run({ $ }) { - const response = await this.specific.initializeChatThread({ - userId: this.userId, - title: this.title, + const { + specific, + ...data + } = this; + + const customFields = this.specific.parseCustomFields(data); + + const response = await specific.mutation({ + $, + model: "createConversation", + data: `{ + ${this.assignee + ? `assignee: { + connectOrIgnore: { + email: "${this.assignee}" + } + }` + : ""} + ${this.companyId + ? `company: { + connect: { + id: "${this.companyId}" + } + }` + : ""} + ${this.contactId + ? `contact: { + connect: { + id: "${this.contactId}" + } + }` + : ""} + content: "${this.content}" + ${customFields + ? `customFields: ${customFields}` + : ""} + ${this.insertedAt + ? `insertedAt: "${this.insertedAt}"` + : ""} + ${this.sourceId + ? `source: { + connect: { + id: "${this.sourceId}" + } + }` + : ""} + ${this.sourceUrl + ? `sourceUrl: "${this.sourceUrl}"` + : ""} + }`, + fields: ` + customFields + id + insertedAt + name + plainText + sourceUrl + assignee { + email + fullName + id + } + company { + contactsCount + customFields + id + name + visitorId + } + contact { + customFields + email + id + name + visitorId + } + source { + id + name + }`, }); - $.export("$summary", `Successfully created conversation for user ID: ${this.userId}`); + if (response.errors) throw new Error(response.errors[0].message); + + $.export("$summary", `Successfully created conversation for user ID: ${response.data?.createConversation?.id}`); return response; }, }; + diff --git a/components/specific/actions/find-company/find-company.mjs b/components/specific/actions/find-company/find-company.mjs index 71a9b063029b6..c263149ee895c 100644 --- a/components/specific/actions/find-company/find-company.mjs +++ b/components/specific/actions/find-company/find-company.mjs @@ -1,11 +1,10 @@ import specific from "../../specific.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "specific-find-company", name: "Find Company", - description: "Retrieve details of a specified company. [See the documentation](https://public-api.specific.app/docs/introduction/welcome)", - version: "0.0.{{ts}}", + description: "Retrieve details of a specified company. [See the documentation](https://public-api.specific.app/docs/types/Query)", + version: "0.0.1", type: "action", props: { specific, @@ -17,9 +16,17 @@ export default { }, }, async run({ $ }) { - const response = await this.specific.retrieveCompanyDetails({ - companyId: this.companyId, + const response = await this.specific.query({ + $, + model: "companies", + where: `{id: {equals: "${this.companyId}"}}`, + fields: `id + name + customFields + visitorId + contactsCount`, }); + $.export("$summary", `Successfully retrieved details for company ID: ${this.companyId}`); return response; }, diff --git a/components/specific/actions/update-create-contact/update-create-contact.mjs b/components/specific/actions/update-create-contact/update-create-contact.mjs index 6c38875f29f99..78bdce87db262 100644 --- a/components/specific/actions/update-create-contact/update-create-contact.mjs +++ b/components/specific/actions/update-create-contact/update-create-contact.mjs @@ -1,34 +1,111 @@ import specific from "../../specific.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "specific-update-create-contact", name: "Update or Create Contact", description: "Modify an existing contact's details or create a new one if the specified contact does not exist. [See the documentation](https://public-api.specific.app/docs/types/contact)", - version: "0.0.{{ts}}", + version: "0.0.1", type: "action", props: { specific, - contactId: { + contactEmail: { propDefinition: [ specific, - "contactId", + "contactEmail", ], + reloadProps: true, }, - contactInfo: { + companyId: { propDefinition: [ specific, - "contactInfo", + "companyId", ], + optional: true, + }, + name: { + type: "string", + label: "Name", + description: "Contact's name.", + optional: true, + }, + email: { + type: "string", + label: "New Email", + description: "New email to update", + optional: true, }, }, + async additionalProps() { + const props = {}; + if (this.content) { + const { data: { customFields } } = await this.specific.query({ + model: "customFields", + where: "{type: {equals: contact }}", + fields: "name", + }); + for (const { name } of customFields) { + props[`customField-${name}`] = { + type: "string", + label: name, + description: `Custom Field: ${name}`, + optional: true, + }; + } + } + return props; + }, async run({ $ }) { - const response = await this.specific.modifyOrCreateContact({ - contactId: this.contactId, - contactInfo: this.contactInfo, + const { + specific, + ...data + } = this; + + const customFields = this.specific.parseCustomFields(data); + + const response = await specific.mutation({ + $, + model: "createOrUpdateContact", + on: "CreatedOrUpdatedContacts", + data: `{ + ${this.companyId + ? `company: { + connect: { + id: "${this.companyId}" + } + }` + : ""} + ${customFields + ? `customFields: ${customFields}` + : ""} + ${this.email + ? `email: "${this.email}"` + : ""} + ${this.name + ? `name: "${this.name}"` + : ""} + }`, + where: `{email: "${this.contactEmail}"}`, + fields: ` + contacts { + id + name + email + visitorId + customFields + company { + contactsCount + customFields + id + name + visitorId + } + }`, }); - $.export("$summary", `Successfully updated or created contact with ID ${this.contactId}`); + if (response.errors) throw new Error(response.errors[0].message); + + $.export("$summary", `Successfully updated or created contact with email ${this.contactEmail}`); return response; }, }; + diff --git a/components/specific/common/utils.mjs b/components/specific/common/utils.mjs new file mode 100644 index 0000000000000..d0af679ba89cc --- /dev/null +++ b/components/specific/common/utils.mjs @@ -0,0 +1,8 @@ +export const stringifyObject = (obj) => { + if (!obj) return undefined; + + if (Array.isArray(obj)) { + return JSON.stringify(obj); + } + return obj; +}; diff --git a/components/specific/package.json b/components/specific/package.json index 42b1103e6b620..d5a4503190f70 100644 --- a/components/specific/package.json +++ b/components/specific/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/specific", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Specific Components", "main": "specific.app.mjs", "keywords": [ @@ -11,5 +11,9 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.0" } } + diff --git a/components/specific/sources/common/base.mjs b/components/specific/sources/common/base.mjs new file mode 100644 index 0000000000000..a50e5e820c6e6 --- /dev/null +++ b/components/specific/sources/common/base.mjs @@ -0,0 +1,82 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { stringifyObject } from "../../common/utils.mjs"; +import specific from "../../specific.app.mjs"; + +export default { + props: { + specific, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: "$.service.db", + code: { + type: "string", + label: "Code", + description: "Webhook's secret code", + secret: true, + }, + sourceId: { + propDefinition: [ + specific, + "sourceId", + ], + type: "string[]", + optional: true, + }, + }, + hooks: { + async activate() { + const { data } = await this.specific.mutation({ + model: "subscribeWebhook", + data: `{ + code: "${this.code}" + operation: ["${this.getOperation()}"] + ${this.sourceId?.length + ? `sources: ${stringifyObject(this.sourceId)}` + : ""} + url: "${this.http.endpoint}" + }`, + fields: ` + inactive + inactiveReason + operation + url + `, + on: "Webhook", + onValidationError: true, + }); + + if (data?.subscribeWebhook?.fieldErrors?.length) { + throw new ConfigurationError(data.subscribeWebhook?.fieldErrors[0].message); + } + + }, + async deactivate() { + await this.specific.mutation({ + model: "unsubscribeWebhook", + where: `{url: "${this.http.endpoint}"}`, + fields: ` + inactive + inactiveReason + operation + url + `, + on: "Webhook", + }); + + }, + }, + async run({ + headers, body, + }) { + if (headers["x-code"] != this.code) return; + + const ts = Date.parse(body.insertedAt || new Date()); + this.$emit(body, { + id: `${body.id || body.workspaceId}-${ts}`, + summary: this.getSummary(body), + ts: ts, + }); + }, +}; diff --git a/components/specific/sources/new-contact-instant/new-contact-instant.mjs b/components/specific/sources/new-contact-instant/new-contact-instant.mjs index fcf8f179a6366..1bfe57c155ca5 100644 --- a/components/specific/sources/new-contact-instant/new-contact-instant.mjs +++ b/components/specific/sources/new-contact-instant/new-contact-instant.mjs @@ -1,50 +1,22 @@ -import specific from "../../specific.app.mjs"; -import { axios } from "@pipedream/platform"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "specific-new-contact-instant", - name: "New Contact Created", - description: "Emit new event whenever a new contact is created. [See the documentation](https://public-api.specific.app/docs/introduction/welcome)", - version: "0.0.{{ts}}", + name: "New Contact Created (Instant)", + description: "Emit new event whenever a new contact is created.", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - specific: { - type: "app", - app: "specific", + methods: { + ...common.methods, + getOperation() { + return "new-contact"; }, - http: { - type: "$.interface.http", - customResponse: false, + getSummary(body) { + return `New contact created: ${body.name}`; }, - db: "$.service.db", - }, - hooks: { - async deploy() { - // Emit historical events - const contacts = await this.specific.emitNewContactCreated(); - for (const contact of contacts) { - this.$emit(contact, { - id: contact.id, - summary: `New contact: ${contact.name}`, - ts: Date.parse(contact.createdAt), - }); - } - }, - async activate() { - const webhookId = await this.specific.emitNewContactCreated(); - this.db.set("webhookId", webhookId); - }, - async deactivate() { - const webhookId = this.db.get("webhookId"); - await this.specific.emitNewContactCreated(webhookId); - }, - }, - async run(event) { - this.$emit(event.body, { - id: event.body.id, - summary: `New contact created: ${event.body.name}`, - ts: Date.parse(event.body.createdAt), - }); }, + sampleEmit, }; diff --git a/components/specific/sources/new-contact-instant/test-event.mjs b/components/specific/sources/new-contact-instant/test-event.mjs new file mode 100644 index 0000000000000..d2d4989e32463 --- /dev/null +++ b/components/specific/sources/new-contact-instant/test-event.mjs @@ -0,0 +1,7 @@ +export default { + "id": "specific_cnC1e5fe56v53o", + "email": "user@specific.app", + "name": "User Name", + "custom_fields": {}, + "workspace_id": "12345678-134-134-1234-1234567890" +} \ No newline at end of file diff --git a/components/specific/sources/new-conversation-instant/new-conversation-instant.mjs b/components/specific/sources/new-conversation-instant/new-conversation-instant.mjs index 7d5c38766db48..2b002a1816557 100644 --- a/components/specific/sources/new-conversation-instant/new-conversation-instant.mjs +++ b/components/specific/sources/new-conversation-instant/new-conversation-instant.mjs @@ -1,41 +1,22 @@ -import specific from "../../specific.app.mjs"; -import { axios } from "@pipedream/platform"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "specific-new-conversation-instant", name: "New Conversation Instant", - description: "Emit new event whenever a new conversation is initiated. [See the documentation](https://public-api.specific.app/docs/introduction/welcome)", - version: "0.0.{{ts}}", + description: "Emit new event whenever a new conversation is initiated.", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - specific: { - type: "app", - app: "specific", + methods: { + ...common.methods, + getOperation() { + return "new-conversation"; }, - http: { - type: "$.interface.http", - customResponse: false, + getSummary(body) { + return `New conversation initiated: ${body.name}`; }, - db: "$.service.db", - }, - hooks: { - async deploy() { - // No historical data to fetch - }, - async activate() { - // No activation steps needed - }, - async deactivate() { - // No deactivation steps needed - }, - }, - async run(event) { - const response = await this.specific.emitNewConversationInitiated(); - this.$emit(response, { - id: response.id, - summary: `New conversation initiated: ${response.id}`, - ts: Date.now(), - }); }, + sampleEmit, }; diff --git a/components/specific/sources/new-conversation-instant/test-event.mjs b/components/specific/sources/new-conversation-instant/test-event.mjs new file mode 100644 index 0000000000000..4569ed2d32bde --- /dev/null +++ b/components/specific/sources/new-conversation-instant/test-event.mjs @@ -0,0 +1,24 @@ +export default { + "text": "Conversation Text\n", + "name": "Conversation Name", + "customFields": { + "callId": "1234", + "release": "12" + }, + "assignee": null, + "company": { + "id": "specific_cnC1e5fe56v53o", + "name": "Secret Intelligence Service", + "customFields": {} + }, + "contact": { + "id": "specific_cnC1e5fe56v53o", + "name": "James Bond", + "email": "james@cia.gov", + "customFields": {} + }, + "source": null, + "insertedAt": "2024-07-19T20:28:17", + "sourceUrl": "htpps://urltest.com", + "workspaceId": "12345678-134-134-1234-1234567890" +} \ No newline at end of file diff --git a/components/specific/specific.app.mjs b/components/specific/specific.app.mjs index ebe970c531a6f..975bef2ac8c9a 100644 --- a/components/specific/specific.app.mjs +++ b/components/specific/specific.app.mjs @@ -4,95 +4,163 @@ export default { type: "app", app: "specific", propDefinitions: { - userId: { + customFields: { type: "string", - label: "User ID", - description: "The ID of the user to identify the conversation's owner", + label: "customFields", + description: "customFields", }, - title: { + insertedAt: { type: "string", - label: "Title", - description: "An optional title for the conversation", - optional: true, + label: "Inserted At", + description: "Date and time when the conversation was inserted in format YYYY-MM-DDTHH:mm:ss.sssZ", + default: (new Date).toISOString(), + }, + sourceId: { + type: "string", + label: "Source Id", + description: "The Id of the source associated with the conversation", + async options() { + return await this.listAsyncOptions({ + model: "sources", + }); + }, }, contactId: { type: "string", - label: "Contact ID", - description: "The ID of the contact to be modified or created", + label: "Contact Id", + description: "The id of the contact associated with the conversation", + async options() { + return await this.listAsyncOptions({ + model: "contacts", + }); + }, }, - contactInfo: { - type: "object", - label: "Contact Info", - description: "The updated details of the contact", + contactEmail: { + type: "string", + label: "Contact Email", + description: "The email of the contact", + async options() { + return await this.listAsyncOptions({ + model: "contacts", + fields: ` + name + email`, + }); + }, }, companyId: { type: "string", label: "Company ID", description: "The ID of the company to retrieve details for", + async options() { + return await this.listAsyncOptions({ + model: "companies", + }); + }, }, }, methods: { _baseUrl() { - return "https://public-api.specific.app"; + return "https://public-api.specific.app/graphql"; }, - 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}`, - }, - }); + _headers() { + return { + Authorization: `${this.$auth.api_key}`, + }; }, - async emitNewContactCreated() { - return this._makeRequest({ + _makeRequest({ + $ = this, ...opts + }) { + return axios($, { method: "POST", - path: "/contacts", - data: {}, // No props required + url: this._baseUrl(), + headers: this._headers(), + ...opts, }); }, - async emitNewConversationInitiated() { + query({ + model, where, fields, + }) { return this._makeRequest({ - method: "POST", - path: "/conversations", - data: {}, // No props required + data: { + query: `query { + ${model} ${where + ? `(where: ${where})` + : ""} { + ${fields} + } + }`, + }, }); }, - async initializeChatThread({ - userId, title, + mutation({ + model, data = null, fields, on, where, onValidationError = false, }) { return this._makeRequest({ - method: "POST", - path: "/conversations", data: { - userId, - title, + query: `mutation { + ${model} ( + ${data + ? `data: ${data}` + : ""} ${where + ? `where: ${where}` + : ""}) { + ...on ${on || model} { + ${fields} + } + ...on DbError { + message + } + ...on GraphQLError { + message + } + ${onValidationError + ? ` + ...on ValidationError { + message + fieldErrors { + message + path + } + }` + : ""} + } + }`, }, }); }, - async modifyOrCreateContact({ - contactId, contactInfo, + async listAsyncOptions ({ + model, fields = ` + id + name`, }) { - return this._makeRequest({ - method: "PUT", - path: `/contacts/${contactId}`, - data: contactInfo, + const { data } = await this.query({ + model, + fields, }); + + return data + ? data[model]?.map(({ + id, email, name: label, + }) => ({ + label, + value: id || email, + })) + : []; }, - async retrieveCompanyDetails({ companyId }) { - return this._makeRequest({ - method: "GET", - path: `/companies/${companyId}`, - }); + parseCustomFields(data) { + let customFields = Object.keys(data) + .filter((key) => key.startsWith("customField-")) + .reduce((res, key) => { + res[key.substring(12)] = data[key]; + return res; + }, {}); + + return JSON.stringify(customFields) + .replace(/"([^"]+)":/g, ` + $1:`) + .replace(/,/g, ` + `); }, }, }; From 70d7d077e6498043497a88ed4764398034658684 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 22 Jul 2024 12:34:33 -0300 Subject: [PATCH 3/4] some changes --- .../specific/actions/create-conversation/create-conversation.mjs | 1 + components/specific/actions/find-company/find-company.mjs | 1 + 2 files changed, 2 insertions(+) diff --git a/components/specific/actions/create-conversation/create-conversation.mjs b/components/specific/actions/create-conversation/create-conversation.mjs index b67484a01a796..152ca87f35439 100644 --- a/components/specific/actions/create-conversation/create-conversation.mjs +++ b/components/specific/actions/create-conversation/create-conversation.mjs @@ -155,6 +155,7 @@ export default { id name }`, + on: "Conversation", }); if (response.errors) throw new Error(response.errors[0].message); diff --git a/components/specific/actions/find-company/find-company.mjs b/components/specific/actions/find-company/find-company.mjs index c263149ee895c..0aacbc19bce04 100644 --- a/components/specific/actions/find-company/find-company.mjs +++ b/components/specific/actions/find-company/find-company.mjs @@ -25,6 +25,7 @@ export default { customFields visitorId contactsCount`, + on: "Company", }); $.export("$summary", `Successfully retrieved details for company ID: ${this.companyId}`); From 534bc44ae9c8b7a332e85101ca7d527c3cb248ca Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Mon, 22 Jul 2024 12:34:38 -0300 Subject: [PATCH 4/4] pnpm update --- pnpm-lock.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55908b64e245b..f696921f7d9f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8682,7 +8682,10 @@ importers: specifiers: {} components/specific: - specifiers: {} + specifiers: + '@pipedream/platform': ^3.0.0 + dependencies: + '@pipedream/platform': 3.0.0 components/speechace: specifiers: