Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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: 'name@example.tld\'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月01日T00: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+
letwaitTill=newDate(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)

AltStyle によって変換されたページ (->オリジナル) /