From 92687ba7cf675f48048bba88cde6a60b6597dfee Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Wed, 13 Nov 2024 15:19:17 -0500 Subject: [PATCH 1/6] init --- components/zerobounce/.gitignore | 3 - .../actions/ai-scoring/ai-scoring.mjs | 18 ++++++ .../file-validation/file-validation.mjs | 18 ++++++ .../actions/validate-email/validate-email.mjs | 18 ++++++ components/zerobounce/app/zerobounce.app.ts | 13 ---- components/zerobounce/zerobounce.app.mjs | 61 +++++++++++++++++++ 6 files changed, 115 insertions(+), 16 deletions(-) delete mode 100644 components/zerobounce/.gitignore create mode 100644 components/zerobounce/actions/ai-scoring/ai-scoring.mjs create mode 100644 components/zerobounce/actions/file-validation/file-validation.mjs create mode 100644 components/zerobounce/actions/validate-email/validate-email.mjs delete mode 100644 components/zerobounce/app/zerobounce.app.ts create mode 100644 components/zerobounce/zerobounce.app.mjs diff --git a/components/zerobounce/.gitignore b/components/zerobounce/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/zerobounce/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/zerobounce/actions/ai-scoring/ai-scoring.mjs b/components/zerobounce/actions/ai-scoring/ai-scoring.mjs new file mode 100644 index 0000000000000..8bf1890e84afa --- /dev/null +++ b/components/zerobounce/actions/ai-scoring/ai-scoring.mjs @@ -0,0 +1,18 @@ +import zerobounce from "../../zerobounce.app.mjs"; + +export default { + key: "zerobounce-ai-scoring", + name: "AI Scoring", + description: "Estimates a reliability score based on ZeroBounce's AI for the provided email. [See the documentation](https://www.zerobounce.net/docs/ai-scoring-api/)", + version: "0.0.{{ts}}", + type: "action", + props: { + zerobounce, + email: zerobounce.propDefinitions.email, + }, + async run({ $ }) { + const response = await this.zerobounce.getReliabilityScore(this.email); + $.export("$summary", `Successfully estimated reliability score for email: ${this.email}`); + return response; + }, +}; diff --git a/components/zerobounce/actions/file-validation/file-validation.mjs b/components/zerobounce/actions/file-validation/file-validation.mjs new file mode 100644 index 0000000000000..cc4b3d7f4122b --- /dev/null +++ b/components/zerobounce/actions/file-validation/file-validation.mjs @@ -0,0 +1,18 @@ +import zerobounce from "../../zerobounce.app.mjs"; + +export default { + key: "zerobounce-file-validation", + name: "Validate Emails in File", + description: "Performs email validation on all the addresses contained in a provided file. [See the documentation](https://www.zerobounce.net/docs/email-validation-api-quickstart/)", + version: "0.0.{{ts}}", + type: "action", + props: { + zerobounce, + file: zerobounce.propDefinitions.file, + }, + async run({ $ }) { + const response = await this.zerobounce.validateEmailsInFile(this.file); + $.export("$summary", "Successfully validated emails in file"); + return response; + }, +}; diff --git a/components/zerobounce/actions/validate-email/validate-email.mjs b/components/zerobounce/actions/validate-email/validate-email.mjs new file mode 100644 index 0000000000000..0d12044c83dc1 --- /dev/null +++ b/components/zerobounce/actions/validate-email/validate-email.mjs @@ -0,0 +1,18 @@ +import zerobounce from "../../zerobounce.app.mjs"; + +export default { + key: "zerobounce-validate-email", + name: "Validate Email", + description: "Validates a specific email. [See the documentation](https://www.zerobounce.net/docs/email-validation-api-quickstart/)", + version: "0.0.{{ts}}", + type: "action", + props: { + zerobounce, + email: zerobounce.propDefinitions.email, + }, + async run({ $ }) { + const response = await this.zerobounce.validateEmail(this.email); + $.export("$summary", `Successfully validated email: ${this.email}`); + return response; + }, +}; diff --git a/components/zerobounce/app/zerobounce.app.ts b/components/zerobounce/app/zerobounce.app.ts deleted file mode 100644 index 56554ac30099a..0000000000000 --- a/components/zerobounce/app/zerobounce.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "zerobounce", - 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/zerobounce/zerobounce.app.mjs b/components/zerobounce/zerobounce.app.mjs new file mode 100644 index 0000000000000..3e72146ed860d --- /dev/null +++ b/components/zerobounce/zerobounce.app.mjs @@ -0,0 +1,61 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "zerobounce", + propDefinitions: { + email: { + type: "string", + label: "Email", + description: "The email address to be validated", + }, + file: { + type: "string", + label: "File", + description: "The file that contains email addresses to be validated", + }, + }, + methods: { + _baseUrl() { + return "https://api.zerobounce.net/v2"; + }, + async _makeRequest(opts = {}) { + const { + $ = this, + method = "GET", + path, + headers, + ...otherOpts + } = opts; + return axios($, { + ...otherOpts, + method, + url: this._baseUrl() + path, + headers: { + ...headers, + "user-agent": "@PipedreamHQ/pipedream v0.1", + "Authorization": `Bearer ${this.$auth.oauth_access_token}`, + }, + }); + }, + async validateEmail(email) { + return this._makeRequest({ + path: `/validate?email=${email}`, + }); + }, + async validateEmailsInFile(file) { + return this._makeRequest({ + method: "POST", + path: "/sendfile", + data: { + file: file, + }, + }); + }, + async getReliabilityScore(email) { + return this._makeRequest({ + path: `/score?email=${email}`, + }); + }, + }, +}; From 65d44b6e67d50b31126b3f4c09d14d37cc9a8630 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Wed, 13 Nov 2024 17:44:58 -0500 Subject: [PATCH 2/6] new components --- .../actions/ai-scoring/ai-scoring.mjs | 17 ++- .../file-validation/file-validation.mjs | 118 +++++++++++++++++- .../get-validation-results-file.mjs | 39 ++++++ .../actions/validate-email/validate-email.mjs | 31 ++++- components/zerobounce/package.json | 10 +- components/zerobounce/zerobounce.app.mjs | 52 ++++---- 6 files changed, 222 insertions(+), 45 deletions(-) create mode 100644 components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs diff --git a/components/zerobounce/actions/ai-scoring/ai-scoring.mjs b/components/zerobounce/actions/ai-scoring/ai-scoring.mjs index 8bf1890e84afa..d191cea4053af 100644 --- a/components/zerobounce/actions/ai-scoring/ai-scoring.mjs +++ b/components/zerobounce/actions/ai-scoring/ai-scoring.mjs @@ -3,15 +3,24 @@ import zerobounce from "../../zerobounce.app.mjs"; export default { key: "zerobounce-ai-scoring", name: "AI Scoring", - description: "Estimates a reliability score based on ZeroBounce's AI for the provided email. [See the documentation](https://www.zerobounce.net/docs/ai-scoring-api/)", - version: "0.0.{{ts}}", + description: "Estimates a reliability score based on ZeroBounce's AI for the provided email. [See the documentation](https://www.zerobounce.net/docs/ai-scoring-api/#single_email_scoring)", + version: "0.0.1", type: "action", props: { zerobounce, - email: zerobounce.propDefinitions.email, + email: { + type: "string", + label: "Email", + description: "The email address that you want to retrieve Scoring data for", + }, }, async run({ $ }) { - const response = await this.zerobounce.getReliabilityScore(this.email); + const response = await this.zerobounce.getReliabilityScore({ + $, + params: { + email: this.email, + }, + }); $.export("$summary", `Successfully estimated reliability score for email: ${this.email}`); return response; }, diff --git a/components/zerobounce/actions/file-validation/file-validation.mjs b/components/zerobounce/actions/file-validation/file-validation.mjs index cc4b3d7f4122b..5dda0a76170b9 100644 --- a/components/zerobounce/actions/file-validation/file-validation.mjs +++ b/components/zerobounce/actions/file-validation/file-validation.mjs @@ -1,18 +1,128 @@ import zerobounce from "../../zerobounce.app.mjs"; +import fs from "fs"; +import FormData from "form-data"; +import path from "path"; export default { key: "zerobounce-file-validation", name: "Validate Emails in File", description: "Performs email validation on all the addresses contained in a provided file. [See the documentation](https://www.zerobounce.net/docs/email-validation-api-quickstart/)", - version: "0.0.{{ts}}", + version: "0.0.1", type: "action", props: { zerobounce, - file: zerobounce.propDefinitions.file, + filePath: { + type: "string", + label: "File Path", + description: "The path to a csv or txt file in the `/tmp` directory. [See the documentation on working with files](https://pipedream.com/docs/code/nodejs/working-with-files/#writing-a-file-to-tmp)", + }, + emailAddressColumn: { + type: "integer", + label: "Email Address Column", + description: "The column index of the email address in the file. Index starts from 1.", + }, + firstNameColumn: { + type: "integer", + label: "First Name Column", + description: "The column index of the first name column. Index starts from 1.", + optional: true, + }, + lastNameColumn: { + type: "integer", + label: "Last Name Column", + description: "The column index of the last name column. Index starts from 1.", + optional: true, + }, + ipAddressColumn: { + type: "integer", + label: "IP Address Column", + description: "The IP Address the email signed up from. Index starts from 1", + optional: true, + }, + hasHeaderRow: { + type: "boolean", + label: "Has Header Row", + description: "If the first row from the submitted file is a header row", + optional: true, + }, + removeDuplicates: { + type: "boolean", + label: "Remove Duplicates", + description: "If you want the system to remove duplicate emails. Default is `true`. Please note that if we remove more than 50% of the lines because of duplicates (parameter is true), system will return a 400 bad request error as a safety net to let you know that more than 50% of the file has been modified.", + optional: true, + }, + returnUrl: { + type: "string", + label: "Return URL", + description: "The URL will be used to call back when the validation is completed", + optional: true, + }, + callbackWithRerun: { + type: "boolean", + label: "Callback With Rerun", + description: "Use the `$.flow.rerun` Node.js helper to rerun the step when the validation is completed. Overrides the `rerunUrl` prop. This will increase execution time and credit usage as a result. [See the documentation(https://pipedream.com/docs/code/nodejs/rerun/#flow-rerun)", + optional: true, + }, }, async run({ $ }) { - const response = await this.zerobounce.validateEmailsInFile(this.file); - $.export("$summary", "Successfully validated emails in file"); + let response, summary; + const { run } = $.context; + + if (run.runs === 1) { + let returnUrl = this.returnUrl; + if (this.callbackWithRerun) { + ({ resume_url: returnUrl } = $.flow.rerun(600000, null, 1)); + } + + const filePath = this.filePath.includes("tmp/") + ? this.filePath + : `/tmp/${this.filePath}`; + const fileName = path.basename(filePath); + const fileContent = fs.readFileSync(filePath); + + const formData = new FormData(); + formData.append("file", fileContent, fileName); + formData.append("email_address_column", this.emailAddressColumn); + formData.append("api_key", this.zerobounce.$auth.api_key); + if (this.firstNameColumn) { + formData.append("first_name_column", this.firstNameColumn); + } + if (this.lastNameColumn) { + formData.append("last_name_column", this.lastNameColumn); + } + if (this.ipAddressColumn) { + formData.append("ip_address_column", this.ipAddressColumn); + } + if (this.hasHeaderRow) { + formData.append("has_header_row", this.hasHeaderRow + ? "true" + : "false"); + } + if (this.removeDuplicates) { + formData.append("remove_duplicate", this.removeDuplicates + ? "true" + : "false"); + } + if (returnUrl) { + formData.append("return_url", returnUrl); + } + + response = await this.zerobounce.validateEmailsInFile({ + $, + data: formData, + headers: { + ...formData.getHeaders(), + }, + }); + summary = "Successfully sent file for validation"; + } + + if (run.callback_request) { + response = run.callback_request.body; + summary = "Successfully validated emails in file"; + } + + $.export("$summary", summary); return response; }, }; diff --git a/components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs b/components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs new file mode 100644 index 0000000000000..ea733c9e030e8 --- /dev/null +++ b/components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs @@ -0,0 +1,39 @@ +import zerobounce from "../../zerobounce.app.mjs"; +import fs from "fs"; + +export default { + key: "zerobounce-get-validation-results-file", + name: "Get Validation Results File", + description: "Downloads the validation results for a file submitted using sendfile API. [See the documentation](https://www.zerobounce.net/docs/email-validation-api-quickstart/#get_file__v2__)", + version: "0.0.1", + type: "action", + props: { + zerobounce, + fileId: { + type: "string", + label: "File ID", + description: "The file_id returned when sending the file for validation", + }, + fileName: { + type: "string", + label: "File Name", + description: "The filename to save the file as in the \"/tmp\" directory", + }, + }, + async run({ $ }) { + const response = await this.zerobounce.getResultsFile({ + $, + params: { + file_id: this.fileId, + }, + responseType: "arraybuffer", + }); + + const filePath = `/tmp/${this.fileName}`; + fs.writeFileSync(filePath, response); + + $.export("$summary", `File saved to ${filePath}`); + + return filePath; + }, +}; diff --git a/components/zerobounce/actions/validate-email/validate-email.mjs b/components/zerobounce/actions/validate-email/validate-email.mjs index 0d12044c83dc1..45399108a0c80 100644 --- a/components/zerobounce/actions/validate-email/validate-email.mjs +++ b/components/zerobounce/actions/validate-email/validate-email.mjs @@ -3,15 +3,38 @@ import zerobounce from "../../zerobounce.app.mjs"; export default { key: "zerobounce-validate-email", name: "Validate Email", - description: "Validates a specific email. [See the documentation](https://www.zerobounce.net/docs/email-validation-api-quickstart/)", - version: "0.0.{{ts}}", + description: "Validates a specific email. [See the documentation](https://www.zerobounce.net/docs/email-validation-api-quickstart/#validate_emails__v2__)", + version: "0.0.1", type: "action", props: { zerobounce, - email: zerobounce.propDefinitions.email, + email: { + type: "string", + label: "Email", + description: "The email address to be validated", + }, + ipAddress: { + type: "string", + label: "IP Address", + description: "The IP Address the email signed up from", + optional: true, + }, + activityData: { + type: "boolean", + label: "Activity Data", + description: "If set to `true`, Activity Data information will be appended to the validation result", + optional: true, + }, }, async run({ $ }) { - const response = await this.zerobounce.validateEmail(this.email); + const response = await this.zerobounce.validateEmail({ + $, + params: { + email: this.email, + ip_address: this.ipAddress || "", + activity_data: this.activityData, + }, + }); $.export("$summary", `Successfully validated email: ${this.email}`); return response; }, diff --git a/components/zerobounce/package.json b/components/zerobounce/package.json index b94be62eec373..cbe8e85ba7dc8 100644 --- a/components/zerobounce/package.json +++ b/components/zerobounce/package.json @@ -1,16 +1,20 @@ { "name": "@pipedream/zerobounce", - "version": "0.0.2", + "version": "0.1.0", "description": "Pipedream ZeroBounce Components", - "main": "dist/app/zerobounce.app.mjs", + "main": "zerobounce.app.mjs", "keywords": [ "pipedream", "zerobounce" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/zerobounce", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3", + "form-data": "^4.0.1", + "path": "^0.12.7" } } diff --git a/components/zerobounce/zerobounce.app.mjs b/components/zerobounce/zerobounce.app.mjs index 3e72146ed860d..88ecf05bc55dd 100644 --- a/components/zerobounce/zerobounce.app.mjs +++ b/components/zerobounce/zerobounce.app.mjs @@ -3,58 +3,50 @@ import { axios } from "@pipedream/platform"; export default { type: "app", app: "zerobounce", - propDefinitions: { - email: { - type: "string", - label: "Email", - description: "The email address to be validated", - }, - file: { - type: "string", - label: "File", - description: "The file that contains email addresses to be validated", - }, - }, methods: { _baseUrl() { return "https://api.zerobounce.net/v2"; }, - async _makeRequest(opts = {}) { + _makeRequest(opts = {}) { const { $ = this, - method = "GET", path, - headers, + url, + params, ...otherOpts } = opts; return axios($, { ...otherOpts, - method, - url: this._baseUrl() + path, - headers: { - ...headers, - "user-agent": "@PipedreamHQ/pipedream v0.1", - "Authorization": `Bearer ${this.$auth.oauth_access_token}`, + url: url || `${this._baseUrl()}${path}`, + params: { + ...params, + api_key: this.$auth.api_key, }, }); }, - async validateEmail(email) { + validateEmail(opts = {}) { return this._makeRequest({ - path: `/validate?email=${email}`, + path: "/validate", + ...opts, }); }, - async validateEmailsInFile(file) { + getReliabilityScore(opts = {}) { + return this._makeRequest({ + path: "/scoring", + ...opts, + }); + }, + validateEmailsInFile(opts = {}) { return this._makeRequest({ method: "POST", - path: "/sendfile", - data: { - file: file, - }, + url: "https://bulkapi.zerobounce.net/v2/sendfile", + ...opts, }); }, - async getReliabilityScore(email) { + getResultsFile(opts = {}) { return this._makeRequest({ - path: `/score?email=${email}`, + url: "https://bulkapi.zerobounce.net/v2/getfile", + ...opts, }); }, }, From 8a80d3b6ea505280f5f866e73b1bd6e18b3b2bfc Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Wed, 13 Nov 2024 17:48:05 -0500 Subject: [PATCH 3/6] pnpm-lock.yaml --- pnpm-lock.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1961b265a9372..470f1c341983b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11842,7 +11842,14 @@ importers: '@pipedream/platform': 3.0.3 components/zerobounce: - specifiers: {} + specifiers: + '@pipedream/platform': ^3.0.3 + form-data: ^4.0.1 + path: ^0.12.7 + dependencies: + '@pipedream/platform': 3.0.3 + form-data: 4.0.1 + path: 0.12.7 components/zerotier: specifiers: From 8a7445c53f6c5b44c5c6574dfdd385c6021a8e4e Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Wed, 13 Nov 2024 18:03:28 -0500 Subject: [PATCH 4/6] fix prop description --- .../zerobounce/actions/file-validation/file-validation.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/zerobounce/actions/file-validation/file-validation.mjs b/components/zerobounce/actions/file-validation/file-validation.mjs index 5dda0a76170b9..92384e9928ff1 100644 --- a/components/zerobounce/actions/file-validation/file-validation.mjs +++ b/components/zerobounce/actions/file-validation/file-validation.mjs @@ -60,7 +60,7 @@ export default { callbackWithRerun: { type: "boolean", label: "Callback With Rerun", - description: "Use the `$.flow.rerun` Node.js helper to rerun the step when the validation is completed. Overrides the `rerunUrl` prop. This will increase execution time and credit usage as a result. [See the documentation(https://pipedream.com/docs/code/nodejs/rerun/#flow-rerun)", + description: "Use the `$.flow.rerun` Node.js helper to rerun the step when the validation is completed. Overrides the `rerunUrl` prop. This will increase execution time and credit usage as a result. [See the documentation](https://pipedream.com/docs/code/nodejs/rerun/#flow-rerun)", optional: true, }, }, From 05b0338f1ad4c900b8b7e34d271260053a56d31b Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Fri, 15 Nov 2024 17:53:39 -0500 Subject: [PATCH 5/6] validate fileId --- .../get-validation-results-file.mjs | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs b/components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs index ea733c9e030e8..5f822fe9f879a 100644 --- a/components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs +++ b/components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs @@ -1,5 +1,6 @@ import zerobounce from "../../zerobounce.app.mjs"; import fs from "fs"; +import { ConfigurationError } from "@pipedream/platform"; export default { key: "zerobounce-get-validation-results-file", @@ -12,7 +13,7 @@ export default { fileId: { type: "string", label: "File ID", - description: "The file_id returned when sending the file for validation", + description: "The file_id returned when sending the file for validation. Can be found on your Zerobounce \"Validate\" tab under Results next to each filename. Click on the ID circle.", }, fileName: { type: "string", @@ -20,12 +21,38 @@ export default { description: "The filename to save the file as in the \"/tmp\" directory", }, }, + methods: { + getResultsFile({ + $, ...opts + }) { + return this.zerobounce.getResultsFile({ + $, + params: { + file_id: this.fileId, + }, + ...opts, + }); + }, + async validateFileId({ $ }) { + try { + const x = await this.getResultsFile({ + $, + }); + console.log(x); + return true; + } catch { + throw new ConfigurationError("File not found. Make sure the File ID is correct"); + } + }, + }, async run({ $ }) { - const response = await this.zerobounce.getResultsFile({ + if (!(await this.validateFileId({ + $, + }))) { + return; + } + const response = await this.getResultsFile({ $, - params: { - file_id: this.fileId, - }, responseType: "arraybuffer", }); From ce836cd4a5e79108cc89f55fa45827b5fece9b61 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Fri, 15 Nov 2024 17:58:15 -0500 Subject: [PATCH 6/6] remove console.log --- .../get-validation-results-file.mjs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs b/components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs index 5f822fe9f879a..a4fb5146ee7db 100644 --- a/components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs +++ b/components/zerobounce/actions/get-validation-results-file/get-validation-results-file.mjs @@ -35,11 +35,9 @@ export default { }, async validateFileId({ $ }) { try { - const x = await this.getResultsFile({ + return await this.getResultsFile({ $, }); - console.log(x); - return true; } catch { throw new ConfigurationError("File not found. Make sure the File ID is correct"); }