@nocobase/plugin-workflow-request 2.1.0-beta.8 → 2.2.0-alpha.1
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/client/RequestInstruction.d.ts +8 -0
- package/dist/client/index.js +1 -1
- package/dist/externalVersion.js +6 -5
- package/dist/locale/en-US.json +2 -1
- package/dist/locale/zh-CN.json +3 -1
- package/dist/node_modules/joi/dist/joi-browser.min.js +1 -0
- package/dist/node_modules/joi/lib/annotate.js +175 -0
- package/dist/node_modules/joi/lib/base.js +1069 -0
- package/dist/node_modules/joi/lib/cache.js +143 -0
- package/dist/node_modules/joi/lib/common.js +216 -0
- package/dist/node_modules/joi/lib/compile.js +283 -0
- package/dist/node_modules/joi/lib/errors.js +271 -0
- package/dist/node_modules/joi/lib/extend.js +312 -0
- package/dist/node_modules/joi/lib/index.d.ts +2365 -0
- package/dist/node_modules/joi/lib/index.js +1 -0
- package/dist/node_modules/joi/lib/manifest.js +476 -0
- package/dist/node_modules/joi/lib/messages.js +178 -0
- package/dist/node_modules/joi/lib/modify.js +267 -0
- package/dist/node_modules/joi/lib/ref.js +414 -0
- package/dist/node_modules/joi/lib/schemas.js +302 -0
- package/dist/node_modules/joi/lib/state.js +166 -0
- package/dist/node_modules/joi/lib/template.js +463 -0
- package/dist/node_modules/joi/lib/trace.js +346 -0
- package/dist/node_modules/joi/lib/types/alternatives.js +364 -0
- package/dist/node_modules/joi/lib/types/any.js +174 -0
- package/dist/node_modules/joi/lib/types/array.js +809 -0
- package/dist/node_modules/joi/lib/types/binary.js +100 -0
- package/dist/node_modules/joi/lib/types/boolean.js +150 -0
- package/dist/node_modules/joi/lib/types/date.js +233 -0
- package/dist/node_modules/joi/lib/types/function.js +93 -0
- package/dist/node_modules/joi/lib/types/keys.js +1067 -0
- package/dist/node_modules/joi/lib/types/link.js +168 -0
- package/dist/node_modules/joi/lib/types/number.js +363 -0
- package/dist/node_modules/joi/lib/types/object.js +22 -0
- package/dist/node_modules/joi/lib/types/string.js +850 -0
- package/dist/node_modules/joi/lib/types/symbol.js +102 -0
- package/dist/node_modules/joi/lib/validator.js +750 -0
- package/dist/node_modules/joi/lib/values.js +263 -0
- package/dist/node_modules/joi/node_modules/@hapi/topo/lib/index.d.ts +60 -0
- package/dist/node_modules/joi/node_modules/@hapi/topo/lib/index.js +225 -0
- package/dist/node_modules/joi/node_modules/@hapi/topo/package.json +30 -0
- package/dist/node_modules/joi/package.json +1 -0
- package/dist/server/RequestInstruction.d.ts +15 -5
- package/dist/server/RequestInstruction.js +143 -49
- package/package.json +3 -2
|
@@ -39,10 +39,14 @@ __export(RequestInstruction_exports, {
|
|
|
39
39
|
default: () => RequestInstruction_default
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(RequestInstruction_exports);
|
|
42
|
-
var
|
|
42
|
+
var import_joi = __toESM(require("joi"));
|
|
43
43
|
var import_lodash = require("lodash");
|
|
44
44
|
var import_plugin_workflow = require("@nocobase/plugin-workflow");
|
|
45
45
|
var import_plugin_file_manager = __toESM(require("@nocobase/plugin-file-manager"));
|
|
46
|
+
var import_utils = require("@nocobase/utils");
|
|
47
|
+
function toRequestError(error) {
|
|
48
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
49
|
+
}
|
|
46
50
|
function getContentTypeTransformer(mimeType, app) {
|
|
47
51
|
switch (mimeType) {
|
|
48
52
|
case "text/plain":
|
|
@@ -85,8 +89,30 @@ function getContentTypeTransformer(mimeType, app) {
|
|
|
85
89
|
};
|
|
86
90
|
}
|
|
87
91
|
}
|
|
88
|
-
|
|
92
|
+
function createInvalidUrlError(cause) {
|
|
93
|
+
if (cause instanceof TypeError && typeof cause.code !== "undefined") {
|
|
94
|
+
return cause;
|
|
95
|
+
}
|
|
96
|
+
const error = new TypeError("Invalid URL");
|
|
97
|
+
error.code = "ERR_INVALID_URL";
|
|
98
|
+
return error;
|
|
99
|
+
}
|
|
100
|
+
function validateUrl(url) {
|
|
101
|
+
if (!url) {
|
|
102
|
+
throw createInvalidUrlError();
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
const parsed = new URL(url);
|
|
106
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
107
|
+
throw createInvalidUrlError();
|
|
108
|
+
}
|
|
109
|
+
} catch (error) {
|
|
110
|
+
throw createInvalidUrlError(error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function request(config, app, signal) {
|
|
89
114
|
const { url, method = "POST", contentType = "application/json", data, timeout = 5e3 } = config;
|
|
115
|
+
validateUrl(url);
|
|
90
116
|
const headers = (config.headers ?? []).reduce((result, header) => {
|
|
91
117
|
const name = (0, import_lodash.trim)(header.name);
|
|
92
118
|
if (name.toLowerCase() === "content-type") {
|
|
@@ -102,12 +128,13 @@ async function request(config, app) {
|
|
|
102
128
|
headers["Content-Type"] = contentType;
|
|
103
129
|
}
|
|
104
130
|
const transformer = getContentTypeTransformer(contentType, app);
|
|
105
|
-
return
|
|
131
|
+
return (0, import_utils.serverRequest)({
|
|
106
132
|
url: (0, import_lodash.trim)(url),
|
|
107
133
|
method,
|
|
108
134
|
headers,
|
|
109
135
|
params,
|
|
110
136
|
timeout,
|
|
137
|
+
signal,
|
|
111
138
|
...method.toLowerCase() !== "get" && data != null ? {
|
|
112
139
|
data: transformer ? await transformer(data) : data
|
|
113
140
|
} : {}
|
|
@@ -118,46 +145,91 @@ function responseSuccess(response, onlyData = false) {
|
|
|
118
145
|
status: response.status,
|
|
119
146
|
statusText: response.statusText,
|
|
120
147
|
headers: response.headers,
|
|
121
|
-
config: response.config,
|
|
122
148
|
data: response.data
|
|
123
149
|
};
|
|
124
150
|
}
|
|
125
151
|
function responseFailure(error) {
|
|
126
|
-
|
|
127
|
-
message: error.message
|
|
128
|
-
stack: error.stack
|
|
152
|
+
const result = {
|
|
153
|
+
message: error instanceof Error ? error.message : String(error)
|
|
129
154
|
};
|
|
130
|
-
if (error.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
} else if (error.request) {
|
|
140
|
-
result = error.toJSON();
|
|
141
|
-
}
|
|
155
|
+
if (typeof (error == null ? void 0 : error.code) !== "undefined") {
|
|
156
|
+
result["code"] = error.code;
|
|
157
|
+
}
|
|
158
|
+
if ((error == null ? void 0 : error.isAxiosError) && error.response) {
|
|
159
|
+
Object.assign(result, {
|
|
160
|
+
status: error.response.status,
|
|
161
|
+
statusText: error.response.statusText,
|
|
162
|
+
data: error.response.data
|
|
163
|
+
});
|
|
142
164
|
}
|
|
143
165
|
return result;
|
|
144
166
|
}
|
|
167
|
+
function failureStatus(config, error) {
|
|
168
|
+
if (config.ignoreFail) {
|
|
169
|
+
return import_plugin_workflow.JOB_STATUS.RESOLVED;
|
|
170
|
+
}
|
|
171
|
+
return (error == null ? void 0 : error.code) === "ECONNABORTED" ? import_plugin_workflow.JOB_STATUS.ABORTED : import_plugin_workflow.JOB_STATUS.FAILED;
|
|
172
|
+
}
|
|
173
|
+
const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
|
|
174
|
+
const CONTENT_TYPES = [
|
|
175
|
+
"application/json",
|
|
176
|
+
"application/x-www-form-urlencoded",
|
|
177
|
+
"multipart/form-data",
|
|
178
|
+
"application/xml",
|
|
179
|
+
"text/plain"
|
|
180
|
+
];
|
|
181
|
+
function logFailureDebug(logger, error) {
|
|
182
|
+
if (!(error == null ? void 0 : error.isAxiosError)) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (error.response) {
|
|
186
|
+
logger.debug("request failed response details", {
|
|
187
|
+
status: error.response.status,
|
|
188
|
+
statusText: error.response.statusText,
|
|
189
|
+
data: error.response.data
|
|
190
|
+
});
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
logger.debug("request failed error details", responseFailure(error));
|
|
194
|
+
}
|
|
145
195
|
class RequestInstruction_default extends import_plugin_workflow.Instruction {
|
|
146
|
-
|
|
196
|
+
configSchema = import_joi.default.object({
|
|
197
|
+
url: import_joi.default.string(),
|
|
198
|
+
method: import_joi.default.string().valid(...METHODS),
|
|
199
|
+
contentType: import_joi.default.string().valid(...CONTENT_TYPES),
|
|
200
|
+
headers: import_joi.default.array().items(
|
|
201
|
+
import_joi.default.object({
|
|
202
|
+
name: import_joi.default.string(),
|
|
203
|
+
value: import_joi.default.string()
|
|
204
|
+
})
|
|
205
|
+
),
|
|
206
|
+
params: import_joi.default.array().items(
|
|
207
|
+
import_joi.default.object({
|
|
208
|
+
name: import_joi.default.string(),
|
|
209
|
+
value: import_joi.default.string()
|
|
210
|
+
})
|
|
211
|
+
),
|
|
212
|
+
data: import_joi.default.alternatives().try(import_joi.default.object(), import_joi.default.array(), import_joi.default.string()),
|
|
213
|
+
timeout: import_joi.default.number().integer().positive().default(5e3),
|
|
214
|
+
ignoreFail: import_joi.default.boolean().default(false),
|
|
215
|
+
onlyData: import_joi.default.boolean().default(false)
|
|
216
|
+
});
|
|
217
|
+
async run(node, prevJob, processor, options) {
|
|
147
218
|
const config = processor.getParsedValue(node.config, node.id);
|
|
148
219
|
const { workflow } = processor.execution;
|
|
149
220
|
const sync = this.workflow.isWorkflowSync(workflow);
|
|
150
221
|
if (sync) {
|
|
151
222
|
try {
|
|
152
|
-
const response = await request(config, this.workflow.app);
|
|
223
|
+
const response = await request(config, this.workflow.app, options == null ? void 0 : options.signal);
|
|
153
224
|
return {
|
|
154
225
|
status: import_plugin_workflow.JOB_STATUS.RESOLVED,
|
|
155
226
|
result: responseSuccess(response, config.onlyData)
|
|
156
227
|
};
|
|
157
228
|
} catch (error) {
|
|
229
|
+
logFailureDebug(this.workflow.app.logger, error);
|
|
158
230
|
return {
|
|
159
|
-
status: config
|
|
160
|
-
result: error
|
|
231
|
+
status: failureStatus(config, error),
|
|
232
|
+
result: responseFailure(error)
|
|
161
233
|
};
|
|
162
234
|
}
|
|
163
235
|
}
|
|
@@ -168,34 +240,55 @@ class RequestInstruction_default extends import_plugin_workflow.Instruction {
|
|
|
168
240
|
upstreamId: (prevJob == null ? void 0 : prevJob.id) ?? null
|
|
169
241
|
});
|
|
170
242
|
await processor.exit();
|
|
243
|
+
const abortHandle = processor.createBackgroundAbortHandle();
|
|
171
244
|
const jobDone = { status: import_plugin_workflow.JOB_STATUS.PENDING };
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
245
|
+
const settleRequest = async () => {
|
|
246
|
+
try {
|
|
247
|
+
processor.logger.info(`request (#${node.id}) sent to "${config.url}", waiting for response...`);
|
|
248
|
+
const response = await request(config, this.workflow.app, abortHandle.signal);
|
|
249
|
+
processor.logger.info(`request (#${node.id}) response success, status: ${response.status}`);
|
|
250
|
+
jobDone.status = import_plugin_workflow.JOB_STATUS.RESOLVED;
|
|
251
|
+
jobDone.result = responseSuccess(response, config.onlyData);
|
|
252
|
+
} catch (caught) {
|
|
253
|
+
const error = toRequestError(caught);
|
|
254
|
+
if (error.isAxiosError) {
|
|
255
|
+
if (error.response) {
|
|
256
|
+
processor.logger.info(`request (#${node.id}) failed with response, status: ${error.response.status}`);
|
|
257
|
+
} else if (error.request) {
|
|
258
|
+
processor.logger.error(`request (#${node.id}) failed without response: ${error.message}`);
|
|
259
|
+
} else {
|
|
260
|
+
processor.logger.error(`request (#${node.id}) initiation failed: ${error.message}`);
|
|
261
|
+
}
|
|
262
|
+
} else {
|
|
263
|
+
processor.logger.error(`request (#${node.id}) failed unexpectedly: ${error.message}`);
|
|
264
|
+
}
|
|
265
|
+
logFailureDebug(processor.logger, error);
|
|
266
|
+
jobDone.status = abortHandle.signal.aborted ? import_plugin_workflow.JOB_STATUS.ABORTED : failureStatus(config, error);
|
|
267
|
+
jobDone.result = responseFailure(error);
|
|
268
|
+
} finally {
|
|
269
|
+
abortHandle.dispose();
|
|
270
|
+
const job = await this.workflow.app.db.getRepository("jobs").findOne({
|
|
271
|
+
filterByTk: id
|
|
272
|
+
});
|
|
273
|
+
const execution = await job.getExecution();
|
|
274
|
+
const aborted = await this.workflow.abortExecutionIfExpired(execution);
|
|
275
|
+
if (!aborted) {
|
|
276
|
+
await execution.reload();
|
|
277
|
+
await job.reload();
|
|
278
|
+
}
|
|
279
|
+
if (!aborted && execution.status === import_plugin_workflow.EXECUTION_STATUS.STARTED && job.status === import_plugin_workflow.JOB_STATUS.PENDING) {
|
|
280
|
+
job.set(jobDone);
|
|
281
|
+
job.execution = execution;
|
|
282
|
+
this.workflow.resume(job);
|
|
184
283
|
} else {
|
|
185
|
-
processor.logger.
|
|
284
|
+
processor.logger.warn(`request (#${node.id}) result discarded because execution (${execution.id}) is ended`);
|
|
186
285
|
}
|
|
187
|
-
} else {
|
|
188
|
-
processor.logger.error(`request (#${node.id}) failed unexpectedly: ${error.message}`);
|
|
189
286
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
});
|
|
196
|
-
job.set(jobDone);
|
|
197
|
-
this.workflow.resume(job);
|
|
198
|
-
}
|
|
287
|
+
};
|
|
288
|
+
settleRequest().catch((caught) => {
|
|
289
|
+
const error = toRequestError(caught);
|
|
290
|
+
processor.logger.error(`request (#${node.id}) async settling failed: ${error.message}`, { error });
|
|
291
|
+
});
|
|
199
292
|
}
|
|
200
293
|
async resume(node, job, processor) {
|
|
201
294
|
const { ignoreFail } = node.config;
|
|
@@ -212,9 +305,10 @@ class RequestInstruction_default extends import_plugin_workflow.Instruction {
|
|
|
212
305
|
result: responseSuccess(response, config.onlyData)
|
|
213
306
|
};
|
|
214
307
|
} catch (error) {
|
|
308
|
+
logFailureDebug(this.workflow.app.logger, error);
|
|
215
309
|
return {
|
|
216
|
-
status: config
|
|
217
|
-
result: error
|
|
310
|
+
status: failureStatus(config, error),
|
|
311
|
+
result: responseFailure(error)
|
|
218
312
|
};
|
|
219
313
|
}
|
|
220
314
|
}
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"description": "Send HTTP requests to any HTTP service for data interaction in workflow.",
|
|
7
7
|
"description.ru-RU": "Отправляет HTTP-запросы к любому HTTP-сервису для взаимодействия с данными в рабочем процессе.",
|
|
8
8
|
"description.zh-CN": "可用于在工作流中向任意 HTTP 服务发送请求,进行数据交互。",
|
|
9
|
-
"version": "2.
|
|
9
|
+
"version": "2.2.0-alpha.1",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"main": "./dist/server/index.js",
|
|
12
12
|
"homepage": "https://docs.nocobase.com/handbook/workflow-request",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"antd": "5.x",
|
|
17
17
|
"axios": "^1.7.0",
|
|
18
|
+
"joi": "^17.13.3",
|
|
18
19
|
"react": "18.x",
|
|
19
20
|
"react-i18next": "^11.15.1"
|
|
20
21
|
},
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
"@nocobase/server": "2.x",
|
|
27
28
|
"@nocobase/test": "2.x"
|
|
28
29
|
},
|
|
29
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "303663aba6c6eefa27e6a6435b4c0352074ec40f",
|
|
30
31
|
"keywords": [
|
|
31
32
|
"Workflow"
|
|
32
33
|
]
|