diff --git a/components/papersign/actions/copy-document/copy-document.mjs b/components/papersign/actions/copy-document/copy-document.mjs new file mode 100644 index 0000000000000..19202f76c34a5 --- /dev/null +++ b/components/papersign/actions/copy-document/copy-document.mjs @@ -0,0 +1,65 @@ +import papersign from "../../papersign.app.mjs"; + +export default { + key: "papersign-copy-document", + name: "Copy Document", + description: "Duplicates a given document. [See the documentation](https://paperform.readme.io/reference/papersigncopydocument)", + version: "0.0.1", + type: "action", + props: { + papersign, + documentId: { + propDefinition: [ + papersign, + "documentId", + ], + }, + name: { + type: "string", + label: "Name", + description: "The new name of the copied document", + optional: true, + }, + spaceId: { + propDefinition: [ + papersign, + "spaceId", + ], + optional: true, + }, + path: { + type: "string", + label: "Path", + description: "The path to copy the document to. Maximum depth is 4 levels. Any missing folders will be created.", + optional: true, + }, + folderId: { + propDefinition: [ + papersign, + "folderId", + ], + optional: true, + }, + }, + async run({ $ }) { + const data = {}; + if (this.folderId) { + data.folder_id = this.folderId; + } else { + data.space_id = this.spaceId; + data.path = this.path; + } + + const response = await this.papersign.duplicateDocument({ + $, + documentId: this.documentId, + data: { + ...data, + name: this.name, + }, + }); + + $.export("$summary", `Successfully copied document: ${response.results.document.name}`); + return response; + }, +}; diff --git a/components/papersign/actions/get-document/get-document.mjs b/components/papersign/actions/get-document/get-document.mjs new file mode 100644 index 0000000000000..d566fdd390518 --- /dev/null +++ b/components/papersign/actions/get-document/get-document.mjs @@ -0,0 +1,27 @@ +import papersign from "../../papersign.app.mjs"; + +export default { + key: "papersign-get-document", + name: "Get Document", + description: "Retrieve a document using a specified ID. [See the documentation](https://paperform.readme.io/reference/getpapersigndocument)", + version: "0.0.1", + type: "action", + props: { + papersign, + documentId: { + propDefinition: [ + papersign, + "documentId", + ], + }, + }, + async run({ $ }) { + const response = await this.papersign.getDocument({ + $, + documentId: this.documentId, + }); + + $.export("$summary", `Successfully retrieved document with ID: ${this.documentId}`); + return response; + }, +}; diff --git a/components/papersign/actions/send-document/send-document.mjs b/components/papersign/actions/send-document/send-document.mjs new file mode 100644 index 0000000000000..1847319e6a907 --- /dev/null +++ b/components/papersign/actions/send-document/send-document.mjs @@ -0,0 +1,115 @@ +import { ConfigurationError } from "@pipedream/platform"; +import { parseObject } from "../../common/utils.mjs"; +import papersign from "../../papersign.app.mjs"; + +export default { + key: "papersign-send-document", + name: "Send Document", + description: "Dispatches a document to a specified recipient. [See the documentation](https://paperform.readme.io/reference/papersignsenddocument)", + version: "0.0.1", + type: "action", + props: { + papersign, + documentId: { + propDefinition: [ + papersign, + "documentId", + ], + }, + expiration: { + type: "string", + label: "Expiration", + description: "The expiration date of the document. Must be at least 30 minutes in the future. **Format: YYYY-MM-DDTHH:MM:SS.SSSZ**", + optional: true, + }, + inviteMessage: { + type: "string", + label: "Invite Message", + description: "The message to include in the invitation email, up to 1000 characters.", + optional: true, + }, + fromUserEmail: { + type: "string", + label: "From User Email", + description: "The email address of a User on your team's account to send the document from.", + optional: true, + }, + documentRecipientEmails: { + type: "string[]", + label: "Document Recipient Emails", + description: "An array of recipient emails for the document.", + optional: true, + }, + firstAfterDays: { + type: "integer", + label: "Automatic Reminder - First After Days", + description: "The number of days after the document is sent to send the reminder.", + optional: true, + }, + followUpEveryDays: { + type: "integer", + label: "Automatic Reminder - Follow Up Every Days", + description: "The number of days to wait between reminders.", + optional: true, + }, + signers: { + type: "string[]", + label: "Signers", + description: "An array of objects of signers. **Object format: {\"key\": \"123\",\"name\": \"Jack Smith\",\"email\": \"signer@example.com\",\"phone\": \"123 456 7899\",\"job_title\": \"Account Manager\",\"company\": \"Explosive Startup\",\"custom_attributes\": [{\"key\": \"Relationship\",\"label\": \"Relationship to the company\",\"value\": \"CEO\"}]}**", + optional: true, + }, + variables: { + type: "object", + label: "Variables", + description: "The key: value of the document variables.", + optional: true, + }, + copy: { + type: "boolean", + label: "Copy", + description: "Whether to copy before sending.", + optional: true, + }, + }, + async run({ $ }) { + if ( + (this.firstAfterDays && !this.followUpEveryDays) || + (!this.firstAfterDays && this.followUpEveryDays) + ) { + throw new ConfigurationError("You must fill in the fields 'First After Days' and 'Follow Up Every Days' or none of them"); + } + + const automaticReminders = {}; + if (this.firstAfterDays) { + automaticReminders.first_after_days = this.firstAfterDays; + automaticReminders.follow_up_every_days = this.followUpEveryDays; + } + + const variables = []; + if (this.variables) { + for (const key of Object.keys(parseObject(this.variables))) { + variables.push({ + key, + value: this.variables[key], + }); + } + } + + const response = await this.papersign.sendDocument({ + $, + documentId: this.documentId, + data: { + expiration: this.expiration, + inviteMessage: this.inviteMessage, + fromUserEmail: this.fromUserEmail, + documentRecipientEmails: parseObject(this.documentRecipientEmails), + automaticReminders, + signers: parseObject(this.signers), + variables, + copy: this.copy, + }, + }); + $.export("$summary", `Document sent successfully with ID ${this.documentId}`); + return response; + }, +}; diff --git a/components/papersign/common/constants.mjs b/components/papersign/common/constants.mjs new file mode 100644 index 0000000000000..28a6656d208ad --- /dev/null +++ b/components/papersign/common/constants.mjs @@ -0,0 +1,12 @@ +export const LIMIT = 100; + +export const SCOPE_OPTIONS = [ + { + label: "Direct Children", + value: "folder.direct_children", + }, + { + label: "All Descendants", + value: "folder.all_descendants", + }, +]; diff --git a/components/papersign/common/utils.mjs b/components/papersign/common/utils.mjs new file mode 100644 index 0000000000000..9050833bcda0b --- /dev/null +++ b/components/papersign/common/utils.mjs @@ -0,0 +1,32 @@ +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 parseObject(item); + } + } + return item; + }); + } + if (typeof obj === "string") { + try { + return JSON.parse(obj); + } catch (e) { + return obj; + } + } + if (typeof obj === "object") { + for (const [ + key, + value, + ] of Object.entries(obj)) { + obj[key] = parseObject(value); + } + } + return obj; +}; diff --git a/components/papersign/package.json b/components/papersign/package.json index 8e3df417d8ff5..656e518ce4d24 100644 --- a/components/papersign/package.json +++ b/components/papersign/package.json @@ -1,6 +1,6 @@ { "name": "@pipedream/papersign", - "version": "0.0.1", + "version": "0.1.0", "description": "Pipedream Papersign Components", "main": "papersign.app.mjs", "keywords": [ @@ -11,5 +11,8 @@ "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } -} \ No newline at end of file +} diff --git a/components/papersign/papersign.app.mjs b/components/papersign/papersign.app.mjs index d51851226db7e..2f3e59c9ec6f2 100644 --- a/components/papersign/papersign.app.mjs +++ b/components/papersign/papersign.app.mjs @@ -1,11 +1,122 @@ +import { axios } from "@pipedream/platform"; +import { LIMIT } from "./common/constants.mjs"; + export default { type: "app", app: "papersign", - propDefinitions: {}, + propDefinitions: { + documentId: { + type: "string", + label: "Document ID", + description: "Enter the ID of the Papersign document", + async options({ page }) { + return await this.list({ + module: "documents", + page, + }); + }, + }, + spaceId: { + type: "string", + label: "Space ID", + description: "Enter the ID of the Papersign space", + async options({ page }) { + return await this.list({ + module: "spaces", + page, + }); + }, + }, + folderId: { + type: "string", + label: "Folder ID", + description: "Enter the ID of the Papersign folder. `If folder id is present, Space Id and Path will be ignored`.", + async options({ page }) { + return await this.list({ + module: "folders", + page, + }); + }, + }, + }, methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); + _baseUrl() { + return "https://api.paperform.co/v1/papersign"; + }, + _headers() { + return { + "Authorization": `Bearer ${this.$auth.access_token}`, + "accept": "application/json", + }; + }, + _makeRequest({ + $ = this, path, ...opts + }) { + return axios($, { + url: this._baseUrl() + path, + headers: this._headers(), + ...opts, + }); + }, + async list({ + module, page, ...opts + }) { + const { results } = await this._makeRequest({ + path: `/${module}`, + params: { + limit: LIMIT, + skip: LIMIT * page, + }, + ...opts, + }); + + return results[module].map(({ + id: value, name: label, + }) => ({ + value, + label, + })); + }, + duplicateDocument({ + documentId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/documents/${documentId}/copy`, + ...opts, + }); + }, + getDocument({ + documentId, ...opts + }) { + return this._makeRequest({ + path: `/documents/${documentId}`, + ...opts, + }); + }, + sendDocument({ + documentId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/documents/${documentId}/send`, + ...opts, + }); + }, + createWebhook({ + folderId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/folders/${folderId}/webhooks`, + ...opts, + }); + }, + deleteWebhook(hookId) { + return this._makeRequest({ + method: "DELETE", + path: `/webhooks/${hookId}`, + }); }, }, -}; \ No newline at end of file +}; diff --git a/components/papersign/sources/common/base.mjs b/components/papersign/sources/common/base.mjs new file mode 100644 index 0000000000000..3fcd824eb6ed5 --- /dev/null +++ b/components/papersign/sources/common/base.mjs @@ -0,0 +1,64 @@ +import { SCOPE_OPTIONS } from "../../common/constants.mjs"; +import papersign from "../../papersign.app.mjs"; + +export default { + props: { + papersign, + http: { + type: "$.interface.http", + customResponse: false, + }, + db: "$.service.db", + folderId: { + propDefinition: [ + papersign, + "folderId", + ], + }, + name: { + type: "string", + label: "Name", + description: "The name of the webhook.", + optional: true, + }, + scope: { + type: "string", + label: "Scope", + description: "The scope of the webhook", + options: SCOPE_OPTIONS, + }, + }, + methods: { + _setHookId(hookId) { + this.db.set("hookId", hookId); + }, + _getHookId() { + return this.db.get("hookId"); + }, + }, + hooks: { + async activate() { + const { results: { webhook } } = await this.papersign.createWebhook({ + folderId: this.folderId, + data: { + name: this.name, + target_url: this.http.endpoint, + scope: this.scope, + triggers: this.getTriggers(), + }, + }); + this._setHookId(webhook.id); + }, + async deactivate() { + const webhookId = this._getHookId(); + await this.papersign.deleteWebhook(webhookId); + }, + }, + async run({ body }) { + this.$emit(body, { + id: body.id, + summary: this.getSummary(body), + ts: Date.parse(body.created_at), + }); + }, +}; diff --git a/components/papersign/sources/new-document-completed-instant/new-document-completed-instant.mjs b/components/papersign/sources/new-document-completed-instant/new-document-completed-instant.mjs new file mode 100644 index 0000000000000..40175d61ff4f1 --- /dev/null +++ b/components/papersign/sources/new-document-completed-instant/new-document-completed-instant.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "papersign-new-document-completed-instant", + name: "New Document Completed (Instant)", + description: "Emit new event when a document is completed, i.e., all signers have signed.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getTriggers() { + return [ + "document.completed", + ]; + }, + getSummary(body) { + return `Document '${body.document_name}' completed`; + }, + }, + sampleEmit, +}; diff --git a/components/papersign/sources/new-document-completed-instant/test-event.mjs b/components/papersign/sources/new-document-completed-instant/test-event.mjs new file mode 100644 index 0000000000000..568316a81e779 --- /dev/null +++ b/components/papersign/sources/new-document-completed-instant/test-event.mjs @@ -0,0 +1,10 @@ +export default { + "id": "671ba9030af55bff3b7a8c00", + "document_id": "671ba9030af55bff3b7a8c00", + "document_name": "Document Name", + "type": "document.completed", + "data": { + "document_url": "https://sign.papersign.com/sign/671ba9030af55bff3b7a8c00/completed-document?expires=1737659429&signature=671ba9030af55bff3b7a8c00671ba9030af55bff3b7a8c00" + }, + "created_at": "2024-10-25T19:10:29+00:00" +} \ No newline at end of file diff --git a/components/papersign/sources/new-event-instant/new-event-instant.mjs b/components/papersign/sources/new-event-instant/new-event-instant.mjs new file mode 100644 index 0000000000000..229f94dbabcf7 --- /dev/null +++ b/components/papersign/sources/new-event-instant/new-event-instant.mjs @@ -0,0 +1,33 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "papersign-new-event-instant", + name: "New Event in Papersign (Instant)", + description: "Emit new event when any document or signer action occurs.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getTriggers() { + return [ + "document.sent", + "document.completed", + "document.cancelled", + "document.rejected", + "document.expired", + "signer.notified", + "signer.viewed", + "signer.consent_accepted", + "signer.nominated", + "signer.signed", + ]; + }, + getSummary({ data }) { + return `New event: ${data.type}`; + }, + }, + sampleEmit, +}; diff --git a/components/papersign/sources/new-event-instant/test-event.mjs b/components/papersign/sources/new-event-instant/test-event.mjs new file mode 100644 index 0000000000000..568316a81e779 --- /dev/null +++ b/components/papersign/sources/new-event-instant/test-event.mjs @@ -0,0 +1,10 @@ +export default { + "id": "671ba9030af55bff3b7a8c00", + "document_id": "671ba9030af55bff3b7a8c00", + "document_name": "Document Name", + "type": "document.completed", + "data": { + "document_url": "https://sign.papersign.com/sign/671ba9030af55bff3b7a8c00/completed-document?expires=1737659429&signature=671ba9030af55bff3b7a8c00671ba9030af55bff3b7a8c00" + }, + "created_at": "2024-10-25T19:10:29+00:00" +} \ No newline at end of file diff --git a/components/papersign/sources/new-signer-signed-instant/new-signer-signed-instant.mjs b/components/papersign/sources/new-signer-signed-instant/new-signer-signed-instant.mjs new file mode 100644 index 0000000000000..16b08fa730384 --- /dev/null +++ b/components/papersign/sources/new-signer-signed-instant/new-signer-signed-instant.mjs @@ -0,0 +1,24 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "papersign-new-signer-signed-instant", + name: "New Signer Signed (Instant)", + description: "Emit new event when a signer signs a document.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getTriggers() { + return [ + "signer.signed", + ]; + }, + getSummary({ data }) { + return `Document signed by signer ${data.by.name}`; + }, + }, + sampleEmit, +}; diff --git a/components/papersign/sources/new-signer-signed-instant/test-event.mjs b/components/papersign/sources/new-signer-signed-instant/test-event.mjs new file mode 100644 index 0000000000000..912a48198cf59 --- /dev/null +++ b/components/papersign/sources/new-signer-signed-instant/test-event.mjs @@ -0,0 +1,7 @@ +export default { + "project": "project_slug", + "translated": 100, + "resource": "resource_slug", + "event": "translation_completed", + "language": "lang_code" +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 095e5916ef9b9..dc84d165cab42 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7090,7 +7090,10 @@ importers: '@pipedream/platform': 3.0.3 components/papersign: - specifiers: {} + specifiers: + '@pipedream/platform': ^3.0.3 + dependencies: + '@pipedream/platform': 3.0.3 components/papertrail: specifiers: {} @@ -13157,6 +13160,55 @@ packages: - aws-crt dev: false + /@aws-sdk/client-sso-oidc/3.600.0_tdq3komn4zwyd65w7klbptsu34: + resolution: {integrity: sha512-7+I8RWURGfzvChyNQSyj5/tKrqRbzRl7H+BnTOf/4Vsw1nFOi5ROhlhD4X/Y0QCTacxnaoNcIrqnY7uGGvVRzw==} + engines: {node: '>=16.0.0'} + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/client-sts': 3.600.0 + '@aws-sdk/core': 3.598.0 + '@aws-sdk/credential-provider-node': 3.600.0_f7n47caigsrjd2lr2szmwfuee4 + '@aws-sdk/middleware-host-header': 3.598.0 + '@aws-sdk/middleware-logger': 3.598.0 + '@aws-sdk/middleware-recursion-detection': 3.598.0 + '@aws-sdk/middleware-user-agent': 3.598.0 + '@aws-sdk/region-config-resolver': 3.598.0 + '@aws-sdk/types': 3.598.0 + '@aws-sdk/util-endpoints': 3.598.0 + '@aws-sdk/util-user-agent-browser': 3.598.0 + '@aws-sdk/util-user-agent-node': 3.598.0 + '@smithy/config-resolver': 3.0.3 + '@smithy/core': 2.2.3 + '@smithy/fetch-http-handler': 3.2.1 + '@smithy/hash-node': 3.0.2 + '@smithy/invalid-dependency': 3.0.2 + '@smithy/middleware-content-length': 3.0.2 + '@smithy/middleware-endpoint': 3.0.4 + '@smithy/middleware-retry': 3.0.6 + '@smithy/middleware-serde': 3.0.3 + '@smithy/middleware-stack': 3.0.3 + '@smithy/node-config-provider': 3.1.3 + '@smithy/node-http-handler': 3.1.2 + '@smithy/protocol-http': 4.0.3 + '@smithy/smithy-client': 3.1.6 + '@smithy/types': 3.3.0 + '@smithy/url-parser': 3.0.3 + '@smithy/util-base64': 3.0.0 + '@smithy/util-body-length-browser': 3.0.0 + '@smithy/util-body-length-node': 3.0.0 + '@smithy/util-defaults-mode-browser': 3.0.6 + '@smithy/util-defaults-mode-node': 3.0.6 + '@smithy/util-endpoints': 2.0.3 + '@smithy/util-middleware': 3.0.3 + '@smithy/util-retry': 3.0.2 + '@smithy/util-utf8': 3.0.0 + tslib: 2.6.3 + transitivePeerDependencies: + - '@aws-sdk/client-sts' + - aws-crt + dev: false + /@aws-sdk/client-sso/3.423.0: resolution: {integrity: sha512-znIufHkwhCIePgaYciIs3x/+BpzR57CZzbCKHR9+oOvGyufEPPpUT5bFLvbwTgfiVkTjuk6sG/ES3U5Bc+xtrA==} engines: {node: '>=14.0.0'} @@ -13392,7 +13444,7 @@ packages: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0 + '@aws-sdk/client-sso-oidc': 3.600.0_tdq3komn4zwyd65w7klbptsu34 '@aws-sdk/core': 3.598.0 '@aws-sdk/credential-provider-node': 3.600.0_f7n47caigsrjd2lr2szmwfuee4 '@aws-sdk/middleware-host-header': 3.598.0 @@ -13434,55 +13486,6 @@ packages: - aws-crt dev: false - /@aws-sdk/client-sts/3.600.0_dseaa2p5u2yk67qiepewcq3hkq: - resolution: {integrity: sha512-KQG97B7LvTtTiGmjlrG1LRAY8wUvCQzrmZVV5bjrJ/1oXAU7DITYwVbSJeX9NWg6hDuSk0VE3MFwIXS2SvfLIA==} - engines: {node: '>=16.0.0'} - dependencies: - '@aws-crypto/sha256-browser': 5.2.0 - '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/client-sso-oidc': 3.600.0 - '@aws-sdk/core': 3.598.0 - '@aws-sdk/credential-provider-node': 3.600.0_f7n47caigsrjd2lr2szmwfuee4 - '@aws-sdk/middleware-host-header': 3.598.0 - '@aws-sdk/middleware-logger': 3.598.0 - '@aws-sdk/middleware-recursion-detection': 3.598.0 - '@aws-sdk/middleware-user-agent': 3.598.0 - '@aws-sdk/region-config-resolver': 3.598.0 - '@aws-sdk/types': 3.598.0 - '@aws-sdk/util-endpoints': 3.598.0 - '@aws-sdk/util-user-agent-browser': 3.598.0 - '@aws-sdk/util-user-agent-node': 3.598.0 - '@smithy/config-resolver': 3.0.3 - '@smithy/core': 2.2.3 - '@smithy/fetch-http-handler': 3.2.1 - '@smithy/hash-node': 3.0.2 - '@smithy/invalid-dependency': 3.0.2 - '@smithy/middleware-content-length': 3.0.2 - '@smithy/middleware-endpoint': 3.0.4 - '@smithy/middleware-retry': 3.0.6 - '@smithy/middleware-serde': 3.0.3 - '@smithy/middleware-stack': 3.0.3 - '@smithy/node-config-provider': 3.1.3 - '@smithy/node-http-handler': 3.1.2 - '@smithy/protocol-http': 4.0.3 - '@smithy/smithy-client': 3.1.6 - '@smithy/types': 3.3.0 - '@smithy/url-parser': 3.0.3 - '@smithy/util-base64': 3.0.0 - '@smithy/util-body-length-browser': 3.0.0 - '@smithy/util-body-length-node': 3.0.0 - '@smithy/util-defaults-mode-browser': 3.0.6 - '@smithy/util-defaults-mode-node': 3.0.6 - '@smithy/util-endpoints': 2.0.3 - '@smithy/util-middleware': 3.0.3 - '@smithy/util-retry': 3.0.2 - '@smithy/util-utf8': 3.0.0 - tslib: 2.6.3 - transitivePeerDependencies: - - '@aws-sdk/client-sso-oidc' - - aws-crt - dev: false - /@aws-sdk/core/3.556.0: resolution: {integrity: sha512-vJaSaHw2kPQlo11j/Rzuz0gk1tEaKdz+2ser0f0qZ5vwFlANjt08m/frU17ctnVKC1s58bxpctO/1P894fHLrA==} engines: {node: '>=14.0.0'} @@ -17775,7 +17778,7 @@ packages: '@aws-sdk/client-sns': 3.423.0 '@aws-sdk/client-sqs': 3.423.0 '@aws-sdk/client-ssm': 3.423.0 - '@aws-sdk/client-sts': 3.600.0_dseaa2p5u2yk67qiepewcq3hkq + '@aws-sdk/client-sts': 3.600.0 '@aws-sdk/s3-request-presigner': 3.609.0 '@pipedream/helper_functions': 0.3.12 '@pipedream/platform': 1.6.6