2023-06-07 02:01:57 -07:00
import type {
ITriggerFunctions ,
IDataObject ,
ILoadOptionsFunctions ,
INodeListSearchResult ,
INodeListSearchItems ,
} from 'n8n-workflow' ;
import pgPromise from 'pg-promise' ;
import type pg from 'pg-promise/typescript/pg-subset' ;
2023-08-01 02:56:54 -07:00
export function prepareNames ( id : string , mode : string , additionalFields : IDataObject ) {
let suffix = id . replace ( /-/g , '_' ) ;
if ( mode === 'manual' ) {
suffix = ` ${ suffix } _manual ` ;
}
let functionName =
( additionalFields . functionName as string ) || ` n8n_trigger_function_ ${ suffix } () ` ;
if ( ! ( functionName . includes ( '(' ) && functionName . includes ( ')' ) ) ) {
functionName = ` ${ functionName } () ` ;
}
const triggerName = ( additionalFields . triggerName as string ) || ` n8n_trigger_ ${ suffix } ` ;
const channelName = ( additionalFields . channelName as string ) || ` n8n_channel_ ${ suffix } ` ;
if ( channelName . includes ( '-' ) ) {
throw new Error ( 'Channel name cannot contain hyphens (-)' ) ;
}
return { functionName , triggerName , channelName } ;
}
2023-06-07 02:01:57 -07:00
export async function pgTriggerFunction (
this : ITriggerFunctions ,
db : pgPromise.IDatabase < { } , pg . IClient > ,
2023-08-01 02:56:54 -07:00
additionalFields : IDataObject ,
functionName : string ,
triggerName : string ,
channelName : string ,
2023-06-07 02:01:57 -07:00
) : Promise < void > {
const schema = this . getNodeParameter ( 'schema' , 'public' , { extractValue : true } ) as string ;
const tableName = this . getNodeParameter ( 'tableName' , undefined , {
extractValue : true ,
} ) as string ;
2023-08-01 02:56:54 -07:00
2023-06-07 02:01:57 -07:00
const target = ` ${ schema } ." ${ tableName } " ` ;
2023-08-01 02:56:54 -07:00
2023-06-07 02:01:57 -07:00
const firesOn = this . getNodeParameter ( 'firesOn' , 0 ) as string ;
2023-08-01 02:56:54 -07:00
2023-06-07 02:01:57 -07:00
const functionReplace =
"CREATE OR REPLACE FUNCTION $1:raw RETURNS trigger LANGUAGE 'plpgsql' COST 100 VOLATILE NOT LEAKPROOF AS $BODY$ begin perform pg_notify('$2:raw', row_to_json($3:raw)::text); return null; end; $BODY$;" ;
2023-08-01 02:56:54 -07:00
2023-06-07 02:01:57 -07:00
const dropIfExist = 'DROP TRIGGER IF EXISTS $1:raw ON $2:raw' ;
2023-08-01 02:56:54 -07:00
2023-06-07 02:01:57 -07:00
const functionExists =
"CREATE FUNCTION $1:raw RETURNS trigger LANGUAGE 'plpgsql' COST 100 VOLATILE NOT LEAKPROOF AS $BODY$ begin perform pg_notify('$2:raw', row_to_json($3:raw)::text); return null; end; $BODY$" ;
2023-08-01 02:56:54 -07:00
2023-06-07 02:01:57 -07:00
const trigger =
'CREATE TRIGGER $4:raw AFTER $3:raw ON $1:raw FOR EACH ROW EXECUTE FUNCTION $2:raw' ;
2023-08-01 02:56:54 -07:00
2023-06-07 02:01:57 -07:00
const whichData = firesOn === 'DELETE' ? 'old' : 'new' ;
2023-08-01 02:56:54 -07:00
2023-06-07 02:01:57 -07:00
if ( channelName . includes ( '-' ) ) {
throw new Error ( 'Channel name cannot contain hyphens (-)' ) ;
}
2023-08-01 02:56:54 -07:00
const replaceIfExists = additionalFields . replaceIfExists ? ? false ;
2023-06-07 02:01:57 -07:00
try {
2023-08-01 02:56:54 -07:00
if ( replaceIfExists || ! ( additionalFields . triggerName ? ? additionalFields . functionName ) ) {
2023-06-07 02:01:57 -07:00
await db . any ( functionReplace , [ functionName , channelName , whichData ] ) ;
await db . any ( dropIfExist , [ triggerName , target , whichData ] ) ;
} else {
await db . any ( functionExists , [ functionName , channelName , whichData ] ) ;
}
await db . any ( trigger , [ target , functionName , firesOn , triggerName ] ) ;
2023-08-01 02:56:54 -07:00
} catch ( error ) {
if ( ( error as Error ) . message . includes ( 'near "-"' ) ) {
2023-06-07 02:01:57 -07:00
throw new Error ( 'Names cannot contain hyphens (-)' ) ;
}
2023-08-01 02:56:54 -07:00
throw error ;
2023-06-07 02:01:57 -07:00
}
}
export async function initDB ( this : ITriggerFunctions | ILoadOptionsFunctions ) {
const credentials = await this . getCredentials ( 'postgres' ) ;
const pgp = pgPromise ( {
// prevent spam in console "WARNING: Creating a duplicate database object for the same connection."
noWarnings : true ,
} ) ;
const config : IDataObject = {
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 ,
} ;
if ( credentials . allowUnauthorizedCerts === true ) {
config . ssl = {
rejectUnauthorized : false ,
} ;
} else {
config . ssl = ! [ 'disable' , undefined ] . includes ( credentials . ssl as string | undefined ) ;
config . sslmode = ( credentials . ssl as string ) || 'disable' ;
}
2023-08-01 02:56:54 -07:00
const db = pgp ( config ) ;
return { db , pgp } ;
2023-06-07 02:01:57 -07:00
}
export async function searchSchema ( this : ILoadOptionsFunctions ) : Promise < INodeListSearchResult > {
2023-08-01 02:56:54 -07:00
const { db , pgp } = await initDB . call ( this ) ;
2023-06-07 02:01:57 -07:00
const schemaList = await db . any ( 'SELECT schema_name FROM information_schema.schemata' ) ;
2023-08-01 02:56:54 -07:00
const results : INodeListSearchItems [ ] = ( schemaList as IDataObject [ ] ) . map ( ( s ) = > ( {
2023-06-07 02:01:57 -07:00
name : s.schema_name as string ,
value : s.schema_name as string ,
} ) ) ;
2023-08-01 02:56:54 -07:00
pgp . end ( ) ;
2023-06-07 02:01:57 -07:00
return { results } ;
}
export async function searchTables ( this : ILoadOptionsFunctions ) : Promise < INodeListSearchResult > {
const schema = this . getNodeParameter ( 'schema' , 0 ) as IDataObject ;
2023-08-01 02:56:54 -07:00
const { db , pgp } = await initDB . call ( this ) ;
2023-06-07 02:01:57 -07:00
let tableList = [ ] ;
try {
tableList = await db . any (
'SELECT table_name FROM information_schema.tables WHERE table_schema = $1' ,
[ schema . value ] ,
) ;
} catch ( error ) {
throw new Error ( error as string ) ;
}
2023-08-01 02:56:54 -07:00
const results : INodeListSearchItems [ ] = ( tableList as IDataObject [ ] ) . map ( ( s ) = > ( {
2023-06-07 02:01:57 -07:00
name : s.table_name as string ,
value : s.table_name as string ,
} ) ) ;
2023-08-01 02:56:54 -07:00
pgp . end ( ) ;
2023-06-07 02:01:57 -07:00
return { results } ;
}