From 42f8e635ed20990a741144a3a9db93afe9bdb020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Fri, 2 Feb 2024 16:27:43 +0300 Subject: [PATCH 1/7] feat(jotform): add jotform integration --- .../src/apps/jotform/assets/favicon.svg | 1 + .../backend/src/apps/jotform/auth/index.js | 32 +++++++++++++++++++ .../apps/jotform/auth/is-still-verified.js | 8 +++++ .../apps/jotform/auth/verify-credentials.js | 14 ++++++++ .../apps/jotform/common/add-auth-header.js | 9 ++++++ .../apps/jotform/common/get-current-user.js | 7 ++++ .../src/apps/jotform/common/set-base-url.js | 11 +++++++ packages/backend/src/apps/jotform/index.js | 17 ++++++++++ packages/docs/pages/.vitepress/config.js | 6 ++++ .../docs/pages/apps/jotform/connection.md | 12 +++++++ .../docs/pages/public/favicons/jotform.svg | 1 + 11 files changed, 118 insertions(+) create mode 100644 packages/backend/src/apps/jotform/assets/favicon.svg create mode 100644 packages/backend/src/apps/jotform/auth/index.js create mode 100644 packages/backend/src/apps/jotform/auth/is-still-verified.js create mode 100644 packages/backend/src/apps/jotform/auth/verify-credentials.js create mode 100644 packages/backend/src/apps/jotform/common/add-auth-header.js create mode 100644 packages/backend/src/apps/jotform/common/get-current-user.js create mode 100644 packages/backend/src/apps/jotform/common/set-base-url.js create mode 100644 packages/backend/src/apps/jotform/index.js create mode 100644 packages/docs/pages/apps/jotform/connection.md create mode 100644 packages/docs/pages/public/favicons/jotform.svg diff --git a/packages/backend/src/apps/jotform/assets/favicon.svg b/packages/backend/src/apps/jotform/assets/favicon.svg new file mode 100644 index 00000000..99500044 --- /dev/null +++ b/packages/backend/src/apps/jotform/assets/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/backend/src/apps/jotform/auth/index.js b/packages/backend/src/apps/jotform/auth/index.js new file mode 100644 index 00000000..baf817e1 --- /dev/null +++ b/packages/backend/src/apps/jotform/auth/index.js @@ -0,0 +1,32 @@ +import verifyCredentials from './verify-credentials.js'; +import isStillVerified from './is-still-verified.js'; + +export default { + fields: [ + { + key: 'instanceUrl', + label: 'Jotform instance URL', + type: 'string', + required: false, + readOnly: false, + value: null, + placeholder: 'https://${subdomain}.jotform.com', + description: 'If you have an enterprise plan, you can use your api url.', + clickToCopy: true, + }, + { + key: 'apiKey', + label: 'API Key', + type: 'string', + required: true, + readOnly: false, + value: null, + placeholder: null, + description: 'Jotform API key of your account.', + clickToCopy: false, + }, + ], + + verifyCredentials, + isStillVerified, +}; diff --git a/packages/backend/src/apps/jotform/auth/is-still-verified.js b/packages/backend/src/apps/jotform/auth/is-still-verified.js new file mode 100644 index 00000000..39867547 --- /dev/null +++ b/packages/backend/src/apps/jotform/auth/is-still-verified.js @@ -0,0 +1,8 @@ +import getCurrentUser from '../common/get-current-user.js'; + +const isStillVerified = async ($) => { + const user = await getCurrentUser($); + return !!user.username; +}; + +export default isStillVerified; diff --git a/packages/backend/src/apps/jotform/auth/verify-credentials.js b/packages/backend/src/apps/jotform/auth/verify-credentials.js new file mode 100644 index 00000000..c99fae89 --- /dev/null +++ b/packages/backend/src/apps/jotform/auth/verify-credentials.js @@ -0,0 +1,14 @@ +import getCurrentUser from '../common/get-current-user.js'; + +const verifyCredentials = async ($) => { + const user = await getCurrentUser($); + + const screenName = [user.username, user.email].filter(Boolean).join(' @ '); + + await $.auth.set({ + screenName, + apiKey: $.auth.data.apiKey, + }); +}; + +export default verifyCredentials; diff --git a/packages/backend/src/apps/jotform/common/add-auth-header.js b/packages/backend/src/apps/jotform/common/add-auth-header.js new file mode 100644 index 00000000..4d6296fb --- /dev/null +++ b/packages/backend/src/apps/jotform/common/add-auth-header.js @@ -0,0 +1,9 @@ +const addAuthHeader = ($, requestConfig) => { + if ($.auth.data?.apiKey) { + requestConfig.headers['APIKEY'] = `${$.auth.data.apiKey}`; + } + + return requestConfig; +}; + +export default addAuthHeader; diff --git a/packages/backend/src/apps/jotform/common/get-current-user.js b/packages/backend/src/apps/jotform/common/get-current-user.js new file mode 100644 index 00000000..e0cf0186 --- /dev/null +++ b/packages/backend/src/apps/jotform/common/get-current-user.js @@ -0,0 +1,7 @@ +const getCurrentUser = async ($) => { + const response = await $.http.get('/user'); + const currentUser = response.data.content; + return currentUser; +}; + +export default getCurrentUser; diff --git a/packages/backend/src/apps/jotform/common/set-base-url.js b/packages/backend/src/apps/jotform/common/set-base-url.js new file mode 100644 index 00000000..135149b1 --- /dev/null +++ b/packages/backend/src/apps/jotform/common/set-base-url.js @@ -0,0 +1,11 @@ +const setBaseUrl = ($, requestConfig) => { + if ($.auth.data.instanceUrl) { + requestConfig.baseURL = $.auth.data.instanceUrl; + } else if ($.app.apiBaseUrl) { + requestConfig.baseURL = $.app.apiBaseUrl; + } + + return requestConfig; +}; + +export default setBaseUrl; diff --git a/packages/backend/src/apps/jotform/index.js b/packages/backend/src/apps/jotform/index.js new file mode 100644 index 00000000..697a024b --- /dev/null +++ b/packages/backend/src/apps/jotform/index.js @@ -0,0 +1,17 @@ +import defineApp from '../../helpers/define-app.js'; +import addAuthHeader from './common/add-auth-header.js'; +import auth from './auth/index.js'; +import setBaseUrl from './common/set-base-url.js'; + +export default defineApp({ + name: 'Jotform', + key: 'jotform', + iconUrl: '{BASE_URL}/apps/jotform/assets/favicon.svg', + authDocUrl: 'https://automatisch.io/docs/apps/jotform/connection', + supportsConnections: true, + baseUrl: 'https://www.jotform.com', + apiBaseUrl: 'https://api.jotform.com', + primaryColor: 'FF6100', + beforeRequest: [setBaseUrl, addAuthHeader], + auth, +}); diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js index 0912d90b..c83c75e6 100644 --- a/packages/docs/pages/.vitepress/config.js +++ b/packages/docs/pages/.vitepress/config.js @@ -252,6 +252,12 @@ export default defineConfig({ { text: 'Connection', link: '/apps/invoice-ninja/connection' }, ], }, + { + text: 'Jotform', + collapsible: true, + collapsed: true, + items: [{ text: 'Connection', link: '/apps/jotform/connection' }], + }, { text: 'Mailchimp', collapsible: true, diff --git a/packages/docs/pages/apps/jotform/connection.md b/packages/docs/pages/apps/jotform/connection.md new file mode 100644 index 00000000..0b49f8f9 --- /dev/null +++ b/packages/docs/pages/apps/jotform/connection.md @@ -0,0 +1,12 @@ +# Jotform + +:::info +This page explains the steps you need to follow to set up the Jotform +connection in Automatisch. If any of the steps are outdated, please let us know! +::: + +1. Login to your Jotform account: [https://www.jotform.com/](https://www.jotform.com/). +2. Click on your account image and go to **Settings**. +3. Click on the **API** tab on the left. +4. Copy **API key** from the page to the `API Key` field on Automatisch. +5. Now, you can start using the Jotform connection with Automatisch. diff --git a/packages/docs/pages/public/favicons/jotform.svg b/packages/docs/pages/public/favicons/jotform.svg new file mode 100644 index 00000000..99500044 --- /dev/null +++ b/packages/docs/pages/public/favicons/jotform.svg @@ -0,0 +1 @@ + \ No newline at end of file From 530a9205178336910489addc9ace67c35de6e509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Mon, 5 Feb 2024 14:50:48 +0300 Subject: [PATCH 2/7] feat(jotform): add new submissions trigger --- .../src/apps/jotform/dynamic-data/index.js | 3 + .../jotform/dynamic-data/list-forms/index.js | 41 +++++++ packages/backend/src/apps/jotform/index.js | 4 + .../src/apps/jotform/triggers/index.js | 3 + .../jotform/triggers/new-submissions/index.js | 109 ++++++++++++++++++ packages/docs/pages/.vitepress/config.js | 5 +- packages/docs/pages/apps/jotform/triggers.md | 12 ++ packages/docs/pages/guide/available-apps.md | 1 + 8 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 packages/backend/src/apps/jotform/dynamic-data/index.js create mode 100644 packages/backend/src/apps/jotform/dynamic-data/list-forms/index.js create mode 100644 packages/backend/src/apps/jotform/triggers/index.js create mode 100644 packages/backend/src/apps/jotform/triggers/new-submissions/index.js create mode 100644 packages/docs/pages/apps/jotform/triggers.md diff --git a/packages/backend/src/apps/jotform/dynamic-data/index.js b/packages/backend/src/apps/jotform/dynamic-data/index.js new file mode 100644 index 00000000..0a58430e --- /dev/null +++ b/packages/backend/src/apps/jotform/dynamic-data/index.js @@ -0,0 +1,3 @@ +import listForms from './list-forms/index.js'; + +export default [listForms]; diff --git a/packages/backend/src/apps/jotform/dynamic-data/list-forms/index.js b/packages/backend/src/apps/jotform/dynamic-data/list-forms/index.js new file mode 100644 index 00000000..180425bc --- /dev/null +++ b/packages/backend/src/apps/jotform/dynamic-data/list-forms/index.js @@ -0,0 +1,41 @@ +export default { + name: 'List forms', + key: 'listForms', + + async run($) { + const forms = { + data: [], + }; + let hasMore = false; + + const params = { + limit: 1000, + offset: 0, + orderby: 'created_at', + }; + + do { + const { data } = await $.http.get('/user/forms', { params }); + params.offset = params.offset + params.limit; + + if (data.content?.length) { + for (const form of data.content) { + if (form.status === 'ENABLED') { + forms.data.push({ + value: form.id, + name: form.title, + }); + } + } + } + + if (data.resultSet.count > data.resultSet.limit) { + hasMore = true; + } else { + hasMore = false; + } + } while (hasMore); + + return forms; + }, +}; diff --git a/packages/backend/src/apps/jotform/index.js b/packages/backend/src/apps/jotform/index.js index 697a024b..b4242bdc 100644 --- a/packages/backend/src/apps/jotform/index.js +++ b/packages/backend/src/apps/jotform/index.js @@ -2,6 +2,8 @@ import defineApp from '../../helpers/define-app.js'; import addAuthHeader from './common/add-auth-header.js'; import auth from './auth/index.js'; import setBaseUrl from './common/set-base-url.js'; +import triggers from './triggers/index.js'; +import dynamicData from './dynamic-data/index.js'; export default defineApp({ name: 'Jotform', @@ -14,4 +16,6 @@ export default defineApp({ primaryColor: 'FF6100', beforeRequest: [setBaseUrl, addAuthHeader], auth, + triggers, + dynamicData, }); diff --git a/packages/backend/src/apps/jotform/triggers/index.js b/packages/backend/src/apps/jotform/triggers/index.js new file mode 100644 index 00000000..b25ac2ce --- /dev/null +++ b/packages/backend/src/apps/jotform/triggers/index.js @@ -0,0 +1,3 @@ +import newSubmissions from './new-submissions/index.js'; + +export default [newSubmissions]; diff --git a/packages/backend/src/apps/jotform/triggers/new-submissions/index.js b/packages/backend/src/apps/jotform/triggers/new-submissions/index.js new file mode 100644 index 00000000..e5c99460 --- /dev/null +++ b/packages/backend/src/apps/jotform/triggers/new-submissions/index.js @@ -0,0 +1,109 @@ +import Crypto from 'crypto'; +import { URLSearchParams } from 'url'; +import defineTrigger from '../../../../helpers/define-trigger.js'; + +export default defineTrigger({ + name: 'New submissions', + key: 'newSubmissions', + type: 'webhook', + description: + 'Triggers when a new submission has been added to a specific form.', + arguments: [ + { + label: 'Form', + key: 'formId', + type: 'dropdown', + required: true, + description: '', + variables: true, + source: { + type: 'query', + name: 'getDynamicData', + arguments: [ + { + name: 'key', + value: 'listForms', + }, + ], + }, + }, + ], + + async run($) { + const dataItem = { + raw: $.request.body, + meta: { + internalId: Crypto.randomUUID(), + }, + }; + + $.pushTriggerItem(dataItem); + }, + + async testRun($) { + const sampleEventData = { + ip: '127.0.0.1', + type: 'WEB', + appID: '', + event: '', + action: '', + formID: Crypto.randomUUID(), + parent: '', + pretty: 'Name:test, E-mail:user@automatisch.io', + teamID: '', + unread: '', + product: '', + subject: '', + isSilent: '', + username: 'username', + deviceIDs: 'Array', + formTitle: 'Opt-In Form-Get Free Email Updates!', + fromTable: '', + customBody: '', + documentID: '', + rawRequest: '', + webhookURL: '', + customTitle: '', + trackAction: 'Array', + customParams: '', + submissionID: Crypto.randomUUID(), + customBodyParams: 'Array', + customTitleParams: 'Array', + }; + + const dataItem = { + raw: sampleEventData, + meta: { + internalId: sampleEventData.submissionID, + }, + }; + + $.pushTriggerItem(dataItem); + }, + + async registerHook($) { + const formId = $.step.parameters.formId; + + const params = new URLSearchParams({ + webhookURL: $.webhookUrl, + }); + + const { data } = await $.http.post( + `/form/${formId}/webhooks`, + params.toString() + ); + + await $.flow.setRemoteWebhookId(data.content[0]); + }, + + async unregisterHook($) { + const formId = $.step.parameters.formId; + + const { data } = await $.http.get(`/form/${formId}/webhooks`); + + const webhookURLs = Object.values(data.content); + const webhookId = webhookURLs.findIndex((url) => url === $.webhookUrl); + + await $.http.delete(`/form/${formId}/webhooks/${webhookId}`); + }, +}); diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js index c83c75e6..ad681517 100644 --- a/packages/docs/pages/.vitepress/config.js +++ b/packages/docs/pages/.vitepress/config.js @@ -256,7 +256,10 @@ export default defineConfig({ text: 'Jotform', collapsible: true, collapsed: true, - items: [{ text: 'Connection', link: '/apps/jotform/connection' }], + items: [ + { text: 'Triggers', link: '/apps/jotform/triggers' }, + { text: 'Connection', link: '/apps/jotform/connection' }, + ], }, { text: 'Mailchimp', diff --git a/packages/docs/pages/apps/jotform/triggers.md b/packages/docs/pages/apps/jotform/triggers.md new file mode 100644 index 00000000..febf5f55 --- /dev/null +++ b/packages/docs/pages/apps/jotform/triggers.md @@ -0,0 +1,12 @@ +--- +favicon: /favicons/jotform.svg +items: + - name: New submissions + desc: Triggers when a new submission has been added to a specific form. +--- + + + + diff --git a/packages/docs/pages/guide/available-apps.md b/packages/docs/pages/guide/available-apps.md index e2ace1b9..faebf873 100644 --- a/packages/docs/pages/guide/available-apps.md +++ b/packages/docs/pages/guide/available-apps.md @@ -25,6 +25,7 @@ The following integrations are currently supported by Automatisch. - [HTTP Request](/apps/http-request/actions) - [HubSpot](/apps/hubspot/actions) - [Invoice Ninja](/apps/invoice-ninja/triggers) +- [Jotform](/apps/jotform/triggers) - [Mailchimp](/apps/mailchimp/triggers) - [MailerLite](/apps/mailerlite/triggers) - [Mattermost](/apps/mattermost/actions) From ed2b1029f686d670c23ec4ac9904bce262f3778e Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Wed, 14 Aug 2024 14:12:16 +0000 Subject: [PATCH 3/7] docs(jotform/connection): incorporate API URL field --- packages/docs/pages/apps/jotform/connection.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/docs/pages/apps/jotform/connection.md b/packages/docs/pages/apps/jotform/connection.md index 0b49f8f9..1c1179e7 100644 --- a/packages/docs/pages/apps/jotform/connection.md +++ b/packages/docs/pages/apps/jotform/connection.md @@ -8,5 +8,8 @@ connection in Automatisch. If any of the steps are outdated, please let us know! 1. Login to your Jotform account: [https://www.jotform.com/](https://www.jotform.com/). 2. Click on your account image and go to **Settings**. 3. Click on the **API** tab on the left. -4. Copy **API key** from the page to the `API Key` field on Automatisch. -5. Now, you can start using the Jotform connection with Automatisch. +4. Click on the **Create New Key** button. +5. Give "Full Access" permission to the created API key. +6. Copy the **API key** from the page to the `API Key` field on Automatisch. +7. Enter your API URL in the respective field if it's different than the default value. For EU, it's https://eu-api.jotform.com. For HIPAA, it's https://hipaa-api.jotform.com. For the Jotform Enterprise customers, it should be the API URL of your Jotform Enterprise instance, e.g. https://subdomain.jotform.com/API or https://your-domain.com/API. More information may be found on the [Jotform API documentation](https://api.jotform.com/docs/). +8. Now, you can start using the Jotform connection with Automatisch. From 27c36b644d0ba9ea42386a18256f350cac5862a4 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Wed, 14 Aug 2024 14:12:30 +0000 Subject: [PATCH 4/7] fix(jotform/list-forms): fix pagination --- .../backend/src/apps/jotform/dynamic-data/list-forms/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/apps/jotform/dynamic-data/list-forms/index.js b/packages/backend/src/apps/jotform/dynamic-data/list-forms/index.js index 180425bc..c96f34e6 100644 --- a/packages/backend/src/apps/jotform/dynamic-data/list-forms/index.js +++ b/packages/backend/src/apps/jotform/dynamic-data/list-forms/index.js @@ -29,7 +29,7 @@ export default { } } - if (data.resultSet.count > data.resultSet.limit) { + if (data.resultSet.count >= data.resultSet.limit) { hasMore = true; } else { hasMore = false; From 7506bf186b9ee23dd7a5982d48a072f1898cb965 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Wed, 14 Aug 2024 14:12:49 +0000 Subject: [PATCH 5/7] feat(jotform): prettify screen name --- packages/backend/src/apps/jotform/auth/verify-credentials.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/backend/src/apps/jotform/auth/verify-credentials.js b/packages/backend/src/apps/jotform/auth/verify-credentials.js index c99fae89..0acb407d 100644 --- a/packages/backend/src/apps/jotform/auth/verify-credentials.js +++ b/packages/backend/src/apps/jotform/auth/verify-credentials.js @@ -3,10 +3,8 @@ import getCurrentUser from '../common/get-current-user.js'; const verifyCredentials = async ($) => { const user = await getCurrentUser($); - const screenName = [user.username, user.email].filter(Boolean).join(' @ '); - await $.auth.set({ - screenName, + screenName: user.name, apiKey: $.auth.data.apiKey, }); }; From ec740c07faf82cfde14558d6dd3d04ecfc3b38dc Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Wed, 14 Aug 2024 14:13:04 +0000 Subject: [PATCH 6/7] feat(jotform/auth): show default API URL --- packages/backend/src/apps/jotform/auth/index.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/apps/jotform/auth/index.js b/packages/backend/src/apps/jotform/auth/index.js index baf817e1..9d5cf656 100644 --- a/packages/backend/src/apps/jotform/auth/index.js +++ b/packages/backend/src/apps/jotform/auth/index.js @@ -4,14 +4,13 @@ import isStillVerified from './is-still-verified.js'; export default { fields: [ { - key: 'instanceUrl', - label: 'Jotform instance URL', + key: 'apiUrl', + label: 'API URL', type: 'string', required: false, readOnly: false, - value: null, - placeholder: 'https://${subdomain}.jotform.com', - description: 'If you have an enterprise plan, you can use your api url.', + value: 'https://api.jotform.com', + placeholder: 'https://${subdomain}.jotform.com/api', clickToCopy: true, }, { @@ -22,7 +21,6 @@ export default { readOnly: false, value: null, placeholder: null, - description: 'Jotform API key of your account.', clickToCopy: false, }, ], From 566f9dd5cc151a98785a6c6c30d9ac172208d5df Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Wed, 14 Aug 2024 14:14:55 +0000 Subject: [PATCH 7/7] fix(jotform): use right custom API URL --- packages/backend/src/apps/jotform/common/set-base-url.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/apps/jotform/common/set-base-url.js b/packages/backend/src/apps/jotform/common/set-base-url.js index 135149b1..d2f6851d 100644 --- a/packages/backend/src/apps/jotform/common/set-base-url.js +++ b/packages/backend/src/apps/jotform/common/set-base-url.js @@ -1,6 +1,6 @@ const setBaseUrl = ($, requestConfig) => { - if ($.auth.data.instanceUrl) { - requestConfig.baseURL = $.auth.data.instanceUrl; + if ($.auth.data.apiUrl) { + requestConfig.baseURL = $.auth.data.apiUrl; } else if ($.app.apiBaseUrl) { requestConfig.baseURL = $.app.apiBaseUrl; }