diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 9492b4cb6..2759c6d79 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -185,6 +185,7 @@ const config = convict({ // in the editor. saveDataManualExecutions: { doc: 'Save data of executions when started manually via editor', + format: 'Boolean', default: false, env: 'EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS' }, @@ -196,16 +197,19 @@ const config = convict({ // a future version. pruneData: { doc: 'Delete data of past executions on a rolling basis', + format: 'Boolean', default: false, env: 'EXECUTIONS_DATA_PRUNE' }, pruneDataMaxAge: { doc: 'How old (hours) the execution data has to be to get deleted', + format: Number, default: 336, env: 'EXECUTIONS_DATA_MAX_AGE' }, pruneDataTimeout: { doc: 'Timeout (seconds) after execution data has been pruned', + format: Number, default: 3600, env: 'EXECUTIONS_DATA_PRUNE_TIMEOUT' }, diff --git a/packages/cli/migrations/ormconfig.ts b/packages/cli/migrations/ormconfig.ts index 2a0cda0d9..1ea583beb 100644 --- a/packages/cli/migrations/ormconfig.ts +++ b/packages/cli/migrations/ormconfig.ts @@ -44,9 +44,9 @@ module.exports = [ "logging": false, "host": "localhost", "username": "postgres", - "password": "docker", + "password": "", "port": 5432, - "database": "postgres", + "database": "n8n", "schema": "public", "entities": Object.values(PostgresDb), "migrations": [ @@ -68,7 +68,7 @@ module.exports = [ "username": "root", "password": "password", "host": "localhost", - "port": "3308", + "port": "3306", "logging": false, "entities": Object.values(MySQLDb), "migrations": [ @@ -90,7 +90,7 @@ module.exports = [ "username": "root", "password": "password", "host": "localhost", - "port": "3308", + "port": "3306", "logging": false, "entities": Object.values(MySQLDb), "migrations": [ @@ -105,4 +105,4 @@ module.exports = [ "subscribersDir": "./src/databases/mysqldb/Subscribers" } }, -]; \ No newline at end of file +]; diff --git a/packages/cli/package.json b/packages/cli/package.json index e9b16ad9e..4749a2ed0 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "0.73.1", + "version": "0.74.0", "description": "n8n Workflow Automation Tool", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -100,10 +100,10 @@ "lodash.get": "^4.4.2", "mongodb": "^3.5.5", "mysql2": "^2.0.1", - "n8n-core": "~0.38.0", - "n8n-editor-ui": "~0.49.0", - "n8n-nodes-base": "~0.68.1", - "n8n-workflow": "~0.34.0", + "n8n-core": "~0.39.0", + "n8n-editor-ui": "~0.50.0", + "n8n-nodes-base": "~0.69.0", + "n8n-workflow": "~0.35.0", "oauth-1.0a": "^2.2.6", "open": "^7.0.0", "pg": "^7.11.0", diff --git a/packages/cli/src/Db.ts b/packages/cli/src/Db.ts index 3739b779e..06eeca797 100644 --- a/packages/cli/src/Db.ts +++ b/packages/cli/src/Db.ts @@ -35,21 +35,25 @@ export let collections: IDatabaseCollections = { import { InitialMigration1587669153312, WebhookModel1589476000887, + CreateIndexStoppedAt1594828256133, } from './databases/postgresdb/migrations'; import { InitialMigration1587563438936, WebhookModel1592679094242, + CreateIndexStoppedAt1594910478695, } from './databases/mongodb/migrations'; import { InitialMigration1588157391238, WebhookModel1592447867632, + CreateIndexStoppedAt1594902918301, } from './databases/mysqldb/migrations'; import { InitialMigration1588102412422, WebhookModel1592445003908, + CreateIndexStoppedAt1594825041918, } from './databases/sqlite/migrations'; import * as path from 'path'; @@ -71,7 +75,11 @@ export async function init(): Promise { entityPrefix, url: await GenericHelpers.getConfigValue('database.mongodb.connectionUrl') as string, useNewUrlParser: true, - migrations: [InitialMigration1587563438936, WebhookModel1592679094242], + migrations: [ + InitialMigration1587563438936, + WebhookModel1592679094242, + CreateIndexStoppedAt1594910478695, + ], migrationsRun: true, migrationsTableName: `${entityPrefix}migrations`, }; @@ -104,7 +112,11 @@ export async function init(): Promise { port: await GenericHelpers.getConfigValue('database.postgresdb.port') as number, username: await GenericHelpers.getConfigValue('database.postgresdb.user') as string, schema: config.get('database.postgresdb.schema'), - migrations: [InitialMigration1587669153312, WebhookModel1589476000887], + migrations: [ + InitialMigration1587669153312, + WebhookModel1589476000887, + CreateIndexStoppedAt1594828256133, + ], migrationsRun: true, migrationsTableName: `${entityPrefix}migrations`, ssl, @@ -123,7 +135,11 @@ export async function init(): Promise { 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, - migrations: [InitialMigration1588157391238, WebhookModel1592447867632], + migrations: [ + InitialMigration1588157391238, + WebhookModel1592447867632, + CreateIndexStoppedAt1594902918301, + ], migrationsRun: true, migrationsTableName: `${entityPrefix}migrations`, }; @@ -135,7 +151,11 @@ export async function init(): Promise { type: 'sqlite', database: path.join(n8nFolder, 'database.sqlite'), entityPrefix, - migrations: [InitialMigration1588102412422, WebhookModel1592445003908], + migrations: [ + InitialMigration1588102412422, + WebhookModel1592445003908, + CreateIndexStoppedAt1594825041918 + ], migrationsRun: true, migrationsTableName: `${entityPrefix}migrations`, }; diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index ee248e4d1..09ef7e18a 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -1711,9 +1711,11 @@ class App { // Read the index file and replace the path placeholder const editorUiPath = require.resolve('n8n-editor-ui'); const filePath = pathJoin(pathDirname(editorUiPath), 'dist', 'index.html'); - let readIndexFile = readFileSync(filePath, 'utf8'); const n8nPath = config.get('path'); + + let readIndexFile = readFileSync(filePath, 'utf8'); readIndexFile = readIndexFile.replace(/\/%BASE_PATH%\//g, n8nPath); + readIndexFile = readIndexFile.replace(/\/favicon.ico/g, `${n8nPath}/favicon.ico`); // Serve the altered index.html file separately this.app.get(`/index.html`, async (req: express.Request, res: express.Response) => { diff --git a/packages/cli/src/databases/mongodb/ExecutionEntity.ts b/packages/cli/src/databases/mongodb/ExecutionEntity.ts index ba5071a36..02a639d66 100644 --- a/packages/cli/src/databases/mongodb/ExecutionEntity.ts +++ b/packages/cli/src/databases/mongodb/ExecutionEntity.ts @@ -39,6 +39,7 @@ export class ExecutionEntity implements IExecutionFlattedDb { @Column('Date') startedAt: Date; + @Index() @Column('Date') stoppedAt: Date; diff --git a/packages/cli/src/databases/mongodb/migrations/151594910478695-CreateIndexStoppedAt.ts b/packages/cli/src/databases/mongodb/migrations/151594910478695-CreateIndexStoppedAt.ts new file mode 100644 index 000000000..9cfe4480d --- /dev/null +++ b/packages/cli/src/databases/mongodb/migrations/151594910478695-CreateIndexStoppedAt.ts @@ -0,0 +1,22 @@ +import { MigrationInterface } from "typeorm"; +import { + MongoQueryRunner, +} from 'typeorm/driver/mongodb/MongoQueryRunner'; + +import * as config from '../../../../config'; + +export class CreateIndexStoppedAt1594910478695 implements MigrationInterface { + name = 'CreateIndexStoppedAt1594910478695' + + public async up(queryRunner: MongoQueryRunner): Promise { + const tablePrefix = config.get('database.tablePrefix'); + await queryRunner.manager.createCollectionIndex(`${tablePrefix}execution_entity`, 'stoppedAt', { name: `IDX_${tablePrefix}execution_entity_stoppedAt`}); + } + + public async down(queryRunner: MongoQueryRunner): Promise { + const tablePrefix = config.get('database.tablePrefix'); + await queryRunner.manager.dropCollectionIndex + (`${tablePrefix}execution_entity`, `IDX_${tablePrefix}execution_entity_stoppedAt`); + } + +} diff --git a/packages/cli/src/databases/mongodb/migrations/index.ts b/packages/cli/src/databases/mongodb/migrations/index.ts index 4072ab582..ae4a6deb3 100644 --- a/packages/cli/src/databases/mongodb/migrations/index.ts +++ b/packages/cli/src/databases/mongodb/migrations/index.ts @@ -1,2 +1,3 @@ export * from './1587563438936-InitialMigration'; export * from './1592679094242-WebhookModel'; +export * from './151594910478695-CreateIndexStoppedAt'; diff --git a/packages/cli/src/databases/mysqldb/ExecutionEntity.ts b/packages/cli/src/databases/mysqldb/ExecutionEntity.ts index e0c084fcf..3db01032b 100644 --- a/packages/cli/src/databases/mysqldb/ExecutionEntity.ts +++ b/packages/cli/src/databases/mysqldb/ExecutionEntity.ts @@ -39,6 +39,7 @@ export class ExecutionEntity implements IExecutionFlattedDb { @Column('datetime') startedAt: Date; + @Index() @Column('datetime') stoppedAt: Date; diff --git a/packages/cli/src/databases/mysqldb/migrations/1594902918301-CreateIndexStoppedAt.ts b/packages/cli/src/databases/mysqldb/migrations/1594902918301-CreateIndexStoppedAt.ts new file mode 100644 index 000000000..2cc4c8636 --- /dev/null +++ b/packages/cli/src/databases/mysqldb/migrations/1594902918301-CreateIndexStoppedAt.ts @@ -0,0 +1,20 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +import * as config from '../../../../config'; + +export class CreateIndexStoppedAt1594902918301 implements MigrationInterface { + name = 'CreateIndexStoppedAt1594902918301' + + public async up(queryRunner: QueryRunner): Promise { + const tablePrefix = config.get('database.tablePrefix'); + + await queryRunner.query('CREATE INDEX `IDX_' + tablePrefix + 'cefb067df2402f6aed0638a6c1` ON `' + tablePrefix + 'execution_entity` (`stoppedAt`)'); + } + + public async down(queryRunner: QueryRunner): Promise { + const tablePrefix = config.get('database.tablePrefix'); + + await queryRunner.query('DROP INDEX `IDX_' + tablePrefix + 'cefb067df2402f6aed0638a6c1` ON `' + tablePrefix + 'execution_entity`'); + } + +} diff --git a/packages/cli/src/databases/mysqldb/migrations/index.ts b/packages/cli/src/databases/mysqldb/migrations/index.ts index 9e5abd3b8..7c0cb217e 100644 --- a/packages/cli/src/databases/mysqldb/migrations/index.ts +++ b/packages/cli/src/databases/mysqldb/migrations/index.ts @@ -1,2 +1,3 @@ export * from './1588157391238-InitialMigration'; export * from './1592447867632-WebhookModel'; +export * from './1594902918301-CreateIndexStoppedAt'; diff --git a/packages/cli/src/databases/postgresdb/ExecutionEntity.ts b/packages/cli/src/databases/postgresdb/ExecutionEntity.ts index 8a7f691f0..8b45336c2 100644 --- a/packages/cli/src/databases/postgresdb/ExecutionEntity.ts +++ b/packages/cli/src/databases/postgresdb/ExecutionEntity.ts @@ -39,6 +39,7 @@ export class ExecutionEntity implements IExecutionFlattedDb { @Column('timestamp') startedAt: Date; + @Index() @Column('timestamp') stoppedAt: Date; diff --git a/packages/cli/src/databases/postgresdb/migrations/1594828256133-CreateIndexStoppedAt.ts b/packages/cli/src/databases/postgresdb/migrations/1594828256133-CreateIndexStoppedAt.ts new file mode 100644 index 000000000..7dd957863 --- /dev/null +++ b/packages/cli/src/databases/postgresdb/migrations/1594828256133-CreateIndexStoppedAt.ts @@ -0,0 +1,25 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +import * as config from '../../../../config'; + +export class CreateIndexStoppedAt1594828256133 implements MigrationInterface { + name = 'CreateIndexStoppedAt1594828256133' + + public async up(queryRunner: QueryRunner): Promise { + let tablePrefix = config.get('database.tablePrefix'); + const tablePrefixPure = tablePrefix; + const schema = config.get('database.postgresdb.schema'); + if (schema) { + tablePrefix = schema + '.' + tablePrefix; + } + + await queryRunner.query(`CREATE INDEX IF NOT EXISTS IDX_${tablePrefixPure}33228da131bb1112247cf52a42 ON ${tablePrefix}execution_entity ("stoppedAt") `); + } + + public async down(queryRunner: QueryRunner): Promise { + let tablePrefix = config.get('database.tablePrefix'); + + await queryRunner.query(`DROP INDEX IDX_${tablePrefix}33228da131bb1112247cf52a42`); + } + +} diff --git a/packages/cli/src/databases/postgresdb/migrations/index.ts b/packages/cli/src/databases/postgresdb/migrations/index.ts index 827f01796..3b1053706 100644 --- a/packages/cli/src/databases/postgresdb/migrations/index.ts +++ b/packages/cli/src/databases/postgresdb/migrations/index.ts @@ -1,3 +1,4 @@ export * from './1587669153312-InitialMigration'; export * from './1589476000887-WebhookModel'; +export * from './1594828256133-CreateIndexStoppedAt'; diff --git a/packages/cli/src/databases/sqlite/ExecutionEntity.ts b/packages/cli/src/databases/sqlite/ExecutionEntity.ts index 825fed7fb..bb7de2605 100644 --- a/packages/cli/src/databases/sqlite/ExecutionEntity.ts +++ b/packages/cli/src/databases/sqlite/ExecutionEntity.ts @@ -39,6 +39,7 @@ export class ExecutionEntity implements IExecutionFlattedDb { @Column() startedAt: Date; + @Index() @Column() stoppedAt: Date; diff --git a/packages/cli/src/databases/sqlite/migrations/1594825041918-CreateIndexStoppedAt.ts b/packages/cli/src/databases/sqlite/migrations/1594825041918-CreateIndexStoppedAt.ts new file mode 100644 index 000000000..cfa481202 --- /dev/null +++ b/packages/cli/src/databases/sqlite/migrations/1594825041918-CreateIndexStoppedAt.ts @@ -0,0 +1,20 @@ +import {MigrationInterface, QueryRunner} from "typeorm"; + +import * as config from '../../../../config'; + +export class CreateIndexStoppedAt1594825041918 implements MigrationInterface { + name = 'CreateIndexStoppedAt1594825041918' + + public async up(queryRunner: QueryRunner): Promise { + const tablePrefix = config.get('database.tablePrefix'); + + await queryRunner.query(`CREATE INDEX "IDX_${tablePrefix}cefb067df2402f6aed0638a6c1" ON "execution_entity" ("stoppedAt") `); + } + + public async down(queryRunner: QueryRunner): Promise { + const tablePrefix = config.get('database.tablePrefix'); + + await queryRunner.query(`DROP INDEX "IDX_${tablePrefix}cefb067df2402f6aed0638a6c1"`); + } + +} diff --git a/packages/cli/src/databases/sqlite/migrations/index.ts b/packages/cli/src/databases/sqlite/migrations/index.ts index a830a007c..f0a2068b9 100644 --- a/packages/cli/src/databases/sqlite/migrations/index.ts +++ b/packages/cli/src/databases/sqlite/migrations/index.ts @@ -1,2 +1,3 @@ export * from './1588102412422-InitialMigration'; export * from './1592445003908-WebhookModel'; +export * from './1594825041918-CreateIndexStoppedAt' diff --git a/packages/core/package.json b/packages/core/package.json index fe195d2e3..4fb55a9d8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "0.38.0", + "version": "0.39.0", "description": "Core functionality of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -46,7 +46,7 @@ "file-type": "^14.6.2", "lodash.get": "^4.4.2", "mime-types": "^2.1.27", - "n8n-workflow": "~0.34.0", + "n8n-workflow": "~0.35.0", "p-cancelable": "^2.0.0", "request": "^2.88.2", "request-promise-native": "^1.0.7" diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 552fe7933..4bcc6b857 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "0.49.0", + "version": "0.50.0", "description": "Workflow Editor UI for n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -14,7 +14,7 @@ "url": "git+https://github.com/n8n-io/n8n.git" }, "scripts": { - "build": "vue-cli-service build", + "build": "cross-env VUE_APP_PUBLIC_PATH=\"/%BASE_PATH%/\" vue-cli-service build", "dev": "npm run serve", "lint": "vue-cli-service lint", "serve": "cross-env VUE_APP_URL_BASE_API=http://localhost:5678/ vue-cli-service serve", @@ -66,7 +66,7 @@ "lodash.debounce": "^4.0.8", "lodash.get": "^4.4.2", "lodash.set": "^4.3.2", - "n8n-workflow": "~0.34.0", + "n8n-workflow": "~0.35.0", "node-sass": "^4.12.0", "prismjs": "^1.17.1", "quill": "^2.0.0-dev.3", diff --git a/packages/editor-ui/public/index.html b/packages/editor-ui/public/index.html index 9193fda97..b533aea17 100644 --- a/packages/editor-ui/public/index.html +++ b/packages/editor-ui/public/index.html @@ -4,13 +4,13 @@ - + n8n.io - Workflow Automation
diff --git a/packages/editor-ui/src/components/ExpressionInput.vue b/packages/editor-ui/src/components/ExpressionInput.vue index 543eb12f2..d0c734e2c 100644 --- a/packages/editor-ui/src/components/ExpressionInput.vue +++ b/packages/editor-ui/src/components/ExpressionInput.vue @@ -122,6 +122,13 @@ export default mixins( readOnly: !!this.resolvedValue, modules: { autoformat: {}, + keyboard: { + bindings: { + 'list autofill': { + prefix: /^$/, + }, + }, + }, }, }); diff --git a/packages/editor-ui/src/components/MainSidebar.vue b/packages/editor-ui/src/components/MainSidebar.vue index a9cf787ef..baea830b0 100644 --- a/packages/editor-ui/src/components/MainSidebar.vue +++ b/packages/editor-ui/src/components/MainSidebar.vue @@ -209,7 +209,7 @@ export default mixins( return { aboutDialogVisible: false, // @ts-ignore - basePath: window.BASE_PATH, + basePath: this.$store.getters.getBaseUrl, isCollapsed: true, credentialNewDialogVisible: false, credentialOpenDialogVisible: false, diff --git a/packages/editor-ui/src/router.ts b/packages/editor-ui/src/router.ts index 4754098c9..348a1ea66 100644 --- a/packages/editor-ui/src/router.ts +++ b/packages/editor-ui/src/router.ts @@ -9,7 +9,7 @@ Vue.use(Router); export default new Router({ mode: 'history', // @ts-ignore - base: window.BASE_PATH, + base: window.BASE_PATH === '/%BASE_PATH%/' ? '/' : window.BASE_PATH, routes: [ { path: '/execution/:id', diff --git a/packages/editor-ui/src/store.ts b/packages/editor-ui/src/store.ts index 0e1e5f14c..e0ba93b7c 100644 --- a/packages/editor-ui/src/store.ts +++ b/packages/editor-ui/src/store.ts @@ -39,7 +39,7 @@ export const store = new Vuex.Store({ activeActions: [] as string[], activeNode: null as string | null, // @ts-ignore - baseUrl: window.BASE_PATH ? window.BASE_PATH : '/', + baseUrl: process.env.VUE_APP_URL_BASE_API ? process.env.VUE_APP_URL_BASE_API : (window.BASE_PATH === '/%BASE_PATH%/' ? '/' : window.BASE_PATH), credentials: null as ICredentialsResponse[] | null, credentialTypes: null as ICredentialType[] | null, endpointWebhook: 'webhook', diff --git a/packages/editor-ui/vue.config.js b/packages/editor-ui/vue.config.js index c5ffc5fed..cdcd8259f 100644 --- a/packages/editor-ui/vue.config.js +++ b/packages/editor-ui/vue.config.js @@ -29,5 +29,5 @@ module.exports = { }, }, }, - publicPath: process.env.VUE_APP_PUBLIC_PATH ? process.env.VUE_APP_PUBLIC_PATH : '/%BASE_PATH%/', + publicPath: process.env.VUE_APP_PUBLIC_PATH ? process.env.VUE_APP_PUBLIC_PATH : '/', }; diff --git a/packages/nodes-base/credentials/CrateDb.credentials.ts b/packages/nodes-base/credentials/CrateDb.credentials.ts new file mode 100644 index 000000000..a5e0fb776 --- /dev/null +++ b/packages/nodes-base/credentials/CrateDb.credentials.ts @@ -0,0 +1,69 @@ +import { ICredentialType, NodePropertyTypes } from 'n8n-workflow'; + +export class CrateDb implements ICredentialType { + name = 'crateDb'; + displayName = 'CrateDB'; + properties = [ + { + displayName: 'Host', + name: 'host', + type: 'string' as NodePropertyTypes, + default: 'localhost', + }, + { + displayName: 'Database', + name: 'database', + type: 'string' as NodePropertyTypes, + default: 'doc', + }, + { + displayName: 'User', + name: 'user', + type: 'string' as NodePropertyTypes, + default: 'crate', + }, + { + displayName: 'Password', + name: 'password', + type: 'string' as NodePropertyTypes, + typeOptions: { + password: true, + }, + default: '', + }, + { + displayName: 'SSL', + name: 'ssl', + type: 'options' as NodePropertyTypes, + options: [ + { + name: 'disable', + value: 'disable', + }, + { + name: 'allow', + value: 'allow', + }, + { + name: 'require', + value: 'require', + }, + { + name: 'verify (not implemented)', + value: 'verify', + }, + { + name: 'verify-full (not implemented)', + value: 'verify-full', + }, + ], + default: 'disable', + }, + { + displayName: 'Port', + name: 'port', + type: 'number' as NodePropertyTypes, + default: 5432, + }, + ]; +} diff --git a/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts b/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts new file mode 100644 index 000000000..1d7ec41db --- /dev/null +++ b/packages/nodes-base/nodes/CrateDb/CrateDb.node.ts @@ -0,0 +1,246 @@ +import { IExecuteFunctions } from 'n8n-core'; +import { IDataObject, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow'; + +import * as pgPromise from 'pg-promise'; + +import { pgInsert, pgQuery, pgUpdate } from '../Postgres/Postgres.node.functions'; + +export class CrateDb implements INodeType { + description: INodeTypeDescription = { + displayName: 'CrateDB', + name: 'crateDb', + icon: 'file:cratedb.png', + group: ['input'], + version: 1, + description: 'Gets, add and update data in CrateDB.', + defaults: { + name: 'CrateDB', + color: '#47889f', + }, + inputs: ['main'], + outputs: ['main'], + credentials: [ + { + name: 'crateDb', + required: true, + }, + ], + properties: [ + { + displayName: 'Operation', + name: 'operation', + type: 'options', + options: [ + { + name: 'Execute Query', + value: 'executeQuery', + description: 'Executes a SQL query.', + }, + { + name: 'Insert', + value: 'insert', + description: 'Insert rows in database.', + }, + { + name: 'Update', + value: 'update', + description: 'Updates rows in database.', + }, + ], + default: 'insert', + description: 'The operation to perform.', + }, + + // ---------------------------------- + // executeQuery + // ---------------------------------- + { + displayName: 'Query', + name: 'query', + type: 'string', + typeOptions: { + rows: 5, + }, + displayOptions: { + show: { + operation: ['executeQuery'], + }, + }, + default: '', + placeholder: 'SELECT id, name FROM product WHERE id < 40', + required: true, + description: 'The SQL query to execute.', + }, + + // ---------------------------------- + // insert + // ---------------------------------- + { + displayName: 'Schema', + name: 'schema', + type: 'string', + displayOptions: { + show: { + operation: ['insert'], + }, + }, + default: 'public', + required: true, + description: 'Name of the schema the table belongs to', + }, + { + displayName: 'Table', + name: 'table', + type: 'string', + displayOptions: { + show: { + operation: ['insert'], + }, + }, + default: '', + required: true, + description: 'Name of the table in which to insert data to.', + }, + { + displayName: 'Columns', + name: 'columns', + type: 'string', + displayOptions: { + show: { + operation: ['insert'], + }, + }, + default: '', + placeholder: 'id,name,description', + description: + 'Comma separated list of the properties which should used as columns for the new rows.', + }, + { + displayName: 'Return Fields', + name: 'returnFields', + type: 'string', + displayOptions: { + show: { + operation: ['insert'], + }, + }, + default: '*', + description: 'Comma separated list of the fields that the operation will return', + }, + + // ---------------------------------- + // update + // ---------------------------------- + { + displayName: 'Table', + name: 'table', + type: 'string', + displayOptions: { + show: { + operation: ['update'], + }, + }, + default: '', + required: true, + description: 'Name of the table in which to update data in', + }, + { + displayName: 'Update Key', + name: 'updateKey', + type: 'string', + displayOptions: { + show: { + operation: ['update'], + }, + }, + default: 'id', + required: true, + description: + 'Name of the property which decides which rows in the database should be updated. Normally that would be "id".', + }, + { + displayName: 'Columns', + name: 'columns', + type: 'string', + displayOptions: { + show: { + operation: ['update'], + }, + }, + default: '', + placeholder: 'name,description', + description: + 'Comma separated list of the properties which should used as columns for rows to update.', + }, + ], + }; + + async execute(this: IExecuteFunctions): Promise { + const credentials = this.getCredentials('crateDb'); + + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + + const pgp = pgPromise(); + + const config = { + host: credentials.host as string, + port: credentials.port as number, + database: credentials.database as string, + user: credentials.user as string, + password: credentials.password as string, + ssl: !['disable', undefined].includes(credentials.ssl as string | undefined), + sslmode: (credentials.ssl as string) || 'disable', + }; + + const db = pgp(config); + + let returnItems = []; + + const items = this.getInputData(); + const operation = this.getNodeParameter('operation', 0) as string; + + if (operation === 'executeQuery') { + // ---------------------------------- + // executeQuery + // ---------------------------------- + + const queryResult = await pgQuery(this.getNodeParameter, pgp, db, items); + + returnItems = this.helpers.returnJsonArray(queryResult as IDataObject[]); + } else if (operation === 'insert') { + // ---------------------------------- + // insert + // ---------------------------------- + + const [insertData, insertItems] = await pgInsert(this.getNodeParameter, pgp, db, items); + + // Add the id to the data + for (let i = 0; i < insertData.length; i++) { + returnItems.push({ + json: { + ...insertData[i], + ...insertItems[i], + }, + }); + } + } else if (operation === 'update') { + // ---------------------------------- + // update + // ---------------------------------- + + const updateItems = await pgUpdate(this.getNodeParameter, pgp, db, items); + + returnItems = this.helpers.returnJsonArray(updateItems); + } else { + await pgp.end(); + throw new Error(`The operation "${operation}" is not supported!`); + } + + // Close the connection + await pgp.end(); + + return this.prepareOutputData(returnItems); + } +} diff --git a/packages/nodes-base/nodes/CrateDb/cratedb.png b/packages/nodes-base/nodes/CrateDb/cratedb.png new file mode 100644 index 000000000..d2e90eade Binary files /dev/null and b/packages/nodes-base/nodes/CrateDb/cratedb.png differ diff --git a/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts b/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts index 6140568bc..20c8248c5 100644 --- a/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts +++ b/packages/nodes-base/nodes/Dropbox/Dropbox.node.ts @@ -2,6 +2,7 @@ import { BINARY_ENCODING, IExecuteFunctions, } from 'n8n-core'; + import { IDataObject, INodeTypeDescription, @@ -9,8 +10,9 @@ import { INodeType, } from 'n8n-workflow'; -import { OptionsWithUri } from 'request'; - +import { + OptionsWithUri +} from 'request'; export class Dropbox implements INodeType { description: INodeTypeDescription = { @@ -23,7 +25,7 @@ export class Dropbox implements INodeType { description: 'Access data on Dropbox', defaults: { name: 'Dropbox', - color: '#22BB44', + color: '#0061FF', }, inputs: ['main'], outputs: ['main'], @@ -454,6 +456,7 @@ export class Dropbox implements INodeType { let requestMethod = ''; let body: IDataObject | Buffer; let isJson = false; + let query: IDataObject = {}; let headers: IDataObject; @@ -470,8 +473,9 @@ export class Dropbox implements INodeType { // ---------------------------------- requestMethod = 'POST'; - headers['Dropbox-API-Arg'] = JSON.stringify({ - path: this.getNodeParameter('path', i) as string, + + query.arg = JSON.stringify({ + path: this.getNodeParameter('path', i) as string }); endpoint = 'https://content.dropboxapi.com/2/files/download'; @@ -483,9 +487,10 @@ export class Dropbox implements INodeType { requestMethod = 'POST'; headers['Content-Type'] = 'application/octet-stream'; - headers['Dropbox-API-Arg'] = JSON.stringify({ + + query.arg = JSON.stringify({ mode: 'overwrite', - path: this.getNodeParameter('path', i) as string, + path: this.getNodeParameter('path', i) as string }); endpoint = 'https://content.dropboxapi.com/2/files/upload'; @@ -594,8 +599,8 @@ export class Dropbox implements INodeType { const options: OptionsWithUri = { headers, method: requestMethod, - qs: {}, uri: endpoint, + qs: query, json: isJson, }; diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 17ab0f0ad..d5e179614 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "0.68.1", + "version": "0.69.0", "description": "Base nodes of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io", @@ -46,6 +46,7 @@ "dist/credentials/CodaApi.credentials.js", "dist/credentials/CopperApi.credentials.js", "dist/credentials/CalendlyApi.credentials.js", + "dist/credentials/CrateDb.credentials.js", "dist/credentials/DisqusApi.credentials.js", "dist/credentials/DriftApi.credentials.js", "dist/credentials/DriftOAuth2Api.credentials.js", @@ -182,6 +183,7 @@ "dist/nodes/Cockpit/Cockpit.node.js", "dist/nodes/Coda/Coda.node.js", "dist/nodes/Copper/CopperTrigger.node.js", + "dist/nodes/CrateDb/CrateDb.node.js", "dist/nodes/Cron.node.js", "dist/nodes/Crypto.node.js", "dist/nodes/DateTime.node.js", @@ -338,7 +340,7 @@ "@types/xml2js": "^0.4.3", "gulp": "^4.0.0", "jest": "^24.9.0", - "n8n-workflow": "~0.34.0", + "n8n-workflow": "~0.35.0", "ts-jest": "^24.0.2", "tslint": "^5.17.0", "typescript": "~3.7.4" @@ -364,7 +366,7 @@ "mongodb": "^3.5.5", "mssql": "^6.2.0", "mysql2": "^2.0.1", - "n8n-core": "~0.38.0", + "n8n-core": "~0.39.0", "nodemailer": "^6.4.6", "pdf-parse": "^1.1.1", "pg-promise": "^9.0.3", diff --git a/packages/workflow/package.json b/packages/workflow/package.json index b65cf8cb6..aed6e160b 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "0.34.0", + "version": "0.35.0", "description": "Workflow base code of n8n", "license": "SEE LICENSE IN LICENSE.md", "homepage": "https://n8n.io",