From 3c9bc53a79e442448e56bc5e56c4809933d9c25f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C4=B1dvan=20Akca?= Date: Fri, 23 Jun 2023 19:47:04 +0300 Subject: [PATCH 01/11] feat(google-sheets): add create spreadsheet action --- .../actions/create-spreadsheet/index.ts | 105 ++++++++++++++++++ .../src/apps/google-sheets/actions/index.ts | 3 +- .../docs/pages/apps/google-sheets/actions.md | 4 +- 3 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 packages/backend/src/apps/google-sheets/actions/create-spreadsheet/index.ts diff --git a/packages/backend/src/apps/google-sheets/actions/create-spreadsheet/index.ts b/packages/backend/src/apps/google-sheets/actions/create-spreadsheet/index.ts new file mode 100644 index 00000000..b072cb7a --- /dev/null +++ b/packages/backend/src/apps/google-sheets/actions/create-spreadsheet/index.ts @@ -0,0 +1,105 @@ +import defineAction from '../../../../helpers/define-action'; + +type THeaders = { + __id: string; + header: string; +}[]; + +export default defineAction({ + name: 'Create spreadsheet', + key: 'createSpreadsheet', + description: + 'Create a blank spreadsheet or duplicate an existing spreadsheet. Optionally, provide headers.', + arguments: [ + { + label: 'Title', + key: 'title', + type: 'string' as const, + required: true, + description: '', + variables: true, + }, + { + label: 'Spreadsheet to copy', + key: 'spreadsheetId', + type: 'dropdown' as const, + required: false, + description: 'Choose a spreadsheet to copy its data.', + variables: true, + source: { + type: 'query', + name: 'getDynamicData', + arguments: [ + { + name: 'key', + value: 'listSpreadsheets', + }, + ], + }, + }, + { + label: 'Headers', + key: 'headers', + type: 'dynamic' as const, + required: false, + description: + 'These headers are ignored if "Spreadsheet to Copy" is selected.', + fields: [ + { + label: 'Header', + key: 'header', + type: 'string' as const, + required: true, + variables: true, + }, + ], + }, + ], + + async run($) { + if ($.step.parameters.spreadsheetId) { + const body = { name: $.step.parameters.title }; + + const { data } = await $.http.post( + `https://www.googleapis.com/drive/v3/files/${$.step.parameters.spreadsheetId}/copy`, + body + ); + + $.setActionItem({ + raw: data, + }); + } else { + const headers = $.step.parameters.headers as THeaders; + const values = headers.map((entry) => entry.header); + + const spreadsheetBody = { + properties: { + title: $.step.parameters.title, + }, + sheets: [ + { + data: [ + { + startRow: 0, + startColumn: 0, + rowData: [ + { + values: values.map((header) => ({ + userEnteredValue: { stringValue: header }, + })), + }, + ], + }, + ], + }, + ], + }; + + const { data } = await $.http.post('/v4/spreadsheets', spreadsheetBody); + + $.setActionItem({ + raw: data, + }); + } + }, +}); diff --git a/packages/backend/src/apps/google-sheets/actions/index.ts b/packages/backend/src/apps/google-sheets/actions/index.ts index 7b0c8d2d..98d4a649 100644 --- a/packages/backend/src/apps/google-sheets/actions/index.ts +++ b/packages/backend/src/apps/google-sheets/actions/index.ts @@ -1,3 +1,4 @@ import createSpreadsheetRow from './create-spreadsheet-row'; +import createSpreadsheet from './create-spreadsheet'; -export default [createSpreadsheetRow]; +export default [createSpreadsheetRow, createSpreadsheet]; diff --git a/packages/docs/pages/apps/google-sheets/actions.md b/packages/docs/pages/apps/google-sheets/actions.md index 87e5ebfe..a9bcc86d 100644 --- a/packages/docs/pages/apps/google-sheets/actions.md +++ b/packages/docs/pages/apps/google-sheets/actions.md @@ -2,7 +2,9 @@ favicon: /favicons/google-sheets.svg items: - name: Create Spreadsheet Row - desc: Creates a new row in a specific spreadsheet + desc: Creates a new row in a specific spreadsheet. + - name: Create Spreadsheet + desc: Create a blank spreadsheet or duplicate an existing spreadsheet. Optionally, provide headers. --- + + \ No newline at end of file diff --git a/packages/docs/pages/apps/odoo/connection.md b/packages/docs/pages/apps/odoo/connection.md new file mode 100644 index 00000000..49b0d270 --- /dev/null +++ b/packages/docs/pages/apps/odoo/connection.md @@ -0,0 +1,16 @@ +# Odoo + +:::info +This page explains the steps you need to follow to set up the Odoo +connection in Automatisch. If any of the steps are outdated, please let us know! +::: + +To create a connection, you need to supply the following information: + +1. Fill the **Host Name** field with the Odoo host. +1. Fill the **Port** field with the Odoo port. +1. Fill the **Database Name** field with the Odoo database. +1. Fill the **Email Address** field with the email address of the account that will be intereacting with the database. +1. Fill the **API Key** field with the API key for your Odoo account. + +Odoo's [API documentation](https://www.odoo.com/documentation/latest/developer/reference/external_api.html#api-keys) explains how to create API keys. diff --git a/packages/docs/pages/guide/available-apps.md b/packages/docs/pages/guide/available-apps.md index 2fd0e483..b3df524b 100644 --- a/packages/docs/pages/guide/available-apps.md +++ b/packages/docs/pages/guide/available-apps.md @@ -20,6 +20,7 @@ Following integrations are currently supported by Automatisch. - [HTTP Request](/apps/http-request/actions) - [Notion](/apps/notion/triggers) - [Ntfy](/apps/ntfy/actions) +- [Odoo](/apps/odoo/actions) - [OpenAI](/apps/openai/actions) - [PostgreSQL](/apps/postgresql/actions) - [RSS](/apps/rss/triggers) diff --git a/packages/docs/pages/public/favicons/odoo.svg b/packages/docs/pages/public/favicons/odoo.svg new file mode 100644 index 00000000..aeb5dd77 --- /dev/null +++ b/packages/docs/pages/public/favicons/odoo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 14e43ef5..99d33a9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4328,6 +4328,13 @@ dependencies: "@types/node" "*" +"@types/xmlrpc@^1.3.7": + version "1.3.7" + resolved "https://registry.yarnpkg.com/@types/xmlrpc/-/xmlrpc-1.3.7.tgz#a95e8636fe9b848772088cfaa8021d0ad0ad99a0" + integrity sha512-T+jYEZz/dJvI40dkqx/FNNkyyWDyOb0HgQDpni48r4NyB8n7xjKFDACi8O3NkAWz5cLWEmKRzWfzCEZ5EB6CVg== + dependencies: + "@types/node" "*" + "@types/yargs-parser@*": version "20.2.1" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129" @@ -15412,7 +15419,7 @@ sax@1.2.1: resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o= -sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: +sax@1.2.x, sax@>=0.6.0, sax@^1.2.4, sax@~1.2.4: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== @@ -17982,6 +17989,11 @@ xml2js@0.4.19: sax ">=0.6.0" xmlbuilder "~9.0.1" +xmlbuilder@8.2.x: + version "8.2.2" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-8.2.2.tgz#69248673410b4ba42e1a6136551d2922335aa773" + integrity sha512-eKRAFz04jghooy8muekqzo8uCSVNeyRedbuJrp0fovbLIi7wlsYtdUn3vBAAPq2Y3/0xMz2WMEUQ8yhVVO9Stw== + xmlbuilder@~9.0.1: version "9.0.7" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" @@ -17992,6 +18004,14 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xmlrpc@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/xmlrpc/-/xmlrpc-1.3.2.tgz#26b2ea347848d028aac7e7514b5351976de3e83d" + integrity sha512-jQf5gbrP6wvzN71fgkcPPkF4bF/Wyovd7Xdff8d6/ihxYmgETQYSuTc+Hl+tsh/jmgPLro/Aro48LMFlIyEKKQ== + dependencies: + sax "1.2.x" + xmlbuilder "8.2.x" + xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" From 6c5039f1ba97ff974ffd669b1211046570eb81d7 Mon Sep 17 00:00:00 2001 From: Ali BARIN Date: Thu, 29 Jun 2023 16:03:44 +0200 Subject: [PATCH 07/11] fix(odoo): add missing empty type file (#1165) --- packages/backend/src/apps/odoo/index.d.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 packages/backend/src/apps/odoo/index.d.ts diff --git a/packages/backend/src/apps/odoo/index.d.ts b/packages/backend/src/apps/odoo/index.d.ts new file mode 100644 index 00000000..e69de29b From 676027245fa83a496227f6450b8d744ffc73b9a6 Mon Sep 17 00:00:00 2001 From: KrzysztofDK <106629516+gh-kdk@users.noreply.github.com> Date: Thu, 29 Jun 2023 16:45:57 +0200 Subject: [PATCH 08/11] feat(mattermost): add auth and send message to channel action * feat: mattermost integration * post review adjustments --- .../src/apps/mattermost/actions/index.ts | 3 + .../send-a-message-to-channel/index.ts | 42 ++++++++++++++ .../send-a-message-to-channel/post-message.ts | 27 +++++++++ .../src/apps/mattermost/assets/favicon.svg | 6 ++ .../apps/mattermost/auth/generate-auth-url.ts | 18 ++++++ .../backend/src/apps/mattermost/auth/index.ts | 57 +++++++++++++++++++ .../apps/mattermost/auth/is-still-verified.ts | 9 +++ .../mattermost/auth/verify-credentials.ts | 44 ++++++++++++++ .../apps/mattermost/common/add-auth-header.ts | 12 ++++ .../common/add-x-requested-with-header.ts | 11 ++++ .../apps/mattermost/common/get-base-url.ts | 7 +++ .../mattermost/common/get-current-user.ts | 9 +++ .../apps/mattermost/common/set-base-url.ts | 9 +++ .../src/apps/mattermost/dynamic-data/index.ts | 3 + .../dynamic-data/list-channels/index.ts | 36 ++++++++++++ .../backend/src/apps/mattermost/index.d.ts | 0 packages/backend/src/apps/mattermost/index.ts | 22 +++++++ packages/docs/pages/.vitepress/config.js | 9 +++ .../docs/pages/apps/mattermost/actions.md | 12 ++++ .../docs/pages/apps/mattermost/connection.md | 19 +++++++ packages/docs/pages/guide/available-apps.md | 1 + 21 files changed, 356 insertions(+) create mode 100644 packages/backend/src/apps/mattermost/actions/index.ts create mode 100644 packages/backend/src/apps/mattermost/actions/send-a-message-to-channel/index.ts create mode 100644 packages/backend/src/apps/mattermost/actions/send-a-message-to-channel/post-message.ts create mode 100644 packages/backend/src/apps/mattermost/assets/favicon.svg create mode 100644 packages/backend/src/apps/mattermost/auth/generate-auth-url.ts create mode 100644 packages/backend/src/apps/mattermost/auth/index.ts create mode 100644 packages/backend/src/apps/mattermost/auth/is-still-verified.ts create mode 100644 packages/backend/src/apps/mattermost/auth/verify-credentials.ts create mode 100644 packages/backend/src/apps/mattermost/common/add-auth-header.ts create mode 100644 packages/backend/src/apps/mattermost/common/add-x-requested-with-header.ts create mode 100644 packages/backend/src/apps/mattermost/common/get-base-url.ts create mode 100644 packages/backend/src/apps/mattermost/common/get-current-user.ts create mode 100644 packages/backend/src/apps/mattermost/common/set-base-url.ts create mode 100644 packages/backend/src/apps/mattermost/dynamic-data/index.ts create mode 100644 packages/backend/src/apps/mattermost/dynamic-data/list-channels/index.ts create mode 100644 packages/backend/src/apps/mattermost/index.d.ts create mode 100644 packages/backend/src/apps/mattermost/index.ts create mode 100644 packages/docs/pages/apps/mattermost/actions.md create mode 100644 packages/docs/pages/apps/mattermost/connection.md diff --git a/packages/backend/src/apps/mattermost/actions/index.ts b/packages/backend/src/apps/mattermost/actions/index.ts new file mode 100644 index 00000000..d28a6432 --- /dev/null +++ b/packages/backend/src/apps/mattermost/actions/index.ts @@ -0,0 +1,3 @@ +import sendMessageToChannel from './send-a-message-to-channel'; + +export default [sendMessageToChannel]; diff --git a/packages/backend/src/apps/mattermost/actions/send-a-message-to-channel/index.ts b/packages/backend/src/apps/mattermost/actions/send-a-message-to-channel/index.ts new file mode 100644 index 00000000..39734a99 --- /dev/null +++ b/packages/backend/src/apps/mattermost/actions/send-a-message-to-channel/index.ts @@ -0,0 +1,42 @@ +import defineAction from '../../../../helpers/define-action'; +import postMessage from './post-message'; + +export default defineAction({ + name: 'Send a message to channel', + key: 'sendMessageToChannel', + description: 'Sends a message to a channel you specify.', + arguments: [ + { + label: 'Channel', + key: 'channel', + type: 'dropdown' as const, + required: true, + description: 'Pick a channel to send the message to.', + variables: true, + source: { + type: 'query', + name: 'getDynamicData', + arguments: [ + { + name: 'key', + value: 'listChannels', + }, + ], + }, + }, + { + label: 'Message text', + key: 'message', + type: 'string' as const, + required: true, + description: 'The content of your new message.', + variables: true, + }, + ], + + async run($) { + const message = await postMessage($); + + return message; + }, +}); diff --git a/packages/backend/src/apps/mattermost/actions/send-a-message-to-channel/post-message.ts b/packages/backend/src/apps/mattermost/actions/send-a-message-to-channel/post-message.ts new file mode 100644 index 00000000..86bb7586 --- /dev/null +++ b/packages/backend/src/apps/mattermost/actions/send-a-message-to-channel/post-message.ts @@ -0,0 +1,27 @@ +import { IGlobalVariable } from '@automatisch/types'; + +type TData = { + channel_id: string; + message: string; +}; + +const postMessage = async ($: IGlobalVariable) => { + const { parameters } = $.step; + const channel_id = parameters.channel as string; + const message = parameters.message as string; + + const data: TData = { + channel_id, + message, + }; + + const response = await $.http.post('/api/v4/posts', data); + + const actionData = { + raw: response?.data, + }; + + $.setActionItem(actionData); +}; + +export default postMessage; diff --git a/packages/backend/src/apps/mattermost/assets/favicon.svg b/packages/backend/src/apps/mattermost/assets/favicon.svg new file mode 100644 index 00000000..1d5bf91f --- /dev/null +++ b/packages/backend/src/apps/mattermost/assets/favicon.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/packages/backend/src/apps/mattermost/auth/generate-auth-url.ts b/packages/backend/src/apps/mattermost/auth/generate-auth-url.ts new file mode 100644 index 00000000..a497e54a --- /dev/null +++ b/packages/backend/src/apps/mattermost/auth/generate-auth-url.ts @@ -0,0 +1,18 @@ +import { IGlobalVariable } from '@automatisch/types'; +import { URL, URLSearchParams } from 'url'; +import getBaseUrl from '../common/get-base-url'; + +export default async function generateAuthUrl($: IGlobalVariable) { + const searchParams = new URLSearchParams({ + client_id: $.auth.data.clientId as string, + redirect_uri: $.auth.data.oAuthRedirectUrl as string, + response_type: 'code', + }); + + const baseUrl = getBaseUrl($); + const path = `/oauth/authorize?${searchParams.toString()}`; + + await $.auth.set({ + url: new URL(path, baseUrl).toString(), + }); +} diff --git a/packages/backend/src/apps/mattermost/auth/index.ts b/packages/backend/src/apps/mattermost/auth/index.ts new file mode 100644 index 00000000..853cf1eb --- /dev/null +++ b/packages/backend/src/apps/mattermost/auth/index.ts @@ -0,0 +1,57 @@ +import generateAuthUrl from './generate-auth-url'; +import verifyCredentials from './verify-credentials'; +import isStillVerified from './is-still-verified'; + +export default { + fields: [ + { + key: 'oAuthRedirectUrl', + label: 'OAuth Redirect URL', + type: 'string' as const, + required: true, + readOnly: true, + value: '{WEB_APP_URL}/app/mattermost/connections/add', + placeholder: null, + description: + 'When asked to input an OAuth callback or redirect URL in Mattermost OAuth, enter the URL above.', + clickToCopy: true, + }, + { + key: 'instanceUrl', + label: 'Mattermost instance URL', + type: 'string' as const, + required: false, + readOnly: false, + value: null, + placeholder: null, + description: 'Your Mattermost instance URL', + clickToCopy: true, + }, + { + key: 'clientId', + label: 'Client id', + type: 'string' as const, + required: true, + readOnly: false, + value: null, + placeholder: null, + description: null, + clickToCopy: false, + }, + { + key: 'clientSecret', + label: 'Client secret', + type: 'string' as const, + required: true, + readOnly: false, + value: null, + placeholder: null, + description: null, + clickToCopy: false, + }, + ], + + generateAuthUrl, + verifyCredentials, + isStillVerified, +}; diff --git a/packages/backend/src/apps/mattermost/auth/is-still-verified.ts b/packages/backend/src/apps/mattermost/auth/is-still-verified.ts new file mode 100644 index 00000000..befb7694 --- /dev/null +++ b/packages/backend/src/apps/mattermost/auth/is-still-verified.ts @@ -0,0 +1,9 @@ +import { IGlobalVariable } from '@automatisch/types'; +import getCurrentUser from '../common/get-current-user'; + +const isStillVerified = async ($: IGlobalVariable) => { + const user = await getCurrentUser($); + return !!user.id; +}; + +export default isStillVerified; diff --git a/packages/backend/src/apps/mattermost/auth/verify-credentials.ts b/packages/backend/src/apps/mattermost/auth/verify-credentials.ts new file mode 100644 index 00000000..da0538ea --- /dev/null +++ b/packages/backend/src/apps/mattermost/auth/verify-credentials.ts @@ -0,0 +1,44 @@ +import { IGlobalVariable } from '@automatisch/types'; +import getCurrentUser from '../common/get-current-user'; + +const verifyCredentials = async ($: IGlobalVariable) => { + const oauthRedirectUrlField = $.app.auth.fields.find( + (field) => field.key == 'oAuthRedirectUrl' + ); + const redirectUri = oauthRedirectUrlField.value as string; + const params = { + client_id: $.auth.data.clientId, + client_secret: $.auth.data.clientSecret, + code: $.auth.data.code, + grant_type: 'authorization_code', + redirect_uri: redirectUri, + }; + const headers = { + 'Content-Type': 'application/x-www-form-urlencoded', // This is not documented yet required + }; + const response = await $.http.post('/oauth/access_token', null, { + params, + headers, + }); + + const { + data: { access_token, refresh_token, scope, token_type }, + } = response; + + $.auth.data.accessToken = response.data.access_token; + + const currentUser = await getCurrentUser($); + + await $.auth.set({ + clientId: $.auth.data.clientId, + clientSecret: $.auth.data.clientSecret, + accessToken: access_token, + refreshToken: refresh_token, + scope: scope, + tokenType: token_type, + userId: currentUser.id, + screenName: currentUser.username, + }); +}; + +export default verifyCredentials; diff --git a/packages/backend/src/apps/mattermost/common/add-auth-header.ts b/packages/backend/src/apps/mattermost/common/add-auth-header.ts new file mode 100644 index 00000000..edbff231 --- /dev/null +++ b/packages/backend/src/apps/mattermost/common/add-auth-header.ts @@ -0,0 +1,12 @@ +import { TBeforeRequest } from '@automatisch/types'; + +const addAuthHeader: TBeforeRequest = ($, requestConfig) => { + if ($.auth.data?.accessToken) { + requestConfig.headers = requestConfig.headers || {}; + requestConfig.headers.Authorization = `Bearer ${$.auth.data.accessToken}`; + } + + return requestConfig; +}; + +export default addAuthHeader; diff --git a/packages/backend/src/apps/mattermost/common/add-x-requested-with-header.ts b/packages/backend/src/apps/mattermost/common/add-x-requested-with-header.ts new file mode 100644 index 00000000..65d89643 --- /dev/null +++ b/packages/backend/src/apps/mattermost/common/add-x-requested-with-header.ts @@ -0,0 +1,11 @@ +import { TBeforeRequest } from '@automatisch/types'; + +const addXRequestedWithHeader: TBeforeRequest = ($, requestConfig) => { + // This is not documented yet required + // ref. https://forum.mattermost.com/t/solved-invalid-or-expired-session-please-login-again/6772 + requestConfig.headers = requestConfig.headers || {}; + requestConfig.headers['X-Requested-With'] = `XMLHttpRequest`; + return requestConfig; +}; + +export default addXRequestedWithHeader; diff --git a/packages/backend/src/apps/mattermost/common/get-base-url.ts b/packages/backend/src/apps/mattermost/common/get-base-url.ts new file mode 100644 index 00000000..77538eca --- /dev/null +++ b/packages/backend/src/apps/mattermost/common/get-base-url.ts @@ -0,0 +1,7 @@ +import { IGlobalVariable } from '@automatisch/types'; + +const getBaseUrl = ($: IGlobalVariable): string => { + return $.auth.data.instanceUrl as string; +}; + +export default getBaseUrl; diff --git a/packages/backend/src/apps/mattermost/common/get-current-user.ts b/packages/backend/src/apps/mattermost/common/get-current-user.ts new file mode 100644 index 00000000..c6f42624 --- /dev/null +++ b/packages/backend/src/apps/mattermost/common/get-current-user.ts @@ -0,0 +1,9 @@ +import { IGlobalVariable, IJSONObject } from '@automatisch/types'; + +const getCurrentUser = async ($: IGlobalVariable): Promise => { + const response = await $.http.get('/api/v4/users/me'); + const currentUser = response.data; + return currentUser; +}; + +export default getCurrentUser; diff --git a/packages/backend/src/apps/mattermost/common/set-base-url.ts b/packages/backend/src/apps/mattermost/common/set-base-url.ts new file mode 100644 index 00000000..8f3aab56 --- /dev/null +++ b/packages/backend/src/apps/mattermost/common/set-base-url.ts @@ -0,0 +1,9 @@ +import { TBeforeRequest } from '@automatisch/types'; + +const setBaseUrl: TBeforeRequest = ($, requestConfig) => { + requestConfig.baseURL = $.auth.data.instanceUrl as string; + + return requestConfig; +}; + +export default setBaseUrl; diff --git a/packages/backend/src/apps/mattermost/dynamic-data/index.ts b/packages/backend/src/apps/mattermost/dynamic-data/index.ts new file mode 100644 index 00000000..fae496fc --- /dev/null +++ b/packages/backend/src/apps/mattermost/dynamic-data/index.ts @@ -0,0 +1,3 @@ +import listChannels from './list-channels'; + +export default [listChannels]; diff --git a/packages/backend/src/apps/mattermost/dynamic-data/list-channels/index.ts b/packages/backend/src/apps/mattermost/dynamic-data/list-channels/index.ts new file mode 100644 index 00000000..35cde864 --- /dev/null +++ b/packages/backend/src/apps/mattermost/dynamic-data/list-channels/index.ts @@ -0,0 +1,36 @@ +import { IGlobalVariable, IJSONObject } from '@automatisch/types'; + +type TChannel = { + id: string; + display_name: string; +}; + +type TResponse = { + data: TChannel[]; +}; + +export default { + name: 'List channels', + key: 'listChannels', + + async run($: IGlobalVariable) { + const channels: { + data: IJSONObject[]; + error: IJSONObject | null; + } = { + data: [], + error: null, + }; + + const response: TResponse = await $.http.get('/api/v4/users/me/channels'); // this endpoint will return only channels user joined, there is no endpoint to list all channels available for user + + for (const channel of response.data) { + channels.data.push({ + value: channel.id as string, + name: (channel.display_name as string) || (channel.id as string), // it's possible for channel to not have any name thus falling back to using id + }); + } + + return channels; + }, +}; diff --git a/packages/backend/src/apps/mattermost/index.d.ts b/packages/backend/src/apps/mattermost/index.d.ts new file mode 100644 index 00000000..e69de29b diff --git a/packages/backend/src/apps/mattermost/index.ts b/packages/backend/src/apps/mattermost/index.ts new file mode 100644 index 00000000..8d049421 --- /dev/null +++ b/packages/backend/src/apps/mattermost/index.ts @@ -0,0 +1,22 @@ +import defineApp from '../../helpers/define-app'; +import addAuthHeader from './common/add-auth-header'; +import addXRequestedWithHeader from './common/add-x-requested-with-header'; +import setBaseUrl from './common/set-base-url'; +import auth from './auth'; +import actions from './actions'; +import dynamicData from './dynamic-data'; + +export default defineApp({ + name: 'Mattermost', + key: 'mattermost', + iconUrl: '{BASE_URL}/apps/mattermost/assets/favicon.svg', + authDocUrl: 'https://automatisch.io/docs/apps/slack/connection', + baseUrl: 'https://mattermost.com', + apiBaseUrl: '', // there is no cloud version of this app, user always need to provide address of own instance when creating connection + primaryColor: '4a154b', + supportsConnections: true, + beforeRequest: [setBaseUrl, addXRequestedWithHeader, addAuthHeader], + auth, + actions, + dynamicData, +}); diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js index 7f0fbbaa..66550b42 100644 --- a/packages/docs/pages/.vitepress/config.js +++ b/packages/docs/pages/.vitepress/config.js @@ -142,6 +142,15 @@ export default defineConfig({ { text: 'Connection', link: '/apps/http-request/connection' }, ], }, + { + text: 'Mattermost', + collapsible: true, + collapsed: true, + items: [ + { text: 'Triggers', link: '/apps/mattermost/actions' }, + { text: 'Connection', link: '/apps/notion/connection' }, + ], + }, { text: 'Notion', collapsible: true, diff --git a/packages/docs/pages/apps/mattermost/actions.md b/packages/docs/pages/apps/mattermost/actions.md new file mode 100644 index 00000000..b4ed063e --- /dev/null +++ b/packages/docs/pages/apps/mattermost/actions.md @@ -0,0 +1,12 @@ +--- +favicon: /favicons/mattermost.svg +items: + - name: Send a message to channel + desc: Sends a message to a channel you specify. +--- + + + + diff --git a/packages/docs/pages/apps/mattermost/connection.md b/packages/docs/pages/apps/mattermost/connection.md new file mode 100644 index 00000000..9fa6187c --- /dev/null +++ b/packages/docs/pages/apps/mattermost/connection.md @@ -0,0 +1,19 @@ +# Mattermost + +:::info +This page explains the steps you need to follow to set up the Mattermost +connection in Automatisch. If any of the steps are outdated, please let us know! +::: + +1. Go to the `/integrations/oauth2-apps/add` page of your Mattermost server to register a **new OAuth application**. + - You can find details about registering new Mattermost oAuth application at https://docs.mattermost.com/integrations/cloud-oauth-2-0-applications.html#register-your-application-in-mattermost. +2. Fill in the **Display Name** field. +3. Fill in the **Description** field. +4. Fill in the **Homepage** field. +5. Copy **OAuth Redirect URL** from Automatisch to the **Callback URLs** field on Mattermost page. +6. Click on the **Save** button at the end of the form on Mattermost page. +7. Copy the **Client ID** value from the following page to the `Client ID` field on Automatisch. +8. Copy the **Client Secret** value from the same page to the `Client Secret` field on Automatisch. +9. Click **Done** button on MAttermost page. +10. Click **Submit** button on Automatisch. +11. Congrats! Start using your new Mattermost connection within the flows. diff --git a/packages/docs/pages/guide/available-apps.md b/packages/docs/pages/guide/available-apps.md index b3df524b..58b4370e 100644 --- a/packages/docs/pages/guide/available-apps.md +++ b/packages/docs/pages/guide/available-apps.md @@ -18,6 +18,7 @@ Following integrations are currently supported by Automatisch. - [Google Forms](/apps/google-forms/triggers) - [Google Sheets](/apps/google-sheets/triggers) - [HTTP Request](/apps/http-request/actions) +- [Mattermost](/apps/mattermost/actions) - [Notion](/apps/notion/triggers) - [Ntfy](/apps/ntfy/actions) - [Odoo](/apps/odoo/actions) From f3bf418997b864aa16fb43f3f4abd8bb95f01d2b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Jun 2023 17:51:47 +0200 Subject: [PATCH 09/11] chore(deps): bump fast-xml-parser from 4.2.4 to 4.2.5 (#1164) Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 4.2.4 to 4.2.5. - [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases) - [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md) - [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/v4.2.4...v4.2.5) --- updated-dependencies: - dependency-name: fast-xml-parser dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 99d33a9e..dd154cea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8848,9 +8848,9 @@ fast-redact@^3.0.0: integrity sha512-YN+CYfCVRVMUZOUPeinHNKgytM1wPI/C/UCLEi56EsY2dwwvI00kIJHJoI7pMVqGoMew8SMZ2SSfHKHULHXDsg== fast-xml-parser@^4.0.11: - version "4.2.4" - resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.4.tgz#6e846ede1e56ad9e5ef07d8720809edf0ed07e9b" - integrity sha512-fbfMDvgBNIdDJLdLOwacjFAPYt67tr31H9ZhWSm45CDAxvd0I6WTlSOUo7K2P/K5sA5JgMKG64PI3DMcaFdWpQ== + version "4.2.5" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz#a6747a09296a6cb34f2ae634019bf1738f3b421f" + integrity sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g== dependencies: strnum "^1.0.5" From 9fb4dca39b9eadae803b1448d9a7388799e9e708 Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Wed, 5 Jul 2023 14:29:19 +0200 Subject: [PATCH 10/11] docs(mattermost): Fix links of mattermost app --- packages/backend/src/apps/mattermost/index.ts | 2 +- packages/docs/pages/.vitepress/config.js | 4 ++-- packages/docs/pages/public/favicons/mattermost.svg | 6 ++++++ 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 packages/docs/pages/public/favicons/mattermost.svg diff --git a/packages/backend/src/apps/mattermost/index.ts b/packages/backend/src/apps/mattermost/index.ts index 8d049421..91a75b8e 100644 --- a/packages/backend/src/apps/mattermost/index.ts +++ b/packages/backend/src/apps/mattermost/index.ts @@ -10,7 +10,7 @@ export default defineApp({ name: 'Mattermost', key: 'mattermost', iconUrl: '{BASE_URL}/apps/mattermost/assets/favicon.svg', - authDocUrl: 'https://automatisch.io/docs/apps/slack/connection', + authDocUrl: 'https://automatisch.io/docs/apps/mattermost/connection', baseUrl: 'https://mattermost.com', apiBaseUrl: '', // there is no cloud version of this app, user always need to provide address of own instance when creating connection primaryColor: '4a154b', diff --git a/packages/docs/pages/.vitepress/config.js b/packages/docs/pages/.vitepress/config.js index 66550b42..1af34b4f 100644 --- a/packages/docs/pages/.vitepress/config.js +++ b/packages/docs/pages/.vitepress/config.js @@ -147,8 +147,8 @@ export default defineConfig({ collapsible: true, collapsed: true, items: [ - { text: 'Triggers', link: '/apps/mattermost/actions' }, - { text: 'Connection', link: '/apps/notion/connection' }, + { text: 'Actions', link: '/apps/mattermost/actions' }, + { text: 'Connection', link: '/apps/mattermost/connection' }, ], }, { diff --git a/packages/docs/pages/public/favicons/mattermost.svg b/packages/docs/pages/public/favicons/mattermost.svg new file mode 100644 index 00000000..47da0ba1 --- /dev/null +++ b/packages/docs/pages/public/favicons/mattermost.svg @@ -0,0 +1,6 @@ + + + + + + From d9118436487b3d000dbc663627ffe75c5bc1bc14 Mon Sep 17 00:00:00 2001 From: Faruk AYDIN Date: Tue, 11 Jul 2023 15:58:02 +0200 Subject: [PATCH 11/11] fix(twilio): Receive SMS webhook payload --- .../twilio/triggers/receive-sms/fetch-messages.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/apps/twilio/triggers/receive-sms/fetch-messages.ts b/packages/backend/src/apps/twilio/triggers/receive-sms/fetch-messages.ts index f6c7228a..fdf8a488 100644 --- a/packages/backend/src/apps/twilio/triggers/receive-sms/fetch-messages.ts +++ b/packages/backend/src/apps/twilio/triggers/receive-sms/fetch-messages.ts @@ -11,8 +11,20 @@ const fetchMessages = async ($: IGlobalVariable) => { response = await $.http.get(requestPath); response.data.messages.forEach((message: IJSONObject) => { + const computedMessage = { + To: message.to, + Body: message.body, + From: message.from, + SmsSid: message.sid, + NumMedia: message.num_media, + SmsStatus: message.status, + AccountSid: message.account_sid, + ApiVersion: message.api_version, + NumSegments: message.num_segments, + }; + const dataItem = { - raw: message, + raw: computedMessage, meta: { internalId: message.date_sent as string, },