Skip to content

Commit

Permalink
refactor: Upgrade typeorm to 0.3.x (#5151)
Browse files Browse the repository at this point in the history
  • Loading branch information
netroy authored Jan 13, 2023
1 parent 6608e69 commit 0a5ab56
Show file tree
Hide file tree
Showing 85 changed files with 573 additions and 630 deletions.
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

0 comments on commit 0a5ab56

Please sign in to comment.