Added support for MySQL

* In packages/cli/src/Db.ts, conditional test of dbType replaced by a
switch;
* removeAll() function adapted to not cause an error using MySQL;
* Added the cross-env module in the "serve" script in the
packages/editor-ui/package.json file. This was done to ensure
compatibility between platforms when declaring environment variables.
Without it, Windows compilation would give an error, for example;
* .idea added to .gitignore (IntelliJ IDEA solutions);
This commit is contained in:
Guilherme Almeida Girardi 2020-02-10 13:09:06 -03:00
parent 1777f171bd
commit 3bdd9096e1
11 changed files with 260 additions and 45 deletions

1
.gitignore vendored
View file

@ -11,3 +11,4 @@ google-generated-credentials.json
_START_PACKAGE
.env
.vscode
.idea

View file

@ -8,7 +8,7 @@ const config = convict({
database: {
type: {
doc: 'Type of database to use',
format: ['sqlite', 'mongodb', 'postgresdb'],
format: ['sqlite', 'mongodb', 'mysqldb', 'postgresdb'],
default: 'sqlite',
env: 'DB_TYPE'
},
@ -52,6 +52,38 @@ const config = convict({
env: 'DB_POSTGRESDB_USER'
},
},
mysqldb: {
database: {
doc: 'MySQL Database',
format: String,
default: 'n8n',
env: 'DB_MYSQLDB_DATABASE'
},
host: {
doc: 'MySQL Host',
format: String,
default: 'localhost',
env: 'DB_MYSQLDB_HOST'
},
password: {
doc: 'MySQL Password',
format: String,
default: '',
env: 'DB_MYSQLDB_PASSWORD'
},
port: {
doc: 'MySQL Port',
format: Number,
default: 5432,
env: 'DB_MYSQLDB_PORT'
},
user: {
doc: 'MySQL User',
format: String,
default: 'root',
env: 'DB_MYSQLDB_USER'
},
},
},
executions: {

View file

@ -18,6 +18,7 @@ import {
MongoDb,
PostgresDb,
SQLite,
MySQLDb,
} from './databases';
export let collections: IDatabaseCollections = {
@ -36,33 +37,53 @@ export async function init(synchronize?: boolean): Promise<IDatabaseCollections>
let connectionOptions: ConnectionOptions;
let dbNotExistError: string | undefined;
if (dbType === 'mongodb') {
entities = MongoDb;
connectionOptions = {
type: 'mongodb',
url: await GenericHelpers.getConfigValue('database.mongodb.connectionUrl') as string,
useNewUrlParser: true,
};
} else if (dbType === 'postgresdb') {
dbNotExistError = 'does not exist';
entities = PostgresDb;
connectionOptions = {
type: 'postgres',
database: await GenericHelpers.getConfigValue('database.postgresdb.database') as string,
host: await GenericHelpers.getConfigValue('database.postgresdb.host') as string,
password: await GenericHelpers.getConfigValue('database.postgresdb.password') as string,
port: await GenericHelpers.getConfigValue('database.postgresdb.port') as number,
username: await GenericHelpers.getConfigValue('database.postgresdb.user') as string,
};
} else if (dbType === 'sqlite') {
dbNotExistError = 'no such table:';
entities = SQLite;
connectionOptions = {
type: 'sqlite',
database: path.join(n8nFolder, 'database.sqlite'),
};
} else {
throw new Error(`The database "${dbType}" is currently not supported!`);
switch (dbType) {
case 'mongodb':
entities = MongoDb;
connectionOptions = {
type: 'mongodb',
url: await GenericHelpers.getConfigValue('database.mongodb.connectionUrl') as string,
useNewUrlParser: true,
};
break;
case 'postgresdb':
dbNotExistError = 'does not exist';
entities = PostgresDb;
connectionOptions = {
type: 'postgres',
database: await GenericHelpers.getConfigValue('database.postgresdb.database') as string,
host: await GenericHelpers.getConfigValue('database.postgresdb.host') as string,
password: await GenericHelpers.getConfigValue('database.postgresdb.password') as string,
port: await GenericHelpers.getConfigValue('database.postgresdb.port') as number,
username: await GenericHelpers.getConfigValue('database.postgresdb.user') as string,
};
break;
case 'mysqldb':
dbNotExistError = 'does not exist';
entities = MySQLDb;
connectionOptions = {
type: 'mysql',
database: await GenericHelpers.getConfigValue('database.mysqldb.database') as string,
host: await GenericHelpers.getConfigValue('database.mysqldb.host') as string,
password: await GenericHelpers.getConfigValue('database.mysqldb.password') as string,
port: await GenericHelpers.getConfigValue('database.mysqldb.port') as number,
username: await GenericHelpers.getConfigValue('database.mysqldb.user') as string
};
break;
case 'sqlite':
dbNotExistError = 'no such table:';
entities = SQLite;
connectionOptions = {
type: 'sqlite',
database: path.join(n8nFolder, 'database.sqlite'),
};
break;
default:
throw new Error(`The database "${dbType}" is currently not supported!`);
}
Object.assign(connectionOptions, {

View file

@ -87,7 +87,7 @@ export interface ICredentialsDecryptedResponse extends ICredentialsDecryptedDb {
id: string;
}
export type DatabaseType = 'mongodb' | 'postgresdb' | 'sqlite';
export type DatabaseType = 'mongodb' | 'postgresdb' | 'mysqldb' | 'sqlite';
export type SaveExecutionDataType = 'all' | 'none';
export interface IExecutionBase {

View file

@ -208,26 +208,32 @@ export class TestWebhooks {
}
const nodeTypes = NodeTypes();
const findQuery = {
where: {
id: findIn(this.activeWebhooks.getWorkflowIds())
},
} as FindManyOptions;
/*
* Here I check first if WorkflowIds is empty. This is done because when entering an empty array for TypeORM's In option, a syntax error is generated in MySQL.
* Because the SQL is: ... FROM `workflow_entity`` WorkflowEntity` WHERE `WorkflowEntity`.`id` IN ()
*
* The empty IN function is not accepted in MySQL.
*/
const WorkflowIds = this.activeWebhooks.getWorkflowIds();
if (WorkflowIds.length > 0) {
const findQuery = {
where: {
id: findIn(WorkflowIds)
},
} as FindManyOptions;
const workflowsDb = await Db.collections.Workflow!.find(findQuery);
const workflows: Workflow[] = [];
for (const workflowData of workflowsDb) {
const workflow = new Workflow(workflowData.id.toString(), workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings);
workflows.push(workflow);
const workflowsDb = await Db.collections.Workflow!.find(findQuery);
const workflows: Workflow[] = [];
for (const workflowData of workflowsDb) {
const workflow = new Workflow(workflowData.id.toString(), workflowData.nodes, workflowData.connections, workflowData.active, nodeTypes, workflowData.staticData, workflowData.settings);
workflows.push(workflow);
}
return this.activeWebhooks.removeAll(workflows);
}
return this.activeWebhooks.removeAll(workflows);
}
}
let testWebhooksInstance: TestWebhooks | undefined;
export function getInstance(): TestWebhooks {

View file

@ -1,9 +1,11 @@
import * as MongoDb from './mongodb';
import * as PostgresDb from './postgresdb';
import * as SQLite from './sqlite';
import * as MySQLDb from './mysqldb';
export {
MongoDb,
PostgresDb,
SQLite,
MySQLDb,
};

View file

@ -0,0 +1,44 @@
import {
ICredentialNodeAccess,
} from 'n8n-workflow';
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('json')
nodesAccess: ICredentialNodeAccess[];
@Column('timestamp')
createdAt: Date;
@Column('timestamp')
updatedAt: Date;
}

View file

@ -0,0 +1,51 @@
import {
WorkflowExecuteMode,
} from 'n8n-workflow';
import {
IExecutionFlattedDb,
IWorkflowDb,
} from '../../';
import {
Column,
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('timestamp')
startedAt: Date;
@Column('timestamp')
stoppedAt: Date;
@Column('json')
workflowData: IWorkflowDb;
@Index()
@Column({ nullable: true })
workflowId: string;
}

View file

@ -0,0 +1,55 @@
import {
IConnections,
IDataObject,
INode,
IWorkflowSettings,
} from 'n8n-workflow';
import {
IWorkflowDb,
} from '../../';
import {
Column,
Entity,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity()
export class WorkflowEntity implements IWorkflowDb {
@PrimaryGeneratedColumn()
id: number;
@Column({
length: 128
})
name: string;
@Column()
active: boolean;
@Column('json')
nodes: INode[];
@Column('json')
connections: IConnections;
@Column('timestamp')
createdAt: Date;
@Column('timestamp')
updatedAt: Date;
@Column({
type: 'json',
nullable: true,
})
settings?: IWorkflowSettings;
@Column({
type: 'json',
nullable: true,
})
staticData?: IDataObject;
}

View file

@ -0,0 +1,3 @@
export * from './CredentialsEntity';
export * from './ExecutionEntity';
export * from './WorkflowEntity';

View file

@ -17,7 +17,7 @@
"build": "vue-cli-service build",
"dev": "npm run serve",
"lint": "vue-cli-service lint",
"serve": "VUE_APP_URL_BASE_API=http://localhost:5678/ vue-cli-service serve",
"serve": "cross-env VUE_APP_URL_BASE_API=http://localhost:5678/ vue-cli-service serve",
"test": "npm run test:unit",
"tslint": "tslint -p tsconfig.json -c tslint.json",
"test:e2e": "vue-cli-service test:e2e",