From 29088ab955a79e8a33a35caa498eb0d3e1f078df Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Thu, 5 Dec 2024 14:29:20 -0300 Subject: [PATCH 1/6] signaturit init --- .../create-certified-email.mjs | 80 ++++++ ...create-signature-request-from-template.mjs | 120 +++++++++ .../send-signature-request-reminder.mjs | 27 ++ components/signaturit/app/signaturit.app.ts | 2 +- components/signaturit/signaturit.app.mjs | 238 ++++++++++++++++++ .../new-signed-document.mjs | 72 ++++++ 6 files changed, 538 insertions(+), 1 deletion(-) create mode 100644 components/signaturit/actions/create-certified-email/create-certified-email.mjs create mode 100644 components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs create mode 100644 components/signaturit/actions/send-signature-request-reminder/send-signature-request-reminder.mjs create mode 100644 components/signaturit/signaturit.app.mjs create mode 100644 components/signaturit/sources/new-signed-document/new-signed-document.mjs diff --git a/components/signaturit/actions/create-certified-email/create-certified-email.mjs b/components/signaturit/actions/create-certified-email/create-certified-email.mjs new file mode 100644 index 0000000000000..f1b38e3ee3f2b --- /dev/null +++ b/components/signaturit/actions/create-certified-email/create-certified-email.mjs @@ -0,0 +1,80 @@ +import signaturit from "../../signaturit.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "signaturit-create-certified-email", + name: "Create Certified Email", + description: "Initiates the creation of a certified email. [See the documentation]()", + version: "0.0.{{ts}}", + type: "action", + props: { + signaturit: { + type: "app", + app: "signaturit", + }, + attachments: { + propDefinition: [ + signaturit, + "attachments", + ], + }, + recipients: { + propDefinition: [ + signaturit, + "recipients", + ], + }, + type: { + propDefinition: [ + signaturit, + "type", + ], + }, + templates: { + propDefinition: [ + signaturit, + "templates", + ], + }, + body: { + propDefinition: [ + signaturit, + "body", + ], + optional: true, + }, + brandingId: { + propDefinition: [ + signaturit, + "brandingId", + ], + optional: true, + }, + eventsUrl: { + propDefinition: [ + signaturit, + "eventsUrl", + ], + optional: true, + }, + data: { + propDefinition: [ + signaturit, + "data", + ], + optional: true, + }, + subject: { + propDefinition: [ + signaturit, + "subject", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.signaturit.createCertifiedEmail(); + $.export("$summary", `Created certified email with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs b/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs new file mode 100644 index 0000000000000..7a5e6a1946f7c --- /dev/null +++ b/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs @@ -0,0 +1,120 @@ +import signaturit from "../../signaturit.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "signaturit-create-signature-request-from-template", + name: "Create Signature Request from Template", + description: "Creates a signature request using a pre-existing template. [See the documentation]()", + version: "0.0.{{ts}}", + type: "action", + props: { + signaturit, + files: { + propDefinition: [ + signaturit, + "files", + ], + }, + name: { + propDefinition: [ + signaturit, + "name", + ], + }, + recipients: { + propDefinition: [ + signaturit, + "recipients", + ], + }, + body: { + propDefinition: [ + signaturit, + "body", + ], + optional: true, + }, + brandingId: { + propDefinition: [ + signaturit, + "brandingId", + ], + optional: true, + }, + callbackUrl: { + propDefinition: [ + signaturit, + "callbackUrl", + ], + optional: true, + }, + data: { + propDefinition: [ + signaturit, + "data", + ], + optional: true, + }, + deliveryType: { + propDefinition: [ + signaturit, + "deliveryType", + ], + optional: true, + }, + expireTime: { + propDefinition: [ + signaturit, + "expireTime", + ], + optional: true, + }, + eventsUrl: { + propDefinition: [ + signaturit, + "eventsUrl", + ], + optional: true, + }, + replyTo: { + propDefinition: [ + signaturit, + "replyTo", + ], + optional: true, + }, + reminders: { + propDefinition: [ + signaturit, + "reminders", + ], + optional: true, + }, + signingMode: { + propDefinition: [ + signaturit, + "signingMode", + ], + optional: true, + }, + subject: { + propDefinition: [ + signaturit, + "subject", + ], + optional: true, + }, + type: { + propDefinition: [ + signaturit, + "typeSignature", + ], + optional: true, + }, + }, + async run({ $ }) { + const response = await this.signaturit.createSignatureRequest(); + $.export("$summary", `Created signature request with ID: ${response.id}`); + return response; + }, +}; diff --git a/components/signaturit/actions/send-signature-request-reminder/send-signature-request-reminder.mjs b/components/signaturit/actions/send-signature-request-reminder/send-signature-request-reminder.mjs new file mode 100644 index 0000000000000..1df3970db9896 --- /dev/null +++ b/components/signaturit/actions/send-signature-request-reminder/send-signature-request-reminder.mjs @@ -0,0 +1,27 @@ +import signaturit from "../../signaturit.app.mjs"; +import { axios } from "@pipedream/platform"; + +export default { + key: "signaturit-send-signature-request-reminder", + name: "Send Signature Request Reminder", + description: "Sends a reminder for a pending signature request. [See the documentation](https://docs.signaturit.com/api/latest)", + version: "0.0.{{ts}}", + type: "action", + props: { + signaturit: { + type: "app", + app: "signaturit", + }, + signatureRequestId: { + propDefinition: [ + signaturit, + "signatureRequestId", + ], + }, + }, + async run({ $ }) { + const response = await this.signaturit.sendReminder(); + $.export("$summary", `Sent reminder for signature request ${this.signatureRequestId}`); + return response; + }, +}; diff --git a/components/signaturit/app/signaturit.app.ts b/components/signaturit/app/signaturit.app.ts index e2cef98c1f866..a47338ec2210a 100644 --- a/components/signaturit/app/signaturit.app.ts +++ b/components/signaturit/app/signaturit.app.ts @@ -10,4 +10,4 @@ export default defineApp({ console.log(Object.keys(this.$auth)); }, }, -}); \ No newline at end of file +}); diff --git a/components/signaturit/signaturit.app.mjs b/components/signaturit/signaturit.app.mjs new file mode 100644 index 0000000000000..9424953dab2a5 --- /dev/null +++ b/components/signaturit/signaturit.app.mjs @@ -0,0 +1,238 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "signaturit", + version: "0.0.{{ts}}", + propDefinitions: { + // Required Props for Creating Certified Email + attachments: { + type: "string[]", + label: "Attachments", + description: "List of file URLs or paths to attach to the email.", + }, + recipients: { + type: "string[]", + label: "Recipients", + description: "List of recipients in JSON format, e.g., '{\"name\": \"John Doe\", \"email\": \"john@example.com\"}'.", + }, + type: { + type: "string", + label: "Type", + description: "Type of certified email.", + options: [ + "email", + "sms", + "url", + ], + }, + templates: { + type: "string[]", + label: "Templates", + description: "List of template IDs or names to use in the email.", + }, + // Required Props for Creating Signature Request + files: { + type: "string[]", + label: "Files", + description: "List of file URLs or paths to attach for the signature request.", + }, + name: { + type: "string", + label: "Name", + description: "Name assigned to the signature request.", + }, + // Required Prop for Sending Reminder + signatureRequestId: { + type: "string", + label: "Signature Request ID", + description: "ID of the signature request to send a reminder for.", + }, + // Optional Props + body: { + type: "string", + label: "Body", + description: "Email/SMS body.", + optional: true, + }, + brandingId: { + type: "string", + label: "Branding ID", + description: "ID of the branding to use.", + optional: true, + }, + eventsUrl: { + type: "string", + label: "Events URL", + description: "URL to receive real-time events.", + optional: true, + }, + data: { + type: "string[]", + label: "Custom Data", + description: "Custom key-value data in JSON format.", + optional: true, + }, + subject: { + type: "string", + label: "Subject", + description: "Email subject.", + optional: true, + }, + callbackUrl: { + type: "string", + label: "Callback URL", + description: "URL to redirect the user after signing.", + optional: true, + }, + deliveryType: { + type: "string", + label: "Delivery Type", + description: "Delivery type for signature request.", + options: [ + "email", + "sms", + "url", + ], + optional: true, + }, + expireTime: { + type: "integer", + label: "Expiration Time (days)", + description: "Expiration time of the document in days (max 365).", + min: 1, + max: 365, + optional: true, + }, + replyTo: { + type: "string", + label: "Reply To", + description: "Email Reply-To address.", + optional: true, + }, + reminders: { + type: "integer[]", + label: "Reminders (days)", + description: "Reminders in days to send automatic reminders.", + optional: true, + }, + signingMode: { + type: "string", + label: "Signing Mode", + description: "Signing mode: sequential or parallel.", + options: [ + "sequential", + "parallel", + ], + optional: true, + }, + typeSignature: { + type: "string", + label: "Signature Type", + description: "Type of signature request.", + optional: true, + }, + }, + methods: { + _baseUrl() { + return "https://api.signaturit.com/v3"; + }, + async _makeRequest(opts = {}) { + const { + $, method = "GET", path = "/", headers, ...otherOpts + } = opts; + return axios($, { + ...otherOpts, + method, + url: this._baseUrl() + path, + headers: { + ...headers, + "Authorization": `Bearer ${this.$auth.access_token}`, + }, + }); + }, + async createCertifiedEmail() { + return this._makeRequest({ + method: "POST", + path: "/emails.json", + data: { + attachments: this.attachments, + recipients: JSON.parse(`[${this.recipients.join(",")}]`), + type: this.type, + templates: this.templates, + body: this.body, + branding_id: this.brandingId, + events_url: this.eventsUrl, + data: this.data + ? JSON.parse(`[${this.data.join(",")}]`) + : undefined, + subject: this.subject, + }, + }); + }, + async createSignatureRequest() { + return this._makeRequest({ + method: "POST", + path: "/signatures.json", + data: { + files: this.files, + name: this.name, + recipients: JSON.parse(`[${this.recipients.join(",")}]`), + body: this.body, + branding_id: this.brandingId, + callback_url: this.callbackUrl, + data: this.data + ? JSON.parse(`[${this.data.join(",")}]`) + : undefined, + delivery_type: this.deliveryType, + expire_time: this.expireTime, + events_url: this.eventsUrl, + reply_to: this.replyTo, + reminders: this.reminders, + signing_mode: this.signingMode, + subject: this.subject, + type: this.typeSignature, + }, + }); + }, + async sendReminder() { + return this._makeRequest({ + method: "POST", + path: `/signatures/${this.signatureRequestId}/reminders.json`, + }); + }, + async listCompletedSignatures() { + return this.paginate(this.getCompletedSignatures, { + status: "completed", + }); + }, + async getCompletedSignatures(opts = {}) { + return this._makeRequest({ + method: "GET", + path: "/signatures.json", + params: { + status: opts.status, + page: opts.page || 1, + }, + }); + }, + async paginate(fn, params) { + let results = []; + let page = 1; + let hasMore = true; + while (hasMore) { + const response = await fn({ + ...params, + page, + }); + if (!response || response.length === 0) { + hasMore = false; + } else { + results = results.concat(response); + page += 1; + } + } + return results; + }, + }, +}; diff --git a/components/signaturit/sources/new-signed-document/new-signed-document.mjs b/components/signaturit/sources/new-signed-document/new-signed-document.mjs new file mode 100644 index 0000000000000..a64baef2f87c2 --- /dev/null +++ b/components/signaturit/sources/new-signed-document/new-signed-document.mjs @@ -0,0 +1,72 @@ +import { + axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, +} from "@pipedream/platform"; +import signaturit from "../../signaturit.app.mjs"; + +export default { + key: "signaturit-new-signed-document", + name: "New Signed Document", + description: "Emit new event when a document has been newly signed. [See the documentation]().", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + signaturit, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + hooks: { + async deploy() { + const signatures = await this.signaturit.listCompletedSignatures(); + const sortedSignatures = signatures.sort( + (a, b) => new Date(b.created_at) - new Date(a.created_at), + ).slice(0, 50); + + sortedSignatures.forEach((signature) => { + this.$emit(signature, { + id: signature.id, + summary: `New signed document: ${signature.name}`, + ts: new Date(signature.created_at).getTime(), + }); + }); + + if (sortedSignatures.length > 0) { + const latestTimestamp = new Date(sortedSignatures[0].created_at).getTime(); + await this.db.set("last_run", latestTimestamp); + } + }, + async activate() { + // No action needed on activate for polling source + }, + async deactivate() { + // No action needed on deactivate for polling source + }, + }, + async run() { + const lastRun = await this.db.get("last_run") || 0; + const signatures = await this.signaturit.listCompletedSignatures(); + const newSignatures = signatures.filter((signature) => { + const signatureTime = new Date(signature.created_at).getTime(); + return signatureTime > lastRun; + }).sort((a, b) => new Date(a.created_at) - new Date(b.created_at)); + + newSignatures.forEach((signature) => { + this.$emit(signature, { + id: signature.id, + summary: `New signed document: ${signature.name}`, + ts: new Date(signature.created_at).getTime(), + }); + }); + + if (newSignatures.length > 0) { + const latestSignature = newSignatures[newSignatures.length - 1]; + const latestTimestamp = new Date(latestSignature.created_at).getTime(); + await this.db.set("last_run", latestTimestamp); + } + }, +}; From 87cac626874a9fd91e1182d36fd0ed2ab86ec644 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 10 Dec 2024 10:39:10 -0300 Subject: [PATCH 2/6] [Components] signaturit #13223 Sources - New Signed Document Actions - Create Certified Email - Create Signature Request From Template - Send Signature Request Reminder --- components/signaturit/.gitignore | 3 - .../create-certified-email.mjs | 100 +++++-- ...create-signature-request-from-template.mjs | 195 +++++++++--- .../send-signature-request-reminder.mjs | 15 +- components/signaturit/app/signaturit.app.ts | 13 - components/signaturit/common/constants.mjs | 57 ++++ components/signaturit/common/utils.mjs | 39 +++ components/signaturit/package.json | 9 +- components/signaturit/signaturit.app.mjs | 281 +++++++----------- components/signaturit/sources/common/base.mjs | 62 ++++ .../new-signed-document.mjs | 71 +---- .../new-signed-document/test-event.mjs | 24 ++ 12 files changed, 526 insertions(+), 343 deletions(-) delete mode 100644 components/signaturit/.gitignore delete mode 100644 components/signaturit/app/signaturit.app.ts create mode 100644 components/signaturit/common/constants.mjs create mode 100644 components/signaturit/common/utils.mjs create mode 100644 components/signaturit/sources/common/base.mjs create mode 100644 components/signaturit/sources/new-signed-document/test-event.mjs diff --git a/components/signaturit/.gitignore b/components/signaturit/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/signaturit/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/signaturit/actions/create-certified-email/create-certified-email.mjs b/components/signaturit/actions/create-certified-email/create-certified-email.mjs index f1b38e3ee3f2b..8bedaa6cdf933 100644 --- a/components/signaturit/actions/create-certified-email/create-certified-email.mjs +++ b/components/signaturit/actions/create-certified-email/create-certified-email.mjs @@ -1,41 +1,20 @@ +import FormData from "form-data"; +import fs from "fs"; +import { TYPE_OPTIONS } from "../../common/constants.mjs"; +import { + checkTmp, + parseObject, +} from "../../common/utils.mjs"; import signaturit from "../../signaturit.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "signaturit-create-certified-email", name: "Create Certified Email", - description: "Initiates the creation of a certified email. [See the documentation]()", - version: "0.0.{{ts}}", + description: "Initiates the creation of a certified email. [See the documentation](https://docs.signaturit.com/api/latest#emails_create_email)", + version: "0.0.1", type: "action", props: { - signaturit: { - type: "app", - app: "signaturit", - }, - attachments: { - propDefinition: [ - signaturit, - "attachments", - ], - }, - recipients: { - propDefinition: [ - signaturit, - "recipients", - ], - }, - type: { - propDefinition: [ - signaturit, - "type", - ], - }, - templates: { - propDefinition: [ - signaturit, - "templates", - ], - }, + signaturit, body: { propDefinition: [ signaturit, @@ -64,6 +43,26 @@ export default { ], optional: true, }, + attachments: { + propDefinition: [ + signaturit, + "attachments", + ], + optional: true, + }, + recipients: { + propDefinition: [ + signaturit, + "recipients", + ], + }, + type: { + type: "string", + label: "Type", + description: "Type of certified email.", + options: TYPE_OPTIONS, + optional: true, + }, subject: { propDefinition: [ signaturit, @@ -73,7 +72,44 @@ export default { }, }, async run({ $ }) { - const response = await this.signaturit.createCertifiedEmail(); + const formData = new FormData(); + + let i = 0; + for (const recipient of parseObject(this.recipients)) { + for (const [ + key, + value, + ] of Object.entries(recipient)) { + formData.append(`recipients[${i}][${key}]`, value); + } + i++; + } + if (this.data) { + for (const [ + key, + value, + ] of Object.entries(this.data)) { + formData.append(`data[${key}]`, value); + } + i++; + } + if (this.body) formData.append("body", this.body); + if (this.brandingId) formData.append("branding_id", this.brandingId); + if (this.eventsUrl) formData.append("events_url", this.eventsUrl); + if (this.type) formData.append("type", this.type); + if (this.subject) formData.append("subject", this.subject); + + if (this.attachments) { + let j = 0; + for (const file of parseObject(this.attachments)) { + formData.append(`attachments[${j++}]`, fs.createReadStream(checkTmp(file))); + } + } + const response = await this.signaturit.createCertifiedEmail({ + $, + data: formData, + headers: formData.getHeaders(), + }); $.export("$summary", `Created certified email with ID: ${response.id}`); return response; }, diff --git a/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs b/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs index 7a5e6a1946f7c..a9121e515ce03 100644 --- a/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs +++ b/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs @@ -1,37 +1,30 @@ +import FormData from "form-data"; +import fs from "fs"; +import { + DELIVERY_TYPE_OPTIONS, + SIGNATURE_TYPE_OPTIONS, + SIGNING_MODE_OPTIONS, +} from "../../common/constants.mjs"; +import { + checkTmp, + parseObject, +} from "../../common/utils.mjs"; import signaturit from "../../signaturit.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "signaturit-create-signature-request-from-template", name: "Create Signature Request from Template", - description: "Creates a signature request using a pre-existing template. [See the documentation]()", - version: "0.0.{{ts}}", + description: "Creates a signature request using a pre-existing template. [See the documentation](https://docs.signaturit.com/api/latest#signatures_create_signature)", + version: "0.0.1", type: "action", props: { signaturit, - files: { - propDefinition: [ - signaturit, - "files", - ], - }, - name: { - propDefinition: [ - signaturit, - "name", - ], - }, - recipients: { - propDefinition: [ - signaturit, - "recipients", - ], - }, body: { propDefinition: [ signaturit, "body", ], + description: "Email body (html code is allowed) for **email** and **sms** type requests. Note: For **sms** request types it will be truncated to 120 characters Note: For **sms** the body should contain the tag **{{url}}** where we will include the document url.", optional: true, }, brandingId: { @@ -42,10 +35,9 @@ export default { optional: true, }, callbackUrl: { - propDefinition: [ - signaturit, - "callbackUrl", - ], + type: "string", + label: "Callback URL", + description: "Url to redirect the user when finish the signature process.", optional: true, }, data: { @@ -56,17 +48,18 @@ export default { optional: true, }, deliveryType: { - propDefinition: [ - signaturit, - "deliveryType", - ], + type: "string", + label: "Delivery Type", + description: "Delivery type for signature request.", + options: DELIVERY_TYPE_OPTIONS, optional: true, }, expireTime: { - propDefinition: [ - signaturit, - "expireTime", - ], + type: "integer", + label: "Expiration Time (days)", + description: "Expiration time of the document in days (max 365).", + min: 1, + max: 365, optional: true, }, eventsUrl: { @@ -76,25 +69,49 @@ export default { ], optional: true, }, - replyTo: { + files: { propDefinition: [ signaturit, - "replyTo", + "attachments", ], + label: "Files", optional: true, }, - reminders: { + name: { + type: "string", + label: "Name", + description: "Name assigned to the signature request.", + }, + recipients: { propDefinition: [ signaturit, - "reminders", + "recipients", ], + description: "List of recipients in JSON format, e.g., '{\"name\": \"John Doe\", \"email\": \"john@example.com\", \"phone\": \"34555667788\"}'. [See the documentation](https://docs.signaturit.com/api/latest#signatures_create_signature) for further information.", + }, + cc: { + type: "string[]", + label: "CC", + description: "List with email recipients containing name and email for people that will receive a copy of the signed document when the process is completed. e.g., '{\"name\": \"John Doe\", \"email\": \"john@example.com\"}'.", + optional: true, + }, + replyTo: { + type: "string", + label: "Reply To", + description: "Email Reply-To address.", + optional: true, + }, + reminders: { + type: "string[]", + label: "Reminders (days)", + description: "Reminders in days to send automatic reminders. You can set it 0 to disable reminders. E.g. [1,3,4]", optional: true, }, signingMode: { - propDefinition: [ - signaturit, - "signingMode", - ], + type: "string", + label: "Signing Mode", + description: "The signing mode lets you control the order in which your recipients receive and sign your documents.", + options: SIGNING_MODE_OPTIONS, optional: true, }, subject: { @@ -104,16 +121,106 @@ export default { ], optional: true, }, - type: { + templates: { propDefinition: [ signaturit, - "typeSignature", + "templates", ], + }, + type: { + type: "string", + label: "Type", + description: "The type of the signature.", + options: SIGNATURE_TYPE_OPTIONS, optional: true, }, }, + methods: { + transformArray({ + arr, prefixBase, formData, + }) { + let result = []; + function processObject(obj, prefix = "") { + for (let key in obj) { + if (Array.isArray(obj[key])) { + obj[key].forEach((item, index) => { + processObject(item, `${prefix}[${key}][${index}]`); + }); + } else if (typeof obj[key] === "object") { + processObject(obj[key], `${prefix}[${key}]`); + } else { + result.push({ + key: `${prefixBase}${prefix}[${key}]`, + value: `${obj[key]}`, + }); + } + } + } + arr.map((item, index) => { + processObject(item, `[${index}]`); + }); + for (const { + key, value, + } of result) { + formData.append(key, value); + } + return result; + }, + }, async run({ $ }) { - const response = await this.signaturit.createSignatureRequest(); + const formData = new FormData(); + this.transformArray({ + arr: parseObject(this.recipients), + prefixBase: "recipients", + formData, + }); + if (this.cc) { + let j; + for (const item of parseObject(this.cc)) { + formData.append(`cc[${j++}]`, item); + } + } + if (this.data) { + for (const [ + key, + value, + ] of Object.entries(parseObject(this.data))) { + formData.append(`data[${key}]`, value); + } + } + if (this.templates) { + let k = 0; + for (const templateId of parseObject(this.templates)) { + formData.append(`templates[${k++}]`, templateId); + } + } + if (this.reminders) { + let m = 0; + for (const reminder of parseObject(this.reminders)) { + formData.append(`reminders[${m++}]`, reminder); + } + } + if (this.body) formData.append("body", this.body); + if (this.brandingId) formData.append("branding_id", this.brandingId); + if (this.callbackUrl) formData.append("callback_url", this.callbackUrl); + if (this.deliveryType) formData.append("delivery_type", this.deliveryType); + if (this.expireTime) formData.append("expire_time", this.expireTime); + if (this.eventsUrl) formData.append("events_url", this.eventsUrl); + if (this.name) formData.append("name", this.name); + if (this.replyTo) formData.append("reply_to", this.replyTo); + if (this.type) formData.append("type", this.type); + + if (this.files) { + let k = 0; + for (const file of parseObject(this.files)) { + formData.append(`files[${k++}]`, fs.createReadStream(checkTmp(file))); + } + } + const response = await this.signaturit.createSignatureRequest({ + $, + data: formData, + headers: formData.getHeaders(), + }); $.export("$summary", `Created signature request with ID: ${response.id}`); return response; }, diff --git a/components/signaturit/actions/send-signature-request-reminder/send-signature-request-reminder.mjs b/components/signaturit/actions/send-signature-request-reminder/send-signature-request-reminder.mjs index 1df3970db9896..7ce371d9838f7 100644 --- a/components/signaturit/actions/send-signature-request-reminder/send-signature-request-reminder.mjs +++ b/components/signaturit/actions/send-signature-request-reminder/send-signature-request-reminder.mjs @@ -1,17 +1,13 @@ import signaturit from "../../signaturit.app.mjs"; -import { axios } from "@pipedream/platform"; export default { key: "signaturit-send-signature-request-reminder", name: "Send Signature Request Reminder", - description: "Sends a reminder for a pending signature request. [See the documentation](https://docs.signaturit.com/api/latest)", - version: "0.0.{{ts}}", + description: "Sends a reminder for a pending signature request. [See the documentation](https://docs.signaturit.com/api/latest#signatures_send_reminder)", + version: "0.0.1", type: "action", props: { - signaturit: { - type: "app", - app: "signaturit", - }, + signaturit, signatureRequestId: { propDefinition: [ signaturit, @@ -20,7 +16,10 @@ export default { }, }, async run({ $ }) { - const response = await this.signaturit.sendReminder(); + const response = await this.signaturit.sendReminder({ + $, + signatureRequestId: this.signatureRequestId, + }); $.export("$summary", `Sent reminder for signature request ${this.signatureRequestId}`); return response; }, diff --git a/components/signaturit/app/signaturit.app.ts b/components/signaturit/app/signaturit.app.ts deleted file mode 100644 index a47338ec2210a..0000000000000 --- a/components/signaturit/app/signaturit.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "signaturit", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); diff --git a/components/signaturit/common/constants.mjs b/components/signaturit/common/constants.mjs new file mode 100644 index 0000000000000..4f6a575022b0b --- /dev/null +++ b/components/signaturit/common/constants.mjs @@ -0,0 +1,57 @@ +export const LIMIT = 100; + +export const TYPE_OPTIONS = [ + { + label: "Delivery - Send the email as it is certifying the delivery process.", + value: "delivery", + }, + { + label: "Open Document - Send a modified version of the email with a button that redirects the user to our platform to open the **PDF** attachments. With this method, you can track when the user opens the attached files. Note: This method only supports **PDF** documents to be attached.", + value: "open_document", + }, + { + label: "Open Every Document - This type works like the **Open Document** type but allows to track the opening of every **PDF** file in emails with multiple attachments.", + value: "open_every_document", + }, +]; + +export const DELIVERY_TYPE_OPTIONS = [ + { + label: "Email - The signature request is sent by email. This is the default behavior when no type is specified.", + value: "email", + }, + { + label: "SMS - The signature request is sent by SMS. You must include the **phone** in the **recipients** parameter.", + value: "sms", + }, + { + label: "URL - The signature request is not sent to the signer. Instead of this, the creation request will return a **url** parameter that you can open in the browser to complete the signature.", + value: "url", + }, +]; + +export const SIGNING_MODE_OPTIONS = [ + { + label: "Sequential - Each recipient receives the request once the previous recipient has completed their action.", + value: "sequential", + }, + { + label: "Parallel - All recipients receive the request in parallel.", + value: "parallel", + }, +]; + +export const SIGNATURE_TYPE_OPTIONS = [ + { + label: "Simple - A simple signature request is created.", + value: "simple", + }, + { + label: "Advanced - An advanced signature request is created. We capture the biometric information of the signer with the signature draw.", + value: "advanced", + }, + { + label: "Smart - The system creates different type of signature depending in the user device. A simple signature is created for desktop pcs and advanced signature is created for mobile and tablet devices.", + value: "smart", + }, +]; diff --git a/components/signaturit/common/utils.mjs b/components/signaturit/common/utils.mjs new file mode 100644 index 0000000000000..b75bc9340048a --- /dev/null +++ b/components/signaturit/common/utils.mjs @@ -0,0 +1,39 @@ +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; +}; + +export const checkTmp = (filename) => { + if (!filename.startsWith("/tmp")) { + return `/tmp/${filename}`; + } + return filename; +}; + +export const getDateFormatted = (dateStr) => { + const date = new Date(dateStr); + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + const day = String(date.getDate()).padStart(2, "0"); + return `${year}-${month}-${day}`; +}; diff --git a/components/signaturit/package.json b/components/signaturit/package.json index 90b869197bb74..250dfbf47b71c 100644 --- a/components/signaturit/package.json +++ b/components/signaturit/package.json @@ -1,16 +1,19 @@ { "name": "@pipedream/signaturit", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Signaturit Components", - "main": "dist/app/signaturit.app.mjs", + "main": "signaturit.app.mjs", "keywords": [ "pipedream", "signaturit" ], - "files": ["dist"], "homepage": "https://pipedream.com/apps/signaturit", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3", + "fs": "^0.0.1-security" } } diff --git a/components/signaturit/signaturit.app.mjs b/components/signaturit/signaturit.app.mjs index 9424953dab2a5..150b024880a63 100644 --- a/components/signaturit/signaturit.app.mjs +++ b/components/signaturit/signaturit.app.mjs @@ -1,238 +1,169 @@ import { axios } from "@pipedream/platform"; +import { LIMIT } from "./common/constants.mjs"; export default { type: "app", app: "signaturit", - version: "0.0.{{ts}}", propDefinitions: { - // Required Props for Creating Certified Email - attachments: { - type: "string[]", - label: "Attachments", - description: "List of file URLs or paths to attach to the email.", - }, - recipients: { - type: "string[]", - label: "Recipients", - description: "List of recipients in JSON format, e.g., '{\"name\": \"John Doe\", \"email\": \"john@example.com\"}'.", - }, - type: { - type: "string", - label: "Type", - description: "Type of certified email.", - options: [ - "email", - "sms", - "url", - ], - }, - templates: { - type: "string[]", - label: "Templates", - description: "List of template IDs or names to use in the email.", - }, - // Required Props for Creating Signature Request - files: { - type: "string[]", - label: "Files", - description: "List of file URLs or paths to attach for the signature request.", - }, - name: { - type: "string", - label: "Name", - description: "Name assigned to the signature request.", - }, - // Required Prop for Sending Reminder - signatureRequestId: { - type: "string", - label: "Signature Request ID", - description: "ID of the signature request to send a reminder for.", - }, - // Optional Props body: { type: "string", label: "Body", - description: "Email/SMS body.", - optional: true, + description: "Email body (html code is allowed).", }, brandingId: { type: "string", label: "Branding ID", description: "ID of the branding to use.", - optional: true, + async options() { + const data = await this.listBrandings(); + + return data.map(({ id }) => id); + }, }, eventsUrl: { type: "string", label: "Events URL", description: "URL to receive real-time events.", - optional: true, }, data: { - type: "string[]", + type: "object", label: "Custom Data", description: "Custom key-value data in JSON format.", - optional: true, }, - subject: { - type: "string", - label: "Subject", - description: "Email subject.", - optional: true, + attachments: { + type: "string[]", + label: "Attachments", + description: "A list of paths to the files saved to the `/tmp` directory (e.g. `/tmp/example.pdf`). [See the documentation](https://pipedream.com/docs/workflows/steps/code/nodejs/working-with-files/#the-tmp-directory).", }, - callbackUrl: { - type: "string", - label: "Callback URL", - description: "URL to redirect the user after signing.", - optional: true, + recipients: { + type: "string[]", + label: "Recipients", + description: "List of recipients in JSON format, e.g., '{\"name\": \"John Doe\", \"email\": \"john@example.com\"}'. [See the documentation](https://docs.signaturit.com/api/latest#emails_create_email) for further information.", }, - deliveryType: { - type: "string", - label: "Delivery Type", - description: "Delivery type for signature request.", - options: [ - "email", - "sms", - "url", - ], - optional: true, - }, - expireTime: { - type: "integer", - label: "Expiration Time (days)", - description: "Expiration time of the document in days (max 365).", - min: 1, - max: 365, - optional: true, - }, - replyTo: { + subject: { type: "string", - label: "Reply To", - description: "Email Reply-To address.", - optional: true, + label: "Subject", + description: "Email subject for **email** type requests.", }, - reminders: { - type: "integer[]", - label: "Reminders (days)", - description: "Reminders in days to send automatic reminders.", - optional: true, + templates: { + type: "string[]", + label: "Templates", + description: "Templates to use in the signature request.", + async options({ page }) { + const data = await this.listTemplates({ + params: { + limit: LIMIT, + offset: LIMIT * page, + }, + }); + + return data.map(({ + id: value, name: label, + }) => ({ + label, + value, + })); + }, }, - signingMode: { - type: "string", - label: "Signing Mode", - description: "Signing mode: sequential or parallel.", - options: [ - "sequential", - "parallel", - ], - optional: true, - }, - typeSignature: { + signatureRequestId: { type: "string", - label: "Signature Type", - description: "Type of signature request.", - optional: true, + label: "Signature Request ID", + description: "ID of the signature request to send a reminder for.", + async options({ page }) { + const data = await this.listSignatureRequests({ + params: { + limit: LIMIT, + offset: LIMIT * page, + }, + }); + + return data.map(({ id }) => id); + }, }, }, methods: { _baseUrl() { - return "https://api.signaturit.com/v3"; + return `https://${this.$auth.domain}.signaturit.com/v3`; + }, + _headers(headers = {}) { + return { + ...headers, + "Authorization": `Bearer ${this.$auth.access_token}`, + }; }, - async _makeRequest(opts = {}) { - const { - $, method = "GET", path = "/", headers, ...otherOpts - } = opts; + _makeRequest({ + $ = this, path, headers, ...opts + }) { return axios($, { - ...otherOpts, - method, url: this._baseUrl() + path, - headers: { - ...headers, - "Authorization": `Bearer ${this.$auth.access_token}`, - }, + headers: this._headers(headers), + ...opts, + }); + }, + listBrandings() { + return this._makeRequest({ + path: "/brandings.json", + }); + }, + listTemplates() { + return this._makeRequest({ + path: "/templates.json", }); }, - async createCertifiedEmail() { + createCertifiedEmail(opts = {}) { return this._makeRequest({ method: "POST", path: "/emails.json", - data: { - attachments: this.attachments, - recipients: JSON.parse(`[${this.recipients.join(",")}]`), - type: this.type, - templates: this.templates, - body: this.body, - branding_id: this.brandingId, - events_url: this.eventsUrl, - data: this.data - ? JSON.parse(`[${this.data.join(",")}]`) - : undefined, - subject: this.subject, - }, + ...opts, }); }, - async createSignatureRequest() { + createSignatureRequest(opts = {}) { return this._makeRequest({ method: "POST", path: "/signatures.json", - data: { - files: this.files, - name: this.name, - recipients: JSON.parse(`[${this.recipients.join(",")}]`), - body: this.body, - branding_id: this.brandingId, - callback_url: this.callbackUrl, - data: this.data - ? JSON.parse(`[${this.data.join(",")}]`) - : undefined, - delivery_type: this.deliveryType, - expire_time: this.expireTime, - events_url: this.eventsUrl, - reply_to: this.replyTo, - reminders: this.reminders, - signing_mode: this.signingMode, - subject: this.subject, - type: this.typeSignature, - }, + ...opts, }); }, - async sendReminder() { + sendReminder({ signatureRequestId }) { return this._makeRequest({ method: "POST", - path: `/signatures/${this.signatureRequestId}/reminders.json`, + path: `/signatures/${signatureRequestId}/reminder.json`, }); }, - async listCompletedSignatures() { - return this.paginate(this.getCompletedSignatures, { - status: "completed", - }); - }, - async getCompletedSignatures(opts = {}) { + listSignatureRequests(opts = {}) { return this._makeRequest({ method: "GET", path: "/signatures.json", - params: { - status: opts.status, - page: opts.page || 1, - }, + ...opts, }); }, - async paginate(fn, params) { - let results = []; - let page = 1; - let hasMore = true; - while (hasMore) { - const response = await fn({ - ...params, - page, + async *paginate({ + fn, params = {}, maxResults = null, ...opts + }) { + let hasMore = false; + let count = 0; + let page = 0; + + do { + params.limit = LIMIT; + params.offset = LIMIT * page; + page++; + + const data = await fn({ + params, + ...opts, }); - if (!response || response.length === 0) { - hasMore = false; - } else { - results = results.concat(response); - page += 1; + for (const d of data) { + yield d; + + if (maxResults && ++count === maxResults) { + return count; + } } - } - return results; + + hasMore = data.length; + + } while (hasMore); }, }, }; diff --git a/components/signaturit/sources/common/base.mjs b/components/signaturit/sources/common/base.mjs new file mode 100644 index 0000000000000..97ced71101be7 --- /dev/null +++ b/components/signaturit/sources/common/base.mjs @@ -0,0 +1,62 @@ +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; +import { getDateFormatted } from "../../common/utils.mjs"; +import app from "../../signaturit.app.mjs"; + +export default { + props: { + app, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastDate() { + return this.db.get("lastDate") || 0; + }, + _setLastDate(lastDate) { + this.db.set("lastDate", lastDate); + }, + async emitEvent(maxResults = false) { + const lastDate = this._getLastDate(); + + const response = this.app.paginate({ + fn: this.app.listSignatureRequests, + params: { + status: "completed", + since: getDateFormatted(lastDate), + }, + maxResults, + }); + + let responseArray = []; + for await (const item of response) { + if (Date.parse(item.created_at) <= lastDate) break; + responseArray.push(item); + } + + if (responseArray.length) { + this._setLastDate(Date.parse(responseArray[0].created_at)); + } + + for (const item of responseArray.reverse()) { + this.$emit(item, { + id: item.id, + summary: `New signed document: ${item.id}`, + ts: Date.parse(item.created_at), + }); + } + }, + }, + hooks: { + async deploy() { + await this.emitEvent(25); + }, + }, + async run() { + await this.emitEvent(); + }, +}; diff --git a/components/signaturit/sources/new-signed-document/new-signed-document.mjs b/components/signaturit/sources/new-signed-document/new-signed-document.mjs index a64baef2f87c2..3b0e998e6c2c3 100644 --- a/components/signaturit/sources/new-signed-document/new-signed-document.mjs +++ b/components/signaturit/sources/new-signed-document/new-signed-document.mjs @@ -1,72 +1,13 @@ -import { - axios, DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, -} from "@pipedream/platform"; -import signaturit from "../../signaturit.app.mjs"; +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; export default { + ...common, key: "signaturit-new-signed-document", name: "New Signed Document", - description: "Emit new event when a document has been newly signed. [See the documentation]().", - version: "0.0.{{ts}}", + description: "Emit new event when a document has been newly signed.", + version: "0.0.1", type: "source", dedupe: "unique", - props: { - signaturit, - db: "$.service.db", - timer: { - type: "$.interface.timer", - default: { - intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, - }, - }, - }, - hooks: { - async deploy() { - const signatures = await this.signaturit.listCompletedSignatures(); - const sortedSignatures = signatures.sort( - (a, b) => new Date(b.created_at) - new Date(a.created_at), - ).slice(0, 50); - - sortedSignatures.forEach((signature) => { - this.$emit(signature, { - id: signature.id, - summary: `New signed document: ${signature.name}`, - ts: new Date(signature.created_at).getTime(), - }); - }); - - if (sortedSignatures.length > 0) { - const latestTimestamp = new Date(sortedSignatures[0].created_at).getTime(); - await this.db.set("last_run", latestTimestamp); - } - }, - async activate() { - // No action needed on activate for polling source - }, - async deactivate() { - // No action needed on deactivate for polling source - }, - }, - async run() { - const lastRun = await this.db.get("last_run") || 0; - const signatures = await this.signaturit.listCompletedSignatures(); - const newSignatures = signatures.filter((signature) => { - const signatureTime = new Date(signature.created_at).getTime(); - return signatureTime > lastRun; - }).sort((a, b) => new Date(a.created_at) - new Date(b.created_at)); - - newSignatures.forEach((signature) => { - this.$emit(signature, { - id: signature.id, - summary: `New signed document: ${signature.name}`, - ts: new Date(signature.created_at).getTime(), - }); - }); - - if (newSignatures.length > 0) { - const latestSignature = newSignatures[newSignatures.length - 1]; - const latestTimestamp = new Date(latestSignature.created_at).getTime(); - await this.db.set("last_run", latestTimestamp); - } - }, + sampleEmit, }; diff --git a/components/signaturit/sources/new-signed-document/test-event.mjs b/components/signaturit/sources/new-signed-document/test-event.mjs new file mode 100644 index 0000000000000..cf77f1539cd29 --- /dev/null +++ b/components/signaturit/sources/new-signed-document/test-event.mjs @@ -0,0 +1,24 @@ +export default { + created_at: "2014-08-21T08:53:35+0000", + data: [], + documents: [ + { + email: "john@signaturit.com", + events: [ + { + type: "email_processed", + created_at: "2014-08-21T08:55:14+0000" + } + ], + file: { + name: "document.pdf", + pages: 11, + size: 72218, + }, + id: "9781f42d-2910-11e4-b3d4-0aa7697eb409", + name: "John", + status: "ready", + } + ], + id: "974e6f6c-2910-11e4-b3d4-0aa7697eb409" +} \ No newline at end of file From c7dbef95373c4ac4f10ed7c5d6d031cfd3086e8f Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 10 Dec 2024 10:40:22 -0300 Subject: [PATCH 3/6] pnpm update --- pnpm-lock.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b63ea3ed13242..fd6e8b5142dc2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9364,7 +9364,14 @@ importers: specifier: ^1.5.1 version: 1.6.6 - components/signaturit: {} + components/signaturit: + dependencies: + '@pipedream/platform': + specifier: ^3.0.3 + version: 3.0.3 + fs: + specifier: ^0.0.1-security + version: 0.0.1-security components/signerx: dependencies: From b90d7d475c7d5154527e936e0aed20241b90e1f7 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Tue, 10 Dec 2024 10:51:19 -0300 Subject: [PATCH 4/6] Update components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- .../create-signature-request-from-template.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs b/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs index a9121e515ce03..11198fe5734ea 100644 --- a/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs +++ b/components/signaturit/actions/create-signature-request-from-template/create-signature-request-from-template.mjs @@ -175,7 +175,7 @@ export default { formData, }); if (this.cc) { - let j; + let j = 0; for (const item of parseObject(this.cc)) { formData.append(`cc[${j++}]`, item); } From a7c26a0a0d508bc8cdcb6a14040f579a97f886b3 Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 11 Dec 2024 15:41:45 -0300 Subject: [PATCH 5/6] some adjusts --- components/signaturit/package.json | 1 + components/signaturit/signaturit.app.mjs | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/components/signaturit/package.json b/components/signaturit/package.json index 250dfbf47b71c..81bceb2a914a3 100644 --- a/components/signaturit/package.json +++ b/components/signaturit/package.json @@ -14,6 +14,7 @@ }, "dependencies": { "@pipedream/platform": "^3.0.3", + "form-data": "^4.0.1", "fs": "^0.0.1-security" } } diff --git a/components/signaturit/signaturit.app.mjs b/components/signaturit/signaturit.app.mjs index 150b024880a63..7078264908002 100644 --- a/components/signaturit/signaturit.app.mjs +++ b/components/signaturit/signaturit.app.mjs @@ -124,10 +124,13 @@ export default { ...opts, }); }, - sendReminder({ signatureRequestId }) { + sendReminder({ + signatureRequestId, ...opts + }) { return this._makeRequest({ method: "POST", path: `/signatures/${signatureRequestId}/reminder.json`, + ...opts, }); }, listSignatureRequests(opts = {}) { From 5a79456cc3ea6924514545a6917f744781a04abf Mon Sep 17 00:00:00 2001 From: Luan Cazarine Date: Wed, 11 Dec 2024 16:27:59 -0300 Subject: [PATCH 6/6] pnpm update --- pnpm-lock.yaml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aa3ec22b1c3e0..07bfc072b6085 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9388,6 +9388,9 @@ importers: '@pipedream/platform': specifier: ^3.0.3 version: 3.0.3 + form-data: + specifier: ^4.0.1 + version: 4.0.1 fs: specifier: ^0.0.1-security version: 0.0.1-security @@ -24496,22 +24499,22 @@ packages: superagent@3.8.1: resolution: {integrity: sha512-VMBFLYgFuRdfeNQSMLbxGSLfmXL/xc+OO+BZp41Za/NRDBet/BNbkRJrYzCUu0u4GU0i/ml2dtT8b9qgkw9z6Q==} engines: {node: '>= 4.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@4.1.0: resolution: {integrity: sha512-FT3QLMasz0YyCd4uIi5HNe+3t/onxMyEho7C3PSqmti3Twgy2rXT4fmkTz6wRL6bTF4uzPcfkUCa8u4JWHw8Ag==} engines: {node: '>= 6.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@5.3.1: resolution: {integrity: sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ==} engines: {node: '>= 7.0.0'} - deprecated: Please upgrade to v7.0.2+ of superagent. We have fixed numerous issues with streams, form-data, attach(), filesystem errors not bubbling up (ENOENT on attach()), and all tests are now passing. See the releases tab for more information at . + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net superagent@7.1.6: resolution: {integrity: sha512-gZkVCQR1gy/oUXr+kxJMLDjla434KmSOKbx5iGD30Ql+AkJQ/YlPKECJy2nhqOsHLjGHzoDTXNSjhnvWhzKk7g==} engines: {node: '>=6.4.0 <13 || >=14'} - deprecated: Please downgrade to v7.1.5 if you need IE/ActiveXObject support OR upgrade to v8.0.0 as we no longer support IE and published an incorrect patch version (see https://github.com/visionmedia/superagent/issues/1731) + deprecated: Please upgrade to v9.0.0+ as we have fixed a public vulnerability with formidable dependency. Note that v9.0.0+ requires Node.js v14.18.0+. See https://github.com/ladjs/superagent/pull/1800 for insight. This project is supported and maintained by the team at Forward Email @ https://forwardemail.net supports-color@2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==}