Skip to content

Commit f681ef5

Browse files
author
Pankil Joshi
committed
Added wait and resume by webhook functionality
1 parent 5176ae0 commit f681ef5

File tree

4 files changed

+336
-200
lines changed

4 files changed

+336
-200
lines changed

nodes/Lowcoder/AppDescription.ts

Lines changed: 53 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,12 @@
11
import type { INodeProperties } from 'n8n-workflow';
22

3-
export const appOperations: INodeProperties[] = [
4-
// ----------------------------------
5-
// app
6-
// ----------------------------------
7-
{
8-
displayName: 'Operation',
9-
name: 'operation',
10-
type: 'options',
11-
noDataExpression: true,
12-
displayOptions: {
13-
show: {
14-
resource: ['app'],
15-
},
16-
},
17-
options: [
18-
{
19-
name: 'Create',
20-
value: 'create',
21-
description: 'Create a new app',
22-
action: 'Create a app',
23-
},
24-
{
25-
name: 'Delete',
26-
value: 'delete',
27-
description: 'Delete a app',
28-
action: 'Delete a app',
29-
},
30-
{
31-
name: 'Get',
32-
value: 'get',
33-
description: 'Get the data of a app',
34-
action: 'Get a app',
35-
},
36-
{
37-
name: 'Update',
38-
value: 'update',
39-
description: 'Update a app',
40-
action: 'Update a app',
41-
},
42-
],
43-
default: 'create',
44-
},
45-
];
46-
473
export const appFields: INodeProperties[] = [
484
// ----------------------------------
495
// app:create
506
// ----------------------------------
51-
{
52-
displayName: 'Name',
53-
name: 'name',
54-
type: 'string',
55-
default: '',
56-
placeholder: 'My app',
57-
required: true,
58-
displayOptions: {
59-
show: {
60-
operation: ['create'],
61-
resource: ['app'],
62-
},
63-
},
64-
description: 'The name of the app',
65-
},
667
{
678
displayName: 'App',
68-
name: 'id',
9+
name: 'appId',
6910
type: 'resourceLocator',
7011
default: { mode: 'list', value: '' },
7112
required: true,
@@ -79,12 +20,6 @@ export const appFields: INodeProperties[] = [
7920
searchListMethod: 'searchApps',
8021
},
8122
},
82-
{
83-
displayName: 'By Name',
84-
name: 'name',
85-
type: 'string',
86-
placeholder: '[email protected]\'s new app'
87-
},
8823
{
8924
displayName: 'ID',
9025
name: 'id',
@@ -101,12 +36,57 @@ export const appFields: INodeProperties[] = [
10136
placeholder: '65129e728953c27f7d15b64d',
10237
},
10338
],
104-
displayOptions: {
105-
show: {
106-
operation: ['get', 'delete', 'update'],
107-
resource: ['app'],
108-
},
109-
},
11039
description: 'The ID of the app',
11140
}
112-
];
41+
];
42+
43+
export const httpMethodsProperty: INodeProperties = {
44+
displayName: 'HTTP Method',
45+
name: 'httpMethod',
46+
type: 'options',
47+
options: [
48+
{
49+
name: 'DELETE',
50+
value: 'DELETE',
51+
},
52+
{
53+
name: 'GET',
54+
value: 'GET',
55+
},
56+
{
57+
name: 'HEAD',
58+
value: 'HEAD',
59+
},
60+
{
61+
name: 'PATCH',
62+
value: 'PATCH',
63+
},
64+
{
65+
name: 'POST',
66+
value: 'POST',
67+
},
68+
{
69+
name: 'PUT',
70+
value: 'PUT',
71+
},
72+
],
73+
default: 'GET',
74+
description: 'The HTTP method to listen to',
75+
};
76+
77+
export const optionsProperty: INodeProperties = {
78+
displayName: 'Options',
79+
name: 'options',
80+
type: 'collection',
81+
placeholder: 'Add Option',
82+
default: {},
83+
options: [
84+
{
85+
displayName: 'Ignore Bots',
86+
name: 'ignoreBots',
87+
type: 'boolean',
88+
default: false,
89+
description: 'Whether to ignore requests from bots like link previewers and web crawlers',
90+
}
91+
],
92+
};

nodes/Lowcoder/Lowcoder.node.ts

Lines changed: 75 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
1-
import {
2-
IExecuteFunctions,
3-
} from 'n8n-core';
1+
42

53
import {
6-
IDataObject,
7-
INodeExecutionData,
84
INodeType,
95
INodeTypeDescription,
106
ILoadOptionsFunctions,
117
INodeListSearchResult,
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';
1816
import { apiRequest } from './GenericFunctions';
17+
import isbot from 'isbot';
1918

2019
interface LowcoderAppType {
2120
applicationId: string;
2221
name: string;
22+
applicationType: number
2323
}
2424

25+
const WAIT_TIME_UNLIMITED = '3000-01-01T00:00:00.000Z';
26+
2527
export class Lowcoder implements INodeType {
2628
description: INodeTypeDescription = {
2729
displayName: 'Lowcoder',
@@ -30,7 +32,7 @@ export class Lowcoder implements INodeType {
3032
icon: 'file:lowcoder.png',
3133
group: ['transform'],
3234
version: 1,
33-
subtitle: '={{ $parameter["operation"] + ": " + $parameter["resource"] }}',
35+
subtitle: '={{$parameter["resource"] }}:{{ $parameter["appId"]',
3436
description: 'Consume Lowcoder API',
3537
defaults: {
3638
name: 'Lowcoder',
@@ -42,7 +44,22 @@ export class Lowcoder implements INodeType {
4244
name: 'lowcoderApi',
4345
required: 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+
],
4663
properties: [
4764
{
4865
displayName: 'Resource',
@@ -57,8 +74,16 @@ export class Lowcoder implements INodeType {
5774
],
5875
default: '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 {
79104
withContainerSize: false
80105
},
81106
);
82-
console.log(searchResults);
83107

84108
return {
85109
results: searchResults.data.map((b: LowcoderAppType) => ({
86-
name: b.name,
110+
name: `${b.name} (${b.applicationType == 2 ? "Module" : "App"})`,
87111
value: 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
}

0 commit comments

Comments
 (0)