From a8feaa833a84261e06dac581163a37196f326587 Mon Sep 17 00:00:00 2001 From: Ricardo Fernandes Date: Thu, 3 Oct 2024 15:08:42 -0300 Subject: [PATCH] feat: added component for docnify --- .../add-recipient-to-document.mjs | 42 +++++++ .../create-document-from-template.mjs | 54 +++++++++ .../actions/send-document/send-document.mjs | 29 +++++ components/docnify/docnify.app.mjs | 110 ++++++++++++++++++ components/docnify/package.json | 18 +++ components/docnify/sources/common/base.mjs | 63 ++++++++++ .../new-document-completed.mjs | 29 +++++ .../new-document-completed/test-event.mjs | 12 ++ .../new-document-created.mjs | 26 +++++ .../new-document-created/test-event.mjs | 12 ++ .../new-document-signed.mjs | 39 +++++++ .../new-document-signed/test-event.mjs | 12 ++ pnpm-lock.yaml | 6 + 13 files changed, 452 insertions(+) create mode 100644 components/docnify/actions/add-recipient-to-document/add-recipient-to-document.mjs create mode 100644 components/docnify/actions/create-document-from-template/create-document-from-template.mjs create mode 100644 components/docnify/actions/send-document/send-document.mjs create mode 100644 components/docnify/docnify.app.mjs create mode 100644 components/docnify/package.json create mode 100644 components/docnify/sources/common/base.mjs create mode 100644 components/docnify/sources/new-document-completed/new-document-completed.mjs create mode 100644 components/docnify/sources/new-document-completed/test-event.mjs create mode 100644 components/docnify/sources/new-document-created/new-document-created.mjs create mode 100644 components/docnify/sources/new-document-created/test-event.mjs create mode 100644 components/docnify/sources/new-document-signed/new-document-signed.mjs create mode 100644 components/docnify/sources/new-document-signed/test-event.mjs diff --git a/components/docnify/actions/add-recipient-to-document/add-recipient-to-document.mjs b/components/docnify/actions/add-recipient-to-document/add-recipient-to-document.mjs new file mode 100644 index 0000000000000..f472fb6b632a1 --- /dev/null +++ b/components/docnify/actions/add-recipient-to-document/add-recipient-to-document.mjs @@ -0,0 +1,42 @@ +import docnify from "../../docnify.app.mjs"; + +export default { + key: "docnify-add-recipient-to-document", + name: "Add Recipient To Document", + description: "Add a recipient to an existing Docnify document. [See the documentation]([See the documentation](https://app.docnify.io/api/v1/openapi))", + version: "0.0.1", + type: "action", + props: { + docnify, + documentId: { + propDefinition: [ + docnify, + "documentId", + ], + }, + name: { + type: "string", + label: "Name", + description: "Name of the recipient", + }, + email: { + type: "string", + label: "Email", + description: "Email address of the recipient", + }, + }, + async run({ $ }) { + const response = await this.docnify.addRecipientToDocument({ + $, + documentId: this.documentId, + data: { + name: this.name, + email: this.email, + role: "SIGNER", + }, + }); + + $.export("$summary", `Successfully added recipient to document ${this.documentId}`); + return response; + }, +}; diff --git a/components/docnify/actions/create-document-from-template/create-document-from-template.mjs b/components/docnify/actions/create-document-from-template/create-document-from-template.mjs new file mode 100644 index 0000000000000..d8d9e16a32a9b --- /dev/null +++ b/components/docnify/actions/create-document-from-template/create-document-from-template.mjs @@ -0,0 +1,54 @@ +import docnify from "../../docnify.app.mjs"; + +export default { + key: "docnify-create-document-from-template", + name: "Create Document From Template", + description: "Create a new document in Docnify from a pre-existing template. [See the documentation](https://app.docnify.io/api/v1/openapi)", + version: "0.0.1", + type: "action", + props: { + docnify, + templateId: { + propDefinition: [ + docnify, + "templateId", + ], + }, + title: { + type: "string", + label: "Title", + description: "Document title. Will override the original title defined in the template.", + optional: true, + }, + subject: { + type: "string", + label: "Subject", + description: "Document subject. Will override the original subject defined in the template.", + optional: true, + }, + message: { + type: "string", + label: "Message", + description: "Document message. Will override the original message defined in the template.", + optional: true, + }, + }, + async run({ $ }) { + const response = await this.docnify.createDocumentFromTemplate({ + $, + templateId: this.templateId, + data: { + title: this.title, + recipients: [], + meta: this.subject || this.message + ? { + subject: this.subject, + message: this.message, + } + : undefined, + }, + }); + $.export("$summary", `Successfully created document with ID: ${response.documentId}`); + return response; + }, +}; diff --git a/components/docnify/actions/send-document/send-document.mjs b/components/docnify/actions/send-document/send-document.mjs new file mode 100644 index 0000000000000..61f899d275355 --- /dev/null +++ b/components/docnify/actions/send-document/send-document.mjs @@ -0,0 +1,29 @@ +import docnify from "../../docnify.app.mjs"; + +export default { + key: "docnify-send-document", + name: "Send Document", + description: "Send a document within Docnify for signing. [See the documentation](https://app.docnify.io/api/v1/openapi)", + version: "0.0.1", + type: "action", + props: { + docnify, + documentId: { + propDefinition: [ + docnify, + "documentId", + ], + }, + }, + async run({ $ }) { + const response = await this.docnify.sendDocumentForSigning({ + $, + documentId: this.documentId, + data: { + sendEmail: true, + }, + }); + $.export("$summary", `Document with ID ${this.documentId} sent successfully.`); + return response; + }, +}; diff --git a/components/docnify/docnify.app.mjs b/components/docnify/docnify.app.mjs new file mode 100644 index 0000000000000..e3e9546f24592 --- /dev/null +++ b/components/docnify/docnify.app.mjs @@ -0,0 +1,110 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "docnify", + propDefinitions: { + templateId: { + type: "string", + label: "Template ID", + description: "The ID of the pre-existing template", + async options({ page }) { + const { templates } = await this.listTemplates({ + params: { + page: page + 1, + }, + }); + return templates?.map(({ + id: value, title: label, + }) => ({ + value, + label, + })) || []; + }, + }, + documentId: { + type: "string", + label: "Document ID", + description: "The ID of the document", + async options({ page }) { + const { documents } = await this.listDocuments({ + params: { + page: page + 1, + }, + }); + return documents?.map(({ + id: value, title: label, + }) => ({ + value, + label, + })) || []; + }, + }, + }, + methods: { + _baseUrl() { + return `${this.$auth.url}/api/v1`; + }, + _makeRequest(opts = {}) { + const { + $ = this, + path, + ...otherOpts + } = opts; + return axios($, { + ...otherOpts, + url: `${this._baseUrl()}${path}`, + headers: { + Authorization: `${this.$auth.api_token}`, + }, + }); + }, + listTemplates(opts = {}) { + return this._makeRequest({ + path: "/templates", + ...opts, + }); + }, + listDocuments(opts = {}) { + return this._makeRequest({ + path: "/documents", + ...opts, + }); + }, + getDocument({ + documentId, ...opts + }) { + return this._makeRequest({ + path: `/documents/${documentId}`, + ...opts, + }); + }, + createDocumentFromTemplate({ + templateId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/templates/${templateId}/generate-document`, + ...opts, + }); + }, + sendDocumentForSigning({ + documentId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/documents/${documentId}/send`, + ...opts, + }); + }, + addRecipientToDocument({ + documentId, ...opts + }) { + return this._makeRequest({ + method: "POST", + path: `/documents/${documentId}/recipients`, + ...opts, + }); + }, + }, +}; diff --git a/components/docnify/package.json b/components/docnify/package.json new file mode 100644 index 0000000000000..4bc137a96cb13 --- /dev/null +++ b/components/docnify/package.json @@ -0,0 +1,18 @@ +{ + "name": "@pipedream/docnify", + "version": "0.1.0", + "description": "Pipedream Docnify Components", + "main": "docnify.app.mjs", + "keywords": [ + "pipedream", + "docnify" + ], + "homepage": "https://pipedream.com/apps/docnify", + "author": "Pipedream (https://pipedream.com/)", + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.1" + } +} diff --git a/components/docnify/sources/common/base.mjs b/components/docnify/sources/common/base.mjs new file mode 100644 index 0000000000000..be7a1abbefa61 --- /dev/null +++ b/components/docnify/sources/common/base.mjs @@ -0,0 +1,63 @@ +import docnify from "../../docnify.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + props: { + docnify, + db: "$.service.db", + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(lastTs) { + this.db.set("lastTs", lastTs); + }, + isRelevant() { + return true; + }, + generateMeta() { + throw new Error("generateMeta is not implemented"); + }, + }, + async run() { + const lastTs = this._getLastTs(); + let maxTs = lastTs; + const tsField = this.getTsField(); + + const docs = []; + let total; + const params = { + page: 1, + }; + do { + const { documents } = await this.docnify.listDocuments({ + params, + }); + for (const doc of documents) { + const ts = Date.parse(doc[tsField]); + if (ts >= lastTs) { + if (await this.isRelevant(doc, lastTs)) { + docs.push(doc); + } + maxTs = Math.max(ts, maxTs); + } + } + total = documents?.length; + params.page++; + } while (total > 0); + + for (const doc of docs) { + const meta = await this.generateMeta(doc); + this.$emit(doc, meta); + } + + this._setLastTs(maxTs); + }, +}; diff --git a/components/docnify/sources/new-document-completed/new-document-completed.mjs b/components/docnify/sources/new-document-completed/new-document-completed.mjs new file mode 100644 index 0000000000000..203e833f06f1d --- /dev/null +++ b/components/docnify/sources/new-document-completed/new-document-completed.mjs @@ -0,0 +1,29 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "docnify-new-document-completed", + name: "New Document Completed", + description: "Emit new event when a document is signed by all recipients.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getTsField() { + return "updatedAt"; + }, + isRelevant(doc) { + return doc.status === "COMPLETED"; + }, + generateMeta(doc) { + return { + id: doc.id, + summary: `New Document Completed: ${doc.id}`, + ts: Date.parse(doc[this.getTsField()]), + }; + }, + }, + sampleEmit, +}; diff --git a/components/docnify/sources/new-document-completed/test-event.mjs b/components/docnify/sources/new-document-completed/test-event.mjs new file mode 100644 index 0000000000000..887d4c07c0ca0 --- /dev/null +++ b/components/docnify/sources/new-document-completed/test-event.mjs @@ -0,0 +1,12 @@ +export default { + "id": 21983, + "externalId": null, + "userId": 6780, + "teamId": null, + "title": "file-sample_150kB.pdf", + "status": "COMPLETED", + "documentDataId": "cm0eaghs2002nl70cza7g41z1", + "createdAt": "2024-08-28T20:08:22.289Z", + "updatedAt": "2024-08-28T20:24:03.342Z", + "completedAt": "2024-08-28T20:24:03.342Z" +} \ No newline at end of file diff --git a/components/docnify/sources/new-document-created/new-document-created.mjs b/components/docnify/sources/new-document-created/new-document-created.mjs new file mode 100644 index 0000000000000..d47a60833c6d9 --- /dev/null +++ b/components/docnify/sources/new-document-created/new-document-created.mjs @@ -0,0 +1,26 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "docnify-new-document-created", + name: "New Document Created", + description: "Emit new event when a new document is created.", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getTsField() { + return "createdAt"; + }, + generateMeta(doc) { + return { + id: doc.id, + summary: `New Document Created: ${doc.id}`, + ts: Date.parse(doc[this.getTsField()]), + }; + }, + }, + sampleEmit, +}; diff --git a/components/docnify/sources/new-document-created/test-event.mjs b/components/docnify/sources/new-document-created/test-event.mjs new file mode 100644 index 0000000000000..db4b18198f30b --- /dev/null +++ b/components/docnify/sources/new-document-created/test-event.mjs @@ -0,0 +1,12 @@ +export default { + "id": 21983, + "externalId": null, + "userId": 6780, + "teamId": null, + "title": "file-sample_150kB.pdf", + "status": "PENDING", + "documentDataId": "cm0eaghs2002nl70cza7g41z1", + "createdAt": "2024-08-28T20:08:22.289Z", + "updatedAt": "2024-08-28T20:24:03.342Z", + "completedAt": null +} \ No newline at end of file diff --git a/components/docnify/sources/new-document-signed/new-document-signed.mjs b/components/docnify/sources/new-document-signed/new-document-signed.mjs new file mode 100644 index 0000000000000..f1e49a145df28 --- /dev/null +++ b/components/docnify/sources/new-document-signed/new-document-signed.mjs @@ -0,0 +1,39 @@ +import common from "../common/base.mjs"; +import sampleEmit from "./test-event.mjs"; + +export default { + ...common, + key: "docnify-new-document-signed", + name: "New Document Signed", + description: "Emit new event when a document is signed by a recipient", + version: "0.0.1", + type: "source", + dedupe: "unique", + methods: { + ...common.methods, + getTsField() { + return "updatedAt"; + }, + async isRelevant(doc, lastTs) { + const { recipients } = await this.docnify.getDocument({ + documentId: doc.id, + }); + const recentlySigned = recipients + ?.filter(({ signedAt }) => signedAt && Date.parse(signedAt) >= lastTs); + return !!recentlySigned?.length; + }, + async generateMeta(doc) { + const { recipients } = await this.docnify.getDocument({ + documentId: doc.id, + }); + const signedAts = recipients?.map(({ signedAt }) => Date.parse(signedAt)); + const ts = Math.max(...signedAts); + return { + id: `${doc.id}-${ts}`, + summary: `New Document Signed: ${doc.id}`, + ts, + }; + }, + }, + sampleEmit, +}; diff --git a/components/docnify/sources/new-document-signed/test-event.mjs b/components/docnify/sources/new-document-signed/test-event.mjs new file mode 100644 index 0000000000000..db4b18198f30b --- /dev/null +++ b/components/docnify/sources/new-document-signed/test-event.mjs @@ -0,0 +1,12 @@ +export default { + "id": 21983, + "externalId": null, + "userId": 6780, + "teamId": null, + "title": "file-sample_150kB.pdf", + "status": "PENDING", + "documentDataId": "cm0eaghs2002nl70cza7g41z1", + "createdAt": "2024-08-28T20:08:22.289Z", + "updatedAt": "2024-08-28T20:24:03.342Z", + "completedAt": null +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70f43a35378d9..3e01988cab5c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2614,6 +2614,12 @@ importers: dependencies: '@pipedream/platform': 1.6.2 + components/docnify: + specifiers: + '@pipedream/platform': ^3.0.1 + dependencies: + '@pipedream/platform': 3.0.3 + components/docraptor: specifiers: {}