diff --git a/components/apollo_io_oauth/actions/add-contacts-to-sequence/add-contacts-to-sequence.mjs b/components/apollo_io_oauth/actions/add-contacts-to-sequence/add-contacts-to-sequence.mjs new file mode 100644 index 0000000000000..4a6690c15b9af --- /dev/null +++ b/components/apollo_io_oauth/actions/add-contacts-to-sequence/add-contacts-to-sequence.mjs @@ -0,0 +1,76 @@ +import app from "../../apollo_io_oauth.app.mjs"; + +export default { + key: "apollo_io_oauth-add-contacts-to-sequence", + name: "Add Contacts to Sequence", + description: "Adds one or more contacts to a sequence in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#add-contacts-to-sequence)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + sequenceId: { + propDefinition: [ + app, + "sequenceId", + ], + }, + contactIds: { + propDefinition: [ + app, + "contactId", + ], + type: "string[]", + label: "Contact IDs", + description: "Identifiers of the contacts to add to sequence", + }, + emailAccountId: { + propDefinition: [ + app, + "emailAccountId", + ], + }, + sequenceNoEmail: { + type: "boolean", + label: "Sequence No Email", + description: " Whether to still sequence the contact if he/she does not have an email address", + optional: true, + }, + sequenceActiveInOtherCampaigns: { + type: "boolean", + label: "Sequence Active in Other Campaigns", + description: "Whether to still sequence the contact if he/she is active or paused in another sequence", + optional: true, + }, + sequenceFinishedInOtherCampaigns: { + type: "boolean", + label: "Sequence Finished in Other Campaigns", + description: "Whether to still sequence the contact if he/she already finished another sequence", + optional: true, + }, + }, + async run({ $ }) { + const { contacts } = await this.app.addContactsToSequence({ + $, + sequenceId: this.sequenceId, + data: { + contact_ids: this.contactIds, + emailer_campaign_id: this.sequenceId, + send_email_from_email_account_id: this.emailAccountId, + sequence_no_email: this.sequenceNoEmail, + sequence_active_in_other_campaigns: this.sequenceActiveInOtherCampaigns, + sequence_finished_in_other_campaigns: this.sequenceFinishedInOtherCampaigns, + }, + }); + + $.export("$summary", `Successfully added ${contacts.length} contact${contacts.length === 1 + ? "" + : "s"} to sequence.`); + + return contacts; + }, +}; diff --git a/components/apollo_io_oauth/actions/create-account/create-account.mjs b/components/apollo_io_oauth/actions/create-account/create-account.mjs new file mode 100644 index 0000000000000..629008e3e18ce --- /dev/null +++ b/components/apollo_io_oauth/actions/create-account/create-account.mjs @@ -0,0 +1,58 @@ +import app from "../../apollo_io_oauth.app.mjs"; + +export default { + key: "apollo_io_oauth-create-account", + name: "Create Account", + description: "Creates a new account in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#create-an-account)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + name: { + propDefinition: [ + app, + "name", + ], + }, + domain: { + propDefinition: [ + app, + "domain", + ], + }, + address: { + propDefinition: [ + app, + "address", + ], + description: "The address string for this account, Apollo will intelligently infer the city, state, country, and time zone from your address", + }, + phone: { + propDefinition: [ + app, + "phone", + ], + description: "The corporate phone for this account", + }, + }, + async run({ $ }) { + const { account } = await this.app.createAccount({ + $, + data: { + name: this.name, + domain: this.domain, + raw_address: this.address, + phone_number: this.phone, + }, + }); + + $.export("$summary", `Successfully created account with ID ${account.id}`); + + return account; + }, +}; diff --git a/components/apollo_io_oauth/actions/create-contact/create-contact.mjs b/components/apollo_io_oauth/actions/create-contact/create-contact.mjs new file mode 100644 index 0000000000000..58170fed4b8a7 --- /dev/null +++ b/components/apollo_io_oauth/actions/create-contact/create-contact.mjs @@ -0,0 +1,100 @@ +import app from "../../apollo_io_oauth.app.mjs"; + +export default { + key: "apollo_io_oauth-create-contact", + name: "Create Contact", + description: "Creates a new contact in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#create-a-contact)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + email: { + propDefinition: [ + app, + "email", + ], + }, + firstName: { + propDefinition: [ + app, + "firstName", + ], + }, + lastName: { + propDefinition: [ + app, + "lastName", + ], + }, + title: { + propDefinition: [ + app, + "title", + ], + }, + accountId: { + propDefinition: [ + app, + "accountId", + ], + optional: true, + }, + websiteUrl: { + propDefinition: [ + app, + "websiteUrl", + ], + }, + labelNames: { + propDefinition: [ + app, + "labelNames", + ], + }, + contactStageId: { + propDefinition: [ + app, + "contactStageId", + ], + optional: true, + }, + address: { + propDefinition: [ + app, + "address", + ], + }, + phone: { + propDefinition: [ + app, + "phone", + ], + }, + }, + async run({ $ }) { + const { contact } = await this.app.createContact({ + $, + data: { + email: this.email, + first_name: this.firstName, + last_name: this.lastName, + title: this.title, + account_id: this.accountId, + website_url: this.websiteUrl, + label_names: this.labelNames, + contact_stage_id: this.contactStageId, + present_raw_address: this.address, + direct_phone: this.phone, + }, + }); + + $.export("$summary", `Successfully created contact with ID ${contact.id}`); + + return contact; + }, +}; diff --git a/components/apollo_io_oauth/actions/create-opportunity/create-opportunity.mjs b/components/apollo_io_oauth/actions/create-opportunity/create-opportunity.mjs new file mode 100644 index 0000000000000..fed9d1a215030 --- /dev/null +++ b/components/apollo_io_oauth/actions/create-opportunity/create-opportunity.mjs @@ -0,0 +1,71 @@ +import app from "../../apollo_io_oauth.app.mjs"; + +export default { + key: "apollo_io_oauth-create-opportunity", + name: "Create Opportunity", + description: "Creates a new opportunity in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#create-opportunity)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + ownerId: { + propDefinition: [ + app, + "ownerId", + ], + optional: true, + }, + name: { + propDefinition: [ + app, + "name", + ], + description: "The name of the opportunity.", + }, + amount: { + type: "integer", + label: "Amount", + description: "The amount of money involved in the opportunity/deal.", + optional: true, + }, + opportunityStageId: { + propDefinition: [ + app, + "opportunityStageId", + ], + }, + closedDate: { + type: "string", + label: "Closed Date", + description: "The date the opportunity was closed.", + }, + accountId: { + propDefinition: [ + app, + "accountId", + ], + }, + }, + async run({ $ }) { + const { opportunity } = await this.app.createOpportunity({ + $, + data: { + owner_id: this.ownerId, + name: this.name, + amount: this.amount, + opportunity_stage_id: this.opportunityStageId, + closed_date: this.closedDate, + account_id: this.accountId, + }, + }); + + $.export("$summary", `Successfully created opportunity with ID ${opportunity.id}`); + + return opportunity; + }, +}; diff --git a/components/apollo_io_oauth/actions/create-update-contact/create-update-contact.mjs b/components/apollo_io_oauth/actions/create-update-contact/create-update-contact.mjs new file mode 100644 index 0000000000000..62a6fc5a54ccf --- /dev/null +++ b/components/apollo_io_oauth/actions/create-update-contact/create-update-contact.mjs @@ -0,0 +1,126 @@ +import app from "../../apollo_io_oauth.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "apollo_io_oauth-create-update-contact", + name: "Create Or Update Contact", + description: "Creates or updates a specific contact. If the contact email already exists, it's updated. Otherwise, a new contact is created. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#create-a-contact)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + email: { + propDefinition: [ + app, + "email", + ], + }, + firstName: { + propDefinition: [ + app, + "firstName", + ], + }, + lastName: { + propDefinition: [ + app, + "lastName", + ], + }, + title: { + propDefinition: [ + app, + "title", + ], + }, + accountId: { + propDefinition: [ + app, + "accountId", + ], + optional: true, + }, + websiteUrl: { + propDefinition: [ + app, + "websiteUrl", + ], + }, + labelNames: { + propDefinition: [ + app, + "labelNames", + ], + }, + contactStageId: { + propDefinition: [ + app, + "contactStageId", + ], + optional: true, + }, + address: { + propDefinition: [ + app, + "address", + ], + }, + phone: { + propDefinition: [ + app, + "phone", + ], + }, + }, + async run({ $: step }) { + let contact = {}; + let action = "created"; + let data = utils.cleanObject({ + email: this.email, + first_name: this.firstName, + last_name: this.lastName, + title: this.title, + account_id: this.accountId, + website_url: this.websiteUrl, + label_names: this.labelNames, + contact_stage_id: this.contactStageId, + present_raw_address: this.address, + direct_phone: this.phone, + }); + + const { contacts } = await this.app.searchContacts({ + params: { + q_keywords: this.email, + }, + }); + + if (contacts.length) { + action = "updated"; + contact = contacts[0]; + + await this.app.updateContact({ + step, + contactId: contact.id, + data: { + ...contact, + ...data, + }, + }); + } else { + const response = await this.app.createContact({ + step, + data, + }); + contact = response.contact; + } + + step.export("$summary", `Successfully ${action} contact with ID ${contact.id}`); + + return contact; + }, +}; diff --git a/components/apollo_io_oauth/actions/get-opportunity/get-opportunity.mjs b/components/apollo_io_oauth/actions/get-opportunity/get-opportunity.mjs new file mode 100644 index 0000000000000..f4210b3da0dbb --- /dev/null +++ b/components/apollo_io_oauth/actions/get-opportunity/get-opportunity.mjs @@ -0,0 +1,34 @@ +import app from "../../apollo_io_oauth.app.mjs"; + +export default { + key: "apollo_io_oauth-get-opportunity", + name: "Get Opportunity", + description: "Gets a specific opportunity in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#view-opportunity)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: true, + }, + props: { + app, + opportunityId: { + propDefinition: [ + app, + "opportunityId", + ], + }, + }, + async run({ $ }) { + const { opportunity } = await this.app.getOpportunity({ + $, + opportunityId: this.opportunityId, + }); + + $.export("$summary", `Successfully fetched the opportunity with Id ${this.opportunityId}.`); + + return opportunity; + + }, +}; diff --git a/components/apollo_io_oauth/actions/people-enrichment/people-enrichment.mjs b/components/apollo_io_oauth/actions/people-enrichment/people-enrichment.mjs new file mode 100644 index 0000000000000..7d1be8f3d4f57 --- /dev/null +++ b/components/apollo_io_oauth/actions/people-enrichment/people-enrichment.mjs @@ -0,0 +1,161 @@ +import app from "../../apollo_io_oauth.app.mjs"; + +export default { + key: "apollo_io_oauth-people-enrichment", + name: "People Enrichment", + description: "Enriches a person's information, the more information you pass in, the more likely we can find a match. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#people-enrichment)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + firstName: { + type: "string", + label: "First Name", + description: "The person's first name", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "The person's last name", + optional: true, + }, + name: { + type: "string", + label: "Name", + description: "The full name of the person", + optional: true, + }, + email: { + type: "string", + label: "Email", + description: "The person's email", + optional: true, + }, + hashedEmail: { + type: "string", + label: "Hashed Email", + description: "The hashed email of the person. The email should adhere to either the MD5 or SHA-256 hash format. Example: `8d935115b9ff4489f2d1f9249503cadf` (MD5) or `97817c0c49994eb500ad0a5e7e2d8aed51977b26424d508f66e4e8887746a152` (SHA-256)", + optional: true, + }, + organizationName: { + type: "string", + label: "Organization Name", + description: "The person's company name", + optional: true, + }, + domain: { + type: "string", + label: "Domain", + description: "The domain name for the person's employer. This can be the current employer or a previous employer. Do not include `www.`, the `@` symbol, or similar. Example: `apollo.io` or `microsoft.com`", + optional: true, + }, + personId: { + type: "string", + label: "Person ID", + description: "The Apollo ID for the person", + optional: true, + async options({ page }) { + const { people } = await this.peopleSearch({ + params: { + page: page + 1, + }, + }); + return people?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + linkedinUrl: { + type: "string", + label: "LinkedIn URL", + description: "The URL for the person's LinkedIn profile", + optional: true, + }, + revealPersonalEmails: { + type: "boolean", + label: "Reveal Personal Emails", + description: "Set to `true` if you want to enrich the person's data with personal emails", + optional: true, + }, + revealPhoneNumber: { + type: "boolean", + label: "Reveal Phone Number", + description: "Set to `true` if you want to enrich the person's data with all available phone numbers, including mobile phone numbers", + optional: true, + reloadProps: true, + }, + }, + async additionalProps() { + const props = {}; + if (this.revealPhoneNumber) { + props.webhookUrl = { + type: "string", + label: "Webhook URL", + description: "Enter the webhook URL that specifies where Apollo should send a JSON response that includes the phone number you requested. Required if \"Reveal Phone Number\" is set to `true`", + }; + } + return props; + }, + methods: { + peopleSearch(args = {}) { + return this.app.post({ + path: "/people/search", + ...args, + }); + }, + peopleEnrichment(args = {}) { + return this.app.post({ + path: "/people/match", + ...args, + }); + }, + }, + async run({ $: step }) { + const { + peopleEnrichment, + firstName, + lastName, + name, + email, + hashedEmail, + organizationName, + domain, + personId, + linkedinUrl, + revealPersonalEmails, + revealPhoneNumber, + webhookUrl, + } = this; + + const response = await peopleEnrichment({ + step, + data: { + first_name: firstName, + last_name: lastName, + name, + email, + hashed_email: hashedEmail, + organization_name: organizationName, + domain, + id: personId, + linkedin_url: linkedinUrl, + reveal_personal_emails: revealPersonalEmails, + reveal_phone_number: revealPhoneNumber, + webhook_url: webhookUrl, + }, + }); + + step.export("$summary", `Successfully enriched person with ID ${response.person.id}`); + + return response; + }, +}; diff --git a/components/apollo_io_oauth/actions/search-accounts/search-accounts.mjs b/components/apollo_io_oauth/actions/search-accounts/search-accounts.mjs new file mode 100644 index 0000000000000..30b23bd1e5504 --- /dev/null +++ b/components/apollo_io_oauth/actions/search-accounts/search-accounts.mjs @@ -0,0 +1,68 @@ +import app from "../../apollo_io_oauth.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "apollo_io_oauth-search-accounts", + name: "Search For Accounts", + description: "Search for accounts in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#search-for-accounts)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: true, + }, + props: { + app, + search: { + type: "string", + label: "Search", + description: "The account's name", + }, + accountStageId: { + propDefinition: [ + app, + "accountStageId", + ], + type: "string[]", + optional: true, + }, + sortByField: { + type: "string", + label: "Sort By Field", + description: "The field to sort the response.", + options: [ + "account_last_activity_date", + "account_created_at", + ], + optional: true, + }, + sortAscending: { + type: "boolean", + label: "Sort Ascending", + description: "The order to be applied to the sort.", + optional: true, + }, + }, + async run({ $ }) { + const resourcesStream = this.app.getIterations({ + resourceFn: this.app.searchAccounts, + resourceFnArgs: { + params: { + q_organization_name: this.search, + account_stage_ids: this.accountStageId, + sort_by_field: this.sortByField, + sort_ascending: this.sortAscending, + }, + }, + resourceName: "accounts", + }); + + const accounts = await utils.iterate(resourcesStream); + + $.export("$summary", `Successfully fetched ${accounts.length} accounts.`); + + return accounts; + + }, +}; diff --git a/components/apollo_io_oauth/actions/search-contacts/search-contacts.mjs b/components/apollo_io_oauth/actions/search-contacts/search-contacts.mjs new file mode 100644 index 0000000000000..8d31d272ed901 --- /dev/null +++ b/components/apollo_io_oauth/actions/search-contacts/search-contacts.mjs @@ -0,0 +1,71 @@ +import app from "../../apollo_io_oauth.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "apollo_io_oauth-search-contacts", + name: "Search For Contacts", + description: "Search for contacts in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#search-for-contacts)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: true, + }, + props: { + app, + search: { + type: "string", + label: "Search", + description: "The contact's name, title, company, or email", + }, + contactStageId: { + propDefinition: [ + app, + "contactStageId", + ], + type: "string[]", + optional: true, + }, + sortByField: { + type: "string", + label: "Sort By Field", + description: "The field to sort the response.", + options: [ + "contact_last_activity_date", + "contact_email_last_opened_at", + "contact_email_last_clicked_at", + "contact_created_at", + "contact_updated_at", + ], + optional: true, + }, + sortAscending: { + type: "boolean", + label: "Sort Ascending", + description: "The order to be applied to the sort.", + optional: true, + }, + }, + async run({ $ }) { + const resourcesStream = this.app.getIterations({ + resourceFn: this.app.searchContacts, + resourceFnArgs: { + params: { + q_keywords: this.search, + contact_stage_ids: this.contactStageId, + sort_by_field: this.sortByField, + sort_ascending: this.sortAscending, + }, + }, + resourceName: "contacts", + }); + + const contacts = await utils.iterate(resourcesStream); + + $.export("$summary", `Successfully fetched ${contacts.length} contacts.`); + + return contacts; + + }, +}; diff --git a/components/apollo_io_oauth/actions/search-sequences/search-sequences.mjs b/components/apollo_io_oauth/actions/search-sequences/search-sequences.mjs new file mode 100644 index 0000000000000..bc0d3994d7300 --- /dev/null +++ b/components/apollo_io_oauth/actions/search-sequences/search-sequences.mjs @@ -0,0 +1,41 @@ +import app from "../../apollo_io_oauth.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "apollo_io_oauth-search-sequences", + name: "Search For Sequences", + description: "Search for sequences in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#search-for-sequences)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: false, + openWorldHint: true, + readOnlyHint: true, + }, + props: { + app, + search: { + type: "string", + label: "Search", + description: "The sequence's name", + }, + }, + async run({ $ }) { + const resourcesStream = this.app.getIterations({ + resourceFn: this.app.listSequences, + resourceFnArgs: { + params: { + q_name: this.search, + }, + }, + resourceName: "emailer_campaigns", + }); + + const sequences = await utils.iterate(resourcesStream); + + $.export("$summary", `Successfully fetched ${sequences.length} sequences.`); + + return sequences; + + }, +}; diff --git a/components/apollo_io_oauth/actions/update-account-stage/update-account-stage.mjs b/components/apollo_io_oauth/actions/update-account-stage/update-account-stage.mjs new file mode 100644 index 0000000000000..92ff79e7774c4 --- /dev/null +++ b/components/apollo_io_oauth/actions/update-account-stage/update-account-stage.mjs @@ -0,0 +1,47 @@ +import app from "../../apollo_io_oauth.app.mjs"; + +export default { + key: "apollo_io_oauth-update-account-stage", + name: "Update Account Stage", + description: "Updates the stage of one or more accounts in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#update-account-stage)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + accountIds: { + propDefinition: [ + app, + "accountId", + ], + type: "string[]", + label: "Account IDs", + description: "Identifiers of the accounts to update", + }, + accountStageId: { + propDefinition: [ + app, + "accountStageId", + ], + }, + }, + async run({ $ }) { + const { accounts } = await this.app.updateAccountStage({ + $, + data: { + account_ids: this.accountIds, + account_stage_id: this.accountStageId, + }, + }); + + $.export("$summary", `Successfully updated ${accounts.length} account${accounts.length === 1 + ? "" + : "s"}.`); + + return accounts; + }, +}; diff --git a/components/apollo_io_oauth/actions/update-account/update-account.mjs b/components/apollo_io_oauth/actions/update-account/update-account.mjs new file mode 100644 index 0000000000000..7bc9bd6d3e414 --- /dev/null +++ b/components/apollo_io_oauth/actions/update-account/update-account.mjs @@ -0,0 +1,59 @@ +import app from "../../apollo_io_oauth.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "apollo_io_oauth-update-account", + name: "Update Account", + description: "Updates an existing account in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#update-an-account)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + accountId: { + propDefinition: [ + app, + "accountId", + ], + }, + name: { + propDefinition: [ + app, + "name", + ], + optional: true, + }, + domain: { + propDefinition: [ + app, + "domain", + ], + }, + phone: { + propDefinition: [ + app, + "phone", + ], + description: "The corporate phone for this account", + }, + }, + async run({ $ }) { + const { account } = await this.app.updateAccount({ + $, + accountId: this.accountId, + data: utils.cleanObject({ + name: this.name, + domain: this.domain, + phone_number: this.phone, + }), + }); + + $.export("$summary", `Successfully updated account with ID ${account.id}`); + + return account; + }, +}; diff --git a/components/apollo_io_oauth/actions/update-contact-stage/update-contact-stage.mjs b/components/apollo_io_oauth/actions/update-contact-stage/update-contact-stage.mjs new file mode 100644 index 0000000000000..7376078debed7 --- /dev/null +++ b/components/apollo_io_oauth/actions/update-contact-stage/update-contact-stage.mjs @@ -0,0 +1,47 @@ +import app from "../../apollo_io_oauth.app.mjs"; + +export default { + key: "apollo_io_oauth-update-contact-stage", + name: "Update Contact Stage", + description: "Updates the stage of one or more contacts in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#update-contact-stage)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + contactIds: { + propDefinition: [ + app, + "contactId", + ], + type: "string[]", + label: "Contact IDs", + description: "Identifiers of the contacts to update", + }, + contactStageId: { + propDefinition: [ + app, + "contactStageId", + ], + }, + }, + async run({ $ }) { + const { contacts } = await this.app.updateContactStage({ + $, + data: { + contact_ids: this.contactIds, + contact_stage_id: this.contactStageId, + }, + }); + + $.export("$summary", `Successfully updated ${contacts.length} contact${contacts.length === 1 + ? "" + : "s"}.`); + + return contacts; + }, +}; diff --git a/components/apollo_io_oauth/actions/update-contact/update-contact.mjs b/components/apollo_io_oauth/actions/update-contact/update-contact.mjs new file mode 100644 index 0000000000000..0ecdbc92d80f5 --- /dev/null +++ b/components/apollo_io_oauth/actions/update-contact/update-contact.mjs @@ -0,0 +1,103 @@ +import app from "../../apollo_io_oauth.app.mjs"; +import utils from "../../common/utils.mjs"; + +export default { + key: "apollo_io_oauth-update-contact", + name: "Update Contact", + description: "Updates an existing contact in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#update-a-contact)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + contactId: { + propDefinition: [ + app, + "contactId", + ], + }, + email: { + propDefinition: [ + app, + "email", + ], + optional: true, + }, + firstName: { + propDefinition: [ + app, + "firstName", + ], + }, + lastName: { + propDefinition: [ + app, + "lastName", + ], + }, + title: { + propDefinition: [ + app, + "title", + ], + }, + accountId: { + propDefinition: [ + app, + "accountId", + ], + optional: true, + }, + websiteUrl: { + propDefinition: [ + app, + "websiteUrl", + ], + }, + labelNames: { + propDefinition: [ + app, + "labelNames", + ], + }, + address: { + propDefinition: [ + app, + "address", + ], + }, + phone: { + propDefinition: [ + app, + "phone", + ], + }, + }, + async run({ $ }) { + const { contact } = await this.app.updateContact({ + $, + contactId: this.contactId, + data: utils.cleanObject({ + email: this.email, + first_name: this.firstName, + last_name: this.lastName, + title: this.title, + account_id: this.accountId, + website_url: this.websiteUrl, + label_names: typeof this.labelNames === "string" + ? JSON.parse(this.labelNames) + : this.labelNames, + present_raw_address: this.address, + direct_phone: this.phone, + }), + }); + + $.export("$summary", `Successfully updated contact with ID ${contact.id}`); + + return contact; + }, +}; diff --git a/components/apollo_io_oauth/actions/update-opportunity/update-opportunity.mjs b/components/apollo_io_oauth/actions/update-opportunity/update-opportunity.mjs new file mode 100644 index 0000000000000..2a8aa17358934 --- /dev/null +++ b/components/apollo_io_oauth/actions/update-opportunity/update-opportunity.mjs @@ -0,0 +1,82 @@ +import app from "../../apollo_io_oauth.app.mjs"; + +export default { + key: "apollo_io_oauth-update-opportunity", + name: "Update Opportunity", + description: "Updates an existing opportunity in Apollo.io. [See the documentation](https://apolloio.github.io/apollo-api-docs/?shell#update-opportunity)", + type: "action", + version: "0.0.1", + annotations: { + destructiveHint: true, + openWorldHint: true, + readOnlyHint: false, + }, + props: { + app, + opportunityId: { + propDefinition: [ + app, + "opportunityId", + ], + }, + ownerId: { + propDefinition: [ + app, + "ownerId", + ], + optional: true, + }, + name: { + propDefinition: [ + app, + "name", + ], + description: "The name of the opportunity.", + optional: true, + }, + amount: { + type: "integer", + label: "Amount", + description: "The amount of money involved in the opportunity/deal.", + optional: true, + }, + opportunityStageId: { + propDefinition: [ + app, + "opportunityStageId", + ], + optional: true, + }, + closedDate: { + type: "string", + label: "Closed Date", + description: "The date the opportunity was closed.", + optional: true, + }, + accountId: { + propDefinition: [ + app, + "accountId", + ], + optional: true, + }, + }, + async run({ $ }) { + const { opportunity } = await this.app.updateOpportunity({ + $, + opportunityId: this.opportunityId, + data: { + owner_id: this.ownerId, + name: this.name, + amount: this.amount, + opportunity_stage_id: this.opportunityStageId, + closed_date: this.closedDate, + account_id: this.accountId, + }, + }); + + $.export("$summary", `Successfully updated opportunity with ID ${this.opportunityId}`); + + return opportunity; + }, +}; diff --git a/components/apollo_io_oauth/apollo_io_oauth.app.mjs b/components/apollo_io_oauth/apollo_io_oauth.app.mjs index 7c0c319e6cd81..d46783bbbd6ab 100644 --- a/components/apollo_io_oauth/apollo_io_oauth.app.mjs +++ b/components/apollo_io_oauth/apollo_io_oauth.app.mjs @@ -1,11 +1,437 @@ +import { axios } from "@pipedream/platform"; +import constants from "./common/constants.mjs"; +import utils from "./common/utils.mjs"; + export default { type: "app", app: "apollo_io_oauth", - propDefinitions: {}, + propDefinitions: { + accountId: { + type: "string", + label: "Account ID", + description: "Identifier of the account for this contact", + async options({ page }) { + const { accounts } = await this.searchAccounts({ + params: { + page: page + 1, + }, + }); + return accounts?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + contactId: { + type: "string", + label: "Contact ID", + description: "Identifier of the contact to update", + async options({ page }) { + const { contacts } = await this.searchContacts({ + params: { + page: page + 1, + }, + }); + return contacts?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + contactStageId: { + type: "string", + label: "Contact Stage ID", + description: "Identifier of a contact stage", + async options() { + const { contact_stages: stages } = await this.listContactStages(); + return stages?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + accountStageId: { + type: "string", + label: "Account Stage ID", + description: "Identifier of an account stage", + async options() { + const { account_stages: stages } = await this.listAccountStages(); + return stages?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + sequenceId: { + type: "string", + label: "Sequence ID", + description: "Identifier of a sequence", + async options() { + const { emailer_campaigns: sequences } = await this.listSequences(); + return sequences?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + emailAccountId: { + type: "string", + label: "Email Account ID", + description: "Identifier of the email account to send email from", + async options() { + const { email_accounts: emails } = await this.listEmailAccounts(); + return emails?.map(({ + id: value, email: label, + }) => ({ + value, + label, + })) || []; + }, + }, + ownerId: { + type: "string", + label: "Owner ID", + description: "Identifier of the user to associate as owner.", + async options() { + const { users } = await this.listUsers(); + return users?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + opportunityId: { + type: "string", + label: "Opportunity ID", + description: "The ID of the opportunity.", + async options({ page }) { + const { opportunities } = await this.listOpportunities({ + page: page + 1, + }); + return opportunities?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + opportunityStageId: { + type: "string", + label: "Opportunity Stage ID", + description: "The ID of the current stage.", + async options() { + const { opportunity_stages: stages } = await this.listOpportunityStages(); + return stages?.map(({ + id: value, name: label, + }) => ({ + value, + label, + })) || []; + }, + }, + email: { + type: "string", + label: "Email", + description: "Email address of the contact", + }, + firstName: { + type: "string", + label: "First Name", + description: "First name of the contact", + optional: true, + }, + lastName: { + type: "string", + label: "Last Name", + description: "Last name of the contact", + optional: true, + }, + title: { + type: "string", + label: "Title", + description: "The title this contact", + optional: true, + }, + websiteUrl: { + type: "string", + label: "Website URL", + description: "The organization website Apollo can use to enrich data for you. DO NOT pass in personal social media URLs such as \"http://www.linkedin.com/profile_url\", or your data will be incorrectly enriched. This argument will be ignored if you pass in a valid email.", + optional: true, + }, + labelNames: { + type: "string[]", + label: "Label Names", + description: "A list of names to tag this contact. You can select the labels from the list or create new ones using a custom expression, i.e., `[\"label1\", \"label2\"]`", + optional: true, + async options() { + const response = await this.listLabels(); + return response.map(({ name }) => name) || []; + }, + }, + address: { + type: "string", + label: "Address", + description: "The address string for this contact, Apollo will intelligently infer the city, state, country, and time zone from your address", + optional: true, + }, + phone: { + type: "string", + label: "Phone", + description: "The direct dial phone for this contact", + optional: true, + }, + name: { + type: "string", + label: "Name", + description: "The name of the account", + }, + domain: { + type: "string", + label: "Domain", + description: "The domain of the account you are adding", + optional: true, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + getBaseUrl() { + return `${constants.BASE_URL}${constants.VERSION_PATH}`; + }, + getUrl(path) { + return `${this.getBaseUrl()}${path}`; + }, + getHeaders(headers) { + return { + "Content-Type": "application/json", + "Cache-Control": "no-cache", + "Authorization": `Bearer ${this.$auth.oauth_access_token}`, + ...headers, + }; + }, + getParams(params) { + return { + ...params, + }; + }, + makeRequest({ + step = this, path, headers, params, ...args + } = {}) { + + const config = { + headers: this.getHeaders(headers), + url: this.getUrl(path), + params: this.getParams(params), + ...args, + }; + + return axios(step, config); + }, + post(args = {}) { + return this.makeRequest({ + method: "post", + ...args, + }); + }, + put(args = {}) { + return this.makeRequest({ + method: "put", + ...args, + }); + }, + patch(args = {}) { + return this.makeRequest({ + method: "patch", + ...args, + }); + }, + listContactStages(args = {}) { + return this.makeRequest({ + path: "/contact_stages", + ...args, + }); + }, + listAccountStages(args = {}) { + return this.makeRequest({ + path: "/account_stages", + ...args, + }); + }, + listSequences(args = {}) { + return this.post({ + path: "/emailer_campaigns/search", + ...args, + }); + }, + listEmailAccounts(args = {}) { + return this.makeRequest({ + path: "/email_accounts", + ...args, + }); + }, + listUsers(args = {}) { + return this.makeRequest({ + path: "/users/search", + ...args, + }); + }, + listOpportunities(args = {}) { + return this.makeRequest({ + path: "/opportunities/search", + ...args, + }); + }, + listOpportunityStages(args = {}) { + return this.makeRequest({ + path: "/opportunity_stages", + ...args, + }); + }, + createContact(args = {}) { + return this.post({ + path: "/contacts", + ...args, + }); + }, + updateContact({ + contactId, ...args + }) { + return this.put({ + path: `/contacts/${contactId}`, + ...args, + }); + }, + updateContactStage(args = {}) { + return this.post({ + path: "/contacts/update_stages", + ...args, + }); + }, + createAccount(args = {}) { + return this.post({ + path: "/accounts", + ...args, + }); + }, + createOpportunity(args = {}) { + return this.post({ + path: "/opportunities", + ...args, + }); + }, + updateOpportunity({ + opportunityId, ...args + }) { + return this.patch({ + path: `/opportunities/${opportunityId}`, + ...args, + }); + }, + getOpportunity({ + opportunityId, ...args + }) { + return this.makeRequest({ + path: `/opportunities/${opportunityId}`, + ...args, + }); + }, + updateAccount({ + accountId, ...args + }) { + return this.put({ + path: `/accounts/${accountId}`, + ...args, + }); + }, + updateAccountStage(args = {}) { + return this.post({ + path: "/accounts/bulk_update", + ...args, + }); + }, + addContactsToSequence({ + sequenceId, ...args + }) { + return this.post({ + path: `/emailer_campaigns/${sequenceId}/add_contact_ids`, + ...args, + }); + }, + searchAccounts(args = {}) { + return this.post({ + path: "/accounts/search", + ...args, + }); + }, + searchContacts(args = {}) { + return this.post({ + path: "/contacts/search", + ...args, + }); + }, + listLabels(args = {}) { + return this.makeRequest({ + path: "/labels", + ...args, + }); + }, + async *getIterations({ + resourceFn, + resourceFnArgs, + resourceName, + max = constants.DEFAULT_MAX, + }) { + let page = 1; + let resourcesCount = 0; + + while (true) { + const response = + await resourceFn({ + ...resourceFnArgs, + params: { + ...resourceFnArgs.params, + per_page: constants.DEFAULT_LIMIT, + page, + }, + }); + + const nextResources = resourceName && response[resourceName] || response; + + if (!nextResources?.length) { + console.log("No more resources found"); + return; + } + + for (const resource of nextResources) { + yield resource; + resourcesCount += 1; + + if (resourcesCount >= max) { + return; + } + } + + if (nextResources.length < constants.DEFAULT_LIMIT) { + console.log("No next page"); + return; + } + + page += 1; + } + }, + paginate(args = {}) { + return utils.iterate(this.getIterations(args)); }, }, }; diff --git a/components/apollo_io_oauth/common/constants.mjs b/components/apollo_io_oauth/common/constants.mjs new file mode 100644 index 0000000000000..755dd134476a4 --- /dev/null +++ b/components/apollo_io_oauth/common/constants.mjs @@ -0,0 +1,11 @@ +const BASE_URL = "https://api.apollo.io"; +const VERSION_PATH = "/api/v1"; +const DEFAULT_LIMIT = 100; +const DEFAULT_MAX = 600; + +export default { + BASE_URL, + VERSION_PATH, + DEFAULT_LIMIT, + DEFAULT_MAX, +}; diff --git a/components/apollo_io_oauth/common/utils.mjs b/components/apollo_io_oauth/common/utils.mjs new file mode 100644 index 0000000000000..7c0dbef7b38bd --- /dev/null +++ b/components/apollo_io_oauth/common/utils.mjs @@ -0,0 +1,21 @@ +async function iterate(iterations) { + const items = []; + for await (const item of iterations) { + items.push(item); + } + return items; +} + +function cleanObject(o) { + for (var k in o || {}) { + if (typeof o[k] === "undefined") { + delete o[k]; + } + } + return o; +} + +export default { + iterate, + cleanObject, +}; diff --git a/components/apollo_io_oauth/package.json b/components/apollo_io_oauth/package.json index 388c9d0d8dea4..22afdcf373128 100644 --- a/components/apollo_io_oauth/package.json +++ b/components/apollo_io_oauth/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/apollo_io_oauth", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Apollo.io (OAuth) Components", "main": "apollo_io_oauth.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.1.1" } } \ No newline at end of file