2020-07-07 06:24:21 -07:00
import { IExecuteFunctions } from 'n8n-core' ;
2020-07-26 03:45:39 -07:00
import {
IDataObject ,
INodeExecutionData ,
INodeType ,
INodeTypeDescription ,
2021-04-16 09:33:36 -07:00
NodeOperationError ,
2020-07-26 03:45:39 -07:00
} from 'n8n-workflow' ;
2020-07-07 06:24:21 -07:00
import * as pgPromise from 'pg-promise' ;
2021-04-24 13:55:14 -07:00
import {
pgInsert ,
pgQuery ,
} from '../Postgres/Postgres.node.functions' ;
2020-07-07 06:24:21 -07:00
2020-07-10 01:58:39 -07:00
export class QuestDb implements INodeType {
2020-07-07 06:24:21 -07:00
description : INodeTypeDescription = {
displayName : 'QuestDB' ,
2020-07-10 01:58:39 -07:00
name : 'questDb' ,
2020-07-07 06:24:21 -07:00
icon : 'file:questdb.png' ,
group : [ 'input' ] ,
version : 1 ,
2021-07-03 05:40:16 -07:00
description : 'Get, add and update data in QuestDB' ,
2020-07-07 06:24:21 -07:00
defaults : {
name : 'QuestDB' ,
} ,
inputs : [ 'main' ] ,
outputs : [ 'main' ] ,
credentials : [
{
2020-07-10 01:58:39 -07:00
name : 'questDb' ,
2020-07-07 06:24:21 -07:00
required : true ,
2020-07-08 07:30:21 -07:00
} ,
2020-07-07 06:24:21 -07:00
] ,
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.' ,
2020-10-22 06:46:03 -07:00
} ,
2020-07-07 06:24:21 -07:00
] ,
default : 'insert' ,
description : 'The operation to perform.' ,
} ,
// ----------------------------------
// executeQuery
// ----------------------------------
{
displayName : 'Query' ,
name : 'query' ,
type : 'string' ,
typeOptions : {
2021-09-11 03:58:48 -07:00
alwaysOpenEditWindow : true ,
2020-07-07 06:24:21 -07:00
} ,
displayOptions : {
show : {
2020-07-26 03:45:39 -07:00
operation : [
'executeQuery' ,
] ,
2020-07-07 06:24:21 -07:00
} ,
} ,
default : '' ,
2021-04-30 15:35:34 -07:00
placeholder : 'SELECT id, name FROM product WHERE quantity > $1 AND price <= $2' ,
2020-07-07 06:24:21 -07:00
required : true ,
2021-04-30 15:35:34 -07:00
description : 'The SQL query to execute. You can use n8n expressions or $1 and $2 in conjunction with query parameters.' ,
2020-07-07 06:24:21 -07:00
} ,
// ----------------------------------
// insert
// ----------------------------------
{
displayName : 'Schema' ,
name : 'schema' ,
2021-04-24 13:55:14 -07:00
type : 'hidden' , // Schema is used by pgInsert
2020-07-07 06:24:21 -07:00
displayOptions : {
show : {
2020-07-26 03:45:39 -07:00
operation : [
'insert' ,
] ,
2020-07-07 06:24:21 -07:00
} ,
} ,
2021-04-24 13:55:14 -07:00
default : '' ,
2020-07-07 06:24:21 -07:00
description : 'Name of the schema the table belongs to' ,
} ,
{
displayName : 'Table' ,
name : 'table' ,
type : 'string' ,
displayOptions : {
show : {
2020-07-26 03:45:39 -07:00
operation : [
'insert' ,
] ,
2020-07-07 06:24:21 -07:00
} ,
} ,
default : '' ,
required : true ,
description : 'Name of the table in which to insert data to.' ,
} ,
2021-04-24 13:55:14 -07:00
{
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.' ,
} ,
2020-07-07 06:24:21 -07:00
{
displayName : 'Return Fields' ,
name : 'returnFields' ,
type : 'string' ,
2021-04-24 13:55:14 -07:00
displayOptions : {
show : {
operation : [ 'insert' ] ,
} ,
} ,
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 : { } ,
displayOptions : {
show : {
operation : [
'executeQuery' ,
] ,
} ,
} ,
options : [
{
displayName : 'Mode' ,
name : 'mode' ,
type : 'options' ,
options : [
{
name : 'Independently' ,
value : 'independently' ,
description : 'Execute each query independently' ,
} ,
{
name : 'Transaction' ,
value : 'transaction' ,
description : 'Executes all queries in a single transaction' ,
} ,
] ,
default : 'independently' ,
2021-11-25 09:10:06 -08:00
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/nodes/n8n-nodes-base.questDb/">the docs</a> for more examples.' ,
2021-04-24 13:55:14 -07:00
} ,
2021-04-30 15:35:34 -07:00
{
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.' ,
} ,
2021-04-24 13:55:14 -07:00
] ,
} ,
{
displayName : 'Additional Fields' ,
name : 'additionalFields' ,
type : 'hidden' ,
default : { } ,
2020-07-07 06:24:21 -07:00
displayOptions : {
show : {
2020-07-26 03:45:39 -07:00
operation : [
'insert' ,
] ,
2020-07-07 06:24:21 -07:00
} ,
} ,
} ,
2020-07-08 07:30:21 -07:00
] ,
2020-07-07 06:24:21 -07:00
} ;
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
2021-08-20 09:57:30 -07:00
const credentials = await this . getCredentials ( 'questDb' ) ;
2020-07-07 06:24:21 -07:00
if ( credentials === undefined ) {
2021-04-16 09:33:36 -07:00
throw new NodeOperationError ( this . getNode ( ) , 'No credentials got returned!' ) ;
2020-07-07 06:24:21 -07:00
}
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 ,
2020-07-09 08:16:45 -07:00
ssl : ! [ 'disable' , undefined ] . includes ( credentials . ssl as string | undefined ) ,
2020-07-08 07:30:21 -07:00
sslmode : ( credentials . ssl as string ) || 'disable' ,
2020-07-07 06:24:21 -07:00
} ;
const db = pgp ( config ) ;
2021-04-30 15:35:34 -07:00
let returnItems : INodeExecutionData [ ] = [ ] ;
2020-07-07 06:24:21 -07:00
const items = this . getInputData ( ) ;
const operation = this . getNodeParameter ( 'operation' , 0 ) as string ;
if ( operation === 'executeQuery' ) {
// ----------------------------------
// executeQuery
// ----------------------------------
2021-04-24 13:55:14 -07:00
const additionalFields = this . getNodeParameter ( 'additionalFields' , 0 ) as IDataObject ;
const mode = ( additionalFields . mode || 'independently' ) as string ;
2020-07-07 06:24:21 -07:00
2021-04-24 13:55:14 -07:00
const queryResult = await pgQuery ( this . getNodeParameter , pgp , db , items , this . continueOnFail ( ) , mode ) ;
returnItems = this . helpers . returnJsonArray ( queryResult ) ;
2020-07-07 06:24:21 -07:00
} else if ( operation === 'insert' ) {
// ----------------------------------
// insert
// ----------------------------------
2020-07-26 02:33:20 -07:00
2021-04-24 13:55:14 -07:00
// Transaction and multiple won't work properly with QuestDB.
// So we send queries independently.
await pgInsert ( this . getNodeParameter , pgp , db , items , this . continueOnFail ( ) , 'independently' ) ;
2020-07-23 02:14:03 -07:00
2021-04-24 13:55:14 -07:00
const returnFields = this . getNodeParameter ( 'returnFields' , 0 ) as string ;
const table = this . getNodeParameter ( 'table' , 0 ) as string ;
2020-07-23 02:14:03 -07:00
2021-04-24 13:55:14 -07:00
const insertData = await db . any ( 'SELECT ${columns:name} from ${table:name}' , {
columns : returnFields.split ( ',' ) . map ( value = > value . trim ( ) ) . filter ( value = > ! ! value ) ,
table ,
2020-07-23 02:14:03 -07:00
} ) ;
2020-07-26 02:33:20 -07:00
2021-04-24 13:55:14 -07:00
returnItems = this . helpers . returnJsonArray ( insertData ) ;
2020-07-07 06:24:21 -07:00
} else {
await pgp . end ( ) ;
2021-04-16 09:33:36 -07:00
throw new NodeOperationError ( this . getNode ( ) , ` The operation " ${ operation } " is not supported! ` ) ;
2020-07-07 06:24:21 -07:00
}
// Close the connection
await pgp . end ( ) ;
return this . prepareOutputData ( returnItems ) ;
}
}