@nocobase/plugin-workflow-request 2.1.0-beta.8 → 2.1.0

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.
Files changed (45) hide show
  1. package/dist/client/RequestInstruction.d.ts +8 -0
  2. package/dist/client/index.js +1 -1
  3. package/dist/externalVersion.js +6 -5
  4. package/dist/locale/en-US.json +2 -1
  5. package/dist/locale/zh-CN.json +3 -1
  6. package/dist/node_modules/joi/dist/joi-browser.min.js +1 -0
  7. package/dist/node_modules/joi/lib/annotate.js +175 -0
  8. package/dist/node_modules/joi/lib/base.js +1069 -0
  9. package/dist/node_modules/joi/lib/cache.js +143 -0
  10. package/dist/node_modules/joi/lib/common.js +216 -0
  11. package/dist/node_modules/joi/lib/compile.js +283 -0
  12. package/dist/node_modules/joi/lib/errors.js +271 -0
  13. package/dist/node_modules/joi/lib/extend.js +312 -0
  14. package/dist/node_modules/joi/lib/index.d.ts +2365 -0
  15. package/dist/node_modules/joi/lib/index.js +1 -0
  16. package/dist/node_modules/joi/lib/manifest.js +476 -0
  17. package/dist/node_modules/joi/lib/messages.js +178 -0
  18. package/dist/node_modules/joi/lib/modify.js +267 -0
  19. package/dist/node_modules/joi/lib/ref.js +414 -0
  20. package/dist/node_modules/joi/lib/schemas.js +302 -0
  21. package/dist/node_modules/joi/lib/state.js +166 -0
  22. package/dist/node_modules/joi/lib/template.js +463 -0
  23. package/dist/node_modules/joi/lib/trace.js +346 -0
  24. package/dist/node_modules/joi/lib/types/alternatives.js +364 -0
  25. package/dist/node_modules/joi/lib/types/any.js +174 -0
  26. package/dist/node_modules/joi/lib/types/array.js +809 -0
  27. package/dist/node_modules/joi/lib/types/binary.js +100 -0
  28. package/dist/node_modules/joi/lib/types/boolean.js +150 -0
  29. package/dist/node_modules/joi/lib/types/date.js +233 -0
  30. package/dist/node_modules/joi/lib/types/function.js +93 -0
  31. package/dist/node_modules/joi/lib/types/keys.js +1067 -0
  32. package/dist/node_modules/joi/lib/types/link.js +168 -0
  33. package/dist/node_modules/joi/lib/types/number.js +363 -0
  34. package/dist/node_modules/joi/lib/types/object.js +22 -0
  35. package/dist/node_modules/joi/lib/types/string.js +850 -0
  36. package/dist/node_modules/joi/lib/types/symbol.js +102 -0
  37. package/dist/node_modules/joi/lib/validator.js +750 -0
  38. package/dist/node_modules/joi/lib/values.js +263 -0
  39. package/dist/node_modules/joi/node_modules/@hapi/topo/lib/index.d.ts +60 -0
  40. package/dist/node_modules/joi/node_modules/@hapi/topo/lib/index.js +225 -0
  41. package/dist/node_modules/joi/node_modules/@hapi/topo/package.json +30 -0
  42. package/dist/node_modules/joi/package.json +1 -0
  43. package/dist/server/RequestInstruction.d.ts +15 -5
  44. package/dist/server/RequestInstruction.js +143 -49
  45. 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 import_axios = __toESM(require("axios"));
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
- async function request(config, app) {
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 import_axios.default.request({
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
- let result = {
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.isAxiosError) {
131
- if (error.response) {
132
- Object.assign(result, {
133
- status: error.response.status,
134
- statusText: error.response.statusText,
135
- headers: error.response.headers,
136
- config: error.response.config,
137
- data: error.response.data
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
- async run(node, prevJob, processor) {
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.ignoreFail ? import_plugin_workflow.JOB_STATUS.RESOLVED : import_plugin_workflow.JOB_STATUS.FAILED,
160
- result: error.isAxiosError ? error.toJSON() : error.message
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
- try {
173
- processor.logger.info(`request (#${node.id}) sent to "${config.url}", waiting for response...`);
174
- const response = await request(config, this.workflow.app);
175
- processor.logger.info(`request (#${node.id}) response success, status: ${response.status}`);
176
- jobDone.status = import_plugin_workflow.JOB_STATUS.RESOLVED;
177
- jobDone.result = responseSuccess(response, config.onlyData);
178
- } catch (error) {
179
- if (error.isAxiosError) {
180
- if (error.response) {
181
- processor.logger.info(`request (#${node.id}) failed with response, status: ${error.response.status}`);
182
- } else if (error.request) {
183
- processor.logger.error(`request (#${node.id}) failed without response: ${error.message}`);
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.error(`request (#${node.id}) initiation failed: ${error.message}`);
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
- jobDone.status = config.ignoreFail ? import_plugin_workflow.JOB_STATUS.RESOLVED : import_plugin_workflow.JOB_STATUS.FAILED;
191
- jobDone.result = responseFailure(error);
192
- } finally {
193
- const job = await this.workflow.app.db.getRepository("jobs").findOne({
194
- filterByTk: id
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.ignoreFail ? import_plugin_workflow.JOB_STATUS.RESOLVED : import_plugin_workflow.JOB_STATUS.FAILED,
217
- result: error.isAxiosError ? error.toJSON() : error.message
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.1.0-beta.8",
9
+ "version": "2.1.0",
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": "5099d561c5467292414c1e77ad6bad3730d97344",
30
+ "gitHead": "9373212dd0f22cd985be1e23674d6b454944b9ee",
30
31
  "keywords": [
31
32
  "Workflow"
32
33
  ]