@nocobase/plugin-workflow 0.7.3-alpha.1 → 0.7.4-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.
@@ -9,10 +9,12 @@ import ExecutionModel from './models/Execution';
9
9
  export default class WorkflowPlugin extends Plugin {
10
10
  instructions: Registry<Instruction>;
11
11
  triggers: Registry<Trigger>;
12
+ calculators: Registry<Function>;
13
+ extensions: typeof import("./extensions/assignees").default[];
12
14
  onBeforeSave: (instance: WorkflowModel, options: any) => Promise<void>;
13
15
  getName(): string;
14
16
  load(): Promise<void>;
15
17
  toggle(workflow: WorkflowModel, enable?: boolean): void;
16
- trigger(workflow: any, context: Object, options?: Transactionable): Promise<any>;
18
+ trigger(workflow: WorkflowModel, context: Object, options?: Transactionable): Promise<ExecutionModel | null>;
17
19
  createProcessor(execution: ExecutionModel, options?: {}): Processor;
18
20
  }
@@ -53,6 +53,10 @@ var _instructions = _interopRequireDefault(require("./instructions"));
53
53
 
54
54
  var _Processor = _interopRequireDefault(require("./Processor"));
55
55
 
56
+ var _calculators = _interopRequireDefault(require("./calculators"));
57
+
58
+ var _extensions = _interopRequireDefault(require("./extensions"));
59
+
56
60
  var _constants = require("./constants");
57
61
 
58
62
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -75,6 +79,8 @@ class WorkflowPlugin extends _server().Plugin {
75
79
  _this = this;
76
80
  this.instructions = new (_utils().Registry)();
77
81
  this.triggers = new (_utils().Registry)();
82
+ this.calculators = _calculators.default;
83
+ this.extensions = _extensions.default;
78
84
 
79
85
  this.onBeforeSave = /*#__PURE__*/function () {
80
86
  var _ref = _asyncToGenerator(function* (instance, options) {
@@ -148,11 +154,16 @@ class WorkflowPlugin extends _server().Plugin {
148
154
  (0, _instructions.default)(_this2, options.instructions);
149
155
  db.on('workflows.beforeSave', _this2.onBeforeSave);
150
156
  db.on('workflows.afterSave', model => _this2.toggle(model));
151
- db.on('workflows.afterDestroy', model => _this2.toggle(model, false)); // [Life Cycle]:
157
+ db.on('workflows.afterDestroy', model => _this2.toggle(model, false));
158
+
159
+ _this2.app.on('afterLoadAll', /*#__PURE__*/_asyncToGenerator(function* () {
160
+ return _this2.extensions.reduce((promise, extend) => promise.then(() => extend(_this2)), Promise.resolve());
161
+ })); // [Life Cycle]:
152
162
  // * load all workflows in db
153
163
  // * add all hooks for enabled workflows
154
164
  // * add hooks for create/update[enabled]/delete workflow to add/remove specific hooks
155
165
 
166
+
156
167
  _this2.app.on('beforeStart', /*#__PURE__*/_asyncToGenerator(function* () {
157
168
  const collection = db.getCollection('workflows');
158
169
  const workflows = yield collection.repository.find({
@@ -203,7 +214,7 @@ class WorkflowPlugin extends _server().Plugin {
203
214
  return _asyncToGenerator(function* () {
204
215
  // `null` means not to trigger
205
216
  if (context === null) {
206
- return;
217
+ return null;
207
218
  }
208
219
 
209
220
  let transaction = null;
@@ -220,7 +231,7 @@ class WorkflowPlugin extends _server().Plugin {
220
231
 
221
232
  if (existed) {
222
233
  console.warn(`workflow ${workflow.id} has already been triggered in same execution (${transaction.id}), and newly triggering will be skipped.`);
223
- return;
234
+ return null;
224
235
  }
225
236
  }
226
237
 
@@ -237,8 +237,7 @@ class Processor {
237
237
  }
238
238
  }
239
239
 
240
- let savedJob; // TODO(optimize): many checking of resuming or new could be improved
241
- // could be implemented separately in exec() / resume()
240
+ let savedJob;
242
241
 
243
242
  if (job instanceof _database().Model) {
244
243
  savedJob = yield job.save({
@@ -452,5 +451,5 @@ Processor.StatusMap = {
452
451
  [_constants.JOB_STATUS.PENDING]: _constants.EXECUTION_STATUS.STARTED,
453
452
  [_constants.JOB_STATUS.RESOLVED]: _constants.EXECUTION_STATUS.RESOLVED,
454
453
  [_constants.JOB_STATUS.REJECTED]: _constants.EXECUTION_STATUS.REJECTED,
455
- [_constants.JOB_STATUS.CANCELLED]: _constants.EXECUTION_STATUS.CANCELLED
454
+ [_constants.JOB_STATUS.CANCELED]: _constants.EXECUTION_STATUS.CANCELED
456
455
  };
@@ -9,6 +9,8 @@ var workflows = _interopRequireWildcard(require("./workflows"));
9
9
 
10
10
  var nodes = _interopRequireWildcard(require("./nodes"));
11
11
 
12
+ var jobs = _interopRequireWildcard(require("./jobs"));
13
+
12
14
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
13
15
 
14
16
  function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
@@ -28,10 +30,10 @@ function make(name, mod) {
28
30
  function _default({
29
31
  app
30
32
  }) {
31
- app.actions(_objectSpread(_objectSpread(_objectSpread({}, make('workflows', workflows)), make('workflows.nodes', {
33
+ app.actions(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, make('workflows', workflows)), make('workflows.nodes', {
32
34
  create: nodes.create,
33
35
  destroy: nodes.destroy
34
36
  })), make('flow_nodes', {
35
37
  update: nodes.update
36
- })));
38
+ })), make('jobs', jobs)));
37
39
  }
@@ -0,0 +1,2 @@
1
+ import { Context } from '@nocobase/actions';
2
+ export declare function submit(context: Context, next: any): Promise<never>;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.submit = submit;
7
+
8
+ var _constants = require("../constants");
9
+
10
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
11
+
12
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
13
+
14
+ function submit(_x, _x2) {
15
+ return _submit.apply(this, arguments);
16
+ }
17
+
18
+ function _submit() {
19
+ _submit = _asyncToGenerator(function* (context, next) {
20
+ const values = context.action.params.values;
21
+ const instance = context.body; // NOTE: validate status
22
+
23
+ if (instance.status !== _constants.JOB_STATUS.PENDING) {
24
+ return context.throw(400);
25
+ } // NOTE: validate assignee
26
+
27
+
28
+ instance.set({
29
+ status: values.status,
30
+ result: values.result
31
+ });
32
+ context.status = 202;
33
+ yield next();
34
+ const plugin = context.app.pm.get('@nocobase/plugin-workflow');
35
+ const processor = plugin.createProcessor(instance.execution); // NOTE: resume the process and no `await` for quick returning
36
+
37
+ processor.resume(instance);
38
+ });
39
+ return _submit.apply(this, arguments);
40
+ }
@@ -75,7 +75,7 @@ function migrateConfig(config, oldToNew) {
75
75
  return value.map(item => migrate(item));
76
76
 
77
77
  case 'string':
78
- return value.replace(/(\{\{\$jobsMapByNodeId\.)(\d+)/, (_, prefix, id) => `${prefix}${oldToNew.get(Number.parseInt(id, 10)).id}`);
78
+ return value.replace(/(\{\{\$jobsMapByNodeId\.)([\w-]+)/, (_, prefix, id) => `${prefix}${oldToNew.get(Number.parseInt(id, 10)).id}`);
79
79
 
80
80
  default:
81
81
  return value;
@@ -116,23 +116,23 @@ calculators.register('<', lt);
116
116
  calculators.register('<=', lte);
117
117
 
118
118
  function add(...args) {
119
- return args.reduce((sum, a) => sum + a, 0);
119
+ return args.reduce((sum, a) => sum + (0, _lodash().toNumber)(a), 0);
120
120
  }
121
121
 
122
122
  function minus(a, b) {
123
- return a - b;
123
+ return (0, _lodash().toNumber)(a) - (0, _lodash().toNumber)(b);
124
124
  }
125
125
 
126
126
  function multiple(...args) {
127
- return args.reduce((result, a) => result * a, 1);
127
+ return args.reduce((result, a) => result * (0, _lodash().toNumber)(a), 1);
128
128
  }
129
129
 
130
130
  function divide(a, b) {
131
- return a / b;
131
+ return (0, _lodash().toNumber)(a) / (0, _lodash().toNumber)(b);
132
132
  }
133
133
 
134
134
  function mod(a, b) {
135
- return a % b;
135
+ return (0, _lodash().toNumber)(a) % (0, _lodash().toNumber)(b);
136
136
  }
137
137
 
138
138
  calculators.register('add', add);
@@ -2,13 +2,13 @@ export declare const EXECUTION_STATUS: {
2
2
  STARTED: number;
3
3
  RESOLVED: number;
4
4
  REJECTED: number;
5
- CANCELLED: number;
5
+ CANCELED: number;
6
6
  };
7
7
  export declare const JOB_STATUS: {
8
8
  PENDING: number;
9
9
  RESOLVED: number;
10
10
  REJECTED: number;
11
- CANCELLED: number;
11
+ CANCELED: number;
12
12
  };
13
13
  export declare const BRANCH_INDEX: {
14
14
  DEFAULT: any;
@@ -8,14 +8,14 @@ const EXECUTION_STATUS = {
8
8
  STARTED: 0,
9
9
  RESOLVED: 1,
10
10
  REJECTED: -1,
11
- CANCELLED: -2
11
+ CANCELED: -2
12
12
  };
13
13
  exports.EXECUTION_STATUS = EXECUTION_STATUS;
14
14
  const JOB_STATUS = {
15
15
  PENDING: 0,
16
16
  RESOLVED: 1,
17
17
  REJECTED: -1,
18
- CANCELLED: -2
18
+ CANCELED: -2
19
19
  };
20
20
  exports.JOB_STATUS = JOB_STATUS;
21
21
  const BRANCH_INDEX = {
@@ -0,0 +1,2 @@
1
+ import { Context } from '@nocobase/actions';
2
+ export declare function submit(context: Context, next: any): Promise<never>;
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.submit = submit;
7
+
8
+ function _actions() {
9
+ const data = require("@nocobase/actions");
10
+
11
+ _actions = function _actions() {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ var _constants = require("../../constants");
19
+
20
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
21
+
22
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
23
+
24
+ function submit(_x, _x2) {
25
+ return _submit.apply(this, arguments);
26
+ }
27
+
28
+ function _submit() {
29
+ _submit = _asyncToGenerator(function* (context, next) {
30
+ const repository = _actions().utils.getRepositoryFromParams(context);
31
+
32
+ const _context$action$param = context.action.params,
33
+ filterByTk = _context$action$param.filterByTk,
34
+ values = _context$action$param.values;
35
+ const currentUser = context.state.currentUser;
36
+
37
+ if (!currentUser) {
38
+ return context.throw(401);
39
+ }
40
+
41
+ const instance = yield repository.findOne({
42
+ filterByTk,
43
+ // filter: {
44
+ // userId: currentUser?.id
45
+ // },
46
+ appends: ['job', 'node', 'execution'],
47
+ context
48
+ });
49
+ const _instance$node$config = instance.node.config,
50
+ actions = _instance$node$config.actions,
51
+ assignees = _instance$node$config.assignees; // NOTE: validate status
52
+
53
+ if (instance.status !== _constants.JOB_STATUS.PENDING || instance.job.status !== _constants.JOB_STATUS.PENDING || instance.execution.status !== _constants.EXECUTION_STATUS.STARTED || actions && !actions[values.status]) {
54
+ context.throw(400);
55
+ }
56
+
57
+ if (!assignees.includes(currentUser.id) || instance.userId !== currentUser.id) {
58
+ return context.throw(404);
59
+ } // NOTE: validate assignee
60
+
61
+
62
+ yield instance.update({
63
+ status: values.status,
64
+ result: values.result
65
+ });
66
+ context.body = instance;
67
+ context.status = 202;
68
+ yield next();
69
+ instance.job.latestUserJob = instance;
70
+ const plugin = context.app.pm.get('@nocobase/plugin-workflow');
71
+ const processor = plugin.createProcessor(instance.execution); // NOTE: resume the process and no `await` for quick returning
72
+
73
+ processor.resume(instance.job);
74
+ });
75
+ return _submit.apply(this, arguments);
76
+ }
@@ -0,0 +1,6 @@
1
+ declare const _default: {
2
+ collectionOptions: import("@nocobase/database").CollectionOptions;
3
+ mergeOptions: import("@nocobase/database").MergeOptions;
4
+ extend: boolean;
5
+ };
6
+ export default _default;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ function _database() {
9
+ const data = require("@nocobase/database");
10
+
11
+ _database = function _database() {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ var _default = (0, _database().extend)({
19
+ name: 'jobs',
20
+ fields: [{
21
+ type: 'belongsToMany',
22
+ name: 'users',
23
+ through: 'users_jobs'
24
+ }, {
25
+ type: 'hasMany',
26
+ name: 'usersJobs',
27
+ target: 'users_jobs',
28
+ foreignKey: 'jobId'
29
+ }]
30
+ });
31
+
32
+ exports.default = _default;
@@ -0,0 +1,6 @@
1
+ declare const _default: {
2
+ collectionOptions: import("@nocobase/database").CollectionOptions;
3
+ mergeOptions: import("@nocobase/database").MergeOptions;
4
+ extend: boolean;
5
+ };
6
+ export default _default;
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ function _database() {
9
+ const data = require("@nocobase/database");
10
+
11
+ _database = function _database() {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ var _default = (0, _database().extend)({
19
+ name: 'users',
20
+ fields: [{
21
+ type: 'belongsToMany',
22
+ name: 'jobs',
23
+ through: 'users_jobs'
24
+ }, {
25
+ type: 'hasMany',
26
+ name: 'usersJobs',
27
+ target: 'users_jobs'
28
+ }]
29
+ });
30
+
31
+ exports.default = _default;
@@ -0,0 +1,3 @@
1
+ import { CollectionOptions } from '@nocobase/database';
2
+ declare const _default: CollectionOptions;
3
+ export default _default;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _default = {
8
+ name: 'users_jobs',
9
+ fields: [{
10
+ type: 'integer',
11
+ name: 'id',
12
+ primaryKey: true,
13
+ autoIncrement: true
14
+ }, {
15
+ type: 'belongsTo',
16
+ name: 'job'
17
+ }, {
18
+ type: 'belongsTo',
19
+ name: 'user'
20
+ }, {
21
+ type: 'belongsTo',
22
+ name: 'execution'
23
+ }, {
24
+ type: 'belongsTo',
25
+ name: 'node',
26
+ target: 'flow_nodes'
27
+ }, {
28
+ type: 'belongsTo',
29
+ name: 'workflow'
30
+ }, {
31
+ type: 'integer',
32
+ name: 'status'
33
+ }, {
34
+ type: 'jsonb',
35
+ name: 'result'
36
+ }]
37
+ };
38
+ exports.default = _default;
@@ -0,0 +1,2 @@
1
+ import Plugin from '../../Plugin';
2
+ export default function (plugin: Plugin): Promise<void>;
@@ -0,0 +1,273 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = _default;
7
+
8
+ function _path() {
9
+ const data = _interopRequireDefault(require("path"));
10
+
11
+ _path = function _path() {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ function _utils() {
19
+ const data = require("@nocobase/utils");
20
+
21
+ _utils = function _utils() {
22
+ return data;
23
+ };
24
+
25
+ return data;
26
+ }
27
+
28
+ var _actions = require("./actions");
29
+
30
+ var _constants = require("../../constants");
31
+
32
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
33
+
34
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
35
+
36
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
37
+
38
+ // NOTE: for single record mode (mode: 0/null)
39
+ function middleware(_x, _x2) {
40
+ return _middleware.apply(this, arguments);
41
+ }
42
+
43
+ function _middleware() {
44
+ _middleware = _asyncToGenerator(function* (context, next) {
45
+ const job = context.body,
46
+ state = context.state,
47
+ action = context.action;
48
+ const _job$node$config = job.node.config,
49
+ assignees = _job$node$config.assignees,
50
+ mode = _job$node$config.mode; // NOTE: skip to no user implementation
51
+
52
+ if (!assignees) {
53
+ return next();
54
+ }
55
+
56
+ if (!state.currentUser) {
57
+ return context.throw(401);
58
+ }
59
+
60
+ if (!assignees.includes(state.currentUser.id)) {
61
+ return context.throw(404);
62
+ } // NOTE: multiple record mode could not use jobs:submit action
63
+ // should use users_jobs:submit/:id instead
64
+
65
+
66
+ if (mode) {
67
+ return context.throw(400);
68
+ }
69
+
70
+ yield next();
71
+ const data = {
72
+ userId: context.state.currentUser.id,
73
+ jobId: job.id,
74
+ nodeId: job.nodeId,
75
+ executionId: job.executionId,
76
+ workflowId: job.execution.workflowId,
77
+ status: job.status,
78
+ result: job.result
79
+ }; // NOTE: update users job after main job is done
80
+
81
+ const UserJobModel = context.db.getModel('users_jobs');
82
+ let userJob = yield UserJobModel.findOne({
83
+ where: {
84
+ userId: context.state.currentUser.id,
85
+ jobId: job.id
86
+ }
87
+ });
88
+
89
+ if (userJob) {
90
+ yield userJob.update(data);
91
+ } else {
92
+ userJob = yield UserJobModel.create(data);
93
+ }
94
+ });
95
+ return _middleware.apply(this, arguments);
96
+ }
97
+
98
+ function run(_x3, _x4, _x5) {
99
+ return _run.apply(this, arguments);
100
+ }
101
+
102
+ function _run() {
103
+ _run = _asyncToGenerator(function* (node, prevJob, processor) {
104
+ var _prevJob$id;
105
+
106
+ const _node$config = node.config,
107
+ assignees = _node$config.assignees,
108
+ mode = _node$config.mode;
109
+
110
+ if (!assignees) {
111
+ const plugin = processor.options.plugin;
112
+ const origin = plugin.instructions.get('prompt');
113
+ return origin.constructor.prototype.run.call(this, node, prevJob, processor);
114
+ }
115
+
116
+ const job = yield processor.saveJob({
117
+ status: _constants.JOB_STATUS.PENDING,
118
+ result: mode ? [] : null,
119
+ nodeId: node.id,
120
+ upstreamId: (_prevJob$id = prevJob === null || prevJob === void 0 ? void 0 : prevJob.id) !== null && _prevJob$id !== void 0 ? _prevJob$id : null
121
+ }); // NOTE: batch create users jobs
122
+
123
+ const UserJobModel = processor.options.plugin.db.getModel('users_jobs');
124
+ yield UserJobModel.bulkCreate(assignees.map(userId => ({
125
+ userId,
126
+ jobId: job.id,
127
+ nodeId: node.id,
128
+ executionId: job.executionId,
129
+ workflowId: node.workflowId,
130
+ status: _constants.JOB_STATUS.PENDING
131
+ })), {
132
+ transaction: processor.transaction
133
+ });
134
+ return job;
135
+ });
136
+ return _run.apply(this, arguments);
137
+ }
138
+
139
+ const PROMPT_ASSIGNED_MODE = {
140
+ SINGLE: Symbol('single'),
141
+ ALL: Symbol('all'),
142
+ ANY: Symbol('any'),
143
+ ALL_PERCENTAGE: Symbol('all percentage'),
144
+ ANY_PERCENTAGE: Symbol('any percentage')
145
+ };
146
+ const Modes = {
147
+ [PROMPT_ASSIGNED_MODE.SINGLE]: {
148
+ getStatus(distribution, assignees) {
149
+ const done = distribution.find(item => item.status !== _constants.JOB_STATUS.PENDING && item.count > 0);
150
+ return done ? done.status : null;
151
+ }
152
+
153
+ },
154
+ [PROMPT_ASSIGNED_MODE.ALL]: {
155
+ getStatus(distribution, assignees) {
156
+ const resolved = distribution.find(item => item.status === _constants.JOB_STATUS.RESOLVED);
157
+
158
+ if (resolved && resolved.count === assignees.length) {
159
+ return _constants.JOB_STATUS.RESOLVED;
160
+ } // NOTE: `rejected` or `canceled`
161
+
162
+
163
+ const failed = distribution.find(item => item.status < _constants.JOB_STATUS.PENDING);
164
+
165
+ if (failed && failed.count) {
166
+ return failed.status;
167
+ }
168
+
169
+ return null;
170
+ }
171
+
172
+ },
173
+ [PROMPT_ASSIGNED_MODE.ANY]: {
174
+ getStatus(distribution, assignees) {
175
+ const resolved = distribution.find(item => item.status === _constants.JOB_STATUS.RESOLVED);
176
+
177
+ if (resolved && resolved.count) {
178
+ return _constants.JOB_STATUS.RESOLVED;
179
+ }
180
+
181
+ const failedCount = distribution.reduce((count, item) => item.status < _constants.JOB_STATUS.PENDING ? count + item.count : count, 0); // NOTE: all failures are considered as rejected for now
182
+
183
+ if (failedCount === assignees.length) {
184
+ return _constants.JOB_STATUS.REJECTED;
185
+ }
186
+
187
+ return null;
188
+ }
189
+
190
+ }
191
+ };
192
+
193
+ function getMode(mode) {
194
+ switch (true) {
195
+ case mode === 1:
196
+ return Modes[PROMPT_ASSIGNED_MODE.ALL];
197
+
198
+ case mode === -1:
199
+ return Modes[PROMPT_ASSIGNED_MODE.ANY];
200
+
201
+ case mode > 0:
202
+ return Modes[PROMPT_ASSIGNED_MODE.ALL_PERCENTAGE];
203
+
204
+ case mode < 0:
205
+ return Modes[PROMPT_ASSIGNED_MODE.ANY_PERCENTAGE];
206
+
207
+ default:
208
+ return Modes[PROMPT_ASSIGNED_MODE.SINGLE];
209
+ }
210
+ }
211
+
212
+ function resume(_x6, _x7, _x8) {
213
+ return _resume.apply(this, arguments);
214
+ }
215
+
216
+ function _resume() {
217
+ _resume = _asyncToGenerator(function* (node, job, processor) {
218
+ var _job$latestUserJob$re, _job$latestUserJob, _getMode$getStatus;
219
+
220
+ // NOTE: check all users jobs related if all done then continue as parallel
221
+ const _node$config2 = node.config,
222
+ assignees = _node$config2.assignees,
223
+ mode = _node$config2.mode;
224
+
225
+ if (!assignees) {
226
+ const plugin = processor.options.plugin;
227
+ const origin = plugin.instructions.get('prompt');
228
+ return origin.constructor.prototype.resume.call(this, node, job, processor);
229
+ }
230
+
231
+ const UserJobModel = processor.options.plugin.db.getModel('users_jobs');
232
+ const distribution = yield UserJobModel.count({
233
+ where: {
234
+ jobId: job.id
235
+ },
236
+ group: ['status']
237
+ });
238
+ const submitted = distribution.reduce((count, item) => item.status !== _constants.JOB_STATUS.PENDING ? count + item.count : count, 0);
239
+ const result = mode ? (submitted || 0) / assignees.length : (_job$latestUserJob$re = (_job$latestUserJob = job.latestUserJob) === null || _job$latestUserJob === void 0 ? void 0 : _job$latestUserJob.result) !== null && _job$latestUserJob$re !== void 0 ? _job$latestUserJob$re : job.result;
240
+ job.set({
241
+ status: (_getMode$getStatus = getMode(mode).getStatus(distribution, assignees)) !== null && _getMode$getStatus !== void 0 ? _getMode$getStatus : _constants.JOB_STATUS.PENDING,
242
+ result
243
+ });
244
+ return job;
245
+ });
246
+ return _resume.apply(this, arguments);
247
+ }
248
+
249
+ function _default(_x9) {
250
+ return _ref.apply(this, arguments);
251
+ }
252
+
253
+ function _ref() {
254
+ _ref = _asyncToGenerator(function* (plugin) {
255
+ const instruction = plugin.instructions.get('prompt');
256
+ instruction.extend({
257
+ run,
258
+ resume
259
+ });
260
+ instruction.use(middleware); // TODO(bug): through table should be load first because primary
261
+ // await plugin.db.import({
262
+ // directory: path.join(__dirname, './collections')
263
+ // });
264
+
265
+ plugin.db.collection((0, _utils().requireModule)(_path().default.join(__dirname, './collections/users_jobs')));
266
+ plugin.db.collection((0, _utils().requireModule)(_path().default.join(__dirname, './collections/users')));
267
+ plugin.db.collection((0, _utils().requireModule)(_path().default.join(__dirname, './collections/jobs')));
268
+ plugin.app.actions({
269
+ 'users_jobs:submit': _actions.submit
270
+ });
271
+ });
272
+ return _ref.apply(this, arguments);
273
+ }
@@ -0,0 +1,3 @@
1
+ import assignees from './assignees';
2
+ declare const _default: (typeof assignees)[];
3
+ export default _default;
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _assignees = _interopRequireDefault(require("./assignees"));
9
+
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+
12
+ var _default = [_assignees.default];
13
+ exports.default = _default;
@@ -1,7 +1,8 @@
1
1
  /// <reference types="node" />
2
2
  import Plugin from '..';
3
3
  import Processor from '../Processor';
4
- export default class {
4
+ import { Instruction } from '.';
5
+ export default class implements Instruction {
5
6
  protected plugin: Plugin;
6
7
  timers: Map<number, NodeJS.Timeout>;
7
8
  constructor(plugin: Plugin);
@@ -25,22 +25,6 @@ function _utils() {
25
25
  return data;
26
26
  }
27
27
 
28
- var _prompt = _interopRequireDefault(require("./prompt"));
29
-
30
- var _calculation = _interopRequireDefault(require("./calculation"));
31
-
32
- var _condition = _interopRequireDefault(require("./condition"));
33
-
34
- var _parallel = _interopRequireDefault(require("./parallel"));
35
-
36
- var _query = _interopRequireDefault(require("./query"));
37
-
38
- var _create = _interopRequireDefault(require("./create"));
39
-
40
- var _update = _interopRequireDefault(require("./update"));
41
-
42
- var _destroy = _interopRequireDefault(require("./destroy"));
43
-
44
28
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
45
29
 
46
30
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
@@ -55,23 +39,24 @@ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Sy
55
39
 
56
40
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
57
41
 
42
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
43
+
44
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
45
+
46
+ function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
47
+
58
48
  function _default(plugin, more = {}) {
59
49
  const instructions = plugin.instructions;
60
- instructions.register('prompt', _prompt.default);
61
- instructions.register('calculation', _calculation.default);
62
- instructions.register('condition', _condition.default);
63
- instructions.register('parallel', _parallel.default);
64
- instructions.register('query', _query.default);
65
- instructions.register('create', _create.default);
66
- instructions.register('update', _update.default);
67
- instructions.register('destroy', _destroy.default);
68
- instructions.register('delay', new ((0, _utils().requireModule)(_path().default.join(__dirname, 'delay')))(plugin));
69
-
70
- for (var _i = 0, _Object$entries = Object.entries(more); _i < _Object$entries.length; _i++) {
50
+ const natives = ['calculation', 'condition', 'parallel', 'delay', 'prompt', 'query', 'create', 'update', 'destroy'].reduce((result, key) => Object.assign(result, {
51
+ [key]: key
52
+ }), {});
53
+
54
+ for (var _i = 0, _Object$entries = Object.entries(_objectSpread(_objectSpread({}, more), natives)); _i < _Object$entries.length; _i++) {
71
55
  const _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2),
72
56
  name = _Object$entries$_i[0],
73
- instruction = _Object$entries$_i[1];
57
+ value = _Object$entries$_i[1];
74
58
 
59
+ const instruction = typeof value === 'string' ? (0, _utils().requireModule)(_path().default.isAbsolute(value) ? value : _path().default.join(__dirname, value)) : value;
75
60
  instructions.register(name, typeof instruction === 'function' ? new instruction(plugin) : instruction);
76
61
  }
77
62
  }
@@ -1,7 +1,19 @@
1
- declare const _default: {
1
+ import { Context } from '@nocobase/actions';
2
+ import Plugin from '..';
3
+ import { Instruction } from '.';
4
+ export interface PromptConfig {
5
+ fields: [];
6
+ actions: any;
7
+ }
8
+ export default class implements Instruction {
9
+ protected plugin: Plugin;
10
+ middlewares: any[];
11
+ constructor(plugin: Plugin);
12
+ middleware: (context: Context, next: any) => Promise<void>;
13
+ use(middleware: any): void;
2
14
  run(node: any, input: any, processor: any): {
3
15
  status: number;
4
16
  };
5
17
  resume(node: any, job: any, processor: any): any;
6
- };
7
- export default _default;
18
+ extend(options: Instruction): void;
19
+ }
@@ -5,19 +5,126 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
+ function _koaCompose() {
9
+ const data = _interopRequireDefault(require("koa-compose"));
10
+
11
+ _koaCompose = function _koaCompose() {
12
+ return data;
13
+ };
14
+
15
+ return data;
16
+ }
17
+
18
+ function _actions() {
19
+ const data = require("@nocobase/actions");
20
+
21
+ _actions = function _actions() {
22
+ return data;
23
+ };
24
+
25
+ return data;
26
+ }
27
+
8
28
  var _constants = require("../constants");
9
29
 
10
- var _default = {
30
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
31
+
32
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
33
+
34
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
35
+
36
+ function loadJob(_x, _x2) {
37
+ return _loadJob.apply(this, arguments);
38
+ }
39
+
40
+ function _loadJob() {
41
+ _loadJob = _asyncToGenerator(function* (context, next) {
42
+ const _context$action$param = context.action.params,
43
+ filterByTk = _context$action$param.filterByTk,
44
+ values = _context$action$param.values;
45
+
46
+ if (!context.body) {
47
+ const jobRepo = _actions().utils.getRepositoryFromParams(context);
48
+
49
+ const job = yield jobRepo.findOne({
50
+ filterByTk,
51
+ appends: ['node', 'execution'],
52
+ context
53
+ });
54
+
55
+ if (!filterByTk || !job) {
56
+ return context.throw(404);
57
+ } // cache
58
+
59
+
60
+ context.body = job;
61
+ }
62
+
63
+ const _context$body$node = context.body.node,
64
+ type = _context$body$node.type,
65
+ config = _context$body$node.config;
66
+
67
+ if (type === 'prompt' && config.actions && !config.actions[values.status]) {
68
+ return context.throw(400);
69
+ }
70
+
71
+ yield next();
72
+ });
73
+ return _loadJob.apply(this, arguments);
74
+ }
75
+
76
+ class _default {
77
+ constructor(plugin) {
78
+ var _this = this;
79
+
80
+ this.plugin = void 0;
81
+ this.middlewares = [];
82
+
83
+ this.middleware = /*#__PURE__*/function () {
84
+ var _ref = _asyncToGenerator(function* (context, next) {
85
+ const _context$action = context.action,
86
+ actionName = _context$action.actionName,
87
+ resourceName = _context$action.resourceName;
88
+
89
+ if (actionName === 'submit' && resourceName === 'jobs' && _this.middlewares.length) {
90
+ return (0, _koaCompose().default)([loadJob, ..._this.middlewares])(context, next);
91
+ }
92
+
93
+ yield next();
94
+ });
95
+
96
+ return function (_x3, _x4) {
97
+ return _ref.apply(this, arguments);
98
+ };
99
+ }();
100
+
101
+ this.plugin = plugin;
102
+ plugin.app.resourcer.use(this.middleware);
103
+ }
104
+
105
+ use(middleware) {
106
+ this.middlewares.push(middleware);
107
+ }
108
+
11
109
  run(node, input, processor) {
12
110
  return {
13
111
  status: _constants.JOB_STATUS.PENDING
14
112
  };
15
- },
113
+ }
16
114
 
17
115
  resume(node, job, processor) {
18
- job.set('status', _constants.JOB_STATUS.RESOLVED);
116
+ if (!node.config.actions) {
117
+ job.set('status', _constants.JOB_STATUS.RESOLVED);
118
+ }
119
+
19
120
  return job;
20
121
  }
21
122
 
22
- };
23
- exports.default = _default;
123
+ extend(options) {
124
+ Object.assign(this, options);
125
+ }
126
+
127
+ }
128
+
129
+ exports.default = _default;
130
+ ;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/plugin-workflow",
3
- "version": "0.7.3-alpha.1",
3
+ "version": "0.7.4-alpha.1",
4
4
  "main": "lib/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "licenses": [
@@ -10,17 +10,17 @@
10
10
  }
11
11
  ],
12
12
  "dependencies": {
13
- "@nocobase/actions": "0.7.3-alpha.1",
14
- "@nocobase/client": "0.7.3-alpha.1",
15
- "@nocobase/database": "0.7.3-alpha.1",
16
- "@nocobase/server": "0.7.3-alpha.1",
17
- "@nocobase/utils": "0.7.3-alpha.1",
13
+ "@nocobase/actions": "0.7.4-alpha.1",
14
+ "@nocobase/client": "0.7.4-alpha.1",
15
+ "@nocobase/database": "0.7.4-alpha.1",
16
+ "@nocobase/server": "0.7.4-alpha.1",
17
+ "@nocobase/utils": "0.7.4-alpha.1",
18
18
  "cron-parser": "4.4.0",
19
19
  "json-templates": "^4.2.0",
20
20
  "react-js-cron": "^1.4.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@nocobase/test": "0.7.3-alpha.1"
23
+ "@nocobase/test": "0.7.4-alpha.1"
24
24
  },
25
- "gitHead": "09597f41800ec567f64000e928cd68c28bb927e3"
25
+ "gitHead": "543eb1c0308bb72a5ba54f208586cd78bfda8fa9"
26
26
  }