|
|
|
|
@@ -10,17 +10,7 @@ import { mysqlMigrations } from '@db/migrations/mysqldb';
|
|
|
|
|
import { postgresMigrations } from '@db/migrations/postgresdb';
|
|
|
|
|
import { sqliteMigrations } from '@db/migrations/sqlite';
|
|
|
|
|
import { hashPassword } from '@/UserManagement/UserManagementHelper';
|
|
|
|
|
import { DB_INITIALIZATION_TIMEOUT, MAPPING_TABLES, MAPPING_TABLES_TO_CLEAR } from './constants';
|
|
|
|
|
import {
|
|
|
|
|
randomApiKey,
|
|
|
|
|
randomCredentialPayload,
|
|
|
|
|
randomEmail,
|
|
|
|
|
randomName,
|
|
|
|
|
randomString,
|
|
|
|
|
randomValidPassword,
|
|
|
|
|
} from './random';
|
|
|
|
|
import { categorize, getPostgresSchemaSection } from './utils';
|
|
|
|
|
|
|
|
|
|
import { AuthIdentity } from '@/databases/entities/AuthIdentity';
|
|
|
|
|
import { ExecutionEntity } from '@db/entities/ExecutionEntity';
|
|
|
|
|
import { InstalledNodes } from '@db/entities/InstalledNodes';
|
|
|
|
|
import { InstalledPackages } from '@db/entities/InstalledPackages';
|
|
|
|
|
@@ -35,6 +25,10 @@ import type {
|
|
|
|
|
InstalledPackagePayload,
|
|
|
|
|
MappingName,
|
|
|
|
|
} from './types';
|
|
|
|
|
import { DB_INITIALIZATION_TIMEOUT, MAPPING_TABLES, MAPPING_TABLES_TO_CLEAR } from './constants';
|
|
|
|
|
import { randomApiKey, randomEmail, randomName, randomString, randomValidPassword } from './random';
|
|
|
|
|
import { categorize, getPostgresSchemaSection } from './utils';
|
|
|
|
|
|
|
|
|
|
import type { DatabaseType, ICredentialsDb } from '@/Interfaces';
|
|
|
|
|
|
|
|
|
|
export type TestDBType = 'postgres' | 'mysql';
|
|
|
|
|
@@ -103,7 +97,7 @@ export async function terminate() {
|
|
|
|
|
|
|
|
|
|
async function truncateMappingTables(
|
|
|
|
|
dbType: DatabaseType,
|
|
|
|
|
collections: Array<CollectionName>,
|
|
|
|
|
collections: CollectionName[],
|
|
|
|
|
testDb: Connection,
|
|
|
|
|
) {
|
|
|
|
|
const mappingTables = collections.reduce<string[]>((acc, collection) => {
|
|
|
|
|
@@ -115,7 +109,7 @@ async function truncateMappingTables(
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
if (dbType === 'sqlite') {
|
|
|
|
|
const promises = mappingTables.map((tableName) =>
|
|
|
|
|
const promises = mappingTables.map(async (tableName) =>
|
|
|
|
|
testDb.query(
|
|
|
|
|
`DELETE FROM ${tableName}; DELETE FROM sqlite_sequence WHERE name=${tableName};`,
|
|
|
|
|
),
|
|
|
|
|
@@ -152,16 +146,16 @@ async function truncateMappingTables(
|
|
|
|
|
* @param collections Array of entity names whose tables to truncate.
|
|
|
|
|
* @param testDbName Name of the test DB to truncate tables in.
|
|
|
|
|
*/
|
|
|
|
|
export async function truncate(collections: Array<CollectionName>) {
|
|
|
|
|
export async function truncate(collections: CollectionName[]) {
|
|
|
|
|
const dbType = config.getEnv('database.type');
|
|
|
|
|
const testDb = Db.getConnection();
|
|
|
|
|
|
|
|
|
|
if (dbType === 'sqlite') {
|
|
|
|
|
await testDb.query('PRAGMA foreign_keys=OFF');
|
|
|
|
|
|
|
|
|
|
const truncationPromises = collections.map((collection) => {
|
|
|
|
|
const truncationPromises = collections.map(async (collection) => {
|
|
|
|
|
const tableName = toTableName(collection);
|
|
|
|
|
Db.collections[collection].clear();
|
|
|
|
|
// Db.collections[collection].clear();
|
|
|
|
|
return testDb.query(
|
|
|
|
|
`DELETE FROM ${tableName}; DELETE FROM sqlite_sequence WHERE name=${tableName};`,
|
|
|
|
|
);
|
|
|
|
|
@@ -200,7 +194,7 @@ export async function truncate(collections: Array<CollectionName>) {
|
|
|
|
|
|
|
|
|
|
const hasIdColumn = await testDb
|
|
|
|
|
.query(`SHOW COLUMNS FROM ${tableName}`)
|
|
|
|
|
.then((columns: { Field: string }[]) => columns.find((c) => c.Field === 'id'));
|
|
|
|
|
.then((columns: Array<{ Field: string }>) => columns.find((c) => c.Field === 'id'));
|
|
|
|
|
|
|
|
|
|
if (!hasIdColumn) continue;
|
|
|
|
|
|
|
|
|
|
@@ -218,18 +212,20 @@ function toTableName(sourceName: CollectionName | MappingName) {
|
|
|
|
|
if (isMapping(sourceName)) return MAPPING_TABLES[sourceName];
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
AuthIdentity: 'auth_identity',
|
|
|
|
|
AuthProviderSyncHistory: 'auth_provider_sync_history',
|
|
|
|
|
Credentials: 'credentials_entity',
|
|
|
|
|
Workflow: 'workflow_entity',
|
|
|
|
|
Execution: 'execution_entity',
|
|
|
|
|
Tag: 'tag_entity',
|
|
|
|
|
Webhook: 'webhook_entity',
|
|
|
|
|
InstalledNodes: 'installed_nodes',
|
|
|
|
|
InstalledPackages: 'installed_packages',
|
|
|
|
|
Role: 'role',
|
|
|
|
|
User: 'user',
|
|
|
|
|
Settings: 'settings',
|
|
|
|
|
SharedCredentials: 'shared_credentials',
|
|
|
|
|
SharedWorkflow: 'shared_workflow',
|
|
|
|
|
Settings: 'settings',
|
|
|
|
|
InstalledPackages: 'installed_packages',
|
|
|
|
|
InstalledNodes: 'installed_nodes',
|
|
|
|
|
Tag: 'tag_entity',
|
|
|
|
|
User: 'user',
|
|
|
|
|
Webhook: 'webhook_entity',
|
|
|
|
|
Workflow: 'workflow_entity',
|
|
|
|
|
WorkflowStatistics: 'workflow_statistics',
|
|
|
|
|
EventDestinations: 'event_destinations',
|
|
|
|
|
}[sourceName];
|
|
|
|
|
@@ -243,7 +239,7 @@ function toTableName(sourceName: CollectionName | MappingName) {
|
|
|
|
|
* Save a credential to the test DB, sharing it with a user.
|
|
|
|
|
*/
|
|
|
|
|
export async function saveCredential(
|
|
|
|
|
credentialPayload: CredentialPayload = randomCredentialPayload(),
|
|
|
|
|
credentialPayload: CredentialPayload,
|
|
|
|
|
{ user, role }: { user: User; role: Role },
|
|
|
|
|
) {
|
|
|
|
|
const newCredential = new CredentialsEntity();
|
|
|
|
|
@@ -280,7 +276,7 @@ export async function shareCredentialWithUsers(credential: CredentialsEntity, us
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function affixRoleToSaveCredential(role: Role) {
|
|
|
|
|
return (credentialPayload: CredentialPayload, { user }: { user: User }) =>
|
|
|
|
|
return async (credentialPayload: CredentialPayload, { user }: { user: User }) =>
|
|
|
|
|
saveCredential(credentialPayload, { user, role });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -293,7 +289,7 @@ export function affixRoleToSaveCredential(role: Role) {
|
|
|
|
|
*/
|
|
|
|
|
export async function createUser(attributes: Partial<User> = {}): Promise<User> {
|
|
|
|
|
const { email, password, firstName, lastName, globalRole, ...rest } = attributes;
|
|
|
|
|
const user = {
|
|
|
|
|
const user: Partial<User> = {
|
|
|
|
|
email: email ?? randomEmail(),
|
|
|
|
|
password: await hashPassword(password ?? randomValidPassword()),
|
|
|
|
|
firstName: firstName ?? randomName(),
|
|
|
|
|
@@ -305,11 +301,17 @@ export async function createUser(attributes: Partial<User> = {}): Promise<User>
|
|
|
|
|
return Db.collections.User.save(user);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function createLdapUser(attributes: Partial<User>, ldapId: string): Promise<User> {
|
|
|
|
|
const user = await createUser(attributes);
|
|
|
|
|
await Db.collections.AuthIdentity.save(AuthIdentity.create(user, ldapId, 'ldap'));
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function createOwner() {
|
|
|
|
|
return createUser({ globalRole: await getGlobalOwnerRole() });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function createUserShell(globalRole: Role): Promise<User> {
|
|
|
|
|
export async function createUserShell(globalRole: Role): Promise<User> {
|
|
|
|
|
if (globalRole.scope !== 'global') {
|
|
|
|
|
throw new Error(`Invalid role received: ${JSON.stringify(globalRole)}`);
|
|
|
|
|
}
|
|
|
|
|
@@ -366,7 +368,7 @@ export async function saveInstalledPackage(
|
|
|
|
|
return savedInstalledPackage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function saveInstalledNode(
|
|
|
|
|
export async function saveInstalledNode(
|
|
|
|
|
installedNodePayload: InstalledNodePayload,
|
|
|
|
|
): Promise<InstalledNodes> {
|
|
|
|
|
const newInstalledNode = new InstalledNodes();
|
|
|
|
|
@@ -376,7 +378,7 @@ export function saveInstalledNode(
|
|
|
|
|
return Db.collections.InstalledNodes.save(newInstalledNode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function addApiKey(user: User): Promise<User> {
|
|
|
|
|
export async function addApiKey(user: User): Promise<User> {
|
|
|
|
|
user.apiKey = randomApiKey();
|
|
|
|
|
return Db.collections.User.save(user);
|
|
|
|
|
}
|
|
|
|
|
@@ -385,42 +387,42 @@ export function addApiKey(user: User): Promise<User> {
|
|
|
|
|
// role fetchers
|
|
|
|
|
// ----------------------------------
|
|
|
|
|
|
|
|
|
|
export function getGlobalOwnerRole() {
|
|
|
|
|
export async function getGlobalOwnerRole() {
|
|
|
|
|
return Db.collections.Role.findOneByOrFail({
|
|
|
|
|
name: 'owner',
|
|
|
|
|
scope: 'global',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getGlobalMemberRole() {
|
|
|
|
|
export async function getGlobalMemberRole() {
|
|
|
|
|
return Db.collections.Role.findOneByOrFail({
|
|
|
|
|
name: 'member',
|
|
|
|
|
scope: 'global',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getWorkflowOwnerRole() {
|
|
|
|
|
export async function getWorkflowOwnerRole() {
|
|
|
|
|
return Db.collections.Role.findOneByOrFail({
|
|
|
|
|
name: 'owner',
|
|
|
|
|
scope: 'workflow',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getWorkflowEditorRole() {
|
|
|
|
|
export async function getWorkflowEditorRole() {
|
|
|
|
|
return Db.collections.Role.findOneByOrFail({
|
|
|
|
|
name: 'editor',
|
|
|
|
|
scope: 'workflow',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getCredentialOwnerRole() {
|
|
|
|
|
export async function getCredentialOwnerRole() {
|
|
|
|
|
return Db.collections.Role.findOneByOrFail({
|
|
|
|
|
name: 'owner',
|
|
|
|
|
scope: 'credential',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getAllRoles() {
|
|
|
|
|
export async function getAllRoles() {
|
|
|
|
|
return Promise.all([
|
|
|
|
|
getGlobalOwnerRole(),
|
|
|
|
|
getGlobalMemberRole(),
|
|
|
|
|
@@ -429,6 +431,17 @@ export function getAllRoles() {
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const getAllUsers = async () =>
|
|
|
|
|
Db.collections.User.find({
|
|
|
|
|
relations: ['globalRole', 'authIdentities'],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export const getLdapIdentities = async () =>
|
|
|
|
|
Db.collections.AuthIdentity.find({
|
|
|
|
|
where: { providerType: 'ldap' },
|
|
|
|
|
relations: ['user'],
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// ----------------------------------
|
|
|
|
|
// Execution helpers
|
|
|
|
|
// ----------------------------------
|
|
|
|
|
@@ -438,17 +451,14 @@ export async function createManyExecutions(
|
|
|
|
|
workflow: WorkflowEntity,
|
|
|
|
|
callback: (workflow: WorkflowEntity) => Promise<ExecutionEntity>,
|
|
|
|
|
) {
|
|
|
|
|
const executionsRequests = [...Array(amount)].map((_) => callback(workflow));
|
|
|
|
|
const executionsRequests = [...Array(amount)].map(async (_) => callback(workflow));
|
|
|
|
|
return Promise.all(executionsRequests);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Store a execution in the DB and assign it to a workflow.
|
|
|
|
|
*/
|
|
|
|
|
export async function createExecution(
|
|
|
|
|
attributes: Partial<ExecutionEntity> = {},
|
|
|
|
|
workflow: WorkflowEntity,
|
|
|
|
|
) {
|
|
|
|
|
async function createExecution(attributes: Partial<ExecutionEntity>, workflow: WorkflowEntity) {
|
|
|
|
|
const { data, finished, mode, startedAt, stoppedAt, waitTill } = attributes;
|
|
|
|
|
|
|
|
|
|
const execution = await Db.collections.Execution.save({
|
|
|
|
|
@@ -468,38 +478,21 @@ export async function createExecution(
|
|
|
|
|
* Store a successful execution in the DB and assign it to a workflow.
|
|
|
|
|
*/
|
|
|
|
|
export async function createSuccessfulExecution(workflow: WorkflowEntity) {
|
|
|
|
|
return await createExecution(
|
|
|
|
|
{
|
|
|
|
|
finished: true,
|
|
|
|
|
},
|
|
|
|
|
workflow,
|
|
|
|
|
);
|
|
|
|
|
return createExecution({ finished: true }, workflow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Store an error execution in the DB and assign it to a workflow.
|
|
|
|
|
*/
|
|
|
|
|
export async function createErrorExecution(workflow: WorkflowEntity) {
|
|
|
|
|
return await createExecution(
|
|
|
|
|
{
|
|
|
|
|
finished: false,
|
|
|
|
|
stoppedAt: new Date(),
|
|
|
|
|
},
|
|
|
|
|
workflow,
|
|
|
|
|
);
|
|
|
|
|
return createExecution({ finished: false, stoppedAt: new Date() }, workflow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Store a waiting execution in the DB and assign it to a workflow.
|
|
|
|
|
*/
|
|
|
|
|
export async function createWaitingExecution(workflow: WorkflowEntity) {
|
|
|
|
|
return await createExecution(
|
|
|
|
|
{
|
|
|
|
|
finished: false,
|
|
|
|
|
waitTill: new Date(),
|
|
|
|
|
},
|
|
|
|
|
workflow,
|
|
|
|
|
);
|
|
|
|
|
return createExecution({ finished: false, waitTill: new Date() }, workflow);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----------------------------------
|
|
|
|
|
@@ -509,7 +502,7 @@ export async function createWaitingExecution(workflow: WorkflowEntity) {
|
|
|
|
|
export async function createTag(attributes: Partial<TagEntity> = {}) {
|
|
|
|
|
const { name } = attributes;
|
|
|
|
|
|
|
|
|
|
return await Db.collections.Tag.save({
|
|
|
|
|
return Db.collections.Tag.save({
|
|
|
|
|
name: name ?? randomName(),
|
|
|
|
|
...attributes,
|
|
|
|
|
});
|
|
|
|
|
@@ -524,7 +517,7 @@ export async function createManyWorkflows(
|
|
|
|
|
attributes: Partial<WorkflowEntity> = {},
|
|
|
|
|
user?: User,
|
|
|
|
|
) {
|
|
|
|
|
const workflowRequests = [...Array(amount)].map((_) => createWorkflow(attributes, user));
|
|
|
|
|
const workflowRequests = [...Array(amount)].map(async (_) => createWorkflow(attributes, user));
|
|
|
|
|
return Promise.all(workflowRequests);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -653,7 +646,7 @@ const baseOptions = (type: TestDBType) => ({
|
|
|
|
|
port: config.getEnv(`database.${type}db.port`),
|
|
|
|
|
username: config.getEnv(`database.${type}db.user`),
|
|
|
|
|
password: config.getEnv(`database.${type}db.password`),
|
|
|
|
|
schema: type === 'postgres' ? config.getEnv(`database.postgresdb.schema`) : undefined,
|
|
|
|
|
schema: type === 'postgres' ? config.getEnv('database.postgresdb.schema') : undefined,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|