@nocobase/plugin-workflow 0.10.1-alpha.1 → 0.11.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/client.d.ts +2 -3
  2. package/client.js +1 -30
  3. package/lib/client/AddButton.js +1 -8
  4. package/lib/client/Branch.js +8 -8
  5. package/lib/client/CanvasContent.js +8 -8
  6. package/lib/client/ExecutionCanvas.js +20 -27
  7. package/lib/client/ExecutionPage.js +1 -8
  8. package/lib/client/WorkflowCanvas.js +3 -10
  9. package/lib/client/WorkflowPage.js +1 -8
  10. package/lib/client/WorkflowProvider.js +3 -42
  11. package/lib/client/components/CollectionFieldset.d.ts +1 -1
  12. package/lib/client/components/CollectionFieldset.js +8 -15
  13. package/lib/client/components/Duration.js +5 -5
  14. package/lib/client/components/DynamicExpression.d.ts +3 -3
  15. package/lib/client/components/FieldsSelect.d.ts +1 -1
  16. package/lib/client/components/FieldsSelect.js +6 -6
  17. package/lib/client/components/NodeDescription.js +11 -11
  18. package/lib/client/components/RadioWithTooltip.js +13 -20
  19. package/lib/client/components/ValueBlock.js +14 -21
  20. package/lib/client/components/renderEngineReference.js +1 -8
  21. package/lib/client/index.d.ts +12 -4
  22. package/lib/client/index.js +78 -15
  23. package/lib/client/nodes/aggregate.d.ts +2 -2
  24. package/lib/client/nodes/aggregate.js +1 -1
  25. package/lib/client/nodes/calculation.d.ts +1 -1
  26. package/lib/client/nodes/calculation.js +16 -23
  27. package/lib/client/nodes/condition.d.ts +1 -3
  28. package/lib/client/nodes/condition.js +13 -20
  29. package/lib/client/nodes/create.d.ts +4 -3
  30. package/lib/client/nodes/destroy.d.ts +1 -1
  31. package/lib/client/nodes/index.d.ts +2 -2
  32. package/lib/client/nodes/index.js +79 -86
  33. package/lib/client/nodes/loop.d.ts +1 -1
  34. package/lib/client/nodes/loop.js +35 -34
  35. package/lib/client/nodes/manual/ModeConfig.js +23 -30
  36. package/lib/client/nodes/manual/SchemaConfig.d.ts +3 -3
  37. package/lib/client/nodes/manual/SchemaConfig.js +18 -17
  38. package/lib/client/nodes/manual/WorkflowTodo.js +61 -69
  39. package/lib/client/nodes/manual/WorkflowTodoBlockInitializer.d.ts +2 -5
  40. package/lib/client/nodes/manual/WorkflowTodoBlockInitializer.js +6 -5
  41. package/lib/client/nodes/manual/forms/custom.js +11 -18
  42. package/lib/client/nodes/parallel.js +20 -20
  43. package/lib/client/nodes/query.d.ts +3 -2
  44. package/lib/client/nodes/request.d.ts +2 -2
  45. package/lib/client/nodes/request.js +7 -7
  46. package/lib/client/nodes/update.d.ts +2 -2
  47. package/lib/client/nodes/update.js +1 -1
  48. package/lib/client/schemas/collection.d.ts +1 -1
  49. package/lib/client/schemas/collection.js +3 -10
  50. package/lib/client/style.js +18 -18
  51. package/lib/client/triggers/collection.d.ts +4 -3
  52. package/lib/client/triggers/collection.js +1 -1
  53. package/lib/client/triggers/index.d.ts +2 -2
  54. package/lib/client/triggers/index.js +45 -52
  55. package/lib/client/triggers/schedule/DateFieldsSelect.js +1 -1
  56. package/lib/client/triggers/schedule/EndsByField.js +11 -11
  57. package/lib/client/triggers/schedule/OnField.js +11 -11
  58. package/lib/client/triggers/schedule/RepeatField.js +4 -4
  59. package/lib/client/triggers/schedule/ScheduleConfig.js +17 -24
  60. package/lib/client/triggers/schedule/index.d.ts +2 -1
  61. package/lib/client/triggers/schedule/index.js +2 -2
  62. package/lib/client/variable.d.ts +2 -2
  63. package/lib/client/variable.js +5 -5
  64. package/lib/server/Plugin.d.ts +2 -3
  65. package/lib/server/Plugin.js +8 -9
  66. package/lib/server/Processor.d.ts +2 -4
  67. package/lib/server/actions/nodes.js +7 -7
  68. package/lib/server/fields/expression-field.d.ts +1 -2
  69. package/lib/server/fields/expression-field.js +1 -8
  70. package/lib/server/functions/index.d.ts +2 -3
  71. package/lib/server/index.d.ts +1 -0
  72. package/lib/server/index.js +12 -0
  73. package/lib/server/instructions/aggregate.d.ts +1 -1
  74. package/lib/server/instructions/aggregate.js +5 -5
  75. package/lib/server/instructions/condition.d.ts +2 -1
  76. package/lib/server/instructions/create.d.ts +1 -1
  77. package/lib/server/instructions/delay.d.ts +3 -3
  78. package/lib/server/instructions/delay.js +66 -64
  79. package/lib/server/instructions/destroy.d.ts +1 -1
  80. package/lib/server/instructions/index.d.ts +5 -5
  81. package/lib/server/instructions/loop.d.ts +1 -2
  82. package/lib/server/instructions/manual/actions.js +1 -1
  83. package/lib/server/instructions/manual/forms/index.d.ts +1 -1
  84. package/lib/server/instructions/manual/index.d.ts +1 -1
  85. package/lib/server/instructions/parallel.d.ts +1 -2
  86. package/lib/server/instructions/query.d.ts +1 -1
  87. package/lib/server/instructions/request.d.ts +2 -2
  88. package/lib/server/instructions/request.js +1 -0
  89. package/lib/server/instructions/update.d.ts +1 -1
  90. package/lib/server/migrations/20230221071831-calculation-expression.js +1 -1
  91. package/lib/server/migrations/20230221121203-condition-calculation.js +1 -1
  92. package/lib/server/migrations/20230221162902-jsonb-to-json.js +7 -7
  93. package/lib/server/migrations/20230411034722-manual-multi-form.js +1 -8
  94. package/lib/server/triggers/collection.d.ts +1 -1
  95. package/lib/server/triggers/collection.js +2 -2
  96. package/lib/server/triggers/index.d.ts +1 -1
  97. package/lib/server/triggers/schedule.d.ts +1 -1
  98. package/lib/server/triggers/schedule.js +18 -18
  99. package/lib/server/{models → types}/Execution.d.ts +2 -3
  100. package/lib/server/{models → types}/FlowNode.d.ts +1 -2
  101. package/lib/server/{models → types}/Job.d.ts +1 -2
  102. package/lib/server/{models → types}/Workflow.d.ts +1 -2
  103. package/lib/server/types/index.d.ts +4 -0
  104. package/lib/server/types/index.js +5 -0
  105. package/package.json +40 -20
  106. package/server.d.ts +2 -3
  107. package/server.js +1 -30
  108. package/src/client/AddButton.tsx +99 -0
  109. package/src/client/Branch.tsx +35 -0
  110. package/src/client/CanvasContent.tsx +23 -0
  111. package/src/client/ExecutionCanvas.tsx +168 -0
  112. package/src/client/ExecutionLink.tsx +16 -0
  113. package/src/client/ExecutionPage.tsx +44 -0
  114. package/src/client/ExecutionResourceProvider.tsx +21 -0
  115. package/src/client/FlowContext.ts +7 -0
  116. package/src/client/WorkflowCanvas.tsx +220 -0
  117. package/src/client/WorkflowLink.tsx +16 -0
  118. package/src/client/WorkflowPage.tsx +51 -0
  119. package/src/client/WorkflowProvider.tsx +84 -0
  120. package/src/client/components/CollectionBlockInitializer.tsx +71 -0
  121. package/src/client/components/CollectionFieldset.tsx +158 -0
  122. package/src/client/components/Duration.tsx +45 -0
  123. package/src/client/components/DynamicExpression.tsx +53 -0
  124. package/src/client/components/FieldsSelect.tsx +28 -0
  125. package/src/client/components/FilterDynamicComponent.tsx +15 -0
  126. package/src/client/components/NodeDescription.tsx +44 -0
  127. package/src/client/components/NullRender.tsx +3 -0
  128. package/src/client/components/OpenDrawer.tsx +24 -0
  129. package/src/client/components/RadioWithTooltip.tsx +38 -0
  130. package/src/client/components/ValueBlock.tsx +67 -0
  131. package/src/client/components/renderEngineReference.tsx +30 -0
  132. package/src/client/constants.tsx +91 -0
  133. package/src/client/index.tsx +51 -0
  134. package/src/client/interfaces/expression.tsx +25 -0
  135. package/src/client/locale/en-US.ts +136 -0
  136. package/src/client/locale/es-ES.ts +129 -0
  137. package/src/client/locale/index.ts +18 -0
  138. package/src/client/locale/ja-JP.ts +90 -0
  139. package/src/client/locale/pt-BR.ts +136 -0
  140. package/src/client/locale/ru-RU.ts +90 -0
  141. package/src/client/locale/tr-TR.ts +90 -0
  142. package/src/client/locale/zh-CN.ts +242 -0
  143. package/src/client/nodes/aggregate.tsx +327 -0
  144. package/src/client/nodes/calculation.tsx +217 -0
  145. package/src/client/nodes/condition.tsx +490 -0
  146. package/src/client/nodes/create.tsx +86 -0
  147. package/src/client/nodes/delay.tsx +37 -0
  148. package/src/client/nodes/destroy.tsx +34 -0
  149. package/src/client/nodes/index.tsx +489 -0
  150. package/src/client/nodes/loop.tsx +159 -0
  151. package/src/client/nodes/manual/AssigneesSelect.tsx +33 -0
  152. package/src/client/nodes/manual/DetailsBlockProvider.tsx +80 -0
  153. package/src/client/nodes/manual/FormBlockInitializer.tsx +68 -0
  154. package/src/client/nodes/manual/FormBlockProvider.tsx +75 -0
  155. package/src/client/nodes/manual/ModeConfig.tsx +84 -0
  156. package/src/client/nodes/manual/SchemaConfig.tsx +365 -0
  157. package/src/client/nodes/manual/WorkflowTodo.tsx +611 -0
  158. package/src/client/nodes/manual/WorkflowTodoBlockInitializer.tsx +28 -0
  159. package/src/client/nodes/manual/forms/create.tsx +88 -0
  160. package/src/client/nodes/manual/forms/custom.tsx +388 -0
  161. package/src/client/nodes/manual/forms/update.tsx +130 -0
  162. package/src/client/nodes/manual/index.tsx +162 -0
  163. package/src/client/nodes/manual/utils.ts +28 -0
  164. package/src/client/nodes/parallel.tsx +137 -0
  165. package/src/client/nodes/query.tsx +89 -0
  166. package/src/client/nodes/request.tsx +185 -0
  167. package/src/client/nodes/update.tsx +99 -0
  168. package/src/client/schemas/collection.ts +75 -0
  169. package/src/client/schemas/executions.tsx +169 -0
  170. package/src/client/schemas/workflows.ts +364 -0
  171. package/src/client/style.tsx +312 -0
  172. package/src/client/triggers/collection.tsx +186 -0
  173. package/src/client/triggers/index.tsx +307 -0
  174. package/src/client/triggers/schedule/DateFieldsSelect.tsx +28 -0
  175. package/src/client/triggers/schedule/EndsByField.tsx +40 -0
  176. package/src/client/triggers/schedule/OnField.tsx +50 -0
  177. package/src/client/triggers/schedule/RepeatField.tsx +116 -0
  178. package/src/client/triggers/schedule/ScheduleConfig.tsx +225 -0
  179. package/src/client/triggers/schedule/constants.ts +4 -0
  180. package/src/client/triggers/schedule/index.tsx +72 -0
  181. package/src/client/triggers/schedule/locale/Cron.zh-CN.ts +79 -0
  182. package/src/client/utils.ts +36 -0
  183. package/src/client/variable.tsx +296 -0
  184. package/src/index.ts +1 -0
  185. package/src/server/Plugin.ts +351 -0
  186. package/src/server/Processor.ts +354 -0
  187. package/src/server/__tests__/Plugin.test.ts +398 -0
  188. package/src/server/__tests__/Processor.test.ts +474 -0
  189. package/src/server/__tests__/actions/workflows.test.ts +419 -0
  190. package/src/server/__tests__/collections/categories.ts +23 -0
  191. package/src/server/__tests__/collections/comments.ts +24 -0
  192. package/src/server/__tests__/collections/posts.ts +42 -0
  193. package/src/server/__tests__/collections/replies.ts +9 -0
  194. package/src/server/__tests__/collections/tags.ts +15 -0
  195. package/src/server/__tests__/index.ts +89 -0
  196. package/src/server/__tests__/instructions/aggregate.test.ts +294 -0
  197. package/src/server/__tests__/instructions/calculation.test.ts +265 -0
  198. package/src/server/__tests__/instructions/condition.test.ts +335 -0
  199. package/src/server/__tests__/instructions/create.test.ts +129 -0
  200. package/src/server/__tests__/instructions/delay.test.ts +182 -0
  201. package/src/server/__tests__/instructions/destroy.test.ts +58 -0
  202. package/src/server/__tests__/instructions/loop.test.ts +331 -0
  203. package/src/server/__tests__/instructions/manual.test.ts +854 -0
  204. package/src/server/__tests__/instructions/parallel.test.ts +445 -0
  205. package/src/server/__tests__/instructions/query.test.ts +359 -0
  206. package/src/server/__tests__/instructions/request.test.ts +217 -0
  207. package/src/server/__tests__/instructions/update.test.ts +189 -0
  208. package/src/server/__tests__/triggers/collection.test.ts +298 -0
  209. package/src/server/__tests__/triggers/schedule.test.ts +369 -0
  210. package/src/server/actions/index.ts +25 -0
  211. package/src/server/actions/nodes.ts +214 -0
  212. package/src/server/actions/workflows.ts +178 -0
  213. package/src/server/collections/executions.ts +35 -0
  214. package/src/server/collections/flow_nodes.ts +54 -0
  215. package/src/server/collections/jobs.ts +31 -0
  216. package/src/server/collections/workflows.ts +88 -0
  217. package/src/server/constants.ts +26 -0
  218. package/src/server/fields/expression-field.ts +11 -0
  219. package/src/server/fields/index.ts +7 -0
  220. package/src/server/functions/index.ts +16 -0
  221. package/src/server/index.ts +6 -0
  222. package/src/server/instructions/aggregate.ts +42 -0
  223. package/src/server/instructions/calculation.ts +41 -0
  224. package/src/server/instructions/condition.ts +172 -0
  225. package/src/server/instructions/create.ts +37 -0
  226. package/src/server/instructions/delay.ts +105 -0
  227. package/src/server/instructions/destroy.ts +23 -0
  228. package/src/server/instructions/index.ts +63 -0
  229. package/src/server/instructions/loop.ts +99 -0
  230. package/src/server/instructions/manual/actions.ts +79 -0
  231. package/src/server/instructions/manual/collecions/jobs.ts +17 -0
  232. package/src/server/instructions/manual/collecions/users.ts +15 -0
  233. package/src/server/instructions/manual/collecions/users_jobs.ts +50 -0
  234. package/src/server/instructions/manual/forms/create.ts +22 -0
  235. package/src/server/instructions/manual/forms/index.ts +12 -0
  236. package/src/server/instructions/manual/forms/update.ts +22 -0
  237. package/src/server/instructions/manual/index.ts +184 -0
  238. package/src/server/instructions/parallel.ts +121 -0
  239. package/src/server/instructions/query.ts +31 -0
  240. package/src/server/instructions/request.ts +87 -0
  241. package/src/server/instructions/update.ts +24 -0
  242. package/src/server/migrations/20221129153547-calculation-variables.ts +64 -0
  243. package/src/server/migrations/20230221032941-change-request-body-type.ts +76 -0
  244. package/src/server/migrations/20230221071831-calculation-expression.ts +102 -0
  245. package/src/server/migrations/20230221121203-condition-calculation.ts +82 -0
  246. package/src/server/migrations/20230221162902-jsonb-to-json.ts +51 -0
  247. package/src/server/migrations/20230411034722-manual-multi-form.ts +282 -0
  248. package/src/server/migrations/20230612021134-manual-collection-block.ts +138 -0
  249. package/src/server/triggers/collection.ts +142 -0
  250. package/src/server/triggers/index.ts +22 -0
  251. package/src/server/triggers/schedule.ts +567 -0
  252. package/src/server/types/Execution.ts +26 -0
  253. package/src/server/types/FlowNode.ts +21 -0
  254. package/src/server/types/Job.ts +18 -0
  255. package/src/server/types/Workflow.ts +36 -0
  256. package/src/server/types/index.ts +4 -0
  257. /package/lib/server/{models → types}/Execution.js +0 -0
  258. /package/lib/server/{models → types}/FlowNode.js +0 -0
  259. /package/lib/server/{models → types}/Job.js +0 -0
  260. /package/lib/server/{models → types}/Workflow.js +0 -0
@@ -0,0 +1,335 @@
1
+ import { Application } from '@nocobase/server';
2
+ import Database from '@nocobase/database';
3
+ import { getApp, sleep } from '..';
4
+ import { EXECUTION_STATUS, BRANCH_INDEX } from '../../constants';
5
+
6
+ describe('workflow > instructions > condition', () => {
7
+ let app: Application;
8
+ let db: Database;
9
+ let PostRepo;
10
+ let WorkflowModel;
11
+ let workflow;
12
+
13
+ beforeEach(async () => {
14
+ app = await getApp();
15
+
16
+ db = app.db;
17
+ WorkflowModel = db.getCollection('workflows').model;
18
+ PostRepo = db.getCollection('posts').repository;
19
+
20
+ workflow = await WorkflowModel.create({
21
+ title: 'test workflow',
22
+ enabled: true,
23
+ type: 'collection',
24
+ config: {
25
+ mode: 1,
26
+ collection: 'posts',
27
+ },
28
+ });
29
+ });
30
+
31
+ afterEach(() => db.close());
32
+
33
+ describe('config.rejectOnFalse', () => {});
34
+
35
+ describe('single calculation', () => {
36
+ it('calculation to true downstream', async () => {
37
+ const n1 = await workflow.createNode({
38
+ title: 'condition',
39
+ type: 'condition',
40
+ config: {
41
+ engine: 'math.js',
42
+ expression: '1 == 1',
43
+ },
44
+ });
45
+
46
+ const n2 = await workflow.createNode({
47
+ title: 'true to echo',
48
+ type: 'echo',
49
+ branchIndex: BRANCH_INDEX.ON_TRUE,
50
+ upstreamId: n1.id,
51
+ });
52
+
53
+ const n3 = await workflow.createNode({
54
+ title: 'false to echo',
55
+ type: 'echo',
56
+ branchIndex: BRANCH_INDEX.ON_FALSE,
57
+ upstreamId: n1.id,
58
+ });
59
+
60
+ const post = await PostRepo.create({ values: { title: 't1' } });
61
+
62
+ await sleep(500);
63
+
64
+ const [execution] = await workflow.getExecutions();
65
+ expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
66
+
67
+ const jobs = await execution.getJobs();
68
+ expect(jobs.length).toEqual(2);
69
+ expect(jobs[1].result).toEqual(true);
70
+ });
71
+
72
+ it('calculation to false downstream', async () => {
73
+ const n1 = await workflow.createNode({
74
+ title: 'condition',
75
+ type: 'condition',
76
+ config: {
77
+ engine: 'math.js',
78
+ // false
79
+ expression: '0 == 1',
80
+ },
81
+ });
82
+
83
+ await workflow.createNode({
84
+ title: 'true to echo',
85
+ type: 'echo',
86
+ branchIndex: BRANCH_INDEX.ON_TRUE,
87
+ upstreamId: n1.id,
88
+ });
89
+
90
+ await workflow.createNode({
91
+ title: 'false to echo',
92
+ type: 'echo',
93
+ branchIndex: BRANCH_INDEX.ON_FALSE,
94
+ upstreamId: n1.id,
95
+ });
96
+
97
+ const post = await PostRepo.create({ values: { title: 't1' } });
98
+
99
+ await sleep(500);
100
+
101
+ const [execution] = await workflow.getExecutions();
102
+ expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
103
+
104
+ const jobs = await execution.getJobs();
105
+ expect(jobs.length).toEqual(2);
106
+ expect(jobs[1].result).toEqual(false);
107
+ });
108
+ });
109
+
110
+ describe('group calculation', () => {
111
+ it('and true', async () => {
112
+ const n1 = await workflow.createNode({
113
+ type: 'condition',
114
+ config: {
115
+ calculation: {
116
+ group: {
117
+ type: 'and',
118
+ calculations: [
119
+ { calculator: 'equal', operands: [1, 1] },
120
+ { calculator: 'equal', operands: [1, 1] },
121
+ ],
122
+ },
123
+ },
124
+ },
125
+ });
126
+
127
+ const post = await PostRepo.create({ values: { title: 't1' } });
128
+
129
+ await sleep(500);
130
+
131
+ const [execution] = await workflow.getExecutions();
132
+ const [job] = await execution.getJobs();
133
+ expect(job.result).toBe(true);
134
+ });
135
+
136
+ it('and false', async () => {
137
+ const n1 = await workflow.createNode({
138
+ type: 'condition',
139
+ config: {
140
+ calculation: {
141
+ group: {
142
+ type: 'and',
143
+ calculations: [
144
+ { calculator: 'equal', operands: [1, 1] },
145
+ { calculator: 'equal', operands: [0, 1] },
146
+ ],
147
+ },
148
+ },
149
+ },
150
+ });
151
+
152
+ const post = await PostRepo.create({ values: { title: 't1' } });
153
+
154
+ await sleep(500);
155
+
156
+ const [execution] = await workflow.getExecutions();
157
+ const [job] = await execution.getJobs();
158
+ expect(job.result).toBe(false);
159
+ });
160
+
161
+ it('or true', async () => {
162
+ const n1 = await workflow.createNode({
163
+ type: 'condition',
164
+ config: {
165
+ calculation: {
166
+ group: {
167
+ type: 'or',
168
+ calculations: [
169
+ { calculator: 'equal', operands: [1, 1] },
170
+ { calculator: 'equal', operands: [0, 1] },
171
+ ],
172
+ },
173
+ },
174
+ },
175
+ });
176
+
177
+ const post = await PostRepo.create({ values: { title: 't1' } });
178
+
179
+ await sleep(500);
180
+
181
+ const [execution] = await workflow.getExecutions({ include: ['jobs'] });
182
+ const [job] = execution.jobs;
183
+ expect(job.result).toBe(true);
184
+ });
185
+
186
+ it('or false', async () => {
187
+ const n1 = await workflow.createNode({
188
+ type: 'condition',
189
+ config: {
190
+ calculation: {
191
+ group: {
192
+ type: 'and',
193
+ calculations: [
194
+ { calculator: 'equal', operands: [0, 1] },
195
+ { calculator: 'equal', operands: [0, 1] },
196
+ ],
197
+ },
198
+ },
199
+ },
200
+ });
201
+
202
+ const post = await PostRepo.create({ values: { title: 't1' } });
203
+
204
+ await sleep(500);
205
+
206
+ const [execution] = await workflow.getExecutions();
207
+ const [job] = await execution.getJobs();
208
+ expect(job.result).toBe(false);
209
+ });
210
+
211
+ it('nested', async () => {
212
+ const n1 = await workflow.createNode({
213
+ type: 'condition',
214
+ config: {
215
+ calculation: {
216
+ group: {
217
+ type: 'and',
218
+ calculations: [
219
+ { calculator: 'equal', operands: [1, 1] },
220
+ {
221
+ group: {
222
+ type: 'or',
223
+ calculations: [
224
+ { calculator: 'equal', operands: [0, 1] },
225
+ { calculator: 'equal', operands: [0, 1] },
226
+ ],
227
+ },
228
+ },
229
+ ],
230
+ },
231
+ },
232
+ },
233
+ });
234
+
235
+ const post = await PostRepo.create({ values: { title: 't1' } });
236
+
237
+ await sleep(500);
238
+
239
+ const [execution] = await workflow.getExecutions();
240
+ const [job] = await execution.getJobs();
241
+ expect(job.result).toBe(false);
242
+ });
243
+ });
244
+
245
+ describe('engines', () => {
246
+ it('default as basic', async () => {
247
+ const n1 = await workflow.createNode({
248
+ title: 'condition',
249
+ type: 'condition',
250
+ config: {
251
+ calculation: {
252
+ calculator: 'equal',
253
+ operands: [1, '{{$context.data.read}}'],
254
+ },
255
+ },
256
+ });
257
+
258
+ const post = await PostRepo.create({ values: { read: 1 } });
259
+
260
+ await sleep(500);
261
+
262
+ const [execution] = await workflow.getExecutions();
263
+ expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
264
+
265
+ const [job] = await execution.getJobs();
266
+ expect(job.result).toEqual(true);
267
+ });
268
+
269
+ it('basic engine', async () => {
270
+ const n1 = await workflow.createNode({
271
+ title: 'condition',
272
+ type: 'condition',
273
+ config: {
274
+ engine: 'basic',
275
+ calculation: {
276
+ calculator: 'equal',
277
+ operands: [1, '{{$context.data.read}}'],
278
+ },
279
+ },
280
+ });
281
+
282
+ const post = await PostRepo.create({ values: { read: 1 } });
283
+
284
+ await sleep(500);
285
+
286
+ const [execution] = await workflow.getExecutions();
287
+ expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
288
+
289
+ const [job] = await execution.getJobs();
290
+ expect(job.result).toEqual(true);
291
+ });
292
+
293
+ it('math.js', async () => {
294
+ const n1 = await workflow.createNode({
295
+ title: 'condition',
296
+ type: 'condition',
297
+ config: {
298
+ engine: 'math.js',
299
+ expression: '1 == 1',
300
+ },
301
+ });
302
+
303
+ const post = await PostRepo.create({ values: { title: 't1' } });
304
+
305
+ await sleep(500);
306
+
307
+ const [execution] = await workflow.getExecutions();
308
+ expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
309
+
310
+ const [job] = await execution.getJobs();
311
+ expect(job.result).toEqual(true);
312
+ });
313
+
314
+ it('formula.js', async () => {
315
+ const n1 = await workflow.createNode({
316
+ title: 'condition',
317
+ type: 'condition',
318
+ config: {
319
+ engine: 'formula.js',
320
+ expression: '1 == 1',
321
+ },
322
+ });
323
+
324
+ const post = await PostRepo.create({ values: { title: 't1' } });
325
+
326
+ await sleep(500);
327
+
328
+ const [execution] = await workflow.getExecutions();
329
+ expect(execution.status).toEqual(EXECUTION_STATUS.RESOLVED);
330
+
331
+ const [job] = await execution.getJobs();
332
+ expect(job.result).toEqual(true);
333
+ });
334
+ });
335
+ });
@@ -0,0 +1,129 @@
1
+ import { Application } from '@nocobase/server';
2
+ import Database from '@nocobase/database';
3
+ import { getApp, sleep } from '..';
4
+
5
+ describe('workflow > instructions > create', () => {
6
+ let app: Application;
7
+ let db: Database;
8
+ let PostRepo;
9
+ let ReplyRepo;
10
+ let WorkflowModel;
11
+ let workflow;
12
+
13
+ beforeEach(async () => {
14
+ app = await getApp();
15
+
16
+ db = app.db;
17
+ WorkflowModel = db.getCollection('workflows').model;
18
+ PostRepo = db.getCollection('posts').repository;
19
+ ReplyRepo = db.getCollection('replies').repository;
20
+
21
+ workflow = await WorkflowModel.create({
22
+ title: 'test workflow',
23
+ enabled: true,
24
+ type: 'collection',
25
+ config: {
26
+ mode: 1,
27
+ collection: 'posts',
28
+ },
29
+ });
30
+ });
31
+
32
+ afterEach(() => db.close());
33
+
34
+ describe('create one', () => {
35
+ it('params: from context', async () => {
36
+ const n1 = await workflow.createNode({
37
+ type: 'create',
38
+ config: {
39
+ collection: 'comments',
40
+ params: {
41
+ values: {
42
+ postId: '{{$context.data.id}}',
43
+ },
44
+ },
45
+ },
46
+ });
47
+
48
+ const post = await PostRepo.create({ values: { title: 't1' } });
49
+
50
+ await sleep(500);
51
+
52
+ const [execution] = await workflow.getExecutions();
53
+ const [job] = await execution.getJobs();
54
+ expect(job.result.postId).toBe(post.id);
55
+ });
56
+
57
+ it('params.values with hasMany', async () => {
58
+ const replies = await ReplyRepo.create({ values: [{}, {}] });
59
+
60
+ const n1 = await workflow.createNode({
61
+ type: 'create',
62
+ config: {
63
+ collection: 'comments',
64
+ params: {
65
+ values: {
66
+ replies: replies.map((item) => item.id),
67
+ },
68
+ appends: ['replies'],
69
+ },
70
+ },
71
+ });
72
+
73
+ const post = await PostRepo.create({ values: { title: 't1' } });
74
+
75
+ await sleep(500);
76
+
77
+ const [execution] = await workflow.getExecutions();
78
+ const [job] = await execution.getJobs();
79
+ expect(job.result.replies.length).toBe(2);
80
+ });
81
+
82
+ it('params.appends: belongsTo', async () => {
83
+ const n1 = await workflow.createNode({
84
+ type: 'create',
85
+ config: {
86
+ collection: 'comments',
87
+ params: {
88
+ values: {
89
+ postId: '{{$context.data.id}}',
90
+ },
91
+ appends: ['post'],
92
+ },
93
+ },
94
+ });
95
+
96
+ const post = await PostRepo.create({ values: { title: 't1' } });
97
+
98
+ await sleep(500);
99
+
100
+ const [execution] = await workflow.getExecutions();
101
+ const [job] = await execution.getJobs();
102
+ expect(job.result.post.id).toBe(post.id);
103
+ });
104
+
105
+ it('params.appends: belongsToMany', async () => {
106
+ const n1 = await workflow.createNode({
107
+ type: 'create',
108
+ config: {
109
+ collection: 'tags',
110
+ params: {
111
+ values: {
112
+ posts: ['{{$context.data.id}}'],
113
+ },
114
+ appends: ['posts'],
115
+ },
116
+ },
117
+ });
118
+
119
+ const post = await PostRepo.create({ values: { title: 't1' } });
120
+
121
+ await sleep(500);
122
+
123
+ const [execution] = await workflow.getExecutions();
124
+ const [job] = await execution.getJobs();
125
+ expect(job.result.posts.length).toBe(1);
126
+ expect(job.result.posts[0].id).toBe(post.id);
127
+ });
128
+ });
129
+ });
@@ -0,0 +1,182 @@
1
+ import Database from '@nocobase/database';
2
+ import { Application } from '@nocobase/server';
3
+ import { getApp, sleep } from '..';
4
+ import { EXECUTION_STATUS, JOB_STATUS } from '../../constants';
5
+
6
+ describe('workflow > instructions > delay', () => {
7
+ let app: Application;
8
+ let db: Database;
9
+ let PostRepo;
10
+ let WorkflowModel;
11
+ let workflow;
12
+
13
+ beforeEach(async () => {
14
+ app = await getApp();
15
+
16
+ db = app.db;
17
+
18
+ WorkflowModel = db.getCollection('workflows').model;
19
+ PostRepo = db.getCollection('posts').repository;
20
+
21
+ workflow = await WorkflowModel.create({
22
+ enabled: true,
23
+ type: 'collection',
24
+ config: {
25
+ mode: 1,
26
+ collection: 'posts',
27
+ },
28
+ });
29
+ });
30
+
31
+ afterEach(() => app.stop());
32
+
33
+ describe('runtime', () => {
34
+ it('delay to resolved', async () => {
35
+ const n1 = await workflow.createNode({
36
+ type: 'delay',
37
+ config: {
38
+ duration: 2000,
39
+ endStatus: JOB_STATUS.RESOLVED,
40
+ },
41
+ });
42
+
43
+ const post = await PostRepo.create({ values: { title: 't1' } });
44
+
45
+ await sleep(500);
46
+
47
+ const [e1] = await workflow.getExecutions();
48
+ expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
49
+ const [j1] = await e1.getJobs();
50
+ expect(j1.status).toBe(JOB_STATUS.PENDING);
51
+
52
+ await sleep(2000);
53
+
54
+ const [e2] = await workflow.getExecutions();
55
+ expect(e2.status).toEqual(EXECUTION_STATUS.RESOLVED);
56
+ const [j2] = await e2.getJobs();
57
+ expect(j2.status).toBe(JOB_STATUS.RESOLVED);
58
+ });
59
+
60
+ it('delay to reject', async () => {
61
+ const n1 = await workflow.createNode({
62
+ type: 'delay',
63
+ config: {
64
+ duration: 2000,
65
+ endStatus: JOB_STATUS.FAILED,
66
+ },
67
+ });
68
+
69
+ const post = await PostRepo.create({ values: { title: 't1' } });
70
+
71
+ await sleep(500);
72
+
73
+ const [e1] = await workflow.getExecutions();
74
+ expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
75
+ const [j1] = await e1.getJobs();
76
+ expect(j1.status).toBe(JOB_STATUS.PENDING);
77
+
78
+ await sleep(2000);
79
+
80
+ const [e2] = await workflow.getExecutions();
81
+ expect(e2.status).toEqual(EXECUTION_STATUS.FAILED);
82
+ const [j2] = await e2.getJobs();
83
+ expect(j2.status).toBe(JOB_STATUS.FAILED);
84
+ });
85
+
86
+ it('delay to resolve and rollback in downstream node', async () => {
87
+ const n1 = await workflow.createNode({
88
+ type: 'delay',
89
+ config: {
90
+ duration: 2000,
91
+ endStatus: JOB_STATUS.RESOLVED,
92
+ },
93
+ });
94
+ const n2 = await workflow.createNode({
95
+ type: 'create',
96
+ config: {
97
+ collection: 'comment',
98
+ params: {
99
+ values: {
100
+ status: 'should be number but use string to raise an error',
101
+ },
102
+ },
103
+ },
104
+ upstreamId: n1.id,
105
+ });
106
+ await n1.setDownstream(n2);
107
+
108
+ const post = await PostRepo.create({ values: { title: 't1' } });
109
+
110
+ await sleep(500);
111
+
112
+ const [e1] = await workflow.getExecutions();
113
+ expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
114
+ const [j1] = await e1.getJobs();
115
+ expect(j1.status).toBe(JOB_STATUS.PENDING);
116
+
117
+ await sleep(2000);
118
+
119
+ const [e2] = await workflow.getExecutions();
120
+ expect(e2.status).toEqual(EXECUTION_STATUS.ERROR);
121
+ const [j2, j3] = await e2.getJobs({ order: [['id', 'ASC']] });
122
+ expect(j2.status).toBe(JOB_STATUS.RESOLVED);
123
+ expect(j3.status).toBe(JOB_STATUS.ERROR);
124
+ });
125
+ });
126
+
127
+ describe('app lifecycle', () => {
128
+ beforeEach(async () => {
129
+ await workflow.createNode({
130
+ type: 'delay',
131
+ config: {
132
+ duration: 2000,
133
+ endStatus: JOB_STATUS.RESOLVED,
134
+ },
135
+ });
136
+ });
137
+
138
+ it('restart app should trigger delayed job', async () => {
139
+ const post = await PostRepo.create({ values: { title: 't1' } });
140
+
141
+ await sleep(500);
142
+
143
+ const [e1] = await workflow.getExecutions();
144
+ expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
145
+ const [j1] = await e1.getJobs();
146
+ expect(j1.status).toBe(JOB_STATUS.PENDING);
147
+
148
+ await app.stop();
149
+ await sleep(500);
150
+
151
+ await app.start();
152
+ await sleep(2000);
153
+
154
+ const [e2] = await workflow.getExecutions();
155
+ expect(e2.status).toEqual(EXECUTION_STATUS.RESOLVED);
156
+ const [j2] = await e2.getJobs();
157
+ expect(j2.status).toBe(JOB_STATUS.RESOLVED);
158
+ });
159
+
160
+ it('restart app should trigger missed delayed job', async () => {
161
+ const post = await PostRepo.create({ values: { title: 't1' } });
162
+
163
+ await sleep(500);
164
+
165
+ const [e1] = await workflow.getExecutions();
166
+ expect(e1.status).toEqual(EXECUTION_STATUS.STARTED);
167
+ const [j1] = await e1.getJobs();
168
+ expect(j1.status).toBe(JOB_STATUS.PENDING);
169
+
170
+ await app.stop();
171
+ await sleep(2000);
172
+
173
+ await app.start();
174
+ await sleep(500);
175
+
176
+ const [e2] = await workflow.getExecutions();
177
+ expect(e2.status).toEqual(EXECUTION_STATUS.RESOLVED);
178
+ const [j2] = await e2.getJobs();
179
+ expect(j2.status).toBe(JOB_STATUS.RESOLVED);
180
+ });
181
+ });
182
+ });
@@ -0,0 +1,58 @@
1
+ import { Application } from '@nocobase/server';
2
+ import Database from '@nocobase/database';
3
+ import { getApp, sleep } from '..';
4
+
5
+ describe('workflow > instructions > destroy', () => {
6
+ let app: Application;
7
+ let db: Database;
8
+ let PostRepo;
9
+ let WorkflowModel;
10
+ let workflow;
11
+
12
+ beforeEach(async () => {
13
+ app = await getApp();
14
+
15
+ db = app.db;
16
+ WorkflowModel = db.getCollection('workflows').model;
17
+ PostRepo = db.getCollection('posts').repository;
18
+
19
+ workflow = await WorkflowModel.create({
20
+ title: 'test workflow',
21
+ enabled: true,
22
+ type: 'collection',
23
+ config: {
24
+ mode: 1,
25
+ collection: 'posts',
26
+ },
27
+ });
28
+ });
29
+
30
+ afterEach(() => db.close());
31
+
32
+ describe('destroy one', () => {
33
+ it('params: from context', async () => {
34
+ const n1 = await workflow.createNode({
35
+ type: 'destroy',
36
+ config: {
37
+ collection: 'posts',
38
+ params: {
39
+ filter: {
40
+ id: '{{$context.data.id}}',
41
+ },
42
+ },
43
+ },
44
+ });
45
+
46
+ const post = await PostRepo.create({ values: { title: 't1' } });
47
+
48
+ await sleep(500);
49
+
50
+ const [execution] = await workflow.getExecutions();
51
+ const [job] = await execution.getJobs();
52
+ expect(job.result).toBe(1);
53
+
54
+ const count = await PostRepo.count();
55
+ expect(count).toBe(0);
56
+ });
57
+ });
58
+ });