🚧 insert function extracted

This commit is contained in:
Ben Hesseldieck 2020-07-08 12:01:16 +02:00
parent 9dee7a2a98
commit 0108538fcc
2 changed files with 136 additions and 133 deletions

View file

@ -2,6 +2,33 @@ import { IDataObject, INodeExecutionData } from 'n8n-workflow';
import pgPromise = require('pg-promise'); import pgPromise = require('pg-promise');
import pg = require('pg-promise/typescript/pg-subset'); import pg = require('pg-promise/typescript/pg-subset');
/**
* Returns of copy of the items which only contains the json data and
* of that only the define properties
*
* @param {INodeExecutionData[]} items The items to copy
* @param {string[]} properties The properties it should include
* @returns
*/
function getItemCopy(
items: INodeExecutionData[],
properties: string[],
): IDataObject[] {
// Prepare the data to insert and copy it to be returned
let newItem: IDataObject;
return items.map(item => {
newItem = {};
for (const property of properties) {
if (item.json[property] === undefined) {
newItem[property] = null;
} else {
newItem[property] = JSON.parse(JSON.stringify(item.json[property]));
}
}
return newItem;
});
}
/** /**
* Executes the given SQL query on the database. * Executes the given SQL query on the database.
* *
@ -9,14 +36,14 @@ import pg = require('pg-promise/typescript/pg-subset');
* @param {pgPromise.IMain<{}, pg.IClient>} pgp The pgPromise instance * @param {pgPromise.IMain<{}, pg.IClient>} pgp The pgPromise instance
* @param {pgPromise.IDatabase<{}, pg.IClient>} db The pgPromise database connection * @param {pgPromise.IDatabase<{}, pg.IClient>} db The pgPromise database connection
* @param {input[]} input The Node's input data * @param {input[]} input The Node's input data
* @returns Promise<any> * @returns Promise<Array<object>>
*/ */
export function executeQuery( export function executeQuery(
getNodeParam: Function, getNodeParam: Function,
pgp: pgPromise.IMain<{}, pg.IClient>, pgp: pgPromise.IMain<{}, pg.IClient>,
db: pgPromise.IDatabase<{}, pg.IClient>, db: pgPromise.IDatabase<{}, pg.IClient>,
input: INodeExecutionData[], input: INodeExecutionData[],
): Promise<any> { ): Promise<Array<object>> {
const queries: string[] = []; const queries: string[] = [];
for (let i = 0; i < input.length; i++) { for (let i = 0; i < input.length; i++) {
queries.push(getNodeParam('query', i) as string); queries.push(getNodeParam('query', i) as string);
@ -25,24 +52,47 @@ export function executeQuery(
return db.any(pgp.helpers.concat(queries)); return db.any(pgp.helpers.concat(queries));
} }
// /** /**
// * Returns of copy of the items which only contains the json data and * Returns of copy of the items which only contains the json data and
// * of that only the define properties * of that only the define properties
// * *
// * @param {items[]} items The items execute the query with * @param {Function} getNodeParam The getter of the Node
// * @param {string[]} properties The properties it should include * @param {pgPromise.IMain<{}, pg.IClient>} pgp The pgPromise instance
// * @returns * @param {pgPromise.IDatabase<{}, pg.IClient>} db The pgPromise database connection
// */ * @param {input[]} input The Node's input data
// export function insert( * @returns Promise<object>
// getNodeParam: Function, */
// pgp: pgPromise.IMain<{}, pg.IClient>, export async function executeInsert(
// db: pgPromise.IDatabase<{}, pg.IClient>, getNodeParam: Function,
// items: INodeExecutionData[] pgp: pgPromise.IMain<{}, pg.IClient>,
// ): Promise<any> { db: pgPromise.IDatabase<{}, pg.IClient>,
// const queries: string[] = []; items: INodeExecutionData[],
// for (let i = 0; i < items.length; i++) { ): Promise<Array<IDataObject[]>> {
// queries.push(getNodeParam('query', i) as string); const table = getNodeParam('table', 0) as string;
// } const schema = getNodeParam('schema', 0) as string;
let returnFields = (getNodeParam('returnFields', 0) as string).split(
',',
) as string[];
const columnString = getNodeParam('columns', 0) as string;
const columns = columnString.split(',').map(column => column.trim());
// return db.any(pgp.helpers.concat(queries)); const cs = new pgp.helpers.ColumnSet(columns);
// }
const te = new pgp.helpers.TableName({ table, schema });
// Prepare the data to insert and copy it to be returned
const insertItems = getItemCopy(items, columns);
// Generate the multi-row insert query and return the id of new row
returnFields = returnFields
.map(value => value.trim())
.filter(value => !!value);
const query =
pgp.helpers.insert(insertItems, cs, te) +
(returnFields.length ? ` RETURNING ${returnFields.join(',')}` : '');
// Executing the query to insert the data
const insertData = await db.manyOrNone(query);
return [insertData, insertItems];
}

View file

@ -3,39 +3,12 @@ import {
IDataObject, IDataObject,
INodeExecutionData, INodeExecutionData,
INodeType, INodeType,
INodeTypeDescription INodeTypeDescription,
} from 'n8n-workflow'; } from 'n8n-workflow';
import * as pgPromise from 'pg-promise'; import * as pgPromise from 'pg-promise';
import { executeQuery } from './Postgres.node.functions'; import { executeInsert, executeQuery } from './Postgres.node.functions';
/**
* Returns of copy of the items which only contains the json data and
* of that only the define properties
*
* @param {INodeExecutionData[]} items The items to copy
* @param {string[]} properties The properties it should include
* @returns
*/
function getItemCopy(
items: INodeExecutionData[],
properties: string[]
): IDataObject[] {
// Prepare the data to insert and copy it to be returned
let newItem: IDataObject;
return items.map(item => {
newItem = {};
for (const property of properties) {
if (item.json[property] === undefined) {
newItem[property] = null;
} else {
newItem[property] = JSON.parse(JSON.stringify(item.json[property]));
}
}
return newItem;
});
}
export class Postgres implements INodeType { export class Postgres implements INodeType {
description: INodeTypeDescription = { description: INodeTypeDescription = {
@ -47,15 +20,15 @@ export class Postgres implements INodeType {
description: 'Gets, add and update data in Postgres.', description: 'Gets, add and update data in Postgres.',
defaults: { defaults: {
name: 'Postgres', name: 'Postgres',
color: '#336791' color: '#336791',
}, },
inputs: ['main'], inputs: ['main'],
outputs: ['main'], outputs: ['main'],
credentials: [ credentials: [
{ {
name: 'postgres', name: 'postgres',
required: true required: true,
} },
], ],
properties: [ properties: [
{ {
@ -66,21 +39,21 @@ export class Postgres implements INodeType {
{ {
name: 'Execute Query', name: 'Execute Query',
value: 'executeQuery', value: 'executeQuery',
description: 'Executes a SQL query.' description: 'Executes a SQL query.',
}, },
{ {
name: 'Insert', name: 'Insert',
value: 'insert', value: 'insert',
description: 'Insert rows in database.' description: 'Insert rows in database.',
}, },
{ {
name: 'Update', name: 'Update',
value: 'update', value: 'update',
description: 'Updates rows in database.' description: 'Updates rows in database.',
} },
], ],
default: 'insert', default: 'insert',
description: 'The operation to perform.' description: 'The operation to perform.',
}, },
// ---------------------------------- // ----------------------------------
@ -91,17 +64,17 @@ export class Postgres implements INodeType {
name: 'query', name: 'query',
type: 'string', type: 'string',
typeOptions: { typeOptions: {
rows: 5 rows: 5,
}, },
displayOptions: { displayOptions: {
show: { show: {
operation: ['executeQuery'] operation: ['executeQuery'],
} },
}, },
default: '', default: '',
placeholder: 'SELECT id, name FROM product WHERE id < 40', placeholder: 'SELECT id, name FROM product WHERE id < 40',
required: true, required: true,
description: 'The SQL query to execute.' description: 'The SQL query to execute.',
}, },
// ---------------------------------- // ----------------------------------
@ -113,12 +86,12 @@ export class Postgres implements INodeType {
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
operation: ['insert'] operation: ['insert'],
} },
}, },
default: 'public', default: 'public',
required: true, required: true,
description: 'Name of the schema the table belongs to' description: 'Name of the schema the table belongs to',
}, },
{ {
displayName: 'Table', displayName: 'Table',
@ -126,12 +99,12 @@ export class Postgres implements INodeType {
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
operation: ['insert'] operation: ['insert'],
} },
}, },
default: '', default: '',
required: true, required: true,
description: 'Name of the table in which to insert data to.' description: 'Name of the table in which to insert data to.',
}, },
{ {
displayName: 'Columns', displayName: 'Columns',
@ -139,13 +112,13 @@ export class Postgres implements INodeType {
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
operation: ['insert'] operation: ['insert'],
} },
}, },
default: '', default: '',
placeholder: 'id,name,description', placeholder: 'id,name,description',
description: description:
'Comma separated list of the properties which should used as columns for the new rows.' 'Comma separated list of the properties which should used as columns for the new rows.',
}, },
{ {
displayName: 'Return Fields', displayName: 'Return Fields',
@ -153,12 +126,12 @@ export class Postgres implements INodeType {
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
operation: ['insert'] operation: ['insert'],
} },
}, },
default: '*', default: '*',
description: description:
'Comma separated list of the fields that the operation will return' 'Comma separated list of the fields that the operation will return',
}, },
// ---------------------------------- // ----------------------------------
@ -170,12 +143,12 @@ export class Postgres implements INodeType {
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
operation: ['update'] operation: ['update'],
} },
}, },
default: '', default: '',
required: true, required: true,
description: 'Name of the table in which to update data in' description: 'Name of the table in which to update data in',
}, },
{ {
displayName: 'Update Key', displayName: 'Update Key',
@ -183,13 +156,13 @@ export class Postgres implements INodeType {
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
operation: ['update'] operation: ['update'],
} },
}, },
default: 'id', default: 'id',
required: true, required: true,
description: description:
'Name of the property which decides which rows in the database should be updated. Normally that would be "id".' 'Name of the property which decides which rows in the database should be updated. Normally that would be "id".',
}, },
{ {
displayName: 'Columns', displayName: 'Columns',
@ -197,15 +170,15 @@ export class Postgres implements INodeType {
type: 'string', type: 'string',
displayOptions: { displayOptions: {
show: { show: {
operation: ['update'] operation: ['update'],
} },
}, },
default: '', default: '',
placeholder: 'name,description', placeholder: 'name,description',
description: description:
'Comma separated list of the properties which should used as columns for rows to update.' 'Comma separated list of the properties which should used as columns for rows to update.',
} },
] ],
}; };
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> { async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
@ -224,9 +197,9 @@ export class Postgres implements INodeType {
user: credentials.user as string, user: credentials.user as string,
password: credentials.password as string, password: credentials.password as string,
ssl: !['disable', undefined].includes( ssl: !['disable', undefined].includes(
credentials.ssl as string | undefined credentials.ssl as string | undefined,
), ),
sslmode: (credentials.ssl as string) || 'disable' sslmode: (credentials.ssl as string) || 'disable',
}; };
const db = pgp(config); const db = pgp(config);
@ -245,7 +218,7 @@ export class Postgres implements INodeType {
this.getNodeParameter, this.getNodeParameter,
pgp, pgp,
db, db,
items items,
); );
returnItems = this.helpers.returnJsonArray(queryResult as IDataObject[]); returnItems = this.helpers.returnJsonArray(queryResult as IDataObject[]);
@ -254,40 +227,20 @@ export class Postgres implements INodeType {
// insert // insert
// ---------------------------------- // ----------------------------------
const table = this.getNodeParameter('table', 0) as string; const [insertData, insertItems] = await executeInsert(
const schema = this.getNodeParameter('schema', 0) as string; this.getNodeParameter,
let returnFields = (this.getNodeParameter( pgp,
'returnFields', db,
0 items,
) as string).split(',') as string[]; );
const columnString = this.getNodeParameter('columns', 0) as string;
const columns = columnString.split(',').map(column => column.trim());
const cs = new pgp.helpers.ColumnSet(columns);
const te = new pgp.helpers.TableName({ table, schema });
// Prepare the data to insert and copy it to be returned
const insertItems = getItemCopy(items, columns);
// Generate the multi-row insert query and return the id of new row
returnFields = returnFields
.map(value => value.trim())
.filter(value => !!value);
const query =
pgp.helpers.insert(insertItems, cs, te) +
(returnFields.length ? ` RETURNING ${returnFields.join(',')}` : '');
// Executing the query to insert the data
const insertData = await db.manyOrNone(query);
// Add the id to the data // Add the id to the data
for (let i = 0; i < insertData.length; i++) { for (let i = 0; i < insertData.length; i++) {
returnItems.push({ returnItems.push({
json: { json: {
...insertData[i], ...insertData[i],
...insertItems[i] ...insertItems[i],
} },
}); });
} }
} else if (operation === 'update') { } else if (operation === 'update') {
@ -301,26 +254,26 @@ export class Postgres implements INodeType {
const columns = columnString.split(',').map(column => column.trim()); const columns = columnString.split(',').map(column => column.trim());
// Make sure that the updateKey does also get queried // // Make sure that the updateKey does also get queried
if (!columns.includes(updateKey)) { // if (!columns.includes(updateKey)) {
columns.unshift(updateKey); // columns.unshift(updateKey);
} // }
// Prepare the data to update and copy it to be returned // // Prepare the data to update and copy it to be returned
const updateItems = getItemCopy(items, columns); // const updateItems = getItemCopy(items, columns);
// Generate the multi-row update query // // Generate the multi-row update query
const query = // const query =
pgp.helpers.update(updateItems, columns, table) + // pgp.helpers.update(updateItems, columns, table) +
' WHERE v.' + // ' WHERE v.' +
updateKey + // updateKey +
' = t.' + // ' = t.' +
updateKey; // updateKey;
// Executing the query to update the data // // Executing the query to update the data
await db.none(query); // await db.none(query);
returnItems = this.helpers.returnJsonArray(updateItems as IDataObject[]); // returnItems = this.helpers.returnJsonArray(updateItems as IDataObject[]);
} else { } else {
await pgp.end(); await pgp.end();
throw new Error(`The operation "${operation}" is not supported!`); throw new Error(`The operation "${operation}" is not supported!`);