@nocobase/plugin-workflow-custom-action-trigger 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/client-v2.d.ts +2 -0
- package/client-v2.js +3 -0
- package/dist/client/CustomActionTrigger.d.ts +36 -2
- package/dist/client/index.js +1 -1
- package/dist/client-v2/index.d.ts +10 -0
- package/dist/client-v2/index.js +10 -0
- package/dist/client-v2/locale.d.ts +11 -0
- package/dist/{client/flows.d.ts → client-v2/models/actions/TriggerWorkflowActionModels.d.ts} +6 -8
- package/dist/client-v2/models/actions/index.d.ts +9 -0
- package/dist/client-v2/models/index.d.ts +9 -0
- package/dist/client-v2/plugin.d.ts +15 -0
- package/dist/client-v2/triggers/CustomActionTrigger.d.ts +149 -0
- package/dist/externalVersion.js +14 -12
- 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/CustomActionTrigger.d.ts +17 -4
- package/dist/server/CustomActionTrigger.js +126 -12
- package/dist/server/migrations/20260416000000-grant-trigger-action-to-roles.d.ts +40 -0
- package/dist/server/migrations/20260416000000-grant-trigger-action-to-roles.js +120 -0
- package/package.json +6 -2
|
@@ -39,11 +39,13 @@ __export(CustomActionTrigger_exports, {
|
|
|
39
39
|
default: () => CustomActionTrigger
|
|
40
40
|
});
|
|
41
41
|
module.exports = __toCommonJS(CustomActionTrigger_exports);
|
|
42
|
+
var import_joi = __toESM(require("joi"));
|
|
42
43
|
var import_plugin_error_handler = __toESM(require("@nocobase/plugin-error-handler"));
|
|
43
44
|
var import_plugin_workflow = require("@nocobase/plugin-workflow");
|
|
44
45
|
var import_data_source_manager = require("@nocobase/data-source-manager");
|
|
45
46
|
var import_lodash = require("lodash");
|
|
46
47
|
var import_constants = require("../common/constants");
|
|
48
|
+
const ASYNC_WORKFLOW_TRIGGER_DELAY_MS = 200;
|
|
47
49
|
class CustomActionInterceptionError extends Error {
|
|
48
50
|
status = 400;
|
|
49
51
|
messages = [];
|
|
@@ -54,6 +56,25 @@ class CustomActionInterceptionError extends Error {
|
|
|
54
56
|
}
|
|
55
57
|
class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
56
58
|
static TYPE = import_constants.EVENT_TYPE;
|
|
59
|
+
configSchema = import_joi.default.object({
|
|
60
|
+
type: import_joi.default.number().valid(import_constants.CONTEXT_TYPE.SINGLE_RECORD, import_constants.CONTEXT_TYPE.MULTIPLE_RECORDS, import_constants.CONTEXT_TYPE.GLOBAL).required(),
|
|
61
|
+
collection: import_joi.default.when("type", {
|
|
62
|
+
is: import_joi.default.valid(import_constants.CONTEXT_TYPE.SINGLE_RECORD, import_constants.CONTEXT_TYPE.MULTIPLE_RECORDS),
|
|
63
|
+
then: import_joi.default.string().required().messages({ "any.required": "Collection is required for record-based context" }),
|
|
64
|
+
otherwise: import_joi.default.string().allow(null, "")
|
|
65
|
+
}),
|
|
66
|
+
appends: import_joi.default.array().items(import_joi.default.string()).optional()
|
|
67
|
+
});
|
|
68
|
+
validateConfig(config) {
|
|
69
|
+
const errors = super.validateConfig(config);
|
|
70
|
+
if (errors) {
|
|
71
|
+
return errors;
|
|
72
|
+
}
|
|
73
|
+
if (config.collection) {
|
|
74
|
+
return (0, import_plugin_workflow.validateCollectionField)(config.collection, this.workflow.app.dataSourceManager);
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
57
78
|
async globalTriggerAction(context, next) {
|
|
58
79
|
var _a, _b;
|
|
59
80
|
const { triggerWorkflows, values = {} } = context.action.params;
|
|
@@ -84,8 +105,7 @@ class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
|
84
105
|
const syncGroup = [];
|
|
85
106
|
const asyncGroup = [];
|
|
86
107
|
for (const workflow of workflows) {
|
|
87
|
-
const event = [workflow];
|
|
88
|
-
event.push({ data: values, ...userInfo });
|
|
108
|
+
const event = [workflow, { data: values, ...userInfo }];
|
|
89
109
|
(workflow.sync ? syncGroup : asyncGroup).push(event);
|
|
90
110
|
}
|
|
91
111
|
await this.processEvents({
|
|
@@ -95,7 +115,7 @@ class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
|
95
115
|
});
|
|
96
116
|
}
|
|
97
117
|
triggerAction = async (context, next) => {
|
|
98
|
-
var _a;
|
|
118
|
+
var _a, _b, _c;
|
|
99
119
|
const {
|
|
100
120
|
resourceName,
|
|
101
121
|
params: { filterByTk, values, triggerWorkflows = "", associatedIndex }
|
|
@@ -137,22 +157,26 @@ class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
|
137
157
|
workflows.push(workflow);
|
|
138
158
|
}
|
|
139
159
|
}
|
|
160
|
+
const scopeFilter = (_c = (_b = context.permission) == null ? void 0 : _b.parsedParams) == null ? void 0 : _c.filter;
|
|
140
161
|
const syncGroup = [];
|
|
141
162
|
const asyncGroup = [];
|
|
142
163
|
for (const workflow of workflows) {
|
|
143
|
-
const event = [workflow];
|
|
164
|
+
const event = [workflow, {}];
|
|
144
165
|
const { appends = [] } = workflow.config;
|
|
145
166
|
const dataPath = triggerWorkflowsMap.get(workflow.key);
|
|
146
167
|
const formData = dataPath ? (0, import_lodash.get)(values, dataPath) : values;
|
|
147
168
|
let data = formData;
|
|
148
169
|
if (filterByTk != null) {
|
|
149
170
|
if (Array.isArray(filterByTk)) {
|
|
150
|
-
data = (await repository.find({ filterByTk, appends, context })).map(
|
|
171
|
+
data = (await repository.find({ filterByTk, appends, context, filter: scopeFilter })).map(
|
|
151
172
|
(item) => Object.assign(item.toJSON(), formData)
|
|
152
173
|
);
|
|
153
174
|
} else {
|
|
154
|
-
data = await repository.findOne({ filterByTk, appends, context });
|
|
175
|
+
data = await repository.findOne({ filterByTk, appends, context, filter: scopeFilter });
|
|
155
176
|
if (!data) {
|
|
177
|
+
if (scopeFilter) {
|
|
178
|
+
return context.throw(403, "No permissions");
|
|
179
|
+
}
|
|
156
180
|
continue;
|
|
157
181
|
}
|
|
158
182
|
if (typeof data.toJSON === "function") {
|
|
@@ -161,7 +185,7 @@ class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
|
161
185
|
}
|
|
162
186
|
}
|
|
163
187
|
}
|
|
164
|
-
event
|
|
188
|
+
event[1] = { data, ...userInfo };
|
|
165
189
|
(workflow.sync ? syncGroup : asyncGroup).push(event);
|
|
166
190
|
}
|
|
167
191
|
await this.processEvents({
|
|
@@ -174,7 +198,34 @@ class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
|
174
198
|
super(workflow);
|
|
175
199
|
this.workflow.app.dataSourceManager.afterAddDataSource((dataSource) => {
|
|
176
200
|
dataSource.resourceManager.registerActionHandler("trigger", this.triggerAction);
|
|
177
|
-
dataSource.acl.
|
|
201
|
+
dataSource.acl.setAvailableAction("trigger", {
|
|
202
|
+
displayName: `{{t("Trigger workflow", { ns: "${import_constants.NAMESPACE}" })}}`
|
|
203
|
+
});
|
|
204
|
+
dataSource.acl.setAvailableAction("triggerNew", {
|
|
205
|
+
displayName: `{{t("Trigger workflow", { ns: "${import_constants.NAMESPACE}" })}}`,
|
|
206
|
+
onNewRecord: true
|
|
207
|
+
});
|
|
208
|
+
dataSource.resourceManager.use(
|
|
209
|
+
async (ctx, next) => {
|
|
210
|
+
const { resourceName, actionName, params } = ctx.action ?? {};
|
|
211
|
+
if (actionName === "trigger" && resourceName !== "workflows" && (params == null ? void 0 : params.filterByTk) == null) {
|
|
212
|
+
ctx.action.actionName = "triggerNew";
|
|
213
|
+
ctx.state.__customActionTriggerRenamed = true;
|
|
214
|
+
}
|
|
215
|
+
await next();
|
|
216
|
+
},
|
|
217
|
+
{ tag: "customActionTriggerRename", before: "acl", after: "auth" }
|
|
218
|
+
);
|
|
219
|
+
dataSource.resourceManager.use(
|
|
220
|
+
async (ctx, next) => {
|
|
221
|
+
if (ctx.state.__customActionTriggerRenamed) {
|
|
222
|
+
ctx.action.actionName = "trigger";
|
|
223
|
+
}
|
|
224
|
+
await next();
|
|
225
|
+
},
|
|
226
|
+
{ tag: "customActionTriggerRestore", after: "acl" }
|
|
227
|
+
);
|
|
228
|
+
dataSource.acl.allow("workflows", ["trigger"], "loggedIn");
|
|
178
229
|
});
|
|
179
230
|
workflow.app.pm.get(import_plugin_error_handler.default).errorHandler.register(
|
|
180
231
|
(err) => err.name === "CustomActionInterceptionError",
|
|
@@ -195,34 +246,97 @@ class CustomActionTrigger extends import_plugin_workflow.Trigger {
|
|
|
195
246
|
context.logger.warn("No workflow triggered");
|
|
196
247
|
return context.throw(500, "No action done, please contact the administrator");
|
|
197
248
|
}
|
|
198
|
-
|
|
199
|
-
|
|
249
|
+
const { resourceName, actionName } = context.action;
|
|
250
|
+
for (const [index, event] of syncGroup.entries()) {
|
|
251
|
+
const workflow = event[0];
|
|
252
|
+
const syncLogMeta = {
|
|
253
|
+
workflowId: workflow.id,
|
|
254
|
+
workflowKey: workflow.key,
|
|
255
|
+
workflowTitle: workflow.title,
|
|
256
|
+
resourceName,
|
|
257
|
+
actionName,
|
|
258
|
+
currentSyncOrder: index + 1,
|
|
259
|
+
totalSyncWorkflows: syncGroup.length,
|
|
260
|
+
remainingSyncWorkflows: syncGroup.length - index - 1,
|
|
261
|
+
pendingAsyncWorkflows: asyncGroup.length
|
|
262
|
+
};
|
|
263
|
+
context.logger.debug("[Workflow custom-action]: executing sync workflow", syncLogMeta);
|
|
264
|
+
const processor = await this.workflow.trigger(workflow, event[1], { httpContext: context });
|
|
200
265
|
if (!processor) {
|
|
266
|
+
context.logger.error(
|
|
267
|
+
"[Workflow custom-action]: sync workflow trigger failed before execution created",
|
|
268
|
+
syncLogMeta
|
|
269
|
+
);
|
|
201
270
|
return context.throw(500);
|
|
202
271
|
}
|
|
203
272
|
const { lastSavedJob, nodesMap } = processor;
|
|
204
273
|
const lastNode = nodesMap.get(lastSavedJob == null ? void 0 : lastSavedJob.nodeId);
|
|
205
274
|
if (processor.execution.status === import_plugin_workflow.EXECUTION_STATUS.RESOLVED) {
|
|
206
275
|
if ((lastNode == null ? void 0 : lastNode.type) === "end") {
|
|
276
|
+
context.logger.debug("[Workflow custom-action]: sync workflow ended request chain on end node", {
|
|
277
|
+
...syncLogMeta,
|
|
278
|
+
...(0, import_plugin_workflow.getWorkflowExecutionLogMeta)(workflow, processor)
|
|
279
|
+
});
|
|
207
280
|
return;
|
|
208
281
|
}
|
|
282
|
+
context.logger.debug("[Workflow custom-action]: sync workflow finished successfully", {
|
|
283
|
+
...syncLogMeta,
|
|
284
|
+
...(0, import_plugin_workflow.getWorkflowExecutionLogMeta)(workflow, processor)
|
|
285
|
+
});
|
|
209
286
|
continue;
|
|
210
287
|
}
|
|
211
288
|
if (processor.execution.status < import_plugin_workflow.EXECUTION_STATUS.STARTED) {
|
|
212
289
|
if ((lastNode == null ? void 0 : lastNode.type) !== "end") {
|
|
290
|
+
context.logger.error("[Workflow custom-action]: sync workflow failed", {
|
|
291
|
+
...syncLogMeta,
|
|
292
|
+
...(0, import_plugin_workflow.getWorkflowExecutionLogMeta)(workflow, processor)
|
|
293
|
+
});
|
|
213
294
|
return context.throw(500, "Workflow on your action failed, please contact the administrator");
|
|
214
295
|
}
|
|
296
|
+
context.logger.warn("[Workflow custom-action]: sync workflow intercepted request on end node", {
|
|
297
|
+
...syncLogMeta,
|
|
298
|
+
...(0, import_plugin_workflow.getWorkflowExecutionLogMeta)(workflow, processor)
|
|
299
|
+
});
|
|
215
300
|
const err = new CustomActionInterceptionError("Request is intercepted by workflow");
|
|
216
301
|
err.status = 400;
|
|
217
302
|
err.messages = context.state.messages;
|
|
218
303
|
return context.throw(err.status, err);
|
|
219
304
|
}
|
|
305
|
+
context.logger.error("[Workflow custom-action]: sync workflow is still pending after trigger", {
|
|
306
|
+
...syncLogMeta,
|
|
307
|
+
...(0, import_plugin_workflow.getWorkflowExecutionLogMeta)(workflow, processor)
|
|
308
|
+
});
|
|
220
309
|
return context.throw(500, "Workflow on your action hangs, please contact the administrator");
|
|
221
310
|
}
|
|
222
311
|
await next();
|
|
223
|
-
|
|
224
|
-
|
|
312
|
+
this.scheduleAsyncWorkflowTriggers(context, asyncGroup);
|
|
313
|
+
}
|
|
314
|
+
scheduleAsyncWorkflowTriggers(context, events) {
|
|
315
|
+
if (!events.length) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const triggerAsyncWorkflows = () => {
|
|
319
|
+
const triggerAsyncWorkflow = async (workflow, values) => {
|
|
320
|
+
await this.workflow.trigger(workflow, values);
|
|
321
|
+
};
|
|
322
|
+
setTimeout(() => {
|
|
323
|
+
for (const [workflow, values] of events) {
|
|
324
|
+
triggerAsyncWorkflow(workflow, values).catch((error) => {
|
|
325
|
+
context.logger.error("[Workflow custom-action]: async workflow trigger failed", {
|
|
326
|
+
error,
|
|
327
|
+
workflowId: workflow.id,
|
|
328
|
+
workflowKey: workflow.key,
|
|
329
|
+
workflowTitle: workflow.title
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}, ASYNC_WORKFLOW_TRIGGER_DELAY_MS);
|
|
334
|
+
};
|
|
335
|
+
if (context.res.writableEnded) {
|
|
336
|
+
triggerAsyncWorkflows();
|
|
337
|
+
return;
|
|
225
338
|
}
|
|
339
|
+
context.res.once("finish", triggerAsyncWorkflows);
|
|
226
340
|
}
|
|
227
341
|
validateContext(values, workflow) {
|
|
228
342
|
var _a;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
import { Migration } from '@nocobase/server';
|
|
10
|
+
/**
|
|
11
|
+
* Grant `trigger` / `triggerNew` action permissions for every role, mirroring the role's
|
|
12
|
+
* existing `view` / `create` permissions.
|
|
13
|
+
*
|
|
14
|
+
* Background: The `trigger` action on collection resources was previously allowed for all
|
|
15
|
+
* logged-in users via `acl.allow('*', ['trigger'], 'loggedIn')`. After that global bypass
|
|
16
|
+
* was removed, `trigger` goes through normal ACL checks. Custom-action trigger is now
|
|
17
|
+
* modelled as two ACL actions:
|
|
18
|
+
* - `trigger` — invoked on an existing record; follows `view` (with data scope).
|
|
19
|
+
* - `triggerNew` — invoked on a form that hasn't been persisted yet; follows `create`.
|
|
20
|
+
*
|
|
21
|
+
* 1. Per-data-source strategy (`dataSourcesRoles.strategy.actions`, an array):
|
|
22
|
+
* - `trigger` mirrors `view` (including scope suffix like `:own`). No `view` → no trigger.
|
|
23
|
+
* - `triggerNew` is added (unrestricted) if the strategy contains any `create*` entry.
|
|
24
|
+
* No `create` → no triggerNew. Scope suffixes on `create` are ignored because new-data
|
|
25
|
+
* actions have no data scope.
|
|
26
|
+
*
|
|
27
|
+
* 2. Specific resource configs (`dataSourcesRolesResources` with `usingActionsConfig: true`):
|
|
28
|
+
* - If the resource has a `view` action, create a matching `trigger` action that reuses
|
|
29
|
+
* the same `scopeId`. Skip if `trigger` already exists.
|
|
30
|
+
* - If the resource has a `create` action, create a matching `triggerNew` action
|
|
31
|
+
* (scopeId: null — new-data actions carry no scope). Skip if `triggerNew` already
|
|
32
|
+
* exists.
|
|
33
|
+
*
|
|
34
|
+
* This migration runs only during upgrades (appVersion < 2.0.40), not on fresh installs.
|
|
35
|
+
*/
|
|
36
|
+
export default class extends Migration {
|
|
37
|
+
appVersion: string;
|
|
38
|
+
on: string;
|
|
39
|
+
up(): Promise<void>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var __defProp = Object.defineProperty;
|
|
11
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
var grant_trigger_action_to_roles_exports = {};
|
|
28
|
+
__export(grant_trigger_action_to_roles_exports, {
|
|
29
|
+
default: () => grant_trigger_action_to_roles_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(grant_trigger_action_to_roles_exports);
|
|
32
|
+
var import_server = require("@nocobase/server");
|
|
33
|
+
class grant_trigger_action_to_roles_default extends import_server.Migration {
|
|
34
|
+
appVersion = "<2.1.0-alpha.21";
|
|
35
|
+
on = "afterSync";
|
|
36
|
+
async up() {
|
|
37
|
+
const { db } = this.context;
|
|
38
|
+
await db.sequelize.transaction(async (transaction) => {
|
|
39
|
+
const dsRoles = await db.getRepository("dataSourcesRoles").find({ transaction });
|
|
40
|
+
for (const dsRole of dsRoles) {
|
|
41
|
+
const strategy = dsRole.get("strategy") || {};
|
|
42
|
+
const actions = strategy.actions;
|
|
43
|
+
if (!Array.isArray(actions)) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
let nextActions = null;
|
|
47
|
+
const ensureNextActions = () => {
|
|
48
|
+
if (!nextActions) nextActions = [...actions];
|
|
49
|
+
return nextActions;
|
|
50
|
+
};
|
|
51
|
+
const viewEntry = actions.find((a) => a === "view" || a.startsWith("view:"));
|
|
52
|
+
if (viewEntry) {
|
|
53
|
+
const targetTrigger = viewEntry === "view" ? "trigger" : `trigger:${viewEntry.slice("view:".length)}`;
|
|
54
|
+
const existingIndex = actions.findIndex((a) => a === "trigger" || a.startsWith("trigger:"));
|
|
55
|
+
if (existingIndex === -1) {
|
|
56
|
+
ensureNextActions().push(targetTrigger);
|
|
57
|
+
} else if (actions[existingIndex] !== targetTrigger) {
|
|
58
|
+
ensureNextActions()[existingIndex] = targetTrigger;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const hasCreate = actions.some((a) => a === "create" || a.startsWith("create:"));
|
|
62
|
+
if (hasCreate) {
|
|
63
|
+
const existingIndex = actions.findIndex((a) => a === "triggerNew" || a.startsWith("triggerNew:"));
|
|
64
|
+
if (existingIndex === -1) {
|
|
65
|
+
ensureNextActions().push("triggerNew");
|
|
66
|
+
} else if (actions[existingIndex] !== "triggerNew") {
|
|
67
|
+
ensureNextActions()[existingIndex] = "triggerNew";
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (nextActions) {
|
|
71
|
+
await db.getRepository("dataSourcesRoles").update({
|
|
72
|
+
filter: {
|
|
73
|
+
roleName: dsRole.get("roleName"),
|
|
74
|
+
dataSourceKey: dsRole.get("dataSourceKey")
|
|
75
|
+
},
|
|
76
|
+
values: {
|
|
77
|
+
strategy: { ...strategy, actions: nextActions }
|
|
78
|
+
},
|
|
79
|
+
hooks: false,
|
|
80
|
+
transaction
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const resources = await db.getRepository("dataSourcesRolesResources").find({
|
|
85
|
+
filter: { usingActionsConfig: true },
|
|
86
|
+
appends: ["actions"],
|
|
87
|
+
transaction
|
|
88
|
+
});
|
|
89
|
+
for (const resource of resources) {
|
|
90
|
+
const actions = resource.get("actions") || [];
|
|
91
|
+
const viewAction = actions.find((a) => a.get("name") === "view");
|
|
92
|
+
const hasTrigger = actions.some((a) => a.get("name") === "trigger");
|
|
93
|
+
if (viewAction && !hasTrigger) {
|
|
94
|
+
await db.getRepository("dataSourcesRolesResourcesActions").create({
|
|
95
|
+
values: {
|
|
96
|
+
rolesResourceId: resource.get("id"),
|
|
97
|
+
name: "trigger",
|
|
98
|
+
scopeId: viewAction.get("scopeId") ?? null,
|
|
99
|
+
fields: []
|
|
100
|
+
},
|
|
101
|
+
transaction
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
const createAction = actions.find((a) => a.get("name") === "create");
|
|
105
|
+
const hasTriggerNew = actions.some((a) => a.get("name") === "triggerNew");
|
|
106
|
+
if (createAction && !hasTriggerNew) {
|
|
107
|
+
await db.getRepository("dataSourcesRolesResourcesActions").create({
|
|
108
|
+
values: {
|
|
109
|
+
rolesResourceId: resource.get("id"),
|
|
110
|
+
name: "triggerNew",
|
|
111
|
+
scopeId: null,
|
|
112
|
+
fields: []
|
|
113
|
+
},
|
|
114
|
+
transaction
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/plugin-workflow-custom-action-trigger",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0-alpha.1",
|
|
4
4
|
"displayName": "Workflow: Custom action event",
|
|
5
5
|
"displayName.zh-CN": "工作流:自定义操作事件",
|
|
6
6
|
"description": "Triggers after click a custom action button.",
|
|
7
7
|
"description.zh-CN": "在点击绑定了自定义事件的按钮后触发。适用于对数据行的自定义操作编排。",
|
|
8
8
|
"main": "./dist/server/index.js",
|
|
9
|
+
"devDependencies": {
|
|
10
|
+
"joi": "^17.13.3"
|
|
11
|
+
},
|
|
9
12
|
"homepage": "https://docs.nocobase.com/handbook/workflow-custom-action-trigger",
|
|
10
13
|
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/workflow-custom-action-trigger",
|
|
11
14
|
"peerDependencies": {
|
|
12
15
|
"@nocobase/client": "2.x",
|
|
16
|
+
"@nocobase/client-v2": "2.x",
|
|
13
17
|
"@nocobase/plugin-error-handler": "2.x",
|
|
14
18
|
"@nocobase/plugin-users": "2.x",
|
|
15
19
|
"@nocobase/plugin-workflow": "2.x",
|
|
@@ -27,6 +31,6 @@
|
|
|
27
31
|
],
|
|
28
32
|
"editionLevel": 0
|
|
29
33
|
},
|
|
30
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "303663aba6c6eefa27e6a6435b4c0352074ec40f",
|
|
31
35
|
"license": "Apache-2.0"
|
|
32
36
|
}
|