diff --git a/components/slicktext/.gitignore b/components/slicktext/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/slicktext/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/slicktext/actions/add-contact-to-lists/add-contact-to-lists.mjs b/components/slicktext/actions/add-contact-to-lists/add-contact-to-lists.mjs new file mode 100644 index 0000000000000..59dfe575e25fd --- /dev/null +++ b/components/slicktext/actions/add-contact-to-lists/add-contact-to-lists.mjs @@ -0,0 +1,39 @@ +import { parseObject } from "../../common/utils.mjs"; +import slicktext from "../../slicktext.app.mjs"; + +export default { + key: "slicktext-add-contact-to-lists", + name: "Add Contact To Lists", + description: "Add a contact to lists. [See the documentation](https://api.slicktext.com/docs/v2/lists#scroll-to-add-contacts-to-lists)", + version: "0.0.1", + type: "action", + props: { + slicktext, + contactId: { + propDefinition: [ + slicktext, + "contactId", + ], + }, + listIds: { + propDefinition: [ + slicktext, + "listIds", + ], + }, + }, + async run({ $ }) { + const response = await this.slicktext.addContactToLists({ + $, + data: [ + { + contact_id: this.contactId, + lists: parseObject(this.listIds), + }, + ], + }); + + $.export("$summary", `Successfully added contact with ID: ${this.contactId} to lists with ID: ${parseObject(this.listIds).join()}`); + return response; + }, +}; diff --git a/components/slicktext/actions/common/base.mjs b/components/slicktext/actions/common/base.mjs new file mode 100644 index 0000000000000..3cf53cfdd2e7b --- /dev/null +++ b/components/slicktext/actions/common/base.mjs @@ -0,0 +1,88 @@ +import slicktext from "../../slicktext.app.mjs"; + +export const commonProps = { + firstName: { + propDefinition: [ + slicktext, + "firstName", + ], + optional: true, + }, + lastName: { + propDefinition: [ + slicktext, + "lastName", + ], + optional: true, + }, + email: { + propDefinition: [ + slicktext, + "email", + ], + optional: true, + }, + birthdate: { + propDefinition: [ + slicktext, + "birthdate", + ], + optional: true, + }, + address: { + propDefinition: [ + slicktext, + "address", + ], + optional: true, + }, + city: { + propDefinition: [ + slicktext, + "city", + ], + optional: true, + }, + state: { + propDefinition: [ + slicktext, + "state", + ], + optional: true, + }, + zip: { + propDefinition: [ + slicktext, + "zip", + ], + optional: true, + }, + country: { + propDefinition: [ + slicktext, + "country", + ], + optional: true, + }, + timezone: { + propDefinition: [ + slicktext, + "timezone", + ], + optional: true, + }, + language: { + propDefinition: [ + slicktext, + "language", + ], + optional: true, + }, + optInStatus: { + propDefinition: [ + slicktext, + "optInStatus", + ], + optional: true, + }, +}; diff --git a/components/slicktext/actions/create-contact/create-contact.mjs b/components/slicktext/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..d336836b36847 --- /dev/null +++ b/components/slicktext/actions/create-contact/create-contact.mjs @@ -0,0 +1,41 @@ +import { objectCamelToSnakeCase } from "../../common/utils.mjs"; +import slicktext from "../../slicktext.app.mjs"; +import { commonProps } from "../common/base.mjs"; + +export default { + key: "slicktext-create-contact", + name: "Create Contact", + description: "Add a new contact to your messaging list. [See the documentation](https://api.slicktext.com/docs/v1/contacts)", + version: "0.0.1", + type: "action", + props: { + slicktext, + mobileNumber: { + propDefinition: [ + slicktext, + "mobileNumber", + ], + }, + ...commonProps, + forceDoubleOptIn: { + propDefinition: [ + slicktext, + "forceDoubleOptIn", + ], + optional: true, + }, + }, + async run({ $ }) { + const { + slicktext, + ...data + } = this; + + const response = await slicktext.createContact({ + $, + data: objectCamelToSnakeCase(data), + }); + $.export("$summary", `Successfully initiated opt-in for contact with number: ${this.mobileNumber}`); + return response; + }, +}; diff --git a/components/slicktext/actions/edit-contact/edit-contact.mjs b/components/slicktext/actions/edit-contact/edit-contact.mjs new file mode 100644 index 0000000000000..ae9d9ffa84dc2 --- /dev/null +++ b/components/slicktext/actions/edit-contact/edit-contact.mjs @@ -0,0 +1,44 @@ +import { objectCamelToSnakeCase } from "../../common/utils.mjs"; +import slicktext from "../../slicktext.app.mjs"; +import { commonProps } from "../common/base.mjs"; + +export default { + key: "slicktext-edit-contact", + name: "Edit Contact", + description: "Updates personal details of an existing contact. [See the documentation](https://api.slicktext.com/docs/v1/contacts#6)", + version: "0.0.1", + type: "action", + props: { + slicktext, + contactId: { + propDefinition: [ + slicktext, + "contactId", + ], + }, + mobileNumber: { + propDefinition: [ + slicktext, + "mobileNumber", + ], + optional: true, + }, + ...commonProps, + }, + async run({ $ }) { + const { + slicktext, + contactId, + ...data + } = this; + + const response = await slicktext.updateContact({ + $, + contactId, + data: objectCamelToSnakeCase(data), + }); + + $.export("$summary", `Successfully updated contact with ID: ${this.contactId}`); + return response; + }, +}; diff --git a/components/slicktext/app/slicktext.app.ts b/components/slicktext/app/slicktext.app.ts deleted file mode 100644 index cf7f22419a1a3..0000000000000 --- a/components/slicktext/app/slicktext.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "slicktext", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); diff --git a/components/slicktext/common/constants.mjs b/components/slicktext/common/constants.mjs new file mode 100644 index 0000000000000..38b6153fb48fc --- /dev/null +++ b/components/slicktext/common/constants.mjs @@ -0,0 +1,15 @@ +export const LIMIT = 250; + +export const OPT_IN_STATUS_OPTIONS = [ + "not subscribed", + "subscribed", + "unsubscribed", + "blocked", +]; + +export const TIMEZONE_OPTIONS = [ + "America/New_York", + "America/Chicago", + "America/Denver", + "America/Los_Angeles", +]; diff --git a/components/slicktext/common/utils.mjs b/components/slicktext/common/utils.mjs new file mode 100644 index 0000000000000..f8a334dda44cb --- /dev/null +++ b/components/slicktext/common/utils.mjs @@ -0,0 +1,36 @@ +export const objectCamelToSnakeCase = (obj) => { + return Object.entries(obj).reduce((acc, [ + key, + value, + ]) => { + const newKey = key.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`); + acc[newKey] = value; + return acc; + } + , {}); +}; + +export const parseObject = (obj) => { + if (!obj) return undefined; + + if (Array.isArray(obj)) { + return obj.map((item) => { + if (typeof item === "string") { + try { + return JSON.parse(item); + } catch (e) { + return item; + } + } + return item; + }); + } + if (typeof obj === "string") { + try { + return JSON.parse(obj); + } catch (e) { + return obj; + } + } + return obj; +}; diff --git a/components/slicktext/package.json b/components/slicktext/package.json index 7d77925292b8a..d0777228bc4a7 100644 --- a/components/slicktext/package.json +++ b/components/slicktext/package.json @@ -1,16 +1,18 @@ { "name": "@pipedream/slicktext", - "version": "0.0.3", + "version": "0.1.0", "description": "Pipedream SlickText Components", - "main": "dist/app/slicktext.app.mjs", + "main": "slicktext.app.mjs", "keywords": [ "pipedream", "slicktext" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/slicktext", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/slicktext/slicktext.app.mjs b/components/slicktext/slicktext.app.mjs new file mode 100644 index 0000000000000..a36812d5e550e --- /dev/null +++ b/components/slicktext/slicktext.app.mjs @@ -0,0 +1,188 @@ +import { axios } from "@pipedream/platform"; +import { + LIMIT, OPT_IN_STATUS_OPTIONS, TIMEZONE_OPTIONS, +} from "./common/constants.mjs"; + +export default { + type: "app", + app: "slicktext", + propDefinitions: { + contactId: { + type: "string", + label: "Contact ID", + description: "The ID of the contact to be updated", + async options({ page }) { + const { data } = await this.listContacts({ + params: { + limit: LIMIT, + offset: LIMIT * page, + }, + }); + + return data.map(({ + contact_id: value, first_name: fName, last_name: lName, email, + }) => ({ + label: `${fName} ${lName} (${email})`, + value, + })); + }, + }, + listIds: { + type: "string[]", + label: "List IDs", + description: "A list of **List IDs** that represent the list(s) the contact will be added to", + async options({ page }) { + const { data } = await this.listLists({ + params: { + limit: LIMIT, + offset: LIMIT * page, + }, + }); + + return data.map(({ + contact_list_id: value, name: label, + }) => ({ + label, + value, + })); + }, + }, + mobileNumber: { + type: "string", + label: "Mobile Number", + description: "The US phone number of the contact. Must be at least 10 digits. It will be normalized to digits-only, preceded by a +", + }, + firstName: { + type: "string", + label: "First Name", + description: "The first name of contact", + }, + lastName: { + type: "string", + label: "Last Name", + description: "The last name of contact", + }, + email: { + type: "string", + label: "Email Address", + description: "The email address of the contact", + }, + birthdate: { + type: "string", + label: "Birthdate", + description: "The birthday of the contact formatted as: YYYY-MM-DD", + }, + address: { + type: "string", + label: "Address", + description: "The street address of the contact", + }, + city: { + type: "string", + label: "City", + description: "The city of the contact", + }, + state: { + type: "string", + label: "State", + description: "The state of the contact", + }, + zip: { + type: "string", + label: "Zip Code", + description: "The zip code of the contact", + }, + country: { + type: "string", + label: "Country", + description: "The country of the contact", + }, + timezone: { + type: "string", + label: "Timezone", + description: "The time zone of contact", + options: TIMEZONE_OPTIONS, + }, + language: { + type: "string", + label: "Language", + description: "The primary language of contact (“en”)", + }, + optInStatus: { + type: "string", + label: "Opt-in Status", + description: "The opt-in status of contact (Default is not subscribed)", + options: OPT_IN_STATUS_OPTIONS, + }, + forceDoubleOptIn: { + type: "boolean", + label: "Force Double Opt-in", + description: "Set to “true” to force the user to agree to subscribing via double opt-in message", + }, + }, + methods: { + _baseUrl() { + return "https://dev.slicktext.com/v1"; + }, + _headers() { + return { + authorization: `Bearer ${this.$auth.public_api_key}`, + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + headers: this._headers(), + ...opts, + }); + }, + async getBrandId() { + const response = await this._makeRequest({ + path: "/brands", + }); + return response.brand_id; + }, + async listContacts(opts = {}) { + const brandId = await this.getBrandId(); + return this._makeRequest({ + path: `/brands/${brandId}/contacts`, + ...opts, + }); + }, + async listLists(opts = {}) { + const brandId = await this.getBrandId(); + return this._makeRequest({ + path: `/brands/${brandId}/lists`, + ...opts, + }); + }, + async createContact(opts = {}) { + const brandId = await this.getBrandId(); + return this._makeRequest({ + method: "POST", + path: `/brands/${brandId}/contacts`, + ...opts, + }); + }, + async updateContact({ + contactId, ...opts + }) { + const brandId = await this.getBrandId(); + return this._makeRequest({ + method: "PUT", + path: `/brands/${brandId}/contacts/${contactId}`, + ...opts, + }); + }, + async addContactToLists(opts = {}) { + const brandId = await this.getBrandId(); + return this._makeRequest({ + method: "POST", + path: `/brands/${brandId}/lists/contacts`, + ...opts, + }); + }, + }, +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c3b8832f2cc43..d583c81a55a11 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12134,7 +12134,11 @@ importers: components/slack_demo_app_1: {} - components/slicktext: {} + components/slicktext: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 components/slite: dependencies: