@nocobase/plugin-workflow-request 2.1.0-alpha.4 → 2.1.0-alpha.45

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 (47) hide show
  1. package/LICENSE +201 -661
  2. package/README.md +79 -10
  3. package/dist/client/RequestInstruction.d.ts +8 -0
  4. package/dist/client/index.js +1 -1
  5. package/dist/externalVersion.js +6 -5
  6. package/dist/locale/en-US.json +2 -1
  7. package/dist/locale/zh-CN.json +3 -1
  8. package/dist/node_modules/joi/dist/joi-browser.min.js +1 -0
  9. package/dist/node_modules/joi/lib/annotate.js +175 -0
  10. package/dist/node_modules/joi/lib/base.js +1069 -0
  11. package/dist/node_modules/joi/lib/cache.js +143 -0
  12. package/dist/node_modules/joi/lib/common.js +216 -0
  13. package/dist/node_modules/joi/lib/compile.js +283 -0
  14. package/dist/node_modules/joi/lib/errors.js +271 -0
  15. package/dist/node_modules/joi/lib/extend.js +312 -0
  16. package/dist/node_modules/joi/lib/index.d.ts +2365 -0
  17. package/dist/node_modules/joi/lib/index.js +1 -0
  18. package/dist/node_modules/joi/lib/manifest.js +476 -0
  19. package/dist/node_modules/joi/lib/messages.js +178 -0
  20. package/dist/node_modules/joi/lib/modify.js +267 -0
  21. package/dist/node_modules/joi/lib/ref.js +414 -0
  22. package/dist/node_modules/joi/lib/schemas.js +302 -0
  23. package/dist/node_modules/joi/lib/state.js +166 -0
  24. package/dist/node_modules/joi/lib/template.js +463 -0
  25. package/dist/node_modules/joi/lib/trace.js +346 -0
  26. package/dist/node_modules/joi/lib/types/alternatives.js +364 -0
  27. package/dist/node_modules/joi/lib/types/any.js +174 -0
  28. package/dist/node_modules/joi/lib/types/array.js +809 -0
  29. package/dist/node_modules/joi/lib/types/binary.js +100 -0
  30. package/dist/node_modules/joi/lib/types/boolean.js +150 -0
  31. package/dist/node_modules/joi/lib/types/date.js +233 -0
  32. package/dist/node_modules/joi/lib/types/function.js +93 -0
  33. package/dist/node_modules/joi/lib/types/keys.js +1067 -0
  34. package/dist/node_modules/joi/lib/types/link.js +168 -0
  35. package/dist/node_modules/joi/lib/types/number.js +363 -0
  36. package/dist/node_modules/joi/lib/types/object.js +22 -0
  37. package/dist/node_modules/joi/lib/types/string.js +850 -0
  38. package/dist/node_modules/joi/lib/types/symbol.js +102 -0
  39. package/dist/node_modules/joi/lib/validator.js +750 -0
  40. package/dist/node_modules/joi/lib/values.js +263 -0
  41. package/dist/node_modules/joi/node_modules/@hapi/topo/lib/index.d.ts +60 -0
  42. package/dist/node_modules/joi/node_modules/@hapi/topo/lib/index.js +225 -0
  43. package/dist/node_modules/joi/node_modules/@hapi/topo/package.json +30 -0
  44. package/dist/node_modules/joi/package.json +1 -0
  45. package/dist/server/RequestInstruction.d.ts +15 -5
  46. package/dist/server/RequestInstruction.js +106 -29
  47. package/package.json +4 -3
@@ -39,10 +39,11 @@ __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");
46
47
  function getContentTypeTransformer(mimeType, app) {
47
48
  switch (mimeType) {
48
49
  case "text/plain":
@@ -85,8 +86,30 @@ function getContentTypeTransformer(mimeType, app) {
85
86
  };
86
87
  }
87
88
  }
88
- async function request(config, app) {
89
+ function createInvalidUrlError(cause) {
90
+ if (cause instanceof TypeError && typeof cause.code !== "undefined") {
91
+ return cause;
92
+ }
93
+ const error = new TypeError("Invalid URL");
94
+ error.code = "ERR_INVALID_URL";
95
+ return error;
96
+ }
97
+ function validateUrl(url) {
98
+ if (!url) {
99
+ throw createInvalidUrlError();
100
+ }
101
+ try {
102
+ const parsed = new URL(url);
103
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
104
+ throw createInvalidUrlError();
105
+ }
106
+ } catch (error) {
107
+ throw createInvalidUrlError(error);
108
+ }
109
+ }
110
+ async function request(config, app, signal) {
89
111
  const { url, method = "POST", contentType = "application/json", data, timeout = 5e3 } = config;
112
+ validateUrl(url);
90
113
  const headers = (config.headers ?? []).reduce((result, header) => {
91
114
  const name = (0, import_lodash.trim)(header.name);
92
115
  if (name.toLowerCase() === "content-type") {
@@ -102,12 +125,13 @@ async function request(config, app) {
102
125
  headers["Content-Type"] = contentType;
103
126
  }
104
127
  const transformer = getContentTypeTransformer(contentType, app);
105
- return import_axios.default.request({
128
+ return (0, import_utils.serverRequest)({
106
129
  url: (0, import_lodash.trim)(url),
107
130
  method,
108
131
  headers,
109
132
  params,
110
133
  timeout,
134
+ signal,
111
135
  ...method.toLowerCase() !== "get" && data != null ? {
112
136
  data: transformer ? await transformer(data) : data
113
137
  } : {}
@@ -118,46 +142,91 @@ function responseSuccess(response, onlyData = false) {
118
142
  status: response.status,
119
143
  statusText: response.statusText,
120
144
  headers: response.headers,
121
- config: response.config,
122
145
  data: response.data
123
146
  };
124
147
  }
125
148
  function responseFailure(error) {
126
- let result = {
127
- message: error.message,
128
- stack: error.stack
149
+ const result = {
150
+ message: error instanceof Error ? error.message : String(error)
129
151
  };
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
- }
152
+ if (typeof (error == null ? void 0 : error.code) !== "undefined") {
153
+ result["code"] = error.code;
154
+ }
155
+ if ((error == null ? void 0 : error.isAxiosError) && error.response) {
156
+ Object.assign(result, {
157
+ status: error.response.status,
158
+ statusText: error.response.statusText,
159
+ data: error.response.data
160
+ });
142
161
  }
143
162
  return result;
144
163
  }
164
+ function failureStatus(config, error) {
165
+ if (config.ignoreFail) {
166
+ return import_plugin_workflow.JOB_STATUS.RESOLVED;
167
+ }
168
+ return (error == null ? void 0 : error.code) === "ECONNABORTED" ? import_plugin_workflow.JOB_STATUS.ABORTED : import_plugin_workflow.JOB_STATUS.FAILED;
169
+ }
170
+ const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
171
+ const CONTENT_TYPES = [
172
+ "application/json",
173
+ "application/x-www-form-urlencoded",
174
+ "multipart/form-data",
175
+ "application/xml",
176
+ "text/plain"
177
+ ];
178
+ function logFailureDebug(logger, error) {
179
+ if (!(error == null ? void 0 : error.isAxiosError)) {
180
+ return;
181
+ }
182
+ if (error.response) {
183
+ logger.debug("request failed response details", {
184
+ status: error.response.status,
185
+ statusText: error.response.statusText,
186
+ data: error.response.data
187
+ });
188
+ return;
189
+ }
190
+ logger.debug("request failed error details", responseFailure(error));
191
+ }
145
192
  class RequestInstruction_default extends import_plugin_workflow.Instruction {
146
- async run(node, prevJob, processor) {
193
+ configSchema = import_joi.default.object({
194
+ url: import_joi.default.string(),
195
+ method: import_joi.default.string().valid(...METHODS),
196
+ contentType: import_joi.default.string().valid(...CONTENT_TYPES),
197
+ headers: import_joi.default.array().items(
198
+ import_joi.default.object({
199
+ name: import_joi.default.string(),
200
+ value: import_joi.default.string()
201
+ })
202
+ ),
203
+ params: import_joi.default.array().items(
204
+ import_joi.default.object({
205
+ name: import_joi.default.string(),
206
+ value: import_joi.default.string()
207
+ })
208
+ ),
209
+ data: import_joi.default.alternatives().try(import_joi.default.object(), import_joi.default.array(), import_joi.default.string()),
210
+ timeout: import_joi.default.number().integer().positive().default(5e3),
211
+ ignoreFail: import_joi.default.boolean().default(false),
212
+ onlyData: import_joi.default.boolean().default(false)
213
+ });
214
+ async run(node, prevJob, processor, options) {
147
215
  const config = processor.getParsedValue(node.config, node.id);
148
216
  const { workflow } = processor.execution;
149
217
  const sync = this.workflow.isWorkflowSync(workflow);
150
218
  if (sync) {
151
219
  try {
152
- const response = await request(config, this.workflow.app);
220
+ const response = await request(config, this.workflow.app, options == null ? void 0 : options.signal);
153
221
  return {
154
222
  status: import_plugin_workflow.JOB_STATUS.RESOLVED,
155
223
  result: responseSuccess(response, config.onlyData)
156
224
  };
157
225
  } catch (error) {
226
+ logFailureDebug(this.workflow.app.logger, error);
158
227
  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
228
+ status: failureStatus(config, error),
229
+ result: responseFailure(error)
161
230
  };
162
231
  }
163
232
  }
@@ -171,7 +240,7 @@ class RequestInstruction_default extends import_plugin_workflow.Instruction {
171
240
  const jobDone = { status: import_plugin_workflow.JOB_STATUS.PENDING };
172
241
  try {
173
242
  processor.logger.info(`request (#${node.id}) sent to "${config.url}", waiting for response...`);
174
- const response = await request(config, this.workflow.app);
243
+ const response = await request(config, this.workflow.app, options == null ? void 0 : options.signal);
175
244
  processor.logger.info(`request (#${node.id}) response success, status: ${response.status}`);
176
245
  jobDone.status = import_plugin_workflow.JOB_STATUS.RESOLVED;
177
246
  jobDone.result = responseSuccess(response, config.onlyData);
@@ -187,14 +256,21 @@ class RequestInstruction_default extends import_plugin_workflow.Instruction {
187
256
  } else {
188
257
  processor.logger.error(`request (#${node.id}) failed unexpectedly: ${error.message}`);
189
258
  }
190
- jobDone.status = config.ignoreFail ? import_plugin_workflow.JOB_STATUS.RESOLVED : import_plugin_workflow.JOB_STATUS.FAILED;
259
+ logFailureDebug(processor.logger, error);
260
+ jobDone.status = failureStatus(config, error);
191
261
  jobDone.result = responseFailure(error);
192
262
  } finally {
193
263
  const job = await this.workflow.app.db.getRepository("jobs").findOne({
194
264
  filterByTk: id
195
265
  });
196
- job.set(jobDone);
197
- this.workflow.resume(job);
266
+ const execution = await job.getExecution();
267
+ if (!execution.status) {
268
+ job.set(jobDone);
269
+ job.execution = execution;
270
+ this.workflow.resume(job);
271
+ } else {
272
+ processor.logger.warn(`request (#${node.id}) result discarded because execution (${execution.id}) is ended`);
273
+ }
198
274
  }
199
275
  }
200
276
  async resume(node, job, processor) {
@@ -212,9 +288,10 @@ class RequestInstruction_default extends import_plugin_workflow.Instruction {
212
288
  result: responseSuccess(response, config.onlyData)
213
289
  };
214
290
  } catch (error) {
291
+ logFailureDebug(this.workflow.app.logger, error);
215
292
  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
293
+ status: failureStatus(config, error),
294
+ result: responseFailure(error)
218
295
  };
219
296
  }
220
297
  }
package/package.json CHANGED
@@ -6,8 +6,8 @@
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-alpha.4",
10
- "license": "AGPL-3.0",
9
+ "version": "2.1.0-alpha.45",
10
+ "license": "Apache-2.0",
11
11
  "main": "./dist/server/index.js",
12
12
  "homepage": "https://docs.nocobase.com/handbook/workflow-request",
13
13
  "homepage.ru-RU": "https://docs-ru.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": "32c682485e5f1878b9165be2e9de18000d98f935",
30
+ "gitHead": "e9e24987e12d0ad10a5db8815b1e1b7b447f1938",
30
31
  "keywords": [
31
32
  "Workflow"
32
33
  ]