feat(clickup): add clickup integration
This commit is contained in:
27
packages/backend/src/apps/clickup/assets/favicon.svg
Normal file
27
packages/backend/src/apps/clickup/assets/favicon.svg
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<svg width="185" height="185" viewBox="0 0 185 185" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g filter="url(#filter0_d)">
|
||||||
|
<rect x="30" y="20" width="125" height="125" rx="62.5" fill="white"/>
|
||||||
|
<rect x="30" y="20" width="125" height="125" rx="62.5" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.8789 105.714L69.3974 95.3593C76.5762 104.732 84.1998 109.051 92.6948 109.051C101.143 109.051 108.557 104.781 115.414 95.4832L129.119 105.59C119.232 118.996 106.932 126.079 92.6948 126.079C78.5049 126.079 66.0907 119.046 55.8789 105.714Z" fill="url(#paint0_linear)"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M92.6491 60.7078L68.5883 81.4406L57.4727 68.5407L92.6969 38.1885L127.647 68.5644L116.477 81.417L92.6491 60.7078Z" fill="url(#paint1_linear)"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d" x="0" y="0" width="185" height="185" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||||
|
<feOffset dy="10"/>
|
||||||
|
<feGaussianBlur stdDeviation="15"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.117647 0 0 0 0 0.211765 0 0 0 0.1 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="paint0_linear" x1="55.8789" y1="116.251" x2="129.119" y2="116.251" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#8930FD"/>
|
||||||
|
<stop offset="1" stop-color="#49CCF9"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear" x1="57.4727" y1="67.6025" x2="127.647" y2="67.6025" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FF02F0"/>
|
||||||
|
<stop offset="1" stop-color="#FFC800"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
21
packages/backend/src/apps/clickup/auth/generate-auth-url.js
Normal file
21
packages/backend/src/apps/clickup/auth/generate-auth-url.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import { URLSearchParams } from 'url';
|
||||||
|
|
||||||
|
export default async function generateAuthUrl($) {
|
||||||
|
const oauthRedirectUrlField = $.app.auth.fields.find(
|
||||||
|
(field) => field.key == 'oAuthRedirectUrl'
|
||||||
|
);
|
||||||
|
const redirectUri = oauthRedirectUrlField.value;
|
||||||
|
const state = Math.random().toString();
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
client_id: $.auth.data.clientId,
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
state,
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `https://app.clickup.com/api?${searchParams.toString()}`;
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
url,
|
||||||
|
originalState: state,
|
||||||
|
});
|
||||||
|
}
|
46
packages/backend/src/apps/clickup/auth/index.js
Normal file
46
packages/backend/src/apps/clickup/auth/index.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import generateAuthUrl from './generate-auth-url.js';
|
||||||
|
import verifyCredentials from './verify-credentials.js';
|
||||||
|
import isStillVerified from './is-still-verified.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
key: 'oAuthRedirectUrl',
|
||||||
|
label: 'OAuth Redirect URL',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
readOnly: true,
|
||||||
|
value: '{WEB_APP_URL}/app/clickup/connections/add',
|
||||||
|
placeholder: null,
|
||||||
|
description:
|
||||||
|
'When asked to input a redirect URL in ClickUp, enter the URL above.',
|
||||||
|
clickToCopy: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientId',
|
||||||
|
label: 'Client ID',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: null,
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'clientSecret',
|
||||||
|
label: 'Client Secret',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
readOnly: false,
|
||||||
|
value: null,
|
||||||
|
placeholder: null,
|
||||||
|
description: null,
|
||||||
|
clickToCopy: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
|
||||||
|
generateAuthUrl,
|
||||||
|
verifyCredentials,
|
||||||
|
isStillVerified,
|
||||||
|
};
|
@@ -0,0 +1,8 @@
|
|||||||
|
import getCurrentUser from '../common/get-current-user.js';
|
||||||
|
|
||||||
|
const isStillVerified = async ($) => {
|
||||||
|
const currentUser = await getCurrentUser($);
|
||||||
|
return !!currentUser.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default isStillVerified;
|
31
packages/backend/src/apps/clickup/auth/verify-credentials.js
Normal file
31
packages/backend/src/apps/clickup/auth/verify-credentials.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import getCurrentUser from '../common/get-current-user.js';
|
||||||
|
|
||||||
|
const verifyCredentials = async ($) => {
|
||||||
|
if ($.auth.data.originalState !== $.auth.data.state) {
|
||||||
|
throw new Error(`The 'state' parameter does not match.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await $.http.post('/v2/oauth/token', {
|
||||||
|
client_id: $.auth.data.clientId,
|
||||||
|
client_secret: $.auth.data.clientSecret,
|
||||||
|
code: $.auth.data.code,
|
||||||
|
});
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
accessToken: data.access_token,
|
||||||
|
tokenType: data.token_type,
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentUser = await getCurrentUser($);
|
||||||
|
const screenName = [currentUser.username, currentUser.email]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' @ ');
|
||||||
|
|
||||||
|
await $.auth.set({
|
||||||
|
clientId: $.auth.data.clientId,
|
||||||
|
clientSecret: $.auth.data.clientSecret,
|
||||||
|
screenName,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default verifyCredentials;
|
@@ -0,0 +1,9 @@
|
|||||||
|
const addAuthHeader = ($, requestConfig) => {
|
||||||
|
if ($.auth.data?.accessToken) {
|
||||||
|
requestConfig.headers.Authorization = `${$.auth.data.tokenType} ${$.auth.data.accessToken}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default addAuthHeader;
|
@@ -0,0 +1,6 @@
|
|||||||
|
const getCurrentUser = async ($) => {
|
||||||
|
const { data } = await $.http.get('/v2/user');
|
||||||
|
return data.user;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getCurrentUser;
|
16
packages/backend/src/apps/clickup/index.js
Normal file
16
packages/backend/src/apps/clickup/index.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import defineApp from '../../helpers/define-app.js';
|
||||||
|
import addAuthHeader from './common/add-auth-header.js';
|
||||||
|
import auth from './auth/index.js';
|
||||||
|
|
||||||
|
export default defineApp({
|
||||||
|
name: 'ClickUp',
|
||||||
|
key: 'clickup',
|
||||||
|
baseUrl: 'https://clickup.com',
|
||||||
|
apiBaseUrl: 'https://api.clickup.com/api',
|
||||||
|
iconUrl: '{BASE_URL}/apps/clickup/assets/favicon.svg',
|
||||||
|
authDocUrl: 'https://automatisch.io/docs/apps/clickup/connection',
|
||||||
|
primaryColor: 'FD71AF',
|
||||||
|
supportsConnections: true,
|
||||||
|
beforeRequest: [addAuthHeader],
|
||||||
|
auth,
|
||||||
|
});
|
@@ -77,6 +77,12 @@ export default defineConfig({
|
|||||||
{ text: 'Connection', link: '/apps/datastore/connection' },
|
{ text: 'Connection', link: '/apps/datastore/connection' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
text: 'ClickUp',
|
||||||
|
collapsible: true,
|
||||||
|
collapsed: true,
|
||||||
|
items: [{ text: 'Connection', link: '/apps/clickup/connection' }],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
text: 'DeepL',
|
text: 'DeepL',
|
||||||
collapsible: true,
|
collapsible: true,
|
||||||
|
17
packages/docs/pages/apps/clickup/connection.md
Normal file
17
packages/docs/pages/apps/clickup/connection.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# ClickUp
|
||||||
|
|
||||||
|
:::info
|
||||||
|
This page explains the steps you need to follow to set up the ClickUp
|
||||||
|
connection in Automatisch. If any of the steps are outdated, please let us know!
|
||||||
|
:::
|
||||||
|
|
||||||
|
1. Login to your ClickUp account: [https://app.clickup.com/login](https://app.clickup.com/login).
|
||||||
|
2. Go to **Settings** page.
|
||||||
|
3. Click on the **ClickUp API** tab on the left.
|
||||||
|
4. Click on the **Create an App** button.
|
||||||
|
5. Fill the name field.
|
||||||
|
6. Copy **OAuth Redirect URL** from Automatisch to **Redirect URL(s)** field.
|
||||||
|
7. Copy **Client ID** to **Client ID** field on Automatisch.
|
||||||
|
8. Copy **Client Secret** to **Client Secret** field on Automatisch.
|
||||||
|
9. Click **Submit** button on Automatisch.
|
||||||
|
10. Congrats! Start using your new ClickUp connection within the flows.
|
27
packages/docs/pages/public/favicons/clickup.svg
Normal file
27
packages/docs/pages/public/favicons/clickup.svg
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<svg width="185" height="185" viewBox="0 0 185 185" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g filter="url(#filter0_d)">
|
||||||
|
<rect x="30" y="20" width="125" height="125" rx="62.5" fill="white"/>
|
||||||
|
<rect x="30" y="20" width="125" height="125" rx="62.5" fill="white"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M55.8789 105.714L69.3974 95.3593C76.5762 104.732 84.1998 109.051 92.6948 109.051C101.143 109.051 108.557 104.781 115.414 95.4832L129.119 105.59C119.232 118.996 106.932 126.079 92.6948 126.079C78.5049 126.079 66.0907 119.046 55.8789 105.714Z" fill="url(#paint0_linear)"/>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M92.6491 60.7078L68.5883 81.4406L57.4727 68.5407L92.6969 38.1885L127.647 68.5644L116.477 81.417L92.6491 60.7078Z" fill="url(#paint1_linear)"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d" x="0" y="0" width="185" height="185" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/>
|
||||||
|
<feOffset dy="10"/>
|
||||||
|
<feGaussianBlur stdDeviation="15"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0.0627451 0 0 0 0 0.117647 0 0 0 0 0.211765 0 0 0 0.1 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
<linearGradient id="paint0_linear" x1="55.8789" y1="116.251" x2="129.119" y2="116.251" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#8930FD"/>
|
||||||
|
<stop offset="1" stop-color="#49CCF9"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear" x1="57.4727" y1="67.6025" x2="127.647" y2="67.6025" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FF02F0"/>
|
||||||
|
<stop offset="1" stop-color="#FFC800"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
Reference in New Issue
Block a user