Compare commits

..

1 Commits

Author SHA1 Message Date
Rıdvan Akca
51e96b832d feat(dropbox): add new folders trigger 2024-02-26 17:43:01 +03:00
446 changed files with 5426 additions and 4773 deletions

View File

@@ -28,7 +28,7 @@ cd packages/web
rm -rf .env
echo "
PORT=$WEB_PORT
REACT_APP_BACKEND_URL=http://localhost:$BACKEND_PORT
REACT_APP_GRAPHQL_URL=http://localhost:$BACKEND_PORT/graphql
" >> .env
cd $CURRENT_DIR

18
.eslintrc.js Normal file
View File

@@ -0,0 +1,18 @@
module.exports = {
root: 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'],
},
},
],
};

View File

@@ -22,7 +22,7 @@ jobs:
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- run: yarn --frozen-lockfile
- run: cd packages/backend && yarn lint
- run: yarn lint
- run: echo "🍏 This job's status is ${{ job.status }}."
start-backend-server:
runs-on: ubuntu-latest

View File

@@ -62,9 +62,8 @@ jobs:
run: yarn && yarn lerna bootstrap
- name: Install Playwright Browsers
run: yarn playwright install --with-deps
- name: Build Automatisch web
working-directory: ./packages/web
run: yarn build
- name: Build Automatisch
run: yarn lerna run --scope=@*/{web,cli} build
env:
# Keep this until we clean up warnings in build processes
CI: false

View File

@@ -6,6 +6,7 @@
"start": "lerna run --stream --parallel --scope=@*/{web,backend} dev",
"start:web": "lerna run --stream --scope=@*/web dev",
"start:backend": "lerna run --stream --scope=@*/backend dev",
"lint": "lerna run --no-bail --stream --parallel --scope=@*/{web,backend} lint",
"build:docs": "cd ./packages/docs && yarn install && yarn build"
},
"workspaces": {
@@ -20,6 +21,8 @@
]
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^5.9.1",
"@typescript-eslint/parser": "^5.9.1",
"eslint": "^8.13.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",

View File

@@ -18,8 +18,8 @@ async function fetchAdminRole() {
}
export async function createUser(
email = appConfig.seedUserEmail,
password = appConfig.seedUserPassword
email = 'user@automatisch.io',
password = 'sample'
) {
const UNIQUE_VIOLATION_CODE = '23505';

View File

@@ -11,7 +11,7 @@
"start:worker": "node src/worker.js",
"pretest": "APP_ENV=test node ./test/setup/prepare-test-env.js",
"test": "APP_ENV=test vitest run",
"lint": "eslint .",
"lint": "eslint . --ignore-path ../../.eslintignore",
"db:create": "node ./bin/database/create.js",
"db:seed:user": "node ./bin/database/seed-user.js",
"db:drop": "node ./bin/database/drop.js",
@@ -95,6 +95,7 @@
"url": "https://github.com/automatisch/automatisch/issues"
},
"devDependencies": {
"@typescript-eslint/utils": "^7.0.2",
"nodemon": "^2.0.13",
"supertest": "^6.3.3",
"vitest": "^1.1.3"

View File

@@ -1,27 +0,0 @@
import defineAction from '../../../../helpers/define-action.js';
export default defineAction({
name: 'Get value',
key: 'getValue',
description: 'Get value from the persistent datastore.',
arguments: [
{
label: 'Key',
key: 'key',
type: 'string',
required: true,
description: 'The key of your value to get.',
variables: true,
},
],
async run($) {
const keyValuePair = await $.datastore.get({
key: $.step.parameters.key,
});
$.setActionItem({
raw: keyValuePair,
});
},
});

View File

@@ -1,4 +0,0 @@
import getValue from './get-value/index.js';
import setValue from './set-value/index.js';
export default [getValue, setValue];

View File

@@ -1,36 +0,0 @@
import defineAction from '../../../../helpers/define-action.js';
export default defineAction({
name: 'Set value',
key: 'setValue',
description: 'Set value to the persistent datastore.',
arguments: [
{
label: 'Key',
key: 'key',
type: 'string',
required: true,
description: 'The key of your value to set.',
variables: true,
},
{
label: 'Value',
key: 'value',
type: 'string',
required: true,
description: 'The value to set.',
variables: true,
},
],
async run($) {
const keyValuePair = await $.datastore.set({
key: $.step.parameters.key,
value: $.step.parameters.value,
});
$.setActionItem({
raw: keyValuePair,
});
},
});

View File

@@ -1,13 +0,0 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg" fill="#000000" width="800px" height="800px" viewBox="0 0 32 32" id="icon">
<defs>
<style>.cls-1{fill:none;}</style>
</defs>
<title>datastore</title>
<circle cx="23" cy="23" r="1"/>
<rect x="8" y="22" width="12" height="2"/>
<circle cx="23" cy="9" r="1"/>
<rect x="8" y="8" width="12" height="2"/>
<path d="M26,14a2,2,0,0,0,2-2V6a2,2,0,0,0-2-2H6A2,2,0,0,0,4,6v6a2,2,0,0,0,2,2H8v4H6a2,2,0,0,0-2,2v6a2,2,0,0,0,2,2H26a2,2,0,0,0,2-2V20a2,2,0,0,0-2-2H24V14ZM6,6H26v6H6ZM26,26H6V20H26Zm-4-8H10V14H22Z"/>
<rect id="_Transparent_Rectangle_" data-name="&lt;Transparent Rectangle&gt;" class="cls-1" width="32" height="32"/>
</svg>

Before

Width:  |  Height:  |  Size: 704 B

View File

@@ -1,14 +0,0 @@
import defineApp from '../../helpers/define-app.js';
import actions from './actions/index.js';
export default defineApp({
name: 'Datastore',
key: 'datastore',
iconUrl: '{BASE_URL}/apps/datastore/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/datastore/connection',
supportsConnections: false,
baseUrl: '',
apiBaseUrl: '',
primaryColor: '001F52',
actions,
});

View File

@@ -2,6 +2,7 @@ import defineApp from '../../helpers/define-app.js';
import addAuthHeader from './common/add-auth-header.js';
import auth from './auth/index.js';
import actions from './actions/index.js';
import triggers from './triggers/index.js';
export default defineApp({
name: 'Dropbox',
@@ -15,4 +16,5 @@ export default defineApp({
beforeRequest: [addAuthHeader],
auth,
actions,
triggers,
});

View File

@@ -0,0 +1,3 @@
import newFolders from './new-folders/index.js';
export default [newFolders];

View File

@@ -0,0 +1,61 @@
import defineTrigger from '../../../../helpers/define-trigger.js';
export default defineTrigger({
name: 'New folders',
key: 'newFolders',
pollInterval: 15,
description:
'Triggers when any new folder is added. Ensure that the number of files/folders within the monitored directory remains below 4000.',
arguments: [
{
label: 'Folder',
key: 'folderPath',
type: 'string',
required: true,
description:
'Enter the folder path that you want to follow, like /TextFiles or /Documents/Taxes.',
variables: true,
},
],
async run($) {
const folderPath = $.step.parameters.folderPath;
let endpoint = '/2/files/list_folder';
let next = false;
const params = {
path: folderPath,
recursive: false,
include_deleted: false,
include_has_explicit_shared_members: false,
include_mounted_folders: true,
limit: 2000,
include_non_downloadable_files: true,
};
do {
const { data } = await $.http.post(endpoint, params);
if (data.has_more) {
endpoint += '/continue';
params.cursor = data.cursor;
next = data.has_more;
} else {
next = false;
}
if (data.entries?.length) {
for (const entry of data.entries.reverse()) {
if (entry['.tag'] === 'folder') {
$.pushTriggerItem({
raw: entry,
meta: {
internalId: entry.id,
},
});
}
}
}
} while (next);
},
});

View File

@@ -1,3 +1,4 @@
import FormData from 'form-data';
import defineAction from '../../../../helpers/define-action.js';
export default defineAction({
@@ -5,51 +6,45 @@ export default defineAction({
key: 'newChat',
description: 'Create a new chat session for Helix AI.',
arguments: [
{
label: 'Session ID',
key: 'sessionId',
type: 'string',
required: false,
description:
'ID of the chat session to continue. Leave empty to start a new chat.',
variables: true,
},
{
label: 'System Prompt',
key: 'systemPrompt',
type: 'string',
required: false,
description:
'Optional system prompt to start the chat with. It will be used only for new chat sessions.',
variables: true,
},
{
label: 'Input',
key: 'input',
type: 'string',
required: true,
description: 'User input to start the chat with.',
description: 'Prompt to start the chat with.',
variables: true,
},
],
async run($) {
const response = await $.http.post('/api/v1/sessions/chat', {
session_id: $.step.parameters.sessionId,
system: $.step.parameters.systemPrompt,
messages: [
{
role: 'user',
content: {
content_type: 'text',
parts: [$.step.parameters.input],
const formData = new FormData();
formData.append('input', $.step.parameters.input);
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: response.data,
raw: message,
});
chatGenerated = true;
}
}
},
});

View File

@@ -94,8 +94,6 @@ const appConfig = {
disableFavicon: process.env.DISABLE_FAVICON === 'true',
additionalDrawerLink: process.env.ADDITIONAL_DRAWER_LINK,
additionalDrawerLinkText: process.env.ADDITIONAL_DRAWER_LINK_TEXT,
seedUserEmail: process.env.SEED_USER_EMAIL || 'user@automatisch.io',
seedUserPassword: process.env.SEED_USER_PASSWORD || 'sample',
};
if (!appConfig.encryptionKey) {

View File

@@ -1,6 +1,5 @@
import { vi, describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import Crypto from 'crypto';
import app from '../../../../../app.js';
import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js';
import { createUser } from '../../../../../../test/factories/user.js';
@@ -32,21 +31,5 @@ describe('GET /api/v1/admin/app-auth-clients/:appAuthClientId', () => {
const expectedPayload = getAdminAppAuthClientMock(currentAppAuthClient);
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for not existing app auth client UUID', async () => {
const notExistingAppAuthClientUUID = Crypto.randomUUID();
await request(app)
.get(`/api/v1/admin/app-auth-clients/${notExistingAppAuthClientUUID}`)
.set('Authorization', token)
.expect(404);
});
it('should return bad request response for invalid UUID', async () => {
await request(app)
.get('/api/v1/admin/app-auth-clients/invalidAppAuthClientUUID')
.set('Authorization', token)
.expect(400);
});
});
});

View File

@@ -1,6 +1,5 @@
import { vi, describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import Crypto from 'crypto';
import app from '../../../../../app.js';
import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js';
import { createRole } from '../../../../../../test/factories/role.js';
@@ -21,7 +20,7 @@ describe('GET /api/v1/admin/roles/:roleId', () => {
token = createAuthTokenByUserId(currentUser.id);
});
it('should return role', async () => {
it('should return roles', async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
const response = await request(app)
@@ -36,24 +35,4 @@ describe('GET /api/v1/admin/roles/:roleId', () => {
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for not existing role UUID', async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
const notExistingRoleUUID = Crypto.randomUUID();
await request(app)
.get(`/api/v1/admin/roles/${notExistingRoleUUID}`)
.set('Authorization', token)
.expect(404);
});
it('should return bad request response for invalid UUID', async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
await request(app)
.get('/api/v1/admin/roles/invalidRoleUUID')
.set('Authorization', token)
.expect(400);
});
});

View File

@@ -1,6 +1,5 @@
import { vi, describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import Crypto from 'crypto';
import app from '../../../../../app.js';
import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id.js';
import { createRole } from '../../../../../../test/factories/role.js';
@@ -32,26 +31,4 @@ describe('GET /api/v1/admin/saml-auth-provider/:samlAuthProviderId', () => {
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for not existing saml auth provider UUID', async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
const notExistingSamlAuthProviderUUID = Crypto.randomUUID();
await request(app)
.get(
`/api/v1/admin/saml-auth-providers/${notExistingSamlAuthProviderUUID}`
)
.set('Authorization', token)
.expect(404);
});
it('should return bad request response for invalid UUID', async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
await request(app)
.get('/api/v1/admin/saml-auth-providers/invalidSamlAuthProviderUUID')
.set('Authorization', token)
.expect(400);
});
});

View File

@@ -1,6 +1,5 @@
import { vi, describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import Crypto from 'crypto';
import app from '../../../../../app.js';
import createAuthTokenByUserId from '../../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../../test/factories/user';
@@ -32,24 +31,4 @@ describe('GET /api/v1/admin/users/:userId', () => {
const expectedPayload = getUserMock(anotherUser, anotherUserRole);
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for not existing user UUID', async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
const notExistingUserUUID = Crypto.randomUUID();
await request(app)
.get(`/api/v1/admin/users/${notExistingUserUUID}`)
.set('Authorization', token)
.expect(404);
});
it('should return bad request response for invalid UUID', async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
await request(app)
.get('/api/v1/admin/users/invalidUserUUID')
.set('Authorization', token)
.expect(400);
});
});

View File

@@ -1,6 +1,5 @@
import { vi, describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import Crypto from 'crypto';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id.js';
import { createUser } from '../../../../../test/factories/user.js';
@@ -29,20 +28,4 @@ describe('GET /api/v1/app-auth-clients/:id', () => {
const expectedPayload = getAppAuthClientMock(currentAppAuthClient);
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for not existing app auth client ID', async () => {
const notExistingAppAuthClientUUID = Crypto.randomUUID();
await request(app)
.get(`/api/v1/app-auth-clients/${notExistingAppAuthClientUUID}`)
.set('Authorization', token)
.expect(404);
});
it('should return bad request response for invalid UUID', async () => {
await request(app)
.get('/api/v1/app-auth-clients/invalidAppAuthClientUUID')
.set('Authorization', token)
.expect(400);
});
});

View File

@@ -1,11 +0,0 @@
import App from '../../../../models/app.js';
import { renderObject } from '../../../../helpers/renderer.js';
export default async (request, response) => {
const substeps = await App.findActionSubsteps(
request.params.appKey,
request.params.actionKey
);
renderObject(response, substeps);
};

View File

@@ -1,52 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import App from '../../../../models/app';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../test/factories/user';
import getActionSubstepsMock from '../../../../../test/mocks/rest/api/v1/apps/get-action-substeps.js';
describe('GET /api/v1/apps/:appKey/actions/:actionKey/substeps', () => {
let currentUser, exampleApp, token;
beforeEach(async () => {
currentUser = await createUser();
token = createAuthTokenByUserId(currentUser.id);
exampleApp = await App.findOneByKey('github');
});
it('should return the app auth info', async () => {
const actions = await App.findActionsByKey('github');
const exampleAction = actions.find(
(action) => action.key === 'createIssue'
);
const endpointUrl = `/api/v1/apps/${exampleApp.key}/actions/${exampleAction.key}/substeps`;
const response = await request(app)
.get(endpointUrl)
.set('Authorization', token)
.expect(200);
const expectedPayload = getActionSubstepsMock(exampleAction.substeps);
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for invalid app key', async () => {
await request(app)
.get('/api/v1/apps/invalid-app-key/actions/invalid-actions-key/substeps')
.set('Authorization', token)
.expect(404);
});
it('should return empty array for invalid action key', async () => {
const endpointUrl = `/api/v1/apps/${exampleApp.key}/actions/invalid-action-key/substeps`;
const response = await request(app)
.get(endpointUrl)
.set('Authorization', token)
.expect(200);
expect(response.body.data).toEqual([]);
});
});

View File

@@ -1,8 +0,0 @@
import App from '../../../../models/app.js';
import { renderObject } from '../../../../helpers/renderer.js';
export default async (request, response) => {
const actions = await App.findActionsByKey(request.params.appKey);
renderObject(response, actions, { serializer: 'Action' });
};

View File

@@ -1,35 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import App from '../../../../models/app';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../test/factories/user';
import getActionsMock from '../../../../../test/mocks/rest/api/v1/apps/get-actions.js';
describe('GET /api/v1/apps/:appKey/actions', () => {
let currentUser, token;
beforeEach(async () => {
currentUser = await createUser();
token = createAuthTokenByUserId(currentUser.id);
});
it('should return the app actions', async () => {
const exampleApp = await App.findOneByKey('github');
const response = await request(app)
.get(`/api/v1/apps/${exampleApp.key}/actions`)
.set('Authorization', token)
.expect(200);
const expectedPayload = getActionsMock(exampleApp.actions);
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for invalid app key', async () => {
await request(app)
.get('/api/v1/apps/invalid-app-key/actions')
.set('Authorization', token)
.expect(404);
});
});

View File

@@ -1,8 +0,0 @@
import App from '../../../../models/app.js';
import { renderObject } from '../../../../helpers/renderer.js';
export default async (request, response) => {
const app = await App.findOneByKey(request.params.appKey);
renderObject(response, app, { serializer: 'App' });
};

View File

@@ -1,35 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import App from '../../../../models/app';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../test/factories/user';
import getAppMock from '../../../../../test/mocks/rest/api/v1/apps/get-app.js';
describe('GET /api/v1/apps/:appKey', () => {
let currentUser, token;
beforeEach(async () => {
currentUser = await createUser();
token = createAuthTokenByUserId(currentUser.id);
});
it('should return the app info', async () => {
const exampleApp = await App.findOneByKey('github');
const response = await request(app)
.get(`/api/v1/apps/${exampleApp.key}`)
.set('Authorization', token)
.expect(200);
const expectedPayload = getAppMock(exampleApp);
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for invalid app key', async () => {
await request(app)
.get('/api/v1/apps/invalid-app-key')
.set('Authorization', token)
.expect(404);
});
});

View File

@@ -1,16 +0,0 @@
import App from '../../../../models/app.js';
import { renderObject } from '../../../../helpers/renderer.js';
export default async (request, response) => {
let apps = await App.findAll(request.query.name);
if (request.query.onlyWithTriggers) {
apps = apps.filter((app) => app.triggers?.length);
}
if (request.query.onlyWithActions) {
apps = apps.filter((app) => app.actions?.length);
}
renderObject(response, apps, { serializer: 'App' });
};

View File

@@ -1,63 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import App from '../../../../models/app';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../test/factories/user';
import getAppsMock from '../../../../../test/mocks/rest/api/v1/apps/get-apps.js';
describe('GET /api/v1/apps', () => {
let currentUser, apps, token;
beforeEach(async () => {
currentUser = await createUser();
token = createAuthTokenByUserId(currentUser.id);
apps = await App.findAll();
});
it('should return all apps', async () => {
const response = await request(app)
.get('/api/v1/apps')
.set('Authorization', token)
.expect(200);
const expectedPayload = getAppsMock(apps);
expect(response.body).toEqual(expectedPayload);
});
it('should return all apps filtered by name', async () => {
const appsWithNameGit = apps.filter((app) => app.name.includes('Git'));
const response = await request(app)
.get('/api/v1/apps?name=Git')
.set('Authorization', token)
.expect(200);
const expectedPayload = getAppsMock(appsWithNameGit);
expect(response.body).toEqual(expectedPayload);
});
it('should return only the apps with triggers', async () => {
const appsWithTriggers = apps.filter((app) => app.triggers?.length > 0);
const response = await request(app)
.get('/api/v1/apps?onlyWithTriggers=true')
.set('Authorization', token)
.expect(200);
const expectedPayload = getAppsMock(appsWithTriggers);
expect(response.body).toEqual(expectedPayload);
});
it('should return only the apps with actions', async () => {
const appsWithActions = apps.filter((app) => app.actions?.length > 0);
const response = await request(app)
.get('/api/v1/apps?onlyWithActions=true')
.set('Authorization', token)
.expect(200);
const expectedPayload = getAppsMock(appsWithActions);
expect(response.body).toEqual(expectedPayload);
});
});

View File

@@ -1,8 +0,0 @@
import App from '../../../../models/app.js';
import { renderObject } from '../../../../helpers/renderer.js';
export default async (request, response) => {
const auth = await App.findAuthByKey(request.params.appKey);
renderObject(response, auth, { serializer: 'Auth' });
};

View File

@@ -1,35 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import App from '../../../../models/app';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../test/factories/user';
import getAuthMock from '../../../../../test/mocks/rest/api/v1/apps/get-auth.js';
describe('GET /api/v1/apps/:appKey/auth', () => {
let currentUser, token;
beforeEach(async () => {
currentUser = await createUser();
token = createAuthTokenByUserId(currentUser.id);
});
it('should return the app auth info', async () => {
const exampleApp = await App.findOneByKey('github');
const response = await request(app)
.get(`/api/v1/apps/${exampleApp.key}/auth`)
.set('Authorization', token)
.expect(200);
const expectedPayload = getAuthMock(exampleApp.auth);
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for invalid app key', async () => {
await request(app)
.get('/api/v1/apps/invalid-app-key/auth')
.set('Authorization', token)
.expect(404);
});
});

View File

@@ -1,11 +0,0 @@
import App from '../../../../models/app.js';
import { renderObject } from '../../../../helpers/renderer.js';
export default async (request, response) => {
const substeps = await App.findTriggerSubsteps(
request.params.appKey,
request.params.triggerKey
);
renderObject(response, substeps);
};

View File

@@ -1,52 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import App from '../../../../models/app';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../test/factories/user';
import getTriggerSubstepsMock from '../../../../../test/mocks/rest/api/v1/apps/get-trigger-substeps.js';
describe('GET /api/v1/apps/:appKey/triggers/:triggerKey/substeps', () => {
let currentUser, exampleApp, token;
beforeEach(async () => {
currentUser = await createUser();
token = createAuthTokenByUserId(currentUser.id);
exampleApp = await App.findOneByKey('github');
});
it('should return the app auth info', async () => {
const triggers = await App.findTriggersByKey('github');
const exampleTrigger = triggers.find(
(trigger) => trigger.key === 'newIssues'
);
const endpointUrl = `/api/v1/apps/${exampleApp.key}/triggers/${exampleTrigger.key}/substeps`;
const response = await request(app)
.get(endpointUrl)
.set('Authorization', token)
.expect(200);
const expectedPayload = getTriggerSubstepsMock(exampleTrigger.substeps);
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for invalid app key', async () => {
await request(app)
.get('/api/v1/apps/invalid-app-key/triggers/invalid-trigger-key/substeps')
.set('Authorization', token)
.expect(404);
});
it('should return empty array for invalid trigger key', async () => {
const endpointUrl = `/api/v1/apps/${exampleApp.key}/triggers/invalid-trigger-key/substeps`;
const response = await request(app)
.get(endpointUrl)
.set('Authorization', token)
.expect(200);
expect(response.body.data).toEqual([]);
});
});

View File

@@ -1,8 +0,0 @@
import App from '../../../../models/app.js';
import { renderObject } from '../../../../helpers/renderer.js';
export default async (request, response) => {
const triggers = await App.findTriggersByKey(request.params.appKey);
renderObject(response, triggers, { serializer: 'Trigger' });
};

View File

@@ -1,35 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import App from '../../../../models/app';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../test/factories/user';
import getTriggersMock from '../../../../../test/mocks/rest/api/v1/apps/get-triggers.js';
describe('GET /api/v1/apps/:appKey/triggers', () => {
let currentUser, token;
beforeEach(async () => {
currentUser = await createUser();
token = createAuthTokenByUserId(currentUser.id);
});
it('should return the app triggers', async () => {
const exampleApp = await App.findOneByKey('github');
const response = await request(app)
.get(`/api/v1/apps/${exampleApp.key}/triggers`)
.set('Authorization', token)
.expect(200);
const expectedPayload = getTriggersMock(exampleApp.triggers);
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for invalid app key', async () => {
await request(app)
.get('/api/v1/apps/invalid-app-key/triggers')
.set('Authorization', token)
.expect(404);
});
});

View File

@@ -1,23 +0,0 @@
import { renderObject } from '../../../../helpers/renderer.js';
import paginateRest from '../../../../helpers/pagination-rest.js';
export default async (request, response) => {
const execution = await request.currentUser.authorizedExecutions
.clone()
.withSoftDeleted()
.findById(request.params.executionId)
.throwIfNotFound();
const executionStepsQuery = execution
.$relatedQuery('executionSteps')
.withSoftDeleted()
.withGraphFetched('step')
.orderBy('created_at', 'asc');
const executionSteps = await paginateRest(
executionStepsQuery,
request.query.page
);
renderObject(response, executionSteps);
};

View File

@@ -1,153 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import Crypto from 'crypto';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../test/factories/user';
import { createFlow } from '../../../../../test/factories/flow.js';
import { createStep } from '../../../../../test/factories/step.js';
import { createExecution } from '../../../../../test/factories/execution.js';
import { createExecutionStep } from '../../../../../test/factories/execution-step.js';
import { createPermission } from '../../../../../test/factories/permission';
import getExecutionStepsMock from '../../../../../test/mocks/rest/api/v1/executions/get-execution-steps';
describe('GET /api/v1/executions/:executionId/execution-steps', () => {
let currentUser, currentUserRole, anotherUser, token;
beforeEach(async () => {
currentUser = await createUser();
currentUserRole = await currentUser.$relatedQuery('role');
anotherUser = await createUser();
token = createAuthTokenByUserId(currentUser.id);
});
it('should return the execution steps of current user execution', async () => {
const currentUserFlow = await createFlow({
userId: currentUser.id,
});
const stepOne = await createStep({
flowId: currentUserFlow.id,
type: 'trigger',
});
const stepTwo = await createStep({
flowId: currentUserFlow.id,
type: 'action',
});
const currentUserExecution = await createExecution({
flowId: currentUserFlow.id,
});
const currentUserExecutionStepOne = await createExecutionStep({
executionId: currentUserExecution.id,
stepId: stepOne.id,
});
const currentUserExecutionStepTwo = await createExecutionStep({
executionId: currentUserExecution.id,
stepId: stepTwo.id,
});
await createPermission({
action: 'read',
subject: 'Execution',
roleId: currentUserRole.id,
conditions: ['isCreator'],
});
const response = await request(app)
.get(`/api/v1/executions/${currentUserExecution.id}/execution-steps`)
.set('Authorization', token)
.expect(200);
const expectedPayload = await getExecutionStepsMock(
[currentUserExecutionStepOne, currentUserExecutionStepTwo],
[stepOne, stepTwo]
);
expect(response.body).toEqual(expectedPayload);
});
it('should return the execution steps of another user execution', async () => {
const anotherUserFlow = await createFlow({
userId: anotherUser.id,
});
const stepOne = await createStep({
flowId: anotherUserFlow.id,
type: 'trigger',
});
const stepTwo = await createStep({
flowId: anotherUserFlow.id,
type: 'action',
});
const anotherUserExecution = await createExecution({
flowId: anotherUserFlow.id,
});
const anotherUserExecutionStepOne = await createExecutionStep({
executionId: anotherUserExecution.id,
stepId: stepOne.id,
});
const anotherUserExecutionStepTwo = await createExecutionStep({
executionId: anotherUserExecution.id,
stepId: stepTwo.id,
});
await createPermission({
action: 'read',
subject: 'Execution',
roleId: currentUserRole.id,
conditions: [],
});
const response = await request(app)
.get(`/api/v1/executions/${anotherUserExecution.id}/execution-steps`)
.set('Authorization', token)
.expect(200);
const expectedPayload = await getExecutionStepsMock(
[anotherUserExecutionStepOne, anotherUserExecutionStepTwo],
[stepOne, stepTwo]
);
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for not existing execution step UUID', async () => {
await createPermission({
action: 'read',
subject: 'Execution',
roleId: currentUserRole.id,
conditions: [],
});
const notExistingExcecutionUUID = Crypto.randomUUID();
await request(app)
.get(`/api/v1/executions/${notExistingExcecutionUUID}/execution-steps`)
.set('Authorization', token)
.expect(404);
});
it('should return bad request response for invalid UUID', async () => {
await createPermission({
action: 'read',
subject: 'Execution',
roleId: currentUserRole.id,
conditions: [],
});
await request(app)
.get('/api/v1/executions/invalidExecutionUUID/execution-steps')
.set('Authorization', token)
.expect(400);
});
});

View File

@@ -1,15 +0,0 @@
import { renderObject } from '../../../../helpers/renderer.js';
export default async (request, response) => {
const execution = await request.currentUser.authorizedExecutions
.withGraphFetched({
flow: {
steps: true,
},
})
.withSoftDeleted()
.findById(request.params.executionId)
.throwIfNotFound();
renderObject(response, execution);
};

View File

@@ -1,134 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import Crypto from 'crypto';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../test/factories/user';
import { createFlow } from '../../../../../test/factories/flow.js';
import { createStep } from '../../../../../test/factories/step.js';
import { createExecution } from '../../../../../test/factories/execution.js';
import { createPermission } from '../../../../../test/factories/permission';
import getExecutionMock from '../../../../../test/mocks/rest/api/v1/executions/get-execution';
describe('GET /api/v1/executions/:executionId', () => {
let currentUser, currentUserRole, token;
beforeEach(async () => {
currentUser = await createUser();
currentUserRole = await currentUser.$relatedQuery('role');
token = createAuthTokenByUserId(currentUser.id);
});
it('should return the execution data of current user', async () => {
const currentUserFlow = await createFlow({
userId: currentUser.id,
});
const stepOne = await createStep({
flowId: currentUserFlow.id,
type: 'trigger',
});
const stepTwo = await createStep({
flowId: currentUserFlow.id,
type: 'action',
});
const currentUserExecution = await createExecution({
flowId: currentUserFlow.id,
});
await createPermission({
action: 'read',
subject: 'Execution',
roleId: currentUserRole.id,
conditions: ['isCreator'],
});
const response = await request(app)
.get(`/api/v1/executions/${currentUserExecution.id}`)
.set('Authorization', token)
.expect(200);
const expectedPayload = await getExecutionMock(
currentUserExecution,
currentUserFlow,
[stepOne, stepTwo]
);
expect(response.body).toEqual(expectedPayload);
});
it('should return the execution data of another user', async () => {
const anotherUser = await createUser();
const anotherUserFlow = await createFlow({
userId: anotherUser.id,
});
const stepOne = await createStep({
flowId: anotherUserFlow.id,
type: 'trigger',
});
const stepTwo = await createStep({
flowId: anotherUserFlow.id,
type: 'action',
});
const anotherUserExecution = await createExecution({
flowId: anotherUserFlow.id,
});
await createPermission({
action: 'read',
subject: 'Execution',
roleId: currentUserRole.id,
conditions: [],
});
const response = await request(app)
.get(`/api/v1/executions/${anotherUserExecution.id}`)
.set('Authorization', token)
.expect(200);
const expectedPayload = await getExecutionMock(
anotherUserExecution,
anotherUserFlow,
[stepOne, stepTwo]
);
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for not existing execution UUID', async () => {
await createPermission({
action: 'read',
subject: 'Execution',
roleId: currentUserRole.id,
conditions: [],
});
const notExistingExcecutionUUID = Crypto.randomUUID();
await request(app)
.get(`/api/v1/executions/${notExistingExcecutionUUID}`)
.set('Authorization', token)
.expect(404);
});
it('should return bad request response for invalid UUID', async () => {
await createPermission({
action: 'read',
subject: 'Execution',
roleId: currentUserRole.id,
conditions: [],
});
await request(app)
.get('/api/v1/executions/invalidExecutionUUID')
.set('Authorization', token)
.expect(400);
});
});

View File

@@ -1,26 +0,0 @@
import { renderObject } from '../../../../helpers/renderer.js';
import paginateRest from '../../../../helpers/pagination-rest.js';
export default async (request, response) => {
const executionsQuery = request.currentUser.authorizedExecutions
.withSoftDeleted()
.orderBy('created_at', 'desc')
.withGraphFetched({
flow: {
steps: true,
},
});
const executions = await paginateRest(executionsQuery, request.query.page);
for (const execution of executions.records) {
const executionSteps = await execution.$relatedQuery('executionSteps');
const status = executionSteps.some((step) => step.status === 'failure')
? 'failure'
: 'success';
execution.status = status;
}
renderObject(response, executions);
};

View File

@@ -1,113 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../test/factories/user';
import { createFlow } from '../../../../../test/factories/flow.js';
import { createStep } from '../../../../../test/factories/step.js';
import { createExecution } from '../../../../../test/factories/execution.js';
import { createPermission } from '../../../../../test/factories/permission';
import getExecutionsMock from '../../../../../test/mocks/rest/api/v1/executions/get-executions';
describe('GET /api/v1/executions', () => {
let currentUser, currentUserRole, anotherUser, token;
beforeEach(async () => {
currentUser = await createUser();
currentUserRole = await currentUser.$relatedQuery('role');
anotherUser = await createUser();
token = createAuthTokenByUserId(currentUser.id);
});
it('should return the executions of current user', async () => {
const currentUserFlow = await createFlow({
userId: currentUser.id,
});
const stepOne = await createStep({
flowId: currentUserFlow.id,
type: 'trigger',
});
const stepTwo = await createStep({
flowId: currentUserFlow.id,
type: 'action',
});
const currentUserExecutionOne = await createExecution({
flowId: currentUserFlow.id,
});
const currentUserExecutionTwo = await createExecution({
flowId: currentUserFlow.id,
deletedAt: new Date().toISOString(),
});
await createPermission({
action: 'read',
subject: 'Execution',
roleId: currentUserRole.id,
conditions: ['isCreator'],
});
const response = await request(app)
.get('/api/v1/executions')
.set('Authorization', token)
.expect(200);
const expectedPayload = await getExecutionsMock(
[currentUserExecutionTwo, currentUserExecutionOne],
currentUserFlow,
[stepOne, stepTwo]
);
expect(response.body).toEqual(expectedPayload);
});
it('should return the executions of another user', async () => {
const anotherUserFlow = await createFlow({
userId: anotherUser.id,
});
const stepOne = await createStep({
flowId: anotherUserFlow.id,
type: 'trigger',
});
const stepTwo = await createStep({
flowId: anotherUserFlow.id,
type: 'action',
});
const anotherUserExecutionOne = await createExecution({
flowId: anotherUserFlow.id,
});
const anotherUserExecutionTwo = await createExecution({
flowId: anotherUserFlow.id,
deletedAt: new Date().toISOString(),
});
await createPermission({
action: 'read',
subject: 'Execution',
roleId: currentUserRole.id,
conditions: [],
});
const response = await request(app)
.get('/api/v1/executions')
.set('Authorization', token)
.expect(200);
const expectedPayload = await getExecutionsMock(
[anotherUserExecutionTwo, anotherUserExecutionOne],
anotherUserFlow,
[stepOne, stepTwo]
);
expect(response.body).toEqual(expectedPayload);
});
});

View File

@@ -1,6 +1,5 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import Crypto from 'crypto';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createUser } from '../../../../../test/factories/user';
@@ -69,34 +68,4 @@ describe('GET /api/v1/flows/:flowId', () => {
expect(response.body).toEqual(expectedPayload);
});
it('should return not found response for not existing flow UUID', async () => {
await createPermission({
action: 'read',
subject: 'Flow',
roleId: currentUserRole.id,
conditions: [],
});
const notExistingFlowUUID = Crypto.randomUUID();
await request(app)
.get(`/api/v1/flows/${notExistingFlowUUID}`)
.set('Authorization', token)
.expect(404);
});
it('should return bad request response for invalid UUID', async () => {
await createPermission({
action: 'read',
subject: 'Flow',
roleId: currentUserRole.id,
conditions: [],
});
await request(app)
.get('/api/v1/flows/invalidFlowUUID')
.set('Authorization', token)
.expect(400);
});
});

View File

@@ -2,29 +2,15 @@ import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import app from '../../../../app.js';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createPermission } from '../../../../../test/factories/permission';
import { createRole } from '../../../../../test/factories/role';
import { createUser } from '../../../../../test/factories/user';
import getCurrentUserMock from '../../../../../test/mocks/rest/api/v1/users/get-current-user';
describe('GET /api/v1/users/me', () => {
let role, permissionOne, permissionTwo, currentUser, token;
let role, currentUser, token;
beforeEach(async () => {
role = await createRole();
permissionOne = await createPermission({
roleId: role.id,
});
permissionTwo = await createPermission({
roleId: role.id,
});
currentUser = await createUser({
roleId: role.id,
});
currentUser = await createUser();
role = await currentUser.$relatedQuery('role');
token = createAuthTokenByUserId(currentUser.id);
});
@@ -34,11 +20,7 @@ describe('GET /api/v1/users/me', () => {
.set('Authorization', token)
.expect(200);
const expectedPayload = getCurrentUserMock(currentUser, role, [
permissionOne,
permissionTwo,
]);
const expectedPayload = getCurrentUserMock(currentUser, role);
expect(response.body).toEqual(expectedPayload);
});
});

View File

@@ -1,16 +0,0 @@
export async function up(knex) {
return knex.schema.createTable('datastore', (table) => {
table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid()'));
table.string('key').notNullable();
table.string('value');
table.string('scope').notNullable();
table.uuid('scope_id').notNullable();
table.index(['key', 'scope', 'scope_id']);
table.timestamps(true, true);
});
}
export async function down(knex) {
return knex.schema.dropTable('datastore');
}

View File

@@ -0,0 +1,21 @@
import appConfig from '../../config/app.js';
import { getLicense } from '../../helpers/license.ee.js';
const getAutomatischInfo = async () => {
const license = await getLicense();
const computedLicense = {
id: license ? license.id : null,
name: license ? license.name : null,
expireAt: license ? license.expireAt : null,
verified: license ? true : false,
};
return {
isCloud: appConfig.isCloud,
isMation: appConfig.isMation,
license: computedLicense,
};
};
export default getAutomatischInfo;

View File

@@ -0,0 +1,190 @@
import { vi, describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import app from '../../app';
import * as license from '../../helpers/license.ee';
import appConfig from '../../config/app';
describe('graphQL getAutomatischInfo query', () => {
const query = `
query {
getAutomatischInfo {
isCloud
isMation
license {
id
name
expireAt
verified
}
}
}
`;
describe('and without valid license', () => {
beforeEach(async () => {
vi.spyOn(license, 'getLicense').mockResolvedValue(false);
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
vi.spyOn(appConfig, 'isMation', 'get').mockReturnValue(false);
});
it('should return empty license data', async () => {
const response = await request(app)
.post('/graphql')
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getAutomatischInfo: {
isCloud: false,
isMation: false,
license: {
id: null,
name: null,
expireAt: null,
verified: false,
},
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
});
describe('and with valid license', () => {
beforeEach(async () => {
const mockedLicense = {
id: '123123',
name: 'Test License',
expireAt: '2025-08-09T10:56:54.144Z',
verified: true,
};
vi.spyOn(license, 'getLicense').mockResolvedValue(mockedLicense);
});
describe('and with cloud flag enabled', () => {
beforeEach(async () => {
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
});
it('should return all license data', async () => {
const response = await request(app)
.post('/graphql')
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getAutomatischInfo: {
isCloud: true,
isMation: false,
license: {
expireAt: '2025-08-09T10:56:54.144Z',
id: '123123',
name: 'Test License',
verified: true,
},
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
});
describe('and with cloud flag disabled', () => {
beforeEach(async () => {
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
});
it('should return all license data', async () => {
const response = await request(app)
.post('/graphql')
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getAutomatischInfo: {
isCloud: false,
isMation: false,
license: {
expireAt: '2025-08-09T10:56:54.144Z',
id: '123123',
name: 'Test License',
verified: true,
},
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
});
describe('and with mation flag enabled', () => {
beforeEach(async () => {
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
vi.spyOn(appConfig, 'isMation', 'get').mockReturnValue(true);
});
it('should return all license data', async () => {
const response = await request(app)
.post('/graphql')
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getAutomatischInfo: {
isCloud: false,
isMation: true,
license: {
expireAt: '2025-08-09T10:56:54.144Z',
id: '123123',
name: 'Test License',
verified: true,
},
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
});
describe('and with mation flag disabled', () => {
beforeEach(async () => {
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
vi.spyOn(appConfig, 'isMation', 'get').mockReturnValue(false);
});
it('should return all license data', async () => {
const response = await request(app)
.post('/graphql')
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getAutomatischInfo: {
isMation: false,
isCloud: false,
license: {
expireAt: '2025-08-09T10:56:54.144Z',
id: '123123',
name: 'Test License',
verified: true,
},
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
});
});
});

View File

@@ -3,6 +3,7 @@ import getAppAuthClient from './queries/get-app-auth-client.ee.js';
import getAppAuthClients from './queries/get-app-auth-clients.ee.js';
import getAppConfig from './queries/get-app-config.ee.js';
import getApps from './queries/get-apps.js';
import getAutomatischInfo from './queries/get-automatisch-info.js';
import getBillingAndUsage from './queries/get-billing-and-usage.ee.js';
import getConfig from './queries/get-config.ee.js';
import getConnectedApps from './queries/get-connected-apps.js';
@@ -38,6 +39,7 @@ const queryResolvers = {
getAppAuthClients,
getAppConfig,
getApps,
getAutomatischInfo,
getBillingAndUsage,
getConfig,
getConnectedApps,

View File

@@ -40,6 +40,7 @@ type Query {
key: String!
parameters: JSONObject
): [SubstepArgument]
getAutomatischInfo: GetAutomatischInfo
getBillingAndUsage: GetBillingAndUsage
getCurrentUser: User
getConfig(keys: [String]): JSONObject
@@ -643,6 +644,12 @@ type AppHealth {
version: String
}
type GetAutomatischInfo {
isCloud: Boolean
isMation: Boolean
license: License
}
type License {
id: String
name: String

View File

@@ -42,6 +42,7 @@ const isAuthenticatedRule = rule()(isAuthenticated);
export const authenticationRules = {
Query: {
'*': isAuthenticatedRule,
getAutomatischInfo: allow,
getConfig: allow,
getNotifications: allow,
healthcheck: allow,

View File

@@ -11,18 +11,6 @@ const authorizationList = {
action: 'read',
subject: 'Flow',
},
'GET /api/v1/executions/:executionId': {
action: 'read',
subject: 'Execution',
},
'GET /api/v1/executions/': {
action: 'read',
subject: 'Execution',
},
'GET /api/v1/executions/:executionId/execution-steps': {
action: 'read',
subject: 'Execution',
},
};
export const authorizeUser = async (request, response, next) => {

View File

@@ -1,45 +1,14 @@
import logger from './logger.js';
import objection from 'objection';
import * as Sentry from './sentry.ee.js';
const { NotFoundError, DataError } = objection;
// Do not remove `next` argument as the function signature will not fit for an error handler middleware
// eslint-disable-next-line no-unused-vars
const errorHandler = (error, request, response, next) => {
if (error.message === 'Not Found' || error instanceof NotFoundError) {
response.status(404).end();
const errorHandler = (err, req, res, next) => {
if (err.message === 'Not Found') {
res.status(404).end();
} else {
logger.error(err.message + '\n' + err.stack);
res.status(err.statusCode || 500).send(err.message);
}
if (notFoundAppError(error)) {
response.status(404).end();
}
if (error instanceof DataError) {
response.status(400).end();
}
const statusCode = error.statusCode || 500;
logger.error(request.method + ' ' + request.url + ' ' + statusCode);
logger.error(error.stack);
Sentry.captureException(error, {
tags: { rest: true },
extra: {
url: request?.url,
method: request?.method,
params: request?.params,
},
});
response.status(statusCode).end();
};
const notFoundAppError = (error) => {
return (
error.message.includes('An application with the') ||
error.message.includes("key couldn't be found.")
);
};
export default errorHandler;

View File

@@ -1,7 +1,6 @@
import createHttpClient from './http-client/index.js';
import EarlyExitError from '../errors/early-exit.js';
import AlreadyProcessedError from '../errors/already-processed.js';
import Datastore from '../models/datastore.js';
const globalVariable = async (options) => {
const {
@@ -89,43 +88,6 @@ const globalVariable = async (options) => {
setActionItem: (actionItem) => {
$.actionOutput.data = actionItem;
},
datastore: {
get: async ({ key }) => {
const datastore = await Datastore.query().findOne({
key,
scope: 'flow',
scope_id: $.flow.id,
});
return {
key: datastore.key,
value: datastore.value,
[datastore.key]: datastore.value,
};
},
set: async ({ key, value }) => {
let datastore = await Datastore.query()
.where({ key, scope: 'flow', scope_id: $.flow.id })
.first();
if (datastore) {
await datastore.$query().patchAndFetch({ value: value });
} else {
datastore = await Datastore.query().insert({
key,
value,
scope: 'flow',
scopeId: $.flow.id,
});
}
return {
key: datastore.key,
value: datastore.value,
[datastore.key]: datastore.value,
};
},
},
};
if (request) {

View File

@@ -11,7 +11,7 @@ const isArray = (object) =>
const totalCount = (object) =>
isPaginated(object) ? object.totalCount : isArray(object) ? object.length : 1;
const renderObject = (response, object, options) => {
const renderObject = (response, object) => {
let data = isPaginated(object) ? object.records : object;
const type = isPaginated(object)
@@ -20,9 +20,7 @@ const renderObject = (response, object, options) => {
? object?.[0]?.constructor?.name || 'Object'
: object.constructor.name;
const serializer = options?.serializer
? serializers[options.serializer]
: serializers[type];
const serializer = serializers[type];
if (serializer) {
data = Array.isArray(data)

View File

@@ -3,13 +3,10 @@ import * as Tracing from '@sentry/tracing';
import appConfig from '../config/app.js';
const isSentryEnabled = () => {
if (appConfig.isDev || appConfig.isTest) return false;
return !!appConfig.sentryDsn;
};
const isSentryEnabled = !!appConfig.sentryDsn;
export function init(app) {
if (!isSentryEnabled()) return;
if (!isSentryEnabled) return;
return Sentry.init({
enabled: !!appConfig.sentryDsn,
@@ -24,19 +21,19 @@ export function init(app) {
}
export function attachRequestHandler(app) {
if (!isSentryEnabled()) return;
if (!isSentryEnabled) return;
app.use(Sentry.Handlers.requestHandler());
}
export function attachTracingHandler(app) {
if (!isSentryEnabled()) return;
if (!isSentryEnabled) return;
app.use(Sentry.Handlers.tracingHandler());
}
export function attachErrorHandler(app) {
if (!isSentryEnabled()) return;
if (!isSentryEnabled) return;
app.use(
Sentry.Handlers.errorHandler({
@@ -49,7 +46,7 @@ export function attachErrorHandler(app) {
}
export function captureException(exception, captureContext) {
if (!isSentryEnabled()) return;
if (!isSentryEnabled) return;
return Sentry.captureException(exception, captureContext);
}

View File

@@ -39,47 +39,6 @@ class App {
return appInfoConverter(rawAppData);
}
static async findAuthByKey(key, stripFuncs = false) {
const rawAppData = await getApp(key, stripFuncs);
const appData = appInfoConverter(rawAppData);
return appData?.auth || {};
}
static async findTriggersByKey(key, stripFuncs = false) {
const rawAppData = await getApp(key, stripFuncs);
const appData = appInfoConverter(rawAppData);
return appData?.triggers || [];
}
static async findTriggerSubsteps(appKey, triggerKey, stripFuncs = false) {
const rawAppData = await getApp(appKey, stripFuncs);
const appData = appInfoConverter(rawAppData);
const trigger = appData?.triggers?.find(
(trigger) => trigger.key === triggerKey
);
return trigger?.substeps || [];
}
static async findActionsByKey(key, stripFuncs = false) {
const rawAppData = await getApp(key, stripFuncs);
const appData = appInfoConverter(rawAppData);
return appData?.actions || [];
}
static async findActionSubsteps(appKey, actionKey, stripFuncs = false) {
const rawAppData = await getApp(appKey, stripFuncs);
const appData = appInfoConverter(rawAppData);
const action = appData?.actions?.find((action) => action.key === actionKey);
return action?.substeps || [];
}
static async checkAppAndAction(appKey, actionKey) {
const app = await this.findOneByKey(appKey);

View File

@@ -1,24 +0,0 @@
import Base from './base.js';
class Datastore extends Base {
static tableName = 'datastore';
static jsonSchema = {
type: 'object',
required: ['key', 'value', 'scope', 'scopeId'],
properties: {
id: { type: 'string', format: 'uuid' },
key: { type: 'string', minLength: 1 },
value: { type: 'string' },
scope: {
type: 'string',
enum: ['flow'],
default: 'flow',
},
scopeId: { type: 'string', format: 'uuid' },
},
};
}
export default Datastore;

View File

@@ -149,13 +149,6 @@ class User extends Base {
return conditions.isCreator ? this.$relatedQuery('flows') : Flow.query();
}
get authorizedExecutions() {
const conditions = this.can('read', 'Execution');
return conditions.isCreator
? this.$relatedQuery('executions')
: Execution.query();
}
login(password) {
return bcrypt.compare(password, this.password);
}

View File

@@ -1,42 +0,0 @@
import { Router } from 'express';
import asyncHandler from 'express-async-handler';
import { authenticateUser } from '../../../helpers/authentication.js';
import getAppAction from '../../../controllers/api/v1/apps/get-app.js';
import getAppsAction from '../../../controllers/api/v1/apps/get-apps.js';
import getAuthAction from '../../../controllers/api/v1/apps/get-auth.js';
import getTriggersAction from '../../../controllers/api/v1/apps/get-triggers.js';
import getTriggerSubstepsAction from '../../../controllers/api/v1/apps/get-trigger-substeps.js';
import getActionsAction from '../../../controllers/api/v1/apps/get-actions.js';
import getActionSubstepsAction from '../../../controllers/api/v1/apps/get-action-substeps.js';
const router = Router();
router.get('/', authenticateUser, asyncHandler(getAppsAction));
router.get('/:appKey', authenticateUser, asyncHandler(getAppAction));
router.get('/:appKey/auth', authenticateUser, asyncHandler(getAuthAction));
router.get(
'/:appKey/triggers',
authenticateUser,
asyncHandler(getTriggersAction)
);
router.get(
'/:appKey/triggers/:triggerKey/substeps',
authenticateUser,
asyncHandler(getTriggerSubstepsAction)
);
router.get(
'/:appKey/actions',
authenticateUser,
asyncHandler(getActionsAction)
);
router.get(
'/:appKey/actions/:actionKey/substeps',
authenticateUser,
asyncHandler(getActionSubstepsAction)
);
export default router;

View File

@@ -1,32 +0,0 @@
import { Router } from 'express';
import asyncHandler from 'express-async-handler';
import { authenticateUser } from '../../../helpers/authentication.js';
import { authorizeUser } from '../../../helpers/authorization.js';
import getExecutionsAction from '../../../controllers/api/v1/executions/get-executions.js';
import getExecutionAction from '../../../controllers/api/v1/executions/get-execution.js';
import getExecutionStepsAction from '../../../controllers/api/v1/executions/get-execution-steps.js';
const router = Router();
router.get(
'/',
authenticateUser,
authorizeUser,
asyncHandler(getExecutionsAction)
);
router.get(
'/:executionId',
authenticateUser,
authorizeUser,
asyncHandler(getExecutionAction)
);
router.get(
'/:executionId/execution-steps',
authenticateUser,
authorizeUser,
asyncHandler(getExecutionStepsAction)
);
export default router;

View File

@@ -8,8 +8,6 @@ import usersRouter from './api/v1/users.js';
import paymentRouter from './api/v1/payment.ee.js';
import appAuthClientsRouter from './api/v1/app-auth-clients.js';
import flowsRouter from './api/v1/flows.js';
import appsRouter from './api/v1/apps.js';
import executionsRouter from './api/v1/executions.js';
import samlAuthProvidersRouter from './api/v1/admin/saml-auth-providers.ee.js';
import rolesRouter from './api/v1/admin/roles.ee.js';
import permissionsRouter from './api/v1/admin/permissions.ee.js';
@@ -27,8 +25,6 @@ router.use('/api/v1/users', usersRouter);
router.use('/api/v1/payment', paymentRouter);
router.use('/api/v1/app-auth-clients', appAuthClientsRouter);
router.use('/api/v1/flows', flowsRouter);
router.use('/api/v1/apps', appsRouter);
router.use('/api/v1/executions', executionsRouter);
router.use('/api/v1/admin/saml-auth-providers', samlAuthProvidersRouter);
router.use('/api/v1/admin/roles', rolesRouter);
router.use('/api/v1/admin/permissions', permissionsRouter);

View File

@@ -1,9 +0,0 @@
const actionSerializer = (action) => {
return {
name: action.name,
key: action.key,
description: action.description,
};
};
export default actionSerializer;

View File

@@ -1,21 +0,0 @@
import { describe, it, expect } from 'vitest';
import App from '../models/app';
import actionSerializer from './action';
describe('actionSerializer', () => {
it('should return the action data', async () => {
const actions = await App.findActionsByKey('github');
const action = actions[0];
const expectedPayload = {
description: action.description,
key: action.key,
name: action.name,
pollInterval: action.pollInterval,
showWebhookUrl: action.showWebhookUrl,
type: action.type,
};
expect(actionSerializer(action)).toEqual(expectedPayload);
});
});

View File

@@ -1,12 +0,0 @@
const appSerializer = (app) => {
return {
name: app.name,
key: app.key,
iconUrl: app.iconUrl,
authDocUrl: app.authDocUrl,
supportsConnections: app.supportsConnections,
primaryColor: app.primaryColor,
};
};
export default appSerializer;

View File

@@ -1,20 +0,0 @@
import { describe, it, expect } from 'vitest';
import App from '../models/app';
import appSerializer from './app';
describe('appSerializer', () => {
it('should return app data', async () => {
const app = await App.findOneByKey('deepl');
const expectedPayload = {
name: app.name,
key: app.key,
iconUrl: app.iconUrl,
authDocUrl: app.authDocUrl,
supportsConnections: app.supportsConnections,
primaryColor: app.primaryColor,
};
expect(appSerializer(app)).toEqual(expectedPayload);
});
});

View File

@@ -1,9 +0,0 @@
const authSerializer = (auth) => {
return {
fields: auth.fields,
authenticationSteps: auth.authenticationSteps,
reconnectionSteps: auth.reconnectionSteps,
};
};
export default authSerializer;

View File

@@ -1,17 +0,0 @@
import { describe, it, expect } from 'vitest';
import App from '../models/app';
import authSerializer from './auth';
describe('authSerializer', () => {
it('should return auth data', async () => {
const auth = await App.findAuthByKey('deepl');
const expectedPayload = {
fields: auth.fields,
authenticationSteps: auth.authenticationSteps,
reconnectionSteps: auth.reconnectionSteps,
};
expect(authSerializer(auth)).toEqual(expectedPayload);
});
});

View File

@@ -1,21 +0,0 @@
import stepSerializer from './step.js';
const executionStepSerializer = (executionStep) => {
let executionStepData = {
id: executionStep.id,
dataIn: executionStep.dataIn,
dataOut: executionStep.dataOut,
errorDetails: executionStep.errorDetails,
status: executionStep.status,
createdAt: executionStep.createdAt.getTime(),
updatedAt: executionStep.updatedAt.getTime(),
};
if (executionStep.step) {
executionStepData.step = stepSerializer(executionStep.step);
}
return executionStepData;
};
export default executionStepSerializer;

View File

@@ -1,43 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import executionStepSerializer from './execution-step';
import stepSerializer from './step';
import { createExecutionStep } from '../../test/factories/execution-step';
import { createStep } from '../../test/factories/step';
describe('executionStepSerializer', () => {
let executionStep, step;
beforeEach(async () => {
step = await createStep();
executionStep = await createExecutionStep({
stepId: step.id,
});
});
it('should return the execution step data', async () => {
const expectedPayload = {
id: executionStep.id,
dataIn: executionStep.dataIn,
dataOut: executionStep.dataOut,
errorDetails: executionStep.errorDetails,
status: executionStep.status,
createdAt: executionStep.createdAt.getTime(),
updatedAt: executionStep.updatedAt.getTime(),
};
expect(executionStepSerializer(executionStep)).toEqual(expectedPayload);
});
it('should return the execution step data with the step', async () => {
executionStep.step = step;
const expectedPayload = {
step: stepSerializer(step),
};
expect(executionStepSerializer(executionStep)).toMatchObject(
expectedPayload
);
});
});

View File

@@ -1,22 +0,0 @@
import flowSerializer from './flow.js';
const executionSerializer = (execution) => {
let executionData = {
id: execution.id,
testRun: execution.testRun,
createdAt: execution.createdAt.getTime(),
updatedAt: execution.updatedAt.getTime(),
};
if (execution.status) {
executionData.status = execution.status;
}
if (execution.flow) {
executionData.flow = flowSerializer(execution.flow);
}
return executionData;
};
export default executionSerializer;

View File

@@ -1,52 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import executionSerializer from './execution';
import flowSerializer from './flow';
import { createExecution } from '../../test/factories/execution';
import { createFlow } from '../../test/factories/flow';
describe('executionSerializer', () => {
let flow, execution;
beforeEach(async () => {
flow = await createFlow();
execution = await createExecution({
flowId: flow.id,
});
});
it('should return the execution data', async () => {
const expectedPayload = {
id: execution.id,
testRun: execution.testRun,
createdAt: execution.createdAt.getTime(),
updatedAt: execution.updatedAt.getTime(),
};
expect(executionSerializer(execution)).toEqual(expectedPayload);
});
it('should return the execution data with status', async () => {
execution.status = 'success';
const expectedPayload = {
id: execution.id,
testRun: execution.testRun,
createdAt: execution.createdAt.getTime(),
updatedAt: execution.updatedAt.getTime(),
status: 'success',
};
expect(executionSerializer(execution)).toEqual(expectedPayload);
});
it('should return the execution data with the flow', async () => {
execution.flow = flow;
const expectedPayload = {
flow: flowSerializer(flow),
};
expect(executionSerializer(execution)).toMatchObject(expectedPayload);
});
});

View File

@@ -8,7 +8,7 @@ const flowSerializer = (flow) => {
status: flow.status,
};
if (flow.steps?.length > 0) {
if (flow.steps) {
flowData.steps = flow.steps.map((step) => stepSerializer(step));
}

View File

@@ -5,12 +5,6 @@ import samlAuthProviderSerializer from './saml-auth-provider.ee.js';
import appAuthClientSerializer from './app-auth-client.js';
import flowSerializer from './flow.js';
import stepSerializer from './step.js';
import appSerializer from './app.js';
import authSerializer from './auth.js';
import triggerSerializer from './trigger.js';
import actionSerializer from './action.js';
import executionSerializer from './execution.js';
import executionStepSerializer from './execution-step.js';
const serializers = {
User: userSerializer,
@@ -20,12 +14,6 @@ const serializers = {
AppAuthClient: appAuthClientSerializer,
Flow: flowSerializer,
Step: stepSerializer,
App: appSerializer,
Auth: authSerializer,
Trigger: triggerSerializer,
Action: actionSerializer,
Execution: executionSerializer,
ExecutionStep: executionStepSerializer,
};
export default serializers;

View File

@@ -5,8 +5,8 @@ const permissionSerializer = (permission) => {
action: permission.action,
subject: permission.subject,
conditions: permission.conditions,
createdAt: permission.createdAt.getTime(),
updatedAt: permission.updatedAt.getTime(),
createdAt: permission.createdAt,
updatedAt: permission.updatedAt,
};
};

View File

@@ -16,8 +16,8 @@ describe('permissionSerializer', () => {
action: permission.action,
subject: permission.subject,
conditions: permission.conditions,
createdAt: permission.createdAt.getTime(),
updatedAt: permission.updatedAt.getTime(),
createdAt: permission.createdAt,
updatedAt: permission.updatedAt,
};
expect(permissionSerializer(permission)).toEqual(expectedPayload);

View File

@@ -6,12 +6,12 @@ const roleSerializer = (role) => {
name: role.name,
key: role.key,
description: role.description,
createdAt: role.createdAt.getTime(),
updatedAt: role.updatedAt.getTime(),
createdAt: role.createdAt,
updatedAt: role.updatedAt,
isAdmin: role.isAdmin,
};
if (role.permissions?.length > 0) {
if (role.permissions) {
roleData.permissions = role.permissions.map((permission) =>
permissionSerializer(permission)
);

View File

@@ -29,8 +29,8 @@ describe('roleSerializer', () => {
name: role.name,
key: role.key,
description: role.description,
createdAt: role.createdAt.getTime(),
updatedAt: role.updatedAt.getTime(),
createdAt: role.createdAt,
updatedAt: role.updatedAt,
isAdmin: role.isAdmin,
};

View File

@@ -1,12 +0,0 @@
const triggerSerializer = (trigger) => {
return {
description: trigger.description,
key: trigger.key,
name: trigger.name,
pollInterval: trigger.pollInterval,
showWebhookUrl: trigger.showWebhookUrl,
type: trigger.type,
};
};
export default triggerSerializer;

View File

@@ -1,21 +0,0 @@
import { describe, it, expect } from 'vitest';
import App from '../models/app';
import triggerSerializer from './trigger';
describe('triggerSerializer', () => {
it('should return the trigger data', async () => {
const triggers = await App.findTriggersByKey('github');
const trigger = triggers[0];
const expectedPayload = {
description: trigger.description,
key: trigger.key,
name: trigger.name,
pollInterval: trigger.pollInterval,
showWebhookUrl: trigger.showWebhookUrl,
type: trigger.type,
};
expect(triggerSerializer(trigger)).toEqual(expectedPayload);
});
});

View File

@@ -6,8 +6,8 @@ const userSerializer = (user) => {
let userData = {
id: user.id,
email: user.email,
createdAt: user.createdAt.getTime(),
updatedAt: user.updatedAt.getTime(),
createdAt: user.createdAt,
updatedAt: user.updatedAt,
fullName: user.fullName,
};
@@ -15,7 +15,7 @@ const userSerializer = (user) => {
userData.role = roleSerializer(user.role);
}
if (user.permissions?.length > 0) {
if (user.permissions) {
userData.permissions = user.permissions.map((permission) =>
permissionSerializer(permission)
);

View File

@@ -31,11 +31,11 @@ describe('userSerializer', () => {
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
const expectedPayload = {
createdAt: user.createdAt.getTime(),
createdAt: user.createdAt,
email: user.email,
fullName: user.fullName,
id: user.id,
updatedAt: user.updatedAt.getTime(),
updatedAt: user.updatedAt,
};
expect(userSerializer(user)).toEqual(expectedPayload);
@@ -67,7 +67,7 @@ describe('userSerializer', () => {
it('should return user data with trial expiry date', async () => {
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
await user.$query().patchAndFetch({
await user.$query().patch({
trialExpiryDate: DateTime.now().plus({ days: 30 }).toISODate(),
});

View File

@@ -5,16 +5,16 @@ const getRoleMock = async (role, permissions) => {
name: role.name,
isAdmin: role.isAdmin,
description: role.description,
createdAt: role.createdAt.getTime(),
updatedAt: role.updatedAt.getTime(),
createdAt: role.createdAt.toISOString(),
updatedAt: role.updatedAt.toISOString(),
permissions: permissions.map((permission) => ({
id: permission.id,
action: permission.action,
conditions: permission.conditions,
roleId: permission.roleId,
subject: permission.subject,
createdAt: permission.createdAt.getTime(),
updatedAt: permission.updatedAt.getTime(),
createdAt: permission.createdAt.toISOString(),
updatedAt: permission.updatedAt.toISOString(),
})),
};

View File

@@ -6,8 +6,8 @@ const getRolesMock = async (roles) => {
name: role.name,
isAdmin: role.isAdmin,
description: role.description,
createdAt: role.createdAt.getTime(),
updatedAt: role.updatedAt.getTime(),
createdAt: role.createdAt.toISOString(),
updatedAt: role.updatedAt.toISOString(),
};
});

View File

@@ -1,21 +1,21 @@
const getUserMock = (currentUser, role) => {
return {
data: {
createdAt: currentUser.createdAt.getTime(),
createdAt: currentUser.createdAt.toISOString(),
email: currentUser.email,
fullName: currentUser.fullName,
id: currentUser.id,
role: {
createdAt: role.createdAt.getTime(),
createdAt: role.createdAt.toISOString(),
description: null,
id: role.id,
isAdmin: role.isAdmin,
key: role.key,
name: role.name,
updatedAt: role.updatedAt.getTime(),
updatedAt: role.updatedAt.toISOString(),
},
trialExpiryDate: currentUser.trialExpiryDate.toISOString(),
updatedAt: currentUser.updatedAt.getTime(),
updatedAt: currentUser.updatedAt.toISOString(),
},
meta: {
count: 1,

View File

@@ -3,23 +3,23 @@ const getUsersMock = async (users, roles) => {
const role = roles.find((r) => r.id === user.roleId);
return {
createdAt: user.createdAt.getTime(),
createdAt: user.createdAt.toISOString(),
email: user.email,
fullName: user.fullName,
id: user.id,
role: role
? {
createdAt: role.createdAt.getTime(),
createdAt: role.createdAt.toISOString(),
description: role.description,
id: role.id,
isAdmin: role.isAdmin,
key: role.key,
name: role.name,
updatedAt: role.updatedAt.getTime(),
updatedAt: role.updatedAt.toISOString(),
}
: null,
trialExpiryDate: user.trialExpiryDate.toISOString(),
updatedAt: user.updatedAt.getTime(),
updatedAt: user.updatedAt.toISOString(),
};
});

View File

@@ -1,14 +0,0 @@
const getActionSubstepsMock = (substeps) => {
return {
data: substeps,
meta: {
count: substeps.length,
currentPage: null,
isArray: true,
totalPages: null,
type: 'Object',
},
};
};
export default getActionSubstepsMock;

View File

@@ -1,22 +0,0 @@
const getActionsMock = (actions) => {
const actionsData = actions.map((trigger) => {
return {
name: trigger.name,
key: trigger.key,
description: trigger.description,
};
});
return {
data: actionsData,
meta: {
count: actions.length,
currentPage: null,
isArray: true,
totalPages: null,
type: 'Object',
},
};
};
export default getActionsMock;

View File

@@ -1,21 +0,0 @@
const getAppMock = (app) => {
return {
data: {
authDocUrl: app.authDocUrl,
iconUrl: app.iconUrl,
key: app.key,
name: app.name,
primaryColor: app.primaryColor,
supportsConnections: app.supportsConnections,
},
meta: {
count: 1,
currentPage: null,
isArray: false,
totalPages: null,
type: 'Object',
},
};
};
export default getAppMock;

View File

@@ -1,23 +0,0 @@
const getAppsMock = (apps) => {
const appsData = apps.map((app) => ({
authDocUrl: app.authDocUrl,
iconUrl: app.iconUrl,
key: app.key,
name: app.name,
primaryColor: app.primaryColor,
supportsConnections: app.supportsConnections,
}));
return {
data: appsData,
meta: {
count: appsData.length,
currentPage: null,
isArray: true,
totalPages: null,
type: 'Object',
},
};
};
export default getAppsMock;

View File

@@ -1,18 +0,0 @@
const getAuthMock = (auth) => {
return {
data: {
fields: auth.fields,
authenticationSteps: auth.authenticationSteps,
reconnectionSteps: auth.reconnectionSteps,
},
meta: {
count: 1,
currentPage: null,
isArray: false,
totalPages: null,
type: 'Object',
},
};
};
export default getAuthMock;

View File

@@ -1,14 +0,0 @@
const getTriggerSubstepsMock = (substeps) => {
return {
data: substeps,
meta: {
count: substeps.length,
currentPage: null,
isArray: true,
totalPages: null,
type: 'Object',
},
};
};
export default getTriggerSubstepsMock;

View File

@@ -1,25 +0,0 @@
const getTriggersMock = (triggers) => {
const triggersData = triggers.map((trigger) => {
return {
description: trigger.description,
key: trigger.key,
name: trigger.name,
pollInterval: trigger.pollInterval,
showWebhookUrl: trigger.showWebhookUrl,
type: trigger.type,
};
});
return {
data: triggersData,
meta: {
count: triggers.length,
currentPage: null,
isArray: true,
totalPages: null,
type: 'Object',
},
};
};
export default getTriggersMock;

View File

@@ -1,39 +0,0 @@
const getExecutionStepsMock = async (executionSteps, steps) => {
const data = executionSteps.map((executionStep) => {
const step = steps.find((step) => step.id === executionStep.stepId);
return {
id: executionStep.id,
dataIn: executionStep.dataIn,
dataOut: executionStep.dataOut,
errorDetails: executionStep.errorDetails,
status: executionStep.status,
createdAt: executionStep.createdAt.getTime(),
updatedAt: executionStep.updatedAt.getTime(),
step: {
id: step.id,
type: step.type,
key: step.key,
appKey: step.appKey,
iconUrl: step.iconUrl,
webhookUrl: step.webhookUrl,
status: step.status,
position: step.position,
parameters: step.parameters,
},
};
});
return {
data: data,
meta: {
count: executionSteps.length,
currentPage: 1,
isArray: true,
totalPages: 1,
type: 'ExecutionStep',
},
};
};
export default getExecutionStepsMock;

View File

@@ -1,38 +0,0 @@
const getExecutionMock = async (execution, flow, steps) => {
const data = {
id: execution.id,
testRun: execution.testRun,
createdAt: execution.createdAt.getTime(),
updatedAt: execution.updatedAt.getTime(),
flow: {
id: flow.id,
name: flow.name,
active: flow.active,
status: flow.active ? 'published' : 'draft',
steps: steps.map((step) => ({
id: step.id,
type: step.type,
key: step.key,
appKey: step.appKey,
iconUrl: step.iconUrl,
webhookUrl: step.webhookUrl,
status: step.status,
position: step.position,
parameters: step.parameters,
})),
},
};
return {
data: data,
meta: {
count: 1,
currentPage: null,
isArray: false,
totalPages: null,
type: 'Execution',
},
};
};
export default getExecutionMock;

View File

@@ -1,39 +0,0 @@
const getExecutionsMock = async (executions, flow, steps) => {
const data = executions.map((execution) => ({
id: execution.id,
testRun: execution.testRun,
createdAt: execution.createdAt.getTime(),
updatedAt: execution.updatedAt.getTime(),
status: 'success',
flow: {
id: flow.id,
name: flow.name,
active: flow.active,
status: flow.active ? 'published' : 'draft',
steps: steps.map((step) => ({
id: step.id,
type: step.type,
key: step.key,
appKey: step.appKey,
iconUrl: step.iconUrl,
webhookUrl: step.webhookUrl,
status: step.status,
position: step.position,
parameters: step.parameters,
})),
},
}));
return {
data: data,
meta: {
count: executions.length,
currentPage: 1,
isArray: true,
totalPages: 1,
type: 'Execution',
},
};
};
export default getExecutionsMock;

View File

@@ -1,30 +1,22 @@
const getCurrentUserMock = (currentUser, role, permissions) => {
const getCurrentUserMock = (currentUser, role) => {
return {
data: {
createdAt: currentUser.createdAt.getTime(),
createdAt: currentUser.createdAt.toISOString(),
email: currentUser.email,
fullName: currentUser.fullName,
id: currentUser.id,
permissions: permissions.map((permission) => ({
id: permission.id,
roleId: permission.roleId,
action: permission.action,
subject: permission.subject,
conditions: permission.conditions,
createdAt: permission.createdAt.getTime(),
updatedAt: permission.updatedAt.getTime(),
})),
permissions: [],
role: {
createdAt: role.createdAt.getTime(),
createdAt: role.createdAt.toISOString(),
description: null,
id: role.id,
isAdmin: role.isAdmin,
key: role.key,
name: role.name,
updatedAt: role.updatedAt.getTime(),
updatedAt: role.updatedAt.toISOString(),
},
trialExpiryDate: currentUser.trialExpiryDate.toISOString(),
updatedAt: currentUser.updatedAt.getTime(),
updatedAt: currentUser.updatedAt.toISOString(),
},
meta: {
count: 1,

View File

@@ -41,15 +41,6 @@ export default defineConfig({
{ text: 'Connection', link: '/apps/carbone/connection' },
],
},
{
text: 'Datastore',
collapsible: true,
collapsed: true,
items: [
{ text: 'Actions', link: '/apps/datastore/actions' },
{ text: 'Connection', link: '/apps/datastore/connection' },
],
},
{
text: 'DeepL',
collapsible: true,
@@ -82,6 +73,7 @@ export default defineConfig({
collapsible: true,
collapsed: true,
items: [
{ text: 'Triggers', link: '/apps/dropbox/triggers' },
{ text: 'Actions', link: '/apps/dropbox/actions' },
{ text: 'Connection', link: '/apps/dropbox/connection' },
],

View File

@@ -1,14 +0,0 @@
---
favicon: /favicons/datastore.svg
items:
- name: Get value
desc: Get value from the persistent datastore.
- name: Set value
desc: Set value to the persistent datastore.
---
<script setup>
import CustomListing from '../../components/CustomListing.vue'
</script>
<CustomListing />

View File

@@ -1,3 +0,0 @@
# Datastore
Datastore is a persistent key-value storage system that allows you to store and retrieve data. Currently you can use it within the scope of the flow, meaning you can store and retrieve data within the same flow.

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