@nocobase/plugin-workflow 0.7.0-alpha.25 → 0.7.0-alpha.28

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.
@@ -15,6 +15,11 @@ export default {
15
15
  title: '使用事务',
16
16
  defaultValue: false
17
17
  },
18
+ {
19
+ type: 'uuid',
20
+ name: 'transaction',
21
+ defaultValue: null
22
+ },
18
23
  {
19
24
  interface: 'linkTo',
20
25
  type: 'hasMany',
@@ -1 +1 @@
1
- {"version":3,"file":"executions.js","sourceRoot":"","sources":["../../src/collections/executions.ts"],"names":[],"mappings":"AAEA,eAAe;IACb,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,gBAAgB;IACvB,KAAK,EAAE,MAAM;IACb,MAAM,EAAE;QACN;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,OAAO;SACf;QACD;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,MAAM;YACb,YAAY,EAAE,KAAK;SACpB;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,MAAM;SACd;QACD;YACE,SAAS,EAAE,MAAM;YACjB,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,OAAO;SACf;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,IAAI;SACZ;KACF;CACmB,CAAC","sourcesContent":["import { CollectionOptions } from '@nocobase/database';\n\nexport default {\n name: 'executions',\n model: 'ExecutionModel',\n title: '执行流程',\n fields: [\n {\n interface: 'linkTo',\n type: 'belongsTo',\n name: 'workflow',\n title: '所属工作流'\n },\n {\n type: 'boolean',\n name: 'useTransaction',\n title: '使用事务',\n defaultValue: false\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'jobs',\n title: '流程记录'\n },\n {\n interface: 'json',\n type: 'jsonb',\n name: 'context',\n title: '上下文数据'\n },\n {\n interface: 'select',\n type: 'integer',\n name: 'status',\n title: '状态'\n }\n ]\n} as CollectionOptions;\n"]}
1
+ {"version":3,"file":"executions.js","sourceRoot":"","sources":["../../src/collections/executions.ts"],"names":[],"mappings":"AAEA,eAAe;IACb,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,gBAAgB;IACvB,KAAK,EAAE,MAAM;IACb,MAAM,EAAE;QACN;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,OAAO;SACf;QACD;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,MAAM;YACb,YAAY,EAAE,KAAK;SACpB;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,aAAa;YACnB,YAAY,EAAE,IAAI;SACnB;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,MAAM;SACd;QACD;YACE,SAAS,EAAE,MAAM;YACjB,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,OAAO;SACf;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,IAAI;SACZ;KACF;CACmB,CAAC","sourcesContent":["import { CollectionOptions } from '@nocobase/database';\n\nexport default {\n name: 'executions',\n model: 'ExecutionModel',\n title: '执行流程',\n fields: [\n {\n interface: 'linkTo',\n type: 'belongsTo',\n name: 'workflow',\n title: '所属工作流'\n },\n {\n type: 'boolean',\n name: 'useTransaction',\n title: '使用事务',\n defaultValue: false\n },\n {\n type: 'uuid',\n name: 'transaction',\n defaultValue: null\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'jobs',\n title: '流程记录'\n },\n {\n interface: 'json',\n type: 'jsonb',\n name: 'context',\n title: '上下文数据'\n },\n {\n interface: 'select',\n type: 'integer',\n name: 'status',\n title: '状态'\n }\n ]\n} as CollectionOptions;\n"]}
@@ -7,14 +7,15 @@ export default {
7
7
  interface: 'string',
8
8
  type: 'string',
9
9
  name: 'title',
10
- title: '自动化名称',
10
+ title: '工作流名称',
11
11
  required: true
12
12
  },
13
13
  {
14
14
  interface: 'boolean',
15
15
  type: 'boolean',
16
16
  name: 'enabled',
17
- title: '启用'
17
+ title: '启用',
18
+ defaultValue: false
18
19
  },
19
20
  {
20
21
  interface: 'textarea',
@@ -1 +1 @@
1
- {"version":3,"file":"workflows.js","sourceRoot":"","sources":["../../src/collections/workflows.ts"],"names":[],"mappings":"AAEA,eAAe;IACb,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,eAAe;IACtB,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE;QACN;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,OAAO;YACd,QAAQ,EAAE,IAAI;SACf;QACD;YACE,SAAS,EAAE,SAAS;YACpB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,IAAI;SACZ;QACD;YACE,SAAS,EAAE,UAAU;YACrB,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,IAAI;SACZ;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,IAAI;SACf;QACD;YACE,SAAS,EAAE,MAAM;YACjB,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,IAAI;YACd,YAAY,EAAE,EAAE;SACjB;QACD;YACE,SAAS,EAAE,SAAS;YACpB,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,gBAAgB;YACtB,YAAY,EAAE,IAAI;SACnB;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,YAAY;YACpB,KAAK,EAAE,MAAM;SACd;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,YAAY;YACpB,KAAK,EAAE,MAAM;SACd;KACF;CACmB,CAAC","sourcesContent":["import { CollectionOptions } from '@nocobase/database';\n\nexport default {\n name: 'workflows',\n model: 'WorkflowModel',\n title: '自动化',\n fields: [\n {\n interface: 'string',\n type: 'string',\n name: 'title',\n title: '自动化名称',\n required: true\n },\n {\n interface: 'boolean',\n type: 'boolean',\n name: 'enabled',\n title: '启用'\n },\n {\n interface: 'textarea',\n type: 'text',\n name: 'description',\n title: '描述'\n },\n {\n interface: 'select',\n type: 'string',\n title: '触发方式',\n name: 'type',\n required: true\n },\n {\n interface: 'json',\n type: 'jsonb',\n title: '触发配置',\n name: 'config',\n required: true,\n defaultValue: {}\n },\n {\n interface: 'boolean',\n type: 'boolean',\n title: '使用事务',\n name: 'useTransaction',\n defaultValue: true\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'nodes',\n target: 'flow_nodes',\n title: '流程节点'\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'executions',\n target: 'executions',\n title: '触发执行'\n }\n ]\n} as CollectionOptions;\n"]}
1
+ {"version":3,"file":"workflows.js","sourceRoot":"","sources":["../../src/collections/workflows.ts"],"names":[],"mappings":"AAEA,eAAe;IACb,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,eAAe;IACtB,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE;QACN;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,OAAO;YACd,QAAQ,EAAE,IAAI;SACf;QACD;YACE,SAAS,EAAE,SAAS;YACpB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,IAAI;YACX,YAAY,EAAE,KAAK;SACpB;QACD;YACE,SAAS,EAAE,UAAU;YACrB,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,IAAI;SACZ;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,IAAI;SACf;QACD;YACE,SAAS,EAAE,MAAM;YACjB,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,IAAI;YACd,YAAY,EAAE,EAAE;SACjB;QACD;YACE,SAAS,EAAE,SAAS;YACpB,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,gBAAgB;YACtB,YAAY,EAAE,IAAI;SACnB;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,YAAY;YACpB,KAAK,EAAE,MAAM;SACd;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,YAAY;YACpB,KAAK,EAAE,MAAM;SACd;KACF;CACmB,CAAC","sourcesContent":["import { CollectionOptions } from '@nocobase/database';\n\nexport default {\n name: 'workflows',\n model: 'WorkflowModel',\n title: '自动化',\n fields: [\n {\n interface: 'string',\n type: 'string',\n name: 'title',\n title: '工作流名称',\n required: true\n },\n {\n interface: 'boolean',\n type: 'boolean',\n name: 'enabled',\n title: '启用',\n defaultValue: false\n },\n {\n interface: 'textarea',\n type: 'text',\n name: 'description',\n title: '描述'\n },\n {\n interface: 'select',\n type: 'string',\n title: '触发方式',\n name: 'type',\n required: true\n },\n {\n interface: 'json',\n type: 'jsonb',\n title: '触发配置',\n name: 'config',\n required: true,\n defaultValue: {}\n },\n {\n interface: 'boolean',\n type: 'boolean',\n title: '使用事务',\n name: 'useTransaction',\n defaultValue: true\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'nodes',\n target: 'flow_nodes',\n title: '流程节点'\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'executions',\n target: 'executions',\n title: '触发执行'\n }\n ]\n} as CollectionOptions;\n"]}
@@ -32,7 +32,7 @@ export default class ExecutionModel extends Model {
32
32
  };
33
33
  makeNodes(nodes?: any[]): void;
34
34
  makeJobs(jobs: Array<JobModel>): void;
35
- getTransaction(): Transaction | Promise<Transaction>;
35
+ getTransaction(): Promise<Transaction>;
36
36
  prepare(options: any, commit?: boolean): Promise<void>;
37
37
  start(options: ExecutionOptions): Promise<void>;
38
38
  resume(job: JobModel, options: ExecutionOptions): Promise<void>;
@@ -43,21 +43,27 @@ export default class ExecutionModel extends Model {
43
43
  });
44
44
  }
45
45
  getTransaction() {
46
- const { sequelize } = this.constructor.database;
47
- // @ts-ignore
48
- if (!this.useTransaction) {
49
- return undefined;
50
- }
51
- const { options } = this;
52
- // @ts-ignore
53
- return options.transaction && !options.transaction.finished
54
- ? options.transaction
55
- : sequelize.transaction();
46
+ return __awaiter(this, void 0, void 0, function* () {
47
+ const { sequelize } = this.constructor.database;
48
+ if (!this.useTransaction) {
49
+ return undefined;
50
+ }
51
+ const { options } = this;
52
+ // @ts-ignore
53
+ const transaction = options.transaction && !options.transaction.finished
54
+ ? options.transaction
55
+ : sequelize.transaction();
56
+ // @ts-ignore
57
+ if (this.transaction !== transaction.id) {
58
+ // @ts-ignore
59
+ yield this.update({ transaction: transaction.id }, { transaction });
60
+ }
61
+ return transaction;
62
+ });
56
63
  }
57
64
  prepare(options, commit = false) {
58
65
  return __awaiter(this, void 0, void 0, function* () {
59
66
  this.options = options || {};
60
- // @ts-ignore
61
67
  const transaction = yield this.getTransaction();
62
68
  this.transaction = transaction;
63
69
  if (!this.workflow) {
@@ -1 +1 @@
1
- {"version":3,"file":"Execution.js","sourceRoot":"","sources":["../../src/models/Execution.ts"],"names":[],"mappings":"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","sourcesContent":["import { Database, Model } from '@nocobase/database';\nimport parse from 'json-templates';\nimport { BelongsToGetAssociationMixin, HasManyGetAssociationsMixin, Transaction } from 'sequelize';\nimport { EXECUTION_STATUS, JOB_STATUS } from '../constants';\nimport instructions from '../instructions';\nimport WorkflowModel from './Workflow';\nimport FlowNodeModel from './FlowNode';\nimport JobModel from './Job';\nimport calculators from '../calculators';\n\nexport interface ExecutionOptions {\n transaction?: Transaction;\n}\n\nexport default class ExecutionModel extends Model {\n declare static readonly database: Database;\n\n declare id: number;\n declare title: string;\n declare context: any;\n declare status: number;\n // NOTE: this duplicated column is for transaction in preparing cycle from workflow\n declare useTransaction: boolean;\n\n declare createdAt: Date;\n declare updatedAt: Date;\n\n declare workflow?: WorkflowModel;\n declare getWorkflow: BelongsToGetAssociationMixin<WorkflowModel>;\n\n declare jobs?: JobModel[];\n declare getJobs: HasManyGetAssociationsMixin<JobModel>;\n\n options: ExecutionOptions;\n transaction: Transaction;\n\n nodes: Array<FlowNodeModel> = [];\n nodesMap = new Map<number, FlowNodeModel>();\n jobsMap = new Map<number, JobModel>();\n jobsMapByNodeId: { [key: number]: any } = {};\n\n static StatusMap = {\n [JOB_STATUS.PENDING]: EXECUTION_STATUS.STARTED,\n [JOB_STATUS.RESOLVED]: EXECUTION_STATUS.RESOLVED,\n [JOB_STATUS.REJECTED]: EXECUTION_STATUS.REJECTED,\n [JOB_STATUS.CANCELLED]: EXECUTION_STATUS.CANCELLED,\n };\n\n // make dual linked nodes list then cache\n makeNodes(nodes = []) {\n this.nodes = nodes;\n\n nodes.forEach((node) => {\n this.nodesMap.set(node.id, node);\n });\n\n nodes.forEach((node) => {\n if (node.upstreamId) {\n node.upstream = this.nodesMap.get(node.upstreamId);\n }\n\n if (node.downstreamId) {\n node.downstream = this.nodesMap.get(node.downstreamId);\n }\n });\n }\n\n makeJobs(jobs: Array<JobModel>) {\n jobs.forEach((job) => {\n this.jobsMap.set(job.id, job);\n // TODO: should consider cycle, and from previous job\n this.jobsMapByNodeId[job.nodeId] = job.result;\n });\n }\n\n getTransaction() {\n const { sequelize } = (<typeof WorkflowModel>this.constructor).database;\n // @ts-ignore\n if (!this.useTransaction) {\n return undefined;\n }\n\n const { options } = this;\n\n // @ts-ignore\n return options.transaction && !options.transaction.finished\n ? options.transaction\n : sequelize.transaction();\n }\n\n async prepare(options, commit = false) {\n this.options = options || {};\n // @ts-ignore\n const transaction = await this.getTransaction()\n this.transaction = transaction;\n\n if (!this.workflow) {\n this.workflow = await this.getWorkflow({ transaction });\n }\n\n const nodes = await this.workflow.getNodes({ transaction });\n\n this.makeNodes(nodes);\n\n const jobs = await this.getJobs({\n order: [['id', 'ASC']],\n transaction,\n });\n\n this.makeJobs(jobs);\n\n if (commit) {\n await this.commit();\n }\n }\n\n public async start(options: ExecutionOptions) {\n if (this.status !== EXECUTION_STATUS.STARTED) {\n throw new Error(`execution was ended with status ${this.status}`);\n }\n await this.prepare(options);\n if (this.nodes.length) {\n const head = this.nodes.find(item => !item.upstream);\n await this.run(head, { result: this.context });\n } else {\n await this.exit(null);\n }\n await this.commit();\n }\n\n public async resume(job: JobModel, options: ExecutionOptions) {\n if (this.status !== EXECUTION_STATUS.STARTED) {\n throw new Error(`execution was ended with status ${this.status}`);\n }\n await this.prepare(options);\n const node = this.nodesMap.get(job.nodeId);\n await this.recall(node, job);\n await this.commit();\n }\n\n private async commit() {\n // @ts-ignore\n if (this.transaction && (!this.options.transaction || this.options.transaction.finished)) {\n await this.transaction.commit();\n }\n }\n\n private async exec(instruction: Function, node: FlowNodeModel, prevJob) {\n let job;\n try {\n // call instruction to get result and status\n job = await instruction.call(node, prevJob, this);\n if (!job) {\n return null;\n }\n } catch (err) {\n // for uncaught error, set to rejected\n job = {\n result: err instanceof Error\n ? { message: err.message, stack: process.env.NODE_ENV === 'production' ? [] : err.stack }\n : err,\n status: JOB_STATUS.REJECTED,\n };\n // if previous job is from resuming\n if (prevJob && prevJob.nodeId === node.id) {\n prevJob.set(job);\n job = prevJob;\n }\n }\n\n let savedJob;\n // TODO(optimize): many checking of resuming or new could be improved\n // could be implemented separately in exec() / resume()\n if (job instanceof Model) {\n savedJob = (await job.save({ transaction: this.transaction })) as unknown as JobModel;\n } else {\n const upstreamId = prevJob instanceof Model ? prevJob.get('id') : null;\n savedJob = await this.saveJob({\n nodeId: node.id,\n upstreamId,\n ...job,\n });\n }\n\n if (savedJob.status === JOB_STATUS.RESOLVED && node.downstream) {\n // run next node\n return this.run(node.downstream, savedJob);\n }\n\n // all nodes in scope have been executed\n return this.end(node, savedJob);\n }\n\n public async run(node, input?) {\n const { run } = instructions.get(node.type);\n if (typeof run !== 'function') {\n return Promise.reject(new Error('`run` should be implemented for customized execution of the node'));\n }\n\n return this.exec(run, node, input);\n }\n\n // parent node should take over the control\n public end(node, job) {\n const parentNode = this.findBranchParentNode(node);\n // no parent, means on main flow\n if (parentNode) {\n return this.recall(parentNode, job);\n }\n\n // really done for all nodes\n // * should mark execution as done with last job status\n return this.exit(job);\n }\n\n async recall(node, job) {\n const { resume } = instructions.get(node.type);\n if (typeof resume !== 'function') {\n return Promise.reject(new Error('`resume` should be implemented because the node made branch'));\n }\n\n return this.exec(resume, node, job);\n }\n\n async exit(job: JobModel | null) {\n const status = job ? ExecutionModel.StatusMap[job.status] : EXECUTION_STATUS.RESOLVED;\n await this.update({ status }, { transaction: this.transaction });\n return null;\n }\n\n // TODO(optimize)\n async saveJob(payload) {\n const { database } = <typeof WorkflowModel>this.constructor;\n const { model } = database.getCollection('jobs');\n const [job] = (await model.upsert(\n {\n ...payload,\n executionId: this.id,\n },\n { transaction: this.transaction },\n )) as unknown as [JobModel, boolean | null];\n this.jobsMap.set(job.id, job);\n this.jobsMapByNodeId[job.nodeId] = job.result;\n\n return job;\n }\n\n // find the first node in current branch\n findBranchStartNode(node: FlowNodeModel): FlowNodeModel | null {\n for (let n = node; n; n = n.upstream) {\n if (n.branchIndex !== null) {\n return n;\n }\n }\n return null;\n }\n\n // find the node start current branch\n findBranchParentNode(node: FlowNodeModel): FlowNodeModel | null {\n for (let n = node; n; n = n.upstream) {\n if (n.branchIndex !== null) {\n return n.upstream;\n }\n }\n return null;\n }\n\n findBranchParentJob(job: JobModel, node: FlowNodeModel): JobModel | null {\n for (let j = job; j; j = this.jobsMap.get(j.upstreamId)) {\n if (j.nodeId === node.id) {\n return j;\n }\n }\n return null;\n }\n\n public getParsedValue(value, node?) {\n const injectedFns = {};\n const scope = {\n execution: this,\n node\n };\n for (let [name, fn] of calculators.getEntities()) {\n injectedFns[name] = fn.bind(scope);\n }\n\n return parse(value)({\n $context: this.context,\n $jobsMapByNodeId: this.jobsMapByNodeId,\n $fn: injectedFns\n });\n }\n}\n"]}
1
+ {"version":3,"file":"Execution.js","sourceRoot":"","sources":["../../src/models/Execution.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAY,KAAK,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,MAAM,gBAAgB,CAAC;AAEnC,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,YAAY,MAAM,iBAAiB,CAAC;AAI3C,OAAO,WAAW,MAAM,gBAAgB,CAAC;AAMzC,MAAM,CAAC,OAAO,OAAO,cAAe,SAAQ,KAAK;IAAjD;;QAsBE,UAAK,GAAyB,EAAE,CAAC;QACjC,aAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;QAC5C,YAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;QACtC,oBAAe,GAA2B,EAAE,CAAC;IAmQ/C,CAAC;IA1PC,yCAAyC;IACzC,SAAS,CAAC,KAAK,GAAG,EAAE;QAClB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QAEnB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YACrB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YACrB,IAAI,IAAI,CAAC,UAAU,EAAE;gBACnB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;aACpD;YAED,IAAI,IAAI,CAAC,YAAY,EAAE;gBACrB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;aACxD;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,QAAQ,CAAC,IAAqB;QAC5B,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACnB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YAC9B,qDAAqD;YACrD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC;IAEK,cAAc;;YAClB,MAAM,EAAE,SAAS,EAAE,GAA2B,IAAI,CAAC,WAAY,CAAC,QAAQ,CAAC;YAEzE,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE;gBACxB,OAAO,SAAS,CAAC;aAClB;YAED,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC;YAEzB,aAAa;YACb,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,QAAQ;gBACtE,CAAC,CAAC,OAAO,CAAC,WAAW;gBACrB,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC;YAE5B,aAAa;YACb,IAAI,IAAI,CAAC,WAAW,KAAK,WAAW,CAAC,EAAE,EAAE;gBACvC,aAAa;gBACb,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;aACrE;YACD,OAAO,WAAW,CAAC;QACrB,CAAC;KAAA;IAEK,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK;;YACnC,IAAI,CAAC,OAAO,GAAG,OAAO,IAAI,EAAE,CAAC;YAC7B,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;YAChD,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;YAE/B,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE;gBAClB,IAAI,CAAC,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;aACzD;YAED,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC;YAE5D,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YAEtB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC;gBAC9B,KAAK,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBACtB,WAAW;aACZ,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAEpB,IAAI,MAAM,EAAE;gBACV,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;aACrB;QACH,CAAC;KAAA;IAEY,KAAK,CAAC,OAAyB;;YAC1C,IAAI,IAAI,CAAC,MAAM,KAAK,gBAAgB,CAAC,OAAO,EAAE;gBAC5C,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;aACnE;YACD,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAC5B,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;gBACrB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBACrD,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;aAChD;iBAAM;gBACL,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;aACvB;YACD,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACtB,CAAC;KAAA;IAEY,MAAM,CAAC,GAAa,EAAE,OAAyB;;YAC1D,IAAI,IAAI,CAAC,MAAM,KAAK,gBAAgB,CAAC,OAAO,EAAE;gBAC5C,MAAM,IAAI,KAAK,CAAC,mCAAmC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;aACnE;YACD,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC3C,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC7B,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC;QACtB,CAAC;KAAA;IAEa,MAAM;;YAClB,aAAa;YACb,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE;gBACxF,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC;aACjC;QACH,CAAC;KAAA;IAEa,IAAI,CAAC,WAAqB,EAAE,IAAmB,EAAE,OAAO;;YACpE,IAAI,GAAG,CAAC;YACR,IAAI;gBACF,4CAA4C;gBAC5C,GAAG,GAAG,MAAM,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;gBAClD,IAAI,CAAC,GAAG,EAAE;oBACR,OAAO,IAAI,CAAC;iBACb;aACF;YAAC,OAAO,GAAG,EAAE;gBACZ,sCAAsC;gBACtC,GAAG,GAAG;oBACJ,MAAM,EAAE,GAAG,YAAY,KAAK;wBAC1B,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE;wBACzF,CAAC,CAAC,GAAG;oBACP,MAAM,EAAE,UAAU,CAAC,QAAQ;iBAC5B,CAAC;gBACF,mCAAmC;gBACnC,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE,EAAE;oBACzC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;oBACjB,GAAG,GAAG,OAAO,CAAC;iBACf;aACF;YAED,IAAI,QAAQ,CAAC;YACb,qEAAqE;YACrE,uDAAuD;YACvD,IAAI,GAAG,YAAY,KAAK,EAAE;gBACxB,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAwB,CAAC;aACvF;iBAAM;gBACL,MAAM,UAAU,GAAG,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;gBACvE,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,iBAC3B,MAAM,EAAE,IAAI,CAAC,EAAE,EACf,UAAU,IACP,GAAG,EACN,CAAC;aACJ;YAED,IAAI,QAAQ,CAAC,MAAM,KAAK,UAAU,CAAC,QAAQ,IAAI,IAAI,CAAC,UAAU,EAAE;gBAC9D,gBAAgB;gBAChB,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;aAC5C;YAED,wCAAwC;YACxC,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QAClC,CAAC;KAAA;IAEY,GAAG,CAAC,IAAI,EAAE,KAAM;;YAC3B,MAAM,EAAE,GAAG,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC5C,IAAI,OAAO,GAAG,KAAK,UAAU,EAAE;gBAC7B,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,kEAAkE,CAAC,CAAC,CAAC;aACtG;YAED,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;QACrC,CAAC;KAAA;IAED,2CAA2C;IACpC,GAAG,CAAC,IAAI,EAAE,GAAG;QAClB,MAAM,UAAU,GAAG,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACnD,gCAAgC;QAChC,IAAI,UAAU,EAAE;YACd,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;SACrC;QAED,4BAA4B;QAC5B,uDAAuD;QACvD,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAEK,MAAM,CAAC,IAAI,EAAE,GAAG;;YACpB,MAAM,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC/C,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE;gBAChC,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC,CAAC;aACjG;YAED,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC;QACtC,CAAC;KAAA;IAEK,IAAI,CAAC,GAAoB;;YAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,QAAQ,CAAC;YACtF,MAAM,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YACjE,OAAO,IAAI,CAAC;QACd,CAAC;KAAA;IAED,iBAAiB;IACX,OAAO,CAAC,OAAO;;YACnB,MAAM,EAAE,QAAQ,EAAE,GAA0B,IAAI,CAAC,WAAW,CAAC;YAC7D,MAAM,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YACjD,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,KAAK,CAAC,MAAM,iCAE1B,OAAO,KACV,WAAW,EAAE,IAAI,CAAC,EAAE,KAEtB,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAClC,CAA0C,CAAC;YAC5C,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YAC9B,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC;YAE9C,OAAO,GAAG,CAAC;QACb,CAAC;KAAA;IAED,wCAAwC;IACxC,mBAAmB,CAAC,IAAmB;QACrC,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;YACpC,IAAI,CAAC,CAAC,WAAW,KAAK,IAAI,EAAE;gBAC1B,OAAO,CAAC,CAAC;aACV;SACF;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,qCAAqC;IACrC,oBAAoB,CAAC,IAAmB;QACtC,KAAK,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;YACpC,IAAI,CAAC,CAAC,WAAW,KAAK,IAAI,EAAE;gBAC1B,OAAO,CAAC,CAAC,QAAQ,CAAC;aACnB;SACF;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mBAAmB,CAAC,GAAa,EAAE,IAAmB;QACpD,KAAK,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE;YACvD,IAAI,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE,EAAE;gBACxB,OAAO,CAAC,CAAC;aACV;SACF;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,cAAc,CAAC,KAAK,EAAE,IAAK;QAChC,MAAM,WAAW,GAAG,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG;YACZ,SAAS,EAAE,IAAI;YACf,IAAI;SACL,CAAC;QACF,KAAK,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,WAAW,CAAC,WAAW,EAAE,EAAE;YAChD,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;SACpC;QAED,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC;YAClB,QAAQ,EAAE,IAAI,CAAC,OAAO;YACtB,gBAAgB,EAAE,IAAI,CAAC,eAAe;YACtC,GAAG,EAAE,WAAW;SACjB,CAAC,CAAC;IACL,CAAC;;AAhQM,wBAAS,GAAG;IACjB,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAC,OAAO;IAC9C,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC,QAAQ;IAChD,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAC,QAAQ;IAChD,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,gBAAgB,CAAC,SAAS;CACnD,CAAC","sourcesContent":["import { Database, Model } from '@nocobase/database';\nimport parse from 'json-templates';\nimport { BelongsToGetAssociationMixin, HasManyGetAssociationsMixin, Transaction } from 'sequelize';\nimport { EXECUTION_STATUS, JOB_STATUS } from '../constants';\nimport instructions from '../instructions';\nimport WorkflowModel from './Workflow';\nimport FlowNodeModel from './FlowNode';\nimport JobModel from './Job';\nimport calculators from '../calculators';\n\nexport interface ExecutionOptions {\n transaction?: Transaction;\n}\n\nexport default class ExecutionModel extends Model {\n declare static readonly database: Database;\n\n declare id: number;\n declare title: string;\n declare context: any;\n declare status: number;\n // NOTE: this duplicated column is for transaction in preparing cycle from workflow\n declare useTransaction: boolean;\n\n declare createdAt: Date;\n declare updatedAt: Date;\n\n declare workflow?: WorkflowModel;\n declare getWorkflow: BelongsToGetAssociationMixin<WorkflowModel>;\n\n declare jobs?: JobModel[];\n declare getJobs: HasManyGetAssociationsMixin<JobModel>;\n\n options: ExecutionOptions;\n transaction: Transaction;\n\n nodes: Array<FlowNodeModel> = [];\n nodesMap = new Map<number, FlowNodeModel>();\n jobsMap = new Map<number, JobModel>();\n jobsMapByNodeId: { [key: number]: any } = {};\n\n static StatusMap = {\n [JOB_STATUS.PENDING]: EXECUTION_STATUS.STARTED,\n [JOB_STATUS.RESOLVED]: EXECUTION_STATUS.RESOLVED,\n [JOB_STATUS.REJECTED]: EXECUTION_STATUS.REJECTED,\n [JOB_STATUS.CANCELLED]: EXECUTION_STATUS.CANCELLED,\n };\n\n // make dual linked nodes list then cache\n makeNodes(nodes = []) {\n this.nodes = nodes;\n\n nodes.forEach((node) => {\n this.nodesMap.set(node.id, node);\n });\n\n nodes.forEach((node) => {\n if (node.upstreamId) {\n node.upstream = this.nodesMap.get(node.upstreamId);\n }\n\n if (node.downstreamId) {\n node.downstream = this.nodesMap.get(node.downstreamId);\n }\n });\n }\n\n makeJobs(jobs: Array<JobModel>) {\n jobs.forEach((job) => {\n this.jobsMap.set(job.id, job);\n // TODO: should consider cycle, and from previous job\n this.jobsMapByNodeId[job.nodeId] = job.result;\n });\n }\n\n async getTransaction() {\n const { sequelize } = (<typeof ExecutionModel>this.constructor).database;\n\n if (!this.useTransaction) {\n return undefined;\n }\n\n const { options } = this;\n\n // @ts-ignore\n const transaction = options.transaction && !options.transaction.finished\n ? options.transaction\n : sequelize.transaction();\n\n // @ts-ignore\n if (this.transaction !== transaction.id) {\n // @ts-ignore\n await this.update({ transaction: transaction.id }, { transaction });\n }\n return transaction;\n }\n\n async prepare(options, commit = false) {\n this.options = options || {};\n const transaction = await this.getTransaction();\n this.transaction = transaction;\n\n if (!this.workflow) {\n this.workflow = await this.getWorkflow({ transaction });\n }\n\n const nodes = await this.workflow.getNodes({ transaction });\n\n this.makeNodes(nodes);\n\n const jobs = await this.getJobs({\n order: [['id', 'ASC']],\n transaction,\n });\n\n this.makeJobs(jobs);\n\n if (commit) {\n await this.commit();\n }\n }\n\n public async start(options: ExecutionOptions) {\n if (this.status !== EXECUTION_STATUS.STARTED) {\n throw new Error(`execution was ended with status ${this.status}`);\n }\n await this.prepare(options);\n if (this.nodes.length) {\n const head = this.nodes.find(item => !item.upstream);\n await this.run(head, { result: this.context });\n } else {\n await this.exit(null);\n }\n await this.commit();\n }\n\n public async resume(job: JobModel, options: ExecutionOptions) {\n if (this.status !== EXECUTION_STATUS.STARTED) {\n throw new Error(`execution was ended with status ${this.status}`);\n }\n await this.prepare(options);\n const node = this.nodesMap.get(job.nodeId);\n await this.recall(node, job);\n await this.commit();\n }\n\n private async commit() {\n // @ts-ignore\n if (this.transaction && (!this.options.transaction || this.options.transaction.finished)) {\n await this.transaction.commit();\n }\n }\n\n private async exec(instruction: Function, node: FlowNodeModel, prevJob) {\n let job;\n try {\n // call instruction to get result and status\n job = await instruction.call(node, prevJob, this);\n if (!job) {\n return null;\n }\n } catch (err) {\n // for uncaught error, set to rejected\n job = {\n result: err instanceof Error\n ? { message: err.message, stack: process.env.NODE_ENV === 'production' ? [] : err.stack }\n : err,\n status: JOB_STATUS.REJECTED,\n };\n // if previous job is from resuming\n if (prevJob && prevJob.nodeId === node.id) {\n prevJob.set(job);\n job = prevJob;\n }\n }\n\n let savedJob;\n // TODO(optimize): many checking of resuming or new could be improved\n // could be implemented separately in exec() / resume()\n if (job instanceof Model) {\n savedJob = (await job.save({ transaction: this.transaction })) as unknown as JobModel;\n } else {\n const upstreamId = prevJob instanceof Model ? prevJob.get('id') : null;\n savedJob = await this.saveJob({\n nodeId: node.id,\n upstreamId,\n ...job,\n });\n }\n\n if (savedJob.status === JOB_STATUS.RESOLVED && node.downstream) {\n // run next node\n return this.run(node.downstream, savedJob);\n }\n\n // all nodes in scope have been executed\n return this.end(node, savedJob);\n }\n\n public async run(node, input?) {\n const { run } = instructions.get(node.type);\n if (typeof run !== 'function') {\n return Promise.reject(new Error('`run` should be implemented for customized execution of the node'));\n }\n\n return this.exec(run, node, input);\n }\n\n // parent node should take over the control\n public end(node, job) {\n const parentNode = this.findBranchParentNode(node);\n // no parent, means on main flow\n if (parentNode) {\n return this.recall(parentNode, job);\n }\n\n // really done for all nodes\n // * should mark execution as done with last job status\n return this.exit(job);\n }\n\n async recall(node, job) {\n const { resume } = instructions.get(node.type);\n if (typeof resume !== 'function') {\n return Promise.reject(new Error('`resume` should be implemented because the node made branch'));\n }\n\n return this.exec(resume, node, job);\n }\n\n async exit(job: JobModel | null) {\n const status = job ? ExecutionModel.StatusMap[job.status] : EXECUTION_STATUS.RESOLVED;\n await this.update({ status }, { transaction: this.transaction });\n return null;\n }\n\n // TODO(optimize)\n async saveJob(payload) {\n const { database } = <typeof ExecutionModel>this.constructor;\n const { model } = database.getCollection('jobs');\n const [job] = (await model.upsert(\n {\n ...payload,\n executionId: this.id,\n },\n { transaction: this.transaction },\n )) as unknown as [JobModel, boolean | null];\n this.jobsMap.set(job.id, job);\n this.jobsMapByNodeId[job.nodeId] = job.result;\n\n return job;\n }\n\n // find the first node in current branch\n findBranchStartNode(node: FlowNodeModel): FlowNodeModel | null {\n for (let n = node; n; n = n.upstream) {\n if (n.branchIndex !== null) {\n return n;\n }\n }\n return null;\n }\n\n // find the node start current branch\n findBranchParentNode(node: FlowNodeModel): FlowNodeModel | null {\n for (let n = node; n; n = n.upstream) {\n if (n.branchIndex !== null) {\n return n.upstream;\n }\n }\n return null;\n }\n\n findBranchParentJob(job: JobModel, node: FlowNodeModel): JobModel | null {\n for (let j = job; j; j = this.jobsMap.get(j.upstreamId)) {\n if (j.nodeId === node.id) {\n return j;\n }\n }\n return null;\n }\n\n public getParsedValue(value, node?) {\n const injectedFns = {};\n const scope = {\n execution: this,\n node\n };\n for (let [name, fn] of calculators.getEntities()) {\n injectedFns[name] = fn.bind(scope);\n }\n\n return parse(value)({\n $context: this.context,\n $jobsMapByNodeId: this.jobsMapByNodeId,\n $fn: injectedFns\n });\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { Database, Model } from '@nocobase/database';
2
- import { HasManyCreateAssociationMixin, HasManyGetAssociationsMixin } from 'sequelize';
2
+ import { HasManyCountAssociationsMixin, HasManyCreateAssociationMixin, HasManyGetAssociationsMixin } from 'sequelize';
3
3
  import ExecutionModel from './Execution';
4
4
  import FlowNodeModel from './FlowNode';
5
5
  export default class WorkflowModel extends Model {
@@ -17,6 +17,7 @@ export default class WorkflowModel extends Model {
17
17
  getNodes: HasManyGetAssociationsMixin<FlowNodeModel>;
18
18
  createNode: HasManyCreateAssociationMixin<FlowNodeModel>;
19
19
  executions: ExecutionModel[];
20
+ countExecutions: HasManyCountAssociationsMixin;
20
21
  getExecutions: HasManyGetAssociationsMixin<ExecutionModel>;
21
22
  createExecution: HasManyCreateAssociationMixin<ExecutionModel>;
22
23
  static mount(): Promise<void>;
@@ -55,10 +55,23 @@ export default class WorkflowModel extends Model {
55
55
  return;
56
56
  }
57
57
  const transaction = yield this.getTransaction(options);
58
+ if (this.useTransaction) {
59
+ const existed = yield this.countExecutions({
60
+ where: {
61
+ transaction: transaction.id
62
+ },
63
+ transaction
64
+ });
65
+ if (existed) {
66
+ console.warn(`workflow ${this.id} has already been triggered in same execution (${transaction.id}), and newly triggering will be skipped.`);
67
+ return;
68
+ }
69
+ }
58
70
  const execution = yield this.createExecution({
59
71
  context,
60
72
  status: EXECUTION_STATUS.STARTED,
61
- useTransaction: this.useTransaction
73
+ useTransaction: this.useTransaction,
74
+ transaction: transaction.id
62
75
  }, { transaction });
63
76
  execution.workflow = this;
64
77
  yield execution.start({ transaction });
@@ -1 +1 @@
1
- {"version":3,"file":"Workflow.js","sourceRoot":"","sources":["../../src/models/Workflow.ts"],"names":[],"mappings":"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","sourcesContent":["import { Database, Model } from '@nocobase/database';\nimport { HasManyCreateAssociationMixin, HasManyGetAssociationsMixin } from 'sequelize';\n\nimport triggers from '../triggers';\nimport { EXECUTION_STATUS } from '../constants';\nimport ExecutionModel from './Execution';\nimport FlowNodeModel from './FlowNode';\n\nexport default class WorkflowModel extends Model {\n declare static database: Database;\n\n declare id: number;\n declare title: string;\n declare enabled: boolean;\n declare description?: string;\n declare type: string;\n declare config: any;\n declare useTransaction: boolean;\n\n declare createdAt: Date;\n declare updatedAt: Date;\n\n declare nodes: FlowNodeModel[];\n declare getNodes: HasManyGetAssociationsMixin<FlowNodeModel>;\n declare createNode: HasManyCreateAssociationMixin<FlowNodeModel>;\n\n declare executions: ExecutionModel[];\n declare getExecutions: HasManyGetAssociationsMixin<ExecutionModel>;\n declare createExecution: HasManyCreateAssociationMixin<ExecutionModel>;\n\n static async mount() {\n const collection = this.database.getCollection('workflows');\n const workflows = await collection.repository.find({\n filter: { enabled: true },\n });\n\n workflows.forEach((workflow: WorkflowModel) => {\n workflow.toggle();\n });\n\n this.addHook('afterCreate', (model: WorkflowModel) => model.toggle());\n this.addHook('afterUpdate', (model: WorkflowModel) => model.toggle());\n this.addHook('afterDestroy', (model: WorkflowModel) => model.toggle(false));\n }\n\n getHookId() {\n return `workflow-${this.get('id')}`;\n }\n\n getTransaction(options) {\n if (!this.useTransaction) {\n return undefined;\n }\n\n return options.transaction && !options.transaction.finished\n ? options.transaction\n : (<typeof WorkflowModel>this.constructor).database.sequelize.transaction();\n }\n\n async toggle(enable?: boolean) {\n const type = this.get('type');\n const { on, off } = triggers.get(type);\n if (typeof enable !== 'undefined' ? enable : this.get('enabled')) {\n on.call(this, this.trigger.bind(this));\n } else {\n off.call(this);\n }\n }\n\n async trigger(context: Object, options) {\n // `null` means not to trigger\n if (context === null) {\n return;\n }\n\n const transaction = await this.getTransaction(options);\n\n const execution = await this.createExecution({\n context,\n status: EXECUTION_STATUS.STARTED,\n useTransaction: this.useTransaction\n }, { transaction });\n\n execution.workflow = this;\n\n await execution.start({ transaction });\n\n if (transaction && (!options.transaction || options.transaction.finished)) {\n await transaction.commit();\n }\n\n return execution;\n }\n}\n"]}
1
+ {"version":3,"file":"Workflow.js","sourceRoot":"","sources":["../../src/models/Workflow.ts"],"names":[],"mappings":"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","sourcesContent":["import { Database, Model } from '@nocobase/database';\nimport { HasManyCountAssociationsMixin, HasManyCreateAssociationMixin, HasManyGetAssociationsMixin } from 'sequelize';\n\nimport triggers from '../triggers';\nimport { EXECUTION_STATUS } from '../constants';\nimport ExecutionModel from './Execution';\nimport FlowNodeModel from './FlowNode';\n\nexport default class WorkflowModel extends Model {\n declare static database: Database;\n\n declare id: number;\n declare title: string;\n declare enabled: boolean;\n declare description?: string;\n declare type: string;\n declare config: any;\n declare useTransaction: boolean;\n\n declare createdAt: Date;\n declare updatedAt: Date;\n\n declare nodes: FlowNodeModel[];\n declare getNodes: HasManyGetAssociationsMixin<FlowNodeModel>;\n declare createNode: HasManyCreateAssociationMixin<FlowNodeModel>;\n\n declare executions: ExecutionModel[];\n declare countExecutions: HasManyCountAssociationsMixin;\n declare getExecutions: HasManyGetAssociationsMixin<ExecutionModel>;\n declare createExecution: HasManyCreateAssociationMixin<ExecutionModel>;\n\n static async mount() {\n const collection = this.database.getCollection('workflows');\n const workflows = await collection.repository.find({\n filter: { enabled: true },\n });\n\n workflows.forEach((workflow: WorkflowModel) => {\n workflow.toggle();\n });\n\n this.addHook('afterCreate', (model: WorkflowModel) => model.toggle());\n this.addHook('afterUpdate', (model: WorkflowModel) => model.toggle());\n this.addHook('afterDestroy', (model: WorkflowModel) => model.toggle(false));\n }\n\n getHookId() {\n return `workflow-${this.get('id')}`;\n }\n\n getTransaction(options) {\n if (!this.useTransaction) {\n return undefined;\n }\n\n return options.transaction && !options.transaction.finished\n ? options.transaction\n : (<typeof WorkflowModel>this.constructor).database.sequelize.transaction();\n }\n\n async toggle(enable?: boolean) {\n const type = this.get('type');\n const { on, off } = triggers.get(type);\n if (typeof enable !== 'undefined' ? enable : this.get('enabled')) {\n on.call(this, this.trigger.bind(this));\n } else {\n off.call(this);\n }\n }\n\n async trigger(context: Object, options) {\n // `null` means not to trigger\n if (context === null) {\n return;\n }\n\n const transaction = await this.getTransaction(options);\n\n if (this.useTransaction) {\n const existed = await this.countExecutions({\n where: {\n transaction: transaction.id\n },\n transaction\n });\n\n if (existed) {\n console.warn(`workflow ${this.id} has already been triggered in same execution (${transaction.id}), and newly triggering will be skipped.`);\n return;\n }\n }\n\n const execution = await this.createExecution({\n context,\n status: EXECUTION_STATUS.STARTED,\n useTransaction: this.useTransaction,\n transaction: transaction.id\n }, { transaction });\n\n execution.workflow = this;\n\n await execution.start({ transaction });\n\n if (transaction && (!options.transaction || options.transaction.finished)) {\n await transaction.commit();\n }\n\n return execution;\n }\n}\n"]}
File without changes
@@ -24,18 +24,23 @@ function bindHandler(callback) {
24
24
  };
25
25
  }
26
26
  export default {
27
- name: 'model',
27
+ name: 'collection',
28
28
  on(callback) {
29
+ var _a;
29
30
  const { database } = this.constructor;
30
31
  const { collection, mode } = this.config;
31
32
  const Collection = database.getCollection(collection);
32
33
  if (!Collection) {
33
34
  return;
34
35
  }
35
- // TODO: duplication when mode change should be considered
36
36
  for (let [key, event] of MODE_BITMAP_EVENTS.entries()) {
37
37
  if (mode & key) {
38
- Collection.model.addHook(event, this.getHookId(), bindHandler.call(this, callback));
38
+ if (!((_a = Collection.model.options.hooks[event]) === null || _a === void 0 ? void 0 : _a.find(item => item.name && item.name === this.getHookId()))) {
39
+ Collection.model.addHook(event, this.getHookId(), bindHandler.call(this, callback));
40
+ }
41
+ }
42
+ else {
43
+ Collection.model.removeHook(event, this.getHookId());
39
44
  }
40
45
  }
41
46
  },
@@ -53,4 +58,4 @@ export default {
53
58
  }
54
59
  }
55
60
  };
56
- //# sourceMappingURL=model.js.map
61
+ //# sourceMappingURL=collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.js","sourceRoot":"","sources":["../../src/triggers/collection.ts"],"names":[],"mappings":"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","sourcesContent":["import WorkflowModel from \"../models/Workflow\";\n\nexport interface ModelChangeTriggerConfig {\n collection: string;\n mode: number;\n // TODO: ICondition\n condition: any;\n}\n\nconst MODE_BITMAP = {\n CREATE: 1,\n UPDATE: 2,\n DESTROY: 4\n};\n\nconst MODE_BITMAP_EVENTS = new Map();\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.CREATE, 'afterCreate');\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.UPDATE, 'afterUpdate');\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.DESTROY, 'afterDestroy');\n\n// async function, should return promise\nfunction bindHandler(this: WorkflowModel, callback: Function) {\n const { condition, changed } = this.config;\n return (data: any, options) => {\n // NOTE: if no configured fields changed, do not trigger\n if (changed && changed.length && changed.every(name => !data.changed(name))) {\n return;\n }\n // NOTE: if no configured condition match, do not trigger\n if (condition && condition.$and.length) {\n // TODO: check all conditions in condition against data\n // const calculation = toCalculation(condition);\n }\n\n return callback({ data: data.get() }, options);\n };\n}\n\nexport default {\n name: 'collection',\n on(this: WorkflowModel, callback: Function) {\n const { database } = <typeof WorkflowModel>this.constructor;\n const { collection, mode } = this.config;\n const Collection = database.getCollection(collection);\n if (!Collection) {\n return;\n }\n\n for (let [key, event] of MODE_BITMAP_EVENTS.entries()) {\n if (mode & key) {\n if (!Collection.model.options.hooks[event]?.find(item => item.name && item.name === this.getHookId())) {\n Collection.model.addHook(event, this.getHookId(), bindHandler.call(this, callback));\n }\n } else {\n Collection.model.removeHook(event, this.getHookId());\n }\n }\n },\n off(this: WorkflowModel) {\n const { database } = <typeof WorkflowModel>this.constructor;\n const { collection, mode } = this.config;\n const Collection = database.getCollection(collection);\n if (!Collection) {\n return;\n }\n for (let [key, event] of MODE_BITMAP_EVENTS.entries()) {\n if (mode & key) {\n Collection.model.removeHook(event, this.getHookId());\n }\n }\n }\n}\n"]}
@@ -1,6 +1,6 @@
1
1
  import { Registry } from '@nocobase/utils';
2
- import modelTrigger from './model';
2
+ import collectionlTrigger from './collection';
3
3
  export const triggers = new Registry();
4
4
  export default triggers;
5
- triggers.register(modelTrigger.name, modelTrigger);
5
+ triggers.register(collectionlTrigger.name, collectionlTrigger);
6
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/triggers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,OAAO,YAAY,MAAM,SAAS,CAAC;AAQnC,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAW,CAAC;AAEhD,eAAe,QAAQ,CAAC;AAExB,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC","sourcesContent":["import { Registry } from '@nocobase/utils';\nimport WorkflowModel from '../models/Workflow';\nimport modelTrigger from './model';\n\nexport interface Trigger {\n name: string;\n on(this: WorkflowModel, callback: Function): void;\n off(this: WorkflowModel): void;\n}\n\nexport const triggers = new Registry<Trigger>();\n\nexport default triggers;\n\ntriggers.register(modelTrigger.name, modelTrigger);\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/triggers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAE3C,OAAO,kBAAkB,MAAM,cAAc,CAAC;AAQ9C,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAW,CAAC;AAEhD,eAAe,QAAQ,CAAC;AAExB,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC","sourcesContent":["import { Registry } from '@nocobase/utils';\nimport WorkflowModel from '../models/Workflow';\nimport collectionlTrigger from './collection';\n\nexport interface Trigger {\n name: string;\n on(this: WorkflowModel, callback: Function): void;\n off(this: WorkflowModel): void;\n}\n\nexport const triggers = new Registry<Trigger>();\n\nexport default triggers;\n\ntriggers.register(collectionlTrigger.name, collectionlTrigger);\n"]}
@@ -17,6 +17,11 @@ exports.default = {
17
17
  title: '使用事务',
18
18
  defaultValue: false
19
19
  },
20
+ {
21
+ type: 'uuid',
22
+ name: 'transaction',
23
+ defaultValue: null
24
+ },
20
25
  {
21
26
  interface: 'linkTo',
22
27
  type: 'hasMany',
@@ -1 +1 @@
1
- {"version":3,"file":"executions.js","sourceRoot":"","sources":["../../src/collections/executions.ts"],"names":[],"mappings":";;AAEA,kBAAe;IACb,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,gBAAgB;IACvB,KAAK,EAAE,MAAM;IACb,MAAM,EAAE;QACN;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,OAAO;SACf;QACD;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,MAAM;YACb,YAAY,EAAE,KAAK;SACpB;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,MAAM;SACd;QACD;YACE,SAAS,EAAE,MAAM;YACjB,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,OAAO;SACf;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,IAAI;SACZ;KACF;CACmB,CAAC","sourcesContent":["import { CollectionOptions } from '@nocobase/database';\n\nexport default {\n name: 'executions',\n model: 'ExecutionModel',\n title: '执行流程',\n fields: [\n {\n interface: 'linkTo',\n type: 'belongsTo',\n name: 'workflow',\n title: '所属工作流'\n },\n {\n type: 'boolean',\n name: 'useTransaction',\n title: '使用事务',\n defaultValue: false\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'jobs',\n title: '流程记录'\n },\n {\n interface: 'json',\n type: 'jsonb',\n name: 'context',\n title: '上下文数据'\n },\n {\n interface: 'select',\n type: 'integer',\n name: 'status',\n title: '状态'\n }\n ]\n} as CollectionOptions;\n"]}
1
+ {"version":3,"file":"executions.js","sourceRoot":"","sources":["../../src/collections/executions.ts"],"names":[],"mappings":";;AAEA,kBAAe;IACb,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,gBAAgB;IACvB,KAAK,EAAE,MAAM;IACb,MAAM,EAAE;QACN;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,WAAW;YACjB,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,OAAO;SACf;QACD;YACE,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,gBAAgB;YACtB,KAAK,EAAE,MAAM;YACb,YAAY,EAAE,KAAK;SACpB;QACD;YACE,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,aAAa;YACnB,YAAY,EAAE,IAAI;SACnB;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,MAAM;SACd;QACD;YACE,SAAS,EAAE,MAAM;YACjB,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,OAAO;SACf;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,IAAI;SACZ;KACF;CACmB,CAAC","sourcesContent":["import { CollectionOptions } from '@nocobase/database';\n\nexport default {\n name: 'executions',\n model: 'ExecutionModel',\n title: '执行流程',\n fields: [\n {\n interface: 'linkTo',\n type: 'belongsTo',\n name: 'workflow',\n title: '所属工作流'\n },\n {\n type: 'boolean',\n name: 'useTransaction',\n title: '使用事务',\n defaultValue: false\n },\n {\n type: 'uuid',\n name: 'transaction',\n defaultValue: null\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'jobs',\n title: '流程记录'\n },\n {\n interface: 'json',\n type: 'jsonb',\n name: 'context',\n title: '上下文数据'\n },\n {\n interface: 'select',\n type: 'integer',\n name: 'status',\n title: '状态'\n }\n ]\n} as CollectionOptions;\n"]}
@@ -9,14 +9,15 @@ exports.default = {
9
9
  interface: 'string',
10
10
  type: 'string',
11
11
  name: 'title',
12
- title: '自动化名称',
12
+ title: '工作流名称',
13
13
  required: true
14
14
  },
15
15
  {
16
16
  interface: 'boolean',
17
17
  type: 'boolean',
18
18
  name: 'enabled',
19
- title: '启用'
19
+ title: '启用',
20
+ defaultValue: false
20
21
  },
21
22
  {
22
23
  interface: 'textarea',
@@ -1 +1 @@
1
- {"version":3,"file":"workflows.js","sourceRoot":"","sources":["../../src/collections/workflows.ts"],"names":[],"mappings":";;AAEA,kBAAe;IACb,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,eAAe;IACtB,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE;QACN;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,OAAO;YACd,QAAQ,EAAE,IAAI;SACf;QACD;YACE,SAAS,EAAE,SAAS;YACpB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,IAAI;SACZ;QACD;YACE,SAAS,EAAE,UAAU;YACrB,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,IAAI;SACZ;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,IAAI;SACf;QACD;YACE,SAAS,EAAE,MAAM;YACjB,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,IAAI;YACd,YAAY,EAAE,EAAE;SACjB;QACD;YACE,SAAS,EAAE,SAAS;YACpB,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,gBAAgB;YACtB,YAAY,EAAE,IAAI;SACnB;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,YAAY;YACpB,KAAK,EAAE,MAAM;SACd;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,YAAY;YACpB,KAAK,EAAE,MAAM;SACd;KACF;CACmB,CAAC","sourcesContent":["import { CollectionOptions } from '@nocobase/database';\n\nexport default {\n name: 'workflows',\n model: 'WorkflowModel',\n title: '自动化',\n fields: [\n {\n interface: 'string',\n type: 'string',\n name: 'title',\n title: '自动化名称',\n required: true\n },\n {\n interface: 'boolean',\n type: 'boolean',\n name: 'enabled',\n title: '启用'\n },\n {\n interface: 'textarea',\n type: 'text',\n name: 'description',\n title: '描述'\n },\n {\n interface: 'select',\n type: 'string',\n title: '触发方式',\n name: 'type',\n required: true\n },\n {\n interface: 'json',\n type: 'jsonb',\n title: '触发配置',\n name: 'config',\n required: true,\n defaultValue: {}\n },\n {\n interface: 'boolean',\n type: 'boolean',\n title: '使用事务',\n name: 'useTransaction',\n defaultValue: true\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'nodes',\n target: 'flow_nodes',\n title: '流程节点'\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'executions',\n target: 'executions',\n title: '触发执行'\n }\n ]\n} as CollectionOptions;\n"]}
1
+ {"version":3,"file":"workflows.js","sourceRoot":"","sources":["../../src/collections/workflows.ts"],"names":[],"mappings":";;AAEA,kBAAe;IACb,IAAI,EAAE,WAAW;IACjB,KAAK,EAAE,eAAe;IACtB,KAAK,EAAE,KAAK;IACZ,MAAM,EAAE;QACN;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,OAAO;YACd,QAAQ,EAAE,IAAI;SACf;QACD;YACE,SAAS,EAAE,SAAS;YACpB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,IAAI;YACX,YAAY,EAAE,KAAK;SACpB;QACD;YACE,SAAS,EAAE,UAAU;YACrB,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,aAAa;YACnB,KAAK,EAAE,IAAI;SACZ;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,QAAQ;YACd,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,MAAM;YACZ,QAAQ,EAAE,IAAI;SACf;QACD;YACE,SAAS,EAAE,MAAM;YACjB,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,IAAI;YACd,YAAY,EAAE,EAAE;SACjB;QACD;YACE,SAAS,EAAE,SAAS;YACpB,IAAI,EAAE,SAAS;YACf,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,gBAAgB;YACtB,YAAY,EAAE,IAAI;SACnB;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,OAAO;YACb,MAAM,EAAE,YAAY;YACpB,KAAK,EAAE,MAAM;SACd;QACD;YACE,SAAS,EAAE,QAAQ;YACnB,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,YAAY;YACpB,KAAK,EAAE,MAAM;SACd;KACF;CACmB,CAAC","sourcesContent":["import { CollectionOptions } from '@nocobase/database';\n\nexport default {\n name: 'workflows',\n model: 'WorkflowModel',\n title: '自动化',\n fields: [\n {\n interface: 'string',\n type: 'string',\n name: 'title',\n title: '工作流名称',\n required: true\n },\n {\n interface: 'boolean',\n type: 'boolean',\n name: 'enabled',\n title: '启用',\n defaultValue: false\n },\n {\n interface: 'textarea',\n type: 'text',\n name: 'description',\n title: '描述'\n },\n {\n interface: 'select',\n type: 'string',\n title: '触发方式',\n name: 'type',\n required: true\n },\n {\n interface: 'json',\n type: 'jsonb',\n title: '触发配置',\n name: 'config',\n required: true,\n defaultValue: {}\n },\n {\n interface: 'boolean',\n type: 'boolean',\n title: '使用事务',\n name: 'useTransaction',\n defaultValue: true\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'nodes',\n target: 'flow_nodes',\n title: '流程节点'\n },\n {\n interface: 'linkTo',\n type: 'hasMany',\n name: 'executions',\n target: 'executions',\n title: '触发执行'\n }\n ]\n} as CollectionOptions;\n"]}
@@ -32,7 +32,7 @@ export default class ExecutionModel extends Model {
32
32
  };
33
33
  makeNodes(nodes?: any[]): void;
34
34
  makeJobs(jobs: Array<JobModel>): void;
35
- getTransaction(): Transaction | Promise<Transaction>;
35
+ getTransaction(): Promise<Transaction>;
36
36
  prepare(options: any, commit?: boolean): Promise<void>;
37
37
  start(options: ExecutionOptions): Promise<void>;
38
38
  resume(job: JobModel, options: ExecutionOptions): Promise<void>;
@@ -48,21 +48,27 @@ class ExecutionModel extends database_1.Model {
48
48
  });
49
49
  }
50
50
  getTransaction() {
51
- const { sequelize } = this.constructor.database;
52
- // @ts-ignore
53
- if (!this.useTransaction) {
54
- return undefined;
55
- }
56
- const { options } = this;
57
- // @ts-ignore
58
- return options.transaction && !options.transaction.finished
59
- ? options.transaction
60
- : sequelize.transaction();
51
+ return __awaiter(this, void 0, void 0, function* () {
52
+ const { sequelize } = this.constructor.database;
53
+ if (!this.useTransaction) {
54
+ return undefined;
55
+ }
56
+ const { options } = this;
57
+ // @ts-ignore
58
+ const transaction = options.transaction && !options.transaction.finished
59
+ ? options.transaction
60
+ : sequelize.transaction();
61
+ // @ts-ignore
62
+ if (this.transaction !== transaction.id) {
63
+ // @ts-ignore
64
+ yield this.update({ transaction: transaction.id }, { transaction });
65
+ }
66
+ return transaction;
67
+ });
61
68
  }
62
69
  prepare(options, commit = false) {
63
70
  return __awaiter(this, void 0, void 0, function* () {
64
71
  this.options = options || {};
65
- // @ts-ignore
66
72
  const transaction = yield this.getTransaction();
67
73
  this.transaction = transaction;
68
74
  if (!this.workflow) {
@@ -1 +1 @@
1
- {"version":3,"file":"Execution.js","sourceRoot":"","sources":["../../src/models/Execution.ts"],"names":[],"mappings":"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","sourcesContent":["import { Database, Model } from '@nocobase/database';\nimport parse from 'json-templates';\nimport { BelongsToGetAssociationMixin, HasManyGetAssociationsMixin, Transaction } from 'sequelize';\nimport { EXECUTION_STATUS, JOB_STATUS } from '../constants';\nimport instructions from '../instructions';\nimport WorkflowModel from './Workflow';\nimport FlowNodeModel from './FlowNode';\nimport JobModel from './Job';\nimport calculators from '../calculators';\n\nexport interface ExecutionOptions {\n transaction?: Transaction;\n}\n\nexport default class ExecutionModel extends Model {\n declare static readonly database: Database;\n\n declare id: number;\n declare title: string;\n declare context: any;\n declare status: number;\n // NOTE: this duplicated column is for transaction in preparing cycle from workflow\n declare useTransaction: boolean;\n\n declare createdAt: Date;\n declare updatedAt: Date;\n\n declare workflow?: WorkflowModel;\n declare getWorkflow: BelongsToGetAssociationMixin<WorkflowModel>;\n\n declare jobs?: JobModel[];\n declare getJobs: HasManyGetAssociationsMixin<JobModel>;\n\n options: ExecutionOptions;\n transaction: Transaction;\n\n nodes: Array<FlowNodeModel> = [];\n nodesMap = new Map<number, FlowNodeModel>();\n jobsMap = new Map<number, JobModel>();\n jobsMapByNodeId: { [key: number]: any } = {};\n\n static StatusMap = {\n [JOB_STATUS.PENDING]: EXECUTION_STATUS.STARTED,\n [JOB_STATUS.RESOLVED]: EXECUTION_STATUS.RESOLVED,\n [JOB_STATUS.REJECTED]: EXECUTION_STATUS.REJECTED,\n [JOB_STATUS.CANCELLED]: EXECUTION_STATUS.CANCELLED,\n };\n\n // make dual linked nodes list then cache\n makeNodes(nodes = []) {\n this.nodes = nodes;\n\n nodes.forEach((node) => {\n this.nodesMap.set(node.id, node);\n });\n\n nodes.forEach((node) => {\n if (node.upstreamId) {\n node.upstream = this.nodesMap.get(node.upstreamId);\n }\n\n if (node.downstreamId) {\n node.downstream = this.nodesMap.get(node.downstreamId);\n }\n });\n }\n\n makeJobs(jobs: Array<JobModel>) {\n jobs.forEach((job) => {\n this.jobsMap.set(job.id, job);\n // TODO: should consider cycle, and from previous job\n this.jobsMapByNodeId[job.nodeId] = job.result;\n });\n }\n\n getTransaction() {\n const { sequelize } = (<typeof WorkflowModel>this.constructor).database;\n // @ts-ignore\n if (!this.useTransaction) {\n return undefined;\n }\n\n const { options } = this;\n\n // @ts-ignore\n return options.transaction && !options.transaction.finished\n ? options.transaction\n : sequelize.transaction();\n }\n\n async prepare(options, commit = false) {\n this.options = options || {};\n // @ts-ignore\n const transaction = await this.getTransaction()\n this.transaction = transaction;\n\n if (!this.workflow) {\n this.workflow = await this.getWorkflow({ transaction });\n }\n\n const nodes = await this.workflow.getNodes({ transaction });\n\n this.makeNodes(nodes);\n\n const jobs = await this.getJobs({\n order: [['id', 'ASC']],\n transaction,\n });\n\n this.makeJobs(jobs);\n\n if (commit) {\n await this.commit();\n }\n }\n\n public async start(options: ExecutionOptions) {\n if (this.status !== EXECUTION_STATUS.STARTED) {\n throw new Error(`execution was ended with status ${this.status}`);\n }\n await this.prepare(options);\n if (this.nodes.length) {\n const head = this.nodes.find(item => !item.upstream);\n await this.run(head, { result: this.context });\n } else {\n await this.exit(null);\n }\n await this.commit();\n }\n\n public async resume(job: JobModel, options: ExecutionOptions) {\n if (this.status !== EXECUTION_STATUS.STARTED) {\n throw new Error(`execution was ended with status ${this.status}`);\n }\n await this.prepare(options);\n const node = this.nodesMap.get(job.nodeId);\n await this.recall(node, job);\n await this.commit();\n }\n\n private async commit() {\n // @ts-ignore\n if (this.transaction && (!this.options.transaction || this.options.transaction.finished)) {\n await this.transaction.commit();\n }\n }\n\n private async exec(instruction: Function, node: FlowNodeModel, prevJob) {\n let job;\n try {\n // call instruction to get result and status\n job = await instruction.call(node, prevJob, this);\n if (!job) {\n return null;\n }\n } catch (err) {\n // for uncaught error, set to rejected\n job = {\n result: err instanceof Error\n ? { message: err.message, stack: process.env.NODE_ENV === 'production' ? [] : err.stack }\n : err,\n status: JOB_STATUS.REJECTED,\n };\n // if previous job is from resuming\n if (prevJob && prevJob.nodeId === node.id) {\n prevJob.set(job);\n job = prevJob;\n }\n }\n\n let savedJob;\n // TODO(optimize): many checking of resuming or new could be improved\n // could be implemented separately in exec() / resume()\n if (job instanceof Model) {\n savedJob = (await job.save({ transaction: this.transaction })) as unknown as JobModel;\n } else {\n const upstreamId = prevJob instanceof Model ? prevJob.get('id') : null;\n savedJob = await this.saveJob({\n nodeId: node.id,\n upstreamId,\n ...job,\n });\n }\n\n if (savedJob.status === JOB_STATUS.RESOLVED && node.downstream) {\n // run next node\n return this.run(node.downstream, savedJob);\n }\n\n // all nodes in scope have been executed\n return this.end(node, savedJob);\n }\n\n public async run(node, input?) {\n const { run } = instructions.get(node.type);\n if (typeof run !== 'function') {\n return Promise.reject(new Error('`run` should be implemented for customized execution of the node'));\n }\n\n return this.exec(run, node, input);\n }\n\n // parent node should take over the control\n public end(node, job) {\n const parentNode = this.findBranchParentNode(node);\n // no parent, means on main flow\n if (parentNode) {\n return this.recall(parentNode, job);\n }\n\n // really done for all nodes\n // * should mark execution as done with last job status\n return this.exit(job);\n }\n\n async recall(node, job) {\n const { resume } = instructions.get(node.type);\n if (typeof resume !== 'function') {\n return Promise.reject(new Error('`resume` should be implemented because the node made branch'));\n }\n\n return this.exec(resume, node, job);\n }\n\n async exit(job: JobModel | null) {\n const status = job ? ExecutionModel.StatusMap[job.status] : EXECUTION_STATUS.RESOLVED;\n await this.update({ status }, { transaction: this.transaction });\n return null;\n }\n\n // TODO(optimize)\n async saveJob(payload) {\n const { database } = <typeof WorkflowModel>this.constructor;\n const { model } = database.getCollection('jobs');\n const [job] = (await model.upsert(\n {\n ...payload,\n executionId: this.id,\n },\n { transaction: this.transaction },\n )) as unknown as [JobModel, boolean | null];\n this.jobsMap.set(job.id, job);\n this.jobsMapByNodeId[job.nodeId] = job.result;\n\n return job;\n }\n\n // find the first node in current branch\n findBranchStartNode(node: FlowNodeModel): FlowNodeModel | null {\n for (let n = node; n; n = n.upstream) {\n if (n.branchIndex !== null) {\n return n;\n }\n }\n return null;\n }\n\n // find the node start current branch\n findBranchParentNode(node: FlowNodeModel): FlowNodeModel | null {\n for (let n = node; n; n = n.upstream) {\n if (n.branchIndex !== null) {\n return n.upstream;\n }\n }\n return null;\n }\n\n findBranchParentJob(job: JobModel, node: FlowNodeModel): JobModel | null {\n for (let j = job; j; j = this.jobsMap.get(j.upstreamId)) {\n if (j.nodeId === node.id) {\n return j;\n }\n }\n return null;\n }\n\n public getParsedValue(value, node?) {\n const injectedFns = {};\n const scope = {\n execution: this,\n node\n };\n for (let [name, fn] of calculators.getEntities()) {\n injectedFns[name] = fn.bind(scope);\n }\n\n return parse(value)({\n $context: this.context,\n $jobsMapByNodeId: this.jobsMapByNodeId,\n $fn: injectedFns\n });\n }\n}\n"]}
1
+ {"version":3,"file":"Execution.js","sourceRoot":"","sources":["../../src/models/Execution.ts"],"names":[],"mappings":"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","sourcesContent":["import { Database, Model } from '@nocobase/database';\nimport parse from 'json-templates';\nimport { BelongsToGetAssociationMixin, HasManyGetAssociationsMixin, Transaction } from 'sequelize';\nimport { EXECUTION_STATUS, JOB_STATUS } from '../constants';\nimport instructions from '../instructions';\nimport WorkflowModel from './Workflow';\nimport FlowNodeModel from './FlowNode';\nimport JobModel from './Job';\nimport calculators from '../calculators';\n\nexport interface ExecutionOptions {\n transaction?: Transaction;\n}\n\nexport default class ExecutionModel extends Model {\n declare static readonly database: Database;\n\n declare id: number;\n declare title: string;\n declare context: any;\n declare status: number;\n // NOTE: this duplicated column is for transaction in preparing cycle from workflow\n declare useTransaction: boolean;\n\n declare createdAt: Date;\n declare updatedAt: Date;\n\n declare workflow?: WorkflowModel;\n declare getWorkflow: BelongsToGetAssociationMixin<WorkflowModel>;\n\n declare jobs?: JobModel[];\n declare getJobs: HasManyGetAssociationsMixin<JobModel>;\n\n options: ExecutionOptions;\n transaction: Transaction;\n\n nodes: Array<FlowNodeModel> = [];\n nodesMap = new Map<number, FlowNodeModel>();\n jobsMap = new Map<number, JobModel>();\n jobsMapByNodeId: { [key: number]: any } = {};\n\n static StatusMap = {\n [JOB_STATUS.PENDING]: EXECUTION_STATUS.STARTED,\n [JOB_STATUS.RESOLVED]: EXECUTION_STATUS.RESOLVED,\n [JOB_STATUS.REJECTED]: EXECUTION_STATUS.REJECTED,\n [JOB_STATUS.CANCELLED]: EXECUTION_STATUS.CANCELLED,\n };\n\n // make dual linked nodes list then cache\n makeNodes(nodes = []) {\n this.nodes = nodes;\n\n nodes.forEach((node) => {\n this.nodesMap.set(node.id, node);\n });\n\n nodes.forEach((node) => {\n if (node.upstreamId) {\n node.upstream = this.nodesMap.get(node.upstreamId);\n }\n\n if (node.downstreamId) {\n node.downstream = this.nodesMap.get(node.downstreamId);\n }\n });\n }\n\n makeJobs(jobs: Array<JobModel>) {\n jobs.forEach((job) => {\n this.jobsMap.set(job.id, job);\n // TODO: should consider cycle, and from previous job\n this.jobsMapByNodeId[job.nodeId] = job.result;\n });\n }\n\n async getTransaction() {\n const { sequelize } = (<typeof ExecutionModel>this.constructor).database;\n\n if (!this.useTransaction) {\n return undefined;\n }\n\n const { options } = this;\n\n // @ts-ignore\n const transaction = options.transaction && !options.transaction.finished\n ? options.transaction\n : sequelize.transaction();\n\n // @ts-ignore\n if (this.transaction !== transaction.id) {\n // @ts-ignore\n await this.update({ transaction: transaction.id }, { transaction });\n }\n return transaction;\n }\n\n async prepare(options, commit = false) {\n this.options = options || {};\n const transaction = await this.getTransaction();\n this.transaction = transaction;\n\n if (!this.workflow) {\n this.workflow = await this.getWorkflow({ transaction });\n }\n\n const nodes = await this.workflow.getNodes({ transaction });\n\n this.makeNodes(nodes);\n\n const jobs = await this.getJobs({\n order: [['id', 'ASC']],\n transaction,\n });\n\n this.makeJobs(jobs);\n\n if (commit) {\n await this.commit();\n }\n }\n\n public async start(options: ExecutionOptions) {\n if (this.status !== EXECUTION_STATUS.STARTED) {\n throw new Error(`execution was ended with status ${this.status}`);\n }\n await this.prepare(options);\n if (this.nodes.length) {\n const head = this.nodes.find(item => !item.upstream);\n await this.run(head, { result: this.context });\n } else {\n await this.exit(null);\n }\n await this.commit();\n }\n\n public async resume(job: JobModel, options: ExecutionOptions) {\n if (this.status !== EXECUTION_STATUS.STARTED) {\n throw new Error(`execution was ended with status ${this.status}`);\n }\n await this.prepare(options);\n const node = this.nodesMap.get(job.nodeId);\n await this.recall(node, job);\n await this.commit();\n }\n\n private async commit() {\n // @ts-ignore\n if (this.transaction && (!this.options.transaction || this.options.transaction.finished)) {\n await this.transaction.commit();\n }\n }\n\n private async exec(instruction: Function, node: FlowNodeModel, prevJob) {\n let job;\n try {\n // call instruction to get result and status\n job = await instruction.call(node, prevJob, this);\n if (!job) {\n return null;\n }\n } catch (err) {\n // for uncaught error, set to rejected\n job = {\n result: err instanceof Error\n ? { message: err.message, stack: process.env.NODE_ENV === 'production' ? [] : err.stack }\n : err,\n status: JOB_STATUS.REJECTED,\n };\n // if previous job is from resuming\n if (prevJob && prevJob.nodeId === node.id) {\n prevJob.set(job);\n job = prevJob;\n }\n }\n\n let savedJob;\n // TODO(optimize): many checking of resuming or new could be improved\n // could be implemented separately in exec() / resume()\n if (job instanceof Model) {\n savedJob = (await job.save({ transaction: this.transaction })) as unknown as JobModel;\n } else {\n const upstreamId = prevJob instanceof Model ? prevJob.get('id') : null;\n savedJob = await this.saveJob({\n nodeId: node.id,\n upstreamId,\n ...job,\n });\n }\n\n if (savedJob.status === JOB_STATUS.RESOLVED && node.downstream) {\n // run next node\n return this.run(node.downstream, savedJob);\n }\n\n // all nodes in scope have been executed\n return this.end(node, savedJob);\n }\n\n public async run(node, input?) {\n const { run } = instructions.get(node.type);\n if (typeof run !== 'function') {\n return Promise.reject(new Error('`run` should be implemented for customized execution of the node'));\n }\n\n return this.exec(run, node, input);\n }\n\n // parent node should take over the control\n public end(node, job) {\n const parentNode = this.findBranchParentNode(node);\n // no parent, means on main flow\n if (parentNode) {\n return this.recall(parentNode, job);\n }\n\n // really done for all nodes\n // * should mark execution as done with last job status\n return this.exit(job);\n }\n\n async recall(node, job) {\n const { resume } = instructions.get(node.type);\n if (typeof resume !== 'function') {\n return Promise.reject(new Error('`resume` should be implemented because the node made branch'));\n }\n\n return this.exec(resume, node, job);\n }\n\n async exit(job: JobModel | null) {\n const status = job ? ExecutionModel.StatusMap[job.status] : EXECUTION_STATUS.RESOLVED;\n await this.update({ status }, { transaction: this.transaction });\n return null;\n }\n\n // TODO(optimize)\n async saveJob(payload) {\n const { database } = <typeof ExecutionModel>this.constructor;\n const { model } = database.getCollection('jobs');\n const [job] = (await model.upsert(\n {\n ...payload,\n executionId: this.id,\n },\n { transaction: this.transaction },\n )) as unknown as [JobModel, boolean | null];\n this.jobsMap.set(job.id, job);\n this.jobsMapByNodeId[job.nodeId] = job.result;\n\n return job;\n }\n\n // find the first node in current branch\n findBranchStartNode(node: FlowNodeModel): FlowNodeModel | null {\n for (let n = node; n; n = n.upstream) {\n if (n.branchIndex !== null) {\n return n;\n }\n }\n return null;\n }\n\n // find the node start current branch\n findBranchParentNode(node: FlowNodeModel): FlowNodeModel | null {\n for (let n = node; n; n = n.upstream) {\n if (n.branchIndex !== null) {\n return n.upstream;\n }\n }\n return null;\n }\n\n findBranchParentJob(job: JobModel, node: FlowNodeModel): JobModel | null {\n for (let j = job; j; j = this.jobsMap.get(j.upstreamId)) {\n if (j.nodeId === node.id) {\n return j;\n }\n }\n return null;\n }\n\n public getParsedValue(value, node?) {\n const injectedFns = {};\n const scope = {\n execution: this,\n node\n };\n for (let [name, fn] of calculators.getEntities()) {\n injectedFns[name] = fn.bind(scope);\n }\n\n return parse(value)({\n $context: this.context,\n $jobsMapByNodeId: this.jobsMapByNodeId,\n $fn: injectedFns\n });\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { Database, Model } from '@nocobase/database';
2
- import { HasManyCreateAssociationMixin, HasManyGetAssociationsMixin } from 'sequelize';
2
+ import { HasManyCountAssociationsMixin, HasManyCreateAssociationMixin, HasManyGetAssociationsMixin } from 'sequelize';
3
3
  import ExecutionModel from './Execution';
4
4
  import FlowNodeModel from './FlowNode';
5
5
  export default class WorkflowModel extends Model {
@@ -17,6 +17,7 @@ export default class WorkflowModel extends Model {
17
17
  getNodes: HasManyGetAssociationsMixin<FlowNodeModel>;
18
18
  createNode: HasManyCreateAssociationMixin<FlowNodeModel>;
19
19
  executions: ExecutionModel[];
20
+ countExecutions: HasManyCountAssociationsMixin;
20
21
  getExecutions: HasManyGetAssociationsMixin<ExecutionModel>;
21
22
  createExecution: HasManyCreateAssociationMixin<ExecutionModel>;
22
23
  static mount(): Promise<void>;
@@ -60,10 +60,23 @@ class WorkflowModel extends database_1.Model {
60
60
  return;
61
61
  }
62
62
  const transaction = yield this.getTransaction(options);
63
+ if (this.useTransaction) {
64
+ const existed = yield this.countExecutions({
65
+ where: {
66
+ transaction: transaction.id
67
+ },
68
+ transaction
69
+ });
70
+ if (existed) {
71
+ console.warn(`workflow ${this.id} has already been triggered in same execution (${transaction.id}), and newly triggering will be skipped.`);
72
+ return;
73
+ }
74
+ }
63
75
  const execution = yield this.createExecution({
64
76
  context,
65
77
  status: constants_1.EXECUTION_STATUS.STARTED,
66
- useTransaction: this.useTransaction
78
+ useTransaction: this.useTransaction,
79
+ transaction: transaction.id
67
80
  }, { transaction });
68
81
  execution.workflow = this;
69
82
  yield execution.start({ transaction });
@@ -1 +1 @@
1
- {"version":3,"file":"Workflow.js","sourceRoot":"","sources":["../../src/models/Workflow.ts"],"names":[],"mappings":"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","sourcesContent":["import { Database, Model } from '@nocobase/database';\nimport { HasManyCreateAssociationMixin, HasManyGetAssociationsMixin } from 'sequelize';\n\nimport triggers from '../triggers';\nimport { EXECUTION_STATUS } from '../constants';\nimport ExecutionModel from './Execution';\nimport FlowNodeModel from './FlowNode';\n\nexport default class WorkflowModel extends Model {\n declare static database: Database;\n\n declare id: number;\n declare title: string;\n declare enabled: boolean;\n declare description?: string;\n declare type: string;\n declare config: any;\n declare useTransaction: boolean;\n\n declare createdAt: Date;\n declare updatedAt: Date;\n\n declare nodes: FlowNodeModel[];\n declare getNodes: HasManyGetAssociationsMixin<FlowNodeModel>;\n declare createNode: HasManyCreateAssociationMixin<FlowNodeModel>;\n\n declare executions: ExecutionModel[];\n declare getExecutions: HasManyGetAssociationsMixin<ExecutionModel>;\n declare createExecution: HasManyCreateAssociationMixin<ExecutionModel>;\n\n static async mount() {\n const collection = this.database.getCollection('workflows');\n const workflows = await collection.repository.find({\n filter: { enabled: true },\n });\n\n workflows.forEach((workflow: WorkflowModel) => {\n workflow.toggle();\n });\n\n this.addHook('afterCreate', (model: WorkflowModel) => model.toggle());\n this.addHook('afterUpdate', (model: WorkflowModel) => model.toggle());\n this.addHook('afterDestroy', (model: WorkflowModel) => model.toggle(false));\n }\n\n getHookId() {\n return `workflow-${this.get('id')}`;\n }\n\n getTransaction(options) {\n if (!this.useTransaction) {\n return undefined;\n }\n\n return options.transaction && !options.transaction.finished\n ? options.transaction\n : (<typeof WorkflowModel>this.constructor).database.sequelize.transaction();\n }\n\n async toggle(enable?: boolean) {\n const type = this.get('type');\n const { on, off } = triggers.get(type);\n if (typeof enable !== 'undefined' ? enable : this.get('enabled')) {\n on.call(this, this.trigger.bind(this));\n } else {\n off.call(this);\n }\n }\n\n async trigger(context: Object, options) {\n // `null` means not to trigger\n if (context === null) {\n return;\n }\n\n const transaction = await this.getTransaction(options);\n\n const execution = await this.createExecution({\n context,\n status: EXECUTION_STATUS.STARTED,\n useTransaction: this.useTransaction\n }, { transaction });\n\n execution.workflow = this;\n\n await execution.start({ transaction });\n\n if (transaction && (!options.transaction || options.transaction.finished)) {\n await transaction.commit();\n }\n\n return execution;\n }\n}\n"]}
1
+ {"version":3,"file":"Workflow.js","sourceRoot":"","sources":["../../src/models/Workflow.ts"],"names":[],"mappings":"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","sourcesContent":["import { Database, Model } from '@nocobase/database';\nimport { HasManyCountAssociationsMixin, HasManyCreateAssociationMixin, HasManyGetAssociationsMixin } from 'sequelize';\n\nimport triggers from '../triggers';\nimport { EXECUTION_STATUS } from '../constants';\nimport ExecutionModel from './Execution';\nimport FlowNodeModel from './FlowNode';\n\nexport default class WorkflowModel extends Model {\n declare static database: Database;\n\n declare id: number;\n declare title: string;\n declare enabled: boolean;\n declare description?: string;\n declare type: string;\n declare config: any;\n declare useTransaction: boolean;\n\n declare createdAt: Date;\n declare updatedAt: Date;\n\n declare nodes: FlowNodeModel[];\n declare getNodes: HasManyGetAssociationsMixin<FlowNodeModel>;\n declare createNode: HasManyCreateAssociationMixin<FlowNodeModel>;\n\n declare executions: ExecutionModel[];\n declare countExecutions: HasManyCountAssociationsMixin;\n declare getExecutions: HasManyGetAssociationsMixin<ExecutionModel>;\n declare createExecution: HasManyCreateAssociationMixin<ExecutionModel>;\n\n static async mount() {\n const collection = this.database.getCollection('workflows');\n const workflows = await collection.repository.find({\n filter: { enabled: true },\n });\n\n workflows.forEach((workflow: WorkflowModel) => {\n workflow.toggle();\n });\n\n this.addHook('afterCreate', (model: WorkflowModel) => model.toggle());\n this.addHook('afterUpdate', (model: WorkflowModel) => model.toggle());\n this.addHook('afterDestroy', (model: WorkflowModel) => model.toggle(false));\n }\n\n getHookId() {\n return `workflow-${this.get('id')}`;\n }\n\n getTransaction(options) {\n if (!this.useTransaction) {\n return undefined;\n }\n\n return options.transaction && !options.transaction.finished\n ? options.transaction\n : (<typeof WorkflowModel>this.constructor).database.sequelize.transaction();\n }\n\n async toggle(enable?: boolean) {\n const type = this.get('type');\n const { on, off } = triggers.get(type);\n if (typeof enable !== 'undefined' ? enable : this.get('enabled')) {\n on.call(this, this.trigger.bind(this));\n } else {\n off.call(this);\n }\n }\n\n async trigger(context: Object, options) {\n // `null` means not to trigger\n if (context === null) {\n return;\n }\n\n const transaction = await this.getTransaction(options);\n\n if (this.useTransaction) {\n const existed = await this.countExecutions({\n where: {\n transaction: transaction.id\n },\n transaction\n });\n\n if (existed) {\n console.warn(`workflow ${this.id} has already been triggered in same execution (${transaction.id}), and newly triggering will be skipped.`);\n return;\n }\n }\n\n const execution = await this.createExecution({\n context,\n status: EXECUTION_STATUS.STARTED,\n useTransaction: this.useTransaction,\n transaction: transaction.id\n }, { transaction });\n\n execution.workflow = this;\n\n await execution.start({ transaction });\n\n if (transaction && (!options.transaction || options.transaction.finished)) {\n await transaction.commit();\n }\n\n return execution;\n }\n}\n"]}
File without changes
@@ -26,18 +26,23 @@ function bindHandler(callback) {
26
26
  };
27
27
  }
28
28
  exports.default = {
29
- name: 'model',
29
+ name: 'collection',
30
30
  on(callback) {
31
+ var _a;
31
32
  const { database } = this.constructor;
32
33
  const { collection, mode } = this.config;
33
34
  const Collection = database.getCollection(collection);
34
35
  if (!Collection) {
35
36
  return;
36
37
  }
37
- // TODO: duplication when mode change should be considered
38
38
  for (let [key, event] of MODE_BITMAP_EVENTS.entries()) {
39
39
  if (mode & key) {
40
- Collection.model.addHook(event, this.getHookId(), bindHandler.call(this, callback));
40
+ if (!((_a = Collection.model.options.hooks[event]) === null || _a === void 0 ? void 0 : _a.find(item => item.name && item.name === this.getHookId()))) {
41
+ Collection.model.addHook(event, this.getHookId(), bindHandler.call(this, callback));
42
+ }
43
+ }
44
+ else {
45
+ Collection.model.removeHook(event, this.getHookId());
41
46
  }
42
47
  }
43
48
  },
@@ -55,4 +60,4 @@ exports.default = {
55
60
  }
56
61
  }
57
62
  };
58
- //# sourceMappingURL=model.js.map
63
+ //# sourceMappingURL=collection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collection.js","sourceRoot":"","sources":["../../src/triggers/collection.ts"],"names":[],"mappings":"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","sourcesContent":["import WorkflowModel from \"../models/Workflow\";\n\nexport interface ModelChangeTriggerConfig {\n collection: string;\n mode: number;\n // TODO: ICondition\n condition: any;\n}\n\nconst MODE_BITMAP = {\n CREATE: 1,\n UPDATE: 2,\n DESTROY: 4\n};\n\nconst MODE_BITMAP_EVENTS = new Map();\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.CREATE, 'afterCreate');\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.UPDATE, 'afterUpdate');\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.DESTROY, 'afterDestroy');\n\n// async function, should return promise\nfunction bindHandler(this: WorkflowModel, callback: Function) {\n const { condition, changed } = this.config;\n return (data: any, options) => {\n // NOTE: if no configured fields changed, do not trigger\n if (changed && changed.length && changed.every(name => !data.changed(name))) {\n return;\n }\n // NOTE: if no configured condition match, do not trigger\n if (condition && condition.$and.length) {\n // TODO: check all conditions in condition against data\n // const calculation = toCalculation(condition);\n }\n\n return callback({ data: data.get() }, options);\n };\n}\n\nexport default {\n name: 'collection',\n on(this: WorkflowModel, callback: Function) {\n const { database } = <typeof WorkflowModel>this.constructor;\n const { collection, mode } = this.config;\n const Collection = database.getCollection(collection);\n if (!Collection) {\n return;\n }\n\n for (let [key, event] of MODE_BITMAP_EVENTS.entries()) {\n if (mode & key) {\n if (!Collection.model.options.hooks[event]?.find(item => item.name && item.name === this.getHookId())) {\n Collection.model.addHook(event, this.getHookId(), bindHandler.call(this, callback));\n }\n } else {\n Collection.model.removeHook(event, this.getHookId());\n }\n }\n },\n off(this: WorkflowModel) {\n const { database } = <typeof WorkflowModel>this.constructor;\n const { collection, mode } = this.config;\n const Collection = database.getCollection(collection);\n if (!Collection) {\n return;\n }\n for (let [key, event] of MODE_BITMAP_EVENTS.entries()) {\n if (mode & key) {\n Collection.model.removeHook(event, this.getHookId());\n }\n }\n }\n}\n"]}
@@ -5,8 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.triggers = void 0;
7
7
  const utils_1 = require("@nocobase/utils");
8
- const model_1 = __importDefault(require("./model"));
8
+ const collection_1 = __importDefault(require("./collection"));
9
9
  exports.triggers = new utils_1.Registry();
10
10
  exports.default = exports.triggers;
11
- exports.triggers.register(model_1.default.name, model_1.default);
11
+ exports.triggers.register(collection_1.default.name, collection_1.default);
12
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/triggers/index.ts"],"names":[],"mappings":";;;;;;AAAA,2CAA2C;AAE3C,oDAAmC;AAQtB,QAAA,QAAQ,GAAG,IAAI,gBAAQ,EAAW,CAAC;AAEhD,kBAAe,gBAAQ,CAAC;AAExB,gBAAQ,CAAC,QAAQ,CAAC,eAAY,CAAC,IAAI,EAAE,eAAY,CAAC,CAAC","sourcesContent":["import { Registry } from '@nocobase/utils';\nimport WorkflowModel from '../models/Workflow';\nimport modelTrigger from './model';\n\nexport interface Trigger {\n name: string;\n on(this: WorkflowModel, callback: Function): void;\n off(this: WorkflowModel): void;\n}\n\nexport const triggers = new Registry<Trigger>();\n\nexport default triggers;\n\ntriggers.register(modelTrigger.name, modelTrigger);\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/triggers/index.ts"],"names":[],"mappings":";;;;;;AAAA,2CAA2C;AAE3C,8DAA8C;AAQjC,QAAA,QAAQ,GAAG,IAAI,gBAAQ,EAAW,CAAC;AAEhD,kBAAe,gBAAQ,CAAC;AAExB,gBAAQ,CAAC,QAAQ,CAAC,oBAAkB,CAAC,IAAI,EAAE,oBAAkB,CAAC,CAAC","sourcesContent":["import { Registry } from '@nocobase/utils';\nimport WorkflowModel from '../models/Workflow';\nimport collectionlTrigger from './collection';\n\nexport interface Trigger {\n name: string;\n on(this: WorkflowModel, callback: Function): void;\n off(this: WorkflowModel): void;\n}\n\nexport const triggers = new Registry<Trigger>();\n\nexport default triggers;\n\ntriggers.register(collectionlTrigger.name, collectionlTrigger);\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nocobase/plugin-workflow",
3
- "version": "0.7.0-alpha.25",
3
+ "version": "0.7.0-alpha.28",
4
4
  "main": "lib/index.js",
5
5
  "license": "Apache-2.0",
6
6
  "licenses": [
@@ -15,14 +15,14 @@
15
15
  "build:esm": "tsc --project tsconfig.build.json --module es2015 --outDir esm"
16
16
  },
17
17
  "dependencies": {
18
- "@nocobase/actions": "0.7.0-alpha.25",
19
- "@nocobase/database": "0.7.0-alpha.25",
20
- "@nocobase/server": "0.7.0-alpha.25",
21
- "@nocobase/utils": "0.7.0-alpha.25",
18
+ "@nocobase/actions": "0.7.0-alpha.28",
19
+ "@nocobase/database": "0.7.0-alpha.28",
20
+ "@nocobase/server": "0.7.0-alpha.28",
21
+ "@nocobase/utils": "0.7.0-alpha.28",
22
22
  "json-templates": "^4.2.0"
23
23
  },
24
24
  "devDependencies": {
25
- "@nocobase/test": "0.7.0-alpha.25"
25
+ "@nocobase/test": "0.7.0-alpha.28"
26
26
  },
27
- "gitHead": "b292a36c83a4c4c8cc8c9ebcca15d60b656f2e31"
27
+ "gitHead": "a48d00492ebc34c66c63d9644530c5b8a7c9914a"
28
28
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/triggers/model.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,GAAG;IAClB,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,CAAC;CACX,CAAC;AAEF,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAE,CAAC;AACrC,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC1D,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC1D,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;AAE5D,wCAAwC;AACxC,SAAS,WAAW,CAAsB,QAAkB;IAC1D,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3C,OAAO,CAAC,IAAS,EAAE,OAAO,EAAE,EAAE;QAC5B,wDAAwD;QACxD,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE;YAC3E,OAAO;SACR;QACD,yDAAyD;QACzD,IAAI,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE;YACtC,uDAAuD;YACvD,gDAAgD;SACjD;QAED,OAAO,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC,CAAC;AACJ,CAAC;AAED,eAAe;IACb,IAAI,EAAE,OAAO;IACb,EAAE,CAAsB,QAAkB;QACxC,MAAM,EAAE,QAAQ,EAAE,GAAyB,IAAI,CAAC,WAAW,CAAC;QAC5D,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QACzC,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACtD,IAAI,CAAC,UAAU,EAAE;YACf,OAAO;SACR;QACD,0DAA0D;QAC1D,KAAK,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,kBAAkB,CAAC,OAAO,EAAE,EAAE;YACrD,IAAI,IAAI,GAAG,GAAG,EAAE;gBACd,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;aACrF;SACF;IACH,CAAC;IACD,GAAG;QACD,MAAM,EAAE,QAAQ,EAAE,GAAyB,IAAI,CAAC,WAAW,CAAC;QAC5D,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QACzC,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACtD,IAAI,CAAC,UAAU,EAAE;YACf,OAAO;SACR;QACD,KAAK,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,kBAAkB,CAAC,OAAO,EAAE,EAAE;YACrD,IAAI,IAAI,GAAG,GAAG,EAAE;gBACd,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;aACtD;SACF;IACH,CAAC;CACF,CAAA","sourcesContent":["import WorkflowModel from \"../models/Workflow\";\n\nexport interface ModelChangeTriggerConfig {\n collection: string;\n mode: number;\n // TODO: ICondition\n condition: any;\n}\n\nconst MODE_BITMAP = {\n CREATE: 1,\n UPDATE: 2,\n DESTROY: 4\n};\n\nconst MODE_BITMAP_EVENTS = new Map();\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.CREATE, 'afterCreate');\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.UPDATE, 'afterUpdate');\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.DESTROY, 'afterDestroy');\n\n// async function, should return promise\nfunction bindHandler(this: WorkflowModel, callback: Function) {\n const { condition, changed } = this.config;\n return (data: any, options) => {\n // NOTE: if no configured fields changed, do not trigger\n if (changed && changed.length && changed.every(name => !data.changed(name))) {\n return;\n }\n // NOTE: if no configured condition match, do not trigger\n if (condition && condition.$and.length) {\n // TODO: check all conditions in condition against data\n // const calculation = toCalculation(condition);\n }\n\n return callback({ data: data.get() }, options);\n };\n}\n\nexport default {\n name: 'model',\n on(this: WorkflowModel, callback: Function) {\n const { database } = <typeof WorkflowModel>this.constructor;\n const { collection, mode } = this.config;\n const Collection = database.getCollection(collection);\n if (!Collection) {\n return;\n }\n // TODO: duplication when mode change should be considered\n for (let [key, event] of MODE_BITMAP_EVENTS.entries()) {\n if (mode & key) {\n Collection.model.addHook(event, this.getHookId(), bindHandler.call(this, callback));\n }\n }\n },\n off(this: WorkflowModel) {\n const { database } = <typeof WorkflowModel>this.constructor;\n const { collection, mode } = this.config;\n const Collection = database.getCollection(collection);\n if (!Collection) {\n return;\n }\n for (let [key, event] of MODE_BITMAP_EVENTS.entries()) {\n if (mode & key) {\n Collection.model.removeHook(event, this.getHookId());\n }\n }\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/triggers/model.ts"],"names":[],"mappings":";;AASA,MAAM,WAAW,GAAG;IAClB,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,CAAC;IACT,OAAO,EAAE,CAAC;CACX,CAAC;AAEF,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAE,CAAC;AACrC,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC1D,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAC1D,kBAAkB,CAAC,GAAG,CAAC,WAAW,CAAC,OAAO,EAAE,cAAc,CAAC,CAAC;AAE5D,wCAAwC;AACxC,SAAS,WAAW,CAAsB,QAAkB;IAC1D,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;IAC3C,OAAO,CAAC,IAAS,EAAE,OAAO,EAAE,EAAE;QAC5B,wDAAwD;QACxD,IAAI,OAAO,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE;YAC3E,OAAO;SACR;QACD,yDAAyD;QACzD,IAAI,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE;YACtC,uDAAuD;YACvD,gDAAgD;SACjD;QAED,OAAO,QAAQ,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;IACjD,CAAC,CAAC;AACJ,CAAC;AAED,kBAAe;IACb,IAAI,EAAE,OAAO;IACb,EAAE,CAAsB,QAAkB;QACxC,MAAM,EAAE,QAAQ,EAAE,GAAyB,IAAI,CAAC,WAAW,CAAC;QAC5D,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QACzC,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACtD,IAAI,CAAC,UAAU,EAAE;YACf,OAAO;SACR;QACD,0DAA0D;QAC1D,KAAK,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,kBAAkB,CAAC,OAAO,EAAE,EAAE;YACrD,IAAI,IAAI,GAAG,GAAG,EAAE;gBACd,UAAU,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;aACrF;SACF;IACH,CAAC;IACD,GAAG;QACD,MAAM,EAAE,QAAQ,EAAE,GAAyB,IAAI,CAAC,WAAW,CAAC;QAC5D,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC;QACzC,MAAM,UAAU,GAAG,QAAQ,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;QACtD,IAAI,CAAC,UAAU,EAAE;YACf,OAAO;SACR;QACD,KAAK,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,kBAAkB,CAAC,OAAO,EAAE,EAAE;YACrD,IAAI,IAAI,GAAG,GAAG,EAAE;gBACd,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;aACtD;SACF;IACH,CAAC;CACF,CAAA","sourcesContent":["import WorkflowModel from \"../models/Workflow\";\n\nexport interface ModelChangeTriggerConfig {\n collection: string;\n mode: number;\n // TODO: ICondition\n condition: any;\n}\n\nconst MODE_BITMAP = {\n CREATE: 1,\n UPDATE: 2,\n DESTROY: 4\n};\n\nconst MODE_BITMAP_EVENTS = new Map();\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.CREATE, 'afterCreate');\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.UPDATE, 'afterUpdate');\nMODE_BITMAP_EVENTS.set(MODE_BITMAP.DESTROY, 'afterDestroy');\n\n// async function, should return promise\nfunction bindHandler(this: WorkflowModel, callback: Function) {\n const { condition, changed } = this.config;\n return (data: any, options) => {\n // NOTE: if no configured fields changed, do not trigger\n if (changed && changed.length && changed.every(name => !data.changed(name))) {\n return;\n }\n // NOTE: if no configured condition match, do not trigger\n if (condition && condition.$and.length) {\n // TODO: check all conditions in condition against data\n // const calculation = toCalculation(condition);\n }\n\n return callback({ data: data.get() }, options);\n };\n}\n\nexport default {\n name: 'model',\n on(this: WorkflowModel, callback: Function) {\n const { database } = <typeof WorkflowModel>this.constructor;\n const { collection, mode } = this.config;\n const Collection = database.getCollection(collection);\n if (!Collection) {\n return;\n }\n // TODO: duplication when mode change should be considered\n for (let [key, event] of MODE_BITMAP_EVENTS.entries()) {\n if (mode & key) {\n Collection.model.addHook(event, this.getHookId(), bindHandler.call(this, callback));\n }\n }\n },\n off(this: WorkflowModel) {\n const { database } = <typeof WorkflowModel>this.constructor;\n const { collection, mode } = this.config;\n const Collection = database.getCollection(collection);\n if (!Collection) {\n return;\n }\n for (let [key, event] of MODE_BITMAP_EVENTS.entries()) {\n if (mode & key) {\n Collection.model.removeHook(event, this.getHookId());\n }\n }\n }\n}\n"]}