refactor: Upgrade typeorm to 0.3.x (#5151)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™ 2023-01-13 18:12:22 +01:00 committed by GitHub
parent 6608e69457
commit 0a5ab560b1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
85 changed files with 579 additions and 636 deletions

View file

@ -161,7 +161,7 @@
"lodash.uniqby": "^4.7.0",
"lodash.unset": "^4.5.2",
"luxon": "^3.1.0",
"mysql2": "~2.3.0",
"mysql2": "~2.3.3",
"n8n-core": "~0.151.0",
"n8n-editor-ui": "~0.177.0",
"n8n-nodes-base": "~0.209.0",
@ -175,7 +175,7 @@
"passport": "^0.6.0",
"passport-cookie": "^1.0.9",
"passport-jwt": "^4.0.0",
"pg": "^8.3.0",
"pg": "^8.8.0",
"picocolors": "^1.0.0",
"posthog-node": "^2.2.2",
"prom-client": "^13.1.0",
@ -184,12 +184,12 @@
"semver": "^7.3.8",
"shelljs": "^0.8.5",
"source-map-support": "^0.5.21",
"sqlite3": "^5.1.2",
"sqlite3": "^5.1.4",
"sse-channel": "^4.0.0",
"swagger-ui-express": "^4.3.0",
"syslog-client": "^1.1.1",
"tslib": "1.14.1",
"typeorm": "0.2.45",
"typeorm": "0.3.11",
"uuid": "^8.3.2",
"validator": "13.7.0",
"winston": "^3.3.3",

View file

@ -6,7 +6,6 @@ import bodyParser from 'body-parser';
import bodyParserXml from 'body-parser-xml';
import compression from 'compression';
import parseUrl from 'parseurl';
import { getConnectionManager } from 'typeorm';
import type { RedisOptions } from 'ioredis';
import {
@ -162,10 +161,10 @@ export abstract class AbstractServer {
this.app.get('/healthz', async (req, res) => {
Logger.debug('Health check started!');
const connection = getConnectionManager().get();
const connection = Db.getConnection();
try {
if (!connection.isConnected) {
if (!connection.isInitialized) {
// Connection is not active
throw new ServiceUnavailableError('No active database connection!');
}

View file

@ -200,18 +200,18 @@ export class ActiveWorkflowRunner {
path = path.slice(0, -1);
}
let webhook = await Db.collections.Webhook.findOne({
let webhook = await Db.collections.Webhook.findOneBy({
webhookPath: path,
method: httpMethod,
});
let webhookId: string | undefined;
// check if path is dynamic
if (webhook === undefined) {
if (webhook === null) {
// check if a dynamic webhook path exists
const pathElements = path.split('/');
webhookId = pathElements.shift();
const dynamicWebhooks = await Db.collections.Webhook.find({
const dynamicWebhooks = await Db.collections.Webhook.findBy({
webhookId,
method: httpMethod,
pathLength: pathElements.length,
@ -243,7 +243,7 @@ export class ActiveWorkflowRunner {
webhook = dynamicWebhook;
}
});
if (webhook === undefined) {
if (webhook === null) {
throw new ResponseHelper.NotFoundError(
`The requested webhook "${httpMethod} ${path}" is not registered.`,
WEBHOOK_PROD_UNREGISTERED_HINT,
@ -263,10 +263,11 @@ export class ActiveWorkflowRunner {
});
}
const workflowData = await Db.collections.Workflow.findOne(webhook.workflowId, {
const workflowData = await Db.collections.Workflow.findOne({
where: { id: webhook.workflowId },
relations: ['shared', 'shared.user', 'shared.user.globalRole'],
});
if (workflowData === undefined) {
if (workflowData === null) {
throw new ResponseHelper.NotFoundError(
`Could not find workflow with id "${webhook.workflowId}"`,
);
@ -331,20 +332,19 @@ export class ActiveWorkflowRunner {
/**
* Gets all request methods associated with a single webhook
*
* @param {string} path webhook path
*/
async getWebhookMethods(path: string): Promise<string[]> {
const webhooks = await Db.collections.Webhook.find({ webhookPath: path });
const webhooks = await Db.collections.Webhook.find({
select: ['method'],
where: { webhookPath: path },
});
// Gather all request methods in string array
const webhookMethods: string[] = webhooks.map((webhook) => webhook.method);
return webhookMethods;
return webhooks.map((webhook) => webhook.method);
}
/**
* Returns the ids of the currently active workflows
*
*/
async getActiveWorkflows(user?: User): Promise<IWorkflowDb[]> {
let activeWorkflows: WorkflowEntity[] = [];
@ -378,7 +378,10 @@ export class ActiveWorkflowRunner {
* @param {string} id The id of the workflow to check
*/
async isActive(id: string): Promise<boolean> {
const workflow = await Db.collections.Workflow.findOne(id);
const workflow = await Db.collections.Workflow.findOne({
select: ['active'],
where: { id },
});
return !!workflow?.active;
}
@ -434,6 +437,7 @@ export class ActiveWorkflowRunner {
try {
// eslint-disable-next-line no-await-in-loop
// TODO: this should happen in a transaction, that way we don't need to manually remove this in `catch`
await Db.collections.Webhook.insert(webhook);
const webhookExists = await workflow.runWebhookMethod(
'checkExists',
@ -503,10 +507,11 @@ export class ActiveWorkflowRunner {
*
*/
async removeWorkflowWebhooks(workflowId: string): Promise<void> {
const workflowData = await Db.collections.Workflow.findOne(workflowId, {
const workflowData = await Db.collections.Workflow.findOne({
where: { id: workflowId },
relations: ['shared', 'shared.user', 'shared.user.globalRole'],
});
if (workflowData === undefined) {
if (workflowData === null) {
throw new Error(`Could not find workflow with id "${workflowId}"`);
}
@ -772,7 +777,8 @@ export class ActiveWorkflowRunner {
let workflowInstance: Workflow;
try {
if (workflowData === undefined) {
workflowData = (await Db.collections.Workflow.findOne(workflowId, {
workflowData = (await Db.collections.Workflow.findOne({
where: { id: workflowId },
relations: ['shared', 'shared.user', 'shared.user.globalRole'],
})) as IWorkflowDb;
}
@ -883,7 +889,7 @@ export class ActiveWorkflowRunner {
/**
* Add a workflow to the activation queue.
* Meaning it will keep on trying to activate it in regular
* amounts indefinetly.
* amounts indefinitely.
*/
addQueuedWorkflowActivation(
activationMode: WorkflowActivateMode,
@ -962,6 +968,7 @@ export class ActiveWorkflowRunner {
*
* @param {string} workflowId The id of the workflow to deactivate
*/
// TODO: this should happen in a transaction
async remove(workflowId: string): Promise<void> {
if (this.activeWorkflows !== null) {
// Remove all the webhooks of the workflow

View file

@ -3,10 +3,11 @@ import * as Db from '@/Db';
import { InstalledNodes } from '@db/entities/InstalledNodes';
import { InstalledPackages } from '@db/entities/InstalledPackages';
export async function findInstalledPackage(
packageName: string,
): Promise<InstalledPackages | undefined> {
return Db.collections.InstalledPackages.findOne(packageName, { relations: ['installedNodes'] });
export async function findInstalledPackage(packageName: string): Promise<InstalledPackages | null> {
return Db.collections.InstalledPackages.findOne({
where: { packageName },
relations: ['installedNodes'],
});
}
export async function isPackageInstalled(packageName: string): Promise<boolean> {

View file

@ -270,7 +270,7 @@ export class CredentialsHelper extends ICredentialsHelper {
relations: ['credentials'],
where: { credentials: { id: nodeCredential.id, type }, userId },
}).then((shared) => shared.credentials)
: await Db.collections.Credentials.findOneOrFail({ id: nodeCredential.id, type });
: await Db.collections.Credentials.findOneByOrFail({ id: nodeCredential.id, type });
if (!credential) {
throw new Error(
@ -765,8 +765,8 @@ export async function getCredentialForUser(
*/
export async function getCredentialWithoutUser(
credentialId: string,
): Promise<ICredentialsDb | undefined> {
return Db.collections.Credentials.findOne(credentialId);
): Promise<ICredentialsDb | null> {
return Db.collections.Credentials.findOneBy({ id: credentialId });
}
export function createCredentialsFromCredentialsEntity(

View file

@ -4,12 +4,10 @@
/* eslint-disable no-case-declarations */
/* eslint-disable @typescript-eslint/naming-convention */
import {
Connection,
ConnectionOptions,
createConnection,
DataSource as Connection,
DataSourceOptions as ConnectionOptions,
EntityManager,
EntityTarget,
getRepository,
LoggerOptions,
ObjectLiteral,
Repository,
@ -34,6 +32,8 @@ export const collections = {} as IDatabaseCollections;
export let connection: Connection;
export const getConnection = () => connection!;
export async function transaction<T>(fn: (entityManager: EntityManager) => Promise<T>): Promise<T> {
return connection.transaction(fn);
}
@ -41,7 +41,7 @@ export async function transaction<T>(fn: (entityManager: EntityManager) => Promi
export function linkRepository<Entity extends ObjectLiteral>(
entityClass: EntityTarget<Entity>,
): Repository<Entity> {
return getRepository(entityClass, connection.name);
return connection.getRepository(entityClass);
}
export async function getConnectionOptions(dbType: DatabaseType): Promise<ConnectionOptions> {
@ -124,7 +124,8 @@ export async function init(
migrationsTransactionMode: 'each',
});
connection = await createConnection(connectionOptions);
connection = new Connection(connectionOptions);
await connection.initialize();
if (!testConnectionOptions && dbType === 'sqlite') {
// This specific migration changes database metadata.
@ -146,8 +147,9 @@ export async function init(
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (migrations.length === 0) {
await connection.close();
connection = await createConnection(connectionOptions);
await connection.destroy();
connection = new Connection(connectionOptions);
await connection.initialize();
}
}

View file

@ -86,7 +86,7 @@ async function createApiRouter(
_scopes: unknown,
schema: OpenAPIV3.ApiKeySecurityScheme,
): Promise<boolean> => {
const apiKey = req.headers[schema.name.toLowerCase()];
const apiKey = req.headers[schema.name.toLowerCase()] as string;
const user = await Db.collections.User.findOne({
where: { apiKey },
relations: ['globalRole'],

View file

@ -1,4 +1,3 @@
import type { FindConditions } from 'typeorm';
import { UserSettings, Credentials } from 'n8n-core';
import { IDataObject, INodeProperties, INodePropertyOptions } from 'n8n-workflow';
import * as Db from '@/Db';
@ -10,17 +9,22 @@ import { ExternalHooks } from '@/ExternalHooks';
import { IDependency, IJsonSchema } from '../../../types';
import { CredentialRequest } from '@/requests';
export async function getCredentials(credentialId: string): Promise<ICredentialsDb | undefined> {
return Db.collections.Credentials.findOne(credentialId);
export async function getCredentials(credentialId: string): Promise<ICredentialsDb | null> {
return Db.collections.Credentials.findOneBy({ id: credentialId });
}
export async function getSharedCredentials(
userId: string,
credentialId: string,
relations?: string[],
): Promise<SharedCredentials | undefined> {
const where: FindConditions<SharedCredentials> = { userId, credentialsId: credentialId };
return Db.collections.SharedCredentials.findOne({ where, relations });
): Promise<SharedCredentials | null> {
return Db.collections.SharedCredentials.findOne({
where: {
userId,
credentialsId: credentialId,
},
relations,
});
}
export async function createCredential(
@ -53,7 +57,7 @@ export async function saveCredential(
user: User,
encryptedData: ICredentialsDb,
): Promise<CredentialsEntity> {
const role = await Db.collections.Role.findOneOrFail({
const role = await Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'credential',
});

View file

@ -1,13 +1,12 @@
import { parse } from 'flatted';
import { In, Not, Raw, LessThan, IsNull, FindOperator } from 'typeorm';
import { In, Not, Raw, LessThan, IsNull, FindOptionsWhere } from 'typeorm';
import * as Db from '@/Db';
import type { IExecutionFlattedDb, IExecutionResponseApi } from '@/Interfaces';
import { ExecutionEntity } from '@db/entities/ExecutionEntity';
import { ExecutionStatus } from '@/PublicApi/types';
import type { ExecutionStatus } from '@/PublicApi/types';
function prepareExecutionData(
execution: IExecutionFlattedDb | undefined,
execution: IExecutionFlattedDb | null,
): IExecutionResponseApi | undefined {
if (!execution) return undefined;
@ -21,11 +20,10 @@ function prepareExecutionData(
}
function getStatusCondition(status: ExecutionStatus) {
const condition: {
finished?: boolean;
waitTill?: FindOperator<ExecutionEntity>;
stoppedAt?: FindOperator<ExecutionEntity>;
} = {};
const condition: Pick<
FindOptionsWhere<IExecutionFlattedDb>,
'finished' | 'waitTill' | 'stoppedAt'
> = {};
if (status === 'success') {
condition.finished = true;
@ -65,12 +63,7 @@ export async function getExecutions(params: {
status?: ExecutionStatus;
excludedExecutionsIds?: string[];
}): Promise<IExecutionResponseApi[]> {
type WhereClause = Record<
string,
string | boolean | FindOperator<string | Partial<ExecutionEntity>>
>;
let where: WhereClause = {};
let where: FindOptionsWhere<IExecutionFlattedDb> = {};
if (params.lastId && params.excludedExecutionsIds?.length) {
where.id = Raw((id) => `${id} < :lastId AND ${id} NOT IN (:...excludedExecutionsIds)`, {

View file

@ -7,7 +7,7 @@ export function isInstanceOwner(user: User): boolean {
}
export async function getWorkflowOwnerRole(): Promise<Role> {
return Db.collections.Role.findOneOrFail({
return Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'workflow',
});

View file

@ -1,6 +1,6 @@
import express from 'express';
import { FindConditions, FindManyOptions, In } from 'typeorm';
import { FindManyOptions, FindOptionsWhere, In } from 'typeorm';
import * as ActiveWorkflowRunner from '@/ActiveWorkflowRunner';
import config from '@/config';
@ -100,7 +100,7 @@ export = {
let workflows: WorkflowEntity[];
let count: number;
const where: FindConditions<WorkflowEntity> = {
const where: FindOptionsWhere<WorkflowEntity> = {
...(active !== undefined && { active }),
};
const query: FindManyOptions<WorkflowEntity> = {

View file

@ -17,7 +17,7 @@ function insertIf(condition: boolean, elements: string[]): string[] {
export async function getSharedWorkflowIds(user: User): Promise<string[]> {
const sharedWorkflows = await Db.collections.SharedWorkflow.find({
where: { user },
where: { userId: user.id },
});
return sharedWorkflows.map(({ workflowId }) => workflowId);
@ -26,10 +26,10 @@ export async function getSharedWorkflowIds(user: User): Promise<string[]> {
export async function getSharedWorkflow(
user: User,
workflowId?: string | undefined,
): Promise<SharedWorkflow | undefined> {
): Promise<SharedWorkflow | null> {
return Db.collections.SharedWorkflow.findOne({
where: {
...(!isInstanceOwner(user) && { user }),
...(!isInstanceOwner(user) && { userId: user.id }),
...(workflowId && { workflowId }),
},
relations: [...insertIf(!config.getEnv('workflowTagsDisabled'), ['workflow.tags']), 'workflow'],
@ -45,14 +45,14 @@ export async function getSharedWorkflows(
): Promise<SharedWorkflow[]> {
return Db.collections.SharedWorkflow.find({
where: {
...(!isInstanceOwner(user) && { user }),
...(!isInstanceOwner(user) && { userId: user.id }),
...(options.workflowIds && { workflowId: In(options.workflowIds) }),
},
...(options.relations && { relations: options.relations }),
});
}
export async function getWorkflowById(id: string): Promise<WorkflowEntity | undefined> {
export async function getWorkflowById(id: string): Promise<WorkflowEntity | null> {
return Db.collections.Workflow.findOne({
where: { id },
});

View file

@ -1246,9 +1246,9 @@ class Server extends AbstractServer {
await queue.stopJob(job);
}
const executionDb = (await Db.collections.Execution.findOne(
req.params.id,
)) as IExecutionFlattedDb;
const executionDb = (await Db.collections.Execution.findOneBy({
id: req.params.id,
})) as IExecutionFlattedDb;
const fullExecutionData = ResponseHelper.unflattenExecutionData(executionDb);
const returnData: IExecutionsStopData = {
@ -1452,9 +1452,10 @@ export async function start(): Promise<void> {
// Set up event handling
initEvents();
const workflow = await Db.collections.Workflow!.findOne({
const workflow = await Db.collections.Workflow.findOne({
select: ['createdAt'],
order: { createdAt: 'ASC' },
where: {},
});
await InternalHooksManager.getInstance().onServerStarted(diagnosticInfo, workflow?.createdAt);
}

View file

@ -1,10 +1,10 @@
/* eslint-disable no-param-reassign */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { EntityManager, getConnection } from 'typeorm';
import type { EntityManager } from 'typeorm';
import { getConnection } from '@/Db';
import { TagEntity } from '@db/entities/TagEntity';
import { ITagToImport, ITagWithCountDb, IWorkflowToImport } from '@/Interfaces';
import type { ITagToImport, ITagWithCountDb, IWorkflowToImport } from '@/Interfaces';
// ----------------------------------
// utils

View file

@ -5,11 +5,11 @@ import {
Workflow,
WorkflowOperationError,
} from 'n8n-workflow';
import { FindConditions, In } from 'typeorm';
import { FindOptionsWhere, In } from 'typeorm';
import * as Db from '@/Db';
import config from '@/config';
import type { SharedCredentials } from '@db/entities/SharedCredentials';
import { getRole, getWorkflowOwner, isSharingEnabled } from './UserManagementHelper';
import { getRoleId, getWorkflowOwner, isSharingEnabled } from './UserManagementHelper';
import { WorkflowsService } from '@/workflows/workflows.services';
import { UserService } from '@/user/user.service';
@ -28,7 +28,8 @@ export class PermissionChecker {
// allow if requesting user is instance owner
const user = await Db.collections.User.findOneOrFail(userId, {
const user = await Db.collections.User.findOneOrFail({
where: { id: userId },
relations: ['globalRole'],
});
@ -47,11 +48,11 @@ export class PermissionChecker {
workflowUserIds = workflowSharings.map((s) => s.userId);
}
const credentialsWhere: FindConditions<SharedCredentials> = { userId: In(workflowUserIds) };
const credentialsWhere: FindOptionsWhere<SharedCredentials> = { userId: In(workflowUserIds) };
if (!isSharingEnabled()) {
// If credential sharing is not enabled, get only credentials owned by this user
credentialsWhere.role = await getRole('credential', 'owner');
credentialsWhere.roleId = await getRoleId('credential', 'owner');
}
const credentialSharings = await Db.collections.SharedCredentials.find({

View file

@ -1,6 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { INode, NodeOperationError, Workflow } from 'n8n-workflow';
import { In } from 'typeorm';
import express from 'express';
import { compare, genSaltSync, hash } from 'bcryptjs';
@ -21,7 +20,7 @@ export async function getWorkflowOwner(workflowId: string): Promise<User> {
const workflowOwnerRole = await RoleService.get({ name: 'owner', scope: 'workflow' });
const sharedWorkflow = await Db.collections.SharedWorkflow.findOneOrFail({
where: { workflowId, role: workflowOwnerRole },
where: { workflowId, roleId: workflowOwnerRole?.id ?? undefined },
relations: ['user', 'user.globalRole'],
});
@ -59,35 +58,26 @@ export function isUserManagementDisabled(): boolean {
);
}
async function getInstanceOwnerRole(): Promise<Role> {
const ownerRole = await Db.collections.Role.findOneOrFail({
where: {
name: 'owner',
scope: 'global',
},
});
return ownerRole;
}
export async function getInstanceOwner(): Promise<User> {
const ownerRole = await getInstanceOwnerRole();
const owner = await Db.collections.User.findOneOrFail({
relations: ['globalRole'],
where: {
globalRole: ownerRole,
},
});
return owner;
}
export async function getRole(scope: Role['scope'], name: Role['name']): Promise<Role> {
export async function getRoleId(scope: Role['scope'], name: Role['name']): Promise<Role['id']> {
return Db.collections.Role.findOneOrFail({
select: ['id'],
where: {
name,
scope,
},
}).then((role) => role.id);
}
export async function getInstanceOwner(): Promise<User> {
const ownerRoleId = await getRoleId('global', 'owner');
const owner = await Db.collections.User.findOneOrFail({
relations: ['globalRole'],
where: {
globalRoleId: ownerRoleId,
},
});
return owner;
}
/**
@ -168,7 +158,8 @@ export function addInviteLinktoUser(user: PublicUser, inviterId: string): Public
}
export async function getUserById(userId: string): Promise<User> {
const user = await Db.collections.User.findOneOrFail(userId, {
const user = await Db.collections.User.findOneOrFail({
where: { id: userId },
relations: ['globalRole'],
});
return user;

View file

@ -36,7 +36,8 @@ export function issueJWT(user: User): JwtToken {
}
export async function resolveJwtContent(jwtPayload: JwtPayload): Promise<User> {
const user = await Db.collections.User.findOne(jwtPayload.id, {
const user = await Db.collections.User.findOne({
where: { id: jwtPayload.id },
relations: ['globalRole'],
});

View file

@ -30,14 +30,12 @@ export function authenticationMethods(this: N8nApp): void {
throw new Error('Password is required to log in');
}
let user: User | undefined;
let user: User | null;
try {
user = await Db.collections.User.findOne(
{ email },
{
relations: ['globalRole'],
},
);
user = await Db.collections.User.findOne({
where: { email },
relations: ['globalRole'],
});
} catch (error) {
throw new Error('Unable to access database.');
}
@ -77,7 +75,7 @@ export function authenticationMethods(this: N8nApp): void {
}
try {
user = await Db.collections.User.findOneOrFail({ relations: ['globalRole'] });
user = await Db.collections.User.findOneOrFail({ relations: ['globalRole'], where: {} });
} catch (error) {
throw new ResponseHelper.InternalServerError(
'No users found in database - did you wipe the users table? Create at least one user.',

View file

@ -54,12 +54,10 @@ export function addRoutes(this: N8nApp, ignoredEndpoints: string[], restEndpoint
// skip authentication if user management is disabled
if (isUserManagementDisabled()) {
req.user = await Db.collections.User.findOneOrFail(
{},
{
relations: ['globalRole'],
},
);
req.user = await Db.collections.User.findOneOrFail({
relations: ['globalRole'],
where: {},
});
return next();
}

View file

@ -52,8 +52,9 @@ export function ownerNamespace(this: N8nApp): void {
throw new ResponseHelper.BadRequestError('First and last names are mandatory');
}
let owner = await Db.collections.User.findOne(userId, {
let owner = await Db.collections.User.findOne({
relations: ['globalRole'],
where: { id: userId },
});
if (!owner || (owner.globalRole.scope === 'global' && owner.globalRole.name !== 'owner')) {

View file

@ -52,7 +52,7 @@ export function passwordResetNamespace(this: N8nApp): void {
}
// User should just be able to reset password if one is already present
const user = await Db.collections.User.findOne({ email, password: Not(IsNull()) });
const user = await Db.collections.User.findOneBy({ email, password: Not(IsNull()) });
if (!user?.password) {
Logger.debug(
@ -133,7 +133,7 @@ export function passwordResetNamespace(this: N8nApp): void {
// Timestamp is saved in seconds
const currentTimestamp = Math.floor(Date.now() / 1000);
const user = await Db.collections.User.findOne({
const user = await Db.collections.User.findOneBy({
id,
resetPasswordToken,
resetPasswordTokenExpiration: MoreThanOrEqual(currentTimestamp),
@ -184,7 +184,7 @@ export function passwordResetNamespace(this: N8nApp): void {
// Timestamp is saved in seconds
const currentTimestamp = Math.floor(Date.now() / 1000);
const user = await Db.collections.User.findOne({
const user = await Db.collections.User.findOneBy({
id: userId,
resetPasswordToken,
resetPasswordTokenExpiration: MoreThanOrEqual(currentTimestamp),

View file

@ -85,7 +85,7 @@ export function usersNamespace(this: N8nApp): void {
createUsers[invite.email.toLowerCase()] = null;
});
const role = await Db.collections.Role.findOne({ scope: 'global', name: 'member' });
const role = await Db.collections.Role.findOneBy({ scope: 'global', name: 'member' });
if (!role) {
Logger.error(
@ -434,7 +434,7 @@ export function usersNamespace(this: N8nApp): void {
.getRepository(SharedWorkflow)
.find({
select: ['workflowId'],
where: { userId: userToDelete.id, role: workflowOwnerRole },
where: { userId: userToDelete.id, roleId: workflowOwnerRole?.id },
})
.then((sharedWorkflows) => sharedWorkflows.map(({ workflowId }) => workflowId));
@ -459,7 +459,7 @@ export function usersNamespace(this: N8nApp): void {
.getRepository(SharedCredentials)
.find({
select: ['credentialsId'],
where: { user: userToDelete, role: credentialOwnerRole },
where: { userId: userToDelete.id, roleId: credentialOwnerRole?.id },
})
.then((sharedCredentials) =>
sharedCredentials.map(({ credentialsId }) => credentialsId),
@ -495,11 +495,11 @@ export function usersNamespace(this: N8nApp): void {
const [ownedSharedWorkflows, ownedSharedCredentials] = await Promise.all([
Db.collections.SharedWorkflow.find({
relations: ['workflow'],
where: { user: userToDelete, role: workflowOwnerRole },
where: { userId: userToDelete.id, roleId: workflowOwnerRole?.id },
}),
Db.collections.SharedCredentials.find({
relations: ['credentials'],
where: { user: userToDelete, role: credentialOwnerRole },
where: { userId: userToDelete.id, roleId: credentialOwnerRole?.id },
}),
]);
@ -546,7 +546,7 @@ export function usersNamespace(this: N8nApp): void {
);
}
const reinvitee = await Db.collections.User.findOne({ id: idToReinvite });
const reinvitee = await Db.collections.User.findOneBy({ id: idToReinvite });
if (!reinvitee) {
Logger.debug(

View file

@ -107,9 +107,9 @@ export class WaitTrackerClass {
}
// Also check in database
const execution = await Db.collections.Execution.findOne(executionId);
const execution = await Db.collections.Execution.findOneBy({ id: executionId });
if (execution === undefined || !execution.waitTill) {
if (execution === null || !execution.waitTill) {
throw new Error(`The execution ID "${executionId}" could not be found.`);
}
@ -146,9 +146,11 @@ export class WaitTrackerClass {
(async () => {
// Get the data to execute
const fullExecutionDataFlatted = await Db.collections.Execution.findOne(executionId);
const fullExecutionDataFlatted = await Db.collections.Execution.findOneBy({
id: executionId,
});
if (fullExecutionDataFlatted === undefined) {
if (fullExecutionDataFlatted === null) {
throw new Error(`The execution with the id "${executionId}" does not exist.`);
}

View file

@ -41,9 +41,9 @@ export class WaitingWebhooks {
const executionId = pathParts.shift();
const path = pathParts.join('/');
const execution = await Db.collections.Execution.findOne(executionId);
const execution = await Db.collections.Execution.findOneBy({ id: executionId });
if (execution === undefined) {
if (execution === null) {
throw new ResponseHelper.NotFoundError(`The execution "${executionId} does not exist.`);
}

View file

@ -33,7 +33,7 @@ export async function WorkflowCredentials(nodes: INode[]): Promise<IWorkflowCred
if (!returnCredentials[type][nodeCredentials.id]) {
// eslint-disable-next-line no-await-in-loop
foundCredentials = await Db.collections.Credentials.findOne({
foundCredentials = await Db.collections.Credentials.findOneBy({
id: nodeCredentials.id,
type,
});

View file

@ -402,9 +402,9 @@ export function hookFunctionsPreExecute(parentProcessMode?: string): IWorkflowEx
{ executionId: this.executionId, nodeName },
);
const execution = await Db.collections.Execution.findOne(this.executionId);
const execution = await Db.collections.Execution.findOneBy({ id: this.executionId });
if (execution === undefined) {
if (execution === null) {
// Something went badly wrong if this happens.
// This check is here mostly to make typescript happy.
return;
@ -829,7 +829,7 @@ export async function getWorkflowData(
);
}
let workflowData: IWorkflowBase | undefined;
let workflowData: IWorkflowBase | null;
if (workflowInfo.id !== undefined) {
if (!Db.isInitialized) {
// The first time executeWorkflow gets called the Database has
@ -845,7 +845,7 @@ export async function getWorkflowData(
throw new Error(`The workflow with the id "${workflowInfo.id}" does not exist.`);
}
} else {
workflowData = workflowInfo.code;
workflowData = workflowInfo.code ?? null;
if (workflowData) {
if (!workflowData.id) {
workflowData.id = parentWorkflowId;

View file

@ -88,7 +88,7 @@ export async function executeErrorWorkflow(
): Promise<void> {
// Wrap everything in try/catch to make sure that no errors bubble up and all get caught here
try {
let workflowData;
let workflowData: WorkflowEntity | null = null;
if (workflowId !== workflowErrorData.workflow.id) {
// To make this code easier to understand, we split it in 2 parts:
// 1) Fetch the owner of the errored workflows and then
@ -99,7 +99,7 @@ export async function executeErrorWorkflow(
const user = await getWorkflowOwner(workflowErrorData.workflow.id!);
if (user.globalRole.name === 'owner') {
workflowData = await Db.collections.Workflow.findOne({ id: workflowId });
workflowData = await Db.collections.Workflow.findOneBy({ id: workflowId });
} else {
const sharedWorkflowData = await Db.collections.SharedWorkflow.findOne({
where: { workflowId, userId: user.id },
@ -110,10 +110,10 @@ export async function executeErrorWorkflow(
}
}
} else {
workflowData = await Db.collections.Workflow.findOne({ id: workflowId });
workflowData = await Db.collections.Workflow.findOneBy({ id: workflowId });
}
if (workflowData === undefined) {
if (workflowData === null) {
// The error workflow could not be found
Logger.error(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
@ -254,21 +254,13 @@ export async function saveStaticDataById(
/**
* Returns the static data of workflow
*
* @param {(string)} workflowId The id of the workflow to get static data of
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export async function getStaticDataById(workflowId: string) {
const workflowData = await Db.collections.Workflow.findOne(workflowId, {
const workflowData = await Db.collections.Workflow.findOne({
select: ['staticData'],
where: { id: workflowId },
});
if (workflowData === undefined) {
return {};
}
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
return workflowData.staticData || {};
return workflowData?.staticData ?? {};
}
/**
@ -312,7 +304,7 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi
credentialsByName[nodeCredentialType] = {};
}
if (credentialsByName[nodeCredentialType][name] === undefined) {
const credentials = await Db.collections.Credentials.find({
const credentials = await Db.collections.Credentials.findBy({
name,
type: nodeCredentialType,
});
@ -348,7 +340,7 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi
// check if credentials for ID-type are not yet cached
if (credentialsById[nodeCredentialType][nodeCredentials.id] === undefined) {
// check first if ID-type combination exists
const credentials = await Db.collections.Credentials.findOne({
const credentials = await Db.collections.Credentials.findOneBy({
id: nodeCredentials.id,
type: nodeCredentialType,
});
@ -362,7 +354,7 @@ export async function replaceInvalidCredentials(workflow: WorkflowEntity): Promi
continue;
}
// no credentials found for ID, check if some exist for name
const credsByName = await Db.collections.Credentials.find({
const credsByName = await Db.collections.Credentials.findBy({
name: nodeCredentials.name,
type: nodeCredentialType,
});
@ -414,14 +406,17 @@ export async function isBelowOnboardingThreshold(user: User): Promise<boolean> {
let belowThreshold = true;
const skippedTypes = ['n8n-nodes-base.start', 'n8n-nodes-base.stickyNote'];
const workflowOwnerRole = await Db.collections.Role.findOne({
name: 'owner',
scope: 'workflow',
});
const workflowOwnerRoleId = await Db.collections.Role.findOne({
select: ['id'],
where: {
name: 'owner',
scope: 'workflow',
},
}).then((role) => role?.id);
const ownedWorkflowsIds = await Db.collections.SharedWorkflow.find({
where: {
user,
role: workflowOwnerRole,
userId: user.id,
roleId: workflowOwnerRoleId,
},
select: ['workflowId'],
}).then((ownedWorkflows) => ownedWorkflows.map(({ workflowId }) => workflowId));

View file

@ -527,9 +527,9 @@ export class WorkflowRunner {
reject(error);
}
const executionDb = (await Db.collections.Execution.findOne(
executionId,
)) as IExecutionFlattedDb;
const executionDb = (await Db.collections.Execution.findOneBy({
id: executionId,
})) as IExecutionFlattedDb;
const fullExecutionData = ResponseHelper.unflattenExecutionData(executionDb);
const runData = {
data: fullExecutionData.data,

View file

@ -99,11 +99,14 @@ e2eController.post('/db/setup-owner', bodyParser.json(), async (req, res) => {
}
const globalRole = await Db.collections.Role.findOneOrFail({
name: 'owner',
scope: 'global',
select: ['id'],
where: {
name: 'owner',
scope: 'global',
},
});
const owner = await Db.collections.User.findOneOrFail({ globalRole });
const owner = await Db.collections.User.findOneByOrFail({ globalRoleId: globalRole.id });
await Db.collections.User.update(owner.id, {
email: req.body.email,

View file

@ -1,4 +1,4 @@
import { MoreThanOrEqual } from 'typeorm';
import { FindOperator, MoreThanOrEqual } from 'typeorm';
import { DateUtils } from 'typeorm/util/DateUtils';
import * as Db from '@/Db';
import config from '@/config';
@ -46,7 +46,7 @@ async function getExecutionsInPastDays(days: number) {
return Db.collections.Execution.find({
select: ['workflowData'],
where: {
startedAt: MoreThanOrEqual(utcDate),
startedAt: MoreThanOrEqual(utcDate) as unknown as FindOperator<Date>,
},
});
}

View file

@ -38,12 +38,12 @@ export abstract class BaseCommand extends Command {
};
async getInstanceOwner(): Promise<User> {
const globalRole = await Db.collections.Role.findOneOrFail({
const globalRole = await Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'global',
});
const owner = await Db.collections.User.findOne({ globalRole });
const owner = await Db.collections.User.findOneBy({ globalRoleId: globalRole.id });
if (owner) return owner;
@ -53,6 +53,6 @@ export abstract class BaseCommand extends Command {
await Db.collections.User.save(user);
return Db.collections.User.findOneOrFail({ globalRole });
return Db.collections.User.findOneByOrFail({ globalRoleId: globalRole.id });
}
}

View file

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable no-console */
import { Command, flags } from '@oclif/command';
import { Connection, ConnectionOptions, createConnection } from 'typeorm';
import { DataSource as Connection, DataSourceOptions as ConnectionOptions } from 'typeorm';
import { LoggerProxy } from 'n8n-workflow';
import { getLogger } from '@/Logger';
@ -34,11 +34,12 @@ export class DbRevertMigrationCommand extends Command {
dropSchema: false,
logging: ['query', 'error', 'schema'],
};
connection = await createConnection(connectionOptions);
connection = new Connection(connectionOptions);
await connection.initialize();
await connection.undoLastMigration();
await connection.close();
await connection.destroy();
} catch (error) {
if (connection) await connection.close();
if (connection?.isInitialized) await connection.destroy();
console.error('Error reverting last migration. See log messages for details.');
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument

View file

@ -72,7 +72,7 @@ export class Execute extends Command {
}
let workflowId: string | undefined;
let workflowData: IWorkflowBase | undefined;
let workflowData: IWorkflowBase | null = null;
if (flags.file) {
// Path to workflow is given
try {
@ -91,7 +91,7 @@ export class Execute extends Command {
// Do a basic check if the data in the file looks right
// TODO: Later check with the help of TypeScript data if it is valid or not
if (
workflowData === undefined ||
workflowData === null ||
workflowData.nodes === undefined ||
workflowData.connections === undefined
) {
@ -108,8 +108,8 @@ export class Execute extends Command {
if (flags.id) {
// Id of workflow is given
workflowId = flags.id;
workflowData = await Db.collections.Workflow.findOne(workflowId);
if (workflowData === undefined) {
workflowData = await Db.collections.Workflow.findOneBy({ id: workflowId });
if (workflowData === null) {
console.info(`The workflow with the id "${workflowId}" does not exist.`);
process.exit(1);
}

View file

@ -14,7 +14,7 @@ import { LoggerProxy } from 'n8n-workflow';
import fs from 'fs';
import glob from 'fast-glob';
import { EntityManager, getConnection } from 'typeorm';
import type { EntityManager } from 'typeorm';
import { getLogger } from '@/Logger';
import * as Db from '@/Db';
import { User } from '@db/entities/User';
@ -100,7 +100,7 @@ export class ImportCredentialsCommand extends Command {
totalImported = files.length;
await getConnection().transaction(async (transactionManager) => {
await Db.getConnection().transaction(async (transactionManager) => {
this.transactionManager = transactionManager;
for (const file of files) {
const credential = JSON.parse(fs.readFileSync(file, { encoding: 'utf8' }));
@ -128,7 +128,7 @@ export class ImportCredentialsCommand extends Command {
);
}
await getConnection().transaction(async (transactionManager) => {
await Db.getConnection().transaction(async (transactionManager) => {
this.transactionManager = transactionManager;
for (const credential of credentials) {
if (typeof credential.data === 'object') {
@ -187,7 +187,9 @@ export class ImportCredentialsCommand extends Command {
where: { name: 'owner', scope: 'global' },
});
const owner = await Db.collections.User.findOne({ globalRole: ownerGlobalRole });
const owner =
ownerGlobalRole &&
(await Db.collections.User.findOneBy({ globalRoleId: ownerGlobalRole.id }));
if (!owner) {
throw new Error(`Failed to find owner. ${FIX_INSTRUCTION}`);
@ -197,7 +199,7 @@ export class ImportCredentialsCommand extends Command {
}
private async getAssignee(userId: string) {
const user = await Db.collections.User.findOne(userId);
const user = await Db.collections.User.findOneBy({ id: userId });
if (!user) {
throw new Error(`Failed to find user with ID ${userId}`);

View file

@ -16,7 +16,7 @@ import { INode, INodeCredentialsDetails, LoggerProxy } from 'n8n-workflow';
import fs from 'fs';
import glob from 'fast-glob';
import { UserSettings } from 'n8n-core';
import { EntityManager, getConnection } from 'typeorm';
import type { EntityManager } from 'typeorm';
import { v4 as uuid } from 'uuid';
import { getLogger } from '@/Logger';
import * as Db from '@/Db';
@ -123,7 +123,7 @@ export class ImportWorkflowsCommand extends Command {
totalImported = files.length;
await getConnection().transaction(async (transactionManager) => {
await Db.getConnection().transaction(async (transactionManager) => {
this.transactionManager = transactionManager;
for (const file of files) {
@ -158,7 +158,7 @@ export class ImportWorkflowsCommand extends Command {
totalImported = workflows.length;
await getConnection().transaction(async (transactionManager) => {
await Db.getConnection().transaction(async (transactionManager) => {
this.transactionManager = transactionManager;
for (const workflow of workflows) {
@ -229,7 +229,9 @@ export class ImportWorkflowsCommand extends Command {
where: { name: 'owner', scope: 'global' },
});
const owner = await Db.collections.User.findOne({ globalRole: ownerGlobalRole });
const owner =
ownerGlobalRole &&
(await Db.collections.User.findOneBy({ globalRoleId: ownerGlobalRole?.id }));
if (!owner) {
throw new Error(`Failed to find owner. ${FIX_INSTRUCTION}`);
@ -239,7 +241,7 @@ export class ImportWorkflowsCommand extends Command {
}
private async getAssignee(userId: string) {
const user = await Db.collections.User.findOne(userId);
const user = await Db.collections.User.findOneBy({ id: userId });
if (!user) {
throw new Error(`Failed to find user with ID ${userId}`);

View file

@ -304,7 +304,7 @@ export class Start extends Command {
await UserSettings.getEncryptionKey();
// Load settings from database and set them to config.
const databaseSettings = await Db.collections.Settings.find({ loadOnStartup: true });
const databaseSettings = await Db.collections.Settings.findBy({ loadOnStartup: true });
databaseSettings.forEach((setting) => {
config.set(setting.key, JSON.parse(setting.value));
});

View file

@ -9,23 +9,23 @@ export class Reset extends BaseCommand {
async run(): Promise<void> {
const owner = await this.getInstanceOwner();
const ownerWorkflowRole = await Db.collections.Role.findOneOrFail({
const ownerWorkflowRole = await Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'workflow',
});
const ownerCredentialRole = await Db.collections.Role.findOneOrFail({
const ownerCredentialRole = await Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'credential',
});
await Db.collections.SharedWorkflow.update(
{ user: { id: Not(owner.id) }, role: ownerWorkflowRole },
{ userId: Not(owner.id), roleId: ownerWorkflowRole.id },
{ user: owner },
);
await Db.collections.SharedCredentials.update(
{ user: { id: Not(owner.id) }, role: ownerCredentialRole },
{ userId: Not(owner.id), roleId: ownerCredentialRole.id },
{ user: owner },
);

View file

@ -20,8 +20,6 @@ import {
sleep,
} from 'n8n-workflow';
import { FindOneOptions, getConnectionManager } from 'typeorm';
import { CredentialsOverwrites } from '@/CredentialsOverwrites';
import { CredentialTypes } from '@/CredentialTypes';
import * as Db from '@/Db';
@ -125,7 +123,7 @@ export class Worker extends Command {
async runJob(job: Queue.Job, nodeTypes: INodeTypes): Promise<Queue.JobResponse> {
const { executionId, loadStaticData } = job.data;
const executionDb = await Db.collections.Execution.findOne(executionId);
const executionDb = await Db.collections.Execution.findOneBy({ id: executionId });
if (!executionDb) {
LoggerProxy.error(
@ -145,14 +143,13 @@ export class Worker extends Command {
let { staticData } = currentExecutionDb.workflowData;
if (loadStaticData) {
const findOptions = {
const workflowData = await Db.collections.Workflow.findOne({
select: ['id', 'staticData'],
} as FindOneOptions;
const workflowData = await Db.collections.Workflow.findOne(
currentExecutionDb.workflowData.id,
findOptions,
);
if (workflowData === undefined) {
where: {
id: currentExecutionDb.workflowData.id,
},
});
if (workflowData === null) {
LoggerProxy.error(
'Worker execution failed because workflow could not be found in database.',
{
@ -384,10 +381,10 @@ export class Worker extends Command {
async (req: express.Request, res: express.Response) => {
LoggerProxy.debug('Health check started!');
const connection = getConnectionManager().get();
const connection = Db.getConnection();
try {
if (!connection.isConnected) {
if (!connection.isInitialized) {
// Connection is not active
throw new Error('No active database connection!');
}

View file

@ -177,7 +177,7 @@ credentialsController.patch(
const responseData = await CredentialsService.update(credentialId, newCredentialData);
if (responseData === undefined) {
if (responseData === null) {
throw new ResponseHelper.NotFoundError(
`Credential ID "${credentialId}" could not be found to be updated.`,
);

View file

@ -1,5 +1,5 @@
/* eslint-disable no-param-reassign */
import { DeleteResult, EntityManager, FindConditions, In, Not } from 'typeorm';
import { DeleteResult, EntityManager, FindOptionsWhere, In, Not } from 'typeorm';
import * as Db from '@/Db';
import { RoleService } from '@/role/role.service';
import { CredentialsEntity } from '@db/entities/CredentialsEntity';
@ -33,14 +33,14 @@ export class EECredentialsService extends CredentialsService {
credentialId: string,
relations: string[] = ['credentials'],
{ allowGlobalOwner } = { allowGlobalOwner: true },
): Promise<SharedCredentials | undefined> {
const where: FindConditions<SharedCredentials> = { credentialsId: credentialId };
): Promise<SharedCredentials | null> {
const where: FindOptionsWhere<SharedCredentials> = { credentialsId: credentialId };
// Omit user from where if the requesting user is the global
// owner. This allows the global owner to view and delete
// credentials they don't own.
if (!allowGlobalOwner || user.globalRole.name !== 'owner') {
where.user = { id: user.id };
where.userId = user.id;
}
return Db.collections.SharedCredentials.findOne({
@ -65,7 +65,7 @@ export class EECredentialsService extends CredentialsService {
credentialId: string,
userIds: string[],
): Promise<DeleteResult> {
const conditions: FindConditions<SharedCredentials> = {
const conditions: FindOptionsWhere<SharedCredentials> = {
credentialsId: credentialId,
userId: Not(In(userIds)),
};
@ -86,9 +86,9 @@ export class EECredentialsService extends CredentialsService {
.filter((user) => !user.isPending)
.map((user) =>
Db.collections.SharedCredentials.create({
credentials: credential,
user,
role,
credentialsId: credential.id,
userId: user.id,
roleId: role?.id,
}),
);

View file

@ -10,7 +10,7 @@ import {
LoggerProxy,
NodeHelpers,
} from 'n8n-workflow';
import { FindConditions, FindManyOptions, In } from 'typeorm';
import { FindManyOptions, FindOptionsWhere, In } from 'typeorm';
import * as Db from '@/Db';
import * as ResponseHelper from '@/ResponseHelper';
@ -28,11 +28,12 @@ import { CredentialTypes } from '@/CredentialTypes';
export class CredentialsService {
static async get(
credential: Partial<ICredentialsDb>,
where: FindOptionsWhere<ICredentialsDb>,
options?: { relations: string[] },
): Promise<ICredentialsDb | undefined> {
return Db.collections.Credentials.findOne(credential, {
): Promise<ICredentialsDb | null> {
return Db.collections.Credentials.findOne({
relations: options?.relations,
where,
});
}
@ -88,8 +89,8 @@ export class CredentialsService {
credentialId: string,
relations: string[] = ['credentials'],
{ allowGlobalOwner } = { allowGlobalOwner: true },
): Promise<SharedCredentials | undefined> {
const where: FindConditions<SharedCredentials> = { credentialsId: credentialId };
): Promise<SharedCredentials | null> {
const where: FindOptionsWhere<SharedCredentials> = { credentialsId: credentialId };
// Omit user from where if the requesting user is the global
// owner. This allows the global owner to view and delete
@ -204,7 +205,7 @@ export class CredentialsService {
static async update(
credentialId: string,
newCredentialData: ICredentialsDb,
): Promise<ICredentialsDb | undefined> {
): Promise<ICredentialsDb | null> {
await ExternalHooks().run('credentials.update', [newCredentialData]);
// Update the credentials in DB
@ -212,7 +213,7 @@ export class CredentialsService {
// We sadly get nothing back from "update". Neither if it updated a record
// nor the new value. So query now the updated entry.
return Db.collections.Credentials.findOne(credentialId);
return Db.collections.Credentials.findOneBy({ id: credentialId });
}
static async save(
@ -226,7 +227,7 @@ export class CredentialsService {
await ExternalHooks().run('credentials.create', [encryptedData]);
const role = await Db.collections.Role.findOneOrFail({
const role = await Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'credential',
});

View file

@ -1,10 +1,11 @@
import { Column, Entity, OneToMany, PrimaryGeneratedColumn, Unique } from 'typeorm';
import { Column, Entity, OneToMany, PrimaryColumn, Unique } from 'typeorm';
import { IsString, Length } from 'class-validator';
import type { User } from './User';
import type { SharedWorkflow } from './SharedWorkflow';
import type { SharedCredentials } from './SharedCredentials';
import { AbstractEntity } from './AbstractEntity';
import { idStringifier } from '../utils/transformers';
export type RoleNames = 'owner' | 'member' | 'user' | 'editor';
export type RoleScopes = 'global' | 'workflow' | 'credential';
@ -12,8 +13,8 @@ export type RoleScopes = 'global' | 'workflow' | 'credential';
@Entity()
@Unique(['scope', 'name'])
export class Role extends AbstractEntity {
@PrimaryGeneratedColumn()
id: number;
@PrimaryColumn({ transformer: idStringifier })
id: string;
@Column({ length: 32 })
@IsString({ message: 'Role name must be of type string.' })

View file

@ -1,4 +1,4 @@
import { Entity, ManyToOne, PrimaryColumn, RelationId } from 'typeorm';
import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm';
import type { CredentialsEntity } from './CredentialsEntity';
import type { User } from './User';
import type { Role } from './Role';
@ -10,20 +10,18 @@ export class SharedCredentials extends AbstractEntity {
@ManyToOne('Role', 'sharedCredentials', { nullable: false })
role: Role;
@ManyToOne('User', 'sharedCredentials', { primary: true })
@Column()
roleId: string;
@ManyToOne('User', 'sharedCredentials')
user: User;
@PrimaryColumn()
@RelationId((sharedCredential: SharedCredentials) => sharedCredential.user)
userId: string;
@ManyToOne('CredentialsEntity', 'shared', {
primary: true,
onDelete: 'CASCADE',
})
@ManyToOne('CredentialsEntity', 'shared')
credentials: CredentialsEntity;
@PrimaryColumn({ transformer: idStringifier })
@RelationId((sharedCredential: SharedCredentials) => sharedCredential.credentials)
credentialsId: string;
}

View file

@ -1,4 +1,4 @@
import { Entity, ManyToOne, PrimaryColumn, RelationId } from 'typeorm';
import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm';
import type { WorkflowEntity } from './WorkflowEntity';
import type { User } from './User';
import type { Role } from './Role';
@ -10,20 +10,18 @@ export class SharedWorkflow extends AbstractEntity {
@ManyToOne('Role', 'sharedWorkflows', { nullable: false })
role: Role;
@ManyToOne('User', 'sharedWorkflows', { primary: true })
@Column()
roleId: string;
@ManyToOne('User', 'sharedWorkflows')
user: User;
@PrimaryColumn()
@RelationId((sharedWorkflow: SharedWorkflow) => sharedWorkflow.user)
userId: string;
@ManyToOne('WorkflowEntity', 'shared', {
primary: true,
onDelete: 'CASCADE',
})
@ManyToOne('WorkflowEntity', 'shared')
workflow: WorkflowEntity;
@PrimaryColumn({ transformer: idStringifier })
@RelationId((sharedWorkflow: SharedWorkflow) => sharedWorkflow.workflow)
workflowId: string;
}

View file

@ -74,12 +74,12 @@ export class User extends AbstractEntity implements IUser {
})
settings: IUserSettings | null;
@ManyToOne('Role', 'globalForUsers', {
cascade: true,
nullable: false,
})
@ManyToOne('Role', 'globalForUsers', { nullable: false })
globalRole: Role;
@Column()
globalRoleId: string;
@OneToMany('SharedWorkflow', 'user')
sharedWorkflows: SharedWorkflow[];

View file

@ -1,4 +1,4 @@
import { Column, Entity, RelationId, ManyToOne, PrimaryColumn } from 'typeorm';
import { Column, Entity, ManyToOne, PrimaryColumn } from 'typeorm';
import { idStringifier } from '../utils/transformers';
import { datetimeColumnType } from './AbstractEntity';
import type { WorkflowEntity } from './WorkflowEntity';
@ -22,13 +22,9 @@ export class WorkflowStatistics {
@PrimaryColumn({ length: 128 })
name: StatisticsNames;
@ManyToOne('WorkflowEntity', 'shared', {
primary: true,
onDelete: 'CASCADE',
})
@ManyToOne('WorkflowEntity', 'shared')
workflow: WorkflowEntity;
@PrimaryColumn({ transformer: idStringifier })
@RelationId((workflowStatistics: WorkflowStatistics) => workflowStatistics.workflow)
workflowId: string;
}

View file

@ -1,5 +1,5 @@
import { LoggerProxy, MessageEventBusDestinationOptions } from 'n8n-workflow';
import { DeleteResult } from 'typeorm';
import type { DeleteResult } from 'typeorm';
import { EventMessageTypes } from '../EventMessageClasses/';
import type { MessageEventBusDestination } from '../MessageEventBusDestination/MessageEventBusDestination.ee';
import { MessageEventBusLogWriter } from '../MessageEventBusWriter/MessageEventBusLogWriter';

View file

@ -9,7 +9,7 @@ import * as Db from '@/Db';
import { AbstractEventMessage } from '../EventMessageClasses/AbstractEventMessage';
import { EventMessageTypes } from '../EventMessageClasses';
import { eventBus } from '..';
import { DeleteResult, InsertResult } from 'typeorm';
import type { DeleteResult, InsertResult } from 'typeorm';
export abstract class MessageEventBusDestination implements MessageEventBusDestinationOptions {
// Since you can't have static abstract functions - this just serves as a reminder that you need to implement these. Please.

View file

@ -12,7 +12,7 @@ import {
jsonParse,
Workflow,
} from 'n8n-workflow';
import { FindConditions, FindOperator, In, IsNull, LessThanOrEqual, Not, Raw } from 'typeorm';
import { FindOperator, FindOptionsWhere, In, IsNull, LessThanOrEqual, Not, Raw } from 'typeorm';
import * as ActiveExecutions from '@/ActiveExecutions';
import config from '@/config';
import type { User } from '@/databases/entities/User';
@ -200,7 +200,7 @@ export class ExecutionsService {
.map(({ id }) => id),
);
const findWhere: FindConditions<ExecutionEntity> = { workflowId: In(sharedWorkflowIds) };
const findWhere: FindOptionsWhere<ExecutionEntity> = { workflowId: In(sharedWorkflowIds) };
const rangeQuery: string[] = [];
const rangeQueryParams: {
@ -370,7 +370,9 @@ export class ExecutionsService {
// Loads the currently saved workflow to execute instead of the
// one saved at the time of the execution.
const workflowId = fullExecutionData.workflowData.id as string;
const workflowData = (await Db.collections.Workflow.findOne(workflowId)) as IWorkflowBase;
const workflowData = (await Db.collections.Workflow.findOneBy({
id: workflowId,
})) as IWorkflowBase;
if (workflowData === undefined) {
throw new Error(
@ -453,7 +455,7 @@ export class ExecutionsService {
throw new Error('Either "deleteBefore" or "ids" must be present in the request body');
}
const where: FindConditions<ExecutionEntity> = { workflowId: In(sharedWorkflowIds) };
const where: FindOptionsWhere<ExecutionEntity> = { workflowId: In(sharedWorkflowIds) };
if (deleteBefore) {
// delete executions by date, if user may access the underlying workflows

View file

@ -1,14 +1,14 @@
import { EntityManager } from 'typeorm';
import type { EntityManager, FindOptionsWhere } from 'typeorm';
import * as Db from '@/Db';
import { Role } from '@db/entities/Role';
export class RoleService {
static async get(role: Partial<Role>): Promise<Role | undefined> {
return Db.collections.Role.findOne(role);
static async get(role: FindOptionsWhere<Role>): Promise<Role | null> {
return Db.collections.Role.findOneBy(role);
}
static async trxGet(transaction: EntityManager, role: Partial<Role>) {
return transaction.findOne(Role, role);
static async trxGet(transaction: EntityManager, role: FindOptionsWhere<Role>) {
return transaction.findOneBy(Role, role);
}
static async getUserRoleForWorkflow(userId: string, workflowId: string) {

View file

@ -1,15 +1,16 @@
import { EntityManager, In } from 'typeorm';
import { EntityManager, FindOptionsWhere, In } from 'typeorm';
import * as Db from '@/Db';
import { User } from '@db/entities/User';
export class UserService {
static async get(user: Partial<User>): Promise<User | undefined> {
return Db.collections.User.findOne(user, {
static async get(where: FindOptionsWhere<User>): Promise<User | null> {
return Db.collections.User.findOne({
relations: ['globalRole'],
where,
});
}
static async getByIds(transaction: EntityManager, ids: string[]) {
return transaction.find(User, { id: In(ids) });
return transaction.find(User, { where: { id: In(ids) } });
}
}

View file

@ -17,6 +17,7 @@ import * as TagHelpers from '@/TagHelpers';
import { EECredentialsService as EECredentials } from '../credentials/credentials.service.ee';
import { IExecutionPushResponse } from '@/Interfaces';
import * as GenericHelpers from '@/GenericHelpers';
import { In } from 'typeorm';
// eslint-disable-next-line @typescript-eslint/naming-convention
export const EEWorkflowController = express.Router();
@ -130,8 +131,11 @@ EEWorkflowController.post(
const { tags: tagIds } = req.body;
if (tagIds?.length && !config.getEnv('workflowTagsDisabled')) {
newWorkflow.tags = await Db.collections.Tag.findByIds(tagIds, {
newWorkflow.tags = await Db.collections.Tag.find({
select: ['id', 'name'],
where: {
id: In(tagIds),
},
});
}
@ -157,7 +161,7 @@ EEWorkflowController.post(
await Db.transaction(async (transactionManager) => {
savedWorkflow = await transactionManager.save<WorkflowEntity>(newWorkflow);
const role = await Db.collections.Role.findOneOrFail({
const role = await Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'workflow',
});

View file

@ -23,6 +23,7 @@ import { isBelowOnboardingThreshold } from '@/WorkflowHelpers';
import { EEWorkflowController } from './workflows.controller.ee';
import { WorkflowsService } from './workflows.services';
import { whereClause } from '@/UserManagement/UserManagementHelper';
import { In } from 'typeorm';
export const workflowsController = express.Router();
@ -61,8 +62,11 @@ workflowsController.post(
const { tags: tagIds } = req.body;
if (tagIds?.length && !config.getEnv('workflowTagsDisabled')) {
newWorkflow.tags = await Db.collections.Tag.findByIds(tagIds, {
newWorkflow.tags = await Db.collections.Tag.find({
select: ['id', 'name'],
where: {
id: In(tagIds),
},
});
}
@ -75,7 +79,7 @@ workflowsController.post(
await Db.transaction(async (transactionManager) => {
savedWorkflow = await transactionManager.save<WorkflowEntity>(newWorkflow);
const role = await Db.collections.Role.findOneOrFail({
const role = await Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'workflow',
});

View file

@ -74,13 +74,12 @@ export class EEWorkflowsService extends WorkflowsService {
if (user.isPending) {
return acc;
}
acc.push(
Db.collections.SharedWorkflow.create({
workflow,
user,
role,
}),
);
const entity: Partial<SharedWorkflow> = {
workflowId: workflow.id,
userId: user.id,
roleId: role?.id,
};
acc.push(Db.collections.SharedWorkflow.create(entity));
return acc;
}, []);

View file

@ -1,6 +1,6 @@
import { validate as jsonSchemaValidate } from 'jsonschema';
import { INode, IPinData, JsonObject, jsonParse, LoggerProxy, Workflow } from 'n8n-workflow';
import { FindConditions, In } from 'typeorm';
import { FindOptionsWhere, In } from 'typeorm';
import pick from 'lodash.pick';
import { v4 as uuid } from 'uuid';
import * as ActiveWorkflowRunner from '@/ActiveWorkflowRunner';
@ -48,8 +48,8 @@ export class WorkflowsService {
workflowId: string,
relations: string[] = ['workflow'],
{ allowGlobalOwner } = { allowGlobalOwner: true },
): Promise<SharedWorkflow | undefined> {
const where: FindConditions<SharedWorkflow> = { workflowId };
): Promise<SharedWorkflow | null> {
const where: FindOptionsWhere<SharedWorkflow> = { workflowId };
// Omit user from where if the requesting user is the global
// owner. This allows the global owner to view and delete
@ -103,8 +103,8 @@ export class WorkflowsService {
return pinnedTriggers.find((pt) => pt.name === checkNodeName) ?? null; // partial execution
}
static async get(workflow: Partial<WorkflowEntity>, options?: { relations: string[] }) {
return Db.collections.Workflow.findOne(workflow, options);
static async get(workflow: FindOptionsWhere<WorkflowEntity>, options?: { relations: string[] }) {
return Db.collections.Workflow.findOne({ where: workflow, relations: options?.relations });
}
// Warning: this function is overridden by EE to disregard role list.
@ -299,9 +299,12 @@ export class WorkflowsService {
// We sadly get nothing back from "update". Neither if it updated a record
// nor the new value. So query now the hopefully updated entry.
const updatedWorkflow = await Db.collections.Workflow.findOne(workflowId, { relations });
const updatedWorkflow = await Db.collections.Workflow.findOne({
where: { id: workflowId },
relations,
});
if (updatedWorkflow === undefined) {
if (updatedWorkflow === null) {
throw new ResponseHelper.BadRequestError(
`Workflow with ID "${workflowId}" could not be found to be updated.`,
);

View file

@ -6,19 +6,16 @@ import { CREDENTIALS_REPORT } from '@/audit/constants';
import { getRiskSection } from './utils';
import * as testDb from '../shared/testDb';
let testDbName = '';
beforeAll(async () => {
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
});
beforeEach(async () => {
await testDb.truncate(['Workflow', 'Credentials', 'Execution'], testDbName);
await testDb.truncate(['Workflow', 'Credentials', 'Execution']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('should report credentials not in any use', async () => {

View file

@ -9,19 +9,16 @@ import {
import { getRiskSection, saveManualTriggerWorkflow } from './utils';
import * as testDb from '../shared/testDb';
let testDbName = '';
beforeAll(async () => {
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
});
beforeEach(async () => {
await testDb.truncate(['Workflow'], testDbName);
await testDb.truncate(['Workflow']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('should report expressions in queries', async () => {

View file

@ -5,19 +5,16 @@ import { FILESYSTEM_INTERACTION_NODE_TYPES, FILESYSTEM_REPORT } from '@/audit/co
import { getRiskSection, saveManualTriggerWorkflow } from './utils';
import * as testDb from '../shared/testDb';
let testDbName = '';
beforeAll(async () => {
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
});
beforeEach(async () => {
await testDb.truncate(['Workflow'], testDbName);
await testDb.truncate(['Workflow']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('should report filesystem interaction nodes', async () => {

View file

@ -13,21 +13,18 @@ import * as testDb from '../shared/testDb';
import { toReportTitle } from '@/audit/utils';
import config from '@/config';
let testDbName = '';
beforeAll(async () => {
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
simulateUpToDateInstance();
});
beforeEach(async () => {
await testDb.truncate(['Workflow'], testDbName);
await testDb.truncate(['Workflow']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('should report webhook lacking authentication', async () => {

View file

@ -7,19 +7,16 @@ import { getRiskSection, MOCK_PACKAGE, saveManualTriggerWorkflow } from './utils
import * as testDb from '../shared/testDb';
import { toReportTitle } from '@/audit/utils';
let testDbName = '';
beforeAll(async () => {
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
});
beforeEach(async () => {
await testDb.truncate(['Workflow'], testDbName);
await testDb.truncate(['Workflow']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('should report risky official nodes', async () => {

View file

@ -11,15 +11,13 @@ import type { AuthAgent } from './shared/types';
import * as utils from './shared/utils';
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let globalMemberRole: Role;
let authAgent: AuthAgent;
beforeAll(async () => {
app = await utils.initTestServer({ endpointGroups: ['auth'], applyAuth: true });
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
globalMemberRole = await testDb.getGlobalMemberRole();
@ -31,7 +29,7 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(['User'], testDbName);
await testDb.truncate(['User']);
config.set('userManagement.isInstanceOwnerSetUp', true);
@ -42,7 +40,7 @@ beforeEach(async () => {
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('POST /login should log user in', async () => {

View file

@ -12,7 +12,6 @@ import type { AuthAgent } from './shared/types';
import * as utils from './shared/utils';
let app: express.Application;
let testDbName = '';
let globalMemberRole: Role;
let authAgent: AuthAgent;
@ -21,8 +20,7 @@ beforeAll(async () => {
applyAuth: true,
endpointGroups: ['me', 'auth', 'owner', 'users'],
});
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalMemberRole = await testDb.getGlobalMemberRole();
@ -33,7 +31,7 @@ beforeAll(async () => {
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
ROUTES_REQUIRING_AUTHENTICATION.concat(ROUTES_REQUIRING_AUTHORIZATION).forEach((route) => {

View file

@ -7,23 +7,21 @@ import * as utils from '../shared/utils';
import * as testDb from '../shared/testDb';
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
beforeAll(async () => {
app = await utils.initTestServer({ endpointGroups: ['owner'], applyAuth: true });
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
});
beforeEach(async () => {
await testDb.truncate(['User'], testDbName);
await testDb.truncate(['User']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('user-management:reset should reset DB to default user state', async () => {
@ -31,7 +29,7 @@ test('user-management:reset should reset DB to default user state', async () =>
await Reset.run();
const user = await Db.collections.User.findOne({ globalRole: globalOwnerRole });
const user = await Db.collections.User.findOneBy({ globalRoleId: globalOwnerRole.id });
if (!user) {
fail('No owner found after DB reset to default user state');

View file

@ -14,7 +14,6 @@ import * as utils from './shared/utils';
import type { IUser } from 'n8n-workflow';
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let globalMemberRole: Role;
let credentialOwnerRole: Role;
@ -27,8 +26,7 @@ beforeAll(async () => {
endpointGroups: ['credentials'],
applyAuth: true,
});
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
utils.initConfigFile();
@ -46,11 +44,11 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(['User', 'SharedCredentials', 'Credentials'], testDbName);
await testDb.truncate(['User', 'SharedCredentials', 'Credentials']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
// ----------------------------------------
@ -380,7 +378,7 @@ test('PUT /credentials/:id/share should share the credential with the provided u
const sharedCredentials = await Db.collections.SharedCredentials.find({
relations: ['role'],
where: { credentials: savedCredential },
where: { credentialsId: savedCredential.id },
});
// check that sharings have been removed/added correctly
@ -420,7 +418,7 @@ test('PUT /credentials/:id/share should share the credential with the provided u
// check that sharings got correctly set in DB
const sharedCredentials = await Db.collections.SharedCredentials.find({
relations: ['role'],
where: { credentials: savedCredential, user: { id: In([...memberIds]) } },
where: { credentialsId: savedCredential.id, userId: In([...memberIds]) },
});
expect(sharedCredentials.length).toBe(memberIds.length);
@ -433,7 +431,7 @@ test('PUT /credentials/:id/share should share the credential with the provided u
// check that owner still exists
const ownerSharedCredential = await Db.collections.SharedCredentials.findOneOrFail({
relations: ['role'],
where: { credentials: savedCredential, user: owner },
where: { credentialsId: savedCredential.id, userId: owner.id },
});
expect(ownerSharedCredential.role.name).toBe('owner');
@ -475,7 +473,7 @@ test('PUT /credentials/:id/share should ignore pending sharee', async () => {
expect(response.statusCode).toBe(200);
const sharedCredentials = await Db.collections.SharedCredentials.find({
where: { credentials: savedCredential },
where: { credentialsId: savedCredential.id },
});
expect(sharedCredentials).toHaveLength(1);
@ -493,7 +491,7 @@ test('PUT /credentials/:id/share should ignore non-existing sharee', async () =>
expect(response.statusCode).toBe(200);
const sharedCredentials = await Db.collections.SharedCredentials.find({
where: { credentials: savedCredential },
where: { credentialsId: savedCredential.id },
});
expect(sharedCredentials).toHaveLength(1);
@ -535,7 +533,7 @@ test('PUT /credentials/:id/share should unshare the credential', async () => {
expect(response.statusCode).toBe(200);
const sharedCredentials = await Db.collections.SharedCredentials.find({
where: { credentials: savedCredential },
where: { credentialsId: savedCredential.id },
});
expect(sharedCredentials).toHaveLength(1);

View file

@ -19,7 +19,6 @@ const mockIsCredentialsSharingEnabled = jest.spyOn(UserManagementHelpers, 'isSha
mockIsCredentialsSharingEnabled.mockReturnValue(false);
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let globalMemberRole: Role;
let saveCredential: SaveCredentialFunction;
@ -30,8 +29,7 @@ beforeAll(async () => {
endpointGroups: ['credentials'],
applyAuth: true,
});
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
utils.initConfigFile();
@ -48,11 +46,11 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(['User', 'SharedCredentials', 'Credentials'], testDbName);
await testDb.truncate(['User', 'SharedCredentials', 'Credentials']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
// ----------------------------------------
@ -124,7 +122,7 @@ test('POST /credentials should create cred', async () => {
expect(nodesAccess[0].nodeType).toBe(payload.nodesAccess[0].nodeType);
expect(encryptedData).not.toBe(payload.data);
const credential = await Db.collections.Credentials.findOneOrFail(id);
const credential = await Db.collections.Credentials.findOneByOrFail({ id });
expect(credential.name).toBe(payload.name);
expect(credential.type).toBe(payload.type);
@ -133,7 +131,7 @@ test('POST /credentials should create cred', async () => {
const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({
relations: ['user', 'credentials'],
where: { credentials: credential },
where: { credentialsId: credential.id },
});
expect(sharedCredential.user.id).toBe(ownerShell.id);
@ -191,13 +189,13 @@ test('DELETE /credentials/:id should delete owned cred for owner', async () => {
expect(response.statusCode).toBe(200);
expect(response.body).toEqual({ data: true });
const deletedCredential = await Db.collections.Credentials.findOne(savedCredential.id);
const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id });
expect(deletedCredential).toBeUndefined(); // deleted
expect(deletedCredential).toBeNull(); // deleted
const deletedSharedCredential = await Db.collections.SharedCredentials.findOne();
const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({});
expect(deletedSharedCredential).toBeUndefined(); // deleted
expect(deletedSharedCredential).toBeNull(); // deleted
});
test('DELETE /credentials/:id should delete non-owned cred for owner', async () => {
@ -210,13 +208,13 @@ test('DELETE /credentials/:id should delete non-owned cred for owner', async ()
expect(response.statusCode).toBe(200);
expect(response.body).toEqual({ data: true });
const deletedCredential = await Db.collections.Credentials.findOne(savedCredential.id);
const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id });
expect(deletedCredential).toBeUndefined(); // deleted
expect(deletedCredential).toBeNull(); // deleted
const deletedSharedCredential = await Db.collections.SharedCredentials.findOne();
const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({});
expect(deletedSharedCredential).toBeUndefined(); // deleted
expect(deletedSharedCredential).toBeNull(); // deleted
});
test('DELETE /credentials/:id should delete owned cred for member', async () => {
@ -228,13 +226,13 @@ test('DELETE /credentials/:id should delete owned cred for member', async () =>
expect(response.statusCode).toBe(200);
expect(response.body).toEqual({ data: true });
const deletedCredential = await Db.collections.Credentials.findOne(savedCredential.id);
const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id });
expect(deletedCredential).toBeUndefined(); // deleted
expect(deletedCredential).toBeNull(); // deleted
const deletedSharedCredential = await Db.collections.SharedCredentials.findOne();
const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({});
expect(deletedSharedCredential).toBeUndefined(); // deleted
expect(deletedSharedCredential).toBeNull(); // deleted
});
test('DELETE /credentials/:id should not delete non-owned cred for member', async () => {
@ -246,11 +244,11 @@ test('DELETE /credentials/:id should not delete non-owned cred for member', asyn
expect(response.statusCode).toBe(404);
const shellCredential = await Db.collections.Credentials.findOne(savedCredential.id);
const shellCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id });
expect(shellCredential).toBeDefined(); // not deleted
const deletedSharedCredential = await Db.collections.SharedCredentials.findOne();
const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({});
expect(deletedSharedCredential).toBeDefined(); // not deleted
});
@ -285,7 +283,7 @@ test('PATCH /credentials/:id should update owned cred for owner', async () => {
expect(encryptedData).not.toBe(patchPayload.data);
const credential = await Db.collections.Credentials.findOneOrFail(id);
const credential = await Db.collections.Credentials.findOneByOrFail({ id });
expect(credential.name).toBe(patchPayload.name);
expect(credential.type).toBe(patchPayload.type);
@ -294,7 +292,7 @@ test('PATCH /credentials/:id should update owned cred for owner', async () => {
const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({
relations: ['credentials'],
where: { credentials: credential },
where: { credentialsId: credential.id },
});
expect(sharedCredential.credentials.name).toBe(patchPayload.name); // updated
@ -324,7 +322,7 @@ test('PATCH /credentials/:id should update non-owned cred for owner', async () =
expect(encryptedData).not.toBe(patchPayload.data);
const credential = await Db.collections.Credentials.findOneOrFail(id);
const credential = await Db.collections.Credentials.findOneByOrFail({ id });
expect(credential.name).toBe(patchPayload.name);
expect(credential.type).toBe(patchPayload.type);
@ -333,7 +331,7 @@ test('PATCH /credentials/:id should update non-owned cred for owner', async () =
const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({
relations: ['credentials'],
where: { credentials: credential },
where: { credentialsId: credential.id },
});
expect(sharedCredential.credentials.name).toBe(patchPayload.name); // updated
@ -362,7 +360,7 @@ test('PATCH /credentials/:id should update owned cred for member', async () => {
expect(encryptedData).not.toBe(patchPayload.data);
const credential = await Db.collections.Credentials.findOneOrFail(id);
const credential = await Db.collections.Credentials.findOneByOrFail({ id });
expect(credential.name).toBe(patchPayload.name);
expect(credential.type).toBe(patchPayload.type);
@ -371,7 +369,7 @@ test('PATCH /credentials/:id should update owned cred for member', async () => {
const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({
relations: ['credentials'],
where: { credentials: credential },
where: { credentialsId: credential.id },
});
expect(sharedCredential.credentials.name).toBe(patchPayload.name); // updated
@ -389,7 +387,9 @@ test('PATCH /credentials/:id should not update non-owned cred for member', async
expect(response.statusCode).toBe(404);
const shellCredential = await Db.collections.Credentials.findOneOrFail(savedCredential.id);
const shellCredential = await Db.collections.Credentials.findOneByOrFail({
id: savedCredential.id,
});
expect(shellCredential.name).not.toBe(patchPayload.name); // not updated
});

View file

@ -16,11 +16,11 @@ import {
} from 'n8n-workflow';
import { eventBus } from '@/eventbus';
import { SuperAgentTest } from 'supertest';
import { EventMessageGeneric } from '../../src/eventbus/EventMessageClasses/EventMessageGeneric';
import { MessageEventBusDestinationSyslog } from '../../src/eventbus/MessageEventBusDestination/MessageEventBusDestinationSyslog.ee';
import { MessageEventBusDestinationWebhook } from '../../src/eventbus/MessageEventBusDestination/MessageEventBusDestinationWebhook.ee';
import { MessageEventBusDestinationSentry } from '../../src/eventbus/MessageEventBusDestination/MessageEventBusDestinationSentry.ee';
import { EventMessageAudit } from '../../src/eventbus/EventMessageClasses/EventMessageAudit';
import { EventMessageGeneric } from '@/eventbus/EventMessageClasses/EventMessageGeneric';
import { MessageEventBusDestinationSyslog } from '@/eventbus/MessageEventBusDestination/MessageEventBusDestinationSyslog.ee';
import { MessageEventBusDestinationWebhook } from '@/eventbus/MessageEventBusDestination/MessageEventBusDestinationWebhook.ee';
import { MessageEventBusDestinationSentry } from '@/eventbus/MessageEventBusDestination/MessageEventBusDestinationSentry.ee';
import { EventMessageAudit } from '@/eventbus/EventMessageClasses/EventMessageAudit';
import { v4 as uuid } from 'uuid';
jest.unmock('@/eventbus/MessageEventBus/MessageEventBus');
@ -30,7 +30,6 @@ jest.mock('syslog-client');
const mockedSyslog = syslog as jest.Mocked<typeof syslog>;
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let owner: User;
let unAuthOwnerAgent: SuperAgentTest;
@ -82,8 +81,7 @@ async function confirmIdSent(id: string) {
}
beforeAll(async () => {
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
owner = await testDb.createUser({ globalRole: globalOwnerRole });
@ -121,7 +119,7 @@ beforeEach(async () => {
afterAll(async () => {
jest.mock('@/eventbus/MessageEventBus/MessageEventBus');
await testDb.terminate(testDbName);
await testDb.terminate();
await eventBus.close();
});

View file

@ -13,7 +13,6 @@ const MOCK_RENEW_OFFSET = 259200;
const MOCK_INSTANCE_ID = 'instance-id';
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let globalMemberRole: Role;
let authAgent: AuthAgent;
@ -21,8 +20,7 @@ let license: License;
beforeAll(async () => {
app = await utils.initTestServer({ endpointGroups: ['license'], applyAuth: true });
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
globalMemberRole = await testDb.getGlobalMemberRole();
@ -43,11 +41,11 @@ beforeEach(async () => {
});
afterEach(async () => {
await testDb.truncate(['Settings'], testDbName);
await testDb.truncate(['Settings']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('GET /license should return license information to the instance owner', async () => {

View file

@ -18,15 +18,13 @@ import type { AuthAgent } from './shared/types';
import * as utils from './shared/utils';
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let globalMemberRole: Role;
let authAgent: AuthAgent;
beforeAll(async () => {
app = await utils.initTestServer({ endpointGroups: ['me'], applyAuth: true });
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
globalMemberRole = await testDb.getGlobalMemberRole();
@ -38,12 +36,12 @@ beforeAll(async () => {
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
describe('Owner shell', () => {
beforeEach(async () => {
await testDb.truncate(['User'], testDbName);
await testDb.truncate(['User']);
});
test('GET /me should return sanitized owner shell', async () => {
@ -113,7 +111,7 @@ describe('Owner shell', () => {
expect(globalRole.scope).toBe('global');
expect(apiKey).toBeUndefined();
const storedOwnerShell = await Db.collections.User.findOneOrFail(id);
const storedOwnerShell = await Db.collections.User.findOneByOrFail({ id });
expect(storedOwnerShell.email).toBe(validPayload.email.toLowerCase());
expect(storedOwnerShell.firstName).toBe(validPayload.firstName);
@ -129,7 +127,7 @@ describe('Owner shell', () => {
const response = await authOwnerShellAgent.patch('/me').send(invalidPayload);
expect(response.statusCode).toBe(400);
const storedOwnerShell = await Db.collections.User.findOneOrFail();
const storedOwnerShell = await Db.collections.User.findOneByOrFail({});
expect(storedOwnerShell.email).toBeNull();
expect(storedOwnerShell.firstName).toBeNull();
expect(storedOwnerShell.lastName).toBeNull();
@ -152,7 +150,7 @@ describe('Owner shell', () => {
const response = await authOwnerShellAgent.patch('/me/password').send(payload);
expect([400, 500].includes(response.statusCode)).toBe(true);
const storedMember = await Db.collections.User.findOneOrFail();
const storedMember = await Db.collections.User.findOneByOrFail({});
if (payload.newPassword) {
expect(storedMember.password).not.toBe(payload.newPassword);
@ -164,7 +162,7 @@ describe('Owner shell', () => {
}),
);
const storedOwnerShell = await Db.collections.User.findOneOrFail();
const storedOwnerShell = await Db.collections.User.findOneByOrFail({});
expect(storedOwnerShell.password).toBeNull();
});
@ -241,7 +239,7 @@ describe('Member', () => {
});
afterEach(async () => {
await testDb.truncate(['User'], testDbName);
await testDb.truncate(['User']);
});
test('GET /me should return sanitized member', async () => {
@ -311,7 +309,7 @@ describe('Member', () => {
expect(globalRole.scope).toBe('global');
expect(apiKey).toBeUndefined();
const storedMember = await Db.collections.User.findOneOrFail(id);
const storedMember = await Db.collections.User.findOneByOrFail({ id });
expect(storedMember.email).toBe(validPayload.email.toLowerCase());
expect(storedMember.firstName).toBe(validPayload.firstName);
@ -327,7 +325,7 @@ describe('Member', () => {
const response = await authMemberAgent.patch('/me').send(invalidPayload);
expect(response.statusCode).toBe(400);
const storedMember = await Db.collections.User.findOneOrFail();
const storedMember = await Db.collections.User.findOneByOrFail({});
expect(storedMember.email).toBe(member.email);
expect(storedMember.firstName).toBe(member.firstName);
expect(storedMember.lastName).toBe(member.lastName);
@ -350,7 +348,7 @@ describe('Member', () => {
expect(response.statusCode).toBe(200);
expect(response.body).toEqual(SUCCESS_RESPONSE_BODY);
const storedMember = await Db.collections.User.findOneOrFail();
const storedMember = await Db.collections.User.findOneByOrFail({});
expect(storedMember.password).not.toBe(member.password);
expect(storedMember.password).not.toBe(validPayload.newPassword);
});
@ -363,7 +361,7 @@ describe('Member', () => {
const response = await authMemberAgent.patch('/me/password').send(payload);
expect([400, 500].includes(response.statusCode)).toBe(true);
const storedMember = await Db.collections.User.findOneOrFail();
const storedMember = await Db.collections.User.findOneByOrFail({});
if (payload.newPassword) {
expect(storedMember.password).not.toBe(payload.newPassword);
@ -385,7 +383,9 @@ describe('Member', () => {
expect(response.statusCode).toBe(200);
expect(response.body).toEqual(SUCCESS_RESPONSE_BODY);
const { personalizationAnswers: storedAnswers } = await Db.collections.User.findOneOrFail();
const { personalizationAnswers: storedAnswers } = await Db.collections.User.findOneByOrFail(
{},
);
expect(storedAnswers).toEqual(validPayload);
}
@ -403,7 +403,7 @@ describe('Member', () => {
expect(response.body.data.apiKey).toBeDefined();
expect(response.body.data.apiKey).not.toBeNull();
const storedMember = await Db.collections.User.findOneOrFail(member.id);
const storedMember = await Db.collections.User.findOneByOrFail({ id: member.id });
expect(storedMember.apiKey).toEqual(response.body.data.apiKey);
});
@ -430,7 +430,7 @@ describe('Member', () => {
expect(response.statusCode).toBe(200);
const storedMember = await Db.collections.User.findOneOrFail(member.id);
const storedMember = await Db.collections.User.findOneByOrFail({ id: member.id });
expect(storedMember.apiKey).toBeNull();
});
@ -442,7 +442,7 @@ describe('Owner', () => {
});
afterEach(async () => {
await testDb.truncate(['User'], testDbName);
await testDb.truncate(['User']);
});
test('GET /me should return sanitized owner', async () => {
@ -512,7 +512,7 @@ describe('Owner', () => {
expect(globalRole.scope).toBe('global');
expect(apiKey).toBeUndefined();
const storedOwner = await Db.collections.User.findOneOrFail(id);
const storedOwner = await Db.collections.User.findOneByOrFail({ id });
expect(storedOwner.email).toBe(validPayload.email.toLowerCase());
expect(storedOwner.firstName).toBe(validPayload.firstName);

View file

@ -43,14 +43,12 @@ jest.mock('@/CommunityNodes/packageModel', () => {
const mockedEmptyPackage = mocked(utils.emptyPackage);
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let authAgent: AuthAgent;
beforeAll(async () => {
app = await utils.initTestServer({ endpointGroups: ['nodes'], applyAuth: true });
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
@ -62,14 +60,14 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(['InstalledNodes', 'InstalledPackages', 'User'], testDbName);
await testDb.truncate(['InstalledNodes', 'InstalledPackages', 'User']);
mocked(executeCommand).mockReset();
mocked(findInstalledPackage).mockReset();
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
/**

View file

@ -15,14 +15,12 @@ import type { AuthAgent } from './shared/types';
import * as utils from './shared/utils';
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let authAgent: AuthAgent;
beforeAll(async () => {
app = await utils.initTestServer({ endpointGroups: ['owner'], applyAuth: true });
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
@ -37,11 +35,11 @@ beforeEach(async () => {
});
afterEach(async () => {
await testDb.truncate(['User'], testDbName);
await testDb.truncate(['User']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('POST /owner should create owner and enable isInstanceOwnerSetUp', async () => {
@ -83,7 +81,7 @@ test('POST /owner should create owner and enable isInstanceOwnerSetUp', async ()
expect(globalRole.scope).toBe('global');
expect(apiKey).toBeUndefined();
const storedOwner = await Db.collections.User.findOneOrFail(id);
const storedOwner = await Db.collections.User.findOneByOrFail({ id });
expect(storedOwner.password).not.toBe(newOwnerData.password);
expect(storedOwner.email).toBe(newOwnerData.email);
expect(storedOwner.firstName).toBe(newOwnerData.firstName);
@ -115,7 +113,7 @@ test('POST /owner should create owner with lowercased email', async () => {
expect(id).toBe(ownerShell.id);
expect(email).toBe(newOwnerData.email.toLowerCase());
const storedOwner = await Db.collections.User.findOneOrFail(id);
const storedOwner = await Db.collections.User.findOneByOrFail({ id });
expect(storedOwner.email).toBe(newOwnerData.email.toLowerCase());
});
@ -141,7 +139,7 @@ test('POST /owner/skip-setup should persist skipping setup to the DB', async ()
const skipConfig = config.getEnv('userManagement.skipInstanceOwnerSetup');
expect(skipConfig).toBe(true);
const { value } = await Db.collections.Settings.findOneOrFail({
const { value } = await Db.collections.Settings.findOneByOrFail({
key: 'userManagement.skipInstanceOwnerSetup',
});
expect(value).toBe('true');

View file

@ -17,14 +17,12 @@ import type { Role } from '@db/entities/Role';
jest.mock('@/UserManagement/email/NodeMailer');
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let globalMemberRole: Role;
beforeAll(async () => {
app = await utils.initTestServer({ endpointGroups: ['passwordReset'], applyAuth: true });
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
globalMemberRole = await testDb.getGlobalMemberRole();
@ -34,7 +32,7 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(['User'], testDbName);
await testDb.truncate(['User']);
jest.mock('@/config');
@ -43,7 +41,7 @@ beforeEach(async () => {
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('POST /forgot-password should send password reset email', async () => {
@ -64,7 +62,7 @@ test('POST /forgot-password should send password reset email', async () => {
expect(response.statusCode).toBe(200);
expect(response.body).toEqual({});
const user = await Db.collections.User.findOneOrFail({ email: payload.email });
const user = await Db.collections.User.findOneByOrFail({ email: payload.email });
expect(user.resetPasswordToken).toBeDefined();
expect(user.resetPasswordTokenExpiration).toBeGreaterThan(Math.ceil(Date.now() / 1000));
}),
@ -80,7 +78,7 @@ test('POST /forgot-password should fail if emailing is not set up', async () =>
expect(response.statusCode).toBe(500);
const storedOwner = await Db.collections.User.findOneOrFail({ email: owner.email });
const storedOwner = await Db.collections.User.findOneByOrFail({ email: owner.email });
expect(storedOwner.resetPasswordToken).toBeNull();
});
@ -104,7 +102,7 @@ test('POST /forgot-password should fail with invalid inputs', async () => {
const response = await authlessAgent.post('/forgot-password').send(invalidPayload);
expect(response.statusCode).toBe(400);
const storedOwner = await Db.collections.User.findOneOrFail({ email: owner.email });
const storedOwner = await Db.collections.User.findOneByOrFail({ email: owner.email });
expect(storedOwner.resetPasswordToken).toBeNull();
}),
);
@ -218,7 +216,7 @@ test('POST /change-password should succeed with valid inputs', async () => {
const authToken = utils.getAuthToken(response);
expect(authToken).toBeDefined();
const { password: storedPassword } = await Db.collections.User.findOneOrFail(owner.id);
const { password: storedPassword } = await Db.collections.User.findOneByOrFail({ id: owner.id });
const comparisonResult = await compare(passwordToStore, storedPassword);
expect(comparisonResult).toBe(true);
@ -262,7 +260,7 @@ test('POST /change-password should fail with invalid inputs', async () => {
const response = await authlessAgent.post('/change-password').query(invalidPayload);
expect(response.statusCode).toBe(400);
const { password: storedPassword } = await Db.collections.User.findOneOrFail();
const { password: storedPassword } = await Db.collections.User.findOneByOrFail({});
expect(owner.password).toBe(storedPassword);
}),
);

View file

@ -11,7 +11,6 @@ import type { CredentialPayload, SaveCredentialFunction } from '../shared/types'
import * as testDb from '../shared/testDb';
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let globalMemberRole: Role;
let credentialOwnerRole: Role;
@ -20,8 +19,7 @@ let saveCredential: SaveCredentialFunction;
beforeAll(async () => {
app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false });
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
utils.initConfigFile();
@ -40,11 +38,11 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(['User', 'SharedCredentials', 'Credentials'], testDbName);
await testDb.truncate(['User', 'SharedCredentials', 'Credentials']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('POST /credentials should create credentials', async () => {
@ -76,15 +74,15 @@ test('POST /credentials should create credentials', async () => {
expect(name).toBe(payload.name);
expect(type).toBe(payload.type);
const credential = await Db.collections.Credentials!.findOneOrFail(id);
const credential = await Db.collections.Credentials.findOneByOrFail({ id });
expect(credential.name).toBe(payload.name);
expect(credential.type).toBe(payload.type);
expect(credential.data).not.toBe(payload.data);
const sharedCredential = await Db.collections.SharedCredentials!.findOneOrFail({
const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({
relations: ['user', 'credentials', 'role'],
where: { credentials: credential, user: ownerShell },
where: { credentialsId: credential.id, userId: ownerShell.id },
});
expect(sharedCredential.role).toEqual(credentialOwnerRole);
@ -153,13 +151,13 @@ test('DELETE /credentials/:id should delete owned cred for owner', async () => {
expect(name).toBe(savedCredential.name);
expect(type).toBe(savedCredential.type);
const deletedCredential = await Db.collections.Credentials!.findOne(savedCredential.id);
const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id });
expect(deletedCredential).toBeUndefined(); // deleted
expect(deletedCredential).toBeNull(); // deleted
const deletedSharedCredential = await Db.collections.SharedCredentials!.findOne();
const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({});
expect(deletedSharedCredential).toBeUndefined(); // deleted
expect(deletedSharedCredential).toBeNull(); // deleted
});
test('DELETE /credentials/:id should delete non-owned cred for owner', async () => {
@ -181,13 +179,13 @@ test('DELETE /credentials/:id should delete non-owned cred for owner', async ()
expect(response.statusCode).toBe(200);
const deletedCredential = await Db.collections.Credentials!.findOne(savedCredential.id);
const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id });
expect(deletedCredential).toBeUndefined(); // deleted
expect(deletedCredential).toBeNull(); // deleted
const deletedSharedCredential = await Db.collections.SharedCredentials!.findOne();
const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({});
expect(deletedSharedCredential).toBeUndefined(); // deleted
expect(deletedSharedCredential).toBeNull(); // deleted
});
test('DELETE /credentials/:id should delete owned cred for member', async () => {
@ -211,13 +209,13 @@ test('DELETE /credentials/:id should delete owned cred for member', async () =>
expect(name).toBe(savedCredential.name);
expect(type).toBe(savedCredential.type);
const deletedCredential = await Db.collections.Credentials!.findOne(savedCredential.id);
const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id });
expect(deletedCredential).toBeUndefined(); // deleted
expect(deletedCredential).toBeNull(); // deleted
const deletedSharedCredential = await Db.collections.SharedCredentials!.findOne();
const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({});
expect(deletedSharedCredential).toBeUndefined(); // deleted
expect(deletedSharedCredential).toBeNull(); // deleted
});
test('DELETE /credentials/:id should delete owned cred for member but leave others untouched', async () => {
@ -244,27 +242,27 @@ test('DELETE /credentials/:id should delete owned cred for member but leave othe
expect(name).toBe(savedCredential.name);
expect(type).toBe(savedCredential.type);
const deletedCredential = await Db.collections.Credentials!.findOne(savedCredential.id);
const deletedCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id });
expect(deletedCredential).toBeUndefined(); // deleted
expect(deletedCredential).toBeNull(); // deleted
const deletedSharedCredential = await Db.collections.SharedCredentials!.findOne({
const deletedSharedCredential = await Db.collections.SharedCredentials.findOne({
where: {
credentials: savedCredential,
credentialsId: savedCredential.id,
},
});
expect(deletedSharedCredential).toBeUndefined(); // deleted
expect(deletedSharedCredential).toBeNull(); // deleted
await Promise.all(
[notToBeChangedCredential, notToBeChangedCredential2].map(async (credential) => {
const untouchedCredential = await Db.collections.Credentials!.findOne(credential.id);
const untouchedCredential = await Db.collections.Credentials.findOneBy({ id: credential.id });
expect(untouchedCredential).toEqual(credential); // not deleted
const untouchedSharedCredential = await Db.collections.SharedCredentials!.findOne({
const untouchedSharedCredential = await Db.collections.SharedCredentials.findOne({
where: {
credentials: credential,
credentialsId: credential.id,
},
});
@ -289,11 +287,11 @@ test('DELETE /credentials/:id should not delete non-owned cred for member', asyn
expect(response.statusCode).toBe(404);
const shellCredential = await Db.collections.Credentials!.findOne(savedCredential.id);
const shellCredential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id });
expect(shellCredential).toBeDefined(); // not deleted
const deletedSharedCredential = await Db.collections.SharedCredentials!.findOne();
const deletedSharedCredential = await Db.collections.SharedCredentials.findOneBy({});
expect(deletedSharedCredential).toBeDefined(); // not deleted
});

View file

@ -9,14 +9,12 @@ import * as utils from '../shared/utils';
import * as testDb from '../shared/testDb';
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let workflowRunner: ActiveWorkflowRunner;
beforeAll(async () => {
app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false });
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
@ -30,18 +28,15 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(
[
'SharedCredentials',
'SharedWorkflow',
'User',
'Workflow',
'Credentials',
'Execution',
'Settings',
],
testDbName,
);
await testDb.truncate([
'SharedCredentials',
'SharedWorkflow',
'User',
'Workflow',
'Credentials',
'Execution',
'Settings',
]);
config.set('userManagement.disabled', false);
config.set('userManagement.isInstanceOwnerSetUp', true);
@ -52,7 +47,7 @@ afterEach(async () => {
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('GET /executions/:id should fail due to missing API Key', async () => {

View file

@ -11,7 +11,6 @@ import * as utils from '../shared/utils';
import * as testDb from '../shared/testDb';
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let globalMemberRole: Role;
let workflowOwnerRole: Role;
@ -19,8 +18,7 @@ let workflowRunner: ActiveWorkflowRunner;
beforeAll(async () => {
app = await utils.initTestServer({ endpointGroups: ['publicApi'], applyAuth: false });
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
const [fetchedGlobalOwnerRole, fetchedGlobalMemberRole, fetchedWorkflowOwnerRole] =
await testDb.getAllRoles();
@ -37,10 +35,14 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(
['SharedCredentials', 'SharedWorkflow', 'Tag', 'User', 'Workflow', 'Credentials'],
testDbName,
);
await testDb.truncate([
'SharedCredentials',
'SharedWorkflow',
'Tag',
'User',
'Workflow',
'Credentials',
]);
config.set('userManagement.disabled', false);
config.set('userManagement.isInstanceOwnerSetUp', true);
@ -51,7 +53,7 @@ afterEach(async () => {
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('GET /workflows should fail due to missing API Key', async () => {
@ -537,11 +539,11 @@ test('DELETE /workflows/:id should delete the workflow', async () => {
expect(updatedAt).toEqual(workflow.updatedAt.toISOString());
// make sure the workflow actually deleted from the db
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({
workflow,
const sharedWorkflow = await Db.collections.SharedWorkflow.findOneBy({
workflowId: workflow.id,
});
expect(sharedWorkflow).toBeUndefined();
expect(sharedWorkflow).toBeNull();
});
test('DELETE /workflows/:id should delete non-owned workflow when owner', async () => {
@ -576,11 +578,11 @@ test('DELETE /workflows/:id should delete non-owned workflow when owner', async
expect(updatedAt).toEqual(workflow.updatedAt.toISOString());
// make sure the workflow actually deleted from the db
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({
workflow,
const sharedWorkflow = await Db.collections.SharedWorkflow.findOneBy({
workflowId: workflow.id,
});
expect(sharedWorkflow).toBeUndefined();
expect(sharedWorkflow).toBeNull();
});
test('POST /workflows/:id/activate should fail due to missing API Key', async () => {
@ -679,8 +681,8 @@ test('POST /workflows/:id/activate should set workflow as active', async () => {
// check whether the workflow is on the database
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({
where: {
user: member,
workflow,
userId: member.id,
workflowId: workflow.id,
},
relations: ['workflow'],
});
@ -724,17 +726,17 @@ test('POST /workflows/:id/activate should set non-owned workflow as active when
// check whether the workflow is on the database
const sharedOwnerWorkflow = await Db.collections.SharedWorkflow.findOne({
where: {
user: owner,
workflow,
userId: owner.id,
workflowId: workflow.id,
},
});
expect(sharedOwnerWorkflow).toBeUndefined();
expect(sharedOwnerWorkflow).toBeNull();
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({
where: {
user: member,
workflow,
userId: member.id,
workflowId: workflow.id,
},
relations: ['workflow'],
});
@ -824,8 +826,8 @@ test('POST /workflows/:id/deactivate should deactivate workflow', async () => {
// get the workflow after it was deactivated
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({
where: {
user: member,
workflow,
userId: member.id,
workflowId: workflow.id,
},
relations: ['workflow'],
});
@ -869,17 +871,17 @@ test('POST /workflows/:id/deactivate should deactivate non-owned workflow when o
// check whether the workflow is deactivated in the database
const sharedOwnerWorkflow = await Db.collections.SharedWorkflow.findOne({
where: {
user: owner,
workflow,
userId: owner.id,
workflowId: workflow.id,
},
});
expect(sharedOwnerWorkflow).toBeUndefined();
expect(sharedOwnerWorkflow).toBeNull();
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({
where: {
user: member,
workflow,
userId: member.id,
workflowId: workflow.id,
},
relations: ['workflow'],
});
@ -990,8 +992,8 @@ test('POST /workflows should create workflow', async () => {
// check if created workflow in DB
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({
where: {
user: member,
workflow: response.body,
userId: member.id,
workflowId: response.body.id,
},
relations: ['workflow', 'role'],
});
@ -1170,8 +1172,8 @@ test('PUT /workflows/:id should update workflow', async () => {
// check updated workflow in DB
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({
where: {
user: member,
workflow: response.body,
userId: member.id,
workflowId: response.body.id,
},
relations: ['workflow'],
});
@ -1247,17 +1249,17 @@ test('PUT /workflows/:id should update non-owned workflow if owner', async () =>
// check updated workflow in DB
const sharedOwnerWorkflow = await Db.collections.SharedWorkflow.findOne({
where: {
user: owner,
workflow: response.body,
userId: owner.id,
workflowId: response.body.id,
},
});
expect(sharedOwnerWorkflow).toBeUndefined();
expect(sharedOwnerWorkflow).toBeNull();
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({
where: {
user: member,
workflow: response.body,
userId: member.id,
workflowId: response.body.id,
},
relations: ['workflow', 'role'],
});

View file

@ -1,5 +1,5 @@
import superagent = require('superagent');
import { ObjectLiteral } from 'typeorm';
import type { ObjectLiteral } from 'typeorm';
/**
* Make `SuperTest<T>` string-indexable.

View file

@ -1,5 +1,5 @@
import { UserSettings } from 'n8n-core';
import { Connection, ConnectionOptions, createConnection, getConnection } from 'typeorm';
import { DataSource as Connection, DataSourceOptions as ConnectionOptions } from 'typeorm';
import config from '@/config';
import * as Db from '@/Db';
@ -50,7 +50,7 @@ export async function init() {
// no bootstrap connection required
const testDbName = `n8n_test_sqlite_${randomString(6, 10)}_${Date.now()}`;
await Db.init(getSqliteOptions({ name: testDbName }));
await getConnection(testDbName).runMigrations({ transaction: 'none' });
await Db.getConnection().runMigrations({ transaction: 'none' });
return { testDbName };
}
@ -60,7 +60,7 @@ export async function init() {
const pgOptions = getBootstrapDBOptions('postgres');
try {
bootstrapPostgres = await createConnection(pgOptions);
bootstrapPostgres = await new Connection(pgOptions).initialize();
} catch (error) {
const pgConfig = getPostgresSchemaSection();
@ -82,15 +82,15 @@ export async function init() {
const testDbName = `postgres_${randomString(6, 10)}_${Date.now()}_n8n_test`;
await bootstrapPostgres.query(`CREATE DATABASE ${testDbName}`);
await bootstrapPostgres.close();
await bootstrapPostgres.destroy();
const dbOptions = getDBOptions('postgres', testDbName);
if (dbOptions.schema !== 'public') {
const { schema, migrations, ...options } = dbOptions;
const connection = await createConnection(options);
const connection = await new Connection(options).initialize();
await connection.query(`CREATE SCHEMA IF NOT EXISTS "${schema}"`);
await connection.close();
await connection.destroy();
}
await Db.init(dbOptions);
@ -99,11 +99,11 @@ export async function init() {
}
if (dbType === 'mysqldb') {
const bootstrapMysql = await createConnection(getBootstrapDBOptions('mysql'));
const bootstrapMysql = await new Connection(getBootstrapDBOptions('mysql')).initialize();
const testDbName = `mysql_${randomString(6, 10)}_${Date.now()}_n8n_test`;
await bootstrapMysql.query(`CREATE DATABASE ${testDbName}`);
await bootstrapMysql.close();
await bootstrapMysql.destroy();
await Db.init(getDBOptions('mysql', testDbName));
@ -116,8 +116,8 @@ export async function init() {
/**
* Drop test DB, closing bootstrap connection if existing.
*/
export async function terminate(testDbName: string) {
await getConnection(testDbName).close();
export async function terminate() {
await Db.getConnection().destroy();
}
async function truncateMappingTables(
@ -171,9 +171,9 @@ 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>, testDbName: string) {
export async function truncate(collections: Array<CollectionName>) {
const dbType = config.getEnv('database.type');
const testDb = getConnection(testDbName);
const testDb = Db.getConnection();
if (dbType === 'sqlite') {
await testDb.query('PRAGMA foreign_keys=OFF');
@ -287,12 +287,12 @@ export async function saveCredential(
}
export async function shareCredentialWithUsers(credential: CredentialsEntity, users: User[]) {
const role = await Db.collections.Role.findOne({ scope: 'credential', name: 'user' });
const role = await Db.collections.Role.findOneBy({ scope: 'credential', name: 'user' });
const newSharedCredentials = users.map((user) =>
Db.collections.SharedCredentials.create({
user,
credentials: credential,
role,
userId: user.id,
credentialsId: credential.id,
roleId: role?.id,
}),
);
return Db.collections.SharedCredentials.save(newSharedCredentials);
@ -333,7 +333,7 @@ export function createUserShell(globalRole: Role): Promise<User> {
throw new Error(`Invalid role received: ${JSON.stringify(globalRole)}`);
}
const shell: Partial<User> = { globalRole };
const shell: Partial<User> = { globalRoleId: globalRole.id };
if (globalRole.name !== 'owner') {
shell.email = randomEmail();
@ -405,35 +405,35 @@ export function addApiKey(user: User): Promise<User> {
// ----------------------------------
export function getGlobalOwnerRole() {
return Db.collections.Role.findOneOrFail({
return Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'global',
});
}
export function getGlobalMemberRole() {
return Db.collections.Role.findOneOrFail({
return Db.collections.Role.findOneByOrFail({
name: 'member',
scope: 'global',
});
}
export function getWorkflowOwnerRole() {
return Db.collections.Role.findOneOrFail({
return Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'workflow',
});
}
export function getWorkflowEditorRole() {
return Db.collections.Role.findOneOrFail({
return Db.collections.Role.findOneByOrFail({
name: 'editor',
scope: 'workflow',
});
}
export function getCredentialOwnerRole() {
return Db.collections.Role.findOneOrFail({
return Db.collections.Role.findOneByOrFail({
name: 'owner',
scope: 'credential',
});
@ -641,10 +641,8 @@ export async function createWorkflowWithTrigger(
// ----------------------------------
export async function getWorkflowSharing(workflow: WorkflowEntity) {
return Db.collections.SharedWorkflow.find({
where: {
workflow,
},
return Db.collections.SharedWorkflow.findBy({
workflowId: workflow.id,
});
}

View file

@ -646,7 +646,7 @@ export function getAuthToken(response: request.Response, authCookieName = AUTH_C
// ----------------------------------
export async function isInstanceOwnerSetUp() {
const { value } = await Db.collections.Settings.findOneOrFail({
const { value } = await Db.collections.Settings.findOneByOrFail({
key: 'userManagement.isInstanceOwnerSetUp',
});

View file

@ -25,7 +25,6 @@ import { NodeMailer } from '@/UserManagement/email/NodeMailer';
jest.mock('@/UserManagement/email/NodeMailer');
let app: express.Application;
let testDbName = '';
let globalMemberRole: Role;
let globalOwnerRole: Role;
let workflowOwnerRole: Role;
@ -34,8 +33,7 @@ let authAgent: AuthAgent;
beforeAll(async () => {
app = await utils.initTestServer({ endpointGroups: ['users'], applyAuth: true });
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
const [
fetchedGlobalOwnerRole,
@ -56,10 +54,7 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(
['User', 'SharedCredentials', 'SharedWorkflow', 'Workflow', 'Credentials'],
testDbName,
);
await testDb.truncate(['User', 'SharedCredentials', 'SharedWorkflow', 'Workflow', 'Credentials']);
jest.mock('@/config');
@ -70,7 +65,7 @@ beforeEach(async () => {
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('GET /users should return all users', async () => {
@ -156,28 +151,28 @@ test('DELETE /users/:id should delete the user', async () => {
expect(response.statusCode).toBe(200);
expect(response.body).toEqual(SUCCESS_RESPONSE_BODY);
const user = await Db.collections.User.findOne(userToDelete.id);
expect(user).toBeUndefined(); // deleted
const user = await Db.collections.User.findOneBy({ id: userToDelete.id });
expect(user).toBeNull(); // deleted
const sharedWorkflow = await Db.collections.SharedWorkflow.findOne({
relations: ['user'],
where: { user: userToDelete, role: workflowOwnerRole },
where: { userId: userToDelete.id, roleId: workflowOwnerRole.id },
});
expect(sharedWorkflow).toBeUndefined(); // deleted
expect(sharedWorkflow).toBeNull(); // deleted
const sharedCredential = await Db.collections.SharedCredentials.findOne({
relations: ['user'],
where: { user: userToDelete, role: credentialOwnerRole },
where: { userId: userToDelete.id, roleId: credentialOwnerRole.id },
});
expect(sharedCredential).toBeUndefined(); // deleted
expect(sharedCredential).toBeNull(); // deleted
const workflow = await Db.collections.Workflow.findOne(savedWorkflow.id);
expect(workflow).toBeUndefined(); // deleted
const workflow = await Db.collections.Workflow.findOneBy({ id: savedWorkflow.id });
expect(workflow).toBeNull(); // deleted
// TODO: Include active workflow and check whether webhook has been removed
const credential = await Db.collections.Credentials.findOne(savedCredential.id);
expect(credential).toBeUndefined(); // deleted
const credential = await Db.collections.Credentials.findOneBy({ id: savedCredential.id });
expect(credential).toBeNull(); // deleted
});
test('DELETE /users/:id should fail to delete self', async () => {
@ -187,7 +182,7 @@ test('DELETE /users/:id should fail to delete self', async () => {
expect(response.statusCode).toBe(400);
const user = await Db.collections.User.findOne(owner.id);
const user = await Db.collections.User.findOneBy({ id: owner.id });
expect(user).toBeDefined();
});
@ -202,7 +197,7 @@ test('DELETE /users/:id should fail if user to delete is transferee', async () =
expect(response.statusCode).toBe(400);
const user = await Db.collections.User.findOne(idToDelete);
const user = await Db.collections.User.findOneBy({ id: idToDelete });
expect(user).toBeDefined();
});
@ -226,7 +221,7 @@ test('DELETE /users/:id with transferId should perform transfer', async () => {
const sharedWorkflow = await Db.collections.SharedWorkflow.findOneOrFail({
relations: ['workflow'],
where: { user: owner },
where: { userId: owner.id },
});
expect(sharedWorkflow.workflow).toBeDefined();
@ -234,15 +229,15 @@ test('DELETE /users/:id with transferId should perform transfer', async () => {
const sharedCredential = await Db.collections.SharedCredentials.findOneOrFail({
relations: ['credentials'],
where: { user: owner },
where: { userId: owner.id },
});
expect(sharedCredential.credentials).toBeDefined();
expect(sharedCredential.credentials.id).toBe(savedCredential.id);
const deletedUser = await Db.collections.User.findOne(userToDelete);
const deletedUser = await Db.collections.User.findOneBy({ id: userToDelete.id });
expect(deletedUser).toBeUndefined();
expect(deletedUser).toBeNull();
});
test('GET /resolve-signup-token should validate invite token', async () => {
@ -342,7 +337,7 @@ test('POST /users/:id should fill out a user shell', async () => {
const authToken = utils.getAuthToken(response);
expect(authToken).toBeDefined();
const member = await Db.collections.User.findOneOrFail(memberShell.id);
const member = await Db.collections.User.findOneByOrFail({ id: memberShell.id });
expect(member.firstName).toBe(memberData.firstName);
expect(member.lastName).toBe(memberData.lastName);
expect(member.password).not.toBe(memberData.password);
@ -487,7 +482,7 @@ test('POST /users should email invites and create user shells but ignore existin
expect(error).toBe('Email could not be sent');
}
const storedUser = await Db.collections.User.findOneOrFail(id);
const storedUser = await Db.collections.User.findOneByOrFail({ id });
const { firstName, lastName, personalizationAnswers, password, resetPasswordToken } =
storedUser;

View file

@ -14,7 +14,6 @@ import { randomCredentialPayload } from './shared/random';
import { ActiveWorkflowRunner } from '@/ActiveWorkflowRunner';
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
let globalMemberRole: Role;
let credentialOwnerRole: Role;
@ -29,8 +28,7 @@ beforeAll(async () => {
endpointGroups: ['workflows'],
applyAuth: true,
});
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
globalMemberRole = await testDb.getGlobalMemberRole();
@ -53,11 +51,11 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(['User', 'Workflow', 'SharedWorkflow'], testDbName);
await testDb.truncate(['User', 'Workflow', 'SharedWorkflow']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('Router should switch dynamically', async () => {

View file

@ -9,7 +9,6 @@ import type { IPinData } from 'n8n-workflow';
import { makeWorkflow, MOCK_PINDATA } from './shared/utils';
let app: express.Application;
let testDbName = '';
let globalOwnerRole: Role;
// mock whether sharing is enabled or not
@ -20,8 +19,7 @@ beforeAll(async () => {
endpointGroups: ['workflows'],
applyAuth: true,
});
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
globalOwnerRole = await testDb.getGlobalOwnerRole();
@ -30,11 +28,11 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(['User', 'Workflow', 'SharedWorkflow'], testDbName);
await testDb.truncate(['User', 'Workflow', 'SharedWorkflow']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
test('POST /workflows should store pin data for node in workflow', async () => {

View file

@ -1,5 +1,5 @@
import 'tsconfig-paths/register';
import { createConnection } from 'typeorm';
import { DataSource as Connection } from 'typeorm';
import config from '@/config';
import { getBootstrapDBOptions } from './integration/shared/testDb';
@ -7,7 +7,8 @@ export default async () => {
const dbType = config.getEnv('database.type').replace(/db$/, '');
if (dbType !== 'postgres' && dbType !== 'mysql') return;
const connection = await createConnection(getBootstrapDBOptions(dbType));
const connection = new Connection(getBootstrapDBOptions(dbType));
await connection.initialize();
const query =
dbType === 'postgres' ? 'SELECT datname as "Database" FROM pg_database' : 'SHOW DATABASES';
@ -20,5 +21,5 @@ export default async () => {
const promises = databases.map((dbName) => connection.query(`DROP DATABASE ${dbName};`));
await Promise.all(promises);
await connection.close();
await connection.destroy();
};

View file

@ -25,15 +25,13 @@ import type { SaveCredentialFunction } from '../integration/shared/types';
import { User } from '@/databases/entities/User';
import { SharedWorkflow } from '@/databases/entities/SharedWorkflow';
let testDbName = '';
let mockNodeTypes: INodeTypes;
let credentialOwnerRole: Role;
let workflowOwnerRole: Role;
let saveCredential: SaveCredentialFunction;
beforeAll(async () => {
const initResult = await testDb.init();
testDbName = initResult.testDbName;
await testDb.init();
mockNodeTypes = MockNodeTypes({
loaded: {
@ -51,12 +49,12 @@ beforeAll(async () => {
});
beforeEach(async () => {
await testDb.truncate(['SharedWorkflow', 'SharedCredentials'], testDbName);
await testDb.truncate(['User', 'Workflow', 'Credentials'], testDbName);
await testDb.truncate(['SharedWorkflow', 'SharedCredentials']);
await testDb.truncate(['User', 'Workflow', 'Credentials']);
});
afterAll(async () => {
await testDb.terminate(testDbName);
await testDb.terminate();
});
describe('PermissionChecker.check()', () => {

View file

@ -39,7 +39,7 @@ describe('Telemetry', () => {
beforeEach(() => {
spyTrack.mockClear();
telemetry = new Telemetry(instanceId, n8nVersion);
telemetry = new Telemetry(instanceId);
(telemetry as any).rudderStack = {
flush: () => {},
identify: () => {},

View file

@ -3,7 +3,7 @@ import type { INode, IWorkflowCredentials } from 'n8n-workflow';
import * as Db from '@/Db';
import { WorkflowCredentials } from '@/WorkflowCredentials';
// Define a function used to mock the findOne function
// Define a function used to mock the findOneBy function
async function mockFind({
id,
type,
@ -33,7 +33,7 @@ jest.mock('@/Db', () => {
return {
collections: {
Credentials: {
findOne: jest.fn(mockFind),
findOneBy: jest.fn(mockFind),
},
},
};
@ -54,7 +54,7 @@ describe('WorkflowCredentials', () => {
`Credentials with name "${credentials.name}" for type "test" miss an ID.`,
);
expect(WorkflowCredentials([noIdNode])).rejects.toEqual(expectedError);
expect(mocked(Db.collections.Credentials.findOne)).toHaveBeenCalledTimes(0);
expect(mocked(Db.collections.Credentials.findOneBy)).toHaveBeenCalledTimes(0);
});
test('Should return an error if credentials cannot be found in the DB', () => {
@ -63,7 +63,7 @@ describe('WorkflowCredentials', () => {
`Could not find credentials for type "test" with ID "${credentials.id}".`,
);
expect(WorkflowCredentials([notFoundNode])).rejects.toEqual(expectedError);
expect(mocked(Db.collections.Credentials.findOne)).toHaveBeenCalledTimes(1);
expect(mocked(Db.collections.Credentials.findOneBy)).toHaveBeenCalledTimes(1);
});
test('Should ignore duplicates', async () => {

View file

@ -184,7 +184,7 @@ importers:
lodash.uniqby: ^4.7.0
lodash.unset: ^4.5.2
luxon: ^3.1.0
mysql2: ~2.3.0
mysql2: ~2.3.3
n8n-core: ~0.151.0
n8n-editor-ui: ~0.177.0
n8n-nodes-base: ~0.209.0
@ -199,7 +199,7 @@ importers:
passport: ^0.6.0
passport-cookie: ^1.0.9
passport-jwt: ^4.0.0
pg: ^8.3.0
pg: ^8.8.0
picocolors: ^1.0.0
posthog-node: ^2.2.2
prom-client: ^13.1.0
@ -209,7 +209,7 @@ importers:
semver: ^7.3.8
shelljs: ^0.8.5
source-map-support: ^0.5.21
sqlite3: ^5.1.2
sqlite3: ^5.1.4
sse-channel: ^4.0.0
supertest: ^6.2.2
swagger-ui-express: ^4.3.0
@ -218,7 +218,7 @@ importers:
tsc-alias: ^1.7.0
tsconfig-paths: ^3.14.1
tslib: 1.14.1
typeorm: 0.2.45
typeorm: 0.3.11
uuid: ^8.3.2
validator: 13.7.0
winston: ^3.3.3
@ -299,12 +299,12 @@ importers:
semver: 7.3.8
shelljs: 0.8.5
source-map-support: 0.5.21
sqlite3: 5.1.2
sqlite3: 5.1.4
sse-channel: 4.0.0
swagger-ui-express: 4.5.0_express@4.18.2
syslog-client: 1.1.1
tslib: 1.14.1
typeorm: 0.2.45_b2izk5tn6tm5xb65gvog337urq
typeorm: 0.3.11_a77gzgdqnod3rkvxniiwirlqsi
uuid: 8.3.2
validator: 13.7.0
winston: 3.8.2
@ -3481,6 +3481,7 @@ packages:
/@npmcli/move-file/1.1.2:
resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==}
engines: {node: '>=10'}
deprecated: This functionality has been moved to @npmcli/fs
dependencies:
mkdirp: 1.0.4
rimraf: 3.0.2
@ -6407,10 +6408,6 @@ packages:
dev: true
optional: true
/@types/zen-observable/0.8.3:
resolution: {integrity: sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==}
dev: false
/@typescript-eslint/eslint-plugin/5.45.0_psz44bhp76u27vmulntnlx26h4:
resolution: {integrity: sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
@ -7378,7 +7375,6 @@ packages:
/arg/4.1.3:
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
dev: true
/argparse/1.0.10:
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
@ -9707,7 +9703,6 @@ packages:
/create-require/1.1.1:
resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==}
dev: true
/crelt/1.0.5:
resolution: {integrity: sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==}
@ -9998,7 +9993,6 @@ packages:
/date-fns/2.29.3:
resolution: {integrity: sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==}
engines: {node: '>=0.11'}
dev: true
/date-now/0.1.4:
resolution: {integrity: sha512-AsElvov3LoNB7tf5k37H2jYSB+ZZPMT5sG2QjJCcdlV5chIv6htBUBUui2IKRjgtKAKtCBN7Zbwa+MtwLjSeNw==}
@ -10307,7 +10301,6 @@ packages:
/diff/4.0.2:
resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==}
engines: {node: '>=0.3.1'}
dev: true
/diffie-hellman/5.0.3:
resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==}
@ -10460,7 +10453,6 @@ packages:
/dotenv/16.0.3:
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
engines: {node: '>=12'}
dev: true
/dotenv/8.6.0:
resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==}
@ -19609,8 +19601,8 @@ packages:
resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==}
dev: false
/sqlite3/5.1.2:
resolution: {integrity: sha512-D0Reg6pRWAFXFUnZKsszCI67tthFD8fGPewRddDCX6w4cYwz3MbvuwRICbL+YQjBAh9zbw+lJ/V9oC8nG5j6eg==}
/sqlite3/5.1.4:
resolution: {integrity: sha512-i0UlWAzPlzX3B5XP2cYuhWQJsTtlMD6obOa1PgeEQ4DHEXUuyJkgv50I3isqZAP5oFc2T8OFvakmDh2W6I+YpA==}
requiresBuild: true
peerDependenciesMeta:
node-gyp:
@ -20732,7 +20724,6 @@ packages:
source-map-support: 0.5.21
typescript: 4.8.4
yn: 3.1.1
dev: true
/ts-pnp/1.2.0_typescript@4.8.4:
resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==}
@ -20935,26 +20926,31 @@ packages:
/typedarray/0.0.6:
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
/typeorm/0.2.45_b2izk5tn6tm5xb65gvog337urq:
resolution: {integrity: sha512-c0rCO8VMJ3ER7JQ73xfk0zDnVv0WDjpsP6Q1m6CVKul7DB9iVdWLRjPzc8v2eaeBuomsbZ2+gTaYr8k1gm3bYA==}
/typeorm/0.3.11_a77gzgdqnod3rkvxniiwirlqsi:
resolution: {integrity: sha512-pzdOyWbVuz/z8Ww6gqvBW4nylsM0KLdUCDExr2gR20/x1khGSVxQkjNV/3YqliG90jrWzrknYbYscpk8yxFJVg==}
engines: {node: '>= 12.9.0'}
hasBin: true
peerDependencies:
'@sap/hana-client': ^2.11.14
better-sqlite3: ^7.1.2
'@google-cloud/spanner': ^5.18.0
'@sap/hana-client': ^2.12.25
better-sqlite3: ^7.1.2 || ^8.0.0
hdb-pool: ^0.1.6
ioredis: ^4.28.3
ioredis: ^5.0.4
mongodb: ^3.6.0
mssql: ^6.3.1
mssql: ^7.3.0
mysql2: ^2.2.5
oracledb: ^5.1.0
pg: ^8.5.1
pg-native: ^3.0.0
pg-query-stream: ^4.0.0
redis: ^3.1.1
redis: ^3.1.1 || ^4.0.0
sql.js: ^1.4.0
sqlite3: ^5.0.2
sqlite3: ^5.0.3
ts-node: ^10.7.0
typeorm-aurora-data-api-driver: ^2.0.0
peerDependenciesMeta:
'@google-cloud/spanner':
optional: true
'@sap/hana-client':
optional: true
better-sqlite3:
@ -20983,6 +20979,8 @@ packages:
optional: true
sqlite3:
optional: true
ts-node:
optional: true
typeorm-aurora-data-api-driver:
optional: true
dependencies:
@ -20991,8 +20989,9 @@ packages:
buffer: 6.0.3
chalk: 4.1.2
cli-highlight: 2.1.11
date-fns: 2.29.3
debug: 4.3.4
dotenv: 8.6.0
dotenv: 16.0.3
glob: 7.2.3
ioredis: 5.2.4
js-yaml: 4.1.0
@ -21001,12 +21000,12 @@ packages:
pg: 8.8.0
reflect-metadata: 0.1.13
sha.js: 2.4.11
sqlite3: 5.1.2
sqlite3: 5.1.4
ts-node: 9.1.1_typescript@4.8.4
tslib: 2.4.0
uuid: 8.3.2
xml2js: 0.4.23
yargs: 17.6.0
zen-observable-ts: 1.1.0
transitivePeerDependencies:
- supports-color
dev: false
@ -22715,7 +22714,6 @@ packages:
/yn/3.1.1:
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
engines: {node: '>=6'}
dev: true
/yocto-queue/0.1.0:
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
@ -22747,17 +22745,6 @@ packages:
commander: 2.20.3
dev: true
/zen-observable-ts/1.1.0:
resolution: {integrity: sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA==}
dependencies:
'@types/zen-observable': 0.8.3
zen-observable: 0.8.15
dev: false
/zen-observable/0.8.15:
resolution: {integrity: sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==}
dev: false
/zwitch/1.0.5:
resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==}
dev: true