@nocobase/plugin-workflow-request 0.18.0-alpha.9 → 0.19.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/externalVersion.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module.exports = {
|
|
2
2
|
"@formily/antd-v5": "1.1.9",
|
|
3
|
-
"@nocobase/plugin-workflow": "0.
|
|
4
|
-
"@nocobase/client": "0.
|
|
3
|
+
"@nocobase/plugin-workflow": "0.19.0-alpha.10",
|
|
4
|
+
"@nocobase/client": "0.19.0-alpha.10",
|
|
5
5
|
"react-i18next": "11.18.6",
|
|
6
|
-
"@nocobase/server": "0.
|
|
6
|
+
"@nocobase/server": "0.19.0-alpha.10",
|
|
7
7
|
"axios": "0.26.1"
|
|
8
8
|
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"HTTP request": "HTTP 요청",
|
|
3
|
+
"Send HTTP request to a URL. You can use the variables in the upstream nodes as request headers, parameters and request body.":
|
|
4
|
+
"URL에 HTTP 요청을 보냅니다. 상류 노드의 변수를 요청 헤더, 매개변수 및 요청 본문으로 사용할 수 있습니다.",
|
|
5
|
+
"HTTP method": "HTTP 메서드",
|
|
6
|
+
"URL": "주소",
|
|
7
|
+
"Headers": "헤더",
|
|
8
|
+
"Add request header": "요청 헤더 추가",
|
|
9
|
+
"Parameters": "매개변수",
|
|
10
|
+
"Add parameter": "매개변수 추가",
|
|
11
|
+
"Body": "본문",
|
|
12
|
+
"Use variable": "변수 사용",
|
|
13
|
+
"Format": "형식",
|
|
14
|
+
"Insert": "삽입",
|
|
15
|
+
"Timeout config": "시간 초과 설정",
|
|
16
|
+
"ms": "밀리초",
|
|
17
|
+
"Input request data": "요청 데이터 입력",
|
|
18
|
+
"Only support standard JSON data": "표준 JSON 데이터만 지원합니다",
|
|
19
|
+
"\"Content-Type\" only support \"application/json\", and no need to specify":
|
|
20
|
+
"\"Content-Type\" 헤더는 \"application/json\"만 지원하며 지정할 필요가 없습니다",
|
|
21
|
+
"Ignore failed request and continue workflow": "실패한 요청을 무시하고 워크플로를 계속합니다"
|
|
22
|
+
}
|
|
@@ -56,12 +56,29 @@ async function request(config) {
|
|
|
56
56
|
}
|
|
57
57
|
class RequestInstruction_default extends import_plugin_workflow.Instruction {
|
|
58
58
|
async run(node, prevJob, processor) {
|
|
59
|
+
const config = processor.getParsedValue(node.config, node.id);
|
|
60
|
+
const { workflow } = processor.execution;
|
|
61
|
+
const sync = this.workflow.isWorkflowSync(workflow);
|
|
62
|
+
if (sync) {
|
|
63
|
+
try {
|
|
64
|
+
const response = await request(config);
|
|
65
|
+
return {
|
|
66
|
+
status: import_plugin_workflow.JOB_STATUS.RESOLVED,
|
|
67
|
+
result: response.data
|
|
68
|
+
};
|
|
69
|
+
} catch (error) {
|
|
70
|
+
return {
|
|
71
|
+
status: import_plugin_workflow.JOB_STATUS.FAILED,
|
|
72
|
+
result: error.isAxiosError ? error.toJSON() : error.message
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
59
76
|
const job = await processor.saveJob({
|
|
60
77
|
status: import_plugin_workflow.JOB_STATUS.PENDING,
|
|
61
78
|
nodeId: node.id,
|
|
79
|
+
nodeKey: node.key,
|
|
62
80
|
upstreamId: (prevJob == null ? void 0 : prevJob.id) ?? null
|
|
63
81
|
});
|
|
64
|
-
const config = processor.getParsedValue(node.config, node.id);
|
|
65
82
|
request(config).then((response) => {
|
|
66
83
|
job.set({
|
|
67
84
|
status: import_plugin_workflow.JOB_STATUS.RESOLVED,
|
package/package.json
CHANGED
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
"displayName.zh-CN": "工作流:HTTP 请求节点",
|
|
5
5
|
"description": "Send HTTP requests to any HTTP service for data interaction in workflow.",
|
|
6
6
|
"description.zh-CN": "可用于在工作流中向任意 HTTP 服务发送请求,进行数据交互。",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.19.0-alpha.10",
|
|
8
8
|
"license": "AGPL-3.0",
|
|
9
9
|
"main": "./dist/server/index.js",
|
|
10
|
+
"homepage": "https://docs.nocobase.com/plugins/workflow-request",
|
|
11
|
+
"homepage.zh-CN": "https://docs-cn.nocobase.com/plugins/workflow-request",
|
|
10
12
|
"devDependencies": {
|
|
11
13
|
"antd": "5.x",
|
|
12
14
|
"react": "18.x",
|
|
@@ -19,5 +21,8 @@
|
|
|
19
21
|
"@nocobase/server": "0.x",
|
|
20
22
|
"@nocobase/test": "0.x"
|
|
21
23
|
},
|
|
22
|
-
"gitHead": "
|
|
24
|
+
"gitHead": "d09d81eba67339da36bcec27939a85b35d180770",
|
|
25
|
+
"keywords": [
|
|
26
|
+
"Workflow"
|
|
27
|
+
]
|
|
23
28
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"HTTP request": "HTTP 요청",
|
|
3
|
+
"Send HTTP request to a URL. You can use the variables in the upstream nodes as request headers, parameters and request body.":
|
|
4
|
+
"URL에 HTTP 요청을 보냅니다. 상류 노드의 변수를 요청 헤더, 매개변수 및 요청 본문으로 사용할 수 있습니다.",
|
|
5
|
+
"HTTP method": "HTTP 메서드",
|
|
6
|
+
"URL": "주소",
|
|
7
|
+
"Headers": "헤더",
|
|
8
|
+
"Add request header": "요청 헤더 추가",
|
|
9
|
+
"Parameters": "매개변수",
|
|
10
|
+
"Add parameter": "매개변수 추가",
|
|
11
|
+
"Body": "본문",
|
|
12
|
+
"Use variable": "변수 사용",
|
|
13
|
+
"Format": "형식",
|
|
14
|
+
"Insert": "삽입",
|
|
15
|
+
"Timeout config": "시간 초과 설정",
|
|
16
|
+
"ms": "밀리초",
|
|
17
|
+
"Input request data": "요청 데이터 입력",
|
|
18
|
+
"Only support standard JSON data": "표준 JSON 데이터만 지원합니다",
|
|
19
|
+
"\"Content-Type\" only support \"application/json\", and no need to specify":
|
|
20
|
+
"\"Content-Type\" 헤더는 \"application/json\"만 지원하며 지정할 필요가 없습니다",
|
|
21
|
+
"Ignore failed request and continue workflow": "실패한 요청을 무시하고 워크플로를 계속합니다"
|
|
22
|
+
}
|
|
@@ -41,14 +41,33 @@ async function request(config) {
|
|
|
41
41
|
|
|
42
42
|
export default class extends Instruction {
|
|
43
43
|
async run(node: FlowNodeModel, prevJob, processor: Processor) {
|
|
44
|
+
const config = processor.getParsedValue(node.config, node.id) as RequestConfig;
|
|
45
|
+
|
|
46
|
+
const { workflow } = processor.execution;
|
|
47
|
+
const sync = this.workflow.isWorkflowSync(workflow);
|
|
48
|
+
|
|
49
|
+
if (sync) {
|
|
50
|
+
try {
|
|
51
|
+
const response = await request(config);
|
|
52
|
+
return {
|
|
53
|
+
status: JOB_STATUS.RESOLVED,
|
|
54
|
+
result: response.data,
|
|
55
|
+
};
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return {
|
|
58
|
+
status: JOB_STATUS.FAILED,
|
|
59
|
+
result: error.isAxiosError ? error.toJSON() : error.message,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
44
64
|
const job = await processor.saveJob({
|
|
45
65
|
status: JOB_STATUS.PENDING,
|
|
46
66
|
nodeId: node.id,
|
|
67
|
+
nodeKey: node.key,
|
|
47
68
|
upstreamId: prevJob?.id ?? null,
|
|
48
69
|
});
|
|
49
70
|
|
|
50
|
-
const config = processor.getParsedValue(node.config, node.id) as RequestConfig;
|
|
51
|
-
|
|
52
71
|
// eslint-disable-next-line promise/catch-or-return
|
|
53
72
|
request(config)
|
|
54
73
|
.then((response) => {
|
|
@@ -1,41 +1,42 @@
|
|
|
1
|
+
import { Server } from 'http';
|
|
1
2
|
import jwt from 'jsonwebtoken';
|
|
3
|
+
import Koa from 'koa';
|
|
4
|
+
import bodyParser from 'koa-bodyparser';
|
|
2
5
|
|
|
3
|
-
import { Gateway } from '@nocobase/server';
|
|
4
6
|
import Database from '@nocobase/database';
|
|
5
7
|
import { MockServer } from '@nocobase/test';
|
|
6
8
|
|
|
7
|
-
import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow';
|
|
9
|
+
import PluginWorkflow, { Processor, EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow';
|
|
8
10
|
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
|
9
11
|
|
|
10
|
-
import Plugin from '..';
|
|
11
12
|
import { RequestConfig } from '../RequestInstruction';
|
|
12
13
|
|
|
13
14
|
const HOST = 'localhost';
|
|
14
|
-
const PORT = 12345;
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
16
|
+
function getRandomPort() {
|
|
17
|
+
const minPort = 1024;
|
|
18
|
+
const maxPort = 49151;
|
|
19
|
+
return Math.floor(Math.random() * (maxPort - minPort + 1)) + minPort;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class MockAPI {
|
|
23
|
+
app: Koa;
|
|
24
|
+
server: Server;
|
|
25
|
+
port: number;
|
|
26
|
+
get URL_DATA() {
|
|
27
|
+
return `http://${HOST}:${this.port}/api/data`;
|
|
28
|
+
}
|
|
29
|
+
get URL_400() {
|
|
30
|
+
return `http://${HOST}:${this.port}/api/400`;
|
|
31
|
+
}
|
|
32
|
+
get URL_TIMEOUT() {
|
|
33
|
+
return `http://${HOST}:${this.port}/api/timeout`;
|
|
34
|
+
}
|
|
35
|
+
constructor() {
|
|
36
|
+
this.app = new Koa();
|
|
37
|
+
this.app.use(bodyParser());
|
|
38
|
+
|
|
39
|
+
this.app.use(async (ctx, next) => {
|
|
39
40
|
if (ctx.path === '/api/400') {
|
|
40
41
|
return ctx.throw(400);
|
|
41
42
|
}
|
|
@@ -46,7 +47,6 @@ describe('workflow > instructions > request', () => {
|
|
|
46
47
|
}
|
|
47
48
|
if (ctx.path === '/api/data') {
|
|
48
49
|
await sleep(100);
|
|
49
|
-
ctx.withoutDataWrapping = true;
|
|
50
50
|
ctx.body = {
|
|
51
51
|
meta: { title: ctx.query.title },
|
|
52
52
|
data: { title: ctx.request.body['title'] },
|
|
@@ -54,13 +54,45 @@ describe('workflow > instructions > request', () => {
|
|
|
54
54
|
}
|
|
55
55
|
await next();
|
|
56
56
|
});
|
|
57
|
+
}
|
|
57
58
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
59
|
+
async start() {
|
|
60
|
+
return new Promise((resolve) => {
|
|
61
|
+
this.server = this.app.listen(0, () => {
|
|
62
|
+
this.port = this.server.address()['port'];
|
|
63
|
+
resolve(true);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async close() {
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
this.server.close(() => {
|
|
71
|
+
resolve(true);
|
|
72
|
+
});
|
|
61
73
|
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
describe('workflow > instructions > request', () => {
|
|
78
|
+
let app: MockServer;
|
|
79
|
+
let db: Database;
|
|
80
|
+
let PostRepo;
|
|
81
|
+
let PostCollection;
|
|
82
|
+
let ReplyRepo;
|
|
83
|
+
let WorkflowModel;
|
|
84
|
+
let workflow;
|
|
85
|
+
let api: MockAPI;
|
|
62
86
|
|
|
63
|
-
|
|
87
|
+
beforeEach(async () => {
|
|
88
|
+
api = new MockAPI();
|
|
89
|
+
api.start();
|
|
90
|
+
app = await getApp({
|
|
91
|
+
resourcer: {
|
|
92
|
+
prefix: '/api',
|
|
93
|
+
},
|
|
94
|
+
plugins: ['users', 'auth', 'workflow-request'],
|
|
95
|
+
});
|
|
64
96
|
|
|
65
97
|
db = app.db;
|
|
66
98
|
WorkflowModel = db.getCollection('workflows').model;
|
|
@@ -78,14 +110,17 @@ describe('workflow > instructions > request', () => {
|
|
|
78
110
|
});
|
|
79
111
|
});
|
|
80
112
|
|
|
81
|
-
afterEach(() =>
|
|
113
|
+
afterEach(async () => {
|
|
114
|
+
await api.close();
|
|
115
|
+
await app.destroy();
|
|
116
|
+
});
|
|
82
117
|
|
|
83
118
|
describe('request static app routes', () => {
|
|
84
119
|
it('get data', async () => {
|
|
85
120
|
await workflow.createNode({
|
|
86
121
|
type: 'request',
|
|
87
122
|
config: {
|
|
88
|
-
url: URL_DATA,
|
|
123
|
+
url: api.URL_DATA,
|
|
89
124
|
method: 'GET',
|
|
90
125
|
} as RequestConfig,
|
|
91
126
|
});
|
|
@@ -105,7 +140,7 @@ describe('workflow > instructions > request', () => {
|
|
|
105
140
|
await workflow.createNode({
|
|
106
141
|
type: 'request',
|
|
107
142
|
config: {
|
|
108
|
-
url: URL_TIMEOUT,
|
|
143
|
+
url: api.URL_TIMEOUT,
|
|
109
144
|
method: 'GET',
|
|
110
145
|
timeout: 250,
|
|
111
146
|
} as RequestConfig,
|
|
@@ -134,7 +169,7 @@ describe('workflow > instructions > request', () => {
|
|
|
134
169
|
await workflow.createNode({
|
|
135
170
|
type: 'request',
|
|
136
171
|
config: {
|
|
137
|
-
url: URL_TIMEOUT,
|
|
172
|
+
url: api.URL_TIMEOUT,
|
|
138
173
|
method: 'GET',
|
|
139
174
|
timeout: 250,
|
|
140
175
|
ignoreFail: true,
|
|
@@ -160,7 +195,7 @@ describe('workflow > instructions > request', () => {
|
|
|
160
195
|
await workflow.createNode({
|
|
161
196
|
type: 'request',
|
|
162
197
|
config: {
|
|
163
|
-
url: URL_400,
|
|
198
|
+
url: api.URL_400,
|
|
164
199
|
method: 'GET',
|
|
165
200
|
ignoreFail: false,
|
|
166
201
|
} as RequestConfig,
|
|
@@ -180,7 +215,7 @@ describe('workflow > instructions > request', () => {
|
|
|
180
215
|
await workflow.createNode({
|
|
181
216
|
type: 'request',
|
|
182
217
|
config: {
|
|
183
|
-
url: URL_400,
|
|
218
|
+
url: api.URL_400,
|
|
184
219
|
method: 'GET',
|
|
185
220
|
timeout: 1000,
|
|
186
221
|
ignoreFail: true,
|
|
@@ -201,7 +236,7 @@ describe('workflow > instructions > request', () => {
|
|
|
201
236
|
const n1 = await workflow.createNode({
|
|
202
237
|
type: 'request',
|
|
203
238
|
config: {
|
|
204
|
-
url: URL_DATA,
|
|
239
|
+
url: api.URL_DATA,
|
|
205
240
|
method: 'POST',
|
|
206
241
|
data: { title: '{{$context.data.title}}' },
|
|
207
242
|
} as RequestConfig,
|
|
@@ -222,7 +257,7 @@ describe('workflow > instructions > request', () => {
|
|
|
222
257
|
const n1 = await workflow.createNode({
|
|
223
258
|
type: 'request',
|
|
224
259
|
config: {
|
|
225
|
-
url: URL_DATA,
|
|
260
|
+
url: api.URL_DATA,
|
|
226
261
|
method: 'POST',
|
|
227
262
|
data: { title: '{{$context.data.title}}' },
|
|
228
263
|
} as RequestConfig,
|
|
@@ -254,7 +289,7 @@ describe('workflow > instructions > request', () => {
|
|
|
254
289
|
upstreamId: n1.id,
|
|
255
290
|
branchIndex: 0,
|
|
256
291
|
config: {
|
|
257
|
-
url: URL_DATA,
|
|
292
|
+
url: api.URL_DATA,
|
|
258
293
|
method: 'GET',
|
|
259
294
|
},
|
|
260
295
|
});
|
|
@@ -286,10 +321,14 @@ describe('workflow > instructions > request', () => {
|
|
|
286
321
|
},
|
|
287
322
|
);
|
|
288
323
|
|
|
324
|
+
const server = app.listen(12346, () => {});
|
|
325
|
+
|
|
326
|
+
await sleep(1000);
|
|
327
|
+
|
|
289
328
|
const n1 = await workflow.createNode({
|
|
290
329
|
type: 'request',
|
|
291
330
|
config: {
|
|
292
|
-
url: `http://localhost
|
|
331
|
+
url: `http://localhost:12346/api/categories`,
|
|
293
332
|
method: 'POST',
|
|
294
333
|
headers: [{ name: 'Authorization', value: `Bearer ${token}` }],
|
|
295
334
|
} as RequestConfig,
|
|
@@ -306,6 +345,35 @@ describe('workflow > instructions > request', () => {
|
|
|
306
345
|
const [job] = await execution.getJobs();
|
|
307
346
|
expect(job.status).toBe(JOB_STATUS.RESOLVED);
|
|
308
347
|
expect(job.result.data).toMatchObject({});
|
|
348
|
+
|
|
349
|
+
server.close();
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
describe('sync request', () => {
|
|
354
|
+
it('sync trigger', async () => {
|
|
355
|
+
const syncFlow = await WorkflowModel.create({
|
|
356
|
+
type: 'syncTrigger',
|
|
357
|
+
enabled: true,
|
|
358
|
+
});
|
|
359
|
+
await syncFlow.createNode({
|
|
360
|
+
type: 'request',
|
|
361
|
+
config: {
|
|
362
|
+
url: api.URL_DATA,
|
|
363
|
+
method: 'GET',
|
|
364
|
+
} as RequestConfig,
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
const workflowPlugin = app.pm.get(PluginWorkflow) as PluginWorkflow;
|
|
368
|
+
const processor = (await workflowPlugin.trigger(syncFlow, { data: { title: 't1' } })) as Processor;
|
|
369
|
+
|
|
370
|
+
const [execution] = await syncFlow.getExecutions();
|
|
371
|
+
expect(processor.execution.id).toEqual(execution.id);
|
|
372
|
+
expect(processor.execution.status).toEqual(execution.status);
|
|
373
|
+
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
|
|
374
|
+
const [job] = await execution.getJobs();
|
|
375
|
+
expect(job.status).toEqual(JOB_STATUS.RESOLVED);
|
|
376
|
+
expect(job.result).toEqual({ meta: {}, data: {} });
|
|
309
377
|
});
|
|
310
378
|
});
|
|
311
379
|
});
|