mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-26 13:14:07 -08:00
071e6d6b6e
## Summary <img width="1240" alt="image" src="https://github.com/n8n-io/n8n/assets/8850410/2819f4ce-c343-431a-8a88-a1bc9c4b572a"> <img width="2649" alt="image" src="https://github.com/n8n-io/n8n/assets/8850410/36862aaf-cc4c-4668-bdc8-cf5a6f00babe"> 1. Add code node and open it 3. Click the fullscreen button in the bottom right 4. A fullscreen dialog should appear and allow editing the code 5. Changes made in the fullscreen dialog should be applied to the original code editor when closed It should work the same way for HTML/SQL/JSON editors ⚠️ Modal layout was updated so that modals/dialogs are centered, try to test some modals ## Related tickets and issues https://linear.app/n8n/issue/NODE-1009/add-fullscreen-view-to-code-node ## Review / Merge checklist - [ ] PR title and summary are descriptive. **Remember, the title automatically goes into the changelog. Use `(no-changelog)` otherwise.** ([conventions](https://github.com/n8n-io/n8n/blob/master/.github/pull_request_title_conventions.md)) - [ ] [Docs updated](https://github.com/n8n-io/n8n-docs) or follow-up ticket created. - [ ] Tests included. > A bug is not considered fixed, unless a test is added to prevent it from happening again. > A feature is not complete without tests. --------- Co-authored-by: Giulio Andreini <andreini@netseven.it>
334 lines
8.1 KiB
TypeScript
334 lines
8.1 KiB
TypeScript
import type {
|
|
IExecuteFunctions,
|
|
INodeExecutionData,
|
|
INodeType,
|
|
INodeTypeDescription,
|
|
} from 'n8n-workflow';
|
|
import { NodeOperationError } from 'n8n-workflow';
|
|
|
|
import pgPromise from 'pg-promise';
|
|
import { pgInsert, pgQueryV2, pgUpdate } from '../Postgres/v1/genericFunctions';
|
|
|
|
export class TimescaleDb implements INodeType {
|
|
description: INodeTypeDescription = {
|
|
displayName: 'TimescaleDB',
|
|
name: 'timescaleDb',
|
|
icon: 'file:timescale.svg',
|
|
group: ['input'],
|
|
version: 1,
|
|
description: 'Add and update data in TimescaleDB',
|
|
defaults: {
|
|
name: 'TimescaleDB',
|
|
},
|
|
inputs: ['main'],
|
|
outputs: ['main'],
|
|
credentials: [
|
|
{
|
|
name: 'timescaleDb',
|
|
required: true,
|
|
},
|
|
],
|
|
properties: [
|
|
// eslint-disable-next-line n8n-nodes-base/node-param-operation-without-no-data-expression
|
|
{
|
|
displayName: 'Operation',
|
|
name: 'operation',
|
|
type: 'options',
|
|
options: [
|
|
{
|
|
name: 'Execute Query',
|
|
value: 'executeQuery',
|
|
description: 'Execute an SQL query',
|
|
action: 'Execute a SQL query',
|
|
},
|
|
{
|
|
name: 'Insert',
|
|
value: 'insert',
|
|
description: 'Insert rows in database',
|
|
action: 'Insert rows in database',
|
|
},
|
|
{
|
|
name: 'Update',
|
|
value: 'update',
|
|
description: 'Update rows in database',
|
|
action: 'Update rows in database',
|
|
},
|
|
],
|
|
default: 'insert',
|
|
},
|
|
|
|
// ----------------------------------
|
|
// executeQuery
|
|
// ----------------------------------
|
|
{
|
|
displayName: 'Query',
|
|
name: 'query',
|
|
type: 'string',
|
|
noDataExpression: true,
|
|
typeOptions: {
|
|
editor: 'sqlEditor',
|
|
sqlDialect: 'PostgreSQL',
|
|
},
|
|
displayOptions: {
|
|
show: {
|
|
operation: ['executeQuery'],
|
|
},
|
|
},
|
|
default: '',
|
|
placeholder: 'SELECT id, name FROM product WHERE quantity > $1 AND price <= $2',
|
|
required: true,
|
|
description:
|
|
'The SQL query to execute. You can use n8n expressions or $1 and $2 in conjunction with query parameters.',
|
|
},
|
|
|
|
// ----------------------------------
|
|
// 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',
|
|
},
|
|
|
|
// ----------------------------------
|
|
// update
|
|
// ----------------------------------
|
|
{
|
|
displayName: 'Schema',
|
|
name: 'schema',
|
|
type: 'string',
|
|
displayOptions: {
|
|
show: {
|
|
operation: ['update'],
|
|
},
|
|
},
|
|
default: 'public',
|
|
required: true,
|
|
description: 'Name of the schema the table belongs to',
|
|
},
|
|
{
|
|
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,
|
|
// eslint-disable-next-line n8n-nodes-base/node-param-description-miscased-id
|
|
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',
|
|
},
|
|
// ----------------------------------
|
|
// insert,update
|
|
// ----------------------------------
|
|
{
|
|
displayName: 'Return Fields',
|
|
name: 'returnFields',
|
|
type: 'string',
|
|
displayOptions: {
|
|
show: {
|
|
operation: ['insert', 'update'],
|
|
},
|
|
},
|
|
default: '*',
|
|
description: 'Comma-separated list of the fields that the operation will return',
|
|
},
|
|
// ----------------------------------
|
|
// additional fields
|
|
// ----------------------------------
|
|
{
|
|
displayName: 'Additional Fields',
|
|
name: 'additionalFields',
|
|
type: 'collection',
|
|
placeholder: 'Add Field',
|
|
default: {},
|
|
options: [
|
|
{
|
|
displayName: 'Mode',
|
|
name: 'mode',
|
|
type: 'options',
|
|
options: [
|
|
{
|
|
name: 'Independently',
|
|
value: 'independently',
|
|
description: 'Execute each query independently',
|
|
},
|
|
{
|
|
name: 'Multiple Queries',
|
|
value: 'multiple',
|
|
description: '<b>Default</b>. Sends multiple queries at once to database.',
|
|
},
|
|
{
|
|
name: 'Transaction',
|
|
value: 'transaction',
|
|
description: 'Executes all queries in a single transaction',
|
|
},
|
|
],
|
|
default: 'multiple',
|
|
description:
|
|
'The way queries should be sent to database. Can be used in conjunction with <b>Continue on Fail</b>. See <a href="https://docs.n8n.io/integrations/builtin/app-nodes/n8n-nodes-base.timescaledb/">the docs</a> for more examples',
|
|
},
|
|
{
|
|
displayName: 'Query Parameters',
|
|
name: 'queryParams',
|
|
type: 'string',
|
|
displayOptions: {
|
|
show: {
|
|
'/operation': ['executeQuery'],
|
|
},
|
|
},
|
|
default: '',
|
|
placeholder: 'quantity,price',
|
|
description:
|
|
'Comma-separated list of properties which should be used as query parameters',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
|
|
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
|
const credentials = await this.getCredentials('timescaleDb');
|
|
|
|
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);
|
|
|
|
if (operation === 'executeQuery') {
|
|
// ----------------------------------
|
|
// executeQuery
|
|
// ----------------------------------
|
|
|
|
const queryResult = await pgQueryV2.call(this, pgp, db, items, this.continueOnFail(), {
|
|
resolveExpression: true,
|
|
});
|
|
|
|
returnItems = this.helpers.returnJsonArray(queryResult);
|
|
} else if (operation === 'insert') {
|
|
// ----------------------------------
|
|
// insert
|
|
// ----------------------------------
|
|
|
|
const insertData = await pgInsert(
|
|
this.getNodeParameter,
|
|
pgp,
|
|
db,
|
|
items,
|
|
this.continueOnFail(),
|
|
);
|
|
|
|
// Add the id to the data
|
|
for (let i = 0; i < insertData.length; i++) {
|
|
returnItems.push({
|
|
json: insertData[i],
|
|
});
|
|
}
|
|
} else if (operation === 'update') {
|
|
// ----------------------------------
|
|
// update
|
|
// ----------------------------------
|
|
|
|
const updateItems = await pgUpdate(
|
|
this.getNodeParameter,
|
|
pgp,
|
|
db,
|
|
items,
|
|
this.continueOnFail(),
|
|
);
|
|
|
|
returnItems = this.helpers.returnJsonArray(updateItems);
|
|
} else {
|
|
await db.$pool.end();
|
|
throw new NodeOperationError(
|
|
this.getNode(),
|
|
`The operation "${operation}" is not supported!`,
|
|
);
|
|
}
|
|
|
|
// shuts down the connection pool associated with the db object to allow the process to finish
|
|
await db.$pool.end();
|
|
|
|
return [returnItems];
|
|
}
|
|
}
|