From 4b3213761e111bb8a184f5bbae3cd82113a977c0 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Tue, 15 Oct 2024 15:01:42 -0400 Subject: [PATCH 1/3] init --- components/paperform/.gitignore | 3 - components/paperform/app/paperform.app.ts | 13 ---- components/paperform/paperform.app.mjs | 55 ++++++++++++++++ .../new-submission-instant.mjs | 65 +++++++++++++++++++ 4 files changed, 120 insertions(+), 16 deletions(-) delete mode 100644 components/paperform/.gitignore delete mode 100644 components/paperform/app/paperform.app.ts create mode 100644 components/paperform/paperform.app.mjs create mode 100644 components/paperform/sources/new-submission-instant/new-submission-instant.mjs diff --git a/components/paperform/.gitignore b/components/paperform/.gitignore deleted file mode 100644 index ec761ccab7595..0000000000000 --- a/components/paperform/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.js -*.mjs -dist \ No newline at end of file diff --git a/components/paperform/app/paperform.app.ts b/components/paperform/app/paperform.app.ts deleted file mode 100644 index 6afce6d351aff..0000000000000 --- a/components/paperform/app/paperform.app.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { defineApp } from "@pipedream/types"; - -export default defineApp({ - type: "app", - app: "paperform", - propDefinitions: {}, - methods: { - // this.$auth contains connected account data - authKeys() { - console.log(Object.keys(this.$auth)); - }, - }, -}); diff --git a/components/paperform/paperform.app.mjs b/components/paperform/paperform.app.mjs new file mode 100644 index 0000000000000..a26beddb5deda --- /dev/null +++ b/components/paperform/paperform.app.mjs @@ -0,0 +1,55 @@ +import { axios } from "@pipedream/platform"; + +export default { + type: "app", + app: "paperform", + propDefinitions: { + formId: { + type: "string", + label: "Form ID", + description: "The ID of the form to monitor", + async options() { + const { results } = await this.listForms(); + return results.map((form) => ({ + label: form.name, + value: form.id, + })); + }, + }, + }, + methods: { + _baseUrl() { + return "https://api.paperform.co/v1"; + }, + async _makeRequest(opts = {}) { + const { + $ = this, + method = "GET", + path, + headers, + ...otherOpts + } = opts; + return axios($, { + ...otherOpts, + method, + url: this._baseUrl() + path, + headers: { + ...headers, + Authorization: `Bearer ${this.$auth.api_token}`, + }, + }); + }, + async listForms(opts = {}) { + return this._makeRequest({ + path: "/forms", + ...opts, + }); + }, + async getFormSubmissions(formId, opts = {}) { + return this._makeRequest({ + path: `/forms/${formId}/submissions`, + ...opts, + }); + }, + }, +}; diff --git a/components/paperform/sources/new-submission-instant/new-submission-instant.mjs b/components/paperform/sources/new-submission-instant/new-submission-instant.mjs new file mode 100644 index 0000000000000..30963d35444c7 --- /dev/null +++ b/components/paperform/sources/new-submission-instant/new-submission-instant.mjs @@ -0,0 +1,65 @@ +import paperform from "../../paperform.app.mjs"; +import crypto from "crypto"; + +export default { + key: "paperform-new-submission-instant", + name: "New Submission Instant", + description: "Emits an event when a new submission is made on a specific form in Paperform", + version: "0.0.{{ts}}", + type: "source", + dedupe: "unique", + props: { + paperform, + http: { + type: "$.interface.http", + customResponse: true, + }, + db: "$.service.db", + formId: { + propDefinition: [ + paperform, + "formId", + ], + }, + }, + hooks: { + async activate() { + const webhookId = await this.paperform.createWebhook({ + formId: this.formId, + endpointUrl: this.http.endpoint, + }); + this.db.set("webhookId", webhookId); + }, + async deactivate() { + await this.paperform.deleteWebhook(this.db.get("webhookId")); + }, + }, + async run(event) { + const { + body, headers, + } = event; + if (headers["x-paperform-hmac-sha256"]) { + const hmac = crypto.createHmac("sha256", this.paperform.$auth.api_key); + const digest = hmac.update(JSON.stringify(body)).digest("hex"); + + if (digest === headers["x-paperform-hmac-sha256"]) { + this.http.respond({ + status: 200, + }); + this.$emit(body, { + id: body.id, + summary: `New submission for form ${body.form_id}`, + ts: Date.parse(body.created), + }); + } else { + this.http.respond({ + status: 401, + }); + } + } else { + this.http.respond({ + status: 404, + }); + } + }, +}; From a252e0d15aeceb1498c44d97467085c228cdaba1 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Tue, 15 Oct 2024 16:03:31 -0400 Subject: [PATCH 2/3] new component --- components/paperform/package.json | 10 +-- components/paperform/paperform.app.mjs | 63 ++++++++++---- .../new-submission-instant.mjs | 65 --------------- .../sources/new-submission/new-submission.mjs | 83 +++++++++++++++++++ 4 files changed, 136 insertions(+), 85 deletions(-) delete mode 100644 components/paperform/sources/new-submission-instant/new-submission-instant.mjs create mode 100644 components/paperform/sources/new-submission/new-submission.mjs diff --git a/components/paperform/package.json b/components/paperform/package.json index a37b1da222c9f..25d4783c2b434 100644 --- a/components/paperform/package.json +++ b/components/paperform/package.json @@ -1,18 +1,18 @@ { "name": "@pipedream/paperform", - "version": "0.0.3", + "version": "0.1.0", "description": "Pipedream PaperForm Components", - "main": "dist/app/paperform.app.mjs", + "main": "paperform.app.mjs", "keywords": [ "pipedream", "paperform" ], - "files": [ - "dist" - ], "homepage": "https://pipedream.com/apps/paperform", "author": "Pipedream (https://pipedream.com/)", "publishConfig": { "access": "public" + }, + "dependencies": { + "@pipedream/platform": "^3.0.3" } } diff --git a/components/paperform/paperform.app.mjs b/components/paperform/paperform.app.mjs index a26beddb5deda..fe4ba95739c90 100644 --- a/components/paperform/paperform.app.mjs +++ b/components/paperform/paperform.app.mjs @@ -1,4 +1,5 @@ import { axios } from "@pipedream/platform"; +const DEFAULT_LIMIT = 100; export default { type: "app", @@ -8,12 +9,19 @@ export default { type: "string", label: "Form ID", description: "The ID of the form to monitor", - async options() { - const { results } = await this.listForms(); - return results.map((form) => ({ - label: form.name, - value: form.id, - })); + async options({ page }) { + const { results: { forms } } = await this.listForms({ + params: { + limit: DEFAULT_LIMIT, + skip: page * DEFAULT_LIMIT, + }, + }); + return forms?.map(({ + id: value, title: label, + }) => ({ + label, + value, + })) || []; }, }, }, @@ -21,35 +29,60 @@ export default { _baseUrl() { return "https://api.paperform.co/v1"; }, - async _makeRequest(opts = {}) { + _makeRequest(opts = {}) { const { $ = this, - method = "GET", path, - headers, ...otherOpts } = opts; return axios($, { ...otherOpts, - method, - url: this._baseUrl() + path, + url: `${this._baseUrl()}${path}`, headers: { - ...headers, - Authorization: `Bearer ${this.$auth.api_token}`, + Authorization: `Bearer ${this.$auth.api_key}`, }, }); }, - async listForms(opts = {}) { + listForms(opts = {}) { return this._makeRequest({ path: "/forms", ...opts, }); }, - async getFormSubmissions(formId, opts = {}) { + listSubmissions({ + formId, ...opts + }) { return this._makeRequest({ path: `/forms/${formId}/submissions`, ...opts, }); }, + async *paginate({ + fn, args, resourceKey, max, + }) { + args = { + ...args, + params: { + ...args?.params, + limit: DEFAULT_LIMIT, + skip: 0, + }, + }; + let hasMore, count = 0; + do { + const { + results, has_more: more, + } = await fn(args); + const items = results[resourceKey]; + for (const item of items) { + yield item; + if (max && ++count >= max) { + return; + } + } + hasMore = more; + args.params.skip += args.params.limit; + } while (hasMore); + }, }, }; diff --git a/components/paperform/sources/new-submission-instant/new-submission-instant.mjs b/components/paperform/sources/new-submission-instant/new-submission-instant.mjs deleted file mode 100644 index 30963d35444c7..0000000000000 --- a/components/paperform/sources/new-submission-instant/new-submission-instant.mjs +++ /dev/null @@ -1,65 +0,0 @@ -import paperform from "../../paperform.app.mjs"; -import crypto from "crypto"; - -export default { - key: "paperform-new-submission-instant", - name: "New Submission Instant", - description: "Emits an event when a new submission is made on a specific form in Paperform", - version: "0.0.{{ts}}", - type: "source", - dedupe: "unique", - props: { - paperform, - http: { - type: "$.interface.http", - customResponse: true, - }, - db: "$.service.db", - formId: { - propDefinition: [ - paperform, - "formId", - ], - }, - }, - hooks: { - async activate() { - const webhookId = await this.paperform.createWebhook({ - formId: this.formId, - endpointUrl: this.http.endpoint, - }); - this.db.set("webhookId", webhookId); - }, - async deactivate() { - await this.paperform.deleteWebhook(this.db.get("webhookId")); - }, - }, - async run(event) { - const { - body, headers, - } = event; - if (headers["x-paperform-hmac-sha256"]) { - const hmac = crypto.createHmac("sha256", this.paperform.$auth.api_key); - const digest = hmac.update(JSON.stringify(body)).digest("hex"); - - if (digest === headers["x-paperform-hmac-sha256"]) { - this.http.respond({ - status: 200, - }); - this.$emit(body, { - id: body.id, - summary: `New submission for form ${body.form_id}`, - ts: Date.parse(body.created), - }); - } else { - this.http.respond({ - status: 401, - }); - } - } else { - this.http.respond({ - status: 404, - }); - } - }, -}; diff --git a/components/paperform/sources/new-submission/new-submission.mjs b/components/paperform/sources/new-submission/new-submission.mjs new file mode 100644 index 0000000000000..2ce46137114c6 --- /dev/null +++ b/components/paperform/sources/new-submission/new-submission.mjs @@ -0,0 +1,83 @@ +import paperform from "../../paperform.app.mjs"; +import { DEFAULT_POLLING_SOURCE_TIMER_INTERVAL } from "@pipedream/platform"; + +export default { + key: "paperform-new-submission", + name: "New Submission", + description: "Emit new event when a new submission is made on the specified form in Paperform. [See the documentation](https://paperform.readme.io/reference/listformsubmissions)", + version: "0.0.1", + type: "source", + dedupe: "unique", + props: { + paperform, + timer: { + type: "$.interface.timer", + default: { + intervalSeconds: DEFAULT_POLLING_SOURCE_TIMER_INTERVAL, + }, + }, + db: "$.service.db", + formId: { + propDefinition: [ + paperform, + "formId", + ], + }, + }, + hooks: { + async deploy() { + await this.processEvent(25); + }, + }, + methods: { + _getLastTs() { + return this.db.get("lastTs") || 0; + }, + _setLastTs(lastTs) { + this.db.set("lastTs", lastTs); + }, + generateMeta(submission) { + return { + id: submission.id, + summary: `New Submission ID: ${submission.id}`, + ts: Date.parse(submission.created_at), + }; + }, + async processEvent(max) { + const lastTs = this._getLastTs(); + + const items = this.paperform.paginate({ + fn: this.paperform.listSubmissions, + args: { + formId: this.formId, + }, + resourceKey: "submissions", + max, + }); + + const submissions = []; + for await (const item of items) { + const ts = Date.parse(item.created_at); + if (ts >= lastTs) { + submissions.push(item); + } else { + break; + } + } + + if (!submissions?.length) { + return; + } + + this._setLastTs(Date.parse(submissions[0].created_at)); + + submissions.forEach((submission) => { + const meta = this.generateMeta(submission); + this.$emit(submission, meta); + }); + }, + }, + async run() { + await this.processEvent(); + }, +}; From 62dd48650115aa1e1554fcea4f5baa9da50015b9 Mon Sep 17 00:00:00 2001 From: michelle0927 Date: Tue, 15 Oct 2024 16:05:10 -0400 Subject: [PATCH 3/3] pnpm-lock.yaml --- pnpm-lock.yaml | 107 +++++++++++++++++++++++++------------------------ 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c94e1a6923f80..4ae32e06aace6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7025,7 +7025,10 @@ importers: form-data: 4.0.0 components/paperform: - specifiers: {} + specifiers: + '@pipedream/platform': ^3.0.3 + dependencies: + '@pipedream/platform': 3.0.3 components/papertrail: specifiers: {} @@ -13039,6 +13042,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'} @@ -13274,7 +13326,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 @@ -13316,55 +13368,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'} @@ -17657,7 +17660,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