@nocobase/plugin-workflow-aggregate 1.0.0-alpha.2 → 1.0.0-alpha.3

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.
@@ -1,18 +0,0 @@
1
- import { Plugin } from '@nocobase/client';
2
- import WorkflowPlugin from '@nocobase/plugin-workflow/client';
3
-
4
- import AggregateInstruction from './AggregateInstruction';
5
-
6
- export default class extends Plugin {
7
- async afterAdd() {
8
- // await this.app.pm.add()
9
- }
10
-
11
- async beforeLoad() {}
12
-
13
- // You can get and modify the app instance here
14
- async load() {
15
- const workflow = this.app.pm.get('workflow') as WorkflowPlugin;
16
- workflow.registerInstruction('aggregate', AggregateInstruction);
17
- }
18
- }
package/src/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './server';
2
- export { default } from './server';
@@ -1,12 +0,0 @@
1
- {
2
- "Aggregate": "Aggregate",
3
- "Counting, summing, finding maximum, minimum, and average values for multiple records of a collection or associated data of a record.":
4
- "Counting, summing, finding maximum, minimum, and average values for multiple records of a collection or associated data of a record.",
5
- "Aggregator function": "Aggregator function",
6
- "Target type": "Target type",
7
- "Data of collection": "Data of collection",
8
- "Data of associated collection": "Data of associated collection",
9
- "Field to aggregate": "Field to aggregate",
10
- "Distinct": "Distinct",
11
- "Query result": "Query result"
12
- }
@@ -1,12 +0,0 @@
1
- import { useTranslation } from 'react-i18next';
2
-
3
- export const NAMESPACE = 'workflow-aggregate';
4
-
5
- export function useLang(key: string, options = {}) {
6
- const { t } = usePluginTranslation(options);
7
- return t(key);
8
- }
9
-
10
- export function usePluginTranslation(options) {
11
- return useTranslation(NAMESPACE, options);
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "Aggregate": "집계 조회",
3
- "Counting, summing, finding maximum, minimum, and average values for multiple records of a collection or associated data of a record.":
4
- "데이터 테이블의 여러 레코드 또는 레코드의 관련 데이터에 대한 카운트, 합계, 최대값, 최소값, 평균값을 찾습니다.",
5
- "Aggregator function": "집계 함수",
6
- "Target type": "대상 유형",
7
- "Data of collection": "데이터 테이블 데이터",
8
- "Data of associated collection": "관련 데이터 테이블 데이터",
9
- "Field to aggregate": "집계할 필드",
10
- "Distinct": "중복 제거",
11
- "Query result": "쿼리 결과"
12
- }
@@ -1,12 +0,0 @@
1
- {
2
- "Aggregate": "聚合查询",
3
- "Counting, summing, finding maximum, minimum, and average values for multiple records of a collection or associated data of a record.":
4
- "对一个数据表里的多条数据或者一条数据里的关系数据进行统计、求和、求最大值、最小值、平均值。",
5
- "Aggregator function": "聚合函数",
6
- "Target type": "目标类型",
7
- "Data of collection": "数据表数据",
8
- "Data of associated collection": "关联数据表数据",
9
- "Field to aggregate": "聚合字段",
10
- "Distinct": "去重",
11
- "Query result": "查询结果"
12
- }
@@ -1,41 +0,0 @@
1
- import { parseCollectionName } from '@nocobase/data-source-manager';
2
- import { DataTypes } from '@nocobase/database';
3
- import { Processor, Instruction, JOB_STATUS, FlowNodeModel } from '@nocobase/plugin-workflow';
4
-
5
- const aggregators = {
6
- count: 'count',
7
- sum: 'sum',
8
- avg: 'avg',
9
- min: 'min',
10
- max: 'max',
11
- };
12
-
13
- export default class extends Instruction {
14
- async run(node: FlowNodeModel, input, processor: Processor) {
15
- const { aggregator, associated, collection, association = {}, params = {} } = node.config;
16
- const options = processor.getParsedValue(params, node.id);
17
- const [dataSourceName, collectionName] = parseCollectionName(collection);
18
- const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
19
- const repo = associated
20
- ? collectionManager.getRepository(
21
- `${association?.associatedCollection}.${association.name}`,
22
- processor.getParsedValue(association?.associatedKey, node.id),
23
- )
24
- : collectionManager.getRepository(collectionName);
25
-
26
- if (!options.dataType && aggregator === 'avg') {
27
- options.dataType = DataTypes.DOUBLE;
28
- }
29
-
30
- const result = await repo.aggregate({
31
- ...options,
32
- method: aggregators[aggregator],
33
- // transaction: processor.transaction,
34
- });
35
-
36
- return {
37
- result: options.dataType === DataTypes.DOUBLE ? Number(result) : result,
38
- status: JOB_STATUS.RESOLVED,
39
- };
40
- }
41
- }
@@ -1,11 +0,0 @@
1
- import WorkflowPlugin from '@nocobase/plugin-workflow';
2
- import { Plugin } from '@nocobase/server';
3
-
4
- import AggregateInstruction from './AggregateInstruction';
5
-
6
- export default class extends Plugin {
7
- async load() {
8
- const workflowPlugin = this.app.getPlugin<WorkflowPlugin>(WorkflowPlugin);
9
- workflowPlugin.registerInstruction('aggregate', AggregateInstruction);
10
- }
11
- }
@@ -1,328 +0,0 @@
1
- import Database from '@nocobase/database';
2
- import { Application } from '@nocobase/server';
3
- import { getApp, sleep } from '@nocobase/plugin-workflow-test';
4
-
5
- import Plugin from '..';
6
- import { EXECUTION_STATUS } from '@nocobase/plugin-workflow';
7
-
8
- describe('workflow > instructions > aggregate', () => {
9
- let app: Application;
10
- let db: Database;
11
- let PostRepo;
12
- let CommentRepo;
13
- let TagRepo;
14
- let WorkflowModel;
15
- let workflow;
16
-
17
- beforeEach(async () => {
18
- app = await getApp({
19
- plugins: [Plugin],
20
- });
21
-
22
- db = app.db;
23
- WorkflowModel = db.getCollection('workflows').model;
24
- WorkflowModel = db.getCollection('workflows').model;
25
- PostRepo = db.getCollection('posts').repository;
26
- CommentRepo = db.getCollection('comments').repository;
27
- TagRepo = db.getCollection('tags').repository;
28
-
29
- workflow = await WorkflowModel.create({
30
- enabled: true,
31
- type: 'collection',
32
- config: {
33
- mode: 1,
34
- collection: 'posts',
35
- },
36
- });
37
- });
38
-
39
- afterEach(() => app.destroy());
40
-
41
- describe('based on collection', () => {
42
- it('count', async () => {
43
- const n1 = await workflow.createNode({
44
- type: 'aggregate',
45
- config: {
46
- aggregator: 'count',
47
- collection: 'posts',
48
- params: {
49
- field: 'id',
50
- },
51
- },
52
- });
53
-
54
- const post = await PostRepo.create({ values: { title: 't1' } });
55
-
56
- await sleep(500);
57
-
58
- const [execution] = await workflow.getExecutions();
59
- const [job] = await execution.getJobs();
60
- expect(job.result).toBe(1);
61
- });
62
-
63
- it('sum', async () => {
64
- const n1 = await workflow.createNode({
65
- type: 'aggregate',
66
- config: {
67
- aggregator: 'sum',
68
- collection: 'posts',
69
- params: {
70
- field: 'read',
71
- },
72
- },
73
- });
74
-
75
- const p1 = await PostRepo.create({ values: { title: 't1', read: 1 } });
76
-
77
- await sleep(500);
78
-
79
- const [e1] = await workflow.getExecutions();
80
- const [j1] = await e1.getJobs();
81
- expect(j1.result).toBe(1);
82
-
83
- const p2 = await PostRepo.create({ values: { title: 't2', read: 2 } });
84
-
85
- await sleep(500);
86
-
87
- const [e2] = await workflow.getExecutions({ order: [['id', 'desc']] });
88
- const [j2] = await e2.getJobs();
89
- expect(j2.result).toBe(3);
90
- });
91
-
92
- it('avg', async () => {
93
- const n1 = await workflow.createNode({
94
- type: 'aggregate',
95
- config: {
96
- aggregator: 'avg',
97
- collection: 'posts',
98
- params: {
99
- field: 'read',
100
- },
101
- },
102
- });
103
-
104
- const p1 = await PostRepo.create({ values: { title: 't1', read: 1 } });
105
-
106
- await sleep(500);
107
-
108
- const [e1] = await workflow.getExecutions();
109
- const [j1] = await e1.getJobs();
110
- expect(j1.result).toBe(1);
111
-
112
- const p2 = await PostRepo.create({ values: { title: 't2', read: 2 } });
113
-
114
- await sleep(500);
115
-
116
- const [e2] = await workflow.getExecutions({ order: [['id', 'desc']] });
117
- const [j2] = await e2.getJobs();
118
- expect(j2.result).toBe(1.5);
119
- });
120
-
121
- it('min', async () => {
122
- const n1 = await workflow.createNode({
123
- type: 'aggregate',
124
- config: {
125
- aggregator: 'min',
126
- collection: 'posts',
127
- params: {
128
- field: 'read',
129
- },
130
- },
131
- });
132
-
133
- const p1 = await PostRepo.create({ values: { title: 't1', read: 1 } });
134
-
135
- await sleep(500);
136
-
137
- const [e1] = await workflow.getExecutions();
138
- const [j1] = await e1.getJobs();
139
- expect(j1.result).toBe(1);
140
-
141
- const p2 = await PostRepo.create({ values: { title: 't2', read: 2 } });
142
-
143
- await sleep(500);
144
-
145
- const [e2] = await workflow.getExecutions({ order: [['id', 'desc']] });
146
- const [j2] = await e2.getJobs();
147
- expect(j2.result).toBe(1);
148
- });
149
-
150
- it('max', async () => {
151
- const n1 = await workflow.createNode({
152
- type: 'aggregate',
153
- config: {
154
- aggregator: 'max',
155
- collection: 'posts',
156
- params: {
157
- field: 'read',
158
- },
159
- },
160
- });
161
-
162
- const p1 = await PostRepo.create({ values: { title: 't1', read: 1 } });
163
-
164
- await sleep(500);
165
-
166
- const [e1] = await workflow.getExecutions();
167
- const [j1] = await e1.getJobs();
168
- expect(j1.result).toBe(1);
169
-
170
- const p2 = await PostRepo.create({ values: { title: 't2', read: 2 } });
171
-
172
- await sleep(500);
173
-
174
- const [e2] = await workflow.getExecutions({ order: [['id', 'desc']] });
175
- const [j2] = await e2.getJobs();
176
- expect(j2.result).toBe(2);
177
- });
178
- });
179
-
180
- describe('based on data associated collection', () => {
181
- it('count', async () => {
182
- const n1 = await workflow.createNode({
183
- type: 'aggregate',
184
- config: {
185
- aggregator: 'count',
186
- collection: 'comments',
187
- associated: true,
188
- association: {
189
- name: 'comments',
190
- associatedKey: '{{$context.data.id}}',
191
- associatedCollection: 'posts',
192
- },
193
- params: {
194
- field: 'id',
195
- },
196
- },
197
- });
198
- const n2 = await workflow.createNode({
199
- upstreamId: n1.id,
200
- type: 'aggregate',
201
- config: {
202
- aggregator: 'count',
203
- collection: 'comments',
204
- associated: true,
205
- association: {
206
- name: 'comments',
207
- associatedKey: '{{$context.data.id}}',
208
- associatedCollection: 'posts',
209
- },
210
- params: {
211
- field: 'id',
212
- filter: {
213
- $and: [{ status: 1 }],
214
- },
215
- },
216
- },
217
- });
218
- await n1.setDownstream(n2);
219
-
220
- await CommentRepo.create({ values: [{}, {}] });
221
-
222
- const p1 = await PostRepo.create({ values: { title: 't1', comments: [{}, { status: 1 }] } });
223
-
224
- await sleep(500);
225
-
226
- const [e1] = await workflow.getExecutions();
227
- const [j1, j2] = await e1.getJobs({ order: [['id', 'ASC']] });
228
- expect(j1.result).toBe(2);
229
- expect(j2.result).toBe(1);
230
- });
231
-
232
- it('sum', async () => {
233
- const PostModel = db.getCollection('posts').model;
234
- const p1 = await PostModel.create({ title: 't1', read: 1 });
235
-
236
- const n1 = await workflow.createNode({
237
- type: 'create',
238
- config: {
239
- collection: 'tags',
240
- params: {
241
- values: {
242
- posts: [p1.id, '{{$context.data.id}}'],
243
- },
244
- },
245
- },
246
- });
247
- const n2 = await workflow.createNode({
248
- upstreamId: n1.id,
249
- type: 'aggregate',
250
- config: {
251
- aggregator: 'sum',
252
- collection: 'posts',
253
- associated: true,
254
- association: {
255
- name: 'posts',
256
- associatedKey: `{{$jobsMapByNodeKey.${n1.key}.id}}`,
257
- associatedCollection: 'tags',
258
- },
259
- params: {
260
- field: 'read',
261
- },
262
- },
263
- });
264
- await n1.setDownstream(n2);
265
- const n3 = await workflow.createNode({
266
- upstreamId: n2.id,
267
- type: 'aggregate',
268
- config: {
269
- aggregator: 'sum',
270
- collection: 'posts',
271
- associated: true,
272
- association: {
273
- name: 'posts',
274
- associatedKey: `{{$jobsMapByNodeKey.${n1.key}.id}}`,
275
- associatedCollection: 'tags',
276
- },
277
- params: {
278
- field: 'read',
279
- filter: {
280
- $and: [{ title: 't1' }],
281
- },
282
- },
283
- },
284
- });
285
- await n2.setDownstream(n3);
286
-
287
- await TagRepo.create({ values: [{}, {}] });
288
-
289
- const p2 = await PostRepo.create({ values: { title: 't2', read: 2 } });
290
-
291
- await sleep(500);
292
-
293
- const [e1] = await workflow.getExecutions();
294
- const [j1, j2, j3] = await e1.getJobs({ order: [['id', 'ASC']] });
295
- expect(j2.result).toBe(3);
296
- expect(j3.result).toBe(1);
297
- });
298
- });
299
-
300
- describe('multiple data source', () => {
301
- it('query on another data source', async () => {
302
- const AnotherPostRepo = app.dataSourceManager.dataSources.get('another').collectionManager.getRepository('posts');
303
- const post = await AnotherPostRepo.create({ values: { title: 't1' } });
304
- const p1s = await AnotherPostRepo.find();
305
- expect(p1s.length).toBe(1);
306
-
307
- const n1 = await workflow.createNode({
308
- type: 'aggregate',
309
- config: {
310
- collection: 'another:posts',
311
- aggregator: 'count',
312
- params: {
313
- field: 'id',
314
- },
315
- },
316
- });
317
-
318
- await PostRepo.create({ values: { title: 't1' } });
319
-
320
- await sleep(500);
321
-
322
- const [execution] = await workflow.getExecutions();
323
- expect(execution.status).toBe(EXECUTION_STATUS.RESOLVED);
324
- const [job] = await execution.getJobs();
325
- expect(job.result).toBe(1);
326
- });
327
- });
328
- });
@@ -1 +0,0 @@
1
- export { default } from './Plugin';