@nocobase/plugin-workflow-aggregate 0.20.0-alpha.9 → 0.21.0-alpha.2
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.
- package/dist/client/AggregateInstruction.d.ts +5 -4
- package/dist/client/index.js +4 -4
- package/dist/externalVersion.js +8 -7
- package/dist/server/AggregateInstruction.js +5 -3
- package/package.json +2 -2
- package/src/client/AggregateInstruction.tsx +31 -17
- package/src/client/__e2e__/DataOfCollection.test.ts +40 -20
- package/src/server/AggregateInstruction.ts +6 -4
- package/src/server/__tests__/instruction.test.ts +30 -0
|
@@ -161,11 +161,11 @@ export default class extends Instruction {
|
|
|
161
161
|
title: string;
|
|
162
162
|
'x-decorator': string;
|
|
163
163
|
'x-component': string;
|
|
164
|
+
'x-use-component-props': () => {
|
|
165
|
+
options: any[];
|
|
166
|
+
className: string;
|
|
167
|
+
};
|
|
164
168
|
'x-component-props': {
|
|
165
|
-
useProps(): {
|
|
166
|
-
options: any[];
|
|
167
|
-
className: string;
|
|
168
|
-
};
|
|
169
169
|
dynamicComponent: string;
|
|
170
170
|
};
|
|
171
171
|
'x-reactions': {
|
|
@@ -190,6 +190,7 @@ export default class extends Instruction {
|
|
|
190
190
|
ValueBlock: (() => JSX.Element) & {
|
|
191
191
|
Initializer: () => JSX.Element;
|
|
192
192
|
Result: (props: any) => JSX.Element;
|
|
193
|
+
Designer: () => JSX.Element;
|
|
193
194
|
};
|
|
194
195
|
AssociatedConfig: typeof AssociatedConfig;
|
|
195
196
|
};
|
package/dist/client/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(e,t){typeof exports=="object"&&typeof module!="undefined"?t(exports,require("@nocobase/client"),require("react/jsx-runtime"),require("@formily/react"),require("antd"),require("react"),require("@nocobase/plugin-workflow/client"),require("react-i18next")):typeof define=="function"&&define.amd?define(["exports","@nocobase/client","react/jsx-runtime","@formily/react","antd","react","@nocobase/plugin-workflow/client","react-i18next"],t):(e=typeof globalThis!="undefined"?globalThis:e||self,t(e["@nocobase/plugin-workflow-aggregate"]={},e["@nocobase/client"],e.jsxRuntime,e["@formily/react"],e.antd,e.react,e["@nocobase/plugin-workflow"],e["react-i18next"]))})(this,function(e,t,o,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
(function(e,t){typeof exports=="object"&&typeof module!="undefined"?t(exports,require("@nocobase/client"),require("react/jsx-runtime"),require("@formily/react"),require("antd"),require("react"),require("@nocobase/plugin-workflow/client"),require("react-i18next")):typeof define=="function"&&define.amd?define(["exports","@nocobase/client","react/jsx-runtime","@formily/react","antd","react","@nocobase/plugin-workflow/client","react-i18next"],t):(e=typeof globalThis!="undefined"?globalThis:e||self,t(e["@nocobase/plugin-workflow-aggregate"]={},e["@nocobase/client"],e.jsxRuntime,e["@formily/react"],e.antd,e.react,e["@nocobase/plugin-workflow"],e["react-i18next"]))})(this,function(e,t,o,r,S,h,s,b){"use strict";var J=Object.defineProperty,W=Object.defineProperties;var Y=Object.getOwnPropertyDescriptors;var I=Object.getOwnPropertySymbols;var O=Object.prototype.hasOwnProperty,A=Object.prototype.propertyIsEnumerable;var j=(e,t,o)=>t in e?J(e,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[t]=o,N=(e,t)=>{for(var o in t||(t={}))O.call(t,o)&&j(e,o,t[o]);if(I)for(var o of I(t))A.call(t,o)&&j(e,o,t[o]);return e},V=(e,t)=>W(e,Y(t));var G=(e,t)=>{var o={};for(var r in e)O.call(e,r)&&t.indexOf(r)<0&&(o[r]=e[r]);if(e!=null&&I)for(var r of I(e))t.indexOf(r)<0&&A.call(e,r)&&(o[r]=e[r]);return o};var g=(e,t,o)=>(j(e,typeof t!="symbol"?t+"":t,o),o);var C=(e,t,o)=>new Promise((r,S)=>{var h=i=>{try{b(o.next(i))}catch(v){S(v)}},s=i=>{try{b(o.throw(i))}catch(v){S(v)}},b=i=>i.done?r(i.value):Promise.resolve(i.value).then(h,s);b((o=o.apply(e,t)).next())});const i="workflow-aggregate";function v(l,c={}){const{t:n}=B(c);return n(l)}function B(l){return b.useTranslation(i,l)}function K(l){return["hasMany","belongsToMany"].includes(l.type)}function P(){const l=t.useCompile();return[s.nodesOptions,s.triggerOptions].map(c=>{var d;const n=(d=c.useOptions({types:[K],appends:null,depth:4}))==null?void 0:d.filter(Boolean);return{label:l(c.label),value:c.value,key:c.value,children:l(n),disabled:n&&!n.length}})}function R(d){var u=d,{value:l,onChange:c}=u,n=G(u,["value","onChange"]);const{setValuesIn:x}=r.useForm(),{getCollection:k}=t.useCollectionManager_deprecated(),M=P(),[F,q]=h.useState(M),{associatedKey:_="",name:z}=l!=null?l:{};let w=[];const D=_.match(/^{{(.*)}}$/);D&&(w=[...D[1].trim().split(".").slice(0,-1),z]);const U=m=>C(this,null,function*(){var p;const a=m[m.length-1];!((p=a.children)!=null&&p.length)&&!a.isLeaf&&a.loadChildren&&(yield a.loadChildren(a),q(f=>[...f]))});h.useEffect(()=>{C(this,null,function*(){var p;if(!w||F.length<=1)return;let a=null;for(let f=0;f<w.length;f++){const T=w[f];try{f===0?a=F.find(y=>y.value===T):(a.loadChildren&&!((p=a.children)!=null&&p.length)&&(yield a.loadChildren(a)),a=a.children.find(y=>y.value===T))}catch(y){console.error(y)}}q([...F])})},[l,F.length]);const Q=h.useCallback((m,a)=>{if(!(m!=null&&m.length)){x("collection",null),c({});return}const{field:p}=a.pop();if(!p||!["hasMany","belongsToMany"].includes(p.type))return;const{collectionName:f,target:T,name:y,dataSourceKey:$}=p,X=k(f,$).fields.find(H=>H.primaryKey);x("collection",`${$}:${T}`),c({name:y,associatedKey:`{{${m.slice(0,-1).join(".")}.${X.name}}}`,associatedCollection:t.joinCollectionName($,f)})},[c]);return o.jsx(S.Cascader,V(N({},n),{value:w,options:F,changeOnSelect:!0,onChange:Q,loadData:U}))}class E extends s.Instruction{constructor(){super(...arguments);g(this,"title",`{{t("Aggregate", { ns: "${i}" })}}`);g(this,"type","aggregate");g(this,"group","collection");g(this,"description",`{{t("Counting, summing, finding maximum, minimum, and average values for multiple records of a collection or associated data of a record.", { ns: "${i}" })}}`);g(this,"fieldset",{aggregator:{type:"string",title:`{{t("Aggregator function", { ns: "${i}" })}}`,"x-decorator":"FormItem","x-component":"Radio.Group",enum:[{label:"COUNT",value:"count"},{label:"SUM",value:"sum"},{label:"AVG",value:"avg"},{label:"MIN",value:"min"},{label:"MAX",value:"max"}],required:!0,default:"count"},associated:{type:"boolean",title:`{{t("Target type", { ns: "${i}" })}}`,"x-decorator":"FormItem","x-component":"Radio.Group",enum:[{label:`{{t("Data of collection", { ns: "${i}" })}}`,value:!1},{label:`{{t("Data of associated collection", { ns: "${i}" })}}`,value:!0}],required:!0,default:!1,"x-reactions":[{target:"collection",effects:["onFieldValueChange"],fulfill:{state:{value:null}}},{target:"association",effects:["onFieldValueChange"],fulfill:{state:{value:null}}}]},collectionField:{type:"void","x-decorator":"SchemaComponentContext.Provider","x-decorator-props":{value:{designable:!1}},"x-component":"Grid",properties:{row:{type:"void","x-component":"Grid.Row",properties:{target:{type:"void","x-component":"Grid.Col",properties:{collection:{type:"string",required:!0,"x-decorator":"FormItem","x-component":"DataSourceCollectionCascader",title:`{{t("Data of collection", { ns: "${i}" })}}`,"x-reactions":[{dependencies:["associated"],fulfill:{state:{display:'{{$deps[0] ? "hidden" : "visible"}}'}}},{target:"params.field",effects:["onFieldValueChange"],fulfill:{state:{value:null}}},{target:"params.filter",effects:["onFieldValueChange"],fulfill:{state:{value:null}}}]},association:{type:"object",title:`{{t("Data of associated collection", { ns: "${i}" })}}`,"x-decorator":"FormItem","x-component":"AssociatedConfig","x-component-props":{changeOnSelect:!0},"x-reactions":[{dependencies:["associated"],fulfill:{state:{visible:"{{!!$deps[0]}}"}}}],required:!0}}},field:{type:"void","x-component":"Grid.Col",properties:{"params.field":{type:"string",title:`{{t("Field to aggregate", { ns: "${i}" })}}`,"x-decorator":"FormItem","x-component":"FieldsSelect","x-component-props":{filter(n){return!n.hidden&&n.interface&&!["belongsTo","hasOne","hasMany","belongsToMany"].includes(n.type)}},required:!0,"x-reactions":[{dependencies:["collection"],fulfill:{state:{visible:"{{!!$deps[0]}}"}}}]}}}}}}},params:{type:"object",properties:{distinct:{type:"boolean",title:`{{t("Distinct", { ns: "${i}" })}}`,"x-decorator":"FormItem","x-component":"Checkbox","x-reactions":[{dependencies:["collection","aggregator"],fulfill:{state:{visible:'{{!!$deps[0] && ["count"].includes($deps[1])}}'}}}]},filter:{type:"object",title:'{{t("Filter")}}',"x-decorator":"FormItem","x-component":"Filter","x-use-component-props":()=>{const{values:n}=r.useForm(),[d,u]=t.parseCollectionName(n==null?void 0:n.collection);return{options:t.useCollectionFilterOptions(u,d),className:t.css`
|
|
2
|
+
position: relative;
|
|
3
|
+
width: 100%;
|
|
4
|
+
`}},"x-component-props":{dynamicComponent:"FilterDynamicComponent"},"x-reactions":[{dependencies:["collection"],fulfill:{state:{visible:"{{!!$deps[0]}}"}}}]}}}});g(this,"scope",{useCollectionDataSource:t.useCollectionDataSource});g(this,"components",{SchemaComponentContext:t.SchemaComponentContext,FilterDynamicComponent:s.FilterDynamicComponent,FieldsSelect:s.FieldsSelect,ValueBlock:s.ValueBlock,AssociatedConfig:R})}useVariables({key:n,title:d},{types:u,fieldNames:x=s.defaultFieldNames}){return u&&!u.some(k=>k in s.BaseTypeSets||Object.values(s.BaseTypeSets).some(M=>M.has(k)))?null:{[x.value]:n,[x.label]:d}}useInitializers(n){var u;const d=v("Query result");return n.config.collection?{name:`#${n.id}`,type:"item",title:(u=n.title)!=null?u:`#${n.id}`,Component:s.ValueBlock.Initializer,node:n,resultTitle:d}:null}}class L extends t.Plugin{afterAdd(){return C(this,null,function*(){})}beforeLoad(){return C(this,null,function*(){})}load(){return C(this,null,function*(){this.app.pm.get("workflow").registerInstruction("aggregate",E)})}}e.default=L,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
package/dist/externalVersion.js
CHANGED
|
@@ -2,12 +2,13 @@ module.exports = {
|
|
|
2
2
|
"@formily/react": "2.3.0",
|
|
3
3
|
"antd": "5.12.8",
|
|
4
4
|
"react": "18.2.0",
|
|
5
|
-
"@nocobase/client": "0.
|
|
6
|
-
"@nocobase/plugin-workflow": "0.
|
|
5
|
+
"@nocobase/client": "0.21.0-alpha.2",
|
|
6
|
+
"@nocobase/plugin-workflow": "0.21.0-alpha.2",
|
|
7
7
|
"react-i18next": "11.18.6",
|
|
8
|
-
"@nocobase/
|
|
9
|
-
"@nocobase/
|
|
10
|
-
"@nocobase/
|
|
11
|
-
"@nocobase/test": "0.
|
|
12
|
-
"@nocobase/
|
|
8
|
+
"@nocobase/data-source-manager": "0.21.0-alpha.2",
|
|
9
|
+
"@nocobase/database": "0.21.0-alpha.2",
|
|
10
|
+
"@nocobase/server": "0.21.0-alpha.2",
|
|
11
|
+
"@nocobase/plugin-workflow-test": "0.21.0-alpha.2",
|
|
12
|
+
"@nocobase/test": "0.21.0-alpha.2",
|
|
13
|
+
"@nocobase/utils": "0.21.0-alpha.2"
|
|
13
14
|
};
|
|
@@ -20,6 +20,7 @@ __export(AggregateInstruction_exports, {
|
|
|
20
20
|
default: () => AggregateInstruction_default
|
|
21
21
|
});
|
|
22
22
|
module.exports = __toCommonJS(AggregateInstruction_exports);
|
|
23
|
+
var import_data_source_manager = require("@nocobase/data-source-manager");
|
|
23
24
|
var import_database = require("@nocobase/database");
|
|
24
25
|
var import_plugin_workflow = require("@nocobase/plugin-workflow");
|
|
25
26
|
const aggregators = {
|
|
@@ -33,11 +34,12 @@ class AggregateInstruction_default extends import_plugin_workflow.Instruction {
|
|
|
33
34
|
async run(node, input, processor) {
|
|
34
35
|
const { aggregator, associated, collection, association = {}, params = {} } = node.config;
|
|
35
36
|
const options = processor.getParsedValue(params, node.id);
|
|
36
|
-
const
|
|
37
|
-
const
|
|
37
|
+
const [dataSourceName, collectionName] = (0, import_data_source_manager.parseCollectionName)(collection);
|
|
38
|
+
const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
|
|
39
|
+
const repo = associated ? collectionManager.getRepository(
|
|
38
40
|
`${association == null ? void 0 : association.associatedCollection}.${association.name}`,
|
|
39
41
|
processor.getParsedValue(association == null ? void 0 : association.associatedKey, node.id)
|
|
40
|
-
) :
|
|
42
|
+
) : collectionManager.getRepository(collectionName);
|
|
41
43
|
if (!options.dataType && aggregator === "avg") {
|
|
42
44
|
options.dataType = import_database.DataTypes.DOUBLE;
|
|
43
45
|
}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"displayName.zh-CN": "工作流:聚合查询节点",
|
|
5
5
|
"description": "Used to aggregate data against the database in workflow, such as: statistics, sum, average, etc.",
|
|
6
6
|
"description.zh-CN": "可用于在工作流中对数据库进行聚合查询,如:统计数量、求和、平均值等。",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.21.0-alpha.2",
|
|
8
8
|
"license": "AGPL-3.0",
|
|
9
9
|
"main": "./dist/server/index.js",
|
|
10
10
|
"homepage": "https://docs.nocobase.com/handbook/workflow-aggregate",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"@nocobase/server": "0.x",
|
|
22
22
|
"@nocobase/test": "0.x"
|
|
23
23
|
},
|
|
24
|
-
"gitHead": "
|
|
24
|
+
"gitHead": "90628f2e2da846208fb2d7966ddb4e467d187ffb",
|
|
25
25
|
"keywords": [
|
|
26
26
|
"Workflow"
|
|
27
27
|
]
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
SchemaComponentContext,
|
|
7
7
|
SchemaInitializerItemType,
|
|
8
8
|
css,
|
|
9
|
+
joinCollectionName,
|
|
10
|
+
parseCollectionName,
|
|
9
11
|
useCollectionDataSource,
|
|
10
12
|
useCollectionFilterOptions,
|
|
11
13
|
useCollectionManager_deprecated,
|
|
@@ -116,25 +118,34 @@ function AssociatedConfig({ value, onChange, ...props }): JSX.Element {
|
|
|
116
118
|
// need to get:
|
|
117
119
|
// * source collection (from node.config)
|
|
118
120
|
// * target collection (from field name)
|
|
119
|
-
const { collectionName, target, name } = field;
|
|
121
|
+
const { collectionName, target, name, dataSourceKey } = field;
|
|
120
122
|
|
|
121
|
-
const collection = getCollection(collectionName);
|
|
123
|
+
const collection = getCollection(collectionName, dataSourceKey);
|
|
122
124
|
const primaryKeyField = collection.fields.find((f) => f.primaryKey);
|
|
123
125
|
|
|
124
|
-
setValuesIn('collection', target);
|
|
126
|
+
setValuesIn('collection', `${dataSourceKey}:${target}`);
|
|
125
127
|
|
|
126
128
|
onChange({
|
|
127
129
|
name,
|
|
128
130
|
// primary key data path
|
|
129
131
|
associatedKey: `{{${path.slice(0, -1).join('.')}.${primaryKeyField.name}}}`,
|
|
130
132
|
// data associated collection name
|
|
131
|
-
associatedCollection: collectionName,
|
|
133
|
+
associatedCollection: joinCollectionName(dataSourceKey, collectionName),
|
|
132
134
|
});
|
|
133
135
|
},
|
|
134
136
|
[onChange],
|
|
135
137
|
);
|
|
136
138
|
|
|
137
|
-
return
|
|
139
|
+
return (
|
|
140
|
+
<Cascader
|
|
141
|
+
{...props}
|
|
142
|
+
value={p}
|
|
143
|
+
options={options}
|
|
144
|
+
changeOnSelect
|
|
145
|
+
onChange={onSelectChange}
|
|
146
|
+
loadData={loadData as any}
|
|
147
|
+
/>
|
|
148
|
+
);
|
|
138
149
|
}
|
|
139
150
|
|
|
140
151
|
// based on collection:
|
|
@@ -217,7 +228,7 @@ export default class extends Instruction {
|
|
|
217
228
|
type: 'string',
|
|
218
229
|
required: true,
|
|
219
230
|
'x-decorator': 'FormItem',
|
|
220
|
-
'x-component': '
|
|
231
|
+
'x-component': 'DataSourceCollectionCascader',
|
|
221
232
|
title: `{{t("Data of collection", { ns: "${NAMESPACE}" })}}`,
|
|
222
233
|
'x-reactions': [
|
|
223
234
|
{
|
|
@@ -330,18 +341,21 @@ export default class extends Instruction {
|
|
|
330
341
|
title: '{{t("Filter")}}',
|
|
331
342
|
'x-decorator': 'FormItem',
|
|
332
343
|
'x-component': 'Filter',
|
|
344
|
+
'x-use-component-props': () => {
|
|
345
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
346
|
+
const { values } = useForm();
|
|
347
|
+
const [dataSourceName, collectionName] = parseCollectionName(values?.collection);
|
|
348
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
349
|
+
const options = useCollectionFilterOptions(collectionName, dataSourceName);
|
|
350
|
+
return {
|
|
351
|
+
options,
|
|
352
|
+
className: css`
|
|
353
|
+
position: relative;
|
|
354
|
+
width: 100%;
|
|
355
|
+
`,
|
|
356
|
+
};
|
|
357
|
+
},
|
|
333
358
|
'x-component-props': {
|
|
334
|
-
useProps() {
|
|
335
|
-
const { values } = useForm();
|
|
336
|
-
const options = useCollectionFilterOptions(values?.collection);
|
|
337
|
-
return {
|
|
338
|
-
options,
|
|
339
|
-
className: css`
|
|
340
|
-
position: relative;
|
|
341
|
-
width: 100%;
|
|
342
|
-
`,
|
|
343
|
-
};
|
|
344
|
-
},
|
|
345
359
|
dynamicComponent: 'FilterDynamicComponent',
|
|
346
360
|
},
|
|
347
361
|
'x-reactions': [
|
|
@@ -80,7 +80,8 @@ test.describe('no filter', () => {
|
|
|
80
80
|
const aggregateRecordNodeId = await aggregateRecordNode.node.locator('.workflow-node-id').innerText();
|
|
81
81
|
await aggregateRecordNode.nodeConfigure.click();
|
|
82
82
|
await aggregateRecordNode.collectionDropDown.click();
|
|
83
|
-
await page.
|
|
83
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
84
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
84
85
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
85
86
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
86
87
|
await aggregateRecordNode.submitButton.click();
|
|
@@ -179,7 +180,8 @@ test.describe('no filter', () => {
|
|
|
179
180
|
await aggregateRecordNode.nodeConfigure.click();
|
|
180
181
|
await aggregateRecordNode.sumRadio.click();
|
|
181
182
|
await aggregateRecordNode.collectionDropDown.click();
|
|
182
|
-
await page.
|
|
183
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
184
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
183
185
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
184
186
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
185
187
|
await aggregateRecordNode.submitButton.click();
|
|
@@ -281,7 +283,8 @@ test.describe('no filter', () => {
|
|
|
281
283
|
await aggregateRecordNode.nodeConfigure.click();
|
|
282
284
|
await aggregateRecordNode.avgRadio.click();
|
|
283
285
|
await aggregateRecordNode.collectionDropDown.click();
|
|
284
|
-
await page.
|
|
286
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
287
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
285
288
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
286
289
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
287
290
|
await aggregateRecordNode.submitButton.click();
|
|
@@ -385,7 +388,8 @@ test.describe('no filter', () => {
|
|
|
385
388
|
await aggregateRecordNode.nodeConfigure.click();
|
|
386
389
|
await aggregateRecordNode.minRadio.click();
|
|
387
390
|
await aggregateRecordNode.collectionDropDown.click();
|
|
388
|
-
await page.
|
|
391
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
392
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
389
393
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
390
394
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
391
395
|
await aggregateRecordNode.submitButton.click();
|
|
@@ -488,7 +492,8 @@ test.describe('no filter', () => {
|
|
|
488
492
|
await aggregateRecordNode.nodeConfigure.click();
|
|
489
493
|
await aggregateRecordNode.maxRadio.click();
|
|
490
494
|
await aggregateRecordNode.collectionDropDown.click();
|
|
491
|
-
await page.
|
|
495
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
496
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
492
497
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
493
498
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
494
499
|
await aggregateRecordNode.submitButton.click();
|
|
@@ -590,7 +595,8 @@ test.describe('no filter', () => {
|
|
|
590
595
|
const aggregateRecordNodeId = await aggregateRecordNode.node.locator('.workflow-node-id').innerText();
|
|
591
596
|
await aggregateRecordNode.nodeConfigure.click();
|
|
592
597
|
await aggregateRecordNode.collectionDropDown.click();
|
|
593
|
-
await page.
|
|
598
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
599
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
594
600
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
595
601
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
596
602
|
await aggregateRecordNode.distinctCheckBox.click();
|
|
@@ -690,7 +696,8 @@ test.describe('no filter', () => {
|
|
|
690
696
|
await aggregateRecordNode.nodeConfigure.click();
|
|
691
697
|
await aggregateRecordNode.sumRadio.click();
|
|
692
698
|
await aggregateRecordNode.collectionDropDown.click();
|
|
693
|
-
await page.
|
|
699
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
700
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
694
701
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
695
702
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
696
703
|
await aggregateRecordNode.distinctCheckBox.click();
|
|
@@ -795,7 +802,8 @@ test.describe('no filter', () => {
|
|
|
795
802
|
await aggregateRecordNode.nodeConfigure.click();
|
|
796
803
|
await aggregateRecordNode.avgRadio.click();
|
|
797
804
|
await aggregateRecordNode.collectionDropDown.click();
|
|
798
|
-
await page.
|
|
805
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
806
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
799
807
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
800
808
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
801
809
|
await aggregateRecordNode.distinctCheckBox.click();
|
|
@@ -902,7 +910,8 @@ test.describe('no filter', () => {
|
|
|
902
910
|
await aggregateRecordNode.nodeConfigure.click();
|
|
903
911
|
await aggregateRecordNode.minRadio.click();
|
|
904
912
|
await aggregateRecordNode.collectionDropDown.click();
|
|
905
|
-
await page.
|
|
913
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
914
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
906
915
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
907
916
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
908
917
|
await aggregateRecordNode.distinctCheckBox.click();
|
|
@@ -1006,7 +1015,8 @@ test.describe('no filter', () => {
|
|
|
1006
1015
|
await aggregateRecordNode.nodeConfigure.click();
|
|
1007
1016
|
await aggregateRecordNode.maxRadio.click();
|
|
1008
1017
|
await aggregateRecordNode.collectionDropDown.click();
|
|
1009
|
-
await page.
|
|
1018
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
1019
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
1010
1020
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
1011
1021
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
1012
1022
|
await aggregateRecordNode.distinctCheckBox.click();
|
|
@@ -1111,7 +1121,8 @@ test.describe('filter', () => {
|
|
|
1111
1121
|
const aggregateRecordNodeId = await aggregateRecordNode.node.locator('.workflow-node-id').innerText();
|
|
1112
1122
|
await aggregateRecordNode.nodeConfigure.click();
|
|
1113
1123
|
await aggregateRecordNode.collectionDropDown.click();
|
|
1114
|
-
await page.
|
|
1124
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
1125
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
1115
1126
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
1116
1127
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
1117
1128
|
// 过滤条件
|
|
@@ -1217,7 +1228,8 @@ test.describe('filter', () => {
|
|
|
1217
1228
|
await aggregateRecordNode.nodeConfigure.click();
|
|
1218
1229
|
await aggregateRecordNode.sumRadio.click();
|
|
1219
1230
|
await aggregateRecordNode.collectionDropDown.click();
|
|
1220
|
-
await page.
|
|
1231
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
1232
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
1221
1233
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
1222
1234
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
1223
1235
|
// 过滤条件
|
|
@@ -1327,7 +1339,8 @@ test.describe('filter', () => {
|
|
|
1327
1339
|
await aggregateRecordNode.nodeConfigure.click();
|
|
1328
1340
|
await aggregateRecordNode.avgRadio.click();
|
|
1329
1341
|
await aggregateRecordNode.collectionDropDown.click();
|
|
1330
|
-
await page.
|
|
1342
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
1343
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
1331
1344
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
1332
1345
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
1333
1346
|
// 过滤条件
|
|
@@ -1442,7 +1455,8 @@ test.describe('filter', () => {
|
|
|
1442
1455
|
await aggregateRecordNode.nodeConfigure.click();
|
|
1443
1456
|
await aggregateRecordNode.minRadio.click();
|
|
1444
1457
|
await aggregateRecordNode.collectionDropDown.click();
|
|
1445
|
-
await page.
|
|
1458
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
1459
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
1446
1460
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
1447
1461
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
1448
1462
|
// 过滤条件
|
|
@@ -1553,7 +1567,8 @@ test.describe('filter', () => {
|
|
|
1553
1567
|
await aggregateRecordNode.nodeConfigure.click();
|
|
1554
1568
|
await aggregateRecordNode.maxRadio.click();
|
|
1555
1569
|
await aggregateRecordNode.collectionDropDown.click();
|
|
1556
|
-
await page.
|
|
1570
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
1571
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
1557
1572
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
1558
1573
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
1559
1574
|
// 过滤条件
|
|
@@ -1663,7 +1678,8 @@ test.describe('filter', () => {
|
|
|
1663
1678
|
const aggregateRecordNodeId = await aggregateRecordNode.node.locator('.workflow-node-id').innerText();
|
|
1664
1679
|
await aggregateRecordNode.nodeConfigure.click();
|
|
1665
1680
|
await aggregateRecordNode.collectionDropDown.click();
|
|
1666
|
-
await page.
|
|
1681
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
1682
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
1667
1683
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
1668
1684
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
1669
1685
|
await aggregateRecordNode.distinctCheckBox.click();
|
|
@@ -1770,7 +1786,8 @@ test.describe('filter', () => {
|
|
|
1770
1786
|
await aggregateRecordNode.nodeConfigure.click();
|
|
1771
1787
|
await aggregateRecordNode.sumRadio.click();
|
|
1772
1788
|
await aggregateRecordNode.collectionDropDown.click();
|
|
1773
|
-
await page.
|
|
1789
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
1790
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
1774
1791
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
1775
1792
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
1776
1793
|
await aggregateRecordNode.distinctCheckBox.click();
|
|
@@ -1885,7 +1902,8 @@ test.describe('filter', () => {
|
|
|
1885
1902
|
await aggregateRecordNode.nodeConfigure.click();
|
|
1886
1903
|
await aggregateRecordNode.avgRadio.click();
|
|
1887
1904
|
await aggregateRecordNode.collectionDropDown.click();
|
|
1888
|
-
await page.
|
|
1905
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
1906
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
1889
1907
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
1890
1908
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
1891
1909
|
await aggregateRecordNode.distinctCheckBox.click();
|
|
@@ -2009,7 +2027,8 @@ test.describe('filter', () => {
|
|
|
2009
2027
|
await aggregateRecordNode.nodeConfigure.click();
|
|
2010
2028
|
await aggregateRecordNode.minRadio.click();
|
|
2011
2029
|
await aggregateRecordNode.collectionDropDown.click();
|
|
2012
|
-
await page.
|
|
2030
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
2031
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
2013
2032
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
2014
2033
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
2015
2034
|
await aggregateRecordNode.distinctCheckBox.click();
|
|
@@ -2121,7 +2140,8 @@ test.describe('filter', () => {
|
|
|
2121
2140
|
await aggregateRecordNode.nodeConfigure.click();
|
|
2122
2141
|
await aggregateRecordNode.maxRadio.click();
|
|
2123
2142
|
await aggregateRecordNode.collectionDropDown.click();
|
|
2124
|
-
await page.
|
|
2143
|
+
await page.getByRole('menuitemcheckbox', { name: 'Main right' }).click();
|
|
2144
|
+
await page.getByRole('menuitemcheckbox', { name: aggregateNodeCollectionDisplayName }).click();
|
|
2125
2145
|
await aggregateRecordNode.aggregatedFieldDropDown.click();
|
|
2126
2146
|
await page.getByRole('option', { name: aggregateNodeFieldDisplayName }).click();
|
|
2127
2147
|
await aggregateRecordNode.distinctCheckBox.click();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseCollectionName } from '@nocobase/data-source-manager';
|
|
2
|
+
import { DataTypes } from '@nocobase/database';
|
|
2
3
|
import { Processor, Instruction, JOB_STATUS, FlowNodeModel } from '@nocobase/plugin-workflow';
|
|
3
4
|
|
|
4
5
|
const aggregators = {
|
|
@@ -13,13 +14,14 @@ export default class extends Instruction {
|
|
|
13
14
|
async run(node: FlowNodeModel, input, processor: Processor) {
|
|
14
15
|
const { aggregator, associated, collection, association = {}, params = {} } = node.config;
|
|
15
16
|
const options = processor.getParsedValue(params, node.id);
|
|
16
|
-
const
|
|
17
|
+
const [dataSourceName, collectionName] = parseCollectionName(collection);
|
|
18
|
+
const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
|
|
17
19
|
const repo = associated
|
|
18
|
-
?
|
|
20
|
+
? collectionManager.getRepository(
|
|
19
21
|
`${association?.associatedCollection}.${association.name}`,
|
|
20
22
|
processor.getParsedValue(association?.associatedKey, node.id),
|
|
21
23
|
)
|
|
22
|
-
:
|
|
24
|
+
: collectionManager.getRepository(collectionName);
|
|
23
25
|
|
|
24
26
|
if (!options.dataType && aggregator === 'avg') {
|
|
25
27
|
options.dataType = DataTypes.DOUBLE;
|
|
@@ -3,6 +3,7 @@ import { Application } from '@nocobase/server';
|
|
|
3
3
|
import { getApp, sleep } from '@nocobase/plugin-workflow-test';
|
|
4
4
|
|
|
5
5
|
import Plugin from '..';
|
|
6
|
+
import { EXECUTION_STATUS } from '@nocobase/plugin-workflow';
|
|
6
7
|
|
|
7
8
|
describe('workflow > instructions > aggregate', () => {
|
|
8
9
|
let app: Application;
|
|
@@ -295,4 +296,33 @@ describe('workflow > instructions > aggregate', () => {
|
|
|
295
296
|
expect(j3.result).toBe(1);
|
|
296
297
|
});
|
|
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
|
+
});
|
|
298
328
|
});
|