diff --git a/components/icontact/.gitignore b/components/icontact/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/icontact/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/icontact/actions/create-contact/create-contact.mjs b/components/icontact/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..9e12781492ccc --- /dev/null +++ b/components/icontact/actions/create-contact/create-contact.mjs @@ -0,0 +1,124 @@ +import { STATUS_OPTIONS } from "../../common/constants.mjs"; +import { checkWarnings } from "../../common/utils.mjs"; +import icontact from "../../icontact.app.mjs"; + +export default { + key: "icontact-create-contact", + name: "Create Contact", + description: "Creates a new contact within the iContact account. [See the documentation](https://help.icontact.com/customers/s/article/Contacts-iContact-API?r=153&ui-knowledge-components-aura-actions.KnowledgeArticleVersionCreateDraftFromOnlineAction.createDraftFromOnlineArticle=1)", + version: "0.0.1", + type: "action", + props: { + icontact, + email: { + type: "string", + label: "Email", + description: "The contact's email address. **Note: The email address must be unique**.", + }, + prefix: { + type: "string", + label: "Prefix", + description: "The contact's salutation. **E.g. Miss**", + optional: true, + }, + firstName: { + type: "string", + label: "First Name", + description: "The contact's first name.", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "The contact's last name.", + optional: true, + }, + suffix: { + type: "string", + label: "Suffix", + description: "The contact's name qualifications **E.g. III**.", + optional: true, + }, + street: { + type: "string", + label: "Street", + description: "The contact's street address information.", + optional: true, + }, + street2: { + type: "string", + label: "Street 2", + description: "The contact's line 2 information.", + optional: true, + }, + city: { + type: "string", + label: "City", + description: "The contact's city information.", + optional: true, + }, + state: { + type: "string", + label: "State", + description: "The contact's state information.", + optional: true, + }, + postalCode: { + type: "string", + label: "Postal Code", + description: "The contact's postal code information.", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "The contact's phone number.", + optional: true, + }, + fax: { + type: "string", + label: "Fax", + description: "The contact's fax number.", + optional: true, + }, + business: { + type: "string", + label: "Business", + description: "The contact's business phone number.", + optional: true, + }, + status: { + type: "string", + label: "Status", + description: "The subscription status of the contact.", + options: STATUS_OPTIONS, + optional: true, + }, + }, + async run({ $ }) { + const { + icontact, + ...contact + } = this; + + const { contacts } = await icontact.searchContact({ + params: { + email: contact.email, + }, + }); + + if (contacts.length) throw new Error("A contact with the provided email already exists."); + + const response = await icontact.createContact({ + $, + data: { + contact, + }, + }); + + checkWarnings(response); + + $.export("$summary", `Successfully created contact with ID: ${response.contacts[0].contactId}`); + return response.contacts[0]; + }, +}; diff --git a/components/icontact/actions/create-message/create-message.mjs b/components/icontact/actions/create-message/create-message.mjs new file mode 100644 index 0000000000000..97652ac94b0a4 --- /dev/null +++ b/components/icontact/actions/create-message/create-message.mjs @@ -0,0 +1,82 @@ +import { MESSAGE_TYPE_OPTIONS } from "../../common/constants.mjs"; +import { checkWarnings } from "../../common/utils.mjs"; +import icontact from "../../icontact.app.mjs"; + +export default { + key: "icontact-create-message", + name: "Create and Dispatch Message", + description: "Creates and dispatches a new message using custom HTML. [See the documentation](https://help.icontact.com/customers/s/article/Messages-iContact-API?r=153&ui-knowledge-components-aura-actions.KnowledgeArticleVersionCreateDraftFromOnlineAction.createDraftFromOnlineArticle=1)", + version: "0.0.1", + type: "action", + props: { + icontact, + campaignId: { + propDefinition: [ + icontact, + "campaignId", + ], + }, + messageType: { + type: "string", + label: "Message Type", + description: "The kind of message being added.", + options: MESSAGE_TYPE_OPTIONS, + }, + subject: { + type: "string", + label: "Subject", + description: "The subject line of the email.", + }, + htmlBody: { + type: "string", + label: "HTML Body", + description: "Contains the HTML version of the email message body.", + optional: true, + }, + textBody: { + type: "string", + label: "Text Body", + description: "Contains the text version of the email message body.", + optional: true, + }, + messageName: { + type: "string", + label: "Message Name", + description: "The reference name of the message. This is used for organizational purposes and will not be seen by your contacts.", + optional: true, + }, + previewText: { + type: "string", + label: "Preview Text", + description: "Indicates the preview text that some email systems display before opening the email.", + optional: true, + }, + replyToCampaignId: { + propDefinition: [ + icontact, + "campaignId", + ], + label: "Reply To Campaign Id", + description: "Indicates the sender property where you want reply emails to be sent to.", + optional: true, + }, + }, + async run({ $ }) { + const { + icontact, + ...message + } = this; + + const response = await icontact.createMessage({ + $, + data: { + message, + }, + }); + + checkWarnings(response); + + $.export("$summary", `Successfully created message with ID: ${response.messages[0].messageId}`); + return response.messages[0]; + }, +}; diff --git a/components/icontact/actions/subscribe-contact-list/subscribe-contact-list.mjs b/components/icontact/actions/subscribe-contact-list/subscribe-contact-list.mjs new file mode 100644 index 0000000000000..b95415988f361 --- /dev/null +++ b/components/icontact/actions/subscribe-contact-list/subscribe-contact-list.mjs @@ -0,0 +1,41 @@ +import { checkWarnings } from "../../common/utils.mjs"; +import icontact from "../../icontact.app.mjs"; + +export default { + key: "icontact-subscribe-contact-list", + name: "Subscribe Contact to List", + description: "Adds a contact to a specific list within iContact. [See the documentation](https://help.icontact.com/customers/s/article/Subscriptions-iContact-API?r=153&ui-knowledge-components-aura-actions.KnowledgeArticleVersionCreateDraftFromOnlineAction.createDraftFromOnlineArticle=1)", + version: "0.0.1", + type: "action", + props: { + icontact, + contactId: { + propDefinition: [ + icontact, + "contactId", + ], + }, + listId: { + propDefinition: [ + icontact, + "listId", + ], + }, + }, + async run({ $ }) { + const response = await this.icontact.subscribeContactToList({ + data: { + subscription: { + contactId: this.contactId, + listId: this.listId, + status: "normal", + }, + }, + }); + + checkWarnings(response); + + $.export("$summary", `Successfully created subscription with ID: ${response.subscriptions[0].subscriptionId}`); + return response.subscriptions[0]; + }, +}; diff --git a/components/icontact/app/icontact.app.ts b/components/icontact/app/icontact.app.ts deleted file mode 100644 index 09268f74246d3..0000000000000 --- a/components/icontact/app/icontact.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "icontact", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); \ No newline at end of file diff --git a/components/icontact/common/constants.mjs b/components/icontact/common/constants.mjs new file mode 100644 index 0000000000000..f8fe8fae2d370 --- /dev/null +++ b/components/icontact/common/constants.mjs @@ -0,0 +1,37 @@ +export const STATUS_OPTIONS = [ + { + label: "Normal - Contact can receive emails sent to them.", + value: "normal", + }, + { + label: "Bounced - Contact is unreachable via email.", + value: "bounced", + }, + { + label: "Do Not Contact - Contact is blocked from receiving emails.", + value: "donotcontact", + }, + { + label: "Pending - Contact must confirm a subscription before receiving emails.", + value: "pending", + }, + { + label: "Invitable - Contact must be sent an invitation message before receiving emails.", + value: "invitable", + }, + { + label: "Deleted - Contact was deleted from your records", + value: "deleted", + }, +]; + +export const MESSAGE_TYPE_OPTIONS = [ + { + label: "Normal - An email message.", + value: "normal", + }, + { + label: "Confirmation - An email that requests a contact to confirm their subscription", + value: "confirmation", + }, +]; diff --git a/components/icontact/common/utils.mjs b/components/icontact/common/utils.mjs new file mode 100644 index 0000000000000..429936ef55a4e --- /dev/null +++ b/components/icontact/common/utils.mjs @@ -0,0 +1,10 @@ +import { ConfigurationError } from "@pipedream/platform"; + +export const checkWarnings = (response) => { + if (response.warnings) { + throw new ConfigurationError(response.warnings[0]); + } + if (response.notices) { + throw new ConfigurationError(response.notices[0]); + } +}; diff --git a/components/icontact/icontact.app.mjs b/components/icontact/icontact.app.mjs new file mode 100644 index 0000000000000..52a15385fe926 --- /dev/null +++ b/components/icontact/icontact.app.mjs @@ -0,0 +1,135 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "icontact", + propDefinitions: { + campaignId: { + type: "string", + label: "Campaign ID", + description: "The sender property from which the message will pull its sending information.", + async options() { + const { campaigns } = await this.listCampaigns(); + + return campaigns.map(({ + campaignId: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + contactId: { + type: "string", + label: "Contact ID", + description: "The contact that will be subscribed to the list.", + async options() { + const { contacts } = await this.listContacts(); + + return contacts.map(({ + contactId: value, email: label, + }) => ({ + label, + value, + })); + }, + }, + listId: { + type: "string", + label: "List ID", + description: "The ID of the list", + async options() { + const { lists } = await this.listLists(); + + return lists.map(({ + listId: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + }, + methods: { + _baseUrl() { + return `https://app.icontact.com/icp/a/${this.$auth.account_id}/c/${this.$auth.client_folder_id}`; + }, + _headers() { + return { + "Accept": "application/json", + "Content-Type": "application/json", + "API-Version": "2.2", + "API-AppId": `${this.$auth.api_app_id}`, + "API-Username": `${this.$auth.api_username}`, + "API-Password": `${this.$auth.api_password}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + headers: this._headers(), + ...opts, + }); + }, + createContact(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/contacts", + ...opts, + }); + }, + createMessage(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/messages", + ...opts, + }); + }, + subscribeContactToList(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/subscriptions", + ...opts, + }); + }, + searchContact(opts = {}) { + return this._makeRequest({ + path: "/contacts", + ...opts, + }); + }, + listCampaigns(opts = {}) { + return this._makeRequest({ + path: "/campaigns", + ...opts, + }); + }, + listContacts(opts = {}) { + return this._makeRequest({ + path: "/contacts", + ...opts, + }); + }, + listLists(opts = {}) { + return this._makeRequest({ + path: "/lists", + ...opts, + }); + }, + createWebhook(opts = {}) { + return this._makeRequest({ + method: "POST", + path: "/webhooks", + ...opts, + }); + }, + deleteWebhook(webhookId) { + return this._makeRequest({ + method: "DELETE", + path: `/webhooks/${webhookId}`, + }); + }, + }, +}; diff --git a/components/icontact/package.json b/components/icontact/package.json index 71c16d5269151..1c73c55088f90 100644 --- a/components/icontact/package.json +++ b/components/icontact/package.json @@ -1,16 +1,18 @@ { "name": "@pipedream/icontact", - "version": "0.0.2", + "version": "0.1.0", "description": "Pipedream iContact Components", - "main": "dist/app/icontact.app.mjs", + "main": "icontact.app.mjs", "keywords": [ "pipedream", "icontact" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/icontact", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/icontact/sources/common/base.mjs b/components/icontact/sources/common/base.mjs new file mode 100644 index 0000000000000..9d1c654d89a92 --- /dev/null +++ b/components/icontact/sources/common/base.mjs @@ -0,0 +1,41 @@ +import icontact from "../../icontact.app.mjs"; + +export default { + props: { + icontact, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + }, + methods: { + _getHookId() { + return this.db.get("hookId"); + }, + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + }, + hooks: { + async activate() { + const response = await this.icontact.createWebhook({ + data: [ + { + url: this.http.endpoint, + eventId: this.getEventType(), + }, + ], + }); + this._setHookId(response.webhooks[0].webhookId); + }, + async deactivate() { + const webhookId = this._getHookId(); + await this.icontact.deleteWebhook(webhookId); + }, + }, + async run({ body }) { + const data = body[0]; + this.$emit(data, this.generateMeta(data)); + }, +}; diff --git a/components/icontact/sources/contact-subscribed-instant/contact-subscribed-instant.mjs b/components/icontact/sources/contact-subscribed-instant/contact-subscribed-instant.mjs new file mode 100644 index 0000000000000..36b9d16be3e79 --- /dev/null +++ b/components/icontact/sources/contact-subscribed-instant/contact-subscribed-instant.mjs @@ -0,0 +1,28 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "icontact-contact-subscribed-instant", + name: "New Contact Subscribed (Instant)", + description: "Emit new event when a contact is subscribed to a list.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "contact_subscribed"; + }, + generateMeta({ + contact, list, + }) { + return { + id: `${contact.contactId}-${list.listId}`, + summary: `Contact "${contact.email}" subscribed to list "${list.name}"`, + ts: Date.now(), + }; + }, + }, + sampleEmit, +}; diff --git a/components/icontact/sources/contact-subscribed-instant/test-event.mjs b/components/icontact/sources/contact-subscribed-instant/test-event.mjs new file mode 100644 index 0000000000000..7ae7fb3dd5d20 --- /dev/null +++ b/components/icontact/sources/contact-subscribed-instant/test-event.mjs @@ -0,0 +1,30 @@ +export default { + "contact": { + "contactId":123, + "prefix":"Miss", + "firstName":"Mary", + "lastName":"Smith", + "suffix":"III", + "street":"2635 Meridian Parkway", + "street2":"Suite 100", + "city":"Durham", + "state":"NC", + "postalCode":"27713", + "phone":"8668039462", + "fax":"8668039462", + "business":"8668039462", + "email":"smith@icontact.com", + "createDate":"2009-06-18 22:39:54", + "bounceCount":3, + "status":"normal" + }, + "list": { + "listId": 375628, + "name": "My contacts", + "publicname": "Bakery Newsletter", + "description": "A list of every contact who subscribed on my main web site", + "emailOwnerOnChange": 1, + "welcomeOnManualAdd": 0, + "welcomeOnSignupAdd": 1 + } +} \ No newline at end of file diff --git a/components/icontact/sources/new-contact-instant/new-contact-instant.mjs b/components/icontact/sources/new-contact-instant/new-contact-instant.mjs new file mode 100644 index 0000000000000..42d6cae0d30cc --- /dev/null +++ b/components/icontact/sources/new-contact-instant/new-contact-instant.mjs @@ -0,0 +1,26 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "icontact-new-contact-instant", + name: "New Contact Created (Instant)", + description: "Emit new event when a contact is created.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "contact_created"; + }, + generateMeta({ contact }) { + return { + id: contact.contactId, + summary: `New contact created: ${contact.email}`, + ts: contact.createDate, + }; + }, + }, + sampleEmit, +}; diff --git a/components/icontact/sources/new-contact-instant/test-event.mjs b/components/icontact/sources/new-contact-instant/test-event.mjs new file mode 100644 index 0000000000000..b28417cf8f801 --- /dev/null +++ b/components/icontact/sources/new-contact-instant/test-event.mjs @@ -0,0 +1,21 @@ +export default { + "contact": { + "contactId":123, + "prefix":"Miss", + "firstName":"Mary", + "lastName":"Smith", + "suffix":"III", + "street":"2635 Meridian Parkway", + "street2":"Suite 100", + "city":"Durham", + "state":"NC", + "postalCode":"27713", + "phone":"8668039462", + "fax":"8668039462", + "business":"8668039462", + "email":"smith@icontact.com", + "createDate":"2009-06-18 22:39:54", + "bounceCount":3, + "status":"normal" + } +} \ No newline at end of file diff --git a/components/icontact/sources/updated-contact-instant/test-event.mjs b/components/icontact/sources/updated-contact-instant/test-event.mjs new file mode 100644 index 0000000000000..b28417cf8f801 --- /dev/null +++ b/components/icontact/sources/updated-contact-instant/test-event.mjs @@ -0,0 +1,21 @@ +export default { + "contact": { + "contactId":123, + "prefix":"Miss", + "firstName":"Mary", + "lastName":"Smith", + "suffix":"III", + "street":"2635 Meridian Parkway", + "street2":"Suite 100", + "city":"Durham", + "state":"NC", + "postalCode":"27713", + "phone":"8668039462", + "fax":"8668039462", + "business":"8668039462", + "email":"smith@icontact.com", + "createDate":"2009-06-18 22:39:54", + "bounceCount":3, + "status":"normal" + } +} \ No newline at end of file diff --git a/components/icontact/sources/updated-contact-instant/updated-contact-instant.mjs b/components/icontact/sources/updated-contact-instant/updated-contact-instant.mjs new file mode 100644 index 0000000000000..0afde6a107cff --- /dev/null +++ b/components/icontact/sources/updated-contact-instant/updated-contact-instant.mjs @@ -0,0 +1,26 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "icontact-updated-contact-instant", + name: "Contact Updated (Instant)", + description: "Emit new event when a contact is updated.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getEventType() { + return "contact_updated"; + }, + generateMeta({ contact }) { + return { + id: contact.contactId, + summary: `New contact updated: ${contact.email}`, + ts: Date.now(), + }; + }, + }, + sampleEmit, +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 291bdbd744ffc..74a6da320cf6e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6350,7 +6350,11 @@ importers: components/ical: {} - components/icontact: {} + components/icontact: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/icypeas: dependencies: @@ -15521,14 +15525,6 @@ importers: specifier: ^6.0.0 version: 6.2.0 - modelcontextprotocol/node_modules2/@modelcontextprotocol/sdk/dist/cjs: {} - - modelcontextprotocol/node_modules2/@modelcontextprotocol/sdk/dist/esm: {} - - modelcontextprotocol/node_modules2/zod-to-json-schema/dist/cjs: {} - - modelcontextprotocol/node_modules2/zod-to-json-schema/dist/esm: {} - packages/ai: dependencies: '@pipedream/sdk': @@ -35915,6 +35911,8 @@ snapshots: '@putout/operator-filesystem': 5.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3)) '@putout/operator-json': 2.2.0 putout: 36.13.1(eslint@8.57.1)(typescript@5.6.3) + transitivePeerDependencies: + - supports-color '@putout/operator-regexp@1.0.0(putout@36.13.1(eslint@8.57.1)(typescript@5.6.3))': dependencies: