mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-24 20:24:05 -08:00
refactor(core): Post-release refactorings of Public API (#3495)
* ⚡ Post-release refactorings * 🧪 Add `--forceExit` * 🛠 typing refactor (#3486) * 🐛 Fix middleware arguments * 👕 Fix lint * ⚡ Restore commented out block Co-authored-by: Ben Hesseldieck <1849459+BHesseldieck@users.noreply.github.com>
This commit is contained in:
parent
2ebcf4bb91
commit
b8e3bcc052
|
@ -30,7 +30,7 @@
|
||||||
"start:default": "cd bin && ./n8n",
|
"start:default": "cd bin && ./n8n",
|
||||||
"start:windows": "cd bin && n8n",
|
"start:windows": "cd bin && n8n",
|
||||||
"test": "npm run test:sqlite",
|
"test": "npm run test:sqlite",
|
||||||
"test:sqlite": "export N8N_LOG_LEVEL=silent; export DB_TYPE=sqlite; jest",
|
"test:sqlite": "export N8N_LOG_LEVEL=silent; export DB_TYPE=sqlite; jest --forceExit",
|
||||||
"test:postgres": "export N8N_LOG_LEVEL=silent; export DB_TYPE=postgresdb; jest",
|
"test:postgres": "export N8N_LOG_LEVEL=silent; export DB_TYPE=postgresdb; jest",
|
||||||
"test:postgres:alt-schema": "export DB_POSTGRESDB_SCHEMA=alt_schema; npm run test:postgres",
|
"test:postgres:alt-schema": "export DB_POSTGRESDB_SCHEMA=alt_schema; npm run test:postgres",
|
||||||
"test:mysql": "export N8N_LOG_LEVEL=silent; export DB_TYPE=mysqldb; jest",
|
"test:mysql": "export N8N_LOG_LEVEL=silent; export DB_TYPE=mysqldb; jest",
|
||||||
|
|
|
@ -178,8 +178,6 @@ export interface ICredentialsDecryptedResponse extends ICredentialsDecryptedDb {
|
||||||
export type DatabaseType = 'mariadb' | 'postgresdb' | 'mysqldb' | 'sqlite';
|
export type DatabaseType = 'mariadb' | 'postgresdb' | 'mysqldb' | 'sqlite';
|
||||||
export type SaveExecutionDataType = 'all' | 'none';
|
export type SaveExecutionDataType = 'all' | 'none';
|
||||||
|
|
||||||
export type ExecutionDataFieldFormat = 'empty' | 'flattened' | 'json';
|
|
||||||
|
|
||||||
export interface IExecutionBase {
|
export interface IExecutionBase {
|
||||||
id?: number | string;
|
id?: number | string;
|
||||||
mode: WorkflowExecuteMode;
|
mode: WorkflowExecuteMode;
|
||||||
|
@ -240,7 +238,7 @@ export interface IExecutionResponseApi {
|
||||||
finished: boolean;
|
finished: boolean;
|
||||||
retryOf?: number | string;
|
retryOf?: number | string;
|
||||||
retrySuccessId?: number | string;
|
retrySuccessId?: number | string;
|
||||||
data?: string; // Just that we can remove it
|
data?: object;
|
||||||
waitTill?: Date | null;
|
waitTill?: Date | null;
|
||||||
workflowData: IWorkflowBase;
|
workflowData: IWorkflowBase;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,38 +1,38 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
||||||
/* eslint-disable no-restricted-syntax */
|
|
||||||
/* eslint-disable @typescript-eslint/naming-convention */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
|
||||||
/* eslint-disable import/no-cycle */
|
/* eslint-disable import/no-cycle */
|
||||||
import express, { Router } from 'express';
|
import express, { Router } from 'express';
|
||||||
|
import fs from 'fs/promises';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
import * as OpenApiValidator from 'express-openapi-validator';
|
import * as OpenApiValidator from 'express-openapi-validator';
|
||||||
import { HttpError } from 'express-openapi-validator/dist/framework/types';
|
import { HttpError } from 'express-openapi-validator/dist/framework/types';
|
||||||
import fs from 'fs/promises';
|
|
||||||
import { OpenAPIV3 } from 'openapi-types';
|
import { OpenAPIV3 } from 'openapi-types';
|
||||||
import path from 'path';
|
import swaggerUi from 'swagger-ui-express';
|
||||||
import * as swaggerUi from 'swagger-ui-express';
|
|
||||||
import validator from 'validator';
|
import validator from 'validator';
|
||||||
import * as YAML from 'yamljs';
|
import YAML from 'yamljs';
|
||||||
import { Db, InternalHooksManager } from '..';
|
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
|
import { Db, InternalHooksManager } from '..';
|
||||||
import { getInstanceBaseUrl } from '../UserManagement/UserManagementHelper';
|
import { getInstanceBaseUrl } from '../UserManagement/UserManagementHelper';
|
||||||
|
|
||||||
function createApiRouter(
|
function createApiRouter(
|
||||||
version: string,
|
version: string,
|
||||||
openApiSpecPath: string,
|
openApiSpecPath: string,
|
||||||
hanldersDirectory: string,
|
handlersDirectory: string,
|
||||||
swaggerThemeCss: string,
|
swaggerThemeCss: string,
|
||||||
publicApiEndpoint: string,
|
publicApiEndpoint: string,
|
||||||
): Router {
|
): Router {
|
||||||
const n8nPath = config.getEnv('path');
|
const n8nPath = config.getEnv('path');
|
||||||
const swaggerDocument = YAML.load(openApiSpecPath) as swaggerUi.JsonObject;
|
const swaggerDocument = YAML.load(openApiSpecPath) as swaggerUi.JsonObject;
|
||||||
// add the server depeding on the config so the user can interact with the API
|
// add the server depeding on the config so the user can interact with the API
|
||||||
// from the swagger UI
|
// from the Swagger UI
|
||||||
swaggerDocument.server = [
|
swaggerDocument.server = [
|
||||||
{
|
{
|
||||||
url: `${getInstanceBaseUrl()}/${publicApiEndpoint}/${version}}`,
|
url: `${getInstanceBaseUrl()}/${publicApiEndpoint}/${version}}`,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const apiController = express.Router();
|
const apiController = express.Router();
|
||||||
|
|
||||||
apiController.use(
|
apiController.use(
|
||||||
`/${publicApiEndpoint}/${version}/docs`,
|
`/${publicApiEndpoint}/${version}/docs`,
|
||||||
swaggerUi.serveFiles(swaggerDocument),
|
swaggerUi.serveFiles(swaggerDocument),
|
||||||
|
@ -42,12 +42,14 @@ function createApiRouter(
|
||||||
customfavIcon: `${n8nPath}favicon.ico`,
|
customfavIcon: `${n8nPath}favicon.ico`,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
apiController.use(`/${publicApiEndpoint}/${version}`, express.json());
|
apiController.use(`/${publicApiEndpoint}/${version}`, express.json());
|
||||||
|
|
||||||
apiController.use(
|
apiController.use(
|
||||||
`/${publicApiEndpoint}/${version}`,
|
`/${publicApiEndpoint}/${version}`,
|
||||||
OpenApiValidator.middleware({
|
OpenApiValidator.middleware({
|
||||||
apiSpec: openApiSpecPath,
|
apiSpec: openApiSpecPath,
|
||||||
operationHandlers: hanldersDirectory,
|
operationHandlers: handlersDirectory,
|
||||||
validateRequests: true,
|
validateRequests: true,
|
||||||
validateApiSpec: true,
|
validateApiSpec: true,
|
||||||
formats: [
|
formats: [
|
||||||
|
@ -71,16 +73,12 @@ function createApiRouter(
|
||||||
schema: OpenAPIV3.ApiKeySecurityScheme,
|
schema: OpenAPIV3.ApiKeySecurityScheme,
|
||||||
): Promise<boolean> => {
|
): Promise<boolean> => {
|
||||||
const apiKey = req.headers[schema.name.toLowerCase()];
|
const apiKey = req.headers[schema.name.toLowerCase()];
|
||||||
const user = await Db.collections.User?.findOne({
|
const user = await Db.collections.User.findOne({
|
||||||
where: {
|
where: { apiKey },
|
||||||
apiKey,
|
|
||||||
},
|
|
||||||
relations: ['globalRole'],
|
relations: ['globalRole'],
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InternalHooksManager.getInstance().onUserInvokedApi({
|
void InternalHooksManager.getInstance().onUserInvokedApi({
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
|
@ -97,13 +95,20 @@ function createApiRouter(
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
apiController.use(
|
apiController.use(
|
||||||
(error: HttpError, req: express.Request, res: express.Response, next: express.NextFunction) => {
|
(
|
||||||
|
error: HttpError,
|
||||||
|
_req: express.Request,
|
||||||
|
res: express.Response,
|
||||||
|
_next: express.NextFunction,
|
||||||
|
) => {
|
||||||
return res.status(error.status || 400).json({
|
return res.status(error.status || 400).json({
|
||||||
message: error.message,
|
message: error.message,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return apiController;
|
return apiController;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,11 +119,12 @@ export const loadPublicApiVersions = async (
|
||||||
const folders = await fs.readdir(__dirname);
|
const folders = await fs.readdir(__dirname);
|
||||||
const css = (await fs.readFile(swaggerThemePath)).toString();
|
const css = (await fs.readFile(swaggerThemePath)).toString();
|
||||||
const versions = folders.filter((folderName) => folderName.startsWith('v'));
|
const versions = folders.filter((folderName) => folderName.startsWith('v'));
|
||||||
const apiRouters: express.Router[] = [];
|
|
||||||
for (const version of versions) {
|
const apiRouters = versions.map((version) => {
|
||||||
const openApiPath = path.join(__dirname, version, 'openapi.yml');
|
const openApiPath = path.join(__dirname, version, 'openapi.yml');
|
||||||
apiRouters.push(createApiRouter(version, openApiPath, __dirname, css, publicApiEndpoint));
|
return createApiRouter(version, openApiPath, __dirname, css, publicApiEndpoint);
|
||||||
}
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
apiRouters,
|
apiRouters,
|
||||||
apiLatestVersion: Number(versions.pop()?.charAt(1)) ?? 1,
|
apiLatestVersion: Number(versions.pop()?.charAt(1)) ?? 1,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import express = require('express');
|
import express from 'express';
|
||||||
|
|
||||||
import { CredentialsHelper } from '../../../../CredentialsHelper';
|
import { CredentialsHelper } from '../../../../CredentialsHelper';
|
||||||
import { CredentialTypes } from '../../../../CredentialTypes';
|
import { CredentialTypes } from '../../../../CredentialTypes';
|
||||||
|
|
||||||
import { CredentialsEntity } from '../../../../databases/entities/CredentialsEntity';
|
import { CredentialsEntity } from '../../../../databases/entities/CredentialsEntity';
|
||||||
import { CredentialRequest } from '../../../../requests';
|
import { CredentialRequest } from '../../../../requests';
|
||||||
import { CredentialTypeRequest } from '../../../types';
|
import { CredentialTypeRequest } from '../../../types';
|
||||||
|
@ -29,7 +29,7 @@ export = {
|
||||||
res: express.Response,
|
res: express.Response,
|
||||||
): Promise<express.Response<Partial<CredentialsEntity>>> => {
|
): Promise<express.Response<Partial<CredentialsEntity>>> => {
|
||||||
try {
|
try {
|
||||||
const newCredential = await createCredential(req.body as Partial<CredentialsEntity>);
|
const newCredential = await createCredential(req.body);
|
||||||
|
|
||||||
const encryptedData = await encryptCredential(newCredential);
|
const encryptedData = await encryptCredential(newCredential);
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ export = {
|
||||||
res: express.Response,
|
res: express.Response,
|
||||||
): Promise<express.Response<Partial<CredentialsEntity>>> => {
|
): Promise<express.Response<Partial<CredentialsEntity>>> => {
|
||||||
const { id: credentialId } = req.params;
|
const { id: credentialId } = req.params;
|
||||||
let credentials: CredentialsEntity | undefined;
|
let credential: CredentialsEntity | undefined;
|
||||||
|
|
||||||
if (req.user.globalRole.name !== 'owner') {
|
if (req.user.globalRole.name !== 'owner') {
|
||||||
const shared = await getSharedCredentials(req.user.id, credentialId, [
|
const shared = await getSharedCredentials(req.user.id, credentialId, [
|
||||||
|
@ -65,27 +65,20 @@ export = {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (shared?.role.name === 'owner') {
|
if (shared?.role.name === 'owner') {
|
||||||
credentials = shared.credentials;
|
credential = shared.credentials;
|
||||||
} else {
|
|
||||||
// LoggerProxy.info('Attempt to delete credential blocked due to lack of permissions', {
|
|
||||||
// credentialId,
|
|
||||||
// userId: req.user.id,
|
|
||||||
// });
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
credentials = (await getCredentials(credentialId)) as CredentialsEntity;
|
credential = (await getCredentials(credentialId)) as CredentialsEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!credentials) {
|
if (!credential) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
message: 'Not Found',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await removeCredential(credentials);
|
await removeCredential(credential);
|
||||||
credentials.id = Number(credentialId);
|
credential.id = Number(credentialId);
|
||||||
|
|
||||||
return res.json(sanitizeCredentials(credentials));
|
return res.json(sanitizeCredentials(credential));
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -97,14 +90,12 @@ export = {
|
||||||
try {
|
try {
|
||||||
CredentialTypes().getByName(credentialTypeName);
|
CredentialTypes().getByName(credentialTypeName);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
message: 'Not Found',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let schema = new CredentialsHelper('').getCredentialsProperties(credentialTypeName);
|
const schema = new CredentialsHelper('')
|
||||||
|
.getCredentialsProperties(credentialTypeName)
|
||||||
schema = schema.filter((nodeProperty) => nodeProperty.type !== 'hidden');
|
.filter((property) => property.type !== 'hidden');
|
||||||
|
|
||||||
return res.json(toJsonSchema(schema));
|
return res.json(toJsonSchema(schema));
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,37 +1,36 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-invalid-void-type */
|
||||||
/* eslint-disable consistent-return */
|
|
||||||
import { RequestHandler } from 'express';
|
import express from 'express';
|
||||||
import { validate } from 'jsonschema';
|
import { validate } from 'jsonschema';
|
||||||
|
|
||||||
import { CredentialsHelper, CredentialTypes } from '../../../..';
|
import { CredentialsHelper, CredentialTypes } from '../../../..';
|
||||||
import { CredentialRequest } from '../../../types';
|
import { CredentialRequest } from '../../../types';
|
||||||
import { toJsonSchema } from './credentials.service';
|
import { toJsonSchema } from './credentials.service';
|
||||||
|
|
||||||
export const validCredentialType: RequestHandler = async (
|
export const validCredentialType = (
|
||||||
req: CredentialRequest.Create,
|
req: CredentialRequest.Create,
|
||||||
res,
|
res: express.Response,
|
||||||
next,
|
next: express.NextFunction,
|
||||||
): Promise<any> => {
|
): express.Response | void => {
|
||||||
const { type } = req.body;
|
|
||||||
try {
|
try {
|
||||||
CredentialTypes().getByName(type);
|
CredentialTypes().getByName(req.body.type);
|
||||||
} catch (error) {
|
} catch (_) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({ message: 'req.body.type is not a known type' });
|
||||||
message: 'req.body.type is not a known type',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
next();
|
|
||||||
|
return next();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const validCredentialsProperties: RequestHandler = async (
|
export const validCredentialsProperties = (
|
||||||
req: CredentialRequest.Create,
|
req: CredentialRequest.Create,
|
||||||
res,
|
res: express.Response,
|
||||||
next,
|
next: express.NextFunction,
|
||||||
): Promise<any> => {
|
): express.Response | void => {
|
||||||
const { type, data } = req.body;
|
const { type, data } = req.body;
|
||||||
|
|
||||||
let properties = new CredentialsHelper('').getCredentialsProperties(type);
|
const properties = new CredentialsHelper('')
|
||||||
|
.getCredentialsProperties(type)
|
||||||
properties = properties.filter((nodeProperty) => nodeProperty.type !== 'hidden');
|
.filter((property) => property.type !== 'hidden');
|
||||||
|
|
||||||
const schema = toJsonSchema(properties);
|
const schema = toJsonSchema(properties);
|
||||||
|
|
||||||
|
@ -43,5 +42,5 @@ export const validCredentialsProperties: RequestHandler = async (
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
return next();
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { SharedCredentials } from '../../../../databases/entities/SharedCredenti
|
||||||
import { User } from '../../../../databases/entities/User';
|
import { User } from '../../../../databases/entities/User';
|
||||||
import { externalHooks } from '../../../../Server';
|
import { externalHooks } from '../../../../Server';
|
||||||
import { IDependency, IJsonSchema } from '../../../types';
|
import { IDependency, IJsonSchema } from '../../../types';
|
||||||
|
import { CredentialRequest } from '../../../../requests';
|
||||||
|
|
||||||
export async function getCredentials(
|
export async function getCredentials(
|
||||||
credentialId: number | string,
|
credentialId: number | string,
|
||||||
|
@ -38,7 +39,7 @@ export async function getSharedCredentials(
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createCredential(
|
export async function createCredential(
|
||||||
properties: Partial<CredentialsEntity>,
|
properties: CredentialRequest.CredentialProperties,
|
||||||
): Promise<CredentialsEntity> {
|
): Promise<CredentialsEntity> {
|
||||||
const newCredential = new CredentialsEntity();
|
const newCredential = new CredentialsEntity();
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
import express = require('express');
|
import express from 'express';
|
||||||
|
|
||||||
import { BinaryDataManager } from 'n8n-core';
|
import { BinaryDataManager } from 'n8n-core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getExecutions,
|
getExecutions,
|
||||||
getExecutionInWorkflows,
|
getExecutionInWorkflows,
|
||||||
deleteExecution,
|
deleteExecution,
|
||||||
getExecutionsCount,
|
getExecutionsCount,
|
||||||
} from './executions.service';
|
} from './executions.service';
|
||||||
|
|
||||||
import { ActiveExecutions } from '../../../..';
|
import { ActiveExecutions } from '../../../..';
|
||||||
import { authorize, validCursor } from '../../shared/middlewares/global.middleware';
|
import { authorize, validCursor } from '../../shared/middlewares/global.middleware';
|
||||||
|
|
||||||
import { ExecutionRequest } from '../../../types';
|
import { ExecutionRequest } from '../../../types';
|
||||||
import { getSharedWorkflowIds } from '../workflows/workflows.service';
|
import { getSharedWorkflowIds } from '../workflows/workflows.service';
|
||||||
import { encodeNextCursor } from '../../shared/services/pagination.service';
|
import { encodeNextCursor } from '../../shared/services/pagination.service';
|
||||||
|
@ -20,31 +19,24 @@ export = {
|
||||||
deleteExecution: [
|
deleteExecution: [
|
||||||
authorize(['owner', 'member']),
|
authorize(['owner', 'member']),
|
||||||
async (req: ExecutionRequest.Delete, res: express.Response): Promise<express.Response> => {
|
async (req: ExecutionRequest.Delete, res: express.Response): Promise<express.Response> => {
|
||||||
const { id } = req.params;
|
|
||||||
|
|
||||||
const sharedWorkflowsIds = await getSharedWorkflowIds(req.user);
|
const sharedWorkflowsIds = await getSharedWorkflowIds(req.user);
|
||||||
|
|
||||||
// user does not have workflows hence no executions
|
// user does not have workflows hence no executions
|
||||||
// or the execution he is trying to access belongs to a workflow he does not own
|
// or the execution he is trying to access belongs to a workflow he does not own
|
||||||
if (!sharedWorkflowsIds.length) {
|
if (!sharedWorkflowsIds.length) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
message: 'Not Found',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
// look for the execution on the workflow the user owns
|
// look for the execution on the workflow the user owns
|
||||||
const execution = await getExecutionInWorkflows(id, sharedWorkflowsIds, false);
|
const execution = await getExecutionInWorkflows(id, sharedWorkflowsIds, false);
|
||||||
|
|
||||||
// execution was not found
|
|
||||||
if (!execution) {
|
if (!execution) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
message: 'Not Found',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const binaryDataManager = BinaryDataManager.getInstance();
|
await BinaryDataManager.getInstance().deleteBinaryDataByExecutionId(execution.id.toString());
|
||||||
|
|
||||||
await binaryDataManager.deleteBinaryDataByExecutionId(execution.id.toString());
|
|
||||||
|
|
||||||
await deleteExecution(execution);
|
await deleteExecution(execution);
|
||||||
|
|
||||||
|
@ -56,35 +48,28 @@ export = {
|
||||||
getExecution: [
|
getExecution: [
|
||||||
authorize(['owner', 'member']),
|
authorize(['owner', 'member']),
|
||||||
async (req: ExecutionRequest.Get, res: express.Response): Promise<express.Response> => {
|
async (req: ExecutionRequest.Get, res: express.Response): Promise<express.Response> => {
|
||||||
const { id } = req.params;
|
|
||||||
const { includeData = false } = req.query;
|
|
||||||
|
|
||||||
const sharedWorkflowsIds = await getSharedWorkflowIds(req.user);
|
const sharedWorkflowsIds = await getSharedWorkflowIds(req.user);
|
||||||
|
|
||||||
// user does not have workflows hence no executions
|
// user does not have workflows hence no executions
|
||||||
// or the execution he is trying to access belongs to a workflow he does not own
|
// or the execution he is trying to access belongs to a workflow he does not own
|
||||||
if (!sharedWorkflowsIds.length) {
|
if (!sharedWorkflowsIds.length) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
message: 'Not Found',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { id } = req.params;
|
||||||
|
const { includeData = false } = req.query;
|
||||||
|
|
||||||
// look for the execution on the workflow the user owns
|
// look for the execution on the workflow the user owns
|
||||||
const execution = await getExecutionInWorkflows(id, sharedWorkflowsIds, includeData);
|
const execution = await getExecutionInWorkflows(id, sharedWorkflowsIds, includeData);
|
||||||
|
|
||||||
// execution was not found
|
|
||||||
if (!execution) {
|
if (!execution) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
message: 'Not Found',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const telemetryData = {
|
void InternalHooksManager.getInstance().onUserRetrievedExecution({
|
||||||
user_id: req.user.id,
|
user_id: req.user.id,
|
||||||
public_api: true,
|
public_api: true,
|
||||||
};
|
});
|
||||||
|
|
||||||
void InternalHooksManager.getInstance().onUserRetrievedExecution(telemetryData);
|
|
||||||
|
|
||||||
return res.json(execution);
|
return res.json(execution);
|
||||||
},
|
},
|
||||||
|
@ -106,10 +91,7 @@ export = {
|
||||||
// user does not have workflows hence no executions
|
// user does not have workflows hence no executions
|
||||||
// or the execution he is trying to access belongs to a workflow he does not own
|
// or the execution he is trying to access belongs to a workflow he does not own
|
||||||
if (!sharedWorkflowsIds.length) {
|
if (!sharedWorkflowsIds.length) {
|
||||||
return res.status(200).json({
|
return res.status(200).json({ data: [], nextCursor: null });
|
||||||
data: [],
|
|
||||||
nextCursor: null,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get running workflows so we exclude them from the result
|
// get running workflows so we exclude them from the result
|
||||||
|
@ -134,12 +116,10 @@ export = {
|
||||||
|
|
||||||
const count = await getExecutionsCount(filters);
|
const count = await getExecutionsCount(filters);
|
||||||
|
|
||||||
const telemetryData = {
|
void InternalHooksManager.getInstance().onUserRetrievedAllExecutions({
|
||||||
user_id: req.user.id,
|
user_id: req.user.id,
|
||||||
public_api: true,
|
public_api: true,
|
||||||
};
|
});
|
||||||
|
|
||||||
void InternalHooksManager.getInstance().onUserRetrievedAllExecutions(telemetryData);
|
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
data: executions,
|
data: executions,
|
||||||
|
|
|
@ -1,23 +1,20 @@
|
||||||
import { parse } from 'flatted';
|
import { parse } from 'flatted';
|
||||||
import { In, Not, ObjectLiteral, LessThan, IsNull } from 'typeorm';
|
import { In, Not, ObjectLiteral, LessThan, IsNull } from 'typeorm';
|
||||||
|
|
||||||
import { Db, IExecutionFlattedDb, IExecutionResponseApi } from '../../../..';
|
import { Db, IExecutionFlattedDb, IExecutionResponseApi } from '../../../..';
|
||||||
import { ExecutionStatus } from '../../../types';
|
import { ExecutionStatus } from '../../../types';
|
||||||
|
|
||||||
function prepareExecutionData(
|
function prepareExecutionData(
|
||||||
execution: IExecutionFlattedDb | undefined,
|
execution: IExecutionFlattedDb | undefined,
|
||||||
): IExecutionResponseApi | undefined {
|
): IExecutionResponseApi | undefined {
|
||||||
if (execution === undefined) {
|
if (!execution) return undefined;
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!execution.data) {
|
// @ts-ignore
|
||||||
return execution;
|
if (!execution.data) return execution;
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...execution,
|
...execution,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
data: parse(execution.data) as object,
|
||||||
data: parse(execution.data),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,11 +29,12 @@ function getStatusCondition(status: ExecutionStatus): ObjectLiteral {
|
||||||
condition.stoppedAt = Not(IsNull());
|
condition.stoppedAt = Not(IsNull());
|
||||||
condition.finished = false;
|
condition.finished = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return condition;
|
return condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getExecutionSelectableProperties(includeData?: boolean): Array<keyof IExecutionFlattedDb> {
|
function getExecutionSelectableProperties(includeData?: boolean): Array<keyof IExecutionFlattedDb> {
|
||||||
const returnData: Array<keyof IExecutionFlattedDb> = [
|
const selectFields: Array<keyof IExecutionFlattedDb> = [
|
||||||
'id',
|
'id',
|
||||||
'mode',
|
'mode',
|
||||||
'retryOf',
|
'retryOf',
|
||||||
|
@ -47,10 +45,10 @@ function getExecutionSelectableProperties(includeData?: boolean): Array<keyof IE
|
||||||
'waitTill',
|
'waitTill',
|
||||||
'finished',
|
'finished',
|
||||||
];
|
];
|
||||||
if (includeData) {
|
|
||||||
returnData.push('data');
|
if (includeData) selectFields.push('data');
|
||||||
}
|
|
||||||
return returnData;
|
return selectFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getExecutions(data: {
|
export async function getExecutions(data: {
|
||||||
|
@ -92,6 +90,7 @@ export async function getExecutionsCount(data: {
|
||||||
},
|
},
|
||||||
take: data.limit,
|
take: data.limit,
|
||||||
});
|
});
|
||||||
|
|
||||||
return executions;
|
return executions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,9 +106,13 @@ export async function getExecutionInWorkflows(
|
||||||
workflowId: In(workflows),
|
workflowId: In(workflows),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return prepareExecutionData(execution);
|
return prepareExecutionData(execution);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function deleteExecution(execution: IExecutionResponseApi | undefined): Promise<void> {
|
export async function deleteExecution(
|
||||||
await Db.collections.Execution.remove(execution as IExecutionFlattedDb);
|
execution: IExecutionResponseApi | undefined,
|
||||||
|
): Promise<IExecutionFlattedDb> {
|
||||||
|
// @ts-ignore
|
||||||
|
return Db.collections.Execution.remove(execution);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import express = require('express');
|
import express from 'express';
|
||||||
|
|
||||||
import { FindManyOptions, In } from 'typeorm';
|
import { FindManyOptions, In } from 'typeorm';
|
||||||
|
|
||||||
import { ActiveWorkflowRunner, Db } from '../../../..';
|
import { ActiveWorkflowRunner, Db } from '../../../..';
|
||||||
import config = require('../../../../../config');
|
import config = require('../../../../../config');
|
||||||
import { WorkflowEntity } from '../../../../databases/entities/WorkflowEntity';
|
import { WorkflowEntity } from '../../../../databases/entities/WorkflowEntity';
|
||||||
|
@ -30,25 +32,24 @@ export = {
|
||||||
createWorkflow: [
|
createWorkflow: [
|
||||||
authorize(['owner', 'member']),
|
authorize(['owner', 'member']),
|
||||||
async (req: WorkflowRequest.Create, res: express.Response): Promise<express.Response> => {
|
async (req: WorkflowRequest.Create, res: express.Response): Promise<express.Response> => {
|
||||||
let workflow = req.body;
|
const workflow = req.body;
|
||||||
|
|
||||||
workflow.active = false;
|
workflow.active = false;
|
||||||
|
|
||||||
// if the workflow does not have a start node, add it.
|
|
||||||
if (!hasStartNode(workflow)) {
|
if (!hasStartNode(workflow)) {
|
||||||
workflow.nodes.push(getStartNode());
|
workflow.nodes.push(getStartNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
const role = await getWorkflowOwnerRole();
|
|
||||||
|
|
||||||
await replaceInvalidCredentials(workflow);
|
await replaceInvalidCredentials(workflow);
|
||||||
|
|
||||||
workflow = await createWorkflow(workflow, req.user, role);
|
const role = await getWorkflowOwnerRole();
|
||||||
|
|
||||||
await externalHooks.run('workflow.afterCreate', [workflow]);
|
const createdWorkflow = await createWorkflow(workflow, req.user, role);
|
||||||
void InternalHooksManager.getInstance().onWorkflowCreated(req.user.id, workflow, true);
|
|
||||||
|
|
||||||
return res.json(workflow);
|
await externalHooks.run('workflow.afterCreate', [createdWorkflow]);
|
||||||
|
void InternalHooksManager.getInstance().onWorkflowCreated(req.user.id, createdWorkflow, true);
|
||||||
|
|
||||||
|
return res.json(createdWorkflow);
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
deleteWorkflow: [
|
deleteWorkflow: [
|
||||||
|
@ -61,16 +62,12 @@ export = {
|
||||||
if (!sharedWorkflow) {
|
if (!sharedWorkflow) {
|
||||||
// user trying to access a workflow he does not own
|
// user trying to access a workflow he does not own
|
||||||
// or workflow does not exist
|
// or workflow does not exist
|
||||||
return res.status(404).json({
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
message: 'Not Found',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowRunner = ActiveWorkflowRunner.getInstance();
|
|
||||||
|
|
||||||
if (sharedWorkflow.workflow.active) {
|
if (sharedWorkflow.workflow.active) {
|
||||||
// deactivate before deleting
|
// deactivate before deleting
|
||||||
await workflowRunner.remove(id.toString());
|
await ActiveWorkflowRunner.getInstance().remove(id.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
await Db.collections.Workflow.delete(id);
|
await Db.collections.Workflow.delete(id);
|
||||||
|
@ -91,17 +88,13 @@ export = {
|
||||||
if (!sharedWorkflow) {
|
if (!sharedWorkflow) {
|
||||||
// user trying to access a workflow he does not own
|
// user trying to access a workflow he does not own
|
||||||
// or workflow does not exist
|
// or workflow does not exist
|
||||||
return res.status(404).json({
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
message: 'Not Found',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const telemetryData = {
|
void InternalHooksManager.getInstance().onUserRetrievedWorkflow({
|
||||||
user_id: req.user.id,
|
user_id: req.user.id,
|
||||||
public_api: true,
|
public_api: true,
|
||||||
};
|
});
|
||||||
|
|
||||||
void InternalHooksManager.getInstance().onUserRetrievedWorkflow(telemetryData);
|
|
||||||
|
|
||||||
return res.json(sharedWorkflow.workflow);
|
return res.json(sharedWorkflow.workflow);
|
||||||
},
|
},
|
||||||
|
@ -158,12 +151,10 @@ export = {
|
||||||
count = await getWorkflowsCount(query);
|
count = await getWorkflowsCount(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
const telemetryData = {
|
void InternalHooksManager.getInstance().onUserRetrievedAllWorkflows({
|
||||||
user_id: req.user.id,
|
user_id: req.user.id,
|
||||||
public_api: true,
|
public_api: true,
|
||||||
};
|
});
|
||||||
|
|
||||||
void InternalHooksManager.getInstance().onUserRetrievedAllWorkflows(telemetryData);
|
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
data: workflows,
|
data: workflows,
|
||||||
|
@ -187,18 +178,13 @@ export = {
|
||||||
if (!sharedWorkflow) {
|
if (!sharedWorkflow) {
|
||||||
// user trying to access a workflow he does not own
|
// user trying to access a workflow he does not own
|
||||||
// or workflow does not exist
|
// or workflow does not exist
|
||||||
return res.status(404).json({
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
message: 'Not Found',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the workflow does not have a start node, add it.
|
|
||||||
// else there is nothing you can do in IU
|
|
||||||
if (!hasStartNode(updateData)) {
|
if (!hasStartNode(updateData)) {
|
||||||
updateData.nodes.push(getStartNode());
|
updateData.nodes.push(getStartNode());
|
||||||
}
|
}
|
||||||
|
|
||||||
// check credentials for old format
|
|
||||||
await replaceInvalidCredentials(updateData);
|
await replaceInvalidCredentials(updateData);
|
||||||
|
|
||||||
const workflowRunner = ActiveWorkflowRunner.getInstance();
|
const workflowRunner = ActiveWorkflowRunner.getInstance();
|
||||||
|
@ -215,10 +201,9 @@ export = {
|
||||||
try {
|
try {
|
||||||
await workflowRunner.add(sharedWorkflow.workflowId.toString(), 'update');
|
await workflowRunner.add(sharedWorkflow.workflowId.toString(), 'update');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// todo
|
if (error instanceof Error) {
|
||||||
// remove the type assertion
|
return res.status(400).json({ message: error.message });
|
||||||
const errorObject = error as unknown as { message: string };
|
}
|
||||||
return res.status(400).json({ error: errorObject.message });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,21 +225,19 @@ export = {
|
||||||
if (!sharedWorkflow) {
|
if (!sharedWorkflow) {
|
||||||
// user trying to access a workflow he does not own
|
// user trying to access a workflow he does not own
|
||||||
// or workflow does not exist
|
// or workflow does not exist
|
||||||
return res.status(404).json({
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
message: 'Not Found',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowRunner = ActiveWorkflowRunner.getInstance();
|
|
||||||
|
|
||||||
if (!sharedWorkflow.workflow.active) {
|
if (!sharedWorkflow.workflow.active) {
|
||||||
try {
|
try {
|
||||||
await workflowRunner.add(sharedWorkflow.workflowId.toString(), 'activate');
|
await ActiveWorkflowRunner.getInstance().add(
|
||||||
|
sharedWorkflow.workflowId.toString(),
|
||||||
|
'activate',
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// todo
|
if (error instanceof Error) {
|
||||||
// remove the type assertion
|
return res.status(400).json({ message: error.message });
|
||||||
const errorObject = error as unknown as { message: string };
|
}
|
||||||
return res.status(400).json({ error: errorObject.message });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// change the status to active in the DB
|
// change the status to active in the DB
|
||||||
|
@ -279,9 +262,7 @@ export = {
|
||||||
if (!sharedWorkflow) {
|
if (!sharedWorkflow) {
|
||||||
// user trying to access a workflow he does not own
|
// user trying to access a workflow he does not own
|
||||||
// or workflow does not exist
|
// or workflow does not exist
|
||||||
return res.status(404).json({
|
return res.status(404).json({ message: 'Not Found' });
|
||||||
message: 'Not Found',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const workflowRunner = ActiveWorkflowRunner.getInstance();
|
const workflowRunner = ActiveWorkflowRunner.getInstance();
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
|
import { FindManyOptions, In, UpdateResult } from 'typeorm';
|
||||||
import { intersection } from 'lodash';
|
import { intersection } from 'lodash';
|
||||||
import type { INode } from 'n8n-workflow';
|
import type { INode } from 'n8n-workflow';
|
||||||
import { FindManyOptions, In, UpdateResult } from 'typeorm';
|
|
||||||
|
import { Db } from '../../../..';
|
||||||
import { User } from '../../../../databases/entities/User';
|
import { User } from '../../../../databases/entities/User';
|
||||||
import { WorkflowEntity } from '../../../../databases/entities/WorkflowEntity';
|
import { WorkflowEntity } from '../../../../databases/entities/WorkflowEntity';
|
||||||
import { Db } from '../../../..';
|
|
||||||
import { SharedWorkflow } from '../../../../databases/entities/SharedWorkflow';
|
import { SharedWorkflow } from '../../../../databases/entities/SharedWorkflow';
|
||||||
import { isInstanceOwner } from '../users/users.service';
|
import { isInstanceOwner } from '../users/users.service';
|
||||||
import { Role } from '../../../../databases/entities/Role';
|
import { Role } from '../../../../databases/entities/Role';
|
||||||
|
|
||||||
export async function getSharedWorkflowIds(user: User): Promise<number[]> {
|
export async function getSharedWorkflowIds(user: User): Promise<number[]> {
|
||||||
const sharedWorkflows = await Db.collections.SharedWorkflow.find({
|
const sharedWorkflows = await Db.collections.SharedWorkflow.find({
|
||||||
where: {
|
where: { user },
|
||||||
user,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return sharedWorkflows.map((workflow) => workflow.workflowId);
|
return sharedWorkflows.map((workflow) => workflow.workflowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,14 +21,13 @@ export async function getSharedWorkflow(
|
||||||
user: User,
|
user: User,
|
||||||
workflowId?: string | undefined,
|
workflowId?: string | undefined,
|
||||||
): Promise<SharedWorkflow | undefined> {
|
): Promise<SharedWorkflow | undefined> {
|
||||||
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({
|
return Db.collections.SharedWorkflow.findOne({
|
||||||
where: {
|
where: {
|
||||||
...(!isInstanceOwner(user) && { user }),
|
...(!isInstanceOwner(user) && { user }),
|
||||||
...(workflowId && { workflow: { id: workflowId } }),
|
...(workflowId && { workflow: { id: workflowId } }),
|
||||||
},
|
},
|
||||||
relations: ['workflow'],
|
relations: ['workflow'],
|
||||||
});
|
});
|
||||||
return sharedWorkflow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getSharedWorkflows(
|
export async function getSharedWorkflows(
|
||||||
|
@ -38,23 +37,19 @@ export async function getSharedWorkflows(
|
||||||
workflowIds?: number[];
|
workflowIds?: number[];
|
||||||
},
|
},
|
||||||
): Promise<SharedWorkflow[]> {
|
): Promise<SharedWorkflow[]> {
|
||||||
const sharedWorkflows = await Db.collections.SharedWorkflow.find({
|
return Db.collections.SharedWorkflow.find({
|
||||||
where: {
|
where: {
|
||||||
...(!isInstanceOwner(user) && { user }),
|
...(!isInstanceOwner(user) && { user }),
|
||||||
...(options.workflowIds && { workflow: { id: In(options.workflowIds) } }),
|
...(options.workflowIds && { workflow: { id: In(options.workflowIds) } }),
|
||||||
},
|
},
|
||||||
...(options.relations && { relations: options.relations }),
|
...(options.relations && { relations: options.relations }),
|
||||||
});
|
});
|
||||||
return sharedWorkflows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getWorkflowById(id: number): Promise<WorkflowEntity | undefined> {
|
export async function getWorkflowById(id: number): Promise<WorkflowEntity | undefined> {
|
||||||
const workflow = await Db.collections.Workflow.findOne({
|
return Db.collections.Workflow.findOne({
|
||||||
where: {
|
where: { id },
|
||||||
id,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
return workflow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -63,9 +58,7 @@ export async function getWorkflowById(id: number): Promise<WorkflowEntity | unde
|
||||||
*/
|
*/
|
||||||
export async function getWorkflowIdsViaTags(tags: string[]): Promise<number[]> {
|
export async function getWorkflowIdsViaTags(tags: string[]): Promise<number[]> {
|
||||||
const dbTags = await Db.collections.Tag.find({
|
const dbTags = await Db.collections.Tag.find({
|
||||||
where: {
|
where: { name: In(tags) },
|
||||||
name: In(tags),
|
|
||||||
},
|
|
||||||
relations: ['workflows'],
|
relations: ['workflows'],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -79,11 +72,11 @@ export async function createWorkflow(
|
||||||
user: User,
|
user: User,
|
||||||
role: Role,
|
role: Role,
|
||||||
): Promise<WorkflowEntity> {
|
): Promise<WorkflowEntity> {
|
||||||
let savedWorkflow: unknown;
|
return Db.transaction(async (transactionManager) => {
|
||||||
const newWorkflow = new WorkflowEntity();
|
const newWorkflow = new WorkflowEntity();
|
||||||
Object.assign(newWorkflow, workflow);
|
Object.assign(newWorkflow, workflow);
|
||||||
await Db.transaction(async (transactionManager) => {
|
const savedWorkflow = await transactionManager.save<WorkflowEntity>(newWorkflow);
|
||||||
savedWorkflow = await transactionManager.save<WorkflowEntity>(newWorkflow);
|
|
||||||
const newSharedWorkflow = new SharedWorkflow();
|
const newSharedWorkflow = new SharedWorkflow();
|
||||||
Object.assign(newSharedWorkflow, {
|
Object.assign(newSharedWorkflow, {
|
||||||
role,
|
role,
|
||||||
|
@ -91,8 +84,9 @@ export async function createWorkflow(
|
||||||
workflow: savedWorkflow,
|
workflow: savedWorkflow,
|
||||||
});
|
});
|
||||||
await transactionManager.save<SharedWorkflow>(newSharedWorkflow);
|
await transactionManager.save<SharedWorkflow>(newSharedWorkflow);
|
||||||
|
|
||||||
|
return savedWorkflow;
|
||||||
});
|
});
|
||||||
return savedWorkflow as WorkflowEntity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setWorkflowAsActive(workflow: WorkflowEntity): Promise<UpdateResult> {
|
export async function setWorkflowAsActive(workflow: WorkflowEntity): Promise<UpdateResult> {
|
||||||
|
@ -110,13 +104,11 @@ export async function deleteWorkflow(workflow: WorkflowEntity): Promise<Workflow
|
||||||
export async function getWorkflows(
|
export async function getWorkflows(
|
||||||
options: FindManyOptions<WorkflowEntity>,
|
options: FindManyOptions<WorkflowEntity>,
|
||||||
): Promise<WorkflowEntity[]> {
|
): Promise<WorkflowEntity[]> {
|
||||||
const workflows = await Db.collections.Workflow.find(options);
|
return Db.collections.Workflow.find(options);
|
||||||
return workflows;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getWorkflowsCount(options: FindManyOptions<WorkflowEntity>): Promise<number> {
|
export async function getWorkflowsCount(options: FindManyOptions<WorkflowEntity>): Promise<number> {
|
||||||
const count = await Db.collections.Workflow.count(options);
|
return Db.collections.Workflow.count(options);
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateWorkflow(
|
export async function updateWorkflow(
|
||||||
|
@ -127,9 +119,11 @@ export async function updateWorkflow(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function hasStartNode(workflow: WorkflowEntity): boolean {
|
export function hasStartNode(workflow: WorkflowEntity): boolean {
|
||||||
return !(
|
if (!workflow.nodes.length) return false;
|
||||||
!workflow.nodes.length || !workflow.nodes.find((node) => node.type === 'n8n-nodes-base.start')
|
|
||||||
);
|
const found = workflow.nodes.find((node) => node.type === 'n8n-nodes-base.start');
|
||||||
|
|
||||||
|
return Boolean(found);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getStartNode(): INode {
|
export function getStartNode(): INode {
|
||||||
|
|
|
@ -1,24 +1,31 @@
|
||||||
/* eslint-disable consistent-return */
|
/* eslint-disable @typescript-eslint/no-invalid-void-type */
|
||||||
import { RequestHandler } from 'express';
|
|
||||||
import { PaginatatedRequest } from '../../../types';
|
import express from 'express';
|
||||||
|
|
||||||
|
import { AuthenticatedRequest, PaginatatedRequest } from '../../../types';
|
||||||
import { decodeCursor } from '../services/pagination.service';
|
import { decodeCursor } from '../services/pagination.service';
|
||||||
|
|
||||||
type Role = 'member' | 'owner';
|
export const authorize =
|
||||||
|
(authorizedRoles: readonly string[]) =>
|
||||||
|
(
|
||||||
|
req: AuthenticatedRequest,
|
||||||
|
res: express.Response,
|
||||||
|
next: express.NextFunction,
|
||||||
|
): express.Response | void => {
|
||||||
|
const { name } = req.user.globalRole;
|
||||||
|
|
||||||
|
if (!authorizedRoles.includes(name)) {
|
||||||
|
return res.status(403).json({ message: 'Forbidden' });
|
||||||
|
}
|
||||||
|
|
||||||
export const authorize: (role: Role[]) => RequestHandler = (role: Role[]) => (req, res, next) => {
|
|
||||||
const {
|
|
||||||
globalRole: { name: userRole },
|
|
||||||
} = req.user as { globalRole: { name: Role } };
|
|
||||||
if (role.includes(userRole)) {
|
|
||||||
return next();
|
return next();
|
||||||
}
|
};
|
||||||
return res.status(403).json({
|
|
||||||
message: 'Forbidden',
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// @ts-ignore
|
export const validCursor = (
|
||||||
export const validCursor: RequestHandler = (req: PaginatatedRequest, res, next) => {
|
req: PaginatatedRequest,
|
||||||
|
res: express.Response,
|
||||||
|
next: express.NextFunction,
|
||||||
|
): express.Response | void => {
|
||||||
if (req.query.cursor) {
|
if (req.query.cursor) {
|
||||||
const { cursor } = req.query;
|
const { cursor } = req.query;
|
||||||
try {
|
try {
|
||||||
|
@ -36,5 +43,6 @@ export const validCursor: RequestHandler = (req: PaginatatedRequest, res, next)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next();
|
|
||||||
|
return next();
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,7 +12,6 @@ export const decodeCursor = (cursor: string): PaginationOffsetDecoded | Paginati
|
||||||
};
|
};
|
||||||
|
|
||||||
const encodeOffSetPagination = (pagination: OffsetPagination): string | null => {
|
const encodeOffSetPagination = (pagination: OffsetPagination): string | null => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
||||||
if (pagination.numberOfTotalRecords > pagination.offset + pagination.limit) {
|
if (pagination.numberOfTotalRecords > pagination.offset + pagination.limit) {
|
||||||
return Buffer.from(
|
return Buffer.from(
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
|
6
packages/cli/src/requests.d.ts
vendored
6
packages/cli/src/requests.d.ts
vendored
|
@ -82,7 +82,7 @@ export declare namespace WorkflowRequest {
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
||||||
export declare namespace CredentialRequest {
|
export declare namespace CredentialRequest {
|
||||||
type RequestBody = Partial<{
|
type CredentialProperties = Partial<{
|
||||||
id: string; // delete if sent
|
id: string; // delete if sent
|
||||||
name: string;
|
name: string;
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -90,7 +90,7 @@ export declare namespace CredentialRequest {
|
||||||
data: ICredentialDataDecryptedObject;
|
data: ICredentialDataDecryptedObject;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
type Create = AuthenticatedRequest<{}, {}, RequestBody>;
|
type Create = AuthenticatedRequest<{}, {}, CredentialProperties>;
|
||||||
|
|
||||||
type Get = AuthenticatedRequest<{ id: string }, {}, {}, Record<string, string>>;
|
type Get = AuthenticatedRequest<{ id: string }, {}, {}, Record<string, string>>;
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ export declare namespace CredentialRequest {
|
||||||
|
|
||||||
type GetAll = AuthenticatedRequest<{}, {}, {}, { filter: string }>;
|
type GetAll = AuthenticatedRequest<{}, {}, {}, { filter: string }>;
|
||||||
|
|
||||||
type Update = AuthenticatedRequest<{ id: string }, {}, RequestBody>;
|
type Update = AuthenticatedRequest<{ id: string }, {}, CredentialProperties>;
|
||||||
|
|
||||||
type NewName = WorkflowRequest.NewName;
|
type NewName = WorkflowRequest.NewName;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
|
||||||
import { UserSettings } from 'n8n-core';
|
import { UserSettings } from 'n8n-core';
|
||||||
|
|
||||||
import { Db } from '../../../src';
|
import { Db } from '../../../src';
|
||||||
import { randomApiKey, randomName, randomString } from '../shared/random';
|
import { randomApiKey, randomName, randomString } from '../shared/random';
|
||||||
import * as utils from '../shared/utils';
|
import * as utils from '../shared/utils';
|
||||||
|
@ -9,13 +11,10 @@ import type { User } from '../../../src/databases/entities/User';
|
||||||
import * as testDb from '../shared/testDb';
|
import * as testDb from '../shared/testDb';
|
||||||
import { RESPONSE_ERROR_MESSAGES } from '../../../src/constants';
|
import { RESPONSE_ERROR_MESSAGES } from '../../../src/constants';
|
||||||
|
|
||||||
jest.mock('../../../src/telemetry');
|
|
||||||
|
|
||||||
let app: express.Application;
|
let app: express.Application;
|
||||||
let testDbName = '';
|
let testDbName = '';
|
||||||
let globalOwnerRole: Role;
|
let globalOwnerRole: Role;
|
||||||
let globalMemberRole: Role;
|
let globalMemberRole: Role;
|
||||||
let workflowOwnerRole: Role;
|
|
||||||
let credentialOwnerRole: Role;
|
let credentialOwnerRole: Role;
|
||||||
|
|
||||||
let saveCredential: SaveCredentialFunction;
|
let saveCredential: SaveCredentialFunction;
|
||||||
|
@ -30,13 +29,12 @@ beforeAll(async () => {
|
||||||
const [
|
const [
|
||||||
fetchedGlobalOwnerRole,
|
fetchedGlobalOwnerRole,
|
||||||
fetchedGlobalMemberRole,
|
fetchedGlobalMemberRole,
|
||||||
fetchedWorkflowOwnerRole,
|
_,
|
||||||
fetchedCredentialOwnerRole,
|
fetchedCredentialOwnerRole,
|
||||||
] = await testDb.getAllRoles();
|
] = await testDb.getAllRoles();
|
||||||
|
|
||||||
globalOwnerRole = fetchedGlobalOwnerRole;
|
globalOwnerRole = fetchedGlobalOwnerRole;
|
||||||
globalMemberRole = fetchedGlobalMemberRole;
|
globalMemberRole = fetchedGlobalMemberRole;
|
||||||
workflowOwnerRole = fetchedWorkflowOwnerRole;
|
|
||||||
credentialOwnerRole = fetchedCredentialOwnerRole;
|
credentialOwnerRole = fetchedCredentialOwnerRole;
|
||||||
|
|
||||||
saveCredential = affixRoleToSaveCredential(credentialOwnerRole);
|
saveCredential = affixRoleToSaveCredential(credentialOwnerRole);
|
||||||
|
@ -377,6 +375,7 @@ const credentialPayload = (): CredentialPayload => ({
|
||||||
const dbCredential = () => {
|
const dbCredential = () => {
|
||||||
const credential = credentialPayload();
|
const credential = credentialPayload();
|
||||||
credential.nodesAccess = [{ nodeType: credential.type }];
|
credential.nodesAccess = [{ nodeType: credential.type }];
|
||||||
|
|
||||||
return credential;
|
return credential;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import express = require('express');
|
import express from 'express';
|
||||||
|
|
||||||
import { ActiveWorkflowRunner } from '../../../src';
|
import { ActiveWorkflowRunner } from '../../../src';
|
||||||
import config = require('../../../config');
|
import config from '../../../config';
|
||||||
import { Role } from '../../../src/databases/entities/Role';
|
import { Role } from '../../../src/databases/entities/Role';
|
||||||
import { randomApiKey } from '../shared/random';
|
import { randomApiKey } from '../shared/random';
|
||||||
|
|
||||||
|
@ -11,11 +11,8 @@ import * as testDb from '../shared/testDb';
|
||||||
let app: express.Application;
|
let app: express.Application;
|
||||||
let testDbName = '';
|
let testDbName = '';
|
||||||
let globalOwnerRole: Role;
|
let globalOwnerRole: Role;
|
||||||
|
|
||||||
let workflowRunner: ActiveWorkflowRunner.ActiveWorkflowRunner;
|
let workflowRunner: ActiveWorkflowRunner.ActiveWorkflowRunner;
|
||||||
|
|
||||||
jest.mock('../../../src/telemetry');
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false });
|
app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false });
|
||||||
const initResult = await testDb.init();
|
const initResult = await testDb.init();
|
||||||
|
@ -25,21 +22,29 @@ beforeAll(async () => {
|
||||||
|
|
||||||
utils.initTestTelemetry();
|
utils.initTestTelemetry();
|
||||||
utils.initTestLogger();
|
utils.initTestLogger();
|
||||||
// initializing binary manager leave some async operations open
|
|
||||||
// TODO mockup binary data mannager to avoid error
|
|
||||||
await utils.initBinaryManager();
|
await utils.initBinaryManager();
|
||||||
await utils.initNodeTypes();
|
await utils.initNodeTypes();
|
||||||
|
|
||||||
workflowRunner = await utils.initActiveWorkflowRunner();
|
workflowRunner = await utils.initActiveWorkflowRunner();
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
// do not combine calls - shared tables must be cleared first and separately
|
await testDb.truncate(
|
||||||
await testDb.truncate(['SharedCredentials', 'SharedWorkflow'], testDbName);
|
[
|
||||||
await testDb.truncate(['User', 'Workflow', 'Credentials', 'Execution', 'Settings'], testDbName);
|
'SharedCredentials',
|
||||||
|
'SharedWorkflow',
|
||||||
|
'User',
|
||||||
|
'Workflow',
|
||||||
|
'Credentials',
|
||||||
|
'Execution',
|
||||||
|
'Settings',
|
||||||
|
],
|
||||||
|
testDbName,
|
||||||
|
);
|
||||||
|
|
||||||
config.set('userManagement.disabled', false);
|
config.set('userManagement.disabled', false);
|
||||||
config.set('userManagement.isInstanceOwnerSetUp', true);
|
config.set('userManagement.isInstanceOwnerSetUp', true);
|
||||||
config.set('userManagement.emails.mode', 'smtp');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
@ -50,7 +55,7 @@ afterAll(async () => {
|
||||||
await testDb.terminate(testDbName);
|
await testDb.terminate(testDbName);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('GET /executions/:executionId should fail due to missing API Key', async () => {
|
test('GET /executions/:id should fail due to missing API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -65,7 +70,7 @@ test.skip('GET /executions/:executionId should fail due to missing API Key', asy
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('GET /executions/:executionId should fail due to invalid API Key', async () => {
|
test('GET /executions/:id should fail due to invalid API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
owner.apiKey = 'abcXYZ';
|
owner.apiKey = 'abcXYZ';
|
||||||
|
|
||||||
|
@ -81,7 +86,7 @@ test.skip('GET /executions/:executionId should fail due to invalid API Key', asy
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('GET /executions/:executionId should get an execution', async () => {
|
test('GET /executions/:id should get an execution', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -93,7 +98,7 @@ test.skip('GET /executions/:executionId should get an execution', async () => {
|
||||||
|
|
||||||
const workflow = await testDb.createWorkflow({}, owner);
|
const workflow = await testDb.createWorkflow({}, owner);
|
||||||
|
|
||||||
const execution = await testDb.createSuccessfullExecution(workflow);
|
const execution = await testDb.createSuccessfulExecution(workflow);
|
||||||
|
|
||||||
const response = await authOwnerAgent.get(`/executions/${execution.id}`);
|
const response = await authOwnerAgent.get(`/executions/${execution.id}`);
|
||||||
|
|
||||||
|
@ -122,7 +127,7 @@ test.skip('GET /executions/:executionId should get an execution', async () => {
|
||||||
expect(waitTill).toBeNull();
|
expect(waitTill).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('DELETE /executions/:executionId should fail due to missing API Key', async () => {
|
test('DELETE /executions/:id should fail due to missing API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -137,7 +142,7 @@ test.skip('DELETE /executions/:executionId should fail due to missing API Key',
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('DELETE /executions/:executionId should fail due to invalid API Key', async () => {
|
test('DELETE /executions/:id should fail due to invalid API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
owner.apiKey = 'abcXYZ';
|
owner.apiKey = 'abcXYZ';
|
||||||
|
|
||||||
|
@ -153,7 +158,7 @@ test.skip('DELETE /executions/:executionId should fail due to invalid API Key',
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('DELETE /executions/:executionId should delete an execution', async () => {
|
test('DELETE /executions/:id should delete an execution', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -165,7 +170,7 @@ test.skip('DELETE /executions/:executionId should delete an execution', async ()
|
||||||
|
|
||||||
const workflow = await testDb.createWorkflow({}, owner);
|
const workflow = await testDb.createWorkflow({}, owner);
|
||||||
|
|
||||||
const execution = await testDb.createSuccessfullExecution(workflow);
|
const execution = await testDb.createSuccessfulExecution(workflow);
|
||||||
|
|
||||||
const response = await authOwnerAgent.delete(`/executions/${execution.id}`);
|
const response = await authOwnerAgent.delete(`/executions/${execution.id}`);
|
||||||
|
|
||||||
|
@ -194,7 +199,7 @@ test.skip('DELETE /executions/:executionId should delete an execution', async ()
|
||||||
expect(waitTill).toBeNull();
|
expect(waitTill).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('GET /executions should fail due to missing API Key', async () => {
|
test('GET /executions should fail due to missing API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -209,7 +214,7 @@ test.skip('GET /executions should fail due to missing API Key', async () => {
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('GET /executions should fail due to invalid API Key', async () => {
|
test('GET /executions should fail due to invalid API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
owner.apiKey = 'abcXYZ';
|
owner.apiKey = 'abcXYZ';
|
||||||
|
|
||||||
|
@ -225,7 +230,7 @@ test.skip('GET /executions should fail due to invalid API Key', async () => {
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('GET /executions should retrieve all successfull executions', async () => {
|
test('GET /executions should retrieve all successfull executions', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -237,7 +242,7 @@ test.skip('GET /executions should retrieve all successfull executions', async ()
|
||||||
|
|
||||||
const workflow = await testDb.createWorkflow({}, owner);
|
const workflow = await testDb.createWorkflow({}, owner);
|
||||||
|
|
||||||
const successfullExecution = await testDb.createSuccessfullExecution(workflow);
|
const successfullExecution = await testDb.createSuccessfulExecution(workflow);
|
||||||
|
|
||||||
await testDb.createErrorExecution(workflow);
|
await testDb.createErrorExecution(workflow);
|
||||||
|
|
||||||
|
@ -272,7 +277,7 @@ test.skip('GET /executions should retrieve all successfull executions', async ()
|
||||||
expect(waitTill).toBeNull();
|
expect(waitTill).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('GET /executions should retrieve all error executions', async () => {
|
test('GET /executions should retrieve all error executions', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -284,7 +289,7 @@ test.skip('GET /executions should retrieve all error executions', async () => {
|
||||||
|
|
||||||
const workflow = await testDb.createWorkflow({}, owner);
|
const workflow = await testDb.createWorkflow({}, owner);
|
||||||
|
|
||||||
await testDb.createSuccessfullExecution(workflow);
|
await testDb.createSuccessfulExecution(workflow);
|
||||||
|
|
||||||
const errorExecution = await testDb.createErrorExecution(workflow);
|
const errorExecution = await testDb.createErrorExecution(workflow);
|
||||||
|
|
||||||
|
@ -319,7 +324,7 @@ test.skip('GET /executions should retrieve all error executions', async () => {
|
||||||
expect(waitTill).toBeNull();
|
expect(waitTill).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('GET /executions should return all waiting executions', async () => {
|
test('GET /executions should return all waiting executions', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -331,7 +336,7 @@ test.skip('GET /executions should return all waiting executions', async () => {
|
||||||
|
|
||||||
const workflow = await testDb.createWorkflow({}, owner);
|
const workflow = await testDb.createWorkflow({}, owner);
|
||||||
|
|
||||||
await testDb.createSuccessfullExecution(workflow);
|
await testDb.createSuccessfulExecution(workflow);
|
||||||
|
|
||||||
await testDb.createErrorExecution(workflow);
|
await testDb.createErrorExecution(workflow);
|
||||||
|
|
||||||
|
@ -368,7 +373,7 @@ test.skip('GET /executions should return all waiting executions', async () => {
|
||||||
expect(new Date(waitTill).getTime()).toBeGreaterThan(Date.now() - 1000);
|
expect(new Date(waitTill).getTime()).toBeGreaterThan(Date.now() - 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('GET /executions should retrieve all executions of specific workflow', async () => {
|
test('GET /executions should retrieve all executions of specific workflow', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -384,10 +389,10 @@ test.skip('GET /executions should retrieve all executions of specific workflow',
|
||||||
2,
|
2,
|
||||||
workflow,
|
workflow,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
testDb.createSuccessfullExecution,
|
testDb.createSuccessfulExecution,
|
||||||
);
|
);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await testDb.createManyExecutions(2, workflow2, testDb.createSuccessfullExecution);
|
await testDb.createManyExecutions(2, workflow2, testDb.createSuccessfulExecution);
|
||||||
|
|
||||||
const response = await authOwnerAgent.get(`/executions`).query({
|
const response = await authOwnerAgent.get(`/executions`).query({
|
||||||
workflowId: workflow.id.toString(),
|
workflowId: workflow.id.toString(),
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import express = require('express');
|
import express from 'express';
|
||||||
|
|
||||||
import { ActiveWorkflowRunner, Db } from '../../../src';
|
import { ActiveWorkflowRunner, Db } from '../../../src';
|
||||||
import config = require('../../../config');
|
import config from '../../../config';
|
||||||
import { Role } from '../../../src/databases/entities/Role';
|
import { Role } from '../../../src/databases/entities/Role';
|
||||||
import { randomApiKey } from '../shared/random';
|
import { randomApiKey } from '../shared/random';
|
||||||
|
|
||||||
|
@ -14,11 +14,8 @@ let testDbName = '';
|
||||||
let globalOwnerRole: Role;
|
let globalOwnerRole: Role;
|
||||||
let globalMemberRole: Role;
|
let globalMemberRole: Role;
|
||||||
let workflowOwnerRole: Role;
|
let workflowOwnerRole: Role;
|
||||||
let credentialOwnerRole: Role;
|
|
||||||
let workflowRunner: ActiveWorkflowRunner.ActiveWorkflowRunner;
|
let workflowRunner: ActiveWorkflowRunner.ActiveWorkflowRunner;
|
||||||
|
|
||||||
jest.mock('../../../src/telemetry');
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false });
|
app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false });
|
||||||
const initResult = await testDb.init();
|
const initResult = await testDb.init();
|
||||||
|
@ -28,13 +25,11 @@ beforeAll(async () => {
|
||||||
fetchedGlobalOwnerRole,
|
fetchedGlobalOwnerRole,
|
||||||
fetchedGlobalMemberRole,
|
fetchedGlobalMemberRole,
|
||||||
fetchedWorkflowOwnerRole,
|
fetchedWorkflowOwnerRole,
|
||||||
fetchedCredentialOwnerRole,
|
|
||||||
] = await testDb.getAllRoles();
|
] = await testDb.getAllRoles();
|
||||||
|
|
||||||
globalOwnerRole = fetchedGlobalOwnerRole;
|
globalOwnerRole = fetchedGlobalOwnerRole;
|
||||||
globalMemberRole = fetchedGlobalMemberRole;
|
globalMemberRole = fetchedGlobalMemberRole;
|
||||||
workflowOwnerRole = fetchedWorkflowOwnerRole;
|
workflowOwnerRole = fetchedWorkflowOwnerRole;
|
||||||
credentialOwnerRole = fetchedCredentialOwnerRole;
|
|
||||||
|
|
||||||
utils.initTestTelemetry();
|
utils.initTestTelemetry();
|
||||||
utils.initTestLogger();
|
utils.initTestLogger();
|
||||||
|
@ -43,13 +38,13 @@ beforeAll(async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
// do not combine calls - shared tables must be cleared first and separately
|
await testDb.truncate(
|
||||||
await testDb.truncate(['SharedCredentials', 'SharedWorkflow'], testDbName);
|
['SharedCredentials', 'SharedWorkflow', 'User', 'Workflow', 'Credentials'],
|
||||||
await testDb.truncate(['User', 'Workflow', 'Credentials'], testDbName);
|
testDbName,
|
||||||
|
);
|
||||||
|
|
||||||
config.set('userManagement.disabled', false);
|
config.set('userManagement.disabled', false);
|
||||||
config.set('userManagement.isInstanceOwnerSetUp', true);
|
config.set('userManagement.isInstanceOwnerSetUp', true);
|
||||||
config.set('userManagement.emails.mode', 'smtp');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
|
@ -352,7 +347,7 @@ test('GET /workflows should return all workflows for owner', async () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test('GET /workflows/:workflowId should fail due to missing API Key', async () => {
|
test('GET /workflows/:id should fail due to missing API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
||||||
|
|
||||||
owner.apiKey = null;
|
owner.apiKey = null;
|
||||||
|
@ -369,7 +364,7 @@ test('GET /workflows/:workflowId should fail due to missing API Key', async () =
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('GET /workflows/:workflowId should fail due to invalid API Key', async () => {
|
test('GET /workflows/:id should fail due to invalid API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
owner.apiKey = 'abcXYZ';
|
owner.apiKey = 'abcXYZ';
|
||||||
|
@ -386,7 +381,7 @@ test('GET /workflows/:workflowId should fail due to invalid API Key', async () =
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('GET /workflows/:workflowId should fail due to non existing workflow', async () => {
|
test('GET /workflows/:id should fail due to non existing workflow', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -401,7 +396,7 @@ test('GET /workflows/:workflowId should fail due to non existing workflow', asyn
|
||||||
expect(response.statusCode).toBe(404);
|
expect(response.statusCode).toBe(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('GET /workflows/:workflowId should retrieve workflow', async () => {
|
test('GET /workflows/:id should retrieve workflow', async () => {
|
||||||
const member = await testDb.createUser({ globalRole: globalMemberRole, apiKey: randomApiKey() });
|
const member = await testDb.createUser({ globalRole: globalMemberRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authAgent = utils.createAgent(app, {
|
const authAgent = utils.createAgent(app, {
|
||||||
|
@ -432,7 +427,7 @@ test('GET /workflows/:workflowId should retrieve workflow', async () => {
|
||||||
expect(updatedAt).toEqual(workflow.updatedAt.toISOString());
|
expect(updatedAt).toEqual(workflow.updatedAt.toISOString());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('GET /workflows/:workflowId should retrieve non-owned workflow for owner', async () => {
|
test('GET /workflows/:id should retrieve non-owned workflow for owner', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
const member = await testDb.createUser({ globalRole: globalMemberRole });
|
const member = await testDb.createUser({ globalRole: globalMemberRole });
|
||||||
|
|
||||||
|
@ -464,7 +459,7 @@ test('GET /workflows/:workflowId should retrieve non-owned workflow for owner',
|
||||||
expect(updatedAt).toEqual(workflow.updatedAt.toISOString());
|
expect(updatedAt).toEqual(workflow.updatedAt.toISOString());
|
||||||
});
|
});
|
||||||
|
|
||||||
test('DELETE /workflows/:workflowId should fail due to missing API Key', async () => {
|
test('DELETE /workflows/:id should fail due to missing API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -479,7 +474,7 @@ test('DELETE /workflows/:workflowId should fail due to missing API Key', async (
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('DELETE /workflows/:workflowId should fail due to invalid API Key', async () => {
|
test('DELETE /workflows/:id should fail due to invalid API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
owner.apiKey = 'abcXYZ';
|
owner.apiKey = 'abcXYZ';
|
||||||
|
@ -496,7 +491,7 @@ test('DELETE /workflows/:workflowId should fail due to invalid API Key', async (
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('DELETE /workflows/:workflowId should fail due to non existing workflow', async () => {
|
test('DELETE /workflows/:id should fail due to non existing workflow', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -511,7 +506,7 @@ test('DELETE /workflows/:workflowId should fail due to non existing workflow', a
|
||||||
expect(response.statusCode).toBe(404);
|
expect(response.statusCode).toBe(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('DELETE /workflows/:workflowId should delete the workflow', async () => {
|
test('DELETE /workflows/:id should delete the workflow', async () => {
|
||||||
const member = await testDb.createUser({ globalRole: globalMemberRole, apiKey: randomApiKey() });
|
const member = await testDb.createUser({ globalRole: globalMemberRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authAgent = utils.createAgent(app, {
|
const authAgent = utils.createAgent(app, {
|
||||||
|
@ -549,7 +544,7 @@ test('DELETE /workflows/:workflowId should delete the workflow', async () => {
|
||||||
expect(sharedWorkflow).toBeUndefined();
|
expect(sharedWorkflow).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('DELETE /workflows/:workflowId should delete non-owned workflow when owner', async () => {
|
test('DELETE /workflows/:id should delete non-owned workflow when owner', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
const member = await testDb.createUser({ globalRole: globalMemberRole });
|
const member = await testDb.createUser({ globalRole: globalMemberRole });
|
||||||
|
|
||||||
|
@ -588,7 +583,7 @@ test('DELETE /workflows/:workflowId should delete non-owned workflow when owner'
|
||||||
expect(sharedWorkflow).toBeUndefined();
|
expect(sharedWorkflow).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('POST /workflows/:workflowId/activate should fail due to missing API Key', async () => {
|
test('POST /workflows/:id/activate should fail due to missing API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -603,7 +598,7 @@ test('POST /workflows/:workflowId/activate should fail due to missing API Key',
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('POST /workflows/:workflowId/activate should fail due to invalid API Key', async () => {
|
test('POST /workflows/:id/activate should fail due to invalid API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
owner.apiKey = 'abcXYZ';
|
owner.apiKey = 'abcXYZ';
|
||||||
|
@ -620,7 +615,7 @@ test('POST /workflows/:workflowId/activate should fail due to invalid API Key',
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('POST /workflows/:workflowId/activate should fail due to non existing workflow', async () => {
|
test('POST /workflows/:id/activate should fail due to non existing workflow', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -635,7 +630,7 @@ test('POST /workflows/:workflowId/activate should fail due to non existing workf
|
||||||
expect(response.statusCode).toBe(404);
|
expect(response.statusCode).toBe(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('POST /workflows/:workflowId/activate should fail due to trying to activate a workflow without a trigger', async () => {
|
test('POST /workflows/:id/activate should fail due to trying to activate a workflow without a trigger', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -652,7 +647,7 @@ test('POST /workflows/:workflowId/activate should fail due to trying to activate
|
||||||
expect(response.statusCode).toBe(400);
|
expect(response.statusCode).toBe(400);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('POST /workflows/:workflowId/activate should set workflow as active', async () => {
|
test.skip('POST /workflows/:id/activate should set workflow as active', async () => {
|
||||||
const member = await testDb.createUser({ globalRole: globalMemberRole, apiKey: randomApiKey() });
|
const member = await testDb.createUser({ globalRole: globalMemberRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authAgent = utils.createAgent(app, {
|
const authAgent = utils.createAgent(app, {
|
||||||
|
@ -696,7 +691,7 @@ test.skip('POST /workflows/:workflowId/activate should set workflow as active',
|
||||||
expect(await workflowRunner.isActive(workflow.id.toString())).toBe(true);
|
expect(await workflowRunner.isActive(workflow.id.toString())).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip('POST /workflows/:workflowId/activate should set non-owned workflow as active when owner', async () => {
|
test.skip('POST /workflows/:id/activate should set non-owned workflow as active when owner', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
const member = await testDb.createUser({ globalRole: globalMemberRole });
|
const member = await testDb.createUser({ globalRole: globalMemberRole });
|
||||||
|
|
||||||
|
@ -750,7 +745,7 @@ test.skip('POST /workflows/:workflowId/activate should set non-owned workflow as
|
||||||
expect(await workflowRunner.isActive(workflow.id.toString())).toBe(true);
|
expect(await workflowRunner.isActive(workflow.id.toString())).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('POST /workflows/:workflowId/deactivate should fail due to missing API Key', async () => {
|
test('POST /workflows/:id/deactivate should fail due to missing API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -765,7 +760,7 @@ test('POST /workflows/:workflowId/deactivate should fail due to missing API Key'
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('POST /workflows/:workflowId/deactivate should fail due to invalid API Key', async () => {
|
test('POST /workflows/:id/deactivate should fail due to invalid API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
owner.apiKey = 'abcXYZ';
|
owner.apiKey = 'abcXYZ';
|
||||||
|
@ -782,7 +777,7 @@ test('POST /workflows/:workflowId/deactivate should fail due to invalid API Key'
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('POST /workflows/:workflowId/deactivate should fail due to non existing workflow', async () => {
|
test('POST /workflows/:id/deactivate should fail due to non existing workflow', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -797,7 +792,7 @@ test('POST /workflows/:workflowId/deactivate should fail due to non existing wor
|
||||||
expect(response.statusCode).toBe(404);
|
expect(response.statusCode).toBe(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('POST /workflows/:workflowId/deactivate should deactive workflow', async () => {
|
test('POST /workflows/:id/deactivate should deactive workflow', async () => {
|
||||||
const member = await testDb.createUser({ globalRole: globalMemberRole, apiKey: randomApiKey() });
|
const member = await testDb.createUser({ globalRole: globalMemberRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authAgent = utils.createAgent(app, {
|
const authAgent = utils.createAgent(app, {
|
||||||
|
@ -841,7 +836,7 @@ test('POST /workflows/:workflowId/deactivate should deactive workflow', async ()
|
||||||
expect(await workflowRunner.isActive(workflow.id.toString())).toBe(false);
|
expect(await workflowRunner.isActive(workflow.id.toString())).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('POST /workflows/:workflowId/deactivate should deactive non-owned workflow when owner', async () => {
|
test('POST /workflows/:id/deactivate should deactive non-owned workflow when owner', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
const member = await testDb.createUser({ globalRole: globalMemberRole });
|
const member = await testDb.createUser({ globalRole: globalMemberRole });
|
||||||
|
|
||||||
|
@ -904,7 +899,7 @@ test('POST /workflows should fail due to missing API Key', async () => {
|
||||||
version: 1,
|
version: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await authOwnerAgent.post(`/workflows`);
|
const response = await authOwnerAgent.post('/workflows');
|
||||||
|
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
@ -921,7 +916,7 @@ test('POST /workflows should fail due to invalid API Key', async () => {
|
||||||
version: 1,
|
version: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await authOwnerAgent.post(`/workflows`);
|
const response = await authOwnerAgent.post('/workflows');
|
||||||
|
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
@ -936,7 +931,7 @@ test('POST /workflows should fail due to invalid body', async () => {
|
||||||
version: 1,
|
version: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await authOwnerAgent.post(`/workflows`).send({});
|
const response = await authOwnerAgent.post('/workflows').send({});
|
||||||
|
|
||||||
expect(response.statusCode).toBe(400);
|
expect(response.statusCode).toBe(400);
|
||||||
});
|
});
|
||||||
|
@ -974,7 +969,7 @@ test('POST /workflows should create workflow', async () => {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await authAgent.post(`/workflows`).send(payload);
|
const response = await authAgent.post('/workflows').send(payload);
|
||||||
|
|
||||||
expect(response.statusCode).toBe(200);
|
expect(response.statusCode).toBe(200);
|
||||||
|
|
||||||
|
@ -1005,7 +1000,7 @@ test('POST /workflows should create workflow', async () => {
|
||||||
expect(sharedWorkflow?.role).toEqual(workflowOwnerRole);
|
expect(sharedWorkflow?.role).toEqual(workflowOwnerRole);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PUT /workflows/:workflowId should fail due to missing API Key', async () => {
|
test('PUT /workflows/:id should fail due to missing API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -1020,7 +1015,7 @@ test('PUT /workflows/:workflowId should fail due to missing API Key', async () =
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PUT /workflows/:workflowId should fail due to invalid API Key', async () => {
|
test('PUT /workflows/:id should fail due to invalid API Key', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
owner.apiKey = 'abcXYZ';
|
owner.apiKey = 'abcXYZ';
|
||||||
|
@ -1037,7 +1032,7 @@ test('PUT /workflows/:workflowId should fail due to invalid API Key', async () =
|
||||||
expect(response.statusCode).toBe(401);
|
expect(response.statusCode).toBe(401);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PUT /workflows/:workflowId should fail due to non existing workflow', async () => {
|
test('PUT /workflows/:id should fail due to non existing workflow', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -1073,7 +1068,7 @@ test('PUT /workflows/:workflowId should fail due to non existing workflow', asyn
|
||||||
expect(response.statusCode).toBe(404);
|
expect(response.statusCode).toBe(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PUT /workflows/:workflowId should fail due to invalid body', async () => {
|
test('PUT /workflows/:id should fail due to invalid body', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const authOwnerAgent = utils.createAgent(app, {
|
const authOwnerAgent = utils.createAgent(app, {
|
||||||
|
@ -1108,7 +1103,7 @@ test('PUT /workflows/:workflowId should fail due to invalid body', async () => {
|
||||||
expect(response.statusCode).toBe(400);
|
expect(response.statusCode).toBe(400);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PUT /workflows/:workflowId should update workflow', async () => {
|
test('PUT /workflows/:id should update workflow', async () => {
|
||||||
const member = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const member = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
|
|
||||||
const workflow = await testDb.createWorkflow({}, member);
|
const workflow = await testDb.createWorkflow({}, member);
|
||||||
|
@ -1182,7 +1177,7 @@ test('PUT /workflows/:workflowId should update workflow', async () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PUT /workflows/:workflowId should update non-owned workflow if owner', async () => {
|
test('PUT /workflows/:id should update non-owned workflow if owner', async () => {
|
||||||
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
const owner = await testDb.createUser({ globalRole: globalOwnerRole, apiKey: randomApiKey() });
|
||||||
const member = await testDb.createUser({ globalRole: globalMemberRole });
|
const member = await testDb.createUser({ globalRole: globalMemberRole });
|
||||||
|
|
||||||
|
|
|
@ -323,8 +323,7 @@ export async function createManyExecutions(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a execution in the DB and assigns it to a workflow.
|
* Store a execution in the DB and assign it to a workflow.
|
||||||
* @param user user to assign the workflow to
|
|
||||||
*/
|
*/
|
||||||
export async function createExecution(
|
export async function createExecution(
|
||||||
attributes: Partial<ExecutionEntity> = {},
|
attributes: Partial<ExecutionEntity> = {},
|
||||||
|
@ -346,48 +345,41 @@ export async function createExecution(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a execution in the DB and assigns it to a workflow.
|
* Store a successful execution in the DB and assign it to a workflow.
|
||||||
* @param user user to assign the workflow to
|
|
||||||
*/
|
*/
|
||||||
export async function createSuccessfullExecution(workflow: WorkflowEntity) {
|
export async function createSuccessfulExecution(workflow: WorkflowEntity) {
|
||||||
const execution = await createExecution(
|
return await createExecution(
|
||||||
{
|
{
|
||||||
finished: true,
|
finished: true,
|
||||||
},
|
},
|
||||||
workflow,
|
workflow,
|
||||||
);
|
);
|
||||||
|
|
||||||
return execution;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a execution in the DB and assigns it to a workflow.
|
* Store an error execution in the DB and assign it to a workflow.
|
||||||
* @param user user to assign the workflow to
|
|
||||||
*/
|
*/
|
||||||
export async function createErrorExecution(workflow: WorkflowEntity) {
|
export async function createErrorExecution(workflow: WorkflowEntity) {
|
||||||
const execution = await createExecution(
|
return await createExecution(
|
||||||
{
|
{
|
||||||
finished: false,
|
finished: false,
|
||||||
stoppedAt: new Date(),
|
stoppedAt: new Date(),
|
||||||
},
|
},
|
||||||
workflow,
|
workflow,
|
||||||
);
|
);
|
||||||
return execution;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a execution in the DB and assigns it to a workflow.
|
* Store a waiting execution in the DB and assign it to a workflow.
|
||||||
* @param user user to assign the workflow to
|
|
||||||
*/
|
*/
|
||||||
export async function createWaitingExecution(workflow: WorkflowEntity) {
|
export async function createWaitingExecution(workflow: WorkflowEntity) {
|
||||||
const execution = await createExecution(
|
return await createExecution(
|
||||||
{
|
{
|
||||||
finished: false,
|
finished: false,
|
||||||
waitTill: new Date(),
|
waitTill: new Date(),
|
||||||
},
|
},
|
||||||
workflow,
|
workflow,
|
||||||
);
|
);
|
||||||
return execution;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
@ -417,7 +409,7 @@ export async function createManyWorkflows(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a workflow in the DB (without a trigger) and optionally assigns it to a user.
|
* Store a workflow in the DB (without a trigger) and optionally assign it to a user.
|
||||||
* @param user user to assign the workflow to
|
* @param user user to assign the workflow to
|
||||||
*/
|
*/
|
||||||
export async function createWorkflow(attributes: Partial<WorkflowEntity> = {}, user?: User) {
|
export async function createWorkflow(attributes: Partial<WorkflowEntity> = {}, user?: User) {
|
||||||
|
@ -450,7 +442,7 @@ export async function createWorkflow(attributes: Partial<WorkflowEntity> = {}, u
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Store a workflow in the DB (with a trigger) and optionally assigns it to a user.
|
* Store a workflow in the DB (with a trigger) and optionally assign it to a user.
|
||||||
* @param user user to assign the workflow to
|
* @param user user to assign the workflow to
|
||||||
*/
|
*/
|
||||||
export async function createWorkflowWithTrigger(
|
export async function createWorkflowWithTrigger(
|
||||||
|
@ -487,6 +479,7 @@ export async function createWorkflowWithTrigger(
|
||||||
},
|
},
|
||||||
user,
|
user,
|
||||||
);
|
);
|
||||||
|
|
||||||
return workflow;
|
return workflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,3 +34,12 @@ export type SaveCredentialFunction = (
|
||||||
export type PostgresSchemaSection = {
|
export type PostgresSchemaSection = {
|
||||||
[K in 'host' | 'port' | 'schema' | 'user' | 'password']: { env: string };
|
[K in 'host' | 'port' | 'schema' | 'user' | 'password']: { env: string };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface TriggerTime {
|
||||||
|
mode: string;
|
||||||
|
hour: number;
|
||||||
|
minute: number;
|
||||||
|
dayOfMonth: number;
|
||||||
|
weekeday: number;
|
||||||
|
[key: string]: string | number;
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { randomBytes } from 'crypto';
|
import { randomBytes } from 'crypto';
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
|
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import superagent from 'superagent';
|
import superagent from 'superagent';
|
||||||
import request from 'supertest';
|
import request from 'supertest';
|
||||||
|
@ -7,6 +8,9 @@ import { URL } from 'url';
|
||||||
import bodyParser from 'body-parser';
|
import bodyParser from 'body-parser';
|
||||||
import util from 'util';
|
import util from 'util';
|
||||||
import { createTestAccount } from 'nodemailer';
|
import { createTestAccount } from 'nodemailer';
|
||||||
|
import { set } from 'lodash';
|
||||||
|
import { CronJob } from 'cron';
|
||||||
|
import { BinaryDataManager, UserSettings } from 'n8n-core';
|
||||||
import {
|
import {
|
||||||
ICredentialType,
|
ICredentialType,
|
||||||
IDataObject,
|
IDataObject,
|
||||||
|
@ -19,10 +23,8 @@ import {
|
||||||
ITriggerResponse,
|
ITriggerResponse,
|
||||||
LoggerProxy,
|
LoggerProxy,
|
||||||
} from 'n8n-workflow';
|
} from 'n8n-workflow';
|
||||||
import { BinaryDataManager, UserSettings } from 'n8n-core';
|
|
||||||
import { CronJob } from 'cron';
|
|
||||||
|
|
||||||
import config = require('../../../config');
|
import config from '../../../config';
|
||||||
import { AUTHLESS_ENDPOINTS, PUBLIC_API_REST_PATH_SEGMENT, REST_PATH_SEGMENT } from './constants';
|
import { AUTHLESS_ENDPOINTS, PUBLIC_API_REST_PATH_SEGMENT, REST_PATH_SEGMENT } from './constants';
|
||||||
import { AUTH_COOKIE_NAME } from '../../../src/constants';
|
import { AUTH_COOKIE_NAME } from '../../../src/constants';
|
||||||
import { addRoutes as authMiddleware } from '../../../src/UserManagement/routes';
|
import { addRoutes as authMiddleware } from '../../../src/UserManagement/routes';
|
||||||
|
@ -43,20 +45,17 @@ import { issueJWT } from '../../../src/UserManagement/auth/jwt';
|
||||||
import { getLogger } from '../../../src/Logger';
|
import { getLogger } from '../../../src/Logger';
|
||||||
import { credentialsController } from '../../../src/api/credentials.api';
|
import { credentialsController } from '../../../src/api/credentials.api';
|
||||||
import { loadPublicApiVersions } from '../../../src/PublicApi/';
|
import { loadPublicApiVersions } from '../../../src/PublicApi/';
|
||||||
import type { User } from '../../../src/databases/entities/User';
|
|
||||||
import type { ApiPath, EndpointGroup, PostgresSchemaSection, SmtpTestAccount } from './types';
|
|
||||||
import { Telemetry } from '../../../src/telemetry';
|
|
||||||
import type { N8nApp } from '../../../src/UserManagement/Interfaces';
|
|
||||||
import { set } from 'lodash';
|
|
||||||
interface TriggerTime {
|
|
||||||
mode: string;
|
|
||||||
hour: number;
|
|
||||||
minute: number;
|
|
||||||
dayOfMonth: number;
|
|
||||||
weekeday: number;
|
|
||||||
[key: string]: string | number;
|
|
||||||
}
|
|
||||||
import * as UserManagementMailer from '../../../src/UserManagement/email/UserManagementMailer';
|
import * as UserManagementMailer from '../../../src/UserManagement/email/UserManagementMailer';
|
||||||
|
import type { User } from '../../../src/databases/entities/User';
|
||||||
|
import type {
|
||||||
|
ApiPath,
|
||||||
|
EndpointGroup,
|
||||||
|
PostgresSchemaSection,
|
||||||
|
SmtpTestAccount,
|
||||||
|
TriggerTime,
|
||||||
|
} from './types';
|
||||||
|
import type { N8nApp } from '../../../src/UserManagement/Interfaces';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize a test server.
|
* Initialize a test server.
|
||||||
|
@ -75,7 +74,7 @@ export async function initTestServer({
|
||||||
app: express(),
|
app: express(),
|
||||||
restEndpoint: REST_PATH_SEGMENT,
|
restEndpoint: REST_PATH_SEGMENT,
|
||||||
publicApiEndpoint: PUBLIC_API_REST_PATH_SEGMENT,
|
publicApiEndpoint: PUBLIC_API_REST_PATH_SEGMENT,
|
||||||
...(endpointGroups?.includes('credentials') ? { externalHooks: ExternalHooks() } : {}),
|
externalHooks: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
testServer.app.use(bodyParser.json());
|
testServer.app.use(bodyParser.json());
|
||||||
|
@ -90,13 +89,17 @@ export async function initTestServer({
|
||||||
|
|
||||||
if (!endpointGroups) return testServer.app;
|
if (!endpointGroups) return testServer.app;
|
||||||
|
|
||||||
|
if (endpointGroups.includes('credentials')) {
|
||||||
|
testServer.externalHooks = ExternalHooks();
|
||||||
|
}
|
||||||
|
|
||||||
const [routerEndpoints, functionEndpoints] = classifyEndpointGroups(endpointGroups);
|
const [routerEndpoints, functionEndpoints] = classifyEndpointGroups(endpointGroups);
|
||||||
|
|
||||||
if (routerEndpoints.length) {
|
if (routerEndpoints.length) {
|
||||||
const { apiRouters } = await loadPublicApiVersions(testServer.publicApiEndpoint);
|
const { apiRouters } = await loadPublicApiVersions(testServer.publicApiEndpoint);
|
||||||
const map: Record<string, express.Router | express.Router[]> = {
|
const map: Record<string, express.Router | express.Router[]> = {
|
||||||
credentials: credentialsController,
|
credentials: credentialsController,
|
||||||
publicApi: apiRouters
|
publicApi: apiRouters,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const group of routerEndpoints) {
|
for (const group of routerEndpoints) {
|
||||||
|
@ -743,8 +746,8 @@ export async function initNodeTypes() {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const nodeTypes = NodeTypes();
|
|
||||||
await nodeTypes.init(types);
|
await NodeTypes().init(types);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -759,7 +762,7 @@ export function initTestLogger() {
|
||||||
*/
|
*/
|
||||||
export async function initBinaryManager() {
|
export async function initBinaryManager() {
|
||||||
const binaryDataConfig = config.getEnv('binaryDataManager');
|
const binaryDataConfig = config.getEnv('binaryDataManager');
|
||||||
await BinaryDataManager.init(binaryDataConfig, true);
|
await BinaryDataManager.init(binaryDataConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -783,7 +786,7 @@ export function initConfigFile() {
|
||||||
*/
|
*/
|
||||||
export function createAgent(
|
export function createAgent(
|
||||||
app: express.Application,
|
app: express.Application,
|
||||||
options?: { apiPath?: ApiPath; version?: string | number; auth: boolean; user: User },
|
options?: { auth: boolean; user: User; apiPath?: ApiPath; version?: string | number },
|
||||||
) {
|
) {
|
||||||
const agent = request.agent(app);
|
const agent = request.agent(app);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue