@nocobase/plugin-workflow 0.10.1-alpha.1 → 0.11.1-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 (289) hide show
  1. package/client.d.ts +2 -3
  2. package/client.js +1 -30
  3. package/lib/client/AddButton.js +13 -11
  4. package/lib/client/Branch.js +10 -8
  5. package/lib/client/CanvasContent.js +12 -10
  6. package/lib/client/ExecutionCanvas.js +37 -33
  7. package/lib/client/ExecutionPage.js +4 -9
  8. package/lib/client/WorkflowCanvas.js +18 -15
  9. package/lib/client/WorkflowPage.js +4 -9
  10. package/lib/client/WorkflowProvider.js +1 -40
  11. package/lib/client/components/CollectionBlockInitializer.js +3 -3
  12. package/lib/client/components/CollectionFieldset.d.ts +1 -1
  13. package/lib/client/components/CollectionFieldset.js +15 -16
  14. package/lib/client/components/Duration.js +5 -5
  15. package/lib/client/components/DynamicExpression.d.ts +3 -3
  16. package/lib/client/components/FieldsSelect.d.ts +1 -1
  17. package/lib/client/components/FieldsSelect.js +10 -7
  18. package/lib/client/components/NodeDescription.js +45 -31
  19. package/lib/client/components/RadioWithTooltip.js +13 -20
  20. package/lib/client/components/ValueBlock.js +14 -21
  21. package/lib/client/components/renderEngineReference.js +1 -8
  22. package/lib/client/index.d.ts +12 -4
  23. package/lib/client/index.js +78 -15
  24. package/lib/client/locale/zh-CN.d.ts +5 -1
  25. package/lib/client/locale/zh-CN.js +6 -2
  26. package/lib/client/nodes/aggregate.d.ts +8 -3
  27. package/lib/client/nodes/aggregate.js +5 -4
  28. package/lib/client/nodes/calculation.d.ts +6 -4
  29. package/lib/client/nodes/calculation.js +22 -28
  30. package/lib/client/nodes/condition.d.ts +2 -10
  31. package/lib/client/nodes/condition.js +19 -37
  32. package/lib/client/nodes/create.d.ts +5 -6
  33. package/lib/client/nodes/create.js +1 -3
  34. package/lib/client/nodes/destroy.d.ts +1 -1
  35. package/lib/client/nodes/index.d.ts +2 -3
  36. package/lib/client/nodes/index.js +95 -102
  37. package/lib/client/nodes/loop.d.ts +1 -1
  38. package/lib/client/nodes/loop.js +46 -54
  39. package/lib/client/nodes/manual/FormBlockInitializer.js +6 -5
  40. package/lib/client/nodes/manual/ModeConfig.js +23 -30
  41. package/lib/client/nodes/manual/SchemaConfig.d.ts +4 -5
  42. package/lib/client/nodes/manual/SchemaConfig.js +180 -25
  43. package/lib/client/nodes/manual/WorkflowTodo.js +95 -110
  44. package/lib/client/nodes/manual/WorkflowTodoBlockInitializer.d.ts +2 -5
  45. package/lib/client/nodes/manual/WorkflowTodoBlockInitializer.js +6 -5
  46. package/lib/client/nodes/manual/forms/create.js +8 -1
  47. package/lib/client/nodes/manual/forms/custom.js +22 -22
  48. package/lib/client/nodes/manual/forms/update.js +8 -1
  49. package/lib/client/nodes/manual/index.d.ts +6 -1
  50. package/lib/client/nodes/manual/index.js +5 -4
  51. package/lib/client/nodes/parallel.js +23 -20
  52. package/lib/client/nodes/query.d.ts +3 -5
  53. package/lib/client/nodes/query.js +1 -3
  54. package/lib/client/nodes/request.d.ts +2 -2
  55. package/lib/client/nodes/request.js +7 -7
  56. package/lib/client/nodes/sql.d.ts +26 -0
  57. package/lib/client/{triggers/schedule/DateFieldsSelect.js → nodes/sql.js} +37 -46
  58. package/lib/client/nodes/update.d.ts +2 -2
  59. package/lib/client/nodes/update.js +1 -1
  60. package/lib/client/schemas/collection.d.ts +3 -4
  61. package/lib/client/schemas/collection.js +11 -17
  62. package/lib/client/style.d.ts +18 -13
  63. package/lib/client/style.js +315 -292
  64. package/lib/client/triggers/collection.d.ts +13 -13
  65. package/lib/client/triggers/collection.js +5 -1
  66. package/lib/client/triggers/index.d.ts +3 -4
  67. package/lib/client/triggers/index.js +51 -53
  68. package/lib/client/triggers/schedule/EndsByField.js +11 -11
  69. package/lib/client/triggers/schedule/OnField.js +45 -33
  70. package/lib/client/triggers/schedule/RepeatField.js +4 -4
  71. package/lib/client/triggers/schedule/ScheduleConfig.js +24 -31
  72. package/lib/client/triggers/schedule/index.d.ts +1 -1
  73. package/lib/client/triggers/schedule/index.js +32 -20
  74. package/lib/client/variable.d.ts +31 -13
  75. package/lib/client/variable.js +44 -29
  76. package/lib/server/Plugin.d.ts +3 -6
  77. package/lib/server/Plugin.js +15 -12
  78. package/lib/server/Processor.d.ts +3 -5
  79. package/lib/server/Processor.js +2 -2
  80. package/lib/server/actions/nodes.js +7 -7
  81. package/lib/server/fields/expression-field.d.ts +1 -2
  82. package/lib/server/fields/expression-field.js +1 -8
  83. package/lib/server/functions/index.d.ts +2 -3
  84. package/lib/server/index.d.ts +1 -0
  85. package/lib/server/index.js +12 -0
  86. package/lib/server/instructions/aggregate.d.ts +1 -1
  87. package/lib/server/instructions/aggregate.js +5 -5
  88. package/lib/server/instructions/condition.d.ts +2 -1
  89. package/lib/server/instructions/create.d.ts +2 -2
  90. package/lib/server/instructions/create.js +13 -13
  91. package/lib/server/instructions/delay.d.ts +3 -3
  92. package/lib/server/instructions/delay.js +66 -64
  93. package/lib/server/instructions/destroy.d.ts +1 -1
  94. package/lib/server/instructions/index.d.ts +5 -5
  95. package/lib/server/instructions/index.js +1 -1
  96. package/lib/server/instructions/loop.d.ts +1 -2
  97. package/lib/server/instructions/manual/actions.js +19 -7
  98. package/lib/server/instructions/manual/forms/create.js +7 -1
  99. package/lib/server/instructions/manual/forms/index.d.ts +1 -1
  100. package/lib/server/instructions/manual/forms/update.js +7 -1
  101. package/lib/server/instructions/manual/index.d.ts +1 -1
  102. package/lib/server/instructions/parallel.d.ts +1 -2
  103. package/lib/server/instructions/query.d.ts +1 -1
  104. package/lib/server/instructions/query.js +8 -1
  105. package/lib/server/instructions/request.d.ts +3 -3
  106. package/lib/server/instructions/request.js +5 -2
  107. package/lib/server/instructions/sql.d.ts +12 -0
  108. package/lib/server/instructions/sql.js +34 -0
  109. package/lib/server/instructions/update.d.ts +1 -1
  110. package/lib/server/migrations/20230221071831-calculation-expression.js +1 -1
  111. package/lib/server/migrations/20230221121203-condition-calculation.js +1 -1
  112. package/lib/server/migrations/20230221162902-jsonb-to-json.js +7 -7
  113. package/lib/server/migrations/20230411034722-manual-multi-form.js +1 -8
  114. package/lib/server/migrations/20230710115902-manual-action-values.d.ts +4 -0
  115. package/lib/server/migrations/20230710115902-manual-action-values.js +97 -0
  116. package/lib/server/triggers/collection.d.ts +1 -1
  117. package/lib/server/triggers/collection.js +15 -13
  118. package/lib/server/triggers/index.d.ts +1 -1
  119. package/lib/server/triggers/schedule.d.ts +1 -1
  120. package/lib/server/triggers/schedule.js +18 -18
  121. package/lib/server/{models → types}/Execution.d.ts +2 -3
  122. package/lib/server/{models → types}/FlowNode.d.ts +1 -2
  123. package/lib/server/{models → types}/Job.d.ts +1 -2
  124. package/lib/server/{models → types}/Workflow.d.ts +1 -2
  125. package/lib/server/types/index.d.ts +4 -0
  126. package/lib/server/types/index.js +5 -0
  127. package/lib/server/utils.d.ts +2 -0
  128. package/lib/server/utils.js +21 -0
  129. package/package.json +39 -18
  130. package/server.d.ts +2 -3
  131. package/server.js +1 -30
  132. package/src/client/AddButton.tsx +111 -0
  133. package/src/client/Branch.tsx +37 -0
  134. package/src/client/CanvasContent.tsx +25 -0
  135. package/src/client/ExecutionCanvas.tsx +166 -0
  136. package/src/client/ExecutionLink.tsx +16 -0
  137. package/src/client/ExecutionPage.tsx +45 -0
  138. package/src/client/ExecutionResourceProvider.tsx +21 -0
  139. package/src/client/FlowContext.ts +7 -0
  140. package/src/client/WorkflowCanvas.tsx +221 -0
  141. package/src/client/WorkflowLink.tsx +16 -0
  142. package/src/client/WorkflowPage.tsx +52 -0
  143. package/src/client/WorkflowProvider.tsx +84 -0
  144. package/src/client/components/CollectionBlockInitializer.tsx +71 -0
  145. package/src/client/components/CollectionFieldset.tsx +160 -0
  146. package/src/client/components/Duration.tsx +45 -0
  147. package/src/client/components/DynamicExpression.tsx +53 -0
  148. package/src/client/components/FieldsSelect.tsx +32 -0
  149. package/src/client/components/FilterDynamicComponent.tsx +15 -0
  150. package/src/client/components/NodeDescription.tsx +51 -0
  151. package/src/client/components/NullRender.tsx +3 -0
  152. package/src/client/components/OpenDrawer.tsx +24 -0
  153. package/src/client/components/RadioWithTooltip.tsx +38 -0
  154. package/src/client/components/ValueBlock.tsx +67 -0
  155. package/src/client/components/renderEngineReference.tsx +30 -0
  156. package/src/client/constants.tsx +91 -0
  157. package/src/client/index.tsx +51 -0
  158. package/src/client/interfaces/expression.tsx +25 -0
  159. package/src/client/locale/en-US.ts +136 -0
  160. package/src/client/locale/es-ES.ts +129 -0
  161. package/src/client/locale/index.ts +18 -0
  162. package/src/client/locale/ja-JP.ts +90 -0
  163. package/src/client/locale/pt-BR.ts +136 -0
  164. package/src/client/locale/ru-RU.ts +90 -0
  165. package/src/client/locale/tr-TR.ts +90 -0
  166. package/src/client/locale/zh-CN.ts +248 -0
  167. package/src/client/nodes/aggregate.tsx +327 -0
  168. package/src/client/nodes/calculation.tsx +216 -0
  169. package/src/client/nodes/condition.tsx +463 -0
  170. package/src/client/nodes/create.tsx +85 -0
  171. package/src/client/nodes/delay.tsx +37 -0
  172. package/src/client/nodes/destroy.tsx +34 -0
  173. package/src/client/nodes/index.tsx +485 -0
  174. package/src/client/nodes/loop.tsx +144 -0
  175. package/src/client/nodes/manual/AssigneesSelect.tsx +33 -0
  176. package/src/client/nodes/manual/DetailsBlockProvider.tsx +80 -0
  177. package/src/client/nodes/manual/FormBlockInitializer.tsx +69 -0
  178. package/src/client/nodes/manual/FormBlockProvider.tsx +75 -0
  179. package/src/client/nodes/manual/ModeConfig.tsx +84 -0
  180. package/src/client/nodes/manual/SchemaConfig.tsx +509 -0
  181. package/src/client/nodes/manual/WorkflowTodo.tsx +607 -0
  182. package/src/client/nodes/manual/WorkflowTodoBlockInitializer.tsx +28 -0
  183. package/src/client/nodes/manual/forms/create.tsx +92 -0
  184. package/src/client/nodes/manual/forms/custom.tsx +392 -0
  185. package/src/client/nodes/manual/forms/update.tsx +134 -0
  186. package/src/client/nodes/manual/index.tsx +162 -0
  187. package/src/client/nodes/manual/utils.ts +28 -0
  188. package/src/client/nodes/parallel.tsx +138 -0
  189. package/src/client/nodes/query.tsx +88 -0
  190. package/src/client/nodes/request.tsx +185 -0
  191. package/src/client/nodes/sql.tsx +37 -0
  192. package/src/client/nodes/update.tsx +99 -0
  193. package/src/client/schemas/collection.ts +75 -0
  194. package/src/client/schemas/executions.tsx +169 -0
  195. package/src/client/schemas/workflows.ts +364 -0
  196. package/src/client/style.tsx +347 -0
  197. package/src/client/triggers/collection.tsx +190 -0
  198. package/src/client/triggers/index.tsx +311 -0
  199. package/src/client/triggers/schedule/EndsByField.tsx +40 -0
  200. package/src/client/triggers/schedule/OnField.tsx +64 -0
  201. package/src/client/triggers/schedule/RepeatField.tsx +116 -0
  202. package/src/client/triggers/schedule/ScheduleConfig.tsx +227 -0
  203. package/src/client/triggers/schedule/constants.ts +4 -0
  204. package/src/client/triggers/schedule/index.tsx +78 -0
  205. package/src/client/triggers/schedule/locale/Cron.zh-CN.ts +79 -0
  206. package/src/client/utils.ts +36 -0
  207. package/src/client/variable.tsx +318 -0
  208. package/src/index.ts +1 -0
  209. package/src/server/Plugin.ts +355 -0
  210. package/src/server/Processor.ts +354 -0
  211. package/src/server/__tests__/Plugin.test.ts +398 -0
  212. package/src/server/__tests__/Processor.test.ts +474 -0
  213. package/src/server/__tests__/actions/workflows.test.ts +419 -0
  214. package/src/server/__tests__/collections/categories.ts +27 -0
  215. package/src/server/__tests__/collections/comments.ts +24 -0
  216. package/src/server/__tests__/collections/posts.ts +42 -0
  217. package/src/server/__tests__/collections/replies.ts +9 -0
  218. package/src/server/__tests__/collections/tags.ts +15 -0
  219. package/src/server/__tests__/index.ts +89 -0
  220. package/src/server/__tests__/instructions/aggregate.test.ts +294 -0
  221. package/src/server/__tests__/instructions/calculation.test.ts +265 -0
  222. package/src/server/__tests__/instructions/condition.test.ts +335 -0
  223. package/src/server/__tests__/instructions/create.test.ts +129 -0
  224. package/src/server/__tests__/instructions/delay.test.ts +182 -0
  225. package/src/server/__tests__/instructions/destroy.test.ts +58 -0
  226. package/src/server/__tests__/instructions/loop.test.ts +331 -0
  227. package/src/server/__tests__/instructions/manual.test.ts +1173 -0
  228. package/src/server/__tests__/instructions/parallel.test.ts +445 -0
  229. package/src/server/__tests__/instructions/query.test.ts +359 -0
  230. package/src/server/__tests__/instructions/request.test.ts +247 -0
  231. package/src/server/__tests__/instructions/sql.test.ts +162 -0
  232. package/src/server/__tests__/instructions/update.test.ts +189 -0
  233. package/src/server/__tests__/triggers/collection.test.ts +333 -0
  234. package/src/server/__tests__/triggers/schedule.test.ts +369 -0
  235. package/src/server/actions/index.ts +25 -0
  236. package/src/server/actions/nodes.ts +214 -0
  237. package/src/server/actions/workflows.ts +178 -0
  238. package/src/server/collections/executions.ts +35 -0
  239. package/src/server/collections/flow_nodes.ts +54 -0
  240. package/src/server/collections/jobs.ts +31 -0
  241. package/src/server/collections/workflows.ts +88 -0
  242. package/src/server/constants.ts +26 -0
  243. package/src/server/fields/expression-field.ts +11 -0
  244. package/src/server/fields/index.ts +7 -0
  245. package/src/server/functions/index.ts +16 -0
  246. package/src/server/index.ts +6 -0
  247. package/src/server/instructions/aggregate.ts +42 -0
  248. package/src/server/instructions/calculation.ts +41 -0
  249. package/src/server/instructions/condition.ts +172 -0
  250. package/src/server/instructions/create.ts +39 -0
  251. package/src/server/instructions/delay.ts +105 -0
  252. package/src/server/instructions/destroy.ts +23 -0
  253. package/src/server/instructions/index.ts +64 -0
  254. package/src/server/instructions/loop.ts +99 -0
  255. package/src/server/instructions/manual/actions.ts +91 -0
  256. package/src/server/instructions/manual/collecions/jobs.ts +17 -0
  257. package/src/server/instructions/manual/collecions/users.ts +15 -0
  258. package/src/server/instructions/manual/collecions/users_jobs.ts +50 -0
  259. package/src/server/instructions/manual/forms/create.ts +23 -0
  260. package/src/server/instructions/manual/forms/index.ts +12 -0
  261. package/src/server/instructions/manual/forms/update.ts +23 -0
  262. package/src/server/instructions/manual/index.ts +184 -0
  263. package/src/server/instructions/parallel.ts +121 -0
  264. package/src/server/instructions/query.ts +42 -0
  265. package/src/server/instructions/request.ts +88 -0
  266. package/src/server/instructions/sql.ts +25 -0
  267. package/src/server/instructions/update.ts +24 -0
  268. package/src/server/migrations/20221129153547-calculation-variables.ts +64 -0
  269. package/src/server/migrations/20230221032941-change-request-body-type.ts +76 -0
  270. package/src/server/migrations/20230221071831-calculation-expression.ts +102 -0
  271. package/src/server/migrations/20230221121203-condition-calculation.ts +82 -0
  272. package/src/server/migrations/20230221162902-jsonb-to-json.ts +51 -0
  273. package/src/server/migrations/20230411034722-manual-multi-form.ts +282 -0
  274. package/src/server/migrations/20230612021134-manual-collection-block.ts +138 -0
  275. package/src/server/migrations/20230710115902-manual-action-values.ts +78 -0
  276. package/src/server/triggers/collection.ts +146 -0
  277. package/src/server/triggers/index.ts +22 -0
  278. package/src/server/triggers/schedule.ts +567 -0
  279. package/src/server/types/Execution.ts +26 -0
  280. package/src/server/types/FlowNode.ts +21 -0
  281. package/src/server/types/Job.ts +18 -0
  282. package/src/server/types/Workflow.ts +36 -0
  283. package/src/server/types/index.ts +4 -0
  284. package/src/server/utils.ts +17 -0
  285. package/lib/client/triggers/schedule/DateFieldsSelect.d.ts +0 -2
  286. /package/lib/server/{models → types}/Execution.js +0 -0
  287. /package/lib/server/{models → types}/FlowNode.js +0 -0
  288. /package/lib/server/{models → types}/Job.js +0 -0
  289. /package/lib/server/{models → types}/Workflow.js +0 -0
@@ -0,0 +1,162 @@
1
+ import { Application } from '@nocobase/server';
2
+ import Database from '@nocobase/database';
3
+ import { getApp, sleep } from '..';
4
+ import { EXECUTION_STATUS, JOB_STATUS } from '../../constants';
5
+
6
+ describe('workflow > instructions > sql', () => {
7
+ let app: Application;
8
+ let db: Database;
9
+ let PostRepo;
10
+ let PostCollection;
11
+ let ReplyRepo;
12
+ let WorkflowModel;
13
+ let workflow;
14
+
15
+ beforeEach(async () => {
16
+ app = await getApp();
17
+
18
+ db = app.db;
19
+ WorkflowModel = db.getCollection('workflows').model;
20
+ PostCollection = db.getCollection('posts');
21
+ PostRepo = PostCollection.repository;
22
+ ReplyRepo = db.getCollection('replies').repository;
23
+
24
+ workflow = await WorkflowModel.create({
25
+ title: 'test workflow',
26
+ enabled: true,
27
+ type: 'collection',
28
+ config: {
29
+ mode: 1,
30
+ collection: 'posts',
31
+ },
32
+ });
33
+ });
34
+
35
+ afterEach(async () => await app.destroy());
36
+
37
+ describe('invalid', () => {
38
+ it('no sql', async () => {
39
+ const n1 = await workflow.createNode({
40
+ type: 'sql',
41
+ config: {},
42
+ });
43
+
44
+ const post = await PostRepo.create({ values: { title: 't1' } });
45
+
46
+ await sleep(500);
47
+
48
+ const [execution] = await workflow.getExecutions();
49
+ const [sqlJob] = await execution.getJobs({ order: [['id', 'ASC']] });
50
+ expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
51
+ expect(sqlJob.status).toBe(JOB_STATUS.RESOLVED);
52
+ });
53
+
54
+ it('empty sql', async () => {
55
+ const n1 = await workflow.createNode({
56
+ type: 'sql',
57
+ config: {
58
+ sql: '',
59
+ },
60
+ });
61
+
62
+ const post = await PostRepo.create({ values: { title: 't1' } });
63
+
64
+ await sleep(500);
65
+
66
+ const [execution] = await workflow.getExecutions();
67
+ const [sqlJob] = await execution.getJobs({ order: [['id', 'ASC']] });
68
+ expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
69
+ expect(sqlJob.status).toBe(JOB_STATUS.RESOLVED);
70
+ });
71
+
72
+ it('invalid sql', async () => {
73
+ const n1 = await workflow.createNode({
74
+ type: 'sql',
75
+ config: {
76
+ sql: '1',
77
+ },
78
+ });
79
+
80
+ const post = await PostRepo.create({ values: { title: 't1' } });
81
+
82
+ await sleep(500);
83
+
84
+ const [execution] = await workflow.getExecutions();
85
+ const [sqlJob] = await execution.getJobs({ order: [['id', 'ASC']] });
86
+ expect(execution.status).toBe(EXECUTION_STATUS.ERROR);
87
+ expect(sqlJob.status).toBe(JOB_STATUS.ERROR);
88
+ });
89
+ });
90
+
91
+ describe('sql with variables', () => {
92
+ it('update', async () => {
93
+ const queryInterface = db.sequelize.getQueryInterface();
94
+ const n1 = await workflow.createNode({
95
+ type: 'sql',
96
+ config: {
97
+ sql: `update ${PostCollection.quotedTableName()} set ${queryInterface.quoteIdentifier('read')}={{$context.data.id}} where ${queryInterface.quoteIdentifier('id')}={{$context.data.id}}`,
98
+ },
99
+ });
100
+
101
+ const n2 = await workflow.createNode({
102
+ type: 'query',
103
+ config: {
104
+ collection: 'posts',
105
+ params: {
106
+ filter: {
107
+ id: '{{ $context.data.id }}',
108
+ }
109
+ }
110
+ },
111
+ upstreamId: n1.id,
112
+ });
113
+
114
+ await n1.setDownstream(n2);
115
+
116
+ const post = await PostRepo.create({ values: { title: 't1' } });
117
+
118
+ await sleep(500);
119
+
120
+ const [execution] = await workflow.getExecutions();
121
+ const [sqlJob, queryJob] = await execution.getJobs({ order: [['id', 'ASC']] });
122
+ expect(sqlJob.status).toBe(JOB_STATUS.RESOLVED);
123
+ expect(queryJob.status).toBe(JOB_STATUS.RESOLVED);
124
+ expect(queryJob.result.read).toBe(post.id);
125
+ });
126
+
127
+ it('delete', async () => {
128
+ const queryInterface = db.sequelize.getQueryInterface();
129
+ const n1 = await workflow.createNode({
130
+ type: 'sql',
131
+ config: {
132
+ sql: `delete from ${PostCollection.quotedTableName()} where ${queryInterface.quoteIdentifier('id')}={{$context.data.id}};`,
133
+ },
134
+ });
135
+
136
+ const n2 = await workflow.createNode({
137
+ type: 'query',
138
+ config: {
139
+ collection: 'posts',
140
+ params: {
141
+ filter: {
142
+ id: '{{ $context.data.id }}',
143
+ }
144
+ }
145
+ },
146
+ upstreamId: n1.id,
147
+ });
148
+
149
+ await n1.setDownstream(n2);
150
+
151
+ const post = await PostRepo.create({ values: { title: 't1' } });
152
+
153
+ await sleep(500);
154
+
155
+ const [execution] = await workflow.getExecutions();
156
+ const [sqlJob, queryJob] = await execution.getJobs({ order: [['id', 'ASC']] });
157
+ expect(sqlJob.status).toBe(JOB_STATUS.RESOLVED);
158
+ expect(queryJob.status).toBe(JOB_STATUS.RESOLVED);
159
+ expect(queryJob.result).toBeNull();
160
+ });
161
+ });
162
+ });
@@ -0,0 +1,189 @@
1
+ import { Application } from '@nocobase/server';
2
+ import Database from '@nocobase/database';
3
+ import { getApp, sleep } from '..';
4
+ import type { WorkflowModel as WorkflowModelType } from '../../types';
5
+
6
+ describe('workflow > instructions > update', () => {
7
+ let app: Application;
8
+ let db: Database;
9
+ let PostRepo;
10
+ let WorkflowModel;
11
+ let workflow: WorkflowModelType;
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
+ enabled: true,
22
+ type: 'collection',
23
+ config: {
24
+ mode: 1,
25
+ collection: 'posts',
26
+ },
27
+ });
28
+ });
29
+
30
+ afterEach(() => db.close());
31
+
32
+ describe('update one', () => {
33
+ it('params: from context', async () => {
34
+ const n1 = await workflow.createNode({
35
+ type: 'update',
36
+ config: {
37
+ collection: 'posts',
38
+ params: {
39
+ filter: {
40
+ id: '{{$context.data.id}}',
41
+ },
42
+ values: {
43
+ published: true,
44
+ },
45
+ },
46
+ },
47
+ });
48
+
49
+ const post = await PostRepo.create({ values: { title: 't1' } });
50
+ expect(post.published).toBe(false);
51
+
52
+ await sleep(500);
53
+
54
+ const [execution] = await workflow.getExecutions();
55
+ const [job] = await execution.getJobs();
56
+ expect(job.result).toBe(1);
57
+
58
+ const updatedPost = await PostRepo.findById(post.id);
59
+ expect(updatedPost.published).toBe(true);
60
+ });
61
+
62
+ it('params: from job of node', async () => {
63
+ const n1 = await workflow.createNode({
64
+ type: 'query',
65
+ config: {
66
+ collection: 'posts',
67
+ params: {
68
+ filter: {
69
+ title: 'test',
70
+ },
71
+ },
72
+ },
73
+ });
74
+
75
+ const n2 = await workflow.createNode({
76
+ type: 'update',
77
+ config: {
78
+ collection: 'posts',
79
+ params: {
80
+ filter: {
81
+ id: `{{$jobsMapByNodeId.${n1.id}.id}}`,
82
+ },
83
+ values: {
84
+ title: 'changed',
85
+ },
86
+ },
87
+ },
88
+ upstreamId: n1.id,
89
+ });
90
+
91
+ await n1.setDownstream(n2);
92
+
93
+ // NOTE: the result of post immediately created will not be changed by workflow
94
+ const { id } = await PostRepo.create({ values: { title: 'test' } });
95
+
96
+ await sleep(500);
97
+
98
+ // should get from db
99
+ const post = await PostRepo.findById(id);
100
+ expect(post.title).toBe('changed');
101
+ });
102
+ });
103
+
104
+ describe('update batch', () => {
105
+ it('individualHooks off should not trigger other workflow', async () => {
106
+ const w2 = await WorkflowModel.create({
107
+ enabled: true,
108
+ type: 'collection',
109
+ config: {
110
+ mode: 2,
111
+ collection: 'posts',
112
+ },
113
+ });
114
+
115
+ const n1 = await workflow.createNode({
116
+ type: 'update',
117
+ config: {
118
+ collection: 'posts',
119
+ params: {
120
+ filter: {
121
+ id: '{{$context.data.id}}',
122
+ },
123
+ values: {
124
+ published: true,
125
+ },
126
+ individualHooks: false,
127
+ },
128
+ },
129
+ });
130
+
131
+ const post = await PostRepo.create({ values: { title: 't1' } });
132
+ expect(post.published).toBe(false);
133
+
134
+ await sleep(500);
135
+
136
+ const [execution] = await workflow.getExecutions();
137
+ const [job] = await execution.getJobs();
138
+ expect(job.result).toBe(1);
139
+
140
+ const updatedPost = await PostRepo.findById(post.id);
141
+ expect(updatedPost.published).toBe(true);
142
+
143
+ const w2Exes = await w2.getExecutions();
144
+ expect(w2Exes.length).toBe(0);
145
+ });
146
+
147
+ it('individualHooks on should trigger other workflow', async () => {
148
+ const w2 = await WorkflowModel.create({
149
+ enabled: true,
150
+ type: 'collection',
151
+ config: {
152
+ mode: 2,
153
+ collection: 'posts',
154
+ },
155
+ });
156
+
157
+ const n1 = await workflow.createNode({
158
+ type: 'update',
159
+ config: {
160
+ collection: 'posts',
161
+ params: {
162
+ filter: {
163
+ id: '{{$context.data.id}}',
164
+ },
165
+ values: {
166
+ published: true,
167
+ },
168
+ individualHooks: true,
169
+ },
170
+ },
171
+ });
172
+
173
+ const post = await PostRepo.create({ values: { title: 't1' } });
174
+ expect(post.published).toBe(false);
175
+
176
+ await sleep(500);
177
+
178
+ const [execution] = await workflow.getExecutions();
179
+ const [job] = await execution.getJobs();
180
+ expect(job.result).toBe(1);
181
+
182
+ const updatedPost = await PostRepo.findById(post.id);
183
+ expect(updatedPost.published).toBe(true);
184
+
185
+ const w2Exes = await w2.getExecutions();
186
+ expect(w2Exes.length).toBe(1);
187
+ });
188
+ });
189
+ });
@@ -0,0 +1,333 @@
1
+ import { Application } from '@nocobase/server';
2
+ import Database from '@nocobase/database';
3
+ import { getApp, sleep } from '..';
4
+ import { EXECUTION_STATUS } from '../../constants';
5
+
6
+ describe('workflow > triggers > collection', () => {
7
+ let app: Application;
8
+ let db: Database;
9
+ let CategoryRepo;
10
+ let PostRepo;
11
+ let CommentRepo;
12
+ let TagRepo;
13
+ let WorkflowModel;
14
+
15
+ beforeEach(async () => {
16
+ app = await getApp();
17
+
18
+ db = app.db;
19
+ WorkflowModel = db.getCollection('workflows').model;
20
+ CategoryRepo = db.getCollection('categories').repository;
21
+ PostRepo = db.getCollection('posts').repository;
22
+ CommentRepo = db.getCollection('comments').repository;
23
+ TagRepo = db.getCollection('tags').repository;
24
+ });
25
+
26
+ afterEach(() => db.close());
27
+
28
+ describe('toggle', () => {
29
+ it('when collection change', async () => {
30
+ const workflow = await WorkflowModel.create({
31
+ enabled: true,
32
+ type: 'collection',
33
+ config: {
34
+ mode: 1,
35
+ collection: 'posts',
36
+ },
37
+ });
38
+
39
+ await workflow.update({
40
+ config: {
41
+ ...workflow.config,
42
+ collection: 'comments',
43
+ },
44
+ });
45
+
46
+ const post = await PostRepo.create({ values: { title: 't1' } });
47
+
48
+ await sleep(500);
49
+
50
+ const executions = await workflow.getExecutions();
51
+ expect(executions.length).toBe(0);
52
+ });
53
+ });
54
+
55
+ describe('model context', () => {
56
+ it('with association', async () => {
57
+ const workflow = await WorkflowModel.create({
58
+ enabled: true,
59
+ type: 'collection',
60
+ config: {
61
+ mode: 1,
62
+ collection: 'posts',
63
+ },
64
+ });
65
+
66
+ const post = await PostRepo.create({ values: { title: 't1', category: { title: 'c1' } } });
67
+
68
+ await sleep(500);
69
+
70
+ const executions = await workflow.getExecutions();
71
+ expect(executions.length).toBe(1);
72
+ expect(executions[0].context.data.title).toBe('t1');
73
+ expect(executions[0].context.data.category.title).toBe('c1');
74
+ });
75
+ });
76
+
77
+ describe('config.changed', () => {
78
+ it('no changed config', async () => {
79
+ const workflow = await WorkflowModel.create({
80
+ enabled: true,
81
+ type: 'collection',
82
+ config: {
83
+ mode: 2,
84
+ collection: 'posts',
85
+ },
86
+ });
87
+
88
+ const post = await PostRepo.create({ values: { title: 't1' } });
89
+ await PostRepo.update({ filterByTk: post.id, values: { title: 't2' } });
90
+
91
+ await sleep(500);
92
+
93
+ const executions = await workflow.getExecutions();
94
+ expect(executions.length).toBe(1);
95
+ expect(executions[0].context.data.title).toBe('t2');
96
+ });
97
+
98
+ it('field in changed config', async () => {
99
+ const workflow = await WorkflowModel.create({
100
+ enabled: true,
101
+ type: 'collection',
102
+ config: {
103
+ mode: 2,
104
+ collection: 'posts',
105
+ changed: ['title'],
106
+ },
107
+ });
108
+
109
+ const post = await PostRepo.create({ values: { title: 't1' } });
110
+ await PostRepo.update({ filterByTk: post.id, values: { title: 't2' } });
111
+
112
+ await sleep(500);
113
+
114
+ const executions = await workflow.getExecutions();
115
+ expect(executions.length).toBe(1);
116
+ expect(executions[0].status).toBe(EXECUTION_STATUS.RESOLVED);
117
+ expect(executions[0].context.data.title).toBe('t2');
118
+ });
119
+
120
+ it('field not in changed config', async () => {
121
+ const workflow = await WorkflowModel.create({
122
+ enabled: true,
123
+ type: 'collection',
124
+ config: {
125
+ mode: 2,
126
+ collection: 'posts',
127
+ changed: ['published'],
128
+ },
129
+ });
130
+
131
+ const post = await PostRepo.create({ values: { title: 't1' } });
132
+ await PostRepo.update({ filterByTk: post.id, values: { title: 't2' } });
133
+
134
+ await sleep(500);
135
+
136
+ const executions = await workflow.getExecutions();
137
+ expect(executions.length).toBe(0);
138
+ });
139
+ });
140
+
141
+ describe('config.appends', () => {
142
+ it('non-appended association could not be accessed', async () => {
143
+ const workflow = await WorkflowModel.create({
144
+ enabled: true,
145
+ type: 'collection',
146
+ config: {
147
+ mode: 1,
148
+ collection: 'posts',
149
+ },
150
+ });
151
+
152
+ await workflow.createNode({
153
+ type: 'echo',
154
+ });
155
+
156
+ const category = await CategoryRepo.create({ values: { title: 'c1' } });
157
+
158
+ const post = await PostRepo.create({
159
+ values: {
160
+ title: 't1',
161
+ categoryId: category.id,
162
+ },
163
+ });
164
+
165
+ await sleep(500);
166
+
167
+ const [execution] = await workflow.getExecutions();
168
+ expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
169
+ const [job] = await execution.getJobs();
170
+ expect(job.result.data.category).toBeUndefined();
171
+ });
172
+
173
+ it('appends association could be accessed', async () => {
174
+ const workflow = await WorkflowModel.create({
175
+ enabled: true,
176
+ type: 'collection',
177
+ config: {
178
+ mode: 1,
179
+ collection: 'posts',
180
+ appends: ['category'],
181
+ },
182
+ });
183
+
184
+ await workflow.createNode({
185
+ type: 'echo',
186
+ });
187
+
188
+ const category = await CategoryRepo.create({ values: { title: 'c1' } });
189
+
190
+ const post = await PostRepo.create({
191
+ values: {
192
+ title: 't1',
193
+ categoryId: category.id,
194
+ },
195
+ });
196
+
197
+ await sleep(500);
198
+
199
+ const [execution] = await workflow.getExecutions();
200
+ expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
201
+ const [job] = await execution.getJobs();
202
+ expect(job.result.data.category.title).toBe('c1');
203
+ });
204
+
205
+ it('appends belongsTo null', async () => {
206
+ const workflow = await WorkflowModel.create({
207
+ enabled: true,
208
+ type: 'collection',
209
+ config: {
210
+ mode: 1,
211
+ collection: 'posts',
212
+ appends: ['category'],
213
+ },
214
+ });
215
+
216
+ await workflow.createNode({
217
+ type: 'echo',
218
+ });
219
+
220
+ const post = await PostRepo.create({
221
+ values: {
222
+ title: 't1',
223
+ },
224
+ });
225
+
226
+ await sleep(500);
227
+
228
+ const [execution] = await workflow.getExecutions();
229
+ expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
230
+ const [job] = await execution.getJobs();
231
+ expect(job.result.data.category).toBeNull();
232
+ });
233
+
234
+ it('appends hasMany', async () => {
235
+ const workflow = await WorkflowModel.create({
236
+ enabled: true,
237
+ type: 'collection',
238
+ config: {
239
+ mode: 1,
240
+ collection: 'posts',
241
+ appends: ['comments'],
242
+ },
243
+ });
244
+
245
+ await workflow.createNode({
246
+ type: 'echo',
247
+ });
248
+
249
+ const comments = await CommentRepo.create({ values: [{}] });
250
+
251
+ const post = await PostRepo.create({
252
+ values: {
253
+ title: 't1',
254
+ comments: comments.map((item) => item.id),
255
+ },
256
+ });
257
+
258
+ await sleep(500);
259
+
260
+ const [execution] = await workflow.getExecutions();
261
+ expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
262
+ const [job] = await execution.getJobs();
263
+ expect(job.result.data.comments.length).toBe(1);
264
+ });
265
+
266
+ it('appends belongsToMany', async () => {
267
+ const workflow = await WorkflowModel.create({
268
+ enabled: true,
269
+ type: 'collection',
270
+ config: {
271
+ mode: 1,
272
+ collection: 'posts',
273
+ appends: ['tags'],
274
+ },
275
+ });
276
+
277
+ await workflow.createNode({
278
+ type: 'echo',
279
+ });
280
+
281
+ const tags = await TagRepo.create({ values: [{}] });
282
+
283
+ const post = await PostRepo.create({
284
+ values: {
285
+ title: 't1',
286
+ tags: tags.map((item) => item.id),
287
+ },
288
+ });
289
+
290
+ await sleep(500);
291
+
292
+ const [execution] = await workflow.getExecutions();
293
+ expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
294
+ const [job] = await execution.getJobs();
295
+ expect(job.result.data.tags.length).toBe(1);
296
+ });
297
+
298
+ describe('appends depth > 1', () => {
299
+ it('create with associtions', async () => {
300
+ const workflow = await WorkflowModel.create({
301
+ enabled: true,
302
+ type: 'collection',
303
+ config: {
304
+ mode: 1,
305
+ collection: 'categories',
306
+ appends: ['posts.tags'],
307
+ },
308
+ });
309
+
310
+ const tags = await TagRepo.create({ values: [{}] });
311
+ const tagIds = tags.map((item) => item.id);
312
+
313
+ const category = await CategoryRepo.create({
314
+ values: {
315
+ title: 't1',
316
+ posts: [
317
+ { title: 't1', tags: tagIds },
318
+ { title: 't2', tags: tagIds },
319
+ ],
320
+ },
321
+ });
322
+
323
+ await sleep(500);
324
+
325
+ const [execution] = await workflow.getExecutions();
326
+ expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
327
+ expect(execution.context.data.posts.length).toBe(2);
328
+ expect(execution.context.data.posts.map((item) => item.title)).toEqual(['t1', 't2']);
329
+ expect(execution.context.data.posts.map((item) => item.tags.map((tag) => tag.id))).toEqual([tagIds, tagIds]);
330
+ });
331
+ });
332
+ });
333
+ });