@nocobase/plugin-workflow-request 0.17.0-alpha.4
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/LICENSE +661 -0
- package/README.md +9 -0
- package/README.zh-CN.md +9 -0
- package/client.d.ts +2 -0
- package/client.js +1 -0
- package/dist/client/RequestInstruction.d.ts +180 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.js +1 -0
- package/dist/externalVersion.js +8 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +39 -0
- package/dist/locale/en-US.json +20 -0
- package/dist/locale/es-ES.json +19 -0
- package/dist/locale/fr-FR.json +19 -0
- package/dist/locale/index.d.ts +3 -0
- package/dist/locale/index.js +39 -0
- package/dist/locale/pt-BR.json +19 -0
- package/dist/locale/zh-CN.json +20 -0
- package/dist/server/Plugin.d.ts +6 -0
- package/dist/server/Plugin.js +42 -0
- package/dist/server/RequestInstruction.d.ts +14 -0
- package/dist/server/RequestInstruction.js +89 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +33 -0
- package/package.json +24 -0
- package/server.d.ts +2 -0
- package/server.js +1 -0
- package/src/client/RequestInstruction.tsx +183 -0
- package/src/client/index.ts +19 -0
- package/src/index.ts +2 -0
- package/src/locale/en-US.json +20 -0
- package/src/locale/es-ES.json +19 -0
- package/src/locale/fr-FR.json +19 -0
- package/src/locale/index.ts +12 -0
- package/src/locale/pt-BR.json +19 -0
- package/src/locale/zh-CN.json +20 -0
- package/src/server/Plugin.ts +14 -0
- package/src/server/RequestInstruction.ts +83 -0
- package/src/server/__tests__/instruction.test.ts +311 -0
- package/src/server/index.ts +1 -0
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import jwt from 'jsonwebtoken';
|
|
2
|
+
|
|
3
|
+
import { Gateway } from '@nocobase/server';
|
|
4
|
+
import Database from '@nocobase/database';
|
|
5
|
+
import { MockServer } from '@nocobase/test';
|
|
6
|
+
|
|
7
|
+
import { EXECUTION_STATUS, JOB_STATUS } from '@nocobase/plugin-workflow';
|
|
8
|
+
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
|
9
|
+
|
|
10
|
+
import Plugin from '..';
|
|
11
|
+
import { RequestConfig } from '../RequestInstruction';
|
|
12
|
+
|
|
13
|
+
const HOST = 'localhost';
|
|
14
|
+
const PORT = 12345;
|
|
15
|
+
|
|
16
|
+
const URL_DATA = `http://${HOST}:${PORT}/api/data`;
|
|
17
|
+
const URL_400 = `http://${HOST}:${PORT}/api/400`;
|
|
18
|
+
const URL_TIMEOUT = `http://${HOST}:${PORT}/api/timeout`;
|
|
19
|
+
|
|
20
|
+
describe('workflow > instructions > request', () => {
|
|
21
|
+
let app: MockServer;
|
|
22
|
+
let db: Database;
|
|
23
|
+
let PostRepo;
|
|
24
|
+
let PostCollection;
|
|
25
|
+
let ReplyRepo;
|
|
26
|
+
let WorkflowModel;
|
|
27
|
+
let workflow;
|
|
28
|
+
|
|
29
|
+
beforeEach(async () => {
|
|
30
|
+
app = await getApp({
|
|
31
|
+
plugins: ['users', 'auth', Plugin],
|
|
32
|
+
resourcer: {
|
|
33
|
+
prefix: '/api',
|
|
34
|
+
},
|
|
35
|
+
autoStart: false,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
app.use(async (ctx, next) => {
|
|
39
|
+
if (ctx.path === '/api/400') {
|
|
40
|
+
return ctx.throw(400);
|
|
41
|
+
}
|
|
42
|
+
if (ctx.path === '/api/timeout') {
|
|
43
|
+
await sleep(2000);
|
|
44
|
+
ctx.status = 204;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (ctx.path === '/api/data') {
|
|
48
|
+
await sleep(100);
|
|
49
|
+
ctx.withoutDataWrapping = true;
|
|
50
|
+
ctx.body = {
|
|
51
|
+
meta: { title: ctx.query.title },
|
|
52
|
+
data: { title: ctx.request.body['title'] },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
await next();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
Gateway.getInstance().start({
|
|
59
|
+
port: PORT,
|
|
60
|
+
host: HOST,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
await app.start();
|
|
64
|
+
|
|
65
|
+
db = app.db;
|
|
66
|
+
WorkflowModel = db.getCollection('workflows').model;
|
|
67
|
+
PostCollection = db.getCollection('posts');
|
|
68
|
+
PostRepo = PostCollection.repository;
|
|
69
|
+
ReplyRepo = db.getCollection('replies').repository;
|
|
70
|
+
|
|
71
|
+
workflow = await WorkflowModel.create({
|
|
72
|
+
enabled: true,
|
|
73
|
+
type: 'collection',
|
|
74
|
+
config: {
|
|
75
|
+
mode: 1,
|
|
76
|
+
collection: 'posts',
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
afterEach(() => app.destroy());
|
|
82
|
+
|
|
83
|
+
describe('request static app routes', () => {
|
|
84
|
+
it('get data', async () => {
|
|
85
|
+
await workflow.createNode({
|
|
86
|
+
type: 'request',
|
|
87
|
+
config: {
|
|
88
|
+
url: URL_DATA,
|
|
89
|
+
method: 'GET',
|
|
90
|
+
} as RequestConfig,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
await PostRepo.create({ values: { title: 't1' } });
|
|
94
|
+
|
|
95
|
+
await sleep(500);
|
|
96
|
+
|
|
97
|
+
const [execution] = await workflow.getExecutions();
|
|
98
|
+
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
|
|
99
|
+
const [job] = await execution.getJobs();
|
|
100
|
+
expect(job.status).toEqual(JOB_STATUS.RESOLVED);
|
|
101
|
+
expect(job.result).toEqual({ meta: {}, data: {} });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('timeout', async () => {
|
|
105
|
+
await workflow.createNode({
|
|
106
|
+
type: 'request',
|
|
107
|
+
config: {
|
|
108
|
+
url: URL_TIMEOUT,
|
|
109
|
+
method: 'GET',
|
|
110
|
+
timeout: 250,
|
|
111
|
+
} as RequestConfig,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
await PostRepo.create({ values: { title: 't1' } });
|
|
115
|
+
|
|
116
|
+
await sleep(1000);
|
|
117
|
+
|
|
118
|
+
const [execution] = await workflow.getExecutions();
|
|
119
|
+
const [job] = await execution.getJobs();
|
|
120
|
+
expect(job.status).toEqual(JOB_STATUS.FAILED);
|
|
121
|
+
|
|
122
|
+
expect(job.result).toMatchObject({
|
|
123
|
+
code: 'ECONNABORTED',
|
|
124
|
+
name: 'Error',
|
|
125
|
+
status: null,
|
|
126
|
+
message: 'timeout of 250ms exceeded',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// NOTE: to wait for the response to finish and avoid non finished promise.
|
|
130
|
+
await sleep(1500);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('ignoreFail', async () => {
|
|
134
|
+
await workflow.createNode({
|
|
135
|
+
type: 'request',
|
|
136
|
+
config: {
|
|
137
|
+
url: URL_TIMEOUT,
|
|
138
|
+
method: 'GET',
|
|
139
|
+
timeout: 250,
|
|
140
|
+
ignoreFail: true,
|
|
141
|
+
} as RequestConfig,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
await PostRepo.create({ values: { title: 't1' } });
|
|
145
|
+
|
|
146
|
+
await sleep(1000);
|
|
147
|
+
|
|
148
|
+
const [execution] = await workflow.getExecutions();
|
|
149
|
+
const [job] = await execution.getJobs();
|
|
150
|
+
expect(job.status).toEqual(JOB_STATUS.RESOLVED);
|
|
151
|
+
expect(job.result).toMatchObject({
|
|
152
|
+
code: 'ECONNABORTED',
|
|
153
|
+
name: 'Error',
|
|
154
|
+
status: null,
|
|
155
|
+
message: 'timeout of 250ms exceeded',
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('response 400', async () => {
|
|
160
|
+
await workflow.createNode({
|
|
161
|
+
type: 'request',
|
|
162
|
+
config: {
|
|
163
|
+
url: URL_400,
|
|
164
|
+
method: 'GET',
|
|
165
|
+
ignoreFail: false,
|
|
166
|
+
} as RequestConfig,
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
await PostRepo.create({ values: { title: 't1' } });
|
|
170
|
+
|
|
171
|
+
await sleep(500);
|
|
172
|
+
|
|
173
|
+
const [execution] = await workflow.getExecutions();
|
|
174
|
+
const [job] = await execution.getJobs();
|
|
175
|
+
expect(job.status).toEqual(JOB_STATUS.FAILED);
|
|
176
|
+
expect(job.result.status).toBe(400);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('response 400 ignoreFail', async () => {
|
|
180
|
+
await workflow.createNode({
|
|
181
|
+
type: 'request',
|
|
182
|
+
config: {
|
|
183
|
+
url: URL_400,
|
|
184
|
+
method: 'GET',
|
|
185
|
+
timeout: 1000,
|
|
186
|
+
ignoreFail: true,
|
|
187
|
+
} as RequestConfig,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
await PostRepo.create({ values: { title: 't1' } });
|
|
191
|
+
|
|
192
|
+
await sleep(500);
|
|
193
|
+
|
|
194
|
+
const [execution] = await workflow.getExecutions();
|
|
195
|
+
const [job] = await execution.getJobs();
|
|
196
|
+
expect(job.status).toEqual(JOB_STATUS.RESOLVED);
|
|
197
|
+
expect(job.result.status).toBe(400);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('request with data', async () => {
|
|
201
|
+
const n1 = await workflow.createNode({
|
|
202
|
+
type: 'request',
|
|
203
|
+
config: {
|
|
204
|
+
url: URL_DATA,
|
|
205
|
+
method: 'POST',
|
|
206
|
+
data: { title: '{{$context.data.title}}' },
|
|
207
|
+
} as RequestConfig,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
await PostRepo.create({ values: { title: 't1' } });
|
|
211
|
+
|
|
212
|
+
await sleep(500);
|
|
213
|
+
|
|
214
|
+
const [execution] = await workflow.getExecutions();
|
|
215
|
+
const [job] = await execution.getJobs();
|
|
216
|
+
expect(job.status).toEqual(JOB_STATUS.RESOLVED);
|
|
217
|
+
expect(job.result.data).toEqual({ title: 't1' });
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
// TODO(bug): should not use ejs
|
|
221
|
+
it('request json data with multiple lines', async () => {
|
|
222
|
+
const n1 = await workflow.createNode({
|
|
223
|
+
type: 'request',
|
|
224
|
+
config: {
|
|
225
|
+
url: URL_DATA,
|
|
226
|
+
method: 'POST',
|
|
227
|
+
data: { title: '{{$context.data.title}}' },
|
|
228
|
+
} as RequestConfig,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const title = 't1\n\nline 2';
|
|
232
|
+
await PostRepo.create({
|
|
233
|
+
values: { title },
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
await sleep(500);
|
|
237
|
+
|
|
238
|
+
const [execution] = await workflow.getExecutions();
|
|
239
|
+
const [job] = await execution.getJobs();
|
|
240
|
+
expect(job.status).toEqual(JOB_STATUS.RESOLVED);
|
|
241
|
+
expect(job.result.data).toEqual({ title });
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it.skip('request inside loop', async () => {
|
|
245
|
+
const n1 = await workflow.createNode({
|
|
246
|
+
type: 'loop',
|
|
247
|
+
config: {
|
|
248
|
+
target: 2,
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
const n2 = await workflow.createNode({
|
|
253
|
+
type: 'request',
|
|
254
|
+
upstreamId: n1.id,
|
|
255
|
+
branchIndex: 0,
|
|
256
|
+
config: {
|
|
257
|
+
url: URL_DATA,
|
|
258
|
+
method: 'GET',
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
await PostRepo.create({ values: { title: 't1' } });
|
|
263
|
+
|
|
264
|
+
await sleep(500);
|
|
265
|
+
|
|
266
|
+
const [execution] = await workflow.getExecutions();
|
|
267
|
+
expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
|
|
268
|
+
const jobs = await execution.getJobs({ order: [['id', 'ASC']] });
|
|
269
|
+
expect(jobs.length).toBe(3);
|
|
270
|
+
expect(jobs.map((item) => item.status)).toEqual(Array(3).fill(JOB_STATUS.RESOLVED));
|
|
271
|
+
expect(jobs[0].result).toBe(2);
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe('request db resource', () => {
|
|
276
|
+
it('request db resource', async () => {
|
|
277
|
+
const user = await db.getRepository('users').create({});
|
|
278
|
+
|
|
279
|
+
const token = jwt.sign(
|
|
280
|
+
{
|
|
281
|
+
userId: typeof user.id,
|
|
282
|
+
},
|
|
283
|
+
process.env.APP_KEY,
|
|
284
|
+
{
|
|
285
|
+
expiresIn: '1d',
|
|
286
|
+
},
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
const n1 = await workflow.createNode({
|
|
290
|
+
type: 'request',
|
|
291
|
+
config: {
|
|
292
|
+
url: `http://localhost:${PORT}/api/categories`,
|
|
293
|
+
method: 'POST',
|
|
294
|
+
headers: [{ name: 'Authorization', value: `Bearer ${token}` }],
|
|
295
|
+
} as RequestConfig,
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
await PostRepo.create({ values: { title: 't1' } });
|
|
299
|
+
|
|
300
|
+
await sleep(500);
|
|
301
|
+
|
|
302
|
+
const category = await db.getRepository('categories').findOne({});
|
|
303
|
+
|
|
304
|
+
const [execution] = await workflow.getExecutions();
|
|
305
|
+
expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
|
|
306
|
+
const [job] = await execution.getJobs();
|
|
307
|
+
expect(job.status).toBe(JOB_STATUS.RESOLVED);
|
|
308
|
+
expect(job.result.data).toMatchObject({});
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './Plugin';
|