@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.
@@ -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
  };
@@ -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,s,S,h,r,C){"use strict";var H=Object.defineProperty,J=Object.defineProperties;var W=Object.getOwnPropertyDescriptors;var I=Object.getOwnPropertySymbols;var A=Object.prototype.hasOwnProperty,D=Object.prototype.propertyIsEnumerable;var $=(e,t,o)=>t in e?H(e,t,{enumerable:!0,configurable:!0,writable:!0,value:o}):e[t]=o,O=(e,t)=>{for(var o in t||(t={}))A.call(t,o)&&$(e,o,t[o]);if(I)for(var o of I(t))D.call(t,o)&&$(e,o,t[o]);return e},V=(e,t)=>J(e,W(t));var G=(e,t)=>{var o={};for(var s in e)A.call(e,s)&&t.indexOf(s)<0&&(o[s]=e[s]);if(e!=null&&I)for(var s of I(e))t.indexOf(s)<0&&D.call(e,s)&&(o[s]=e[s]);return o};var g=(e,t,o)=>($(e,typeof t!="symbol"?t+"":t,o),o);var x=(e,t,o)=>new Promise((s,S)=>{var h=i=>{try{C(o.next(i))}catch(b){S(b)}},r=i=>{try{C(o.throw(i))}catch(b){S(b)}},C=i=>i.done?s(i.value):Promise.resolve(i.value).then(h,r);C((o=o.apply(e,t)).next())});const i="workflow-aggregate";function b(l,c={}){const{t:n}=B(c);return n(l)}function B(l){return C.useTranslation(i,l)}function N(l){return["hasMany","belongsToMany"].includes(l.type)}function P(){const l=t.useCompile();return[r.nodesOptions,r.triggerOptions].map(c=>{var p;const n=(p=c.useOptions({types:[N],appends:null,depth:4}))==null?void 0:p.filter(Boolean);return{label:l(c.label),value:c.value,key:c.value,children:l(n),disabled:n&&!n.length}})}function K(p){var f=p,{value:l,onChange:c}=f,n=G(f,["value","onChange"]);const{setValuesIn:v}=s.useForm(),{getCollection:k}=t.useCollectionManager_deprecated(),M=P(),[F,q]=h.useState(M),{associatedKey:L="",name:_}=l!=null?l:{};let w=[];const j=L.match(/^{{(.*)}}$/);j&&(w=[...j[1].trim().split(".").slice(0,-1),_]);const z=m=>x(this,null,function*(){var u;const a=m[m.length-1];!((u=a.children)!=null&&u.length)&&!a.isLeaf&&a.loadChildren&&(yield a.loadChildren(a),q(d=>[...d]))});h.useEffect(()=>{x(this,null,function*(){var u;if(!w||F.length<=1)return;let a=null;for(let d=0;d<w.length;d++){const T=w[d];try{d===0?a=F.find(y=>y.value===T):(a.loadChildren&&!((u=a.children)!=null&&u.length)&&(yield a.loadChildren(a)),a=a.children.find(y=>y.value===T))}catch(y){console.error(y)}}q([...F])})},[l,F.length]);const U=h.useCallback((m,a)=>{if(!(m!=null&&m.length)){v("collection",null),c({});return}const{field:u}=a.pop();if(!u||!["hasMany","belongsToMany"].includes(u.type))return;const{collectionName:d,target:T,name:y}=u,Q=k(d).fields.find(X=>X.primaryKey);v("collection",T),c({name:y,associatedKey:`{{${m.slice(0,-1).join(".")}.${Q.name}}}`,associatedCollection:d})},[c]);return o.jsx(S.Cascader,V(O({},n),{value:w,options:F,onChange:U,loadData:z}))}class R extends r.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":"CollectionSelect",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-component-props":{useProps(){const{values:n}=s.useForm();return{options:t.useCollectionFilterOptions(n==null?void 0:n.collection),className:t.css`
2
- position: relative;
3
- width: 100%;
4
- `}},dynamicComponent:"FilterDynamicComponent"},"x-reactions":[{dependencies:["collection"],fulfill:{state:{visible:"{{!!$deps[0]}}"}}}]}}}});g(this,"scope",{useCollectionDataSource:t.useCollectionDataSource});g(this,"components",{SchemaComponentContext:t.SchemaComponentContext,FilterDynamicComponent:r.FilterDynamicComponent,FieldsSelect:r.FieldsSelect,ValueBlock:r.ValueBlock,AssociatedConfig:K})}useVariables({key:n,title:p},{types:f,fieldNames:v=r.defaultFieldNames}){return f&&!f.some(k=>k in r.BaseTypeSets||Object.values(r.BaseTypeSets).some(M=>M.has(k)))?null:{[v.value]:n,[v.label]:p}}useInitializers(n){var f;const p=b("Query result");return n.config.collection?{name:`#${n.id}`,type:"item",title:(f=n.title)!=null?f:`#${n.id}`,Component:r.ValueBlock.Initializer,node:n,resultTitle:p}:null}}class E extends t.Plugin{afterAdd(){return x(this,null,function*(){})}beforeLoad(){return x(this,null,function*(){})}load(){return x(this,null,function*(){this.app.pm.get("workflow").registerInstruction("aggregate",R)})}}e.default=E,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
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"}})});
@@ -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.20.0-alpha.9",
6
- "@nocobase/plugin-workflow": "0.20.0-alpha.9",
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/database": "0.20.0-alpha.9",
9
- "@nocobase/server": "0.20.0-alpha.9",
10
- "@nocobase/plugin-workflow-test": "0.20.0-alpha.9",
11
- "@nocobase/test": "0.20.0-alpha.9",
12
- "@nocobase/utils": "0.20.0-alpha.9"
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 { database } = node.constructor;
37
- const repo = associated ? database.getRepository(
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
- ) : database.getRepository(collection);
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.20.0-alpha.9",
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": "5473d9039cfdb649a8c8c625edefc9a3ac464be5",
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 <Cascader {...props} value={p} options={options} onChange={onSelectChange} loadData={loadData as any} />;
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': 'CollectionSelect',
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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.getByText(aggregateNodeCollectionDisplayName).click();
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 { BelongsToManyRepository, DataTypes, HasManyRepository } from '@nocobase/database';
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 { database } = <typeof FlowNodeModel>node.constructor;
17
+ const [dataSourceName, collectionName] = parseCollectionName(collection);
18
+ const { collectionManager } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName);
17
19
  const repo = associated
18
- ? database.getRepository<HasManyRepository | BelongsToManyRepository>(
20
+ ? collectionManager.getRepository(
19
21
  `${association?.associatedCollection}.${association.name}`,
20
22
  processor.getParsedValue(association?.associatedKey, node.id),
21
23
  )
22
- : database.getRepository(collection);
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
  });