🚧 Unify database entities

This commit is contained in:
Iván Ovejero 2021-03-11 13:35:57 -03:00
parent 1231c4d670
commit 558ec49cf3
9 changed files with 327 additions and 24 deletions

View file

@ -18,11 +18,7 @@ import { TlsOptions } from 'tls';
import * as config from '../config'; import * as config from '../config';
import { import { entities } from './databases/entities';
MySQLDb,
PostgresDb,
SQLite,
} from './databases';
export let collections: IDatabaseCollections = { export let collections: IDatabaseCollections = {
Credentials: null, Credentials: null,
@ -41,15 +37,12 @@ export async function init(): Promise<IDatabaseCollections> {
const dbType = await GenericHelpers.getConfigValue('database.type') as DatabaseType; const dbType = await GenericHelpers.getConfigValue('database.type') as DatabaseType;
const n8nFolder = UserSettings.getUserN8nFolderPath(); const n8nFolder = UserSettings.getUserN8nFolderPath();
let entities;
let connectionOptions: ConnectionOptions; let connectionOptions: ConnectionOptions;
const entityPrefix = config.get('database.tablePrefix'); const entityPrefix = config.get('database.tablePrefix');
switch (dbType) { switch (dbType) {
case 'postgresdb': case 'postgresdb':
entities = PostgresDb;
const sslCa = await GenericHelpers.getConfigValue('database.postgresdb.ssl.ca') as string; const sslCa = await GenericHelpers.getConfigValue('database.postgresdb.ssl.ca') as string;
const sslCert = await GenericHelpers.getConfigValue('database.postgresdb.ssl.cert') as string; const sslCert = await GenericHelpers.getConfigValue('database.postgresdb.ssl.cert') as string;
const sslKey = await GenericHelpers.getConfigValue('database.postgresdb.ssl.key') as string; const sslKey = await GenericHelpers.getConfigValue('database.postgresdb.ssl.key') as string;
@ -84,7 +77,6 @@ export async function init(): Promise<IDatabaseCollections> {
case 'mariadb': case 'mariadb':
case 'mysqldb': case 'mysqldb':
entities = MySQLDb;
connectionOptions = { connectionOptions = {
type: dbType === 'mysqldb' ? 'mysql' : 'mariadb', type: dbType === 'mysqldb' ? 'mysql' : 'mariadb',
database: await GenericHelpers.getConfigValue('database.mysqldb.database') as string, database: await GenericHelpers.getConfigValue('database.mysqldb.database') as string,
@ -100,7 +92,6 @@ export async function init(): Promise<IDatabaseCollections> {
break; break;
case 'sqlite': case 'sqlite':
entities = SQLite;
connectionOptions = { connectionOptions = {
type: 'sqlite', type: 'sqlite',
database: path.join(n8nFolder, 'database.sqlite'), database: path.join(n8nFolder, 'database.sqlite'),

View file

@ -3,6 +3,7 @@ import * as express from 'express';
import { join as pathJoin } from 'path'; import { join as pathJoin } from 'path';
import { import {
readFile as fsReadFile, readFile as fsReadFile,
readFileSync as fsReadFileSync,
} from 'fs'; } from 'fs';
import { promisify } from 'util'; import { promisify } from 'util';
import { IDataObject } from 'n8n-workflow'; import { IDataObject } from 'n8n-workflow';
@ -13,7 +14,6 @@ const fsReadFileAsync = promisify(fsReadFile);
let versionCache: IPackageVersions | undefined; let versionCache: IPackageVersions | undefined;
/** /**
* Displays a message to the user * Displays a message to the user
* *
@ -82,20 +82,16 @@ export async function getVersions(): Promise<IPackageVersions> {
return versionCache; return versionCache;
} }
/** /**
* Gets value from config with support for "_FILE" environment variables * Retrieves configuration values from env
* *
* @export * @param {string} configKey
* @param {string} configKey The key of the config data to get * @param {IDataObject} currentSchema
* @returns {(Promise<string | boolean | number | undefined>)} * @returns {string | boolean | number | undefined | void}
*/ */
export async function getConfigValue(configKey: string): Promise<string | boolean | number | undefined> { function getConfigFromEnv(configKey: string, currentSchema: IDataObject): string | boolean | number | undefined | void {
const configKeyParts = configKey.split('.'); const configKeyParts = configKey.split('.');
// Get the environment variable
const configSchema = config.getSchema();
let currentSchema = configSchema.properties as IDataObject;
for (const key of configKeyParts) { for (const key of configKeyParts) {
if (currentSchema[key] === undefined) { if (currentSchema[key] === undefined) {
throw new Error(`Key "${key}" of ConfigKey "${configKey}" does not exist`); throw new Error(`Key "${key}" of ConfigKey "${configKey}" does not exist`);
@ -113,18 +109,64 @@ export async function getConfigValue(configKey: string): Promise<string | boolea
} }
// Check if special file enviroment variable exists // Check if special file enviroment variable exists
const fileEnvironmentVariable = process.env[currentSchema.env + '_FILE']; if (process.env[currentSchema.env + '_FILE'] === undefined) {
if (fileEnvironmentVariable === undefined) {
// Does not exist, so return value from config // Does not exist, so return value from config
return config.get(configKey); return config.get(configKey);
} }
}
/**
* Gets value from config with support for "_FILE" environment variables
*
* @export
* @param {string} configKey The key of the config data to get
* @returns {(Promise<string | boolean | number | undefined>)}
*/
export async function getConfigValue(configKey: string): Promise<string | boolean | number | undefined> {
// Get the environment variable
const configSchema = config.getSchema();
const currentSchema = configSchema.properties as IDataObject;
const envConfig = getConfigFromEnv(configKey, currentSchema);
if (envConfig) {
return envConfig;
}
let data; let data;
try { try {
data = await fsReadFileAsync(fileEnvironmentVariable, 'utf8') as string; data = await fsReadFileAsync(process.env[currentSchema.env + '_FILE']!, 'utf8') as string;
} catch (error) { } catch (error) {
if (error.code === 'ENOENT') { if (error.code === 'ENOENT') {
throw new Error(`The file "${fileEnvironmentVariable}" could not be found.`); throw new Error(`The file "${process.env[currentSchema.env + '_FILE']}" could not be found.`);
}
throw error;
}
return data;
}
/**
* Gets value from config with support for "_FILE" environment variables synchronously
*
* @export
* @param {string} configKey The key of the config data to get
* @returns {(string | boolean | number | undefined)}
*/
export function getConfigValueSync(configKey: string): string | boolean | number | undefined {
// Get the environment variable
const configSchema = config.getSchema();
const currentSchema = configSchema.properties as IDataObject;
const envConfig = getConfigFromEnv(configKey, currentSchema);
if (envConfig) {
return envConfig;
}
let data;
try {
data = fsReadFileSync(process.env[currentSchema.env + '_FILE']!, 'utf8') as string;
} catch (error) {
if (error.code === 'ENOENT') {
throw new Error(`The file "${process.env[currentSchema.env + '_FILE']}" could not be found.`);
} }
throw error; throw error;

View file

@ -0,0 +1,48 @@
import {
ICredentialNodeAccess,
} from 'n8n-workflow';
import {
resolveDataType
} from '../utils';
import {
ICredentialsDb,
} from '../..';
import {
Column,
Entity,
Index,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity()
export class CredentialsEntity implements ICredentialsDb {
@PrimaryGeneratedColumn()
id: number;
@Column({
length: 128,
})
name: string;
@Column('text')
data: string;
@Index()
@Column({
length: 32,
})
type: string;
@Column(resolveDataType('json'))
nodesAccess: ICredentialNodeAccess[];
@Column(resolveDataType('datetime'))
createdAt: Date;
@Column(resolveDataType('datetime'))
updatedAt: Date;
}

View file

@ -0,0 +1,56 @@
import {
WorkflowExecuteMode,
} from 'n8n-workflow';
import {
IExecutionFlattedDb,
IWorkflowDb,
} from '../../';
import {
resolveDataType
} from '../utils';
import {
Column,
ColumnOptions,
Entity,
Index,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity()
export class ExecutionEntity implements IExecutionFlattedDb {
@PrimaryGeneratedColumn()
id: number;
@Column('text')
data: string;
@Column()
finished: boolean;
@Column('varchar')
mode: WorkflowExecuteMode;
@Column({ nullable: true })
retryOf: string;
@Column({ nullable: true })
retrySuccessId: string;
@Column(resolveDataType('datetime'))
startedAt: Date;
@Index()
@Column({ type: resolveDataType('datetime') as ColumnOptions['type'], nullable: true })
stoppedAt: Date;
@Column(resolveDataType('json'))
workflowData: IWorkflowDb;
@Index()
@Column({ nullable: true })
workflowId: string;
}

View file

@ -0,0 +1,23 @@
import { Column, Entity, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
import { ITagDb } from '../../Interfaces';
import { WorkflowEntity } from './WorkflowEntity';
@Entity()
export class TagEntity implements ITagDb {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true, length: 24 })
name: string;
@Column()
createdAt: Date;
@Column()
updatedAt: Date;
@ManyToMany(() => WorkflowEntity, workflowEntity => workflowEntity.tags)
workflows: WorkflowEntity[];
}

View file

@ -0,0 +1,33 @@
import {
Column,
Entity,
Index,
PrimaryColumn,
} from 'typeorm';
import {
IWebhookDb,
} from '../../Interfaces';
@Entity()
@Index(['webhookId', 'method', 'pathLength'])
export class WebhookEntity implements IWebhookDb {
@Column()
workflowId: number;
@PrimaryColumn()
webhookPath: string;
@PrimaryColumn()
method: string;
@Column()
node: string;
@Column({ nullable: true })
webhookId: string;
@Column({ nullable: true })
pathLength: number;
}

View file

@ -0,0 +1,70 @@
import {
IConnections,
IDataObject,
INode,
IWorkflowSettings,
} from 'n8n-workflow';
import {
Column,
ColumnOptions,
Entity,
JoinTable,
ManyToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
import {
IWorkflowDb,
} from '../../';
import {
resolveDataType
} from '../utils';
import {
TagEntity,
} from './TagEntity';
@Entity()
export class WorkflowEntity implements IWorkflowDb {
@PrimaryGeneratedColumn()
id: number;
@Column({
length: 128,
})
name: string;
@Column()
active: boolean;
@Column(resolveDataType('json'))
nodes: INode[];
@Column(resolveDataType('json'))
connections: IConnections;
@Column(resolveDataType('datetime'))
createdAt: Date;
@Column(resolveDataType('datetime'))
updatedAt: Date;
@Column({
type: resolveDataType('json') as ColumnOptions['type'],
nullable: true,
})
settings?: IWorkflowSettings;
@Column({
type: resolveDataType('json') as ColumnOptions['type'],
nullable: true,
})
staticData?: IDataObject;
@ManyToMany(() => TagEntity, tagEntity => tagEntity.workflows)
@JoinTable({ name: "workflows_tags" })
tags: TagEntity[];
}

View file

@ -0,0 +1,11 @@
import { CredentialsEntity } from './CredentialsEntity';
import { ExecutionEntity } from './ExecutionEntity';
import { WorkflowEntity } from './WorkflowEntity';
import { WebhookEntity } from './WebhookEntity';
export const entities = {
CredentialsEntity,
ExecutionEntity,
WorkflowEntity,
WebhookEntity,
};

View file

@ -0,0 +1,29 @@
import {
DatabaseType,
GenericHelpers,
} from '../';
/**
* Resolves the data type for the used database type
*
* @export
* @param {string} dataType
* @returns {string}
*/
export function resolveDataType(dataType: string) {
const dbType = GenericHelpers.getConfigValueSync('database.type') as DatabaseType;
const typeMap: { [key in DatabaseType]: { [key: string]: string } } = {
sqlite: {
json: 'simple-json',
},
postgresdb: {
datetime: 'timestamp',
},
mysqldb: {},
mariadb: {},
};
return typeMap[dbType][dataType] ?? dataType;
}