Compare commits
62 Commits
AUT-405
...
remove-typ
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ea81697b5f | ||
![]() |
2ad23ff2e3 | ||
![]() |
aa08b6c339 | ||
![]() |
c00a8704fc | ||
![]() |
b11781efcf | ||
![]() |
889f6f4935 | ||
![]() |
8682f22e68 | ||
![]() |
cbbb76a6c5 | ||
![]() |
cd91f8c711 | ||
![]() |
84f501954b | ||
![]() |
40ae83ed6a | ||
![]() |
c6c02d7c18 | ||
![]() |
8dab118279 | ||
![]() |
7bf74dd867 | ||
![]() |
9f7e2b986b | ||
![]() |
e66b492df3 | ||
![]() |
d070e976b0 | ||
![]() |
0caf6bfabb | ||
![]() |
b842d7938f | ||
![]() |
cebbf84375 | ||
![]() |
8608431490 | ||
![]() |
78ba18b176 | ||
![]() |
f8c30c8526 | ||
![]() |
693c9b85a5 | ||
![]() |
70bb7defd1 | ||
![]() |
160377ca31 | ||
![]() |
2c0ce77a4e | ||
![]() |
77fbb0c9da | ||
![]() |
5971425d23 | ||
![]() |
aefff5c861 | ||
![]() |
a296b5e645 | ||
![]() |
eb486a3a07 | ||
![]() |
062b8521ba | ||
![]() |
1b07f3195a | ||
![]() |
dfa7d4cb8d | ||
![]() |
a14dd9666c | ||
![]() |
b07bd4374f | ||
![]() |
b4e12b0ea8 | ||
![]() |
ee5c17bb85 | ||
![]() |
16c9d3400c | ||
![]() |
4dd994348d | ||
![]() |
f0cbfafc24 | ||
![]() |
d3f38f5488 | ||
![]() |
737090a67a | ||
![]() |
4f66a4d090 | ||
![]() |
df54f909c1 | ||
![]() |
772b195eca | ||
![]() |
87866e34ed | ||
![]() |
c98ac05097 | ||
![]() |
36f991b6f9 | ||
![]() |
a81c5164fc | ||
![]() |
5942482690 | ||
![]() |
4f538ca2fc | ||
![]() |
9f2281a3e2 | ||
![]() |
b0d2f28c78 | ||
![]() |
d4380a4426 | ||
![]() |
ae2738d4cc | ||
![]() |
aa5ae028b2 | ||
![]() |
7ab8c76aa0 | ||
![]() |
8075b65e14 | ||
![]() |
073ce3bf1b | ||
![]() |
80fcbfe01b |
5
.github/workflows/playwright.yml
vendored
5
.github/workflows/playwright.yml
vendored
@@ -4,6 +4,11 @@ on:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
paths:
|
||||
- 'packages/backend/**'
|
||||
- 'packages/e2e-tests/**'
|
||||
- 'packages/web/**'
|
||||
- '!packages/backend/src/apps/**'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
|
28
packages/backend/.eslintrc.js
Normal file
28
packages/backend/.eslintrc.js
Normal file
@@ -0,0 +1,28 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.test.ts', '**/test/**/*.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/ban-ts-comment': ['off'],
|
||||
'@typescript-eslint/no-explicit-any': ['off'],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': ['off'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
@@ -6,10 +6,9 @@ import Role from '../../src/models/role';
|
||||
import '../../src/config/orm';
|
||||
|
||||
async function fetchAdminRole() {
|
||||
const role = await Role
|
||||
.query()
|
||||
const role = await Role.query()
|
||||
.where({
|
||||
key: 'admin'
|
||||
key: 'admin',
|
||||
})
|
||||
.limit(1)
|
||||
.first();
|
||||
@@ -41,7 +40,7 @@ export async function createUser(
|
||||
logger.info('No need to seed a user.');
|
||||
}
|
||||
} catch (err) {
|
||||
if ((err as any).nativeError.code !== UNIQUE_VIOLATION_CODE) {
|
||||
if (err.nativeError.code !== UNIQUE_VIOLATION_CODE) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -68,7 +67,7 @@ export const createDatabase = async (database = appConfig.postgresDatabase) => {
|
||||
await client.query(`CREATE DATABASE ${database}`);
|
||||
logger.info(`Database: ${database} created!`);
|
||||
} catch (err) {
|
||||
if ((err as any).code !== DUPLICATE_DB_CODE) {
|
||||
if (err.code !== DUPLICATE_DB_CODE) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
@@ -85,7 +84,7 @@ export const createDatabaseUser = async (user = appConfig.postgresUsername) => {
|
||||
|
||||
return result;
|
||||
} catch (err) {
|
||||
if ((err as any).code !== DUPLICATE_OBJECT_CODE) {
|
||||
if (err.code !== DUPLICATE_OBJECT_CODE) {
|
||||
throw err;
|
||||
}
|
||||
|
@@ -0,0 +1,102 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Create a scheduled event',
|
||||
key: 'createScheduledEvent',
|
||||
description: 'Creates a scheduled event',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Type',
|
||||
key: 'entityType',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
options: [
|
||||
{ label: 'Stage channel', value: 1 },
|
||||
{ label: 'Voice channel', value: 2 },
|
||||
{ label: 'External', value: 3 }
|
||||
],
|
||||
additionalFields: {
|
||||
type: 'query',
|
||||
name: 'getDynamicFields',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listExternalScheduledEventFields',
|
||||
},
|
||||
{
|
||||
name: 'parameters.entityType',
|
||||
value: '{parameters.entityType}',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Name',
|
||||
key: 'name',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Description',
|
||||
key: 'description',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Image',
|
||||
key: 'image',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
description: 'Image as DataURI scheme [data:image/<jpeg/png/gif>;base64,BASE64_ENCODED_<JPEG/PNG/GIF>_IMAGE_DATA]',
|
||||
variables: true,
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
type entity_metadata = {
|
||||
location: string
|
||||
}
|
||||
|
||||
type guild_event = {
|
||||
channel_id: number,
|
||||
name: string,
|
||||
privacy_level: number,
|
||||
scheduled_start_time: string,
|
||||
scheduled_end_time?: string,
|
||||
description?: string,
|
||||
entity_type?: number,
|
||||
entity_metadata?: entity_metadata,
|
||||
image?: string, //data:image/jpeg;base64,BASE64_ENCODED_JPEG_IMAGE_DATA
|
||||
}
|
||||
|
||||
|
||||
const data: guild_event = {
|
||||
channel_id: $.step.parameters.channel_id as number,
|
||||
name: $.step.parameters.name as string,
|
||||
privacy_level: 2,
|
||||
scheduled_start_time: $.step.parameters.scheduledStartTime as string,
|
||||
scheduled_end_time: $.step.parameters.scheduledEndTime as string,
|
||||
description: $.step.parameters.description as string,
|
||||
entity_type: $.step.parameters.entityType as number,
|
||||
image: $.step.parameters.image as string,
|
||||
};
|
||||
|
||||
const isExternal = $.step.parameters.entityType === 3;
|
||||
if (isExternal) {
|
||||
data.entity_metadata = {
|
||||
location: $.step.parameters.location as string,
|
||||
};
|
||||
data.channel_id = null;
|
||||
}
|
||||
|
||||
const response = await $.http?.post(
|
||||
`/guilds/${$.auth.data.guildId}/scheduled-events`,
|
||||
data
|
||||
);
|
||||
|
||||
$.setActionItem({ raw: response.data });
|
||||
},
|
||||
});
|
@@ -1,3 +1,4 @@
|
||||
import sendMessageToChannel from './send-message-to-channel';
|
||||
import createScheduledEvent from './create-scheduled-event';
|
||||
|
||||
export default [sendMessageToChannel];
|
||||
export default [sendMessageToChannel, createScheduledEvent];
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import listChannels from './list-channels';
|
||||
import listVoiceChannels from './list-voice-channels';
|
||||
|
||||
export default [listChannels];
|
||||
export default [listChannels, listVoiceChannels];
|
||||
|
@@ -0,0 +1,34 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
export default {
|
||||
name: 'List voice channels',
|
||||
key: 'listVoiceChannels',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const channels: {
|
||||
data: IJSONObject[];
|
||||
error: IJSONObject | null;
|
||||
} = {
|
||||
data: [],
|
||||
error: null,
|
||||
};
|
||||
|
||||
const response = await $.http.get(
|
||||
`/guilds/${$.auth.data.guildId}/channels`
|
||||
);
|
||||
|
||||
channels.data = response.data
|
||||
.filter((channel: IJSONObject) => {
|
||||
// filter in voice and stage channels only
|
||||
return channel.type === 2 || channel.type === 13;
|
||||
})
|
||||
.map((channel: IJSONObject) => {
|
||||
return {
|
||||
value: channel.id,
|
||||
name: channel.name,
|
||||
};
|
||||
});
|
||||
|
||||
return channels;
|
||||
},
|
||||
};
|
@@ -0,0 +1,3 @@
|
||||
import listExternalScheduledEventFields from './list-external-scheduled-event-fields';
|
||||
|
||||
export default [listExternalScheduledEventFields];
|
@@ -0,0 +1,83 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
export default {
|
||||
name: 'List external scheduled event fields',
|
||||
key: 'listExternalScheduledEventFields',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const isExternal = $.step.parameters.entityType === 3;
|
||||
|
||||
if (isExternal) {
|
||||
return [
|
||||
{
|
||||
label: 'Location',
|
||||
key: 'location',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'The location of the event (1-100 characters). This will be omitted if type is NOT EXTERNAL',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Start-Time',
|
||||
key: 'scheduledStartTime',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'The time the event will start [ISO8601]',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'End-Time',
|
||||
key: 'scheduledEndTime',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'The time the event will end [ISO8601]. This will be omitted if type is NOT EXTERNAL',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
label: 'Channel',
|
||||
key: 'channel_id',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
description: 'Pick a voice or stage channel to link the event to. This will be omitted if type is EXTERNAL',
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listVoiceChannels',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Location',
|
||||
key: 'location',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
description: 'The location of the event (1-100 characters). This will be omitted if type is NOT EXTERNAL',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'Start-Time',
|
||||
key: 'scheduledStartTime',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'The time the event will start [ISO8601]',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
label: 'End-Time',
|
||||
key: 'scheduledEndTime',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
description: 'The time the event will end [ISO8601]. This will be omitted if type is NOT EXTERNAL',
|
||||
variables: true,
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
@@ -4,6 +4,7 @@ import auth from './auth';
|
||||
import dynamicData from './dynamic-data';
|
||||
import actions from './actions';
|
||||
import triggers from './triggers';
|
||||
import dynamicFields from './dynamic-fields';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Discord',
|
||||
@@ -17,6 +18,7 @@ export default defineApp({
|
||||
beforeRequest: [addAuthHeader],
|
||||
auth,
|
||||
dynamicData,
|
||||
dynamicFields,
|
||||
triggers,
|
||||
actions,
|
||||
});
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import newDatabaseItems from './new-database-items';
|
||||
import updatedDatabaseItems from './updated-database-items';
|
||||
|
||||
export default [newDatabaseItems];
|
||||
export default [newDatabaseItems, updatedDatabaseItems];
|
||||
|
@@ -0,0 +1,33 @@
|
||||
import defineTrigger from '../../../../helpers/define-trigger';
|
||||
import updatedDatabaseItems from './updated-database-items';
|
||||
|
||||
export default defineTrigger({
|
||||
name: 'Updated database items',
|
||||
key: 'updatedDatabaseItems',
|
||||
pollInterval: 15,
|
||||
description:
|
||||
'Triggers when there is an update to an item in a chosen database',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Database',
|
||||
key: 'databaseId',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
variables: false,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listDatabases',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
await updatedDatabaseItems($);
|
||||
},
|
||||
});
|
@@ -0,0 +1,51 @@
|
||||
import { IGlobalVariable } from '@automatisch/types';
|
||||
|
||||
type DatabaseItem = {
|
||||
id: string;
|
||||
last_edited_time: string;
|
||||
};
|
||||
|
||||
type ResponseData = {
|
||||
results: DatabaseItem[];
|
||||
next_cursor?: string;
|
||||
};
|
||||
|
||||
type Payload = {
|
||||
sorts: [
|
||||
{
|
||||
timestamp: 'created_time' | 'last_edited_time';
|
||||
direction: 'ascending' | 'descending';
|
||||
}
|
||||
];
|
||||
start_cursor?: string;
|
||||
};
|
||||
|
||||
const updatedDatabaseItems = async ($: IGlobalVariable) => {
|
||||
const payload: Payload = {
|
||||
sorts: [
|
||||
{
|
||||
timestamp: 'last_edited_time',
|
||||
direction: 'descending',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const databaseId = $.step.parameters.databaseId as string;
|
||||
const path = `/v1/databases/${databaseId}/query`;
|
||||
do {
|
||||
const response = await $.http.post<ResponseData>(path, payload);
|
||||
|
||||
payload.start_cursor = response.data.next_cursor;
|
||||
|
||||
for (const databaseItem of response.data.results) {
|
||||
$.pushTriggerItem({
|
||||
raw: databaseItem,
|
||||
meta: {
|
||||
internalId: `${databaseItem.id}-${databaseItem.last_edited_time}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
} while (payload.start_cursor);
|
||||
};
|
||||
|
||||
export default updatedDatabaseItems;
|
@@ -1,4 +1,3 @@
|
||||
import qs from 'qs';
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
export default defineAction({
|
||||
@@ -19,7 +18,8 @@ export default defineAction({
|
||||
key: 'message',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Message body to be sent, set to triggered if empty or not passed.',
|
||||
description:
|
||||
'Message body to be sent, set to triggered if empty or not passed.',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
@@ -67,22 +67,15 @@ export default defineAction({
|
||||
key: 'delay',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
description: 'Timestamp or duration for delayed delivery. For example, 30min or 9am.',
|
||||
description:
|
||||
'Timestamp or duration for delayed delivery. For example, 30min or 9am.',
|
||||
variables: true,
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const {
|
||||
topic,
|
||||
message,
|
||||
title,
|
||||
email,
|
||||
click,
|
||||
attach,
|
||||
filename,
|
||||
delay
|
||||
} = $.step.parameters;
|
||||
const { topic, message, title, email, click, attach, filename, delay } =
|
||||
$.step.parameters;
|
||||
const payload = {
|
||||
topic,
|
||||
message,
|
||||
@@ -91,7 +84,7 @@ export default defineAction({
|
||||
click,
|
||||
attach,
|
||||
filename,
|
||||
delay
|
||||
delay,
|
||||
};
|
||||
|
||||
const response = await $.http.post('/', payload);
|
||||
|
@@ -11,7 +11,7 @@ export default {
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
description: 'Host name of your Odoo Server',
|
||||
description: 'Host name of your Odoo Server (e.g. sub.domain.com without the protocol)',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
@@ -25,6 +25,27 @@ export default {
|
||||
description: 'Port that the host is running on, defaults to 443 (HTTPS)',
|
||||
clickToCopy: false,
|
||||
},
|
||||
{
|
||||
key: 'secure',
|
||||
label: 'Secure',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: 'true',
|
||||
description: 'True if the host communicates via secure protocol.',
|
||||
variables: false,
|
||||
clickToCopy: false,
|
||||
options: [
|
||||
{
|
||||
label: 'True',
|
||||
value: 'true',
|
||||
},
|
||||
{
|
||||
label: 'False',
|
||||
value: 'false',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'databaseName',
|
||||
label: 'Database Name',
|
||||
@@ -40,7 +61,7 @@ export default {
|
||||
key: 'email',
|
||||
label: 'Email Address',
|
||||
type: 'string' as const,
|
||||
requires: true,
|
||||
required: true,
|
||||
readOnly: false,
|
||||
value: null,
|
||||
placeholder: null,
|
||||
|
@@ -32,8 +32,10 @@ export const asyncMethodCall = async <T = number>($: IGlobalVariable, { method,
|
||||
export const getClient = ($: IGlobalVariable, { path = 'common' }) => {
|
||||
const host = $.auth.data.host as string;
|
||||
const port = Number($.auth.data.port as string);
|
||||
const secure = $.auth.data.secure === 'true';
|
||||
const createClientFunction = secure ? xmlrpc.createSecureClient : xmlrpc.createClient;
|
||||
|
||||
return xmlrpc.createClient(
|
||||
return createClientFunction(
|
||||
{
|
||||
host,
|
||||
port,
|
||||
|
@@ -1,10 +1,22 @@
|
||||
import { TBeforeRequest } from '@automatisch/types';
|
||||
import appConfig from '../../../config/app';
|
||||
|
||||
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
|
||||
const screenName = $.auth.data?.screenName as string;
|
||||
if ($.auth.data?.accessToken) {
|
||||
requestConfig.headers.Authorization = `${$.auth.data.tokenType} ${$.auth.data.accessToken}`;
|
||||
}
|
||||
|
||||
if (screenName) {
|
||||
requestConfig.headers[
|
||||
'User-Agent'
|
||||
] = `web:automatisch:${appConfig.version} (by /u/${screenName})`;
|
||||
} else {
|
||||
requestConfig.headers[
|
||||
'User-Agent'
|
||||
] = `web:automatisch:${appConfig.version}`;
|
||||
}
|
||||
|
||||
return requestConfig;
|
||||
};
|
||||
|
||||
|
3
packages/backend/src/apps/removebg/actions/index.ts
Normal file
3
packages/backend/src/apps/removebg/actions/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
import removeImageBackground from './remove-image-background';
|
||||
|
||||
export default [removeImageBackground];
|
@@ -0,0 +1,82 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Remove image background',
|
||||
key: 'removeImageBackground',
|
||||
description:
|
||||
'Removes the background of an image.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Image file',
|
||||
key: 'imageFileB64',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
description: 'Provide a JPG or PNG file in Base64 format, up to 12 MB (see remove.bg/supported-images)',
|
||||
},
|
||||
{
|
||||
label: 'Size',
|
||||
key: 'size',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
value: 'auto',
|
||||
options: [
|
||||
{ label: 'Auto', value: 'auto' },
|
||||
{ label: 'Preview (up to 0.25 megapixels)', value: 'preview' },
|
||||
{ label: 'Full (up to 10 megapixels)', value: 'full' },
|
||||
]
|
||||
},
|
||||
{
|
||||
label: 'Background color',
|
||||
key: 'bgColor',
|
||||
type: 'string' as const,
|
||||
description: 'Adds a solid color background. Can be a hex color code (e.g. 81d4fa, fff) or a color name (e.g. green)',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
label: 'Background image URL',
|
||||
key: 'bgImageUrl',
|
||||
type: 'string' as const,
|
||||
description: 'Adds a background image from a URL.',
|
||||
required: false,
|
||||
},
|
||||
{
|
||||
label: 'Output image format',
|
||||
key: 'outputFormat',
|
||||
type: 'dropdown' as const,
|
||||
description: 'Note: Use PNG to preserve transparency',
|
||||
required: true,
|
||||
value: 'auto',
|
||||
options: [
|
||||
{ label: 'Auto', value: 'auto' },
|
||||
{ label: 'PNG', value: 'png' },
|
||||
{ label: 'JPG', value: 'jpg' },
|
||||
{ label: 'ZIP', value: 'zip' }
|
||||
]
|
||||
}
|
||||
],
|
||||
async run($) {
|
||||
const imageFileB64 = $.step.parameters.imageFileB64 as string;
|
||||
const size = $.step.parameters.size as string;
|
||||
const bgColor = $.step.parameters.bgColor as string;
|
||||
const bgImageUrl = $.step.parameters.bgImageUrl as string;
|
||||
const outputFormat = $.step.parameters.outputFormat as string;
|
||||
|
||||
const body = JSON.stringify({
|
||||
image_file_b64: imageFileB64,
|
||||
size: size,
|
||||
bg_color: bgColor,
|
||||
bg_image_url: bgImageUrl,
|
||||
format: outputFormat
|
||||
});
|
||||
|
||||
const response = await $.http.post('/removebg', body, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
$.setActionItem({ raw: response.data });
|
||||
}
|
||||
});
|
@@ -1,6 +1,7 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import addAuthHeader from './common/add-auth-header';
|
||||
import auth from './auth';
|
||||
import actions from './actions';
|
||||
|
||||
export default defineApp({
|
||||
name: 'Remove.bg',
|
||||
@@ -13,4 +14,5 @@ export default defineApp({
|
||||
primaryColor: '55636c',
|
||||
beforeRequest: [addAuthHeader],
|
||||
auth,
|
||||
actions,
|
||||
});
|
||||
|
@@ -1,4 +1,3 @@
|
||||
import qs from 'qs';
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
export default defineAction({
|
||||
@@ -11,7 +10,8 @@ export default defineAction({
|
||||
key: 'chatId',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
description: 'Unique identifier for the target chat or username of the target channel (in the format @channelusername).',
|
||||
description:
|
||||
'Unique identifier for the target chat or username of the target channel (in the format @channelusername).',
|
||||
variables: true,
|
||||
},
|
||||
{
|
||||
@@ -28,7 +28,8 @@ export default defineAction({
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
value: false,
|
||||
description: 'Sends the message silently. Users will receive a notification with no sound.',
|
||||
description:
|
||||
'Sends the message silently. Users will receive a notification with no sound.',
|
||||
variables: true,
|
||||
options: [
|
||||
{
|
||||
|
102
packages/backend/src/apps/zendesk/actions/create-user/fields.ts
Normal file
102
packages/backend/src/apps/zendesk/actions/create-user/fields.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
export const fields = [
|
||||
{
|
||||
label: 'Name',
|
||||
key: 'name',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
label: 'Email',
|
||||
key: 'email',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
description:
|
||||
'It is essential to be distinctive. Zendesk prohibits the existence of identical users sharing the same email address.',
|
||||
},
|
||||
{
|
||||
label: 'Details',
|
||||
key: 'details',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
label: 'Notes',
|
||||
key: 'notes',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description:
|
||||
'Within this field, you have the capability to save any remarks or comments you may have concerning the user.',
|
||||
},
|
||||
{
|
||||
label: 'Phone',
|
||||
key: 'phone',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description:
|
||||
"The user's contact number should be entered in the following format: +1 (555) 123-4567.",
|
||||
},
|
||||
{
|
||||
label: 'Tags',
|
||||
key: 'tags',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'A comma separated list of tags.',
|
||||
},
|
||||
{
|
||||
label: 'Role',
|
||||
key: 'role',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description:
|
||||
"It can take on one of the designated roles: 'end-user', 'agent', or 'admin'. If a different value is set or none is specified, the default is 'end-user.'",
|
||||
},
|
||||
{
|
||||
label: 'Organization',
|
||||
key: 'organizationId',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'Assign this user to a specific organization.',
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listOrganizations',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'External Id',
|
||||
key: 'externalId',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description:
|
||||
'An exclusive external identifier; you can utilize this to link organizations with an external record.',
|
||||
},
|
||||
{
|
||||
label: 'Verified',
|
||||
key: 'verified',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
description:
|
||||
"Specify if you can verify that the user's assertion of their identity is accurate.",
|
||||
variables: true,
|
||||
options: [
|
||||
{ label: 'True', value: 'true' },
|
||||
{ label: 'False', value: 'false' },
|
||||
],
|
||||
},
|
||||
];
|
@@ -0,0 +1,53 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
import { fields } from './fields';
|
||||
|
||||
type Payload = {
|
||||
user: IJSONObject;
|
||||
};
|
||||
|
||||
export default defineAction({
|
||||
name: 'Create user',
|
||||
key: 'createUser',
|
||||
description: 'Creates a new user.',
|
||||
arguments: fields,
|
||||
|
||||
async run($) {
|
||||
const {
|
||||
name,
|
||||
email,
|
||||
details,
|
||||
notes,
|
||||
phone,
|
||||
role,
|
||||
organizationId,
|
||||
externalId,
|
||||
verified,
|
||||
} = $.step.parameters;
|
||||
|
||||
const tags = $.step.parameters.tags as string;
|
||||
const formattedTags = tags.split(',');
|
||||
|
||||
const payload: Payload = {
|
||||
user: {
|
||||
name,
|
||||
email,
|
||||
details,
|
||||
notes,
|
||||
phone,
|
||||
organization_id: organizationId,
|
||||
external_id: externalId,
|
||||
verified: verified || 'false',
|
||||
tags: formattedTags,
|
||||
},
|
||||
};
|
||||
|
||||
if (role) {
|
||||
payload.user.role = role;
|
||||
}
|
||||
|
||||
const response = await $.http.post('/api/v2/users', payload);
|
||||
|
||||
$.setActionItem({ raw: response.data });
|
||||
},
|
||||
});
|
@@ -0,0 +1,35 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Delete ticket',
|
||||
key: 'deleteTicket',
|
||||
description: 'Deletes an existing ticket.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Ticket',
|
||||
key: 'ticketId',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
description: 'Select the ticket you want to delete.',
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listFirstPageOfTickets',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const ticketId = $.step.parameters.ticketId;
|
||||
|
||||
const response = await $.http.delete(`/api/v2/tickets/${ticketId}`);
|
||||
|
||||
$.setActionItem({ raw: { data: response.data } });
|
||||
},
|
||||
});
|
@@ -0,0 +1,43 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Delete user',
|
||||
key: 'deleteUser',
|
||||
description: 'Deletes an existing user.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'User',
|
||||
key: 'userId',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
description: 'Select the user you want to modify.',
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listUsers',
|
||||
},
|
||||
{
|
||||
name: 'parameters.showUserRole',
|
||||
value: 'true',
|
||||
},
|
||||
{
|
||||
name: 'parameters.includeAllUsers',
|
||||
value: 'true',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const userId = $.step.parameters.userId;
|
||||
|
||||
const response = await $.http.delete(`/api/v2/users/${userId}`);
|
||||
|
||||
$.setActionItem({ raw: response.data });
|
||||
},
|
||||
});
|
@@ -0,0 +1,32 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Find ticket',
|
||||
key: 'findTicket',
|
||||
description: 'Finds an existing ticket.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'Query',
|
||||
key: 'query',
|
||||
type: 'string' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
description:
|
||||
'Write a search string that specifies the way we will search for the ticket in Zendesk.',
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const query = $.step.parameters.query;
|
||||
|
||||
const params = {
|
||||
query: `type:ticket ${query}`,
|
||||
sort_by: 'created_at',
|
||||
sort_order: 'desc',
|
||||
};
|
||||
|
||||
const response = await $.http.get('/api/v2/search', { params });
|
||||
|
||||
$.setActionItem({ raw: response.data.results[0] });
|
||||
},
|
||||
});
|
@@ -1,3 +1,15 @@
|
||||
import createTicket from './create-ticket';
|
||||
import createUser from './create-user';
|
||||
import deleteTicket from './delete-ticket';
|
||||
import deleteUser from './delete-user';
|
||||
import findTicket from './find-ticket';
|
||||
import updateTicket from './update-ticket';
|
||||
|
||||
export default [createTicket];
|
||||
export default [
|
||||
createTicket,
|
||||
createUser,
|
||||
deleteTicket,
|
||||
deleteUser,
|
||||
findTicket,
|
||||
updateTicket,
|
||||
];
|
||||
|
@@ -0,0 +1,167 @@
|
||||
export const fields = [
|
||||
{
|
||||
label: 'Ticket',
|
||||
key: 'ticketId',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
description: 'Select the ticket you want to change.',
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listFirstPageOfTickets',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Subject',
|
||||
key: 'subject',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
label: 'Assignee',
|
||||
key: 'assigneeId',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description:
|
||||
'Note: An error occurs if the assignee is not in the default group (or the specific group chosen below).',
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listUsers',
|
||||
},
|
||||
{
|
||||
name: 'parameters.showUserRole',
|
||||
value: 'true',
|
||||
},
|
||||
{
|
||||
name: 'parameters.includeAdmins',
|
||||
value: 'true',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Group',
|
||||
key: 'groupId',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'Allocate this ticket to a specific group.',
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listGroups',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'New Status',
|
||||
key: 'status',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: '',
|
||||
options: [
|
||||
{ label: 'New', value: 'new' },
|
||||
{ label: 'Open', value: 'open' },
|
||||
{ label: 'Pending', value: 'pending' },
|
||||
{ label: 'Hold', value: 'hold' },
|
||||
{ label: 'Solved', value: 'solved' },
|
||||
{ label: 'Closed', value: 'closed' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'New comment to add to the ticket',
|
||||
key: 'comment',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: '',
|
||||
},
|
||||
{
|
||||
label: 'Should the first comment be public?',
|
||||
key: 'publicOrNot',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: '',
|
||||
options: [
|
||||
{ label: 'Yes', value: 'yes' },
|
||||
{ label: 'No', value: 'no' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Tags',
|
||||
key: 'tags',
|
||||
type: 'string' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: 'A comma separated list of tags.',
|
||||
},
|
||||
{
|
||||
label: 'Type',
|
||||
key: 'type',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: '',
|
||||
options: [
|
||||
{ label: 'Problem', value: 'problem' },
|
||||
{ label: 'Incident', value: 'incident' },
|
||||
{ label: 'Question', value: 'question' },
|
||||
{ label: 'Task', value: 'task' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Priority',
|
||||
key: 'priority',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: '',
|
||||
options: [
|
||||
{ label: 'Urgent', value: 'urgent' },
|
||||
{ label: 'High', value: 'high' },
|
||||
{ label: 'Normal', value: 'normal' },
|
||||
{ label: 'Low', value: 'low' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Submitter',
|
||||
key: 'submitterId',
|
||||
type: 'dropdown' as const,
|
||||
required: false,
|
||||
variables: true,
|
||||
description: '',
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listUsers',
|
||||
},
|
||||
{
|
||||
name: 'parameters.includeAdmins',
|
||||
value: 'false',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
@@ -0,0 +1,57 @@
|
||||
import defineAction from '../../../../helpers/define-action';
|
||||
import { fields } from './fields';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import omitBy from 'lodash/omitBy';
|
||||
|
||||
export default defineAction({
|
||||
name: 'Update ticket',
|
||||
key: 'updateTicket',
|
||||
description: 'Modify the status of an existing ticket or append comments.',
|
||||
arguments: fields,
|
||||
|
||||
async run($) {
|
||||
const {
|
||||
ticketId,
|
||||
subject,
|
||||
assigneeId,
|
||||
groupId,
|
||||
status,
|
||||
comment,
|
||||
publicOrNot,
|
||||
type,
|
||||
priority,
|
||||
submitterId,
|
||||
} = $.step.parameters;
|
||||
|
||||
const tags = $.step.parameters.tags as string;
|
||||
const formattedTags = tags.split(',');
|
||||
|
||||
const payload = {
|
||||
subject,
|
||||
assignee_id: assigneeId,
|
||||
group_id: groupId,
|
||||
status,
|
||||
comment: {
|
||||
body: comment,
|
||||
public: publicOrNot,
|
||||
},
|
||||
tags: formattedTags,
|
||||
type,
|
||||
priority,
|
||||
submitter_id: submitterId,
|
||||
};
|
||||
|
||||
const fieldsToRemoveIfEmpty = ['group_id', 'status', 'type', 'priority'];
|
||||
|
||||
const filteredPayload = omitBy(
|
||||
payload,
|
||||
(value, key) => fieldsToRemoveIfEmpty.includes(key) && isEmpty(value)
|
||||
);
|
||||
|
||||
const response = await $.http.put(`/api/v2/tickets/${ticketId}`, {
|
||||
ticket: filteredPayload,
|
||||
});
|
||||
|
||||
$.setActionItem({ raw: response.data });
|
||||
},
|
||||
});
|
@@ -1,13 +1,20 @@
|
||||
import listUsers from './list-users';
|
||||
import listBrands from './list-brands';
|
||||
import listFirstPageOfTickets from './list-first-page-of-tickets';
|
||||
import listGroups from './list-groups';
|
||||
import listOrganizations from './list-organizations';
|
||||
import listSharingAgreements from './list-sharing-agreements';
|
||||
import listTicketForms from './list-ticket-forms';
|
||||
import listViews from './list-views';
|
||||
|
||||
export default [
|
||||
listUsers,
|
||||
listBrands,
|
||||
listFirstPageOfTickets,
|
||||
listGroups,
|
||||
listOrganizations,
|
||||
listSharingAgreements,
|
||||
listFirstPageOfTickets,
|
||||
listTicketForms,
|
||||
listViews,
|
||||
];
|
||||
|
@@ -0,0 +1,33 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
export default {
|
||||
name: 'List first page of tickets',
|
||||
key: 'listFirstPageOfTickets',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const tickets: {
|
||||
data: IJSONObject[];
|
||||
} = {
|
||||
data: [],
|
||||
};
|
||||
|
||||
const params = {
|
||||
'page[size]': 100,
|
||||
sort: '-id',
|
||||
};
|
||||
|
||||
const response = await $.http.get('/api/v2/tickets', { params });
|
||||
const allTickets = response.data.tickets;
|
||||
|
||||
if (allTickets?.length) {
|
||||
for (const ticket of allTickets) {
|
||||
tickets.data.push({
|
||||
value: ticket.id,
|
||||
name: ticket.subject,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return tickets;
|
||||
},
|
||||
};
|
@@ -21,7 +21,7 @@ export default {
|
||||
const response = await $.http.get('/api/v2/groups', { params });
|
||||
const allGroups = response?.data?.groups;
|
||||
hasMore = response?.data?.meta?.has_more;
|
||||
params['page[after]'] = response.data.links?.after_cursor;
|
||||
params['page[after]'] = response.data.meta?.after_cursor;
|
||||
|
||||
if (allGroups?.length) {
|
||||
for (const group of allGroups) {
|
||||
|
@@ -0,0 +1,38 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
export default {
|
||||
name: 'List organizations',
|
||||
key: 'listOrganizations',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const organizations: {
|
||||
data: IJSONObject[];
|
||||
} = {
|
||||
data: [],
|
||||
};
|
||||
let hasMore;
|
||||
|
||||
const params = {
|
||||
'page[size]': 100,
|
||||
'page[after]': undefined as unknown as string,
|
||||
};
|
||||
|
||||
do {
|
||||
const response = await $.http.get('/api/v2/organizations', { params });
|
||||
const allOrganizations = response?.data?.organizations;
|
||||
hasMore = response?.data?.meta?.has_more;
|
||||
params['page[after]'] = response.data.meta?.after_cursor;
|
||||
|
||||
if (allOrganizations?.length) {
|
||||
for (const organization of allOrganizations) {
|
||||
organizations.data.push({
|
||||
value: organization.id,
|
||||
name: organization.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
} while (hasMore);
|
||||
|
||||
return organizations;
|
||||
},
|
||||
};
|
@@ -25,7 +25,7 @@ export default {
|
||||
const response = await $.http.get('/api/v2/users', { params });
|
||||
const allUsers = response?.data?.users;
|
||||
hasMore = response?.data?.meta?.has_more;
|
||||
params['page[after]'] = response.data.links?.after_cursor;
|
||||
params['page[after]'] = response.data.meta?.after_cursor;
|
||||
|
||||
if (allUsers?.length) {
|
||||
for (const user of allUsers) {
|
||||
|
@@ -0,0 +1,38 @@
|
||||
import { IGlobalVariable, IJSONObject } from '@automatisch/types';
|
||||
|
||||
export default {
|
||||
name: 'List views',
|
||||
key: 'listViews',
|
||||
|
||||
async run($: IGlobalVariable) {
|
||||
const views: {
|
||||
data: IJSONObject[];
|
||||
} = {
|
||||
data: [],
|
||||
};
|
||||
let hasMore;
|
||||
|
||||
const params = {
|
||||
'page[size]': 100,
|
||||
'page[after]': undefined as unknown as string,
|
||||
};
|
||||
|
||||
do {
|
||||
const response = await $.http.get('/api/v2/views', { params });
|
||||
const allViews = response?.data?.views;
|
||||
hasMore = response?.data?.meta?.has_more;
|
||||
params['page[after]'] = response.data.meta?.after_cursor;
|
||||
|
||||
if (allViews?.length) {
|
||||
for (const view of allViews) {
|
||||
views.data.push({
|
||||
value: view.id,
|
||||
name: view.title,
|
||||
});
|
||||
}
|
||||
}
|
||||
} while (hasMore);
|
||||
|
||||
return views;
|
||||
},
|
||||
};
|
@@ -1,6 +1,7 @@
|
||||
import defineApp from '../../helpers/define-app';
|
||||
import addAuthHeader from './common/add-auth-headers';
|
||||
import auth from './auth';
|
||||
import triggers from './triggers';
|
||||
import actions from './actions';
|
||||
import dynamicData from './dynamic-data';
|
||||
|
||||
@@ -15,6 +16,7 @@ export default defineApp({
|
||||
supportsConnections: true,
|
||||
beforeRequest: [addAuthHeader],
|
||||
auth,
|
||||
triggers,
|
||||
actions,
|
||||
dynamicData,
|
||||
});
|
||||
|
4
packages/backend/src/apps/zendesk/triggers/index.ts
Normal file
4
packages/backend/src/apps/zendesk/triggers/index.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import newTickets from './new-tickets';
|
||||
import newUsers from './new-users';
|
||||
|
||||
export default [newTickets, newUsers];
|
@@ -0,0 +1,59 @@
|
||||
import defineTrigger from '../../../../helpers/define-trigger';
|
||||
|
||||
export default defineTrigger({
|
||||
name: 'New tickets',
|
||||
key: 'newTickets',
|
||||
pollInterval: 15,
|
||||
description: 'Triggers when a new ticket is created in a specific view.',
|
||||
arguments: [
|
||||
{
|
||||
label: 'View',
|
||||
key: 'viewId',
|
||||
type: 'dropdown' as const,
|
||||
required: true,
|
||||
variables: true,
|
||||
source: {
|
||||
type: 'query',
|
||||
name: 'getDynamicData',
|
||||
arguments: [
|
||||
{
|
||||
name: 'key',
|
||||
value: 'listViews',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
async run($) {
|
||||
const viewId = $.step.parameters.viewId;
|
||||
|
||||
const params = {
|
||||
'page[size]': 100,
|
||||
'page[after]': undefined as unknown as string,
|
||||
sort_by: 'nice_id',
|
||||
sort_order: 'desc',
|
||||
};
|
||||
let hasMore;
|
||||
|
||||
do {
|
||||
const response = await $.http.get(`/api/v2/views/${viewId}/tickets`, {
|
||||
params,
|
||||
});
|
||||
const allTickets = response?.data?.tickets;
|
||||
hasMore = response?.data?.meta?.has_more;
|
||||
params['page[after]'] = response.data.meta?.after_cursor;
|
||||
|
||||
if (allTickets?.length) {
|
||||
for (const ticket of allTickets) {
|
||||
$.pushTriggerItem({
|
||||
raw: ticket,
|
||||
meta: {
|
||||
internalId: ticket.id.toString(),
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
} while (hasMore);
|
||||
},
|
||||
});
|
@@ -0,0 +1,83 @@
|
||||
import Crypto from 'crypto';
|
||||
import defineTrigger from '../../../../helpers/define-trigger';
|
||||
|
||||
export default defineTrigger({
|
||||
name: 'New users',
|
||||
key: 'newUsers',
|
||||
type: 'webhook',
|
||||
description: 'Triggers upon the creation of a new user.',
|
||||
|
||||
async run($) {
|
||||
const dataItem = {
|
||||
raw: $.request.body,
|
||||
meta: {
|
||||
internalId: Crypto.randomUUID(),
|
||||
},
|
||||
};
|
||||
|
||||
$.pushTriggerItem(dataItem);
|
||||
},
|
||||
|
||||
async testRun($) {
|
||||
const params = {
|
||||
query: 'type:user',
|
||||
sort_by: 'created_at',
|
||||
sort_order: 'desc',
|
||||
};
|
||||
|
||||
const response = await $.http.get('/api/v2/search', { params });
|
||||
|
||||
const lastUser = response.data.results[0];
|
||||
|
||||
const computedWebhookEvent = {
|
||||
id: Crypto.randomUUID(),
|
||||
time: lastUser.created_at,
|
||||
type: 'zen:event-type:user.created',
|
||||
event: {},
|
||||
detail: {
|
||||
id: lastUser.id,
|
||||
role: lastUser.role,
|
||||
email: lastUser.email,
|
||||
created_at: lastUser.created_at,
|
||||
updated_at: lastUser.updated_at,
|
||||
external_id: lastUser.external_id,
|
||||
organization_id: lastUser.organization_id,
|
||||
default_group_id: lastUser.default_group_id,
|
||||
},
|
||||
subject: `zen:user:${lastUser.id}`,
|
||||
account_id: '',
|
||||
zendesk_event_version: '2022-11-06',
|
||||
};
|
||||
|
||||
const dataItem = {
|
||||
raw: computedWebhookEvent,
|
||||
meta: {
|
||||
internalId: computedWebhookEvent.id,
|
||||
},
|
||||
};
|
||||
|
||||
$.pushTriggerItem(dataItem);
|
||||
},
|
||||
|
||||
async registerHook($) {
|
||||
const payload = {
|
||||
webhook: {
|
||||
name: `Flow ID: ${$.flow.id}`,
|
||||
status: 'active',
|
||||
subscriptions: ['zen:event-type:user.created'],
|
||||
endpoint: $.webhookUrl,
|
||||
http_method: 'POST',
|
||||
request_format: 'json',
|
||||
},
|
||||
};
|
||||
|
||||
const response = await $.http.post('/api/v2/webhooks', payload);
|
||||
const id = response.data.webhook.id;
|
||||
|
||||
await $.flow.setRemoteWebhookId(id);
|
||||
},
|
||||
|
||||
async unregisterHook($) {
|
||||
await $.http.delete(`/api/v2/webhooks/${$.flow.remoteWebhookId}`);
|
||||
},
|
||||
});
|
@@ -1,6 +1,7 @@
|
||||
import { URL } from 'node:url';
|
||||
import * as dotenv from 'dotenv';
|
||||
import path from 'path';
|
||||
import process from 'node:process';
|
||||
|
||||
if (process.env.APP_ENV === 'test') {
|
||||
dotenv.config({ path: path.resolve(__dirname, '../../.env.test') });
|
||||
@@ -8,56 +9,6 @@ if (process.env.APP_ENV === 'test') {
|
||||
dotenv.config();
|
||||
}
|
||||
|
||||
type AppConfig = {
|
||||
host: string;
|
||||
protocol: string;
|
||||
port: string;
|
||||
webAppUrl: string;
|
||||
webhookUrl: string;
|
||||
appEnv: string;
|
||||
logLevel: string;
|
||||
isDev: boolean;
|
||||
isTest: boolean;
|
||||
isProd: boolean;
|
||||
postgresDatabase: string;
|
||||
postgresSchema: string;
|
||||
postgresPort: number;
|
||||
postgresHost: string;
|
||||
postgresUsername: string;
|
||||
postgresPassword?: string;
|
||||
version: string;
|
||||
postgresEnableSsl: boolean;
|
||||
baseUrl: string;
|
||||
encryptionKey: string;
|
||||
webhookSecretKey: string;
|
||||
appSecretKey: string;
|
||||
serveWebAppSeparately: boolean;
|
||||
redisHost: string;
|
||||
redisPort: number;
|
||||
redisUsername: string;
|
||||
redisPassword: string;
|
||||
redisTls: boolean;
|
||||
enableBullMQDashboard: boolean;
|
||||
bullMQDashboardUsername: string;
|
||||
bullMQDashboardPassword: string;
|
||||
telemetryEnabled: boolean;
|
||||
requestBodySizeLimit: string;
|
||||
smtpHost: string;
|
||||
smtpPort: number;
|
||||
smtpSecure: boolean;
|
||||
smtpUser: string;
|
||||
smtpPassword: string;
|
||||
fromEmail: string;
|
||||
isCloud: boolean;
|
||||
isSelfHosted: boolean;
|
||||
paddleVendorId: number;
|
||||
paddleVendorAuthCode: string;
|
||||
paddlePublicKey: string;
|
||||
licenseKey: string;
|
||||
sentryDsn: string;
|
||||
CI: boolean;
|
||||
};
|
||||
|
||||
const host = process.env.HOST || 'localhost';
|
||||
const protocol = process.env.PROTOCOL || 'http';
|
||||
const port = process.env.PORT || '3000';
|
||||
@@ -84,7 +35,7 @@ webhookUrl = webhookUrl.substring(0, webhookUrl.length - 1);
|
||||
|
||||
const appEnv = process.env.APP_ENV || 'development';
|
||||
|
||||
const appConfig: AppConfig = {
|
||||
const appConfig = {
|
||||
host,
|
||||
protocol,
|
||||
port,
|
||||
@@ -127,6 +78,7 @@ const appConfig: AppConfig = {
|
||||
fromEmail: process.env.FROM_EMAIL,
|
||||
isCloud: process.env.AUTOMATISCH_CLOUD === 'true',
|
||||
isSelfHosted: process.env.AUTOMATISCH_CLOUD !== 'true',
|
||||
isMation: process.env.MATION === 'true',
|
||||
paddleVendorId: Number(process.env.PADDLE_VENDOR_ID),
|
||||
paddleVendorAuthCode: process.env.PADDLE_VENDOR_AUTH_CODE,
|
||||
paddlePublicKey: process.env.PADDLE_PUBLIC_KEY,
|
@@ -4,11 +4,10 @@ import process from 'process';
|
||||
import pg from 'pg';
|
||||
pg.types.setTypeParser(20, 'text', parseInt);
|
||||
import knex from 'knex';
|
||||
import type { Knex } from 'knex';
|
||||
import knexConfig from '../../knexfile';
|
||||
import logger from '../helpers/logger';
|
||||
|
||||
export const client: Knex = knex(knexConfig);
|
||||
export const client = knex(knexConfig);
|
||||
|
||||
const CONNECTION_REFUSED = 'ECONNREFUSED';
|
||||
|
@@ -1,16 +1,6 @@
|
||||
import appConfig from './app';
|
||||
|
||||
type TRedisConfig = {
|
||||
host: string,
|
||||
port: number,
|
||||
username?: string,
|
||||
password?: string,
|
||||
tls?: Record<string, unknown>,
|
||||
enableReadyCheck?: boolean,
|
||||
enableOfflineQueue: boolean,
|
||||
}
|
||||
|
||||
const redisConfig: TRedisConfig = {
|
||||
const redisConfig = {
|
||||
host: appConfig.redisHost,
|
||||
port: appConfig.redisPort,
|
||||
username: appConfig.redisUsername,
|
@@ -1,11 +1,9 @@
|
||||
import { Response } from 'express';
|
||||
import { IJSONObject, IRequest } from '@automatisch/types';
|
||||
import crypto from 'crypto';
|
||||
import { serialize } from 'php-serialize';
|
||||
import Billing from '../../helpers/billing/index.ee';
|
||||
import appConfig from '../../config/app';
|
||||
|
||||
export default async (request: IRequest, response: Response) => {
|
||||
export default async (request, response) => {
|
||||
if (!verifyWebhook(request)) {
|
||||
return response.sendStatus(401);
|
||||
}
|
||||
@@ -23,14 +21,14 @@ export default async (request: IRequest, response: Response) => {
|
||||
return response.sendStatus(200);
|
||||
};
|
||||
|
||||
const verifyWebhook = (request: IRequest) => {
|
||||
const verifyWebhook = (request) => {
|
||||
const signature = request.body.p_signature;
|
||||
|
||||
const keys = Object.keys(request.body)
|
||||
.filter((key) => key !== 'p_signature')
|
||||
.sort();
|
||||
|
||||
const sorted: IJSONObject = {};
|
||||
const sorted = {};
|
||||
keys.forEach((key) => {
|
||||
sorted[key] = request.body[key];
|
||||
});
|
@@ -1,12 +1,10 @@
|
||||
import path from 'node:path';
|
||||
import { Response } from 'express';
|
||||
import { IRequest } from '@automatisch/types';
|
||||
|
||||
import Connection from '../../models/connection';
|
||||
import logger from '../../helpers/logger';
|
||||
import handler from '../../helpers/webhook-handler';
|
||||
|
||||
export default async (request: IRequest, response: Response) => {
|
||||
export default async (request, response) => {
|
||||
const computedRequestPayload = {
|
||||
headers: request.headers,
|
||||
body: request.body,
|
||||
@@ -22,7 +20,7 @@ export default async (request: IRequest, response: Response) => {
|
||||
.findById(connectionId)
|
||||
.throwIfNotFound();
|
||||
|
||||
if (!await connection.verifyWebhook(request)) {
|
||||
if (!(await connection.verifyWebhook(request))) {
|
||||
return response.sendStatus(401);
|
||||
}
|
||||
|
@@ -1,11 +1,8 @@
|
||||
import { Response } from 'express';
|
||||
import { IRequest } from '@automatisch/types';
|
||||
|
||||
import Flow from '../../models/flow';
|
||||
import logger from '../../helpers/logger';
|
||||
import handler from '../../helpers/webhook-handler';
|
||||
|
||||
export default async (request: IRequest, response: Response) => {
|
||||
export default async (request, response) => {
|
||||
const computedRequestPayload = {
|
||||
headers: request.headers,
|
||||
body: request.body,
|
@@ -1,22 +1,22 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
|
||||
export default class BaseError extends Error {
|
||||
details = {};
|
||||
statusCode?: number;
|
||||
|
||||
constructor(error?: string | IJSONObject) {
|
||||
let computedError: Record<string, unknown>;
|
||||
constructor(error) {
|
||||
let computedError;
|
||||
|
||||
try {
|
||||
computedError = JSON.parse(error as string);
|
||||
computedError = JSON.parse(error);
|
||||
} catch {
|
||||
computedError = (typeof error === 'string' || Array.isArray(error)) ? { error } : error;
|
||||
computedError =
|
||||
typeof error === 'string' || Array.isArray(error) ? { error } : error;
|
||||
}
|
||||
|
||||
let computedMessage: string;
|
||||
let computedMessage;
|
||||
|
||||
try {
|
||||
// challenge to input to see if it is stringified JSON
|
||||
JSON.parse(error as string);
|
||||
computedMessage = error as string;
|
||||
JSON.parse(error);
|
||||
computedMessage = error;
|
||||
} catch {
|
||||
if (typeof error === 'string') {
|
||||
computedMessage = error;
|
10
packages/backend/src/errors/generate-auth-url.js
Normal file
10
packages/backend/src/errors/generate-auth-url.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import BaseError from './base';
|
||||
|
||||
export default class GenerateAuthUrlError extends BaseError {
|
||||
constructor(error) {
|
||||
const computedError = error.response?.data || error.message;
|
||||
super(computedError);
|
||||
|
||||
this.message = `Error occured while creating authorization URL!`;
|
||||
}
|
||||
}
|
@@ -1,14 +0,0 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import BaseError from './base';
|
||||
|
||||
export default class GenerateAuthUrlError extends BaseError {
|
||||
constructor(error: IJSONObject) {
|
||||
const computedError =
|
||||
((error.response as IJSONObject)?.data as IJSONObject) ||
|
||||
(error.message as string);
|
||||
|
||||
super(computedError);
|
||||
|
||||
this.message = `Error occured while creating authorization URL!`;
|
||||
}
|
||||
}
|
10
packages/backend/src/errors/http.js
Normal file
10
packages/backend/src/errors/http.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import BaseError from './base';
|
||||
|
||||
export default class HttpError extends BaseError {
|
||||
constructor(error) {
|
||||
const computedError = error.response?.data || error.message;
|
||||
super(computedError);
|
||||
|
||||
this.response = error.response;
|
||||
}
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
import type { AxiosResponse, AxiosError } from 'axios';
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import BaseError from './base';
|
||||
|
||||
export default class HttpError extends BaseError {
|
||||
response: AxiosResponse;
|
||||
|
||||
constructor(error: AxiosError) {
|
||||
const computedError =
|
||||
error.response?.data as IJSONObject ||
|
||||
error.message as string;
|
||||
|
||||
super(computedError);
|
||||
|
||||
this.response = error.response;
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
import AppConfig from '../../models/app-config';
|
||||
|
||||
const createAppAuthClient = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const appConfig = await AppConfig.query()
|
||||
.findById(params.input.appConfigId)
|
||||
.throwIfNotFound();
|
||||
|
||||
const appAuthClient = await appConfig
|
||||
.$relatedQuery('appAuthClients')
|
||||
.insert(params.input);
|
||||
|
||||
return appAuthClient;
|
||||
};
|
||||
|
||||
export default createAppAuthClient;
|
@@ -1,35 +0,0 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
appConfigId: string;
|
||||
name: string;
|
||||
formattedAuthDefaults?: IJSONObject;
|
||||
active?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const createAppAuthClient = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.findById(params.input.appConfigId)
|
||||
.throwIfNotFound();
|
||||
|
||||
const appAuthClient = await appConfig
|
||||
.$relatedQuery('appAuthClients')
|
||||
.insert(
|
||||
params.input
|
||||
);
|
||||
|
||||
return appAuthClient;
|
||||
};
|
||||
|
||||
export default createAppAuthClient;
|
@@ -0,0 +1,18 @@
|
||||
import App from '../../models/app';
|
||||
import AppConfig from '../../models/app-config';
|
||||
|
||||
const createAppConfig = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const key = params.input.key;
|
||||
|
||||
const app = await App.findOneByKey(key);
|
||||
|
||||
if (!app) throw new Error('The app cannot be found!');
|
||||
|
||||
const appConfig = await AppConfig.query().insert(params.input);
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default createAppConfig;
|
@@ -1,36 +0,0 @@
|
||||
import App from '../../models/app';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
key: string;
|
||||
allowCustomConnection?: boolean;
|
||||
shared?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const createAppConfig = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const key = params.input.key;
|
||||
|
||||
const app = await App.findOneByKey(key);
|
||||
|
||||
if (!app) throw new Error('The app cannot be found!');
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.insert(
|
||||
params.input
|
||||
);
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default createAppConfig;
|
@@ -1,21 +1,7 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import App from '../../models/app';
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
key: string;
|
||||
appAuthClientId: string;
|
||||
formattedData: IJSONObject;
|
||||
};
|
||||
};
|
||||
|
||||
const createConnection = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const createConnection = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Connection');
|
||||
|
||||
const { key, appAuthClientId } = params.input;
|
||||
@@ -26,16 +12,20 @@ const createConnection = async (
|
||||
|
||||
let formattedData = params.input.formattedData;
|
||||
if (appConfig) {
|
||||
if (appConfig.disabled) throw new Error('This application has been disabled for new connections!');
|
||||
if (appConfig.disabled)
|
||||
throw new Error(
|
||||
'This application has been disabled for new connections!'
|
||||
);
|
||||
|
||||
if (!appConfig.allowCustomConnection && formattedData) throw new Error(`Custom connections cannot be created for ${app.name}!`);
|
||||
if (!appConfig.allowCustomConnection && formattedData)
|
||||
throw new Error(`Custom connections cannot be created for ${app.name}!`);
|
||||
|
||||
if (appConfig.shared && !formattedData) {
|
||||
const authClient = await appConfig
|
||||
.$relatedQuery('appAuthClients')
|
||||
.findById(appAuthClientId)
|
||||
.where({
|
||||
active: true
|
||||
active: true,
|
||||
})
|
||||
.throwIfNotFound();
|
||||
|
||||
@@ -43,8 +33,7 @@ const createConnection = async (
|
||||
}
|
||||
}
|
||||
|
||||
const createdConnection = await context
|
||||
.currentUser
|
||||
const createdConnection = await context.currentUser
|
||||
.$relatedQuery('connections')
|
||||
.insert({
|
||||
key,
|
@@ -1,19 +1,7 @@
|
||||
import App from '../../models/app';
|
||||
import Step from '../../models/step';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
triggerAppKey: string;
|
||||
connectionId: string;
|
||||
};
|
||||
};
|
||||
|
||||
const createFlow = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const createFlow = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Flow');
|
||||
|
||||
const connectionId = params?.input?.connectionId;
|
29
packages/backend/src/graphql/mutations/create-role.ee.js
Normal file
29
packages/backend/src/graphql/mutations/create-role.ee.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import kebabCase from 'lodash/kebabCase';
|
||||
import Role from '../../models/role';
|
||||
|
||||
const createRole = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Role');
|
||||
|
||||
const { name, description, permissions } = params.input;
|
||||
const key = kebabCase(name);
|
||||
|
||||
const existingRole = await Role.query().findOne({ key });
|
||||
|
||||
if (existingRole) {
|
||||
throw new Error('Role already exists!');
|
||||
}
|
||||
|
||||
return await Role.query()
|
||||
.insertGraph(
|
||||
{
|
||||
key,
|
||||
name,
|
||||
description,
|
||||
permissions,
|
||||
},
|
||||
{ relate: ['permissions'] }
|
||||
)
|
||||
.returning('*');
|
||||
};
|
||||
|
||||
export default createRole;
|
@@ -1,34 +0,0 @@
|
||||
import kebabCase from 'lodash/kebabCase';
|
||||
import Permission from '../../models/permission';
|
||||
import Role from '../../models/role';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
name: string;
|
||||
description: string;
|
||||
permissions: Permission[];
|
||||
};
|
||||
};
|
||||
|
||||
const createRole = async (_parent: unknown, params: Params, context: Context) => {
|
||||
context.currentUser.can('create', 'Role');
|
||||
|
||||
const { name, description, permissions } = params.input;
|
||||
const key = kebabCase(name);
|
||||
|
||||
const existingRole = await Role.query().findOne({ key });
|
||||
|
||||
if (existingRole) {
|
||||
throw new Error('Role already exists!');
|
||||
}
|
||||
|
||||
return await Role.query().insertGraph({
|
||||
key,
|
||||
name,
|
||||
description,
|
||||
permissions,
|
||||
}, { relate: ['permissions'] }).returning('*');
|
||||
};
|
||||
|
||||
export default createRole;
|
@@ -1,28 +1,7 @@
|
||||
import App from '../../models/app';
|
||||
import Flow from '../../models/flow';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
key: string;
|
||||
appKey: string;
|
||||
flow: {
|
||||
id: string;
|
||||
};
|
||||
connection: {
|
||||
id: string;
|
||||
};
|
||||
previousStep: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const createStep = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const createStep = async (_parent, params, context) => {
|
||||
const conditions = context.currentUser.can('update', 'Flow');
|
||||
const userFlows = context.currentUser.$relatedQuery('flows');
|
||||
const allFlows = Flow.query();
|
@@ -1,23 +1,7 @@
|
||||
import User from '../../models/user';
|
||||
import Role from '../../models/role';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
fullName: string;
|
||||
email: string;
|
||||
password: string;
|
||||
role: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const createUser = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const createUser = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'User');
|
||||
|
||||
const { fullName, email, password } = params.input;
|
||||
@@ -30,7 +14,7 @@ const createUser = async (
|
||||
throw new Error('User already exists!');
|
||||
}
|
||||
|
||||
const userPayload: Partial<User> = {
|
||||
const userPayload = {
|
||||
fullName,
|
||||
email,
|
||||
password,
|
@@ -1,21 +1,9 @@
|
||||
import Context from '../../types/express/context';
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteAppAuthClient = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteAppAuthClient = async (_parent, params, context) => {
|
||||
context.currentUser.can('delete', 'App');
|
||||
|
||||
await AppAuthClient
|
||||
.query()
|
||||
await AppAuthClient.query()
|
||||
.delete()
|
||||
.findOne({
|
||||
id: params.input.id,
|
@@ -1,16 +1,4 @@
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteConnection = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteConnection = async (_parent, params, context) => {
|
||||
context.currentUser.can('delete', 'Connection');
|
||||
|
||||
await context.currentUser
|
@@ -1,17 +1,11 @@
|
||||
import { Duration } from 'luxon';
|
||||
import Context from '../../types/express/context';
|
||||
import deleteUserQueue from '../../queues/delete-user.ee';
|
||||
import flowQueue from '../../queues/flow';
|
||||
import Flow from '../../models/flow';
|
||||
import Execution from '../../models/execution';
|
||||
import ExecutionStep from '../../models/execution-step';
|
||||
import appConfig from '../../config/app';
|
||||
|
||||
const deleteCurrentUser = async (
|
||||
_parent: unknown,
|
||||
params: never,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteCurrentUser = async (_parent, params, context) => {
|
||||
const id = context.currentUser.id;
|
||||
|
||||
const flows = await context.currentUser.$relatedQuery('flows').where({
|
||||
@@ -32,7 +26,7 @@ const deleteCurrentUser = async (
|
||||
await context.currentUser
|
||||
.$relatedQuery('executions')
|
||||
.select('executions.id')
|
||||
).map((execution: Execution) => execution.id);
|
||||
).map((execution) => execution.id);
|
||||
const flowIds = flows.map((flow) => flow.id);
|
||||
|
||||
await ExecutionStep.query().delete().whereIn('execution_id', executionIds);
|
@@ -1,21 +1,9 @@
|
||||
import Context from '../../types/express/context';
|
||||
import Flow from '../../models/flow';
|
||||
import Execution from '../../models/execution';
|
||||
import ExecutionStep from '../../models/execution-step';
|
||||
import globalVariable from '../../helpers/global-variable';
|
||||
import logger from '../../helpers/logger';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteFlow = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteFlow = async (_parent, params, context) => {
|
||||
const conditions = context.currentUser.can('delete', 'Flow');
|
||||
const isCreator = conditions.isCreator;
|
||||
const allFlows = Flow.query();
|
||||
@@ -43,13 +31,15 @@ const deleteFlow = async (
|
||||
await trigger.unregisterHook($);
|
||||
} catch (error) {
|
||||
// suppress error as the remote resource might have been already deleted
|
||||
logger.debug(`Failed to unregister webhook for flow ${flow.id}: ${error.message}`);
|
||||
logger.debug(
|
||||
`Failed to unregister webhook for flow ${flow.id}: ${error.message}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const executionIds = (
|
||||
await flow.$relatedQuery('executions').select('executions.id')
|
||||
).map((execution: Execution) => execution.id);
|
||||
).map((execution) => execution.id);
|
||||
|
||||
await ExecutionStep.query().delete().whereIn('execution_id', executionIds);
|
||||
|
@@ -1,18 +1,7 @@
|
||||
import Role from '../../models/role';
|
||||
import SamlAuthProvider from '../../models/saml-auth-provider.ee';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteRole = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteRole = async (_parent, params, context) => {
|
||||
context.currentUser.can('delete', 'Role');
|
||||
|
||||
const role = await Role.query().findById(params.input.id).throwIfNotFound();
|
@@ -1,16 +1,4 @@
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteStep = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteStep = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'Flow');
|
||||
|
||||
const step = await context.currentUser
|
@@ -1,19 +1,8 @@
|
||||
import { Duration } from 'luxon';
|
||||
import Context from '../../types/express/context';
|
||||
import User from '../../models/user';
|
||||
import deleteUserQueue from '../../queues/delete-user.ee';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const deleteUser = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const deleteUser = async (_parent, params, context) => {
|
||||
context.currentUser.can('delete', 'User');
|
||||
|
||||
const id = params.input.id;
|
||||
@@ -24,7 +13,7 @@ const deleteUser = async (
|
||||
const jobPayload = { id };
|
||||
const millisecondsFor30Days = Duration.fromObject({ days: 30 }).toMillis();
|
||||
const jobOptions = {
|
||||
delay: millisecondsFor30Days
|
||||
delay: millisecondsFor30Days,
|
||||
};
|
||||
|
||||
await deleteUserQueue.add(jobName, jobPayload, jobOptions);
|
@@ -1,15 +1,4 @@
|
||||
import Context from '../../types/express/context';
|
||||
import Step from '../../models/step';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
type NewStepIds = Record<string, string>;
|
||||
|
||||
function updateStepId(value: string, newStepIds: NewStepIds) {
|
||||
function updateStepId(value, newStepIds) {
|
||||
let newValue = value;
|
||||
|
||||
const stepIdEntries = Object.entries(newStepIds);
|
||||
@@ -24,9 +13,9 @@ function updateStepId(value: string, newStepIds: NewStepIds) {
|
||||
return newValue;
|
||||
}
|
||||
|
||||
function updateStepVariables(parameters: Step['parameters'], newStepIds: NewStepIds): Step['parameters'] {
|
||||
function updateStepVariables(parameters, newStepIds) {
|
||||
const entries = Object.entries(parameters);
|
||||
return entries.reduce((result, [key, value]: [string, unknown]) => {
|
||||
return entries.reduce((result, [key, value]) => {
|
||||
if (typeof value === 'string') {
|
||||
return {
|
||||
...result,
|
||||
@@ -37,7 +26,7 @@ function updateStepVariables(parameters: Step['parameters'], newStepIds: NewStep
|
||||
if (Array.isArray(value)) {
|
||||
return {
|
||||
...result,
|
||||
[key]: value.map(item => updateStepVariables(item, newStepIds)),
|
||||
[key]: value.map((item) => updateStepVariables(item, newStepIds)),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -48,11 +37,7 @@ function updateStepVariables(parameters: Step['parameters'], newStepIds: NewStep
|
||||
}, {});
|
||||
}
|
||||
|
||||
const duplicateFlow = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const duplicateFlow = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Flow');
|
||||
|
||||
const flow = await context.currentUser
|
||||
@@ -69,17 +54,16 @@ const duplicateFlow = async (
|
||||
active: false,
|
||||
});
|
||||
|
||||
const newStepIds: NewStepIds = {};
|
||||
const newStepIds = {};
|
||||
for (const step of flow.steps) {
|
||||
const duplicatedStep = await duplicatedFlow.$relatedQuery('steps')
|
||||
.insert({
|
||||
key: step.key,
|
||||
appKey: step.appKey,
|
||||
type: step.type,
|
||||
connectionId: step.connectionId,
|
||||
position: step.position,
|
||||
parameters: updateStepVariables(step.parameters, newStepIds),
|
||||
});
|
||||
const duplicatedStep = await duplicatedFlow.$relatedQuery('steps').insert({
|
||||
key: step.key,
|
||||
appKey: step.appKey,
|
||||
type: step.type,
|
||||
connectionId: step.connectionId,
|
||||
position: step.position,
|
||||
parameters: updateStepVariables(step.parameters, newStepIds),
|
||||
});
|
||||
|
||||
if (duplicatedStep.isTrigger) {
|
||||
await duplicatedStep.updateWebhookUrl();
|
@@ -1,18 +1,7 @@
|
||||
import Context from '../../types/express/context';
|
||||
import testRun from '../../services/test-run';
|
||||
import Step from '../../models/step';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
stepId: string;
|
||||
};
|
||||
};
|
||||
|
||||
const executeFlow = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const executeFlow = async (_parent, params, context) => {
|
||||
const conditions = context.currentUser.can('update', 'Flow');
|
||||
const isCreator = conditions.isCreator;
|
||||
const allSteps = Step.query();
|
||||
@@ -21,10 +10,7 @@ const executeFlow = async (
|
||||
|
||||
const { stepId } = params.input;
|
||||
|
||||
const untilStep = await baseQuery
|
||||
.clone()
|
||||
.findById(stepId)
|
||||
.throwIfNotFound();
|
||||
const untilStep = await baseQuery.clone().findById(stepId).throwIfNotFound();
|
||||
|
||||
const { executionStep } = await testRun({ stepId });
|
||||
|
@@ -6,13 +6,7 @@ import {
|
||||
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||
} from '../../helpers/remove-job-configuration';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
email: string;
|
||||
};
|
||||
};
|
||||
|
||||
const forgotPassword = async (_parent: unknown, params: Params) => {
|
||||
const forgotPassword = async (_parent, params) => {
|
||||
const { email } = params.input;
|
||||
|
||||
const user = await User.query().findOne({ email: email.toLowerCase() });
|
@@ -1,18 +1,7 @@
|
||||
import Context from '../../types/express/context';
|
||||
import globalVariable from '../../helpers/global-variable';
|
||||
import App from '../../models/app';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const generateAuthUrl = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const generateAuthUrl = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Connection');
|
||||
|
||||
const connection = await context.currentUser
|
@@ -1,14 +1,7 @@
|
||||
import User from '../../models/user';
|
||||
import createAuthTokenByUserId from '../../helpers/create-auth-token-by-user-id';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
};
|
||||
|
||||
const login = async (_parent: unknown, params: Params) => {
|
||||
const login = async (_parent, params) => {
|
||||
const user = await User.query().findOne({
|
||||
email: params.input.email.toLowerCase(),
|
||||
});
|
@@ -1,15 +1,7 @@
|
||||
import User from '../../models/user';
|
||||
import Role from '../../models/role';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
fullName: string;
|
||||
email: string;
|
||||
password: string;
|
||||
};
|
||||
};
|
||||
|
||||
const registerUser = async (_parent: unknown, params: Params) => {
|
||||
const registerUser = async (_parent, params) => {
|
||||
const { fullName, email, password } = params.input;
|
||||
|
||||
const existingUser = await User.query().findOne({
|
@@ -1,16 +1,4 @@
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
|
||||
const resetConnection = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const resetConnection = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Connection');
|
||||
|
||||
let connection = await context.currentUser
|
@@ -1,13 +1,6 @@
|
||||
import User from '../../models/user';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
token: string;
|
||||
password: string;
|
||||
};
|
||||
};
|
||||
|
||||
const resetPassword = async (_parent: unknown, params: Params) => {
|
||||
const resetPassword = async (_parent, params) => {
|
||||
const { token, password } = params.input;
|
||||
|
||||
if (!token) {
|
@@ -0,0 +1,17 @@
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
|
||||
const updateAppAuthClient = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const { id, ...appAuthClientData } = params.input;
|
||||
|
||||
const appAuthClient = await AppAuthClient.query()
|
||||
.findById(id)
|
||||
.throwIfNotFound();
|
||||
|
||||
await appAuthClient.$query().patch(appAuthClientData);
|
||||
|
||||
return appAuthClient;
|
||||
};
|
||||
|
||||
export default updateAppAuthClient;
|
@@ -1,38 +0,0 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
name: string;
|
||||
formattedAuthDefaults?: IJSONObject;
|
||||
active?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const updateAppAuthClient = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const {
|
||||
id,
|
||||
...appAuthClientData
|
||||
} = params.input;
|
||||
|
||||
const appAuthClient = await AppAuthClient
|
||||
.query()
|
||||
.findById(id)
|
||||
.throwIfNotFound();
|
||||
|
||||
await appAuthClient
|
||||
.$query()
|
||||
.patch(appAuthClientData);
|
||||
|
||||
return appAuthClient;
|
||||
};
|
||||
|
||||
export default updateAppAuthClient;
|
@@ -0,0 +1,15 @@
|
||||
import AppConfig from '../../models/app-config';
|
||||
|
||||
const updateAppConfig = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const { id, ...appConfigToUpdate } = params.input;
|
||||
|
||||
const appConfig = await AppConfig.query().findById(id).throwIfNotFound();
|
||||
|
||||
await appConfig.$query().patch(appConfigToUpdate);
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default updateAppConfig;
|
@@ -1,39 +0,0 @@
|
||||
import AppConfig from '../../models/app-config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
allowCustomConnection?: boolean;
|
||||
shared?: boolean;
|
||||
disabled?: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const updateAppConfig = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('update', 'App');
|
||||
|
||||
const {
|
||||
id,
|
||||
...appConfigToUpdate
|
||||
} = params.input;
|
||||
|
||||
const appConfig = await AppConfig
|
||||
.query()
|
||||
.findById(id)
|
||||
.throwIfNotFound();
|
||||
|
||||
await appConfig
|
||||
.$query()
|
||||
.patch(
|
||||
appConfigToUpdate
|
||||
);
|
||||
|
||||
return appConfig;
|
||||
};
|
||||
|
||||
export default updateAppConfig;
|
@@ -1,18 +1,6 @@
|
||||
import type { IJSONValue } from '@automatisch/types';
|
||||
import Config from '../../models/config';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
[index: string]: IJSONValue;
|
||||
};
|
||||
};
|
||||
|
||||
const updateConfig = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const updateConfig = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'Config');
|
||||
|
||||
const config = params.input;
|
@@ -1,20 +1,6 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import Context from '../../types/express/context';
|
||||
import AppAuthClient from '../../models/app-auth-client';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
formattedData?: IJSONObject;
|
||||
appAuthClientId?: string;
|
||||
};
|
||||
};
|
||||
|
||||
const updateConnection = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const updateConnection = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'Connection');
|
||||
|
||||
let connection = await context.currentUser
|
||||
@@ -27,8 +13,7 @@ const updateConnection = async (
|
||||
let formattedData = params.input.formattedData;
|
||||
|
||||
if (params.input.appAuthClientId) {
|
||||
const appAuthClient = await AppAuthClient
|
||||
.query()
|
||||
const appAuthClient = await AppAuthClient.query()
|
||||
.findById(params.input.appAuthClientId)
|
||||
.throwIfNotFound();
|
||||
|
@@ -0,0 +1,11 @@
|
||||
const updateCurrentUser = async (_parent, params, context) => {
|
||||
const user = await context.currentUser.$query().patchAndFetch({
|
||||
email: params.input.email,
|
||||
password: params.input.password,
|
||||
fullName: params.input.fullName,
|
||||
});
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
export default updateCurrentUser;
|
@@ -1,25 +0,0 @@
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
email: string;
|
||||
password: string;
|
||||
fullName: string;
|
||||
};
|
||||
};
|
||||
|
||||
const updateCurrentUser = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const user = await context.currentUser.$query().patchAndFetch({
|
||||
email: params.input.email,
|
||||
password: params.input.password,
|
||||
fullName: params.input.fullName,
|
||||
});
|
||||
|
||||
return user;
|
||||
};
|
||||
|
||||
export default updateCurrentUser;
|
@@ -1,24 +1,15 @@
|
||||
import Flow from '../../models/flow';
|
||||
import Context from '../../types/express/context';
|
||||
import flowQueue from '../../queues/flow';
|
||||
import { REMOVE_AFTER_30_DAYS_OR_150_JOBS, REMOVE_AFTER_7_DAYS_OR_50_JOBS } from '../../helpers/remove-job-configuration';
|
||||
import {
|
||||
REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
||||
REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||
} from '../../helpers/remove-job-configuration';
|
||||
import globalVariable from '../../helpers/global-variable';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
active: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const JOB_NAME = 'flow';
|
||||
const EVERY_15_MINUTES_CRON = '*/15 * * * *';
|
||||
|
||||
const updateFlowStatus = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const updateFlowStatus = async (_parent, params, context) => {
|
||||
const conditions = context.currentUser.can('publish', 'Flow');
|
||||
const isCreator = conditions.isCreator;
|
||||
const allFlows = Flow.query();
|
||||
@@ -74,7 +65,7 @@ const updateFlowStatus = async (
|
||||
repeat: repeatOptions,
|
||||
jobId: flow.id,
|
||||
removeOnComplete: REMOVE_AFTER_7_DAYS_OR_50_JOBS,
|
||||
removeOnFail: REMOVE_AFTER_30_DAYS_OR_150_JOBS
|
||||
removeOnFail: REMOVE_AFTER_30_DAYS_OR_150_JOBS,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
@@ -85,12 +76,9 @@ const updateFlowStatus = async (
|
||||
}
|
||||
}
|
||||
|
||||
flow = await flow
|
||||
.$query()
|
||||
.withGraphFetched('steps')
|
||||
.patchAndFetch({
|
||||
active: newActiveValue,
|
||||
});
|
||||
flow = await flow.$query().withGraphFetched('steps').patchAndFetch({
|
||||
active: newActiveValue,
|
||||
});
|
||||
|
||||
return flow;
|
||||
};
|
@@ -1,17 +1,4 @@
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
|
||||
const updateFlow = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const updateFlow = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'Flow');
|
||||
|
||||
let flow = await context.currentUser
|
@@ -1,35 +1,13 @@
|
||||
import Context from '../../types/express/context';
|
||||
import Role from '../../models/role';
|
||||
import Permission from '../../models/permission';
|
||||
import permissionCatalog from '../../helpers/permission-catalog.ee';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
permissions: Permission[];
|
||||
};
|
||||
};
|
||||
|
||||
const updateRole = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const updateRole = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'Role');
|
||||
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
description,
|
||||
permissions,
|
||||
} = params.input;
|
||||
const { id, name, description, permissions } = params.input;
|
||||
|
||||
const role = await Role
|
||||
.query()
|
||||
.findById(id)
|
||||
.throwIfNotFound();
|
||||
const role = await Role.query().findById(id).throwIfNotFound();
|
||||
|
||||
try {
|
||||
const updatedRole = await Role.transaction(async (trx) => {
|
||||
@@ -38,19 +16,17 @@ const updateRole = async (
|
||||
if (permissions?.length) {
|
||||
const sanitizedPermissions = permissions
|
||||
.filter((permission) => {
|
||||
const {
|
||||
action,
|
||||
subject,
|
||||
conditions,
|
||||
} = permission;
|
||||
const { action, subject, conditions } = permission;
|
||||
|
||||
const relevantAction = permissionCatalog.actions.find(actionCatalogItem => actionCatalogItem.key === action);
|
||||
const relevantAction = permissionCatalog.actions.find(
|
||||
(actionCatalogItem) => actionCatalogItem.key === action
|
||||
);
|
||||
const validSubject = relevantAction.subjects.includes(subject);
|
||||
const validConditions = conditions.every(condition => {
|
||||
return !!permissionCatalog
|
||||
.conditions
|
||||
.find((conditionCatalogItem) => conditionCatalogItem.key === condition);
|
||||
})
|
||||
const validConditions = conditions.every((condition) => {
|
||||
return !!permissionCatalog.conditions.find(
|
||||
(conditionCatalogItem) => conditionCatalogItem.key === condition
|
||||
);
|
||||
});
|
||||
|
||||
return validSubject && validConditions;
|
||||
})
|
||||
@@ -62,22 +38,17 @@ const updateRole = async (
|
||||
await Permission.query().insert(sanitizedPermissions);
|
||||
}
|
||||
|
||||
await role
|
||||
.$query(trx)
|
||||
.patch(
|
||||
{
|
||||
name,
|
||||
description,
|
||||
}
|
||||
);
|
||||
await role.$query(trx).patch({
|
||||
name,
|
||||
description,
|
||||
});
|
||||
|
||||
return await Role
|
||||
.query(trx)
|
||||
return await Role.query(trx)
|
||||
.leftJoinRelated({
|
||||
permissions: true
|
||||
permissions: true,
|
||||
})
|
||||
.withGraphFetched({
|
||||
permissions: true
|
||||
permissions: true,
|
||||
})
|
||||
.findById(id);
|
||||
});
|
@@ -1,29 +1,8 @@
|
||||
import { IJSONObject } from '@automatisch/types';
|
||||
import App from '../../models/app';
|
||||
import Step from '../../models/step';
|
||||
import Connection from '../../models/connection';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
key: string;
|
||||
appKey: string;
|
||||
parameters: IJSONObject;
|
||||
flow: {
|
||||
id: string;
|
||||
};
|
||||
connection: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const updateStep = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const updateStep = async (_parent, params, context) => {
|
||||
const { isCreator } = context.currentUser.can('update', 'Flow');
|
||||
const userSteps = context.currentUser.$relatedQuery('steps');
|
||||
const allSteps = Step.query();
|
||||
@@ -50,11 +29,13 @@ const updateStep = async (
|
||||
|
||||
const userConnections = context.currentUser.$relatedQuery('connections');
|
||||
const allConnections = Connection.query();
|
||||
const baseConnectionsQuery = canSeeAllConnections ? allConnections : userConnections;
|
||||
const baseConnectionsQuery = canSeeAllConnections
|
||||
? allConnections
|
||||
: userConnections;
|
||||
|
||||
const connection = await baseConnectionsQuery
|
||||
.clone()
|
||||
.findById(input.connection?.id)
|
||||
.findById(input.connection?.id);
|
||||
|
||||
if (!connection) {
|
||||
throw new Error('The connection does not exist!');
|
@@ -1,25 +1,9 @@
|
||||
import Context from '../../types/express/context';
|
||||
import User from '../../models/user';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
id: string;
|
||||
email: string;
|
||||
fullName: string;
|
||||
role: {
|
||||
id: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
const updateUser = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
const updateUser = async (_parent, params, context) => {
|
||||
context.currentUser.can('update', 'User');
|
||||
|
||||
const userPayload: Partial<User> = {
|
||||
const userPayload = {
|
||||
email: params.input.email,
|
||||
fullName: params.input.fullName,
|
||||
};
|
@@ -0,0 +1,30 @@
|
||||
import SamlAuthProvider from '../../models/saml-auth-provider.ee';
|
||||
|
||||
const upsertSamlAuthProvider = async (_parent, params, context) => {
|
||||
context.currentUser.can('create', 'SamlAuthProvider');
|
||||
|
||||
const samlAuthProviderPayload = {
|
||||
...params.input,
|
||||
};
|
||||
|
||||
const existingSamlAuthProvider = await SamlAuthProvider.query()
|
||||
.limit(1)
|
||||
.first();
|
||||
|
||||
if (!existingSamlAuthProvider) {
|
||||
const samlAuthProvider = await SamlAuthProvider.query().insert(
|
||||
samlAuthProviderPayload
|
||||
);
|
||||
|
||||
return samlAuthProvider;
|
||||
}
|
||||
|
||||
const samlAuthProvider = await SamlAuthProvider.query().patchAndFetchById(
|
||||
existingSamlAuthProvider.id,
|
||||
samlAuthProviderPayload
|
||||
);
|
||||
|
||||
return samlAuthProvider;
|
||||
};
|
||||
|
||||
export default upsertSamlAuthProvider;
|
@@ -1,52 +0,0 @@
|
||||
import type { SamlConfig } from '@node-saml/passport-saml';
|
||||
import SamlAuthProvider from '../../models/saml-auth-provider.ee';
|
||||
import Context from '../../types/express/context';
|
||||
|
||||
type Params = {
|
||||
input: {
|
||||
name: string;
|
||||
certificate: string;
|
||||
signatureAlgorithm: SamlConfig['signatureAlgorithm'];
|
||||
issuer: string;
|
||||
entryPoint: string;
|
||||
firstnameAttributeName: string;
|
||||
surnameAttributeName: string;
|
||||
emailAttributeName: string;
|
||||
roleAttributeName: string;
|
||||
defaultRoleId: string;
|
||||
active: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
const upsertSamlAuthProvider = async (
|
||||
_parent: unknown,
|
||||
params: Params,
|
||||
context: Context
|
||||
) => {
|
||||
context.currentUser.can('create', 'SamlAuthProvider');
|
||||
|
||||
const samlAuthProviderPayload: Partial<SamlAuthProvider> = {
|
||||
...params.input,
|
||||
};
|
||||
|
||||
const existingSamlAuthProvider = await SamlAuthProvider.query()
|
||||
.limit(1)
|
||||
.first();
|
||||
|
||||
if (!existingSamlAuthProvider) {
|
||||
const samlAuthProvider = await SamlAuthProvider.query().insert(
|
||||
samlAuthProviderPayload
|
||||
);
|
||||
|
||||
return samlAuthProvider;
|
||||
}
|
||||
|
||||
const samlAuthProvider = await SamlAuthProvider.query().patchAndFetchById(
|
||||
existingSamlAuthProvider.id,
|
||||
samlAuthProviderPayload
|
||||
);
|
||||
|
||||
return samlAuthProvider;
|
||||
};
|
||||
|
||||
export default upsertSamlAuthProvider;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user