Compare commits

..

1 Commits

Author SHA1 Message Date
Rıdvan Akca
465dc8ba38 feat(notion): add update database item action 2024-01-15 18:20:13 +03:00
123 changed files with 1579 additions and 6108 deletions

View File

@@ -8,7 +8,7 @@
"version": "latest" "version": "latest"
}, },
"ghcr.io/devcontainers/features/node:1": { "ghcr.io/devcontainers/features/node:1": {
"version": 18 "version": 16
}, },
"ghcr.io/devcontainers/features/common-utils:1": { "ghcr.io/devcontainers/features/common-utils:1": {
"username": "vscode", "username": "vscode",

View File

@@ -6,7 +6,8 @@
"start": "lerna run --stream --parallel --scope=@*/{web,backend} dev", "start": "lerna run --stream --parallel --scope=@*/{web,backend} dev",
"start:web": "lerna run --stream --scope=@*/web dev", "start:web": "lerna run --stream --scope=@*/web dev",
"start:backend": "lerna run --stream --scope=@*/backend dev", "start:backend": "lerna run --stream --scope=@*/backend dev",
"lint": "lerna run --no-bail --stream --parallel --scope=@*/{web,backend} lint", "lint": "lerna run --no-bail --stream --parallel --scope=@*/{web,backend,cli} lint",
"build:watch": "lerna run --no-bail --stream --parallel --scope=@*/{web,backend,cli} build:watch",
"build:docs": "cd ./packages/docs && yarn install && yarn build" "build:docs": "cd ./packages/docs && yarn install && yarn build"
}, },
"workspaces": { "workspaces": {

View File

@@ -33,6 +33,7 @@
"axios": "1.6.0", "axios": "1.6.0",
"bcrypt": "^5.0.1", "bcrypt": "^5.0.1",
"bullmq": "^3.0.0", "bullmq": "^3.0.0",
"copyfiles": "^2.4.1",
"cors": "^2.8.5", "cors": "^2.8.5",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"debug": "~2.6.9", "debug": "~2.6.9",
@@ -44,6 +45,7 @@
"graphql-middleware": "^6.1.15", "graphql-middleware": "^6.1.15",
"graphql-shield": "^7.5.0", "graphql-shield": "^7.5.0",
"graphql-tools": "^8.2.0", "graphql-tools": "^8.2.0",
"graphql-type-json": "^0.3.2",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
"http-errors": "~1.6.3", "http-errors": "~1.6.3",
"http-proxy-agent": "^7.0.0", "http-proxy-agent": "^7.0.0",
@@ -66,6 +68,7 @@
"pluralize": "^8.0.0", "pluralize": "^8.0.0",
"raw-body": "^2.5.2", "raw-body": "^2.5.2",
"showdown": "^2.1.0", "showdown": "^2.1.0",
"stripe": "^11.13.0",
"winston": "^3.7.1", "winston": "^3.7.1",
"xmlrpc": "^1.3.2" "xmlrpc": "^1.3.2"
}, },

View File

@@ -1,6 +1,5 @@
import defineAction from '../../../../helpers/define-action.js'; import defineAction from '../../../../helpers/define-action.js';
import base64ToString from './transformers/base64-to-string.js';
import capitalize from './transformers/capitalize.js'; import capitalize from './transformers/capitalize.js';
import extractEmailAddress from './transformers/extract-email-address.js'; import extractEmailAddress from './transformers/extract-email-address.js';
import extractNumber from './transformers/extract-number.js'; import extractNumber from './transformers/extract-number.js';
@@ -9,12 +8,10 @@ import lowercase from './transformers/lowercase.js';
import markdownToHtml from './transformers/markdown-to-html.js'; import markdownToHtml from './transformers/markdown-to-html.js';
import pluralize from './transformers/pluralize.js'; import pluralize from './transformers/pluralize.js';
import replace from './transformers/replace.js'; import replace from './transformers/replace.js';
import stringToBase64 from './transformers/string-to-base64.js';
import trimWhitespace from './transformers/trim-whitespace.js'; import trimWhitespace from './transformers/trim-whitespace.js';
import useDefaultValue from './transformers/use-default-value.js'; import useDefaultValue from './transformers/use-default-value.js';
const transformers = { const transformers = {
base64ToString,
capitalize, capitalize,
extractEmailAddress, extractEmailAddress,
extractNumber, extractNumber,
@@ -23,7 +20,6 @@ const transformers = {
markdownToHtml, markdownToHtml,
pluralize, pluralize,
replace, replace,
stringToBase64,
trimWhitespace, trimWhitespace,
useDefaultValue, useDefaultValue,
}; };
@@ -41,7 +37,6 @@ export default defineAction({
required: true, required: true,
variables: true, variables: true,
options: [ options: [
{ label: 'Base64 to String', value: 'base64ToString' },
{ label: 'Capitalize', value: 'capitalize' }, { label: 'Capitalize', value: 'capitalize' },
{ label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' }, { label: 'Convert HTML to Markdown', value: 'htmlToMarkdown' },
{ label: 'Convert Markdown to HTML', value: 'markdownToHtml' }, { label: 'Convert Markdown to HTML', value: 'markdownToHtml' },
@@ -50,7 +45,6 @@ export default defineAction({
{ label: 'Lowercase', value: 'lowercase' }, { label: 'Lowercase', value: 'lowercase' },
{ label: 'Pluralize', value: 'pluralize' }, { label: 'Pluralize', value: 'pluralize' },
{ label: 'Replace', value: 'replace' }, { label: 'Replace', value: 'replace' },
{ label: 'String to Base64', value: 'stringToBase64' },
{ label: 'Trim Whitespace', value: 'trimWhitespace' }, { label: 'Trim Whitespace', value: 'trimWhitespace' },
{ label: 'Use Default Value', value: 'useDefaultValue' }, { label: 'Use Default Value', value: 'useDefaultValue' },
], ],

View File

@@ -1,8 +0,0 @@
const base64ToString = ($) => {
const input = $.step.parameters.input;
const decodedString = Buffer.from(input, 'base64').toString('utf8');
return decodedString;
};
export default base64ToString;

View File

@@ -1,8 +0,0 @@
const stringtoBase64 = ($) => {
const input = $.step.parameters.input;
const base64String = Buffer.from(input).toString('base64');
return base64String;
};
export default stringtoBase64;

View File

@@ -1,4 +1,3 @@
import base64ToString from './text/base64-to-string.js';
import capitalize from './text/capitalize.js'; import capitalize from './text/capitalize.js';
import extractEmailAddress from './text/extract-email-address.js'; import extractEmailAddress from './text/extract-email-address.js';
import extractNumber from './text/extract-number.js'; import extractNumber from './text/extract-number.js';
@@ -7,7 +6,6 @@ import lowercase from './text/lowercase.js';
import markdownToHtml from './text/markdown-to-html.js'; import markdownToHtml from './text/markdown-to-html.js';
import pluralize from './text/pluralize.js'; import pluralize from './text/pluralize.js';
import replace from './text/replace.js'; import replace from './text/replace.js';
import stringToBase64 from './text/string-to-base64.js';
import trimWhitespace from './text/trim-whitespace.js'; import trimWhitespace from './text/trim-whitespace.js';
import useDefaultValue from './text/use-default-value.js'; import useDefaultValue from './text/use-default-value.js';
import performMathOperation from './numbers/perform-math-operation.js'; import performMathOperation from './numbers/perform-math-operation.js';
@@ -17,7 +15,6 @@ import formatPhoneNumber from './numbers/format-phone-number.js';
import formatDateTime from './date-time/format-date-time.js'; import formatDateTime from './date-time/format-date-time.js';
const options = { const options = {
base64ToString,
capitalize, capitalize,
extractEmailAddress, extractEmailAddress,
extractNumber, extractNumber,
@@ -26,7 +23,6 @@ const options = {
markdownToHtml, markdownToHtml,
pluralize, pluralize,
replace, replace,
stringToBase64,
trimWhitespace, trimWhitespace,
useDefaultValue, useDefaultValue,
performMathOperation, performMathOperation,

View File

@@ -1,12 +0,0 @@
const base64ToString = [
{
label: 'Input',
key: 'input',
type: 'string',
required: true,
description: 'Text that will be converted from Base64 to string.',
variables: true,
},
];
export default base64ToString;

View File

@@ -1,12 +0,0 @@
const stringToBase64 = [
{
label: 'Input',
key: 'input',
type: 'string',
required: true,
description: 'Text that will be converted to Base64.',
variables: true,
},
];
export default stringToBase64;

View File

@@ -1,5 +1,11 @@
import createDatabaseItem from './create-database-item/index.js'; import createDatabaseItem from './create-database-item/index.js';
import createPage from './create-page/index.js'; import createPage from './create-page/index.js';
import findDatabaseItem from './find-database-item/index.js'; import findDatabaseItem from './find-database-item/index.js';
import updateDatabaseItem from './update-database-item/index.js';
export default [createDatabaseItem, createPage, findDatabaseItem]; export default [
createDatabaseItem,
createPage,
findDatabaseItem,
updateDatabaseItem,
];

View File

@@ -0,0 +1,157 @@
import defineAction from '../../../../helpers/define-action.js';
export default defineAction({
name: 'Update database item',
key: 'updateDatabaseItem',
description: 'Updates a database item.',
arguments: [
{
label: 'Database',
key: 'databaseId',
type: 'dropdown',
required: true,
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listDatabases',
},
],
},
},
{
label: 'Item',
key: 'itemId',
type: 'dropdown',
required: true,
variables: true,
dependsOn: ['parameters.databaseId'],
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listDatabaseItems',
},
{
name: 'parameters.databaseId',
value: '{parameters.databaseId}',
},
],
},
},
{
label: 'Name',
key: 'name',
type: 'string',
required: false,
description:
'This field has a 2000 character limit. Any characters beyond 2000 will not be included.',
variables: true,
},
{
label: 'Tags',
key: 'tags',
type: 'dynamic',
required: false,
description: '',
fields: [
{
label: 'Tag',
key: 'tag',
type: 'dropdown',
required: true,
variables: true,
dependsOn: ['parameters.databaseId'],
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listTags',
},
{
name: 'parameters.databaseId',
value: '{parameters.databaseId}',
},
],
},
},
],
},
{
label: 'Content',
key: 'content',
type: 'string',
required: false,
description:
'You can choose to add extra text to the database item, with a limit of up to 2000 characters if desired.',
variables: true,
},
],
async run($) {
const itemId = $.step.parameters.itemId;
const name = $.step.parameters.name;
const truncatedName = name.slice(0, 2000);
const content = $.step.parameters.content;
const truncatedContent = content.slice(0, 2000);
const tags = $.step.parameters.tags;
const formattedTags = tags
.filter((tag) => tag.tag !== '')
.map((tag) => tag.tag);
const body = {
properties: {},
};
if (truncatedName) {
body.properties.Name = {
title: [
{
text: {
content: truncatedName,
},
},
],
};
}
if (formattedTags?.length) {
body.properties.Tags = {
multi_select: formattedTags.map((tag) => ({ name: tag })),
};
}
if (truncatedContent) {
const response = await $.http.get(`/v1/blocks/${itemId}/children`);
const firstBlockId = response.data.results[0].id;
const body = {
paragraph: {
rich_text: [
{
type: 'text',
text: {
content: truncatedContent,
},
},
],
},
};
await $.http.patch(`/v1/blocks/${firstBlockId}`, body);
}
const { data } = await $.http.patch(`/v1/pages/${itemId}`, body);
$.setActionItem({
raw: data,
});
},
});

View File

@@ -1,4 +1,6 @@
import listDatabaseItems from './list-database-items/index.js';
import listDatabases from './list-databases/index.js'; import listDatabases from './list-databases/index.js';
import listParentPages from './list-parent-pages/index.js'; import listParentPages from './list-parent-pages/index.js';
import listTags from './list-tags/index.js';
export default [listDatabases, listParentPages]; export default [listDatabaseItems, listDatabases, listParentPages, listTags];

View File

@@ -0,0 +1,38 @@
export default {
name: 'List database items',
key: 'listDatabaseItems',
async run($) {
const databases = {
data: [],
error: null,
};
const payload = {
start_cursor: undefined,
};
const databaseId = $.step.parameters.databaseId;
if (!databaseId) {
return databases;
}
do {
const response = await $.http.post(
`/v1/databases/${databaseId}/query`,
payload
);
payload.start_cursor = response.data.next_cursor;
for (const database of response.data.results) {
databases.data.push({
value: database.id,
name:
database.properties.Name?.title?.[0]?.plain_text || 'Untitled Page',
});
}
} while (payload.start_cursor);
return databases;
},
};

View File

@@ -22,7 +22,7 @@ export default {
for (const database of response.data.results) { for (const database of response.data.results) {
databases.data.push({ databases.data.push({
value: database.id, value: database.id,
name: database.title[0].plain_text, name: database.title?.[0]?.plain_text || 'Untitled Database',
}); });
} }
} while (payload.start_cursor); } while (payload.start_cursor);

View File

@@ -0,0 +1,38 @@
export default {
name: 'List tags',
key: 'listTags',
async run($) {
const tags = {
data: [],
error: null,
};
const databaseId = $.step.parameters.databaseId;
let allTags;
if (!databaseId) {
return tags;
}
const response = await $.http.get(`/v1/databases/${databaseId}`);
const tagsExist =
response.data.properties.Tags.multi_select.options.length !== 0;
if (tagsExist) {
allTags = response.data.properties.Tags.multi_select.options.map(
(tag) => tag.name
);
} else {
return tags;
}
for (const tag of allTags) {
tags.data.push({
value: tag,
name: tag,
});
}
return tags;
},
};

View File

@@ -1,29 +0,0 @@
import defineAction from '../../../../helpers/define-action.js';
export default defineAction({
name: 'Delete document',
key: 'deleteDocument',
description: 'Deletes a document.',
arguments: [
{
label: 'Document ID',
key: 'documentId',
type: 'string',
required: true,
description: '',
variables: true,
},
],
async run($) {
const { documentId } = $.step.parameters;
await $.http.delete(`/v1/documents/${documentId}`);
$.setActionItem({
raw: {
result: 'successful',
},
});
},
});

View File

@@ -1,27 +0,0 @@
import defineAction from '../../../../helpers/define-action.js';
export default defineAction({
name: 'Find document',
key: 'findDocument',
description: 'Finds a document.',
arguments: [
{
label: 'Document ID',
key: 'documentId',
type: 'string',
required: true,
description: '',
variables: true,
},
],
async run($) {
const { documentId } = $.step.parameters;
const { data } = await $.http.get(`/v1/documents/${documentId}`);
$.setActionItem({
raw: data.document,
});
},
});

View File

@@ -1,241 +0,0 @@
import defineAction from '../../../../helpers/define-action.js';
export default defineAction({
name: 'Generate document',
key: 'generateDocument',
description: 'Creates a new document.',
arguments: [
{
label: 'Workspace',
key: 'workspaceId',
type: 'dropdown',
required: true,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listWorkspaces',
},
],
},
},
{
label: 'Template',
key: 'templateId',
type: 'dropdown',
required: false,
depensOn: ['parameters.workspaceId'],
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listTemplates',
},
{
name: 'parameters.workspaceId',
value: '{parameters.workspaceId}',
},
],
},
},
{
label: 'Use a Custom Json Structure?',
key: 'useCustomJsonStructure',
type: 'dropdown',
required: true,
description:
'Please indicate "yes" if you would rather create a full JSON payload instead of relying on Automatisch mapping for the Document data.',
variables: true,
options: [
{
label: 'Yes',
value: true,
},
{
label: 'No',
value: false,
},
],
additionalFields: {
type: 'query',
name: 'getDynamicFields',
arguments: [
{
name: 'key',
value: 'listDocumentData',
},
{
name: 'parameters.useCustomJsonStructure',
value: '{parameters.useCustomJsonStructure}',
},
],
},
},
{
label: 'Add Line Items?',
key: 'addLineItems',
type: 'dropdown',
required: true,
description:
'Choose "yes" to include information for Line Items (such as in an invoice).',
variables: true,
options: [
{
label: 'Yes',
value: true,
},
{
label: 'No',
value: false,
},
],
additionalFields: {
type: 'query',
name: 'getDynamicFields',
arguments: [
{
name: 'key',
value: 'listLineItems',
},
{
name: 'parameters.addLineItems',
value: '{parameters.addLineItems}',
},
],
},
},
{
label: 'Custom Filename',
key: 'customFilename',
type: 'string',
required: false,
description:
'You have the option to define a custom filename for generated documents. If left blank, a random value will be assigned.',
variables: true,
},
{
label: 'Meta Data',
key: 'metaData',
type: 'dynamic',
required: false,
description:
'Extra information appended to the generated Document but not accessible within its Template.',
fields: [
{
label: 'Key',
key: 'metaDataKey',
type: 'string',
required: false,
description: '',
variables: true,
},
{
label: 'Value',
key: 'metaDataValue',
type: 'string',
required: false,
description: '',
variables: true,
},
],
},
],
async run($) {
const {
templateId,
useCustomJsonStructure,
customJsonPayload,
customFilename,
addLineItems,
lineItems,
documentData,
metaData,
} = $.step.parameters;
let payload = {};
let meta = {};
const documentDataObject = documentData.reduce((result, entry) => {
const key = entry.documentDataKey?.toLowerCase();
const value = entry.documentDataValue;
if (key && value) {
return {
...result,
[entry.documentDataKey?.toLowerCase()]: entry.documentDataValue,
};
}
return result;
}, {});
const lineItemsObject = lineItems.reduce((result, entry) => {
const key = entry.lineItemKey?.toLowerCase();
const value = entry.lineItemValue;
if (key && value) {
return {
...result,
[entry.lineItemKey?.toLowerCase()]: entry.lineItemValue,
};
}
return result;
}, {});
const metaDataObject = metaData.reduce((result, entry) => {
const key = entry.metaDataKey?.toLowerCase();
const value = entry.metaDataValue;
if (key && value) {
return {
...result,
[entry.metaDataKey?.toLowerCase()]: entry.metaDataValue,
};
}
return result;
}, {});
if (metaDataObject) {
meta = metaDataObject;
}
if (customFilename) {
meta._filename = customFilename;
}
if (useCustomJsonStructure) {
payload = JSON.parse(customJsonPayload);
} else {
payload = documentDataObject;
}
if (addLineItems) {
payload.lineItems = [lineItemsObject];
}
const body = {
document: {
document_template_id: templateId,
meta: JSON.stringify(meta),
payload: JSON.stringify(payload),
status: 'pending',
},
};
const { data } = await $.http.post('/v1/documents', body);
$.setActionItem({
raw: data,
});
},
});

View File

@@ -1,5 +0,0 @@
import deleteDocument from './delete-document/index.js';
import findDocument from './find-document/index.js';
import generateDocument from './generate-document/index.js';
export default [deleteDocument, findDocument, generateDocument];

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 112 KiB

View File

@@ -1,21 +0,0 @@
import verifyCredentials from './verify-credentials.js';
import isStillVerified from './is-still-verified.js';
export default {
fields: [
{
key: 'apiKey',
label: 'API Key',
type: 'string',
required: true,
readOnly: false,
value: null,
placeholder: null,
description: 'PDFMonkey API secret key of your account.',
clickToCopy: false,
},
],
verifyCredentials,
isStillVerified,
};

View File

@@ -1,8 +0,0 @@
import getCurrentUser from '../common/get-current-user.js';
const isStillVerified = async ($) => {
const currentUser = await getCurrentUser($);
return !!currentUser.id;
};
export default isStillVerified;

View File

@@ -1,15 +0,0 @@
import getCurrentUser from '../common/get-current-user.js';
const verifyCredentials = async ($) => {
const currentUser = await getCurrentUser($);
const screenName = [currentUser.desired_name, currentUser.email]
.filter(Boolean)
.join(' @ ');
await $.auth.set({
screenName,
apiKey: $.auth.data.apiKey,
});
};
export default verifyCredentials;

View File

@@ -1,9 +0,0 @@
const addAuthHeader = ($, requestConfig) => {
if ($.auth.data?.apiKey) {
requestConfig.headers.Authorization = `Bearer ${$.auth.data.apiKey}`;
}
return requestConfig;
};
export default addAuthHeader;

View File

@@ -1,8 +0,0 @@
const getCurrentUser = async ($) => {
const response = await $.http.get('/v1/current_user');
const currentUser = response.data.current_user;
return currentUser;
};
export default getCurrentUser;

View File

@@ -1,4 +0,0 @@
import listTemplates from './list-templates/index.js';
import listWorkspaces from './list-workspaces/index.js';
export default [listTemplates, listWorkspaces];

View File

@@ -1,39 +0,0 @@
export default {
name: 'List templates',
key: 'listTemplates',
async run($) {
const templates = {
data: [],
};
const workspaceId = $.step.parameters.workspaceId;
let next = false;
const params = {
page: 'all',
'q[workspace_id]': workspaceId,
};
if (!workspaceId) {
return templates;
}
do {
const { data } = await $.http.get('/v1/document_template_cards', params);
next = data.meta.next_page;
if (!data?.document_template_cards?.length) {
return;
}
for (const template of data.document_template_cards) {
templates.data.push({
value: template.id,
name: template.identifier,
});
}
} while (next);
return templates;
},
};

View File

@@ -1,29 +0,0 @@
export default {
name: 'List workspaces',
key: 'listWorkspaces',
async run($) {
const workspaces = {
data: [],
};
let next = false;
do {
const { data } = await $.http.get('/v1/workspace_cards');
next = data.meta.next_page;
if (!data?.workspace_cards?.length) {
return;
}
for (const workspace of data.workspace_cards) {
workspaces.data.push({
value: workspace.id,
name: workspace.identifier,
});
}
} while (next);
return workspaces;
},
};

View File

@@ -1,4 +0,0 @@
import listDocumentData from './list-document-data/index.js';
import listLineItems from './list-line-items/index.js';
export default [listDocumentData, listLineItems];

View File

@@ -1,48 +0,0 @@
export default {
name: 'List document data',
key: 'listDocumentData',
async run($) {
if ($.step.parameters.useCustomJsonStructure) {
return [
{
label: 'Data for the Document (JSON Payload)',
key: 'customJsonPayload',
type: 'string',
required: false,
description:
'Use the JSON format { "firstname": "John", "lastname": "Doe" }.',
variables: true,
},
];
} else {
return [
{
label: 'Data for the Document',
key: 'documentData',
type: 'dynamic',
required: false,
description: '',
fields: [
{
label: 'Key',
key: 'documentDataKey',
type: 'string',
required: false,
description: '',
variables: true,
},
{
label: 'Value',
key: 'documentDataValue',
type: 'string',
required: false,
description: '',
variables: true,
},
],
},
];
}
},
};

View File

@@ -1,37 +0,0 @@
export default {
name: 'List line items',
key: 'listLineItems',
async run($) {
if ($.step.parameters.addLineItems) {
return [
{
label: 'Line Items',
key: 'lineItems',
type: 'dynamic',
required: false,
description:
'Data for a single item. Available as "lineItems" in your PDFMonkey Template.',
fields: [
{
label: 'Key',
key: 'lineItemKey',
type: 'string',
required: false,
description: '',
variables: true,
},
{
label: 'Value',
key: 'lineItemValue',
type: 'string',
required: false,
description: '',
variables: true,
},
],
},
];
}
},
};

View File

@@ -1,24 +0,0 @@
import defineApp from '../../helpers/define-app.js';
import addAuthHeader from './common/add-auth-header.js';
import auth from './auth/index.js';
import triggers from './triggers/index.js';
import dynamicData from './dynamic-data/index.js';
import actions from './actions/index.js';
import dynamicFields from './dynamic-fields/index.js';
export default defineApp({
name: 'PDFMonkey',
key: 'pdf-monkey',
iconUrl: '{BASE_URL}/apps/pdf-monkey/assets/favicon.svg',
authDocUrl: 'https://automatisch.io/docs/apps/pdf-monkey/connection',
supportsConnections: true,
baseUrl: 'https://pdfmonkey.io',
apiBaseUrl: 'https://api.pdfmonkey.io/api',
primaryColor: '376794',
beforeRequest: [addAuthHeader],
auth,
triggers,
dynamicData,
actions,
dynamicFields,
});

View File

@@ -1,99 +0,0 @@
import defineTrigger from '../../../../helpers/define-trigger.js';
export default defineTrigger({
name: 'Documents Generated',
key: 'documentsGenerated',
pollInterval: 15,
description:
'Triggers upon the successful completion of document generation.',
arguments: [
{
label: 'Workspace',
key: 'workspaceId',
type: 'dropdown',
required: true,
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listWorkspaces',
},
],
},
},
{
label: 'Templates',
key: 'templateIds',
type: 'dynamic',
required: false,
description: 'Apply this trigger exclusively for particular templates.',
fields: [
{
label: 'Template',
key: 'templateId',
type: 'dropdown',
required: false,
depensOn: ['parameters.workspaceId'],
description: '',
variables: true,
source: {
type: 'query',
name: 'getDynamicData',
arguments: [
{
name: 'key',
value: 'listTemplates',
},
{
name: 'parameters.workspaceId',
value: '{parameters.workspaceId}',
},
],
},
},
],
},
],
async run($) {
const workspaceId = $.step.parameters.workspaceId;
const templateIds = $.step.parameters.templateIds;
const allTemplates = templateIds
.map((templateId) => templateId.templateId)
.join(',');
const params = {
'page[size]': 100,
'q[workspace_id]': workspaceId,
'q[status]': 'success',
};
if (!templateIds.length) {
params['q[document_template_id]'] = allTemplates;
}
let next = false;
do {
const { data } = await $.http.get('/v1/document_cards', { params });
if (!data?.document_cards?.length) {
return;
}
next = data.meta.next_page;
for (const document of data.document_cards) {
$.pushTriggerItem({
raw: document,
meta: {
internalId: document.id,
},
});
}
} while (next);
},
});

View File

@@ -1,3 +0,0 @@
import documentsGenerated from './documents-generated/index.js';
export default [documentsGenerated];

View File

@@ -18,9 +18,7 @@ const port = process.env.PORT || '3000';
const serveWebAppSeparately = const serveWebAppSeparately =
process.env.SERVE_WEB_APP_SEPARATELY === 'true' ? true : false; process.env.SERVE_WEB_APP_SEPARATELY === 'true' ? true : false;
let apiUrl = new URL( let apiUrl = new URL(`${protocol}://${host}:${port}`).toString();
process.env.API_URL || `${protocol}://${host}:${port}`
).toString();
apiUrl = apiUrl.substring(0, apiUrl.length - 1); apiUrl = apiUrl.substring(0, apiUrl.length - 1);
// use apiUrl by default, which has less priority over the following cases // use apiUrl by default, which has less priority over the following cases
@@ -90,10 +88,6 @@ const appConfig = {
licenseKey: process.env.LICENSE_KEY, licenseKey: process.env.LICENSE_KEY,
sentryDsn: process.env.SENTRY_DSN, sentryDsn: process.env.SENTRY_DSN,
CI: process.env.CI === 'true', CI: process.env.CI === 'true',
disableNotificationsPage: process.env.DISABLE_NOTIFICATIONS_PAGE === 'true',
disableFavicon: process.env.DISABLE_FAVICON === 'true',
additionalDrawerLink: process.env.ADDITIONAL_DRAWER_LINK,
additionalDrawerLinkText: process.env.ADDITIONAL_DRAWER_LINK_TEXT,
}; };
if (!appConfig.encryptionKey) { if (!appConfig.encryptionKey) {

View File

@@ -1,10 +0,0 @@
import { renderObject } from '../../../../../helpers/renderer.js';
import SamlAuthProvider from '../../../../../models/saml-auth-provider.ee.js';
export default async (request, response) => {
const samlAuthProvider = await SamlAuthProvider.query()
.findById(request.params.samlAuthProviderId)
.throwIfNotFound();
renderObject(response, samlAuthProvider);
};

View File

@@ -1,34 +0,0 @@
import { vi, 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.js';
import { createRole } from '../../../../../../test/factories/role.js';
import { createUser } from '../../../../../../test/factories/user.js';
import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js';
import getSamlAuthProviderMock from '../../../../../../test/mocks/rest/api/v1/saml-auth-providers/get-saml-auth-provider.ee.js';
import * as license from '../../../../../helpers/license.ee.js';
describe('GET /api/v1/admin/saml-auth-provider/:samlAuthProviderId', () => {
let samlAuthProvider, currentUser, token;
beforeEach(async () => {
const role = await createRole({ key: 'admin' });
currentUser = await createUser({ roleId: role.id });
samlAuthProvider = await createSamlAuthProvider();
token = createAuthTokenByUserId(currentUser.id);
});
it('should return saml auth provider with specified id', async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
const response = await request(app)
.get(`/api/v1/admin/saml-auth-providers/${samlAuthProvider.id}`)
.set('Authorization', token)
.expect(200);
const expectedPayload = await getSamlAuthProviderMock(samlAuthProvider);
expect(response.body).toEqual(expectedPayload);
});
});

View File

@@ -1,11 +0,0 @@
import { renderObject } from '../../../../../helpers/renderer.js';
import SamlAuthProvider from '../../../../../models/saml-auth-provider.ee.js';
export default async (request, response) => {
const samlAuthProviders = await SamlAuthProvider.query().orderBy(
'created_at',
'desc'
);
renderObject(response, samlAuthProviders);
};

View File

@@ -1,39 +0,0 @@
import { vi, 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.js';
import { createRole } from '../../../../../../test/factories/role.js';
import { createUser } from '../../../../../../test/factories/user.js';
import { createSamlAuthProvider } from '../../../../../../test/factories/saml-auth-provider.ee.js';
import getSamlAuthProvidersMock from '../../../../../../test/mocks/rest/api/v1/saml-auth-providers/get-saml-auth-providers.ee.js';
import * as license from '../../../../../helpers/license.ee.js';
describe('GET /api/v1/admin/saml-auth-providers', () => {
let samlAuthProviderOne, samlAuthProviderTwo, currentUser, token;
beforeEach(async () => {
const role = await createRole({ key: 'admin' });
currentUser = await createUser({ roleId: role.id });
samlAuthProviderOne = await createSamlAuthProvider();
samlAuthProviderTwo = await createSamlAuthProvider();
token = createAuthTokenByUserId(currentUser.id);
});
it('should return saml auth providers', async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
const response = await request(app)
.get('/api/v1/admin/saml-auth-providers')
.set('Authorization', token)
.expect(200);
const expectedPayload = await getSamlAuthProvidersMock([
samlAuthProviderTwo,
samlAuthProviderOne,
]);
expect(response.body).toEqual(expectedPayload);
});
});

View File

@@ -1,6 +0,0 @@
import appConfig from '../../../../config/app.js';
import { renderObject } from '../../../../helpers/renderer.js';
export default async (request, response) => {
renderObject(response, { version: appConfig.version });
};

View File

@@ -1,26 +0,0 @@
import { describe, it, expect } from 'vitest';
import request from 'supertest';
import app from '../../../../app.js';
describe('GET /api/v1/automatisch/version', () => {
it('should return Automatisch version', async () => {
const response = await request(app)
.get('/api/v1/automatisch/version')
.expect(200);
const expectedPayload = {
data: {
version: '0.10.0',
},
meta: {
count: 1,
currentPage: null,
isArray: false,
totalPages: null,
type: 'Object',
},
};
expect(response.body).toEqual(expectedPayload);
});
});

View File

@@ -1,5 +0,0 @@
import { renderObject } from '../../../../helpers/renderer.js';
export default async (request, response) => {
renderObject(response, request.currentUser);
};

View File

@@ -1,26 +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 getCurrentUserMock from '../../../../../test/mocks/rest/api/v1/users/get-current-user';
describe('GET /api/v1/users/me', () => {
let role, currentUser, token;
beforeEach(async () => {
currentUser = await createUser();
role = await currentUser.$relatedQuery('role');
token = createAuthTokenByUserId(currentUser.id);
});
it('should return current user info', async () => {
const response = await request(app)
.get('/api/v1/users/me')
.set('Authorization', token)
.expect(200);
const expectedPayload = getCurrentUserMock(currentUser, role);
expect(response.body).toEqual(expectedPayload);
});
});

View File

@@ -1,12 +0,0 @@
import { renderObject } from '../../../../helpers/renderer.js';
export default async (request, response) => {
const inTrial = await request.currentUser.inTrial();
const trialInfo = {
inTrial,
expireAt: request.currentUser.trialExpiryDate,
};
renderObject(response, trialInfo);
};

View File

@@ -1,38 +0,0 @@
import { vi, 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.js';
import { createUser } from '../../../../../test/factories/user.js';
import getUserTrialMock from '../../../../../test/mocks/rest/api/v1/users/get-user-trial.js';
import appConfig from '../../../../config/app.js';
import { DateTime } from 'luxon';
import User from '../../../../models/user.js';
describe('GET /api/v1/users/:userId/trial', () => {
let user, token;
beforeEach(async () => {
const trialExpiryDate = DateTime.now().plus({ days: 30 }).toISODate();
user = await createUser({ trialExpiryDate });
token = createAuthTokenByUserId(user.id);
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
});
describe('should return in trial, active subscription and expire at info', () => {
beforeEach(async () => {
vi.spyOn(User.prototype, 'inTrial').mockResolvedValue(false);
vi.spyOn(User.prototype, 'hasActiveSubscription').mockResolvedValue(true);
});
it('should return null', async () => {
const response = await request(app)
.get(`/api/v1/users/${user.id}/trial`)
.set('Authorization', token)
.expect(200);
const expectedResponsePayload = await getUserTrialMock(user);
expect(response.body).toEqual(expectedResponsePayload);
});
});
});

View File

@@ -1,16 +0,0 @@
import { renderObject } from '../../../../helpers/renderer.js';
import User from '../../../../models/user.js';
export default async (request, response) => {
const user = await User.query()
.leftJoinRelated({
role: true,
})
.withGraphFetched({
role: true,
})
.findById(request.params.userId)
.throwIfNotFound();
renderObject(response, user);
};

View File

@@ -1,36 +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 { createPermission } from '../../../../../test/factories/permission';
import getUserMock from '../../../../../test/mocks/rest/api/v1/users/get-user';
describe('GET /api/v1/users/:userId', () => {
let currentUser, currentUserRole, anotherUser, anotherUserRole, token;
beforeEach(async () => {
currentUser = await createUser();
anotherUser = await createUser();
currentUserRole = await currentUser.$relatedQuery('role');
anotherUserRole = await anotherUser.$relatedQuery('role');
await createPermission({
roleId: currentUserRole.id,
action: 'read',
subject: 'User',
});
token = createAuthTokenByUserId(currentUser.id);
});
it('should return specified user info', async () => {
const response = await request(app)
.get(`/api/v1/users/${anotherUser.id}`)
.set('Authorization', token)
.expect(200);
const expectedPayload = getUserMock(anotherUser, anotherUserRole);
expect(response.body).toEqual(expectedPayload);
});
});

View File

@@ -1,18 +0,0 @@
import { renderObject } from '../../../../helpers/renderer.js';
import User from '../../../../models/user.js';
import paginateRest from '../../../../helpers/pagination-rest.js';
export default async (request, response) => {
const usersQuery = User.query()
.leftJoinRelated({
role: true,
})
.withGraphFetched({
role: true,
})
.orderBy('full_name', 'asc');
const users = await paginateRest(usersQuery, request.query.page);
renderObject(response, users);
};

View File

@@ -1,56 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest';
import app from '../../../../app';
import createAuthTokenByUserId from '../../../../helpers/create-auth-token-by-user-id';
import { createRole } from '../../../../../test/factories/role';
import { createPermission } from '../../../../../test/factories/permission';
import { createUser } from '../../../../../test/factories/user';
import getUsersMock from '../../../../../test/mocks/rest/api/v1/users/get-users';
describe('GET /api/v1/users', () => {
let currentUser, currentUserRole, anotherUser, anotherUserRole, token;
beforeEach(async () => {
currentUserRole = await createRole({
key: 'currentUser',
name: 'Current user role',
});
await createPermission({
action: 'read',
subject: 'User',
roleId: currentUserRole.id,
});
currentUser = await createUser({
roleId: currentUserRole.id,
fullName: 'Current User',
});
anotherUserRole = await createRole({
key: 'anotherUser',
name: 'Another user role',
});
anotherUser = await createUser({
roleId: anotherUserRole.id,
fullName: 'Another User',
});
token = createAuthTokenByUserId(currentUser.id);
});
it('should return users data', async () => {
const response = await request(app)
.get('/api/v1/users')
.set('Authorization', token)
.expect(200);
const expectedResponsePayload = await getUsersMock(
[anotherUser, currentUser],
[anotherUserRole, currentUserRole]
);
expect(response.body).toEqual(expectedResponsePayload);
});
});

View File

@@ -1,3 +0,0 @@
export default async (request, response) => {
response.status(200).end();
};

View File

@@ -1,9 +0,0 @@
import { describe, it } from 'vitest';
import request from 'supertest';
import app from '../../app.js';
describe('GET /healthcheck', () => {
it('should return 200 response with version data', async () => {
await request(app).get('/healthcheck').expect(200);
});
});

View File

@@ -1,10 +1,7 @@
import appConfig from '../../config/app.js';
import User from '../../models/user.js'; import User from '../../models/user.js';
import Role from '../../models/role.js'; import Role from '../../models/role.js';
const registerUser = async (_parent, params) => { const registerUser = async (_parent, params) => {
if (!appConfig.isCloud) return;
const { fullName, email, password } = params.input; const { fullName, email, password } = params.input;
const existingUser = await User.query().findOne({ const existingUser = await User.query().findOne({

View File

@@ -1,17 +1,9 @@
import appConfig from '../../config/app.js';
import { hasValidLicense } from '../../helpers/license.ee.js'; import { hasValidLicense } from '../../helpers/license.ee.js';
import Config from '../../models/config.js'; import Config from '../../models/config.js';
const getConfig = async (_parent, params) => { const getConfig = async (_parent, params) => {
if (!(await hasValidLicense())) return {}; if (!(await hasValidLicense())) return {};
const defaultConfig = {
disableNotificationsPage: appConfig.disableNotificationsPage,
disableFavicon: appConfig.disableFavicon,
additionalDrawerLink: appConfig.additionalDrawerLink,
additionalDrawerLinkText: appConfig.additionalDrawerLinkText,
};
const configQuery = Config.query(); const configQuery = Config.query();
if (Array.isArray(params.keys)) { if (Array.isArray(params.keys)) {
@@ -26,7 +18,7 @@ const getConfig = async (_parent, params) => {
computedConfig[key] = value?.data; computedConfig[key] = value?.data;
return computedConfig; return computedConfig;
}, defaultConfig); }, {});
}; };
export default getConfig; export default getConfig;

View File

@@ -2,7 +2,6 @@ import { vi, describe, it, expect, beforeEach } from 'vitest';
import request from 'supertest'; import request from 'supertest';
import app from '../../app'; import app from '../../app';
import { createConfig } from '../../../test/factories/config'; import { createConfig } from '../../../test/factories/config';
import appConfig from '../../config/app';
import * as license from '../../helpers/license.ee'; import * as license from '../../helpers/license.ee';
describe('graphQL getConfig query', () => { describe('graphQL getConfig query', () => {
@@ -57,10 +56,6 @@ describe('graphQL getConfig query', () => {
[configOne.key]: configOne.value.data, [configOne.key]: configOne.value.data,
[configTwo.key]: configTwo.value.data, [configTwo.key]: configTwo.value.data,
[configThree.key]: configThree.value.data, [configThree.key]: configThree.value.data,
disableNotificationsPage: false,
disableFavicon: false,
additionalDrawerLink: undefined,
additionalDrawerLinkText: undefined,
}, },
}, },
}; };
@@ -87,48 +82,6 @@ describe('graphQL getConfig query', () => {
getConfig: { getConfig: {
[configOne.key]: configOne.value.data, [configOne.key]: configOne.value.data,
[configTwo.key]: configTwo.value.data, [configTwo.key]: configTwo.value.data,
disableNotificationsPage: false,
disableFavicon: false,
additionalDrawerLink: undefined,
additionalDrawerLinkText: undefined,
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
});
describe('and with different defaults', () => {
beforeEach(async () => {
vi.spyOn(appConfig, 'disableNotificationsPage', 'get').mockReturnValue(
true
);
vi.spyOn(appConfig, 'disableFavicon', 'get').mockReturnValue(true);
vi.spyOn(appConfig, 'additionalDrawerLink', 'get').mockReturnValue(
'https://automatisch.io'
);
vi.spyOn(appConfig, 'additionalDrawerLinkText', 'get').mockReturnValue(
'Automatisch'
);
});
it('should return custom config', async () => {
const response = await request(app)
.post('/graphql')
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getConfig: {
[configOne.key]: configOne.value.data,
[configTwo.key]: configTwo.value.data,
[configThree.key]: configThree.value.data,
disableNotificationsPage: true,
disableFavicon: true,
additionalDrawerLink: 'https://automatisch.io',
additionalDrawerLinkText: 'Automatisch',
}, },
}, },
}; };

View File

@@ -6,74 +6,100 @@ import { createRole } from '../../../test/factories/role';
import { createUser } from '../../../test/factories/user'; import { createUser } from '../../../test/factories/user';
describe('graphQL getCurrentUser query', () => { describe('graphQL getCurrentUser query', () => {
let role, currentUser, token, requestObject; describe('with unauthenticated user', () => {
it('should throw not authorized error', async () => {
const invalidUserToken = 'invalid-token';
beforeEach(async () => { const query = `
role = await createRole({ query {
key: 'sample', getCurrentUser {
name: 'sample',
});
currentUser = await createUser({
roleId: role.id,
});
token = createAuthTokenByUserId(currentUser.id);
requestObject = request(app).post('/graphql').set('Authorization', token);
});
it('should return user data', async () => {
const query = `
query {
getCurrentUser {
id
email
fullName
email
createdAt
updatedAt
role {
id id
name email
} }
} }
} `;
`;
const response = await requestObject.send({ query }).expect(200); const response = await request(app)
.post('/graphql')
.set('Authorization', invalidUserToken)
.send({ query })
.expect(200);
const expectedResponsePayload = { expect(response.body.errors).toBeDefined();
data: { expect(response.body.errors[0].message).toEqual('Not Authorised!');
getCurrentUser: { });
createdAt: currentUser.createdAt.getTime().toString(),
email: currentUser.email,
fullName: currentUser.fullName,
id: currentUser.id,
role: { id: role.id, name: role.name },
updatedAt: currentUser.updatedAt.getTime().toString(),
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
}); });
it('should not return user password', async () => { describe('with authenticated user', () => {
const query = ` let role, currentUser, token, requestObject;
query {
getCurrentUser { beforeEach(async () => {
id role = await createRole({
email key: 'sample',
password name: 'sample',
});
currentUser = await createUser({
roleId: role.id,
});
token = createAuthTokenByUserId(currentUser.id);
requestObject = request(app).post('/graphql').set('Authorization', token);
});
it('should return user data', async () => {
const query = `
query {
getCurrentUser {
id
email
fullName
email
createdAt
updatedAt
role {
id
name
}
}
} }
} `;
`;
const response = await requestObject.send({ query }).expect(400); const response = await requestObject.send({ query }).expect(200);
expect(response.body.errors).toBeDefined(); const expectedResponsePayload = {
expect(response.body.errors[0].message).toEqual( data: {
'Cannot query field "password" on type "User".' getCurrentUser: {
); createdAt: currentUser.createdAt.getTime().toString(),
email: currentUser.email,
fullName: currentUser.fullName,
id: currentUser.id,
role: { id: role.id, name: role.name },
updatedAt: currentUser.updatedAt.getTime().toString(),
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
it('should not return user password', async () => {
const query = `
query {
getCurrentUser {
id
email
password
}
}
`;
const response = await requestObject.send({ query }).expect(400);
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual(
'Cannot query field "password" on type "User".'
);
});
}); });
}); });

View File

@@ -40,291 +40,307 @@ describe('graphQL getExecutions query', () => {
} }
`; `;
describe('and without correct permissions', () => { const invalidToken = 'invalid-token';
it('should throw not authorized error', async () => {
const userWithoutPermissions = await createUser();
const token = createAuthTokenByUserId(userWithoutPermissions.id);
describe('with unauthenticated user', () => {
it('should throw not authorized error', async () => {
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', token) .set('Authorization', invalidToken)
.send({ query }) .send({ query })
.expect(200); .expect(200);
expect(response.body.errors).toBeDefined(); expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not authorized!'); expect(response.body.errors[0].message).toEqual('Not Authorised!');
}); });
}); });
describe('and with correct permission', () => { describe('with authenticated user', () => {
let role, describe('and without permissions', () => {
currentUser, it('should throw not authorized error', async () => {
anotherUser, const userWithoutPermissions = await createUser();
token, const token = createAuthTokenByUserId(userWithoutPermissions.id);
flowOne,
stepOneForFlowOne,
stepTwoForFlowOne,
executionOne,
flowTwo,
stepOneForFlowTwo,
stepTwoForFlowTwo,
executionTwo,
flowThree,
stepOneForFlowThree,
stepTwoForFlowThree,
executionThree,
expectedResponseForExecutionOne,
expectedResponseForExecutionTwo,
expectedResponseForExecutionThree;
beforeEach(async () => { const response = await request(app)
role = await createRole({ .post('/graphql')
key: 'sample', .set('Authorization', token)
name: 'sample', .send({ query })
.expect(200);
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not authorized!');
}); });
});
currentUser = await createUser({ describe('and with correct permission', () => {
roleId: role.id, let role,
fullName: 'Current User', currentUser,
}); anotherUser,
token,
flowOne,
stepOneForFlowOne,
stepTwoForFlowOne,
executionOne,
flowTwo,
stepOneForFlowTwo,
stepTwoForFlowTwo,
executionTwo,
flowThree,
stepOneForFlowThree,
stepTwoForFlowThree,
executionThree,
expectedResponseForExecutionOne,
expectedResponseForExecutionTwo,
expectedResponseForExecutionThree;
anotherUser = await createUser(); beforeEach(async () => {
role = await createRole({
key: 'sample',
name: 'sample',
});
token = createAuthTokenByUserId(currentUser.id); currentUser = await createUser({
roleId: role.id,
fullName: 'Current User',
});
flowOne = await createFlow({ anotherUser = await createUser();
userId: currentUser.id,
});
stepOneForFlowOne = await createStep({ token = createAuthTokenByUserId(currentUser.id);
flowId: flowOne.id,
});
stepTwoForFlowOne = await createStep({ flowOne = await createFlow({
flowId: flowOne.id, userId: currentUser.id,
}); });
executionOne = await createExecution({ stepOneForFlowOne = await createStep({
flowId: flowOne.id, flowId: flowOne.id,
}); });
await createExecutionStep({ stepTwoForFlowOne = await createStep({
executionId: executionOne.id, flowId: flowOne.id,
stepId: stepOneForFlowOne.id, });
status: 'success',
});
await createExecutionStep({ executionOne = await createExecution({
executionId: executionOne.id, flowId: flowOne.id,
stepId: stepTwoForFlowOne.id, });
status: 'success',
});
flowTwo = await createFlow({ await createExecutionStep({
userId: currentUser.id, executionId: executionOne.id,
}); stepId: stepOneForFlowOne.id,
stepOneForFlowTwo = await createStep({
flowId: flowTwo.id,
});
stepTwoForFlowTwo = await createStep({
flowId: flowTwo.id,
});
executionTwo = await createExecution({
flowId: flowTwo.id,
});
await createExecutionStep({
executionId: executionTwo.id,
stepId: stepOneForFlowTwo.id,
status: 'success',
});
await createExecutionStep({
executionId: executionTwo.id,
stepId: stepTwoForFlowTwo.id,
status: 'failure',
});
flowThree = await createFlow({
userId: anotherUser.id,
});
stepOneForFlowThree = await createStep({
flowId: flowThree.id,
});
stepTwoForFlowThree = await createStep({
flowId: flowThree.id,
});
executionThree = await createExecution({
flowId: flowThree.id,
});
await createExecutionStep({
executionId: executionThree.id,
stepId: stepOneForFlowThree.id,
status: 'success',
});
await createExecutionStep({
executionId: executionThree.id,
stepId: stepTwoForFlowThree.id,
status: 'failure',
});
expectedResponseForExecutionOne = {
node: {
createdAt: executionOne.createdAt.getTime().toString(),
flow: {
active: flowOne.active,
id: flowOne.id,
name: flowOne.name,
steps: [
{
iconUrl: `${appConfig.baseUrl}/apps/${stepOneForFlowOne.appKey}/assets/favicon.svg`,
},
{
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowOne.appKey}/assets/favicon.svg`,
},
],
},
id: executionOne.id,
status: 'success', status: 'success',
testRun: executionOne.testRun,
updatedAt: executionOne.updatedAt.getTime().toString(),
},
};
expectedResponseForExecutionTwo = {
node: {
createdAt: executionTwo.createdAt.getTime().toString(),
flow: {
active: flowTwo.active,
id: flowTwo.id,
name: flowTwo.name,
steps: [
{
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowTwo.appKey}/assets/favicon.svg`,
},
{
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowTwo.appKey}/assets/favicon.svg`,
},
],
},
id: executionTwo.id,
status: 'failure',
testRun: executionTwo.testRun,
updatedAt: executionTwo.updatedAt.getTime().toString(),
},
};
expectedResponseForExecutionThree = {
node: {
createdAt: executionThree.createdAt.getTime().toString(),
flow: {
active: flowThree.active,
id: flowThree.id,
name: flowThree.name,
steps: [
{
iconUrl: `${appConfig.baseUrl}/apps/${stepOneForFlowThree.appKey}/assets/favicon.svg`,
},
{
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowThree.appKey}/assets/favicon.svg`,
},
],
},
id: executionThree.id,
status: 'failure',
testRun: executionThree.testRun,
updatedAt: executionThree.updatedAt.getTime().toString(),
},
};
});
describe('and with isCreator condition', () => {
beforeEach(async () => {
await createPermission({
action: 'read',
subject: 'Execution',
roleId: role.id,
conditions: ['isCreator'],
}); });
});
it('should return executions data of the current user', async () => { await createExecutionStep({
const response = await request(app) executionId: executionOne.id,
.post('/graphql') stepId: stepTwoForFlowOne.id,
.set('Authorization', token) status: 'success',
.send({ query }) });
.expect(200);
const expectedResponsePayload = { flowTwo = await createFlow({
data: { userId: currentUser.id,
getExecutions: { });
edges: [
expectedResponseForExecutionTwo, stepOneForFlowTwo = await createStep({
expectedResponseForExecutionOne, flowId: flowTwo.id,
});
stepTwoForFlowTwo = await createStep({
flowId: flowTwo.id,
});
executionTwo = await createExecution({
flowId: flowTwo.id,
});
await createExecutionStep({
executionId: executionTwo.id,
stepId: stepOneForFlowTwo.id,
status: 'success',
});
await createExecutionStep({
executionId: executionTwo.id,
stepId: stepTwoForFlowTwo.id,
status: 'failure',
});
flowThree = await createFlow({
userId: anotherUser.id,
});
stepOneForFlowThree = await createStep({
flowId: flowThree.id,
});
stepTwoForFlowThree = await createStep({
flowId: flowThree.id,
});
executionThree = await createExecution({
flowId: flowThree.id,
});
await createExecutionStep({
executionId: executionThree.id,
stepId: stepOneForFlowThree.id,
status: 'success',
});
await createExecutionStep({
executionId: executionThree.id,
stepId: stepTwoForFlowThree.id,
status: 'failure',
});
expectedResponseForExecutionOne = {
node: {
createdAt: executionOne.createdAt.getTime().toString(),
flow: {
active: flowOne.active,
id: flowOne.id,
name: flowOne.name,
steps: [
{
iconUrl: `${appConfig.baseUrl}/apps/${stepOneForFlowOne.appKey}/assets/favicon.svg`,
},
{
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowOne.appKey}/assets/favicon.svg`,
},
], ],
pageInfo: { currentPage: 1, totalPages: 1 },
}, },
id: executionOne.id,
status: 'success',
testRun: executionOne.testRun,
updatedAt: executionOne.updatedAt.getTime().toString(),
}, },
}; };
expect(response.body).toEqual(expectedResponsePayload); expectedResponseForExecutionTwo = {
}); node: {
}); createdAt: executionTwo.createdAt.getTime().toString(),
flow: {
describe('and without isCreator condition', () => { active: flowTwo.active,
beforeEach(async () => { id: flowTwo.id,
await createPermission({ name: flowTwo.name,
action: 'read', steps: [
subject: 'Execution', {
roleId: role.id, iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowTwo.appKey}/assets/favicon.svg`,
conditions: [], },
}); {
}); iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowTwo.appKey}/assets/favicon.svg`,
},
it('should return executions data of all users', async () => {
const response = await request(app)
.post('/graphql')
.set('Authorization', token)
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getExecutions: {
edges: [
expectedResponseForExecutionThree,
expectedResponseForExecutionTwo,
expectedResponseForExecutionOne,
], ],
pageInfo: { currentPage: 1, totalPages: 1 },
}, },
id: executionTwo.id,
status: 'failure',
testRun: executionTwo.testRun,
updatedAt: executionTwo.updatedAt.getTime().toString(),
}, },
}; };
expect(response.body).toEqual(expectedResponsePayload); expectedResponseForExecutionThree = {
node: {
createdAt: executionThree.createdAt.getTime().toString(),
flow: {
active: flowThree.active,
id: flowThree.id,
name: flowThree.name,
steps: [
{
iconUrl: `${appConfig.baseUrl}/apps/${stepOneForFlowThree.appKey}/assets/favicon.svg`,
},
{
iconUrl: `${appConfig.baseUrl}/apps/${stepTwoForFlowThree.appKey}/assets/favicon.svg`,
},
],
},
id: executionThree.id,
status: 'failure',
testRun: executionThree.testRun,
updatedAt: executionThree.updatedAt.getTime().toString(),
},
};
}); });
});
describe('and with filters', () => { describe('and with isCreator condition', () => {
beforeEach(async () => { beforeEach(async () => {
await createPermission({ await createPermission({
action: 'read', action: 'read',
subject: 'Execution', subject: 'Execution',
roleId: role.id, roleId: role.id,
conditions: [], conditions: ['isCreator'],
});
});
it('should return executions data of the current user', async () => {
const response = await request(app)
.post('/graphql')
.set('Authorization', token)
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getExecutions: {
edges: [
expectedResponseForExecutionTwo,
expectedResponseForExecutionOne,
],
pageInfo: { currentPage: 1, totalPages: 1 },
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
}); });
}); });
it('should return executions data for the specified flow', async () => { describe('and without isCreator condition', () => {
const query = ` beforeEach(async () => {
await createPermission({
action: 'read',
subject: 'Execution',
roleId: role.id,
conditions: [],
});
});
it('should return executions data of all users', async () => {
const response = await request(app)
.post('/graphql')
.set('Authorization', token)
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getExecutions: {
edges: [
expectedResponseForExecutionThree,
expectedResponseForExecutionTwo,
expectedResponseForExecutionOne,
],
pageInfo: { currentPage: 1, totalPages: 1 },
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
});
describe('and with filters', () => {
beforeEach(async () => {
await createPermission({
action: 'read',
subject: 'Execution',
roleId: role.id,
conditions: [],
});
});
it('should return executions data for the specified flow', async () => {
const query = `
query { query {
getExecutions(limit: 10, offset: 0, filters: { flowId: "${flowOne.id}" }) { getExecutions(limit: 10, offset: 0, filters: { flowId: "${flowOne.id}" }) {
pageInfo { pageInfo {
@@ -352,26 +368,26 @@ describe('graphQL getExecutions query', () => {
} }
`; `;
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', token) .set('Authorization', token)
.send({ query }) .send({ query })
.expect(200); .expect(200);
const expectedResponsePayload = { const expectedResponsePayload = {
data: { data: {
getExecutions: { getExecutions: {
edges: [expectedResponseForExecutionOne], edges: [expectedResponseForExecutionOne],
pageInfo: { currentPage: 1, totalPages: 1 }, pageInfo: { currentPage: 1, totalPages: 1 },
},
}, },
}, };
};
expect(response.body).toEqual(expectedResponsePayload); expect(response.body).toEqual(expectedResponsePayload);
}); });
it('should return only executions data with success status', async () => { it('should return only executions data with success status', async () => {
const query = ` const query = `
query { query {
getExecutions(limit: 10, offset: 0, filters: { status: "success" }) { getExecutions(limit: 10, offset: 0, filters: { status: "success" }) {
pageInfo { pageInfo {
@@ -399,30 +415,30 @@ describe('graphQL getExecutions query', () => {
} }
`; `;
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', token) .set('Authorization', token)
.send({ query }) .send({ query })
.expect(200); .expect(200);
const expectedResponsePayload = { const expectedResponsePayload = {
data: { data: {
getExecutions: { getExecutions: {
edges: [expectedResponseForExecutionOne], edges: [expectedResponseForExecutionOne],
pageInfo: { currentPage: 1, totalPages: 1 }, pageInfo: { currentPage: 1, totalPages: 1 },
},
}, },
}, };
};
expect(response.body).toEqual(expectedResponsePayload); expect(response.body).toEqual(expectedResponsePayload);
}); });
it('should return only executions data within date range', async () => { it('should return only executions data within date range', async () => {
const createdAtFrom = executionOne.createdAt.getTime().toString(); const createdAtFrom = executionOne.createdAt.getTime().toString();
const createdAtTo = executionOne.createdAt.getTime().toString(); const createdAtTo = executionOne.createdAt.getTime().toString();
const query = ` const query = `
query { query {
getExecutions(limit: 10, offset: 0, filters: { createdAt: { from: "${createdAtFrom}", to: "${createdAtTo}" }}) { getExecutions(limit: 10, offset: 0, filters: { createdAt: { from: "${createdAtFrom}", to: "${createdAtTo}" }}) {
pageInfo { pageInfo {
@@ -450,22 +466,23 @@ describe('graphQL getExecutions query', () => {
} }
`; `;
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', token) .set('Authorization', token)
.send({ query }) .send({ query })
.expect(200); .expect(200);
const expectedResponsePayload = { const expectedResponsePayload = {
data: { data: {
getExecutions: { getExecutions: {
edges: [expectedResponseForExecutionOne], edges: [expectedResponseForExecutionOne],
pageInfo: { currentPage: 1, totalPages: 1 }, pageInfo: { currentPage: 1, totalPages: 1 },
},
}, },
}, };
};
expect(response.body).toEqual(expectedResponsePayload); expect(response.body).toEqual(expectedResponsePayload);
});
}); });
}); });
}); });

View File

@@ -40,200 +40,222 @@ describe('graphQL getFlow query', () => {
`; `;
}; };
describe('and without permissions', () => { describe('with unauthenticated user', () => {
it('should throw not authorized error', async () => { it('should throw not authorized error', async () => {
const userWithoutPermissions = await createUser(); const invalidToken = 'invalid-token';
const token = createAuthTokenByUserId(userWithoutPermissions.id);
const flow = await createFlow(); const flow = await createFlow();
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', token) .set('Authorization', invalidToken)
.send({ query: query(flow.id) }) .send({ query: query(flow.id) })
.expect(200); .expect(200);
expect(response.body.errors).toBeDefined(); expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not authorized!'); expect(response.body.errors[0].message).toEqual('Not Authorised!');
}); });
}); });
describe('and with correct permission', () => { describe('with authenticated user', () => {
let currentUser, currentUserRole, currentUserFlow; describe('and without permissions', () => {
it('should throw not authorized error', async () => {
beforeEach(async () => { const userWithoutPermissions = await createUser();
currentUserRole = await createRole(); const token = createAuthTokenByUserId(userWithoutPermissions.id);
currentUser = await createUser({ roleId: currentUserRole.id }); const flow = await createFlow();
currentUserFlow = await createFlow({ userId: currentUser.id });
});
describe('and with isCreator condition', () => {
it('should return executions data of the current user', async () => {
await createPermission({
action: 'read',
subject: 'Flow',
roleId: currentUserRole.id,
conditions: ['isCreator'],
});
const triggerStep = await createStep({
flowId: currentUserFlow.id,
type: 'trigger',
key: 'catchRawWebhook',
webhookPath: `/webhooks/flows/${currentUserFlow.id}`,
});
const actionConnection = await createConnection({
userId: currentUser.id,
formattedData: {
screenName: 'Test',
authenticationKey: 'test key',
},
});
const actionStep = await createStep({
flowId: currentUserFlow.id,
type: 'action',
connectionId: actionConnection.id,
key: 'translateText',
});
const token = createAuthTokenByUserId(currentUser.id);
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', token) .set('Authorization', token)
.send({ query: query(currentUserFlow.id) }) .send({ query: query(flow.id) })
.expect(200); .expect(200);
const expectedResponsePayload = { expect(response.body.errors).toBeDefined();
data: { expect(response.body.errors[0].message).toEqual('Not authorized!');
getFlow: {
active: currentUserFlow.active,
id: currentUserFlow.id,
name: currentUserFlow.name,
status: 'draft',
steps: [
{
appKey: triggerStep.appKey,
connection: null,
iconUrl: `${appConfig.baseUrl}/apps/${triggerStep.appKey}/assets/favicon.svg`,
id: triggerStep.id,
key: 'catchRawWebhook',
parameters: {},
position: 1,
status: triggerStep.status,
type: 'trigger',
webhookUrl: `${appConfig.baseUrl}/webhooks/flows/${currentUserFlow.id}`,
},
{
appKey: actionStep.appKey,
connection: {
createdAt: actionConnection.createdAt.getTime().toString(),
id: actionConnection.id,
verified: actionConnection.verified,
},
iconUrl: `${appConfig.baseUrl}/apps/${actionStep.appKey}/assets/favicon.svg`,
id: actionStep.id,
key: 'translateText',
parameters: {},
position: 1,
status: actionStep.status,
type: 'action',
webhookUrl: 'http://localhost:3000/null',
},
],
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
}); });
}); });
describe('and without isCreator condition', () => { describe('and with correct permission', () => {
it('should return executions data of all users', async () => { let currentUser, currentUserRole, currentUserFlow;
await createPermission({
action: 'read',
subject: 'Flow',
roleId: currentUserRole.id,
conditions: [],
});
const anotherUser = await createUser(); beforeEach(async () => {
const anotherUserFlow = await createFlow({ userId: anotherUser.id }); currentUserRole = await createRole();
currentUser = await createUser({ roleId: currentUserRole.id });
currentUserFlow = await createFlow({ userId: currentUser.id });
});
const triggerStep = await createStep({ describe('and with isCreator condition', () => {
flowId: anotherUserFlow.id, it('should return executions data of the current user', async () => {
type: 'trigger', await createPermission({
key: 'catchRawWebhook', action: 'read',
webhookPath: `/webhooks/flows/${anotherUserFlow.id}`, subject: 'Flow',
}); roleId: currentUserRole.id,
conditions: ['isCreator'],
});
const actionConnection = await createConnection({ const triggerStep = await createStep({
userId: anotherUser.id, flowId: currentUserFlow.id,
formattedData: { type: 'trigger',
screenName: 'Test', key: 'catchRawWebhook',
authenticationKey: 'test key', webhookPath: `/webhooks/flows/${currentUserFlow.id}`,
}, });
});
const actionStep = await createStep({ const actionConnection = await createConnection({
flowId: anotherUserFlow.id, userId: currentUser.id,
type: 'action', formattedData: {
connectionId: actionConnection.id, screenName: 'Test',
key: 'translateText', authenticationKey: 'test key',
});
const token = createAuthTokenByUserId(currentUser.id);
const response = await request(app)
.post('/graphql')
.set('Authorization', token)
.send({ query: query(anotherUserFlow.id) })
.expect(200);
const expectedResponsePayload = {
data: {
getFlow: {
active: anotherUserFlow.active,
id: anotherUserFlow.id,
name: anotherUserFlow.name,
status: 'draft',
steps: [
{
appKey: triggerStep.appKey,
connection: null,
iconUrl: `${appConfig.baseUrl}/apps/${triggerStep.appKey}/assets/favicon.svg`,
id: triggerStep.id,
key: 'catchRawWebhook',
parameters: {},
position: 1,
status: triggerStep.status,
type: 'trigger',
webhookUrl: `${appConfig.baseUrl}/webhooks/flows/${anotherUserFlow.id}`,
},
{
appKey: actionStep.appKey,
connection: {
createdAt: actionConnection.createdAt.getTime().toString(),
id: actionConnection.id,
verified: actionConnection.verified,
},
iconUrl: `${appConfig.baseUrl}/apps/${actionStep.appKey}/assets/favicon.svg`,
id: actionStep.id,
key: 'translateText',
parameters: {},
position: 1,
status: actionStep.status,
type: 'action',
webhookUrl: 'http://localhost:3000/null',
},
],
}, },
}, });
};
expect(response.body).toEqual(expectedResponsePayload); const actionStep = await createStep({
flowId: currentUserFlow.id,
type: 'action',
connectionId: actionConnection.id,
key: 'translateText',
});
const token = createAuthTokenByUserId(currentUser.id);
const response = await request(app)
.post('/graphql')
.set('Authorization', token)
.send({ query: query(currentUserFlow.id) })
.expect(200);
const expectedResponsePayload = {
data: {
getFlow: {
active: currentUserFlow.active,
id: currentUserFlow.id,
name: currentUserFlow.name,
status: 'draft',
steps: [
{
appKey: triggerStep.appKey,
connection: null,
iconUrl: `${appConfig.baseUrl}/apps/${triggerStep.appKey}/assets/favicon.svg`,
id: triggerStep.id,
key: 'catchRawWebhook',
parameters: {},
position: 1,
status: triggerStep.status,
type: 'trigger',
webhookUrl: `${appConfig.baseUrl}/webhooks/flows/${currentUserFlow.id}`,
},
{
appKey: actionStep.appKey,
connection: {
createdAt: actionConnection.createdAt
.getTime()
.toString(),
id: actionConnection.id,
verified: actionConnection.verified,
},
iconUrl: `${appConfig.baseUrl}/apps/${actionStep.appKey}/assets/favicon.svg`,
id: actionStep.id,
key: 'translateText',
parameters: {},
position: 1,
status: actionStep.status,
type: 'action',
webhookUrl: 'http://localhost:3000/null',
},
],
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
});
describe('and without isCreator condition', () => {
it('should return executions data of all users', async () => {
await createPermission({
action: 'read',
subject: 'Flow',
roleId: currentUserRole.id,
conditions: [],
});
const anotherUser = await createUser();
const anotherUserFlow = await createFlow({ userId: anotherUser.id });
const triggerStep = await createStep({
flowId: anotherUserFlow.id,
type: 'trigger',
key: 'catchRawWebhook',
webhookPath: `/webhooks/flows/${anotherUserFlow.id}`,
});
const actionConnection = await createConnection({
userId: anotherUser.id,
formattedData: {
screenName: 'Test',
authenticationKey: 'test key',
},
});
const actionStep = await createStep({
flowId: anotherUserFlow.id,
type: 'action',
connectionId: actionConnection.id,
key: 'translateText',
});
const token = createAuthTokenByUserId(currentUser.id);
const response = await request(app)
.post('/graphql')
.set('Authorization', token)
.send({ query: query(anotherUserFlow.id) })
.expect(200);
const expectedResponsePayload = {
data: {
getFlow: {
active: anotherUserFlow.active,
id: anotherUserFlow.id,
name: anotherUserFlow.name,
status: 'draft',
steps: [
{
appKey: triggerStep.appKey,
connection: null,
iconUrl: `${appConfig.baseUrl}/apps/${triggerStep.appKey}/assets/favicon.svg`,
id: triggerStep.id,
key: 'catchRawWebhook',
parameters: {},
position: 1,
status: triggerStep.status,
type: 'trigger',
webhookUrl: `${appConfig.baseUrl}/webhooks/flows/${anotherUserFlow.id}`,
},
{
appKey: actionStep.appKey,
connection: {
createdAt: actionConnection.createdAt
.getTime()
.toString(),
id: actionConnection.id,
verified: actionConnection.verified,
},
iconUrl: `${appConfig.baseUrl}/apps/${actionStep.appKey}/assets/favicon.svg`,
id: actionStep.id,
key: 'translateText',
parameters: {},
position: 1,
status: actionStep.status,
type: 'action',
webhookUrl: 'http://localhost:3000/null',
},
],
},
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
}); });
}); });
}); });

View File

@@ -17,6 +17,7 @@ describe('graphQL getRole query', () => {
userWithoutPermissions, userWithoutPermissions,
tokenWithPermissions, tokenWithPermissions,
tokenWithoutPermissions, tokenWithoutPermissions,
invalidToken,
permissionOne, permissionOne,
permissionTwo; permissionTwo;
@@ -73,91 +74,108 @@ describe('graphQL getRole query', () => {
tokenWithoutPermissions = createAuthTokenByUserId( tokenWithoutPermissions = createAuthTokenByUserId(
userWithoutPermissions.id userWithoutPermissions.id
); );
invalidToken = 'invalid-token';
}); });
describe('and with valid license', () => { describe('with unauthenticated user', () => {
beforeEach(async () => { it('should throw not authorized error', async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); const response = await request(app)
.post('/graphql')
.set('Authorization', invalidToken)
.send({ query: queryWithValidRole })
.expect(200);
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not Authorised!');
}); });
});
describe('and without permissions', () => { describe('with authenticated user', () => {
it('should throw not authorized error', async () => { describe('and with valid license', () => {
const response = await request(app) beforeEach(async () => {
.post('/graphql') vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
.set('Authorization', tokenWithoutPermissions)
.send({ query: queryWithValidRole })
.expect(200);
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not authorized!');
}); });
});
describe('and correct permissions', () => { describe('and without permissions', () => {
it('should return role data for a valid role id', async () => { it('should throw not authorized error', async () => {
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', tokenWithPermissions) .set('Authorization', tokenWithoutPermissions)
.send({ query: queryWithValidRole }) .send({ query: queryWithValidRole })
.expect(200); .expect(200);
const expectedResponsePayload = { expect(response.body.errors).toBeDefined();
data: { expect(response.body.errors[0].message).toEqual('Not authorized!');
getRole: { });
description: validRole.description, });
id: validRole.id,
isAdmin: validRole.key === 'admin', describe('and correct permissions', () => {
key: validRole.key, it('should return role data for a valid role id', async () => {
name: validRole.name, const response = await request(app)
permissions: [ .post('/graphql')
{ .set('Authorization', tokenWithPermissions)
action: permissionOne.action, .send({ query: queryWithValidRole })
conditions: permissionOne.conditions, .expect(200);
id: permissionOne.id,
subject: permissionOne.subject, const expectedResponsePayload = {
}, data: {
{ getRole: {
action: permissionTwo.action, description: validRole.description,
conditions: permissionTwo.conditions, id: validRole.id,
id: permissionTwo.id, isAdmin: validRole.key === 'admin',
subject: permissionTwo.subject, key: validRole.key,
}, name: validRole.name,
], permissions: [
{
action: permissionOne.action,
conditions: permissionOne.conditions,
id: permissionOne.id,
subject: permissionOne.subject,
},
{
action: permissionTwo.action,
conditions: permissionTwo.conditions,
id: permissionTwo.id,
subject: permissionTwo.subject,
},
],
},
}, },
}, };
};
expect(response.body).toEqual(expectedResponsePayload); expect(response.body).toEqual(expectedResponsePayload);
}); });
it('should return not found for invalid role id', async () => { it('should return not found for invalid role id', async () => {
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', tokenWithPermissions) .set('Authorization', tokenWithPermissions)
.send({ query: queryWithInvalidRole }) .send({ query: queryWithInvalidRole })
.expect(200); .expect(200);
expect(response.body.errors).toBeDefined(); expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('NotFoundError'); expect(response.body.errors[0].message).toEqual('NotFoundError');
});
}); });
}); });
});
describe('and without valid license', () => { describe('and without valid license', () => {
beforeEach(async () => { beforeEach(async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(false); vi.spyOn(license, 'hasValidLicense').mockResolvedValue(false);
}); });
describe('and correct permissions', () => { describe('and correct permissions', () => {
it('should throw not authorized error', async () => { it('should throw not authorized error', async () => {
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', tokenWithPermissions) .set('Authorization', tokenWithPermissions)
.send({ query: queryWithInvalidRole }) .send({ query: queryWithInvalidRole })
.expect(200); .expect(200);
expect(response.body.errors).toBeDefined(); expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not authorized!'); expect(response.body.errors[0].message).toEqual('Not authorized!');
});
}); });
}); });
}); });

View File

@@ -15,7 +15,8 @@ describe('graphQL getRoles query', () => {
userWithPermissions, userWithPermissions,
userWithoutPermissions, userWithoutPermissions,
tokenWithPermissions, tokenWithPermissions,
tokenWithoutPermissions; tokenWithoutPermissions,
invalidToken;
beforeEach(async () => { beforeEach(async () => {
currentUserRole = await createRole({ name: 'Current user role' }); currentUserRole = await createRole({ name: 'Current user role' });
@@ -52,82 +53,99 @@ describe('graphQL getRoles query', () => {
tokenWithoutPermissions = createAuthTokenByUserId( tokenWithoutPermissions = createAuthTokenByUserId(
userWithoutPermissions.id userWithoutPermissions.id
); );
invalidToken = 'invalid-token';
}); });
describe('and with valid license', () => { describe('with unauthenticated user', () => {
beforeEach(async () => { it('should throw not authorized error', async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true); const response = await request(app)
}); .post('/graphql')
.set('Authorization', invalidToken)
.send({ query })
.expect(200);
describe('and without permissions', () => { expect(response.body.errors).toBeDefined();
it('should throw not authorized error', async () => { expect(response.body.errors[0].message).toEqual('Not Authorised!');
const response = await request(app)
.post('/graphql')
.set('Authorization', tokenWithoutPermissions)
.send({ query })
.expect(200);
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not authorized!');
});
});
describe('and correct permissions', () => {
it('should return roles data', async () => {
const response = await request(app)
.post('/graphql')
.set('Authorization', tokenWithPermissions)
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getRoles: [
{
description: currentUserRole.description,
id: currentUserRole.id,
isAdmin: currentUserRole.key === 'admin',
key: currentUserRole.key,
name: currentUserRole.name,
},
{
description: roleOne.description,
id: roleOne.id,
isAdmin: roleOne.key === 'admin',
key: roleOne.key,
name: roleOne.name,
},
{
description: roleSecond.description,
id: roleSecond.id,
isAdmin: roleSecond.key === 'admin',
key: roleSecond.key,
name: roleSecond.name,
},
],
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
}); });
}); });
describe('and without valid license', () => { describe('with authenticated user', () => {
beforeEach(async () => { describe('and with valid license', () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(false); beforeEach(async () => {
vi.spyOn(license, 'hasValidLicense').mockResolvedValue(true);
});
describe('and without permissions', () => {
it('should throw not authorized error', async () => {
const response = await request(app)
.post('/graphql')
.set('Authorization', tokenWithoutPermissions)
.send({ query })
.expect(200);
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not authorized!');
});
});
describe('and correct permissions', () => {
it('should return roles data', async () => {
const response = await request(app)
.post('/graphql')
.set('Authorization', tokenWithPermissions)
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getRoles: [
{
description: currentUserRole.description,
id: currentUserRole.id,
isAdmin: currentUserRole.key === 'admin',
key: currentUserRole.key,
name: currentUserRole.name,
},
{
description: roleOne.description,
id: roleOne.id,
isAdmin: roleOne.key === 'admin',
key: roleOne.key,
name: roleOne.name,
},
{
description: roleSecond.description,
id: roleSecond.id,
isAdmin: roleSecond.key === 'admin',
key: roleSecond.key,
name: roleSecond.name,
},
],
},
};
expect(response.body).toEqual(expectedResponsePayload);
});
});
}); });
describe('and correct permissions', () => { describe('and without valid license', () => {
it('should throw not authorized error', async () => { beforeEach(async () => {
const response = await request(app) vi.spyOn(license, 'hasValidLicense').mockResolvedValue(false);
.post('/graphql') });
.set('Authorization', tokenWithPermissions)
.send({ query })
.expect(200);
expect(response.body.errors).toBeDefined(); describe('and correct permissions', () => {
expect(response.body.errors[0].message).toEqual('Not authorized!'); it('should throw not authorized error', async () => {
const response = await request(app)
.post('/graphql')
.set('Authorization', tokenWithPermissions)
.send({ query })
.expect(200);
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not authorized!');
});
}); });
}); });
}); });

View File

@@ -16,46 +16,34 @@ describe('graphQL getTrialStatus query', () => {
} }
`; `;
let user, userToken; const invalidToken = 'invalid-token';
beforeEach(async () => { describe('with unauthenticated user', () => {
const trialExpiryDate = DateTime.now().plus({ days: 30 }).toISODate(); it('should throw not authorized error', async () => {
user = await createUser({ trialExpiryDate });
userToken = createAuthTokenByUserId(user.id);
});
describe('and with cloud flag disabled', () => {
beforeEach(async () => {
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
});
it('should return null', async () => {
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', userToken) .set('Authorization', invalidToken)
.send({ query }) .send({ query })
.expect(200); .expect(200);
const expectedResponsePayload = { expect(response.body.errors).toBeDefined();
data: { getTrialStatus: null }, expect(response.body.errors[0].message).toEqual('Not Authorised!');
};
expect(response.body).toEqual(expectedResponsePayload);
}); });
}); });
describe('and with cloud flag enabled', () => { describe('with authenticated user', () => {
let user, userToken;
beforeEach(async () => { beforeEach(async () => {
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true); const trialExpiryDate = DateTime.now().plus({ days: 30 }).toISODate();
user = await createUser({ trialExpiryDate });
userToken = createAuthTokenByUserId(user.id);
}); });
describe('and not in trial and has active subscription', () => { describe('and with cloud flag disabled', () => {
beforeEach(async () => { beforeEach(async () => {
vi.spyOn(User.prototype, 'inTrial').mockResolvedValue(false); vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
vi.spyOn(User.prototype, 'hasActiveSubscription').mockResolvedValue(
true
);
}); });
it('should return null', async () => { it('should return null', async () => {
@@ -73,27 +61,56 @@ describe('graphQL getTrialStatus query', () => {
}); });
}); });
describe('and in trial period', () => { describe('and with cloud flag enabled', () => {
beforeEach(async () => { beforeEach(async () => {
vi.spyOn(User.prototype, 'inTrial').mockResolvedValue(true); vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
}); });
it('should return null', async () => { describe('and not in trial and has active subscription', () => {
const response = await request(app) beforeEach(async () => {
.post('/graphql') vi.spyOn(User.prototype, 'inTrial').mockResolvedValue(false);
.set('Authorization', userToken) vi.spyOn(User.prototype, 'hasActiveSubscription').mockResolvedValue(
.send({ query }) true
.expect(200); );
});
const expectedResponsePayload = { it('should return null', async () => {
data: { const response = await request(app)
getTrialStatus: { .post('/graphql')
expireAt: new Date(user.trialExpiryDate).getTime().toString(), .set('Authorization', userToken)
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: { getTrialStatus: null },
};
expect(response.body).toEqual(expectedResponsePayload);
});
});
describe('and in trial period', () => {
beforeEach(async () => {
vi.spyOn(User.prototype, 'inTrial').mockResolvedValue(true);
});
it('should return null', async () => {
const response = await request(app)
.post('/graphql')
.set('Authorization', userToken)
.send({ query })
.expect(200);
const expectedResponsePayload = {
data: {
getTrialStatus: {
expireAt: new Date(user.trialExpiryDate).getTime().toString(),
},
}, },
}, };
};
expect(response.body).toEqual(expectedResponsePayload); expect(response.body).toEqual(expectedResponsePayload);
});
}); });
}); });
}); });

View File

@@ -8,12 +8,37 @@ import { createPermission } from '../../../test/factories/permission';
import { createUser } from '../../../test/factories/user'; import { createUser } from '../../../test/factories/user';
describe('graphQL getUser query', () => { describe('graphQL getUser query', () => {
describe('and without permissions', () => { describe('with unauthenticated user', () => {
it('should throw not authorized error', async () => { it('should throw not authorized error', async () => {
const userWithoutPermissions = await createUser(); const invalidUserId = '123123123';
const anotherUser = await createUser();
const query = ` const query = `
query {
getUser(id: "${invalidUserId}") {
id
email
}
}
`;
const response = await request(app)
.post('/graphql')
.set('Authorization', 'invalid-token')
.send({ query })
.expect(200);
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not Authorised!');
});
});
describe('with authenticated user', () => {
describe('and without permissions', () => {
it('should throw not authorized error', async () => {
const userWithoutPermissions = await createUser();
const anotherUser = await createUser();
const query = `
query { query {
getUser(id: "${anotherUser.id}") { getUser(id: "${anotherUser.id}") {
id id
@@ -22,48 +47,50 @@ describe('graphQL getUser query', () => {
} }
`; `;
const token = createAuthTokenByUserId(userWithoutPermissions.id); const token = createAuthTokenByUserId(userWithoutPermissions.id);
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', token) .set('Authorization', token)
.send({ query }) .send({ query })
.expect(200); .expect(200);
expect(response.body.errors).toBeDefined(); expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not authorized!'); expect(response.body.errors[0].message).toEqual('Not authorized!');
});
});
describe('and correct permissions', () => {
let role, currentUser, anotherUser, token, requestObject;
beforeEach(async () => {
role = await createRole({
key: 'sample',
name: 'sample',
}); });
await createPermission({
action: 'read',
subject: 'User',
roleId: role.id,
});
currentUser = await createUser({
roleId: role.id,
});
anotherUser = await createUser({
roleId: role.id,
});
token = createAuthTokenByUserId(currentUser.id);
requestObject = request(app).post('/graphql').set('Authorization', token);
}); });
it('should return user data for a valid user id', async () => { describe('and correct permissions', () => {
const query = ` let role, currentUser, anotherUser, token, requestObject;
beforeEach(async () => {
role = await createRole({
key: 'sample',
name: 'sample',
});
await createPermission({
action: 'read',
subject: 'User',
roleId: role.id,
});
currentUser = await createUser({
roleId: role.id,
});
anotherUser = await createUser({
roleId: role.id,
});
token = createAuthTokenByUserId(currentUser.id);
requestObject = request(app)
.post('/graphql')
.set('Authorization', token);
});
it('should return user data for a valid user id', async () => {
const query = `
query { query {
getUser(id: "${anotherUser.id}") { getUser(id: "${anotherUser.id}") {
id id
@@ -80,26 +107,26 @@ describe('graphQL getUser query', () => {
} }
`; `;
const response = await requestObject.send({ query }).expect(200); const response = await requestObject.send({ query }).expect(200);
const expectedResponsePayload = { const expectedResponsePayload = {
data: { data: {
getUser: { getUser: {
createdAt: anotherUser.createdAt.getTime().toString(), createdAt: anotherUser.createdAt.getTime().toString(),
email: anotherUser.email, email: anotherUser.email,
fullName: anotherUser.fullName, fullName: anotherUser.fullName,
id: anotherUser.id, id: anotherUser.id,
role: { id: role.id, name: role.name }, role: { id: role.id, name: role.name },
updatedAt: anotherUser.updatedAt.getTime().toString(), updatedAt: anotherUser.updatedAt.getTime().toString(),
},
}, },
}, };
};
expect(response.body).toEqual(expectedResponsePayload); expect(response.body).toEqual(expectedResponsePayload);
}); });
it('should not return user password for a valid user id', async () => { it('should not return user password for a valid user id', async () => {
const query = ` const query = `
query { query {
getUser(id: "${anotherUser.id}") { getUser(id: "${anotherUser.id}") {
id id
@@ -109,18 +136,18 @@ describe('graphQL getUser query', () => {
} }
`; `;
const response = await requestObject.send({ query }).expect(400); const response = await requestObject.send({ query }).expect(400);
expect(response.body.errors).toBeDefined(); expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual( expect(response.body.errors[0].message).toEqual(
'Cannot query field "password" on type "User".' 'Cannot query field "password" on type "User".'
); );
}); });
it('should return not found for invalid user id', async () => { it('should return not found for invalid user id', async () => {
const invalidUserId = Crypto.randomUUID(); const invalidUserId = Crypto.randomUUID();
const query = ` const query = `
query { query {
getUser(id: "${invalidUserId}") { getUser(id: "${invalidUserId}") {
id id
@@ -137,10 +164,11 @@ describe('graphQL getUser query', () => {
} }
`; `;
const response = await requestObject.send({ query }).expect(200); const response = await requestObject.send({ query }).expect(200);
expect(response.body.errors).toBeDefined(); expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('NotFoundError'); expect(response.body.errors[0].message).toEqual('NotFoundError');
});
}); });
}); });
}); });

View File

@@ -30,95 +30,111 @@ describe('graphQL getUsers query', () => {
} }
`; `;
describe('and without permissions', () => { describe('with unauthenticated user', () => {
it('should throw not authorized error', async () => { it('should throw not authorized error', async () => {
const userWithoutPermissions = await createUser();
const token = createAuthTokenByUserId(userWithoutPermissions.id);
const response = await request(app) const response = await request(app)
.post('/graphql') .post('/graphql')
.set('Authorization', token) .set('Authorization', 'invalid-token')
.send({ query }) .send({ query })
.expect(200); .expect(200);
expect(response.body.errors).toBeDefined(); expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not authorized!'); expect(response.body.errors[0].message).toEqual('Not Authorised!');
}); });
}); });
describe('and with correct permissions', () => { describe('with authenticated user', () => {
let role, currentUser, anotherUser, token, requestObject; describe('and without permissions', () => {
it('should throw not authorized error', async () => {
const userWithoutPermissions = await createUser();
const token = createAuthTokenByUserId(userWithoutPermissions.id);
beforeEach(async () => { const response = await request(app)
role = await createRole({ .post('/graphql')
key: 'sample', .set('Authorization', token)
name: 'sample', .send({ query })
.expect(200);
expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual('Not authorized!');
}); });
await createPermission({
action: 'read',
subject: 'User',
roleId: role.id,
});
currentUser = await createUser({
roleId: role.id,
fullName: 'Current User',
});
anotherUser = await createUser({
roleId: role.id,
fullName: 'Another User',
});
token = createAuthTokenByUserId(currentUser.id);
requestObject = request(app).post('/graphql').set('Authorization', token);
}); });
it('should return users data', async () => { describe('and with correct permissions', () => {
const response = await requestObject.send({ query }).expect(200); let role, currentUser, anotherUser, token, requestObject;
const expectedResponsePayload = { beforeEach(async () => {
data: { role = await createRole({
getUsers: { key: 'sample',
edges: [ name: 'sample',
{ });
node: {
email: anotherUser.email, await createPermission({
fullName: anotherUser.fullName, action: 'read',
id: anotherUser.id, subject: 'User',
role: { roleId: role.id,
id: role.id, });
name: role.name,
currentUser = await createUser({
roleId: role.id,
fullName: 'Current User',
});
anotherUser = await createUser({
roleId: role.id,
fullName: 'Another User',
});
token = createAuthTokenByUserId(currentUser.id);
requestObject = request(app)
.post('/graphql')
.set('Authorization', token);
});
it('should return users data', async () => {
const response = await requestObject.send({ query }).expect(200);
const expectedResponsePayload = {
data: {
getUsers: {
edges: [
{
node: {
email: anotherUser.email,
fullName: anotherUser.fullName,
id: anotherUser.id,
role: {
id: role.id,
name: role.name,
},
}, },
}, },
}, {
{ node: {
node: { email: currentUser.email,
email: currentUser.email, fullName: currentUser.fullName,
fullName: currentUser.fullName, id: currentUser.id,
id: currentUser.id, role: {
role: { id: role.id,
id: role.id, name: role.name,
name: role.name, },
}, },
}, },
],
pageInfo: {
currentPage: 1,
totalPages: 1,
}, },
], totalCount: 2,
pageInfo: {
currentPage: 1,
totalPages: 1,
}, },
totalCount: 2,
}, },
}, };
};
expect(response.body).toEqual(expectedResponsePayload); expect(response.body).toEqual(expectedResponsePayload);
}); });
it('should not return users data with password', async () => { it('should not return users data with password', async () => {
const query = ` const query = `
query { query {
getUsers(limit: 10, offset: 0) { getUsers(limit: 10, offset: 0) {
pageInfo { pageInfo {
@@ -137,12 +153,13 @@ describe('graphQL getUsers query', () => {
} }
`; `;
const response = await requestObject.send({ query }).expect(400); const response = await requestObject.send({ query }).expect(400);
expect(response.body.errors).toBeDefined(); expect(response.body.errors).toBeDefined();
expect(response.body.errors[0].message).toEqual( expect(response.body.errors[0].message).toEqual(
'Cannot query field "password" on type "User".' 'Cannot query field "password" on type "User".'
); );
});
}); });
}); });
}); });

View File

@@ -3,7 +3,7 @@ import jwt from 'jsonwebtoken';
import appConfig from '../config/app.js'; import appConfig from '../config/app.js';
import User from '../models/user.js'; import User from '../models/user.js';
export const isAuthenticated = async (_parent, _args, req) => { const isAuthenticated = rule()(async (_parent, _args, req) => {
const token = req.headers['authorization']; const token = req.headers['authorization'];
if (token == null) return false; if (token == null) return false;
@@ -20,47 +20,35 @@ export const isAuthenticated = async (_parent, _args, req) => {
.withGraphFetched({ .withGraphFetched({
role: true, role: true,
permissions: true, permissions: true,
}) });
.throwIfNotFound();
return true; return true;
} catch (error) { } catch (error) {
return false; return false;
} }
}; });
export const authenticateUser = async (request, response, next) => { const authentication = shield(
if (await isAuthenticated(null, null, request)) { {
next(); Query: {
} else { '*': isAuthenticated,
return response.status(401).end(); getAutomatischInfo: allow,
getConfig: allow,
getNotifications: allow,
healthcheck: allow,
listSamlAuthProviders: allow,
},
Mutation: {
'*': isAuthenticated,
forgotPassword: allow,
login: allow,
registerUser: allow,
resetPassword: allow,
},
},
{
allowExternalErrors: true,
} }
}; );
const isAuthenticatedRule = rule()(isAuthenticated);
export const authenticationRules = {
Query: {
'*': isAuthenticatedRule,
getAutomatischInfo: allow,
getConfig: allow,
getNotifications: allow,
healthcheck: allow,
listSamlAuthProviders: allow,
},
Mutation: {
'*': isAuthenticatedRule,
forgotPassword: allow,
login: allow,
registerUser: allow,
resetPassword: allow,
},
};
const authenticationOptions = {
allowExternalErrors: true,
};
const authentication = shield(authenticationRules, authenticationOptions);
export default authentication; export default authentication;

View File

@@ -1,72 +0,0 @@
import { describe, it, expect } from 'vitest';
import { allow } from 'graphql-shield';
import { isAuthenticated, authenticationRules } from './authentication.js';
import { createUser } from '../../test/factories/user.js';
import createAuthTokenByUserId from '../helpers/create-auth-token-by-user-id.js';
describe('isAuthenticated', () => {
it('should return false if no token is provided', async () => {
const req = { headers: {} };
expect(await isAuthenticated(null, null, req)).toBe(false);
});
it('should return false if token is invalid', async () => {
const req = { headers: { authorization: 'invalidToken' } };
expect(await isAuthenticated(null, null, req)).toBe(false);
});
it('should return true if token is valid and there is a user', async () => {
const user = await createUser();
const token = createAuthTokenByUserId(user.id);
const req = { headers: { authorization: token } };
expect(await isAuthenticated(null, null, req)).toBe(true);
});
it('should return false if token is valid and but there is no user', async () => {
const user = await createUser();
const token = createAuthTokenByUserId(user.id);
await user.$query().delete();
const req = { headers: { authorization: token } };
expect(await isAuthenticated(null, null, req)).toBe(false);
});
});
describe('authentication rules', () => {
const getQueryAndMutationNames = (rules) => {
const queries = Object.keys(rules.Query || {});
const mutations = Object.keys(rules.Mutation || {});
return { queries, mutations };
};
const { queries, mutations } = getQueryAndMutationNames(authenticationRules);
describe('for queries', () => {
queries.forEach((query) => {
it(`should apply correct rule for query: ${query}`, () => {
const ruleApplied = authenticationRules.Query[query];
if (query === '*') {
expect(ruleApplied.func).toBe(isAuthenticated);
} else {
expect(ruleApplied).toEqual(allow);
}
});
});
});
describe('for mutations', () => {
mutations.forEach((mutation) => {
it(`should apply correct rule for mutation: ${mutation}`, () => {
const ruleApplied = authenticationRules.Mutation[mutation];
if (mutation === '*') {
expect(ruleApplied.func).toBe(isAuthenticated);
} else {
expect(ruleApplied).toBe(allow);
}
});
});
});
});

View File

@@ -1,33 +0,0 @@
const authorizationList = {
'GET /api/v1/users/:userId': {
action: 'read',
subject: 'User',
},
'GET /api/v1/users/': {
action: 'read',
subject: 'User',
},
};
export const authorizeUser = async (request, response, next) => {
const currentRoute =
request.method + ' ' + request.baseUrl + request.route.path;
const currentRouteRule = authorizationList[currentRoute];
try {
request.currentUser.can(currentRouteRule.action, currentRouteRule.subject);
next();
} catch (error) {
return response.status(403).end();
}
};
export const authorizeAdmin = async (request, response, next) => {
const role = await request.currentUser.$relatedQuery('role');
if (role?.isAdmin) {
next();
} else {
return response.status(403).end();
}
};

View File

@@ -1,11 +0,0 @@
import appConfig from '../config/app.js';
export const checkIsCloud = async (request, response, next) => {
if (appConfig.isCloud) {
next();
} else {
return response.status(404).end();
}
};
export default checkIsCloud;

View File

@@ -1,9 +0,0 @@
import { hasValidLicense } from './license.ee.js';
export const checkIsEnterprise = async (request, response, next) => {
if (await hasValidLicense()) {
next();
} else {
return response.status(404).end();
}
};

View File

@@ -1,9 +1,6 @@
import path from 'path'; import * as path from 'path';
import fs from 'fs'; import * as fs from 'fs';
import handlebars from 'handlebars'; import * as handlebars from 'handlebars';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const compileEmail = (emailPath, replacements = {}) => { const compileEmail = (emailPath, replacements = {}) => {
const filePath = path.join(__dirname, `../views/emails/${emailPath}.ee.hbs`); const filePath = path.join(__dirname, `../views/emails/${emailPath}.ee.hbs`);

View File

@@ -4,8 +4,8 @@ import appConfig from '../config/app.js';
const levels = { const levels = {
error: 0, error: 0,
warn: 1, warn: 1,
http: 2, info: 2,
info: 3, http: 3,
debug: 4, debug: 4,
}; };

View File

@@ -1,25 +0,0 @@
const paginateRest = async (query, page) => {
const pageSize = 10;
page = parseInt(page, 10);
if (isNaN(page) || page < 1) {
page = 1;
}
const [records, count] = await Promise.all([
query.limit(pageSize).offset((page - 1) * pageSize),
query.resultSize(),
]);
return {
pageInfo: {
currentPage: page,
totalPages: Math.ceil(count / pageSize),
},
totalCount: count,
records,
};
};
export default paginateRest;

View File

@@ -1,44 +0,0 @@
import serializers from '../serializers/index.js';
const isPaginated = (object) =>
object?.pageInfo &&
object?.totalCount !== undefined &&
Array.isArray(object?.records);
const isArray = (object) =>
Array.isArray(object) || Array.isArray(object?.records);
const totalCount = (object) =>
isPaginated(object) ? object.totalCount : isArray(object) ? object.length : 1;
const renderObject = (response, object) => {
let data = isPaginated(object) ? object.records : object;
const type = isPaginated(object)
? object.records[0].constructor.name
: Array.isArray(object)
? object[0].constructor.name
: object.constructor.name;
const serializer = serializers[type];
if (serializer) {
data = Array.isArray(data)
? data.map((item) => serializer(item))
: serializer(data);
}
const computedPayload = {
data,
meta: {
type,
count: totalCount(object),
isArray: isArray(object),
currentPage: isPaginated(object) ? object.pageInfo.currentPage : null,
totalPages: isPaginated(object) ? object.pageInfo.totalPages : null,
},
};
return response.json(computedPayload);
};
export { renderObject };

View File

@@ -15,7 +15,7 @@ const webUIHandler = async (app) => {
app.use(express.static(webBuildPath)); app.use(express.static(webBuildPath));
app.get('*', (_req, res) => { app.get('*', (_req, res) => {
res.set('Content-Security-Policy', 'frame-ancestors \'none\';'); res.set('Content-Security-Policy', 'frame-ancestors: none;');
res.set('X-Frame-Options', 'DENY'); res.set('X-Frame-Options', 'DENY');
res.sendFile(indexHtml); res.sendFile(indexHtml);

View File

@@ -1,8 +0,0 @@
import { Router } from 'express';
import versionAction from '../../../controllers/api/v1/automatisch/version.js';
const router = Router();
router.get('/version', versionAction);
export default router;

View File

@@ -1,26 +0,0 @@
import { Router } from 'express';
import { authenticateUser } from '../../../helpers/authentication.js';
import { authorizeAdmin } from '../../../helpers/authorization.js';
import { checkIsEnterprise } from '../../../helpers/check-is-enterprise.js';
import getSamlAuthProvidersAction from '../../../controllers/api/v1/admin/saml-auth-providers/get-saml-auth-providers.ee.js';
import getSamlAuthProviderAction from '../../../controllers/api/v1/admin/saml-auth-providers/get-saml-auth-provider.ee.js';
const router = Router();
router.get(
'/',
authenticateUser,
authorizeAdmin,
checkIsEnterprise,
getSamlAuthProvidersAction
);
router.get(
'/:samlAuthProviderId',
authenticateUser,
authorizeAdmin,
checkIsEnterprise,
getSamlAuthProviderAction
);
export default router;

View File

@@ -1,22 +0,0 @@
import { Router } from 'express';
import { authenticateUser } from '../../../helpers/authentication.js';
import { authorizeUser } from '../../../helpers/authorization.js';
import checkIsCloud from '../../../helpers/check-is-cloud.js';
import getCurrentUserAction from '../../../controllers/api/v1/users/get-current-user.js';
import getUserAction from '../../../controllers/api/v1/users/get-user.js';
import getUsersAction from '../../../controllers/api/v1/users/get-users.js';
import getUserTrialAction from '../../../controllers/api/v1/users/get-user-trial.ee.js';
const router = Router();
router.get('/', authenticateUser, authorizeUser, getUsersAction);
router.get('/me', authenticateUser, getCurrentUserAction);
router.get('/:userId', authenticateUser, authorizeUser, getUserAction);
router.get(
'/:userId/trial',
authenticateUser,
checkIsCloud,
getUserTrialAction
);
export default router;

View File

@@ -1,8 +0,0 @@
import { Router } from 'express';
import indexAction from '../controllers/healthcheck/index.js';
const router = Router();
router.get('/', indexAction);
export default router;

View File

@@ -2,19 +2,11 @@ import { Router } from 'express';
import graphQLInstance from '../helpers/graphql-instance.js'; import graphQLInstance from '../helpers/graphql-instance.js';
import webhooksRouter from './webhooks.js'; import webhooksRouter from './webhooks.js';
import paddleRouter from './paddle.ee.js'; import paddleRouter from './paddle.ee.js';
import healthcheckRouter from './healthcheck.js';
import automatischRouter from './api/v1/automatisch.js';
import usersRouter from './api/v1/users.js';
import samlAuthProvidersRouter from './api/v1/saml-auth-providers.ee.js';
const router = Router(); const router = Router();
router.use('/graphql', graphQLInstance); router.use('/graphql', graphQLInstance);
router.use('/webhooks', webhooksRouter); router.use('/webhooks', webhooksRouter);
router.use('/paddle', paddleRouter); router.use('/paddle', paddleRouter);
router.use('/healthcheck', healthcheckRouter);
router.use('/api/v1/automatisch', automatischRouter);
router.use('/api/v1/users', usersRouter);
router.use('/api/v1/admin/saml-auth-providers', samlAuthProvidersRouter);
export default router; export default router;

View File

@@ -1,13 +0,0 @@
import userSerializer from './user.js';
import roleSerializer from './role.js';
import permissionSerializer from './permission.js';
import samlAuthProviderSerializer from './saml-auth-provider.ee.js';
const serializers = {
User: userSerializer,
Role: roleSerializer,
Permission: permissionSerializer,
SamlAuthProvider: samlAuthProviderSerializer,
};
export default serializers;

View File

@@ -1,13 +0,0 @@
const permissionSerializer = (permission) => {
return {
id: permission.id,
roleId: permission.roleId,
action: permission.action,
subject: permission.subject,
conditions: permission.conditions,
createdAt: permission.createdAt,
updatedAt: permission.updatedAt,
};
};
export default permissionSerializer;

View File

@@ -1,25 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { createPermission } from '../../test/factories/permission';
import permissionSerializer from './permission';
describe('permissionSerializer', () => {
let permission;
beforeEach(async () => {
permission = await createPermission();
});
it('should return permission data', async () => {
const expectedPayload = {
id: permission.id,
roleId: permission.roleId,
action: permission.action,
subject: permission.subject,
conditions: permission.conditions,
createdAt: permission.createdAt,
updatedAt: permission.updatedAt,
};
expect(permissionSerializer(permission)).toEqual(expectedPayload);
});
});

View File

@@ -1,13 +0,0 @@
const roleSerializer = (role) => {
return {
id: role.id,
name: role.name,
key: role.key,
description: role.description,
createdAt: role.createdAt,
updatedAt: role.updatedAt,
isAdmin: role.isAdmin,
};
};
export default roleSerializer;

View File

@@ -1,25 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { createRole } from '../../test/factories/role';
import roleSerializer from './role';
describe('roleSerializer', () => {
let role;
beforeEach(async () => {
role = await createRole();
});
it('should return role data', async () => {
const expectedPayload = {
id: role.id,
name: role.name,
key: role.key,
description: role.description,
createdAt: role.createdAt,
updatedAt: role.updatedAt,
isAdmin: role.isAdmin,
};
expect(roleSerializer(role)).toEqual(expectedPayload);
});
});

View File

@@ -1,18 +0,0 @@
const samlAuthProviderSerializer = (samlAuthProvider) => {
return {
id: samlAuthProvider.id,
name: samlAuthProvider.name,
certificate: samlAuthProvider.certificate,
signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
issuer: samlAuthProvider.issuer,
entryPoint: samlAuthProvider.entryPoint,
firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
surnameAttributeName: samlAuthProvider.surnameAttributeName,
emailAttributeName: samlAuthProvider.emailAttributeName,
roleAttributeName: samlAuthProvider.roleAttributeName,
active: samlAuthProvider.active,
defaultRoleId: samlAuthProvider.defaultRoleId,
};
};
export default samlAuthProviderSerializer;

View File

@@ -1,32 +0,0 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { createSamlAuthProvider } from '../../test/factories/saml-auth-provider.ee.js';
import samlAuthProviderSerializer from './saml-auth-provider.ee.js';
describe('samlAuthProviderSerializer', () => {
let samlAuthProvider;
beforeEach(async () => {
samlAuthProvider = await createSamlAuthProvider();
});
it('should return saml auth provider data', async () => {
const expectedPayload = {
id: samlAuthProvider.id,
name: samlAuthProvider.name,
certificate: samlAuthProvider.certificate,
signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
issuer: samlAuthProvider.issuer,
entryPoint: samlAuthProvider.entryPoint,
firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
surnameAttributeName: samlAuthProvider.surnameAttributeName,
emailAttributeName: samlAuthProvider.emailAttributeName,
roleAttributeName: samlAuthProvider.roleAttributeName,
active: samlAuthProvider.active,
defaultRoleId: samlAuthProvider.defaultRoleId,
};
expect(samlAuthProviderSerializer(samlAuthProvider)).toEqual(
expectedPayload
);
});
});

View File

@@ -1,32 +0,0 @@
import roleSerializer from './role.js';
import permissionSerializer from './permission.js';
import appConfig from '../config/app.js';
const userSerializer = (user) => {
let userData = {
id: user.id,
email: user.email,
createdAt: user.createdAt,
updatedAt: user.updatedAt,
fullName: user.fullName,
roleId: user.roleId,
};
if (user.role) {
userData.role = roleSerializer(user.role);
}
if (user.permissions) {
userData.permissions = user.permissions.map((permission) =>
permissionSerializer(permission)
);
}
if (appConfig.isCloud && user.trialExpiryDate) {
userData.trialExpiryDate = user.trialExpiryDate;
}
return userData;
};
export default userSerializer;

View File

@@ -1,76 +0,0 @@
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { DateTime } from 'luxon';
import appConfig from '../config/app';
import { createUser } from '../../test/factories/user';
import { createPermission } from '../../test/factories/permission';
import userSerializer from './user';
describe('userSerializer', () => {
let user, role, permissionOne, permissionTwo;
beforeEach(async () => {
user = await createUser();
role = await user.$relatedQuery('role');
permissionOne = await createPermission({
roleId: role.id,
action: 'read',
subject: 'User',
});
permissionTwo = await createPermission({
roleId: role.id,
action: 'read',
subject: 'Role',
});
});
it('should return user data', async () => {
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(false);
const expectedPayload = {
createdAt: user.createdAt,
email: user.email,
fullName: user.fullName,
id: user.id,
roleId: user.roleId,
updatedAt: user.updatedAt,
};
expect(userSerializer(user)).toEqual(expectedPayload);
});
it('should return user data with the role', async () => {
user.role = role;
const expectedPayload = {
role,
};
expect(userSerializer(user)).toMatchObject(expectedPayload);
});
it('should return user data with the permissions', async () => {
user.permissions = [permissionOne, permissionTwo];
const expectedPayload = {
permissions: [permissionOne, permissionTwo],
};
expect(userSerializer(user)).toMatchObject(expectedPayload);
});
it('should return user data with trial expiry date', async () => {
vi.spyOn(appConfig, 'isCloud', 'get').mockReturnValue(true);
await user.$query().patch({
trialExpiryDate: DateTime.now().plus({ days: 30 }).toISODate(),
});
const expectedPayload = {
trialExpiryDate: user.trialExpiryDate,
};
expect(userSerializer(user)).toMatchObject(expectedPayload);
});
});

View File

@@ -1,5 +1,4 @@
import { faker } from '@faker-js/faker'; import { faker } from '@faker-js/faker';
import Config from '../../src/models/config';
export const createConfig = async (params = {}) => { export const createConfig = async (params = {}) => {
const configData = { const configData = {
@@ -7,7 +6,10 @@ export const createConfig = async (params = {}) => {
value: params?.value || { data: 'sampleConfig' }, value: params?.value || { data: 'sampleConfig' },
}; };
const config = await Config.query().insert(configData).returning('*'); const [config] = await global.knex
.table('config')
.insert(configData)
.returning('*');
return config; return config;
}; };

View File

@@ -1,6 +1,5 @@
import appConfig from '../../src/config/app'; import appConfig from '../../src/config/app';
import { AES } from 'crypto-js'; import { AES } from 'crypto-js';
import Connection from '../../src/models/connection';
export const createConnection = async (params = {}) => { export const createConnection = async (params = {}) => {
params.key = params?.key || 'deepl'; params.key = params?.key || 'deepl';
@@ -17,7 +16,10 @@ export const createConnection = async (params = {}) => {
appConfig.encryptionKey appConfig.encryptionKey
).toString(); ).toString();
const connection = await Connection.query().insert(params).returning('*'); const [connection] = await global.knex
.table('connections')
.insert(params)
.returning('*');
return connection; return connection;
}; };

View File

@@ -1,4 +1,3 @@
import ExecutionStep from '../../src/models/execution-step';
import { createExecution } from './execution'; import { createExecution } from './execution';
import { createStep } from './step'; import { createStep } from './step';
@@ -9,7 +8,8 @@ export const createExecutionStep = async (params = {}) => {
params.dataIn = params?.dataIn || { dataIn: 'dataIn' }; params.dataIn = params?.dataIn || { dataIn: 'dataIn' };
params.dataOut = params?.dataOut || { dataOut: 'dataOut' }; params.dataOut = params?.dataOut || { dataOut: 'dataOut' };
const executionStep = await ExecutionStep.query() const [executionStep] = await global.knex
.table('executionSteps')
.insert(params) .insert(params)
.returning('*'); .returning('*');

View File

@@ -1,4 +1,3 @@
import Execution from '../../src/models/execution';
import { createFlow } from './flow'; import { createFlow } from './flow';
export const createExecution = async (params = {}) => { export const createExecution = async (params = {}) => {
@@ -7,7 +6,10 @@ export const createExecution = async (params = {}) => {
params.createdAt = params?.createdAt || new Date().toISOString(); params.createdAt = params?.createdAt || new Date().toISOString();
params.updatedAt = params?.updatedAt || new Date().toISOString(); params.updatedAt = params?.updatedAt || new Date().toISOString();
const execution = await Execution.query().insert(params).returning('*'); const [execution] = await global.knex
.table('executions')
.insert(params)
.returning('*');
return execution; return execution;
}; };

View File

@@ -1,4 +1,3 @@
import Flow from '../../src/models/flow';
import { createUser } from './user'; import { createUser } from './user';
export const createFlow = async (params = {}) => { export const createFlow = async (params = {}) => {
@@ -7,7 +6,7 @@ export const createFlow = async (params = {}) => {
params.createdAt = params?.createdAt || new Date().toISOString(); params.createdAt = params?.createdAt || new Date().toISOString();
params.updatedAt = params?.updatedAt || new Date().toISOString(); params.updatedAt = params?.updatedAt || new Date().toISOString();
const flow = await Flow.query().insert(params).returning('*'); const [flow] = await global.knex.table('flows').insert(params).returning('*');
return flow; return flow;
}; };

View File

@@ -1,4 +1,3 @@
import Permission from '../../src/models/permission';
import { createRole } from './role'; import { createRole } from './role';
export const createPermission = async (params = {}) => { export const createPermission = async (params = {}) => {
@@ -7,7 +6,10 @@ export const createPermission = async (params = {}) => {
params.subject = params?.subject || 'User'; params.subject = params?.subject || 'User';
params.conditions = params?.conditions || ['isCreator']; params.conditions = params?.conditions || ['isCreator'];
const permission = await Permission.query().insert(params).returning('*'); const [permission] = await global.knex
.table('permissions')
.insert(params)
.returning('*');
return permission; return permission;
}; };

View File

@@ -1,10 +1,8 @@
import Role from '../../src/models/role';
export const createRole = async (params = {}) => { export const createRole = async (params = {}) => {
params.name = params?.name || 'Viewer'; params.name = params?.name || 'Viewer';
params.key = params?.key || 'viewer'; params.key = params?.key || 'viewer';
const role = await Role.query().insert(params).returning('*'); const [role] = await global.knex.table('roles').insert(params).returning('*');
return role; return role;
}; };

View File

@@ -1,33 +0,0 @@
import { createRole } from './role';
import SamlAuthProvider from '../../src/models/saml-auth-provider.ee.js';
export const createSamlAuthProvider = async (params = {}) => {
params.name = params?.name || 'Keycloak SAML';
params.certificate = params?.certificate || 'certificate';
params.signatureAlgorithm = params?.signatureAlgorithm || 'sha512';
params.entryPoint =
params?.entryPoint ||
'https://example.com/auth/realms/automatisch/protocol/saml';
params.issuer = params?.issuer || 'automatisch-client';
params.firstnameAttributeName =
params?.firstnameAttributeName || 'urn:oid:2.1.1.42';
params.surnameAttributeName =
params?.surnameAttributeName || 'urn:oid:2.1.1.4';
params.emailAttributeName =
params?.emailAttributeName || 'urn:oid:1.1.2342.19200300.100.1.1';
params.roleAttributeName = params?.roleAttributeName || 'Role';
params.defaultRoleId = params?.defaultRoleId || (await createRole()).id;
params.active = params?.active || true;
const samlAuthProvider = await SamlAuthProvider.query()
.insert(params)
.returning('*');
return samlAuthProvider;
};

View File

@@ -1,4 +1,3 @@
import Step from '../../src/models/step';
import { createFlow } from './flow'; import { createFlow } from './flow';
export const createStep = async (params = {}) => { export const createStep = async (params = {}) => {
@@ -17,7 +16,7 @@ export const createStep = async (params = {}) => {
params.appKey = params.appKey =
params?.appKey || (params.type === 'action' ? 'deepl' : 'webhook'); params?.appKey || (params.type === 'action' ? 'deepl' : 'webhook');
const step = await Step.query().insert(params).returning('*'); const [step] = await global.knex.table('steps').insert(params).returning('*');
return step; return step;
}; };

View File

@@ -1,6 +1,5 @@
import { createRole } from './role'; import { createRole } from './role';
import { faker } from '@faker-js/faker'; import { faker } from '@faker-js/faker';
import User from '../../src/models/user';
export const createUser = async (params = {}) => { export const createUser = async (params = {}) => {
params.roleId = params?.roleId || (await createRole()).id; params.roleId = params?.roleId || (await createRole()).id;
@@ -8,7 +7,7 @@ export const createUser = async (params = {}) => {
params.email = params?.email || faker.internet.email(); params.email = params?.email || faker.internet.email();
params.password = params?.password || faker.internet.password(); params.password = params?.password || faker.internet.password();
const user = await User.query().insert(params).returning('*'); const [user] = await global.knex.table('users').insert(params).returning('*');
return user; return user;
}; };

View File

@@ -1,29 +0,0 @@
const getSamlAuthProvidersMock = async (samlAuthProvider) => {
const data = {
active: samlAuthProvider.active,
certificate: samlAuthProvider.certificate,
defaultRoleId: samlAuthProvider.defaultRoleId,
emailAttributeName: samlAuthProvider.emailAttributeName,
entryPoint: samlAuthProvider.entryPoint,
firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
id: samlAuthProvider.id,
issuer: samlAuthProvider.issuer,
name: samlAuthProvider.name,
roleAttributeName: samlAuthProvider.roleAttributeName,
signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
surnameAttributeName: samlAuthProvider.surnameAttributeName,
};
return {
data: data,
meta: {
count: 1,
currentPage: null,
isArray: false,
totalPages: null,
type: 'SamlAuthProvider',
},
};
};
export default getSamlAuthProvidersMock;

View File

@@ -1,31 +0,0 @@
const getSamlAuthProvidersMock = async (samlAuthProviders) => {
const data = samlAuthProviders.map((samlAuthProvider) => {
return {
active: samlAuthProvider.active,
certificate: samlAuthProvider.certificate,
defaultRoleId: samlAuthProvider.defaultRoleId,
emailAttributeName: samlAuthProvider.emailAttributeName,
entryPoint: samlAuthProvider.entryPoint,
firstnameAttributeName: samlAuthProvider.firstnameAttributeName,
id: samlAuthProvider.id,
issuer: samlAuthProvider.issuer,
name: samlAuthProvider.name,
roleAttributeName: samlAuthProvider.roleAttributeName,
signatureAlgorithm: samlAuthProvider.signatureAlgorithm,
surnameAttributeName: samlAuthProvider.surnameAttributeName,
};
});
return {
data: data,
meta: {
count: data.length,
currentPage: null,
isArray: true,
totalPages: null,
type: 'SamlAuthProvider',
},
};
};
export default getSamlAuthProvidersMock;

View File

@@ -1,32 +0,0 @@
const getCurrentUserMock = (currentUser, role) => {
return {
data: {
createdAt: currentUser.createdAt.toISOString(),
email: currentUser.email,
fullName: currentUser.fullName,
id: currentUser.id,
permissions: [],
role: {
createdAt: role.createdAt.toISOString(),
description: null,
id: role.id,
isAdmin: role.isAdmin,
key: role.key,
name: role.name,
updatedAt: role.updatedAt.toISOString(),
},
roleId: role.id,
trialExpiryDate: currentUser.trialExpiryDate.toISOString(),
updatedAt: currentUser.updatedAt.toISOString(),
},
meta: {
count: 1,
currentPage: null,
isArray: false,
totalPages: null,
type: 'User',
},
};
};
export default getCurrentUserMock;

View File

@@ -1,17 +0,0 @@
const getUserTrialMock = async (currentUser) => {
return {
data: {
inTrial: await currentUser.inTrial(),
expireAt: currentUser.trialExpiryDate.toISOString(),
},
meta: {
count: 1,
currentPage: null,
isArray: false,
totalPages: null,
type: 'Object',
},
};
};
export default getUserTrialMock;

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