Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Upgrade typeorm to 0.3.x #5151

Merged
merged 3 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down
5 changes: 2 additions & 3 deletions packages/cli/src/AbstractServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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!');
}
Expand Down
41 changes: 24 additions & 17 deletions packages/cli/src/ActiveWorkflowRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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}"`,
);
Expand Down Expand Up @@ -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[] = [];
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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}"`);
}

Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
9 changes: 5 additions & 4 deletions packages/cli/src/CommunityNodes/packageModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/CredentialsHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
18 changes: 10 additions & 8 deletions packages/cli/src/Db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -34,14 +32,16 @@ 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);
}

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> {
Expand Down Expand Up @@ -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.
Expand All @@ -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();
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/PublicApi/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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(
Expand Down Expand Up @@ -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',
});
Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -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;
Expand Down Expand Up @@ -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)`, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
});
Expand Down
Loading