1- import {
2- IExecuteFunctions ,
3- } from 'n8n-core' ;
1+
42
53import {
6- IDataObject ,
7- INodeExecutionData ,
84INodeType ,
95INodeTypeDescription ,
106ILoadOptionsFunctions ,
117INodeListSearchResult ,
8+ IExecuteFunctions ,
9+ INodeExecutionData ,
10+ IWebhookFunctions ,
11+ IWebhookResponseData ,
12+ NodeApiError ,
1213} from 'n8n-workflow' ;
1314
14- import {
15- OptionsWithUri ,
16- } from 'request' ;
17- import { appFields , appOperations } from './AppDescription' ;
15+ import { appFields , httpMethodsProperty , optionsProperty } from './AppDescription' ;
1816import { apiRequest } from './GenericFunctions' ;
17+ import isbot from 'isbot' ;
1918
2019interface LowcoderAppType {
2120applicationId :string ;
2221name :string ;
22+ applicationType :number
2323}
2424
25+ const WAIT_TIME_UNLIMITED = '3000-01-01T00:00:00.000Z' ;
26+
2527export class Lowcoder implements INodeType {
2628description :INodeTypeDescription = {
2729displayName :'Lowcoder' ,
@@ -30,7 +32,7 @@ export class Lowcoder implements INodeType {
3032icon :'file:lowcoder.png' ,
3133group :[ 'transform' ] ,
3234version :1 ,
33- subtitle :'={{ $parameter["operation "]+ ": " + $parameter["resource"] }} ' ,
35+ subtitle :'={{$parameter["resource "]}}:{{ $parameter["appId"] ' ,
3436description :'Consume Lowcoder API' ,
3537defaults :{
3638name :'Lowcoder' ,
@@ -42,7 +44,22 @@ export class Lowcoder implements INodeType {
4244name :'lowcoderApi' ,
4345required :true ,
4446} ,
45- ] ,
47+ ] ,
48+ webhooks :[
49+ {
50+ name :'default' ,
51+ httpMethod :'={{$parameter["httpMethod"]}}' ,
52+ isFullPath :true ,
53+ responseCode :'200' ,
54+ responseMode :'onReceived' ,
55+ responseData :'allEntries' ,
56+ responseContentType :'={{$parameter["options"]["responseContentType"]}}' ,
57+ responsePropertyName :'={{$parameter["options"]["responsePropertyName"]}}' ,
58+ responseHeaders :'={{$parameter["options"]["responseHeaders"]}}' ,
59+ path :'={{$parameter["appId"] || ""}}' ,
60+ restartWebhook :true ,
61+ } ,
62+ ] ,
4663properties :[
4764{
4865displayName :'Resource' ,
@@ -57,8 +74,16 @@ export class Lowcoder implements INodeType {
5774] ,
5875default :'app' ,
5976} ,
60- ...appOperations ,
61- ...appFields
77+ ...appFields ,
78+ {
79+ displayName :
80+ 'The webhook URL will be generated at run time. It can be referenced with the <strong>$execution.resumeUrl</strong> variable. Send it somewhere before getting to this node. <a href="https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.wait/?utm_source=n8n_app&utm_medium=node_settings_modal-credential_link&utm_campaign=n8n-nodes-base.wait" target="_blank">More info</a>' ,
81+ name :'webhookNotice' ,
82+ type :'notice' ,
83+ default :'' ,
84+ } ,
85+ httpMethodsProperty ,
86+ optionsProperty
6287] ,
6388} ;
6489
@@ -79,60 +104,55 @@ export class Lowcoder implements INodeType {
79104withContainerSize :false
80105} ,
81106) ;
82- console . log ( searchResults ) ;
83107
84108return {
85109results :searchResults . data . map ( ( b :LowcoderAppType ) => ( {
86- name :b . name ,
110+ name :` ${ b . name } ( ${ b . applicationType == 2 ? "Module" : "App" } )` ,
87111value :b . applicationId ,
88112} ) ) ,
89113} ;
90114} ,
91115} ,
92116} ;
93117
94- // The execute method will go here
95- async execute ( this :IExecuteFunctions ) :Promise < INodeExecutionData [ ] [ ] > {
96- const items = this . getInputData ( ) ;
97- let responseData ;
98- const returnData = [ ] ;
99- const resource = this . getNodeParameter ( 'resource' , 0 ) as string ;
100- const operation = this . getNodeParameter ( 'operation' , 0 ) as string ;
118+ async webhook ( this :IWebhookFunctions ) :Promise < IWebhookResponseData > {
119+ const options = this . getNodeParameter ( 'options' , { } ) as {
120+ binaryData :boolean ;
121+ ignoreBots :boolean ;
122+ rawBody :Buffer ;
123+ responseData ?:string ;
124+ } ;
125+ const req = this . getRequestObject ( ) ;
126+ const resp = this . getResponseObject ( ) ;
127+
128+ try {
129+ if ( options . ignoreBots && isbot ( req . headers [ 'user-agent' ] ) ) {
130+ throw new NodeApiError ( this . getNode ( ) , { } , { message :'Authorization data is wrong!' } ) ;
131+ }
132+ } catch ( error ) {
133+ resp . writeHead ( error . responseCode , { 'WWW-Authenticate' :'Basic realm="Webhook"' } ) ;
134+ resp . end ( error . message ) ;
135+ return { noWebhookResponse :true } ;
136+ }
137+ // const { data } = req.body;
138+
139+ const returnItem :INodeExecutionData = {
140+ binary :{ } ,
141+ json :{
142+ headers :req . headers ,
143+ params :req . params ,
144+ query :req . query ,
145+ // body: data,
146+ } ,
147+ } ;
148+ return { workflowData :[ [ returnItem ] ] } ;
149+ }
101150
102- // For each item, make an API call to create a contact
103- for ( let i = 0 ; i < items . length ; i ++ ) {
104- if ( resource === 'app' ) {
105- if ( operation === 'create' ) {
106- // Get email input
107- const email = this . getNodeParameter ( 'email' , i ) as string ;
108- // Get additional fields input
109- const additionalFields = this . getNodeParameter ( 'additionalFields' , i ) as IDataObject ;
110- const data :IDataObject = {
111- email,
112- } ;
151+ async execute ( this :IExecuteFunctions ) :Promise < INodeExecutionData [ ] [ ] > {
113152
114- Object . assign ( data , additionalFields ) ;
153+ let waitTill = new Date ( WAIT_TIME_UNLIMITED ) ;
115154
116- // Make HTTP request according to https://sendgrid.com/docs/api-reference/
117- const options :OptionsWithUri = {
118- headers :{
119- 'Accept' :'application/json' ,
120- } ,
121- method :'PUT' ,
122- body :{
123- contacts :[
124- data ,
125- ] ,
126- } ,
127- uri :`https://api.sendgrid.com/v3/marketing/contacts` ,
128- json :true ,
129- } ;
130- responseData = await this . helpers . requestWithAuthentication . call ( this , 'friendGridApi' , options ) ;
131- returnData . push ( responseData ) ;
132- }
133- }
134- }
135- // Map data to n8n data structure
136- return [ this . helpers . returnJsonArray ( returnData ) ] ;
155+ await this . putExecutionToWait ( waitTill ) ;
156+ return [ this . getInputData ( ) ] ;
137157}
138158}