Compare commits

..

30 Commits

Author SHA1 Message Date
Faruk AYDIN
283c644285 feat: Convert bin files to use JS 2024-01-04 18:31:06 +01:00
Ömer Faruk Aydın
b693c12500 Merge pull request #1515 from automatisch/queues-js
feat: Convert all queues folder to js files
2024-01-04 18:30:42 +01:00
Faruk AYDIN
cbe3db8187 feat: Convert all queues folder to js files 2024-01-04 18:20:08 +01:00
Ömer Faruk Aydın
7bccbc9471 Merge pull request #1514 from automatisch/root-resolvers
feat: Convert root query and mutation resolvers to js
2024-01-04 18:19:29 +01:00
Faruk AYDIN
a452520f1a feat: Convert root query and mutation resolvers to js 2024-01-04 18:12:27 +01:00
Ömer Faruk Aydın
a331b34b49 Merge pull request #1513 from automatisch/mutations-js
feat: Convert all mutation files to js
2024-01-04 18:11:48 +01:00
Faruk AYDIN
8d6f0f8e9e feat: Convert all mutation files to js 2024-01-04 18:04:54 +01:00
Ömer Faruk Aydın
47dd5a1949 Merge pull request #1512 from automatisch/queries-js
feat: Convert all query files to JS
2024-01-04 18:04:21 +01:00
Faruk AYDIN
387f8fd44c feat: Convert all query files to JS 2024-01-04 17:55:29 +01:00
Ömer Faruk Aydın
b69b1f6f67 Merge pull request #1511 from automatisch/workers-js
feat: Convert workers to use js files
2024-01-04 17:55:01 +01:00
Faruk AYDIN
d1427ffd54 feat: Convert workers to use js files 2024-01-04 17:48:52 +01:00
Ömer Faruk Aydın
00c876dd93 Merge pull request #1510 from automatisch/routes-js
feat: Convert routes folder to the js files
2024-01-04 17:48:26 +01:00
Faruk AYDIN
9d1aa9e59a feat: Convert routes folder to the js files 2024-01-04 17:41:52 +01:00
Ömer Faruk Aydın
aceebba99a Merge pull request #1509 from automatisch/errors-folder-js
feat: Convert ts files to js files for errors directory
2024-01-04 17:40:59 +01:00
Faruk AYDIN
7d6a8c4607 feat: Convert service folder to js files 2024-01-04 17:33:03 +01:00
Faruk AYDIN
5fd90355ae feat: Convert error handler ts file to js file 2024-01-04 17:17:25 +01:00
Faruk AYDIN
9b01a2a4da feat: Convert ts files to js files for errors directory 2024-01-04 17:14:28 +01:00
Ömer Faruk Aydın
a2986d70a0 Merge pull request #1508 from automatisch/controllers-js
feat: Convert ts files to js files for controllers
2024-01-04 17:13:39 +01:00
Faruk AYDIN
5dfa38ca99 feat: Convert ts files to js files for controllers 2024-01-04 17:06:19 +01:00
Ömer Faruk Aydın
157c6812cc Merge pull request #1507 from automatisch/config-folder-js
feat: Convert ts files to js files for config folder
2024-01-04 17:05:52 +01:00
Faruk AYDIN
215ff4b74a chore: Add eslint configuration specific to backend 2024-01-04 16:56:29 +01:00
Faruk AYDIN
ca7b8b865a feat: Convert ts files to js files for config folder 2024-01-04 16:52:52 +01:00
Ömer Faruk Aydın
fd0b12f6a1 Merge pull request #1506 from automatisch/allow-js
chore: Allow JS files for the backend package
2024-01-04 16:50:19 +01:00
Faruk AYDIN
2f3b739f9e chore: Allow JS files for the backend package 2024-01-04 16:34:55 +01:00
Ömer Faruk Aydın
3255ddca63 Merge pull request #1503 from automatisch/feature/helix-app
feat: Implement Helix app with new chat action
2024-01-02 21:20:50 +01:00
Faruk AYDIN
b5460712e6 feat: Implement Helix app with new chat action 2024-01-02 19:06:07 +01:00
Ömer Faruk Aydın
6f7dcc2b6e Merge pull request #1498 from automatisch/AUT-557
feat(self-hosted-llm): add send prompt and send chat prompt actions
2024-01-02 14:27:46 +01:00
Ömer Faruk Aydın
70d4800cb1 Merge pull request #1497 from automatisch/AUT-554
feat(azure-openai): add send prompt action
2024-01-02 14:27:20 +01:00
Ali BARIN
fe10523972 test: update first app as azure-openai 2023-12-20 14:51:30 +00:00
Ali BARIN
c975a56245 feat(azure-openai): add send prompt action 2023-12-20 14:48:33 +00:00
138 changed files with 684 additions and 1272 deletions

View 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'],
},
},
],
};

View File

@@ -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;
}

View File

@@ -0,0 +1,3 @@
import newChat from './new-chat';
export default [newChat];

View File

@@ -0,0 +1,50 @@
import FormData from 'form-data';
import defineAction from '../../../../helpers/define-action';
export default defineAction({
name: 'New chat',
key: 'newChat',
description: 'Create a new chat session for Helix AI.',
arguments: [
{
label: 'Input',
key: 'input',
type: 'string' as const,
required: true,
description: 'Prompt to start the chat with.',
variables: true,
},
],
async run($) {
const formData = new FormData();
formData.append('input', $.step.parameters.input as string);
formData.append('mode', 'inference');
formData.append('type', 'text');
const sessionResponse = await $.http.post('/api/v1/sessions', formData, {
headers: {
...formData.getHeaders(),
},
});
const sessionId = sessionResponse.data.id;
let chatGenerated = false;
while (!chatGenerated) {
const response = await $.http.get(`/api/v1/sessions/${sessionId}`);
const message =
response.data.interactions[response.data.interactions.length - 1];
if (message.creator === 'system' && message.state === 'complete') {
$.setActionItem({
raw: message,
});
chatGenerated = true;
}
}
},
});

View File

@@ -0,0 +1,6 @@
<svg role="img" viewBox="0 0 47 68" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.9268 3.41382L15.7675 21.2642L15.5102 52.1134V58.1779C15.4898 58.7861 15.631 59.3888 15.9194 59.9247C16.2078 60.4606 16.6331 60.9105 17.1519 61.2285L24.1843 65.296L25.6299 66.1291C27.7314 67.3481 30.1162 67.9938 32.5456 68.0014C34.975 68.009 37.3638 67.3783 39.4729 66.1724C41.5819 64.9666 43.3372 63.2278 44.563 61.1303C45.7888 59.0328 46.442 56.65 46.4574 54.2206L46.7147 23.3837V17.258C46.7207 16.6458 46.564 16.0429 46.2604 15.5112C45.9569 14.9795 45.5175 14.538 44.9873 14.2319L39.7559 11.218L36.5337 9.36804L21.0602 0.436718C20.5374 0.146102 19.9481 -0.00382494 19.3501 0.00160738C18.752 0.0070397 18.1655 0.167645 17.6482 0.467709C17.1308 0.767772 16.7001 1.19701 16.3984 1.71343C16.0967 2.22985 15.9342 2.81576 15.9268 3.41382Z" fill="#16C3E4" fill-opacity="0.8"></path>
<g style="mix-blend-mode:hard-light">
<path d="M31.3986 35.0108L31.1636 63.0174C31.1434 63.6524 30.9604 64.2716 30.6322 64.8156C30.304 65.3596 29.8415 65.8101 29.2892 66.1241C28.7368 66.4381 28.1131 66.605 27.4777 66.6088C26.8423 66.6126 26.2166 66.4532 25.6605 66.1458L1.80552 52.1981C1.25401 51.8682 0.797643 51.4008 0.481139 50.8416C0.164634 50.2823 -0.00115693 49.6505 6.0768e-06 49.0079L0.23497 21.1867C0.25531 20.5542 0.437144 19.9374 0.763178 19.395C1.08921 18.8526 1.5486 18.4026 2.09766 18.0879C2.64672 17.7731 3.26719 17.604 3.90005 17.5967C4.53292 17.5894 5.15714 17.744 5.71334 18.046L29.5684 31.8082C30.1263 32.1361 30.5887 32.6041 30.9098 33.1659C31.2309 33.7277 31.3994 34.3637 31.3986 35.0108Z" fill="#C43BBB"></path>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,45 @@
import verifyCredentials from './verify-credentials';
import isStillVerified from './is-still-verified';
export default {
fields: [
{
key: 'screenName',
label: 'Screen Name',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description:
'Screen name of your connection to be used on Automatisch UI.',
clickToCopy: false,
},
{
key: 'instanceUrl',
label: 'Helix instance URL',
type: 'string' as const,
required: false,
readOnly: false,
value: 'https://app.tryhelix.ai',
placeholder: 'https://app.tryhelix.ai',
description:
'Your Helix instance URL. Default is https://app.tryhelix.ai.',
clickToCopy: true,
},
{
key: 'apiKey',
label: 'API Key',
type: 'string' as const,
required: true,
readOnly: false,
value: null,
placeholder: null,
description: 'Helix API Key of your account.',
clickToCopy: false,
},
],
verifyCredentials,
isStillVerified,
};

View File

@@ -0,0 +1,9 @@
import { IGlobalVariable } from '@automatisch/types';
import verifyCredentials from './verify-credentials';
const isStillVerified = async ($: IGlobalVariable) => {
await verifyCredentials($);
return true;
};
export default isStillVerified;

View File

@@ -0,0 +1,11 @@
import { IGlobalVariable } from '@automatisch/types';
const verifyCredentials = async ($: IGlobalVariable) => {
await $.http.get('/api/v1/sessions');
await $.auth.set({
screenName: $.auth.data.screenName,
});
};
export default verifyCredentials;

View File

@@ -0,0 +1,12 @@
import { TBeforeRequest } from '@automatisch/types';
const addAuthHeader: TBeforeRequest = ($, requestConfig) => {
if ($.auth.data?.apiKey) {
const authorizationHeader = `Bearer ${$.auth.data.apiKey}`;
requestConfig.headers.Authorization = authorizationHeader;
}
return requestConfig;
};
export default addAuthHeader;

View File

@@ -0,0 +1,13 @@
import { TBeforeRequest } from '@automatisch/types';
const setBaseUrl: TBeforeRequest = ($, requestConfig) => {
if ($.auth.data.instanceUrl) {
requestConfig.baseURL = $.auth.data.instanceUrl as string;
} else if ($.app.apiBaseUrl) {
requestConfig.baseURL = $.app.apiBaseUrl as string;
}
return requestConfig;
};
export default setBaseUrl;

View File

View File

@@ -0,0 +1,19 @@
import defineApp from '../../helpers/define-app';
import setBaseUrl from './common/set-base-url';
import addAuthHeader from './common/add-auth-header';
import auth from './auth';
import actions from './actions';
export default defineApp({
name: 'Helix',
key: 'helix',
baseUrl: 'https://tryhelix.ai',
apiBaseUrl: 'https://app.tryhelix.ai',
iconUrl: '{BASE_URL}/apps/helix/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/helix/connection',
primaryColor: '000000',
supportsConnections: true,
beforeRequest: [setBaseUrl, addAuthHeader],
auth,
actions,
});

View File

@@ -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,57 +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;
isMation: 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';
@@ -85,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,

View File

@@ -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';

View File

@@ -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,

View File

@@ -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];
});

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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;

View 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!`;
}
}

View File

@@ -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!`;
}
}

View 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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;

View 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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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,

View File

@@ -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,

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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();

View File

@@ -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

View File

@@ -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);

View File

@@ -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();

View File

@@ -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 });

View File

@@ -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() });

View File

@@ -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

View File

@@ -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(),
});

View File

@@ -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({

View File

@@ -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

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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

View File

@@ -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);
});

View File

@@ -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!');

View File

@@ -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,
};

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,24 +1,11 @@
import SamlAuthProvider from '../../models/saml-auth-provider.ee';
import SamlAuthProvidersRoleMapping from '../../models/saml-auth-providers-role-mapping.ee';
import Context from '../../types/express/context';
import isEmpty from 'lodash/isEmpty';
type Params = {
input: {
samlAuthProviderId: string;
samlAuthProvidersRoleMappings: [
{
roleId: string;
remoteRoleName: string;
}
];
};
};
const upsertSamlAuthProvidersRoleMappings = async (
_parent: unknown,
params: Params,
context: Context
_parent,
params,
context
) => {
context.currentUser.can('update', 'SamlAuthProvider');

View File

@@ -1,18 +1,7 @@
import Context from '../../types/express/context';
import App from '../../models/app';
import globalVariable from '../../helpers/global-variable';
type Params = {
input: {
id: string;
};
};
const verifyConnection = async (
_parent: unknown,
params: Params,
context: Context
) => {
const verifyConnection = async (_parent, params, context) => {
context.currentUser.can('create', 'Connection');
let connection = await context.currentUser

View File

@@ -1,11 +1,6 @@
import AppAuthClient from '../../models/app-auth-client';
import Context from '../../types/express/context';
type Params = {
id: string;
};
const getAppAuthClient = async (_parent: unknown, params: Params, context: Context) => {
const getAppAuthClient = async (_parent, params, context) => {
let canSeeAllClients = false;
try {
context.currentUser.can('read', 'App');
@@ -15,8 +10,7 @@ const getAppAuthClient = async (_parent: unknown, params: Params, context: Conte
// void
}
const appAuthClient = AppAuthClient
.query()
const appAuthClient = AppAuthClient.query()
.findById(params.id)
.throwIfNotFound();

View File

@@ -1,12 +1,6 @@
import AppConfig from '../../models/app-config';
import Context from '../../types/express/context';
type Params = {
appKey: string;
active: boolean;
};
const getAppAuthClients = async (_parent: unknown, params: Params, context: Context) => {
const getAppAuthClients = async (_parent, params, context) => {
let canSeeAllClients = false;
try {
context.currentUser.can('read', 'App');
@@ -16,8 +10,7 @@ const getAppAuthClients = async (_parent: unknown, params: Params, context: Cont
// void
}
const appConfig = await AppConfig
.query()
const appConfig = await AppConfig.query()
.findOne({
key: params.appKey,
})
@@ -30,8 +23,8 @@ const getAppAuthClients = async (_parent: unknown, params: Params, context: Cont
if (!canSeeAllClients) {
appAuthClients.where({
active: true
})
active: true,
});
}
return await appAuthClients;

View File

@@ -0,0 +1,17 @@
import AppConfig from '../../models/app-config';
const getAppConfig = async (_parent, params, context) => {
context.currentUser.can('create', 'Connection');
const appConfig = await AppConfig.query()
.withGraphFetched({
appAuthClients: true,
})
.findOne({
key: params.key,
});
return appConfig;
};
export default getAppConfig;

View File

@@ -1,23 +0,0 @@
import AppConfig from '../../models/app-config';
import Context from '../../types/express/context';
type Params = {
key: string;
};
const getAppConfig = async (_parent: unknown, params: Params, context: Context) => {
context.currentUser.can('create', 'Connection');
const appConfig = await AppConfig
.query()
.withGraphFetched({
appAuthClients: true
})
.findOne({
key: params.key
});
return appConfig;
};
export default getAppConfig;

View File

@@ -1,17 +1,14 @@
import App from '../../models/app';
import Connection from '../../models/connection';
import Context from '../../types/express/context';
type Params = {
key: string;
};
const getApp = async (_parent: unknown, params: Params, context: Context) => {
const getApp = async (_parent, params, context) => {
const conditions = context.currentUser.can('read', 'Connection');
const userConnections = context.currentUser.$relatedQuery('connections');
const allConnections = Connection.query();
const connectionBaseQuery = conditions.isCreator ? userConnections : allConnections;
const connectionBaseQuery = conditions.isCreator
? userConnections
: allConnections;
const app = await App.findOneByKey(params.key);
@@ -21,7 +18,7 @@ const getApp = async (_parent: unknown, params: Params, context: Context) => {
.select('connections.*')
.withGraphFetched({
appConfig: true,
appAuthClient: true
appAuthClient: true,
})
.fullOuterJoinRelated('steps')
.where({

View File

@@ -0,0 +1,17 @@
import App from '../../models/app';
const getApps = async (_parent, params) => {
const apps = await App.findAll(params.name);
if (params.onlyWithTriggers) {
return apps.filter((app) => app.triggers?.length);
}
if (params.onlyWithActions) {
return apps.filter((app) => app.actions?.length);
}
return apps;
};
export default getApps;

View File

@@ -1,24 +0,0 @@
import { IApp } from '@automatisch/types';
import App from '../../models/app';
type Params = {
name: string;
onlyWithTriggers: boolean;
onlyWithActions: boolean;
};
const getApps = async (_parent: unknown, params: Params) => {
const apps = await App.findAll(params.name);
if (params.onlyWithTriggers) {
return apps.filter((app: IApp) => app.triggers?.length);
}
if (params.onlyWithActions) {
return apps.filter((app: IApp) => app.actions?.length);
}
return apps;
};
export default getApps;

View File

@@ -1,17 +1,8 @@
import { DateTime } from 'luxon';
import { TSubscription } from '@automatisch/types';
import Context from '../../types/express/context';
import Billing from '../../helpers/billing/index.ee';
import Execution from '../../models/execution';
import ExecutionStep from '../../models/execution-step';
import Subscription from '../../models/subscription.ee';
const getBillingAndUsage = async (
_parent: unknown,
_params: unknown,
context: Context
) => {
const getBillingAndUsage = async (_parent, _params, context) => {
const persistedSubscription = await context.currentUser.$relatedQuery(
'currentSubscription'
);
@@ -28,7 +19,7 @@ const getBillingAndUsage = async (
};
};
const paidSubscription = (subscription: Subscription): TSubscription => {
const paidSubscription = (subscription) => {
const currentPlan = Billing.paddlePlans.find(
(plan) => plan.productId === subscription.paddlePlanId
);
@@ -63,7 +54,7 @@ const paidSubscription = (subscription: Subscription): TSubscription => {
};
};
const freeTrialSubscription = (): TSubscription => {
const freeTrialSubscription = () => {
return {
status: null,
monthlyQuota: {
@@ -85,15 +76,15 @@ const freeTrialSubscription = (): TSubscription => {
};
};
const executionIds = async (context: Context) => {
const executionIds = async (context) => {
return (
await context.currentUser
.$relatedQuery('executions')
.select('executions.id')
).map((execution: Execution) => execution.id);
).map((execution) => execution.id);
};
const executionStepCount = async (context: Context) => {
const executionStepCount = async (context) => {
const executionStepCount = await ExecutionStep.query()
.whereIn('execution_id', await executionIds(context))
.andWhere(

View File

@@ -1,11 +1,7 @@
import { hasValidLicense } from '../../helpers/license.ee';
import Config from '../../models/config';
type Params = {
keys: string[];
};
const getConfig = async (_parent: unknown, params: Params) => {
const getConfig = async (_parent, params) => {
if (!(await hasValidLicense())) return {};
const configQuery = Config.query();
@@ -22,7 +18,7 @@ const getConfig = async (_parent: unknown, params: Params) => {
computedConfig[key] = value?.data;
return computedConfig;
}, {} as Record<string, unknown>);
}, {});
};
export default getConfig;

View File

@@ -1,23 +1,15 @@
import { IConnection } from '@automatisch/types';
import App from '../../models/app';
import Context from '../../types/express/context';
import Flow from '../../models/flow';
import Connection from '../../models/connection';
type Params = {
name: string;
};
const getConnectedApps = async (
_parent: unknown,
params: Params,
context: Context
) => {
const getConnectedApps = async (_parent, params, context) => {
const conditions = context.currentUser.can('read', 'Connection');
const userConnections = context.currentUser.$relatedQuery('connections');
const allConnections = Connection.query();
const connectionBaseQuery = conditions.isCreator ? userConnections : allConnections;
const connectionBaseQuery = conditions.isCreator
? userConnections
: allConnections;
const userFlows = context.currentUser.$relatedQuery('flows');
const allFlows = Flow.query();
@@ -51,7 +43,7 @@ const getConnectedApps = async (
})
.map((app) => {
const connection = connections.find(
(connection) => (connection as IConnection).key === app.key
(connection) => connection.key === app.key
);
app.connectionCount = connection?.count || 0;

View File

@@ -0,0 +1,5 @@
const getCurrentUser = async (_parent, _params, context) => {
return context.currentUser;
};
export default getCurrentUser;

View File

@@ -1,11 +0,0 @@
import Context from '../../types/express/context';
const getCurrentUser = async (
_parent: unknown,
_params: unknown,
context: Context
) => {
return context.currentUser;
};
export default getCurrentUser;

View File

@@ -1,22 +1,10 @@
import { IDynamicData, IJSONObject } from '@automatisch/types';
import Context from '../../types/express/context';
import App from '../../models/app';
import Step from '../../models/step';
import ExecutionStep from '../../models/execution-step';
import globalVariable from '../../helpers/global-variable';
import computeParameters from '../../helpers/compute-parameters';
type Params = {
stepId: string;
key: string;
parameters: IJSONObject;
};
const getDynamicData = async (
_parent: unknown,
params: Params,
context: Context
) => {
const getDynamicData = async (_parent, params, context) => {
const conditions = context.currentUser.can('update', 'Flow');
const userSteps = context.currentUser.$relatedQuery('steps');
const allSteps = Step.query();
@@ -40,9 +28,7 @@ const getDynamicData = async (
const app = await App.findOneByKey(step.appKey);
const $ = await globalVariable({ connection, app, flow, step });
const command = app.dynamicData.find(
(data: IDynamicData) => data.key === params.key
);
const command = app.dynamicData.find((data) => data.key === params.key);
// apply run-time parameters that're not persisted yet
for (const parameterKey in params.parameters) {
@@ -53,12 +39,17 @@ const getDynamicData = async (
const lastExecution = await flow.$relatedQuery('lastExecution');
const lastExecutionId = lastExecution?.id;
const priorExecutionSteps = lastExecutionId ? await ExecutionStep.query().where({
execution_id: lastExecutionId,
}) : [];
const priorExecutionSteps = lastExecutionId
? await ExecutionStep.query().where({
execution_id: lastExecutionId,
})
: [];
// compute variables in parameters
const computedParameters = computeParameters($.step.parameters, priorExecutionSteps);
const computedParameters = computeParameters(
$.step.parameters,
priorExecutionSteps
);
$.step.parameters = computedParameters;

View File

@@ -1,20 +1,8 @@
import { IDynamicFields, IJSONObject } from '@automatisch/types';
import Context from '../../types/express/context';
import App from '../../models/app';
import Step from '../../models/step';
import globalVariable from '../../helpers/global-variable';
type Params = {
stepId: string;
key: string;
parameters: IJSONObject;
};
const getDynamicFields = async (
_parent: unknown,
params: Params,
context: Context
) => {
const getDynamicFields = async (_parent, params, context) => {
const conditions = context.currentUser.can('update', 'Flow');
const userSteps = context.currentUser.$relatedQuery('steps');
const allSteps = Step.query();
@@ -37,16 +25,14 @@ const getDynamicFields = async (
const app = await App.findOneByKey(step.appKey);
const $ = await globalVariable({ connection, app, flow: step.flow, step });
const command = app.dynamicFields.find(
(data: IDynamicFields) => data.key === params.key
);
const command = app.dynamicFields.find((data) => data.key === params.key);
for (const parameterKey in params.parameters) {
const parameterValue = params.parameters[parameterKey];
$.step.parameters[parameterKey] = parameterValue;
}
const additionalFields = await command.run($) || [];
const additionalFields = (await command.run($)) || [];
return additionalFields;
};

View File

@@ -1,22 +1,13 @@
import Context from '../../types/express/context';
import paginate from '../../helpers/pagination';
import Execution from '../../models/execution';
type Params = {
executionId: string;
limit: number;
offset: number;
};
const getExecutionSteps = async (
_parent: unknown,
params: Params,
context: Context
) => {
const getExecutionSteps = async (_parent, params, context) => {
const conditions = context.currentUser.can('read', 'Execution');
const userExecutions = context.currentUser.$relatedQuery('executions');
const allExecutions = Execution.query();
const executionBaseQuery = conditions.isCreator ? userExecutions : allExecutions;
const executionBaseQuery = conditions.isCreator
? userExecutions
: allExecutions;
const execution = await executionBaseQuery
.clone()

View File

@@ -1,19 +1,12 @@
import Context from '../../types/express/context';
import Execution from '../../models/execution';
type Params = {
executionId: string;
};
const getExecution = async (
_parent: unknown,
params: Params,
context: Context
) => {
const getExecution = async (_parent, params, context) => {
const conditions = context.currentUser.can('read', 'Execution');
const userExecutions = context.currentUser.$relatedQuery('executions');
const allExecutions = Execution.query();
const executionBaseQuery = conditions.isCreator ? userExecutions : allExecutions;
const executionBaseQuery = conditions.isCreator
? userExecutions
: allExecutions;
const execution = await executionBaseQuery
.clone()

View File

@@ -1,36 +1,18 @@
import { raw } from 'objection';
import { DateTime } from 'luxon';
import Context from '../../types/express/context';
import Execution from '../../models/execution';
import paginate from '../../helpers/pagination';
type Filters = {
flowId?: string;
status?: string;
createdAt?: {
from?: string;
to?: string;
};
}
type Params = {
limit: number;
offset: number;
filters?: Filters;
};
const getExecutions = async (
_parent: unknown,
params: Params,
context: Context
) => {
const getExecutions = async (_parent, params, context) => {
const conditions = context.currentUser.can('read', 'Execution');
const filters = params.filters;
const userExecutions = context.currentUser.$relatedQuery('executions');
const allExecutions = Execution.query();
const executionBaseQuery = conditions.isCreator ? userExecutions : allExecutions;
const executionBaseQuery = conditions.isCreator
? userExecutions
: allExecutions;
const selectStatusStatement = `
case
@@ -48,8 +30,7 @@ const getExecutions = async (
.groupBy('executions.id')
.orderBy('created_at', 'desc');
const computedExecutions = Execution
.query()
const computedExecutions = Execution.query()
.with('executions', executions)
.withSoftDeleted()
.withGraphFetched({
@@ -69,20 +50,16 @@ const getExecutions = async (
if (filters?.createdAt) {
const createdAtFilter = filters.createdAt;
if (createdAtFilter.from) {
const isoFromDateTime = DateTime
.fromMillis(
parseInt(createdAtFilter.from, 10)
)
.toISO();
const isoFromDateTime = DateTime.fromMillis(
parseInt(createdAtFilter.from, 10)
).toISO();
computedExecutions.where('executions.created_at', '>=', isoFromDateTime);
}
if (createdAtFilter.to) {
const isoToDateTime = DateTime
.fromMillis(
parseInt(createdAtFilter.to, 10)
)
.toISO();
const isoToDateTime = DateTime.fromMillis(
parseInt(createdAtFilter.to, 10)
).toISO();
computedExecutions.where('executions.created_at', '<=', isoToDateTime);
}
}

View File

@@ -1,11 +1,6 @@
import Context from '../../types/express/context';
import Flow from '../../models/flow';
type Params = {
id: string;
};
const getFlow = async (_parent: unknown, params: Params, context: Context) => {
const getFlow = async (_parent, params, context) => {
const conditions = context.currentUser.can('read', 'Flow');
const userFlows = context.currentUser.$relatedQuery('flows');
const allFlows = Flow.query();

View File

@@ -1,16 +1,7 @@
import Flow from '../../models/flow';
import Context from '../../types/express/context';
import paginate from '../../helpers/pagination';
type Params = {
appKey?: string;
connectionId?: string;
name?: string;
limit: number;
offset: number;
};
const getFlows = async (_parent: unknown, params: Params, context: Context) => {
const getFlows = async (_parent, params, context) => {
const conditions = context.currentUser.can('read', 'Flow');
const userFlows = context.currentUser.$relatedQuery('flows');
const allFlows = Flow.query();

View File

@@ -1,12 +1,9 @@
import Context from '../../types/express/context';
import Billing from '../../helpers/billing/index.ee';
const getInvoices = async (
_parent: unknown,
_params: unknown,
context: Context
) => {
const subscription = await context.currentUser.$relatedQuery('currentSubscription');
const getInvoices = async (_parent, _params, context) => {
const subscription = await context.currentUser.$relatedQuery(
'currentSubscription'
);
if (!subscription) {
return;

Some files were not shown because too many files have changed in this diff Show More