@nocobase/flow-engine 3.0.0-alpha.6 → 3.0.0-alpha.7
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/lib/flowContext.js +10 -4
- package/lib/utils/associationObjectVariable.d.ts +10 -0
- package/lib/utils/associationObjectVariable.js +10 -7
- package/lib/utils/index.d.ts +2 -2
- package/lib/utils/index.js +4 -0
- package/lib/utils/params-resolvers.d.ts +2 -0
- package/lib/utils/params-resolvers.js +9 -0
- package/lib/utils/variablesParams.js +5 -0
- package/lib/views/createViewMeta.js +18 -16
- package/package.json +4 -4
- package/src/flowContext.ts +12 -2
- package/src/utils/__tests__/variablesParams.test.ts +28 -1
- package/src/utils/associationObjectVariable.ts +9 -6
- package/src/utils/index.ts +11 -2
- package/src/utils/params-resolvers.ts +10 -0
- package/src/utils/variablesParams.ts +10 -0
- package/src/views/createViewMeta.ts +19 -18
package/lib/flowContext.js
CHANGED
|
@@ -2384,7 +2384,7 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
2384
2384
|
return this.resolveJsonTemplate(template);
|
|
2385
2385
|
});
|
|
2386
2386
|
this.defineMethod("resolveJsonTemplate", async function(template) {
|
|
2387
|
-
var _a, _b;
|
|
2387
|
+
var _a, _b, _c;
|
|
2388
2388
|
const used = (0, import_utils.extractUsedVariablePaths)(template);
|
|
2389
2389
|
const usedVarNames = Object.keys(used || {});
|
|
2390
2390
|
if (!usedVarNames.length) {
|
|
@@ -2412,13 +2412,13 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
2412
2412
|
let serverResolved = template;
|
|
2413
2413
|
if (needServer) {
|
|
2414
2414
|
const inferRecordRefWithMeta = /* @__PURE__ */ __name((ctx) => {
|
|
2415
|
-
var _a2, _b2,
|
|
2415
|
+
var _a2, _b2, _c2, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
2416
2416
|
const ref = (0, import_variablesParams.inferRecordRef)(ctx);
|
|
2417
2417
|
if (ref) return ref;
|
|
2418
2418
|
try {
|
|
2419
2419
|
const tk = (_b2 = (_a2 = ctx == null ? void 0 : ctx.resource) == null ? void 0 : _a2.getMeta) == null ? void 0 : _b2.call(_a2, "currentFilterByTk");
|
|
2420
2420
|
if (typeof tk === "undefined" || tk === null) return void 0;
|
|
2421
|
-
const collection = ((
|
|
2421
|
+
const collection = ((_c2 = ctx == null ? void 0 : ctx.collection) == null ? void 0 : _c2.name) || ((_j = (_i = (_h = (_g = (_f = (_e = (_d = ctx == null ? void 0 : ctx.resource) == null ? void 0 : _d.getResourceName) == null ? void 0 : _e.call(_d)) == null ? void 0 : _f.split) == null ? void 0 : _g.call(_f, ".")) == null ? void 0 : _h.slice) == null ? void 0 : _i.call(_h, -1)) == null ? void 0 : _j[0]);
|
|
2422
2422
|
if (!collection) return void 0;
|
|
2423
2423
|
const dataSourceKey = ((_k = ctx == null ? void 0 : ctx.collection) == null ? void 0 : _k.dataSourceKey) || ((_m = (_l = ctx == null ? void 0 : ctx.resource) == null ? void 0 : _l.getDataSourceKey) == null ? void 0 : _m.call(_l));
|
|
2424
2424
|
return { collection, dataSourceKey, filterByTk: tk };
|
|
@@ -2461,6 +2461,11 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
2461
2461
|
}, "collectFromMeta");
|
|
2462
2462
|
const inputFromMeta = await collectFromMeta();
|
|
2463
2463
|
const autoInput = { ...inputFromMeta };
|
|
2464
|
+
const viewPaths = serverVarPaths.view || [];
|
|
2465
|
+
if (!autoInput.view && viewPaths.some((path) => path === "record" || path.startsWith("record.") || path.startsWith("record["))) {
|
|
2466
|
+
const recordRef = (0, import_variablesParams.inferViewRecordRef)(this);
|
|
2467
|
+
if (recordRef) autoInput.view = { record: recordRef };
|
|
2468
|
+
}
|
|
2464
2469
|
try {
|
|
2465
2470
|
const varName = "formValues";
|
|
2466
2471
|
const neededPaths = serverVarPaths[varName] || [];
|
|
@@ -2512,11 +2517,12 @@ const _FlowEngineContext = class _FlowEngineContext extends BaseFlowEngineContex
|
|
|
2512
2517
|
if (this.api) {
|
|
2513
2518
|
try {
|
|
2514
2519
|
serverResolved = await (0, import_params_resolvers.enqueueVariablesResolve)(this, {
|
|
2520
|
+
rd: (0, import_params_resolvers.buildFlowModelResolveDescriptor)(this, (_a = this.model) == null ? void 0 : _a.uid),
|
|
2515
2521
|
template,
|
|
2516
2522
|
contextParams: autoContextParams || {}
|
|
2517
2523
|
});
|
|
2518
2524
|
} catch (e) {
|
|
2519
|
-
(
|
|
2525
|
+
(_c = (_b = this.logger) == null ? void 0 : _b.warn) == null ? void 0 : _c.call(_b, { err: e }, "variables:resolve failed, fallback to client-only");
|
|
2520
2526
|
serverResolved = template;
|
|
2521
2527
|
}
|
|
2522
2528
|
}
|
|
@@ -8,6 +8,16 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { Collection } from '../data-source';
|
|
10
10
|
import type { FlowContext, PropertyMetaFactory } from '../flowContext';
|
|
11
|
+
/**
|
|
12
|
+
* 从值中提取主键:
|
|
13
|
+
* - 支持主键原始值(string/number)
|
|
14
|
+
* - 支持对象(按主键名取值)
|
|
15
|
+
*
|
|
16
|
+
* @param value 字段当前值
|
|
17
|
+
* @param primaryKey 主键字段名
|
|
18
|
+
* @returns 解析出的主键值,无法解析时返回 undefined
|
|
19
|
+
*/
|
|
20
|
+
export declare function getAssociationFilterByTk(value: unknown, primaryKey: string | string[]): unknown;
|
|
11
21
|
/**
|
|
12
22
|
* 创建一个用于“对象类变量”(如 formValues / item)的 `resolveOnServer` 判定函数。
|
|
13
23
|
* 仅当访问路径以“关联字段名”开头(且继续访问其子属性)时,返回 true 交由服务端解析;
|
|
@@ -38,7 +38,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
38
38
|
var associationObjectVariable_exports = {};
|
|
39
39
|
__export(associationObjectVariable_exports, {
|
|
40
40
|
createAssociationAwareObjectMetaFactory: () => createAssociationAwareObjectMetaFactory,
|
|
41
|
-
createAssociationSubpathResolver: () => createAssociationSubpathResolver
|
|
41
|
+
createAssociationSubpathResolver: () => createAssociationSubpathResolver,
|
|
42
|
+
getAssociationFilterByTk: () => getAssociationFilterByTk
|
|
42
43
|
});
|
|
43
44
|
module.exports = __toCommonJS(associationObjectVariable_exports);
|
|
44
45
|
var import_lodash = __toESM(require("lodash"));
|
|
@@ -58,13 +59,14 @@ function findFieldByName(collection, name) {
|
|
|
58
59
|
return fields.find((f) => f.name === name);
|
|
59
60
|
}
|
|
60
61
|
__name(findFieldByName, "findFieldByName");
|
|
61
|
-
function
|
|
62
|
+
function getAssociationFilterByTk(value, primaryKey) {
|
|
62
63
|
if (value == null) return void 0;
|
|
63
64
|
if (Array.isArray(primaryKey)) {
|
|
64
65
|
if (typeof value !== "object" || !value) return void 0;
|
|
66
|
+
const record = value;
|
|
65
67
|
const out = {};
|
|
66
68
|
for (const k of primaryKey) {
|
|
67
|
-
const v =
|
|
69
|
+
const v = record[k];
|
|
68
70
|
if (typeof v === "undefined" || v === null) return void 0;
|
|
69
71
|
out[k] = v;
|
|
70
72
|
}
|
|
@@ -76,7 +78,7 @@ function toFilterByTk(value, primaryKey) {
|
|
|
76
78
|
}
|
|
77
79
|
return void 0;
|
|
78
80
|
}
|
|
79
|
-
__name(
|
|
81
|
+
__name(getAssociationFilterByTk, "getAssociationFilterByTk");
|
|
80
82
|
function createAssociationSubpathResolver(collectionAccessor, valueAccessor) {
|
|
81
83
|
return (p) => {
|
|
82
84
|
if (!p || !p.includes(".")) return false;
|
|
@@ -122,7 +124,7 @@ function createAssociationAwareObjectMetaFactory(collectionAccessor, title, valu
|
|
|
122
124
|
const associationValue = obj[name];
|
|
123
125
|
if (associationValue == null) continue;
|
|
124
126
|
if (Array.isArray(associationValue)) {
|
|
125
|
-
const ids = associationValue.map((item) =>
|
|
127
|
+
const ids = associationValue.map((item) => getAssociationFilterByTk(item, primaryKey)).filter((v) => v != null);
|
|
126
128
|
if (ids.length) {
|
|
127
129
|
params[name] = {
|
|
128
130
|
collection: target,
|
|
@@ -131,7 +133,7 @@ function createAssociationAwareObjectMetaFactory(collectionAccessor, title, valu
|
|
|
131
133
|
};
|
|
132
134
|
}
|
|
133
135
|
} else {
|
|
134
|
-
const id =
|
|
136
|
+
const id = getAssociationFilterByTk(associationValue, primaryKey);
|
|
135
137
|
if (id != null) {
|
|
136
138
|
params[name] = {
|
|
137
139
|
collection: target,
|
|
@@ -153,5 +155,6 @@ __name(createAssociationAwareObjectMetaFactory, "createAssociationAwareObjectMet
|
|
|
153
155
|
// Annotate the CommonJS export names for ESM import in node:
|
|
154
156
|
0 && (module.exports = {
|
|
155
157
|
createAssociationAwareObjectMetaFactory,
|
|
156
|
-
createAssociationSubpathResolver
|
|
158
|
+
createAssociationSubpathResolver,
|
|
159
|
+
getAssociationFilterByTk
|
|
157
160
|
});
|
package/lib/utils/index.d.ts
CHANGED
|
@@ -11,11 +11,11 @@ export { escapeT, getT, tExpr } from './translation';
|
|
|
11
11
|
export { FlowCancelSaveException, FlowExitAllException, FlowExitException } from './exceptions';
|
|
12
12
|
export { defineAction } from './flow-definitions';
|
|
13
13
|
export { isInheritedFrom } from './inheritance';
|
|
14
|
-
export { resolveCreateModelOptions, resolveDefaultParams, resolveExpressions } from './params-resolvers';
|
|
14
|
+
export { buildFlowModelResolveDescriptor, resolveCreateModelOptions, resolveDefaultParams, resolveExpressions, } from './params-resolvers';
|
|
15
15
|
export { compileUiSchema, resolveStepUiSchema, resolveStepDisabledInSettings, resolveUiMode, shouldHideEventInSettings, shouldHideStepInSettings, } from './schema-utils';
|
|
16
16
|
export { setupRuntimeContextSteps } from './setupRuntimeContextSteps';
|
|
17
17
|
export { createCollectionContextMeta } from './createCollectionContextMeta';
|
|
18
|
-
export { createAssociationAwareObjectMetaFactory, createAssociationSubpathResolver } from './associationObjectVariable';
|
|
18
|
+
export { createAssociationAwareObjectMetaFactory, createAssociationSubpathResolver, getAssociationFilterByTk, } from './associationObjectVariable';
|
|
19
19
|
export { buildRecordMeta, collectContextParamsForTemplate, createCurrentRecordMetaFactory, createRecordResolveOnServerWithLocal, createRecordMetaFactory, extractUsedVariableNames, extractUsedVariablePaths, inferRecordRef, type RecordParamsBuilder, } from './variablesParams';
|
|
20
20
|
export { extractPropertyPath, formatPathToVariable, isVariableExpression } from './context';
|
|
21
21
|
export { clearAutoFlowError, getAutoFlowError, setAutoFlowError, type AutoFlowError } from './autoFlowError';
|
package/lib/utils/index.js
CHANGED
|
@@ -34,6 +34,7 @@ __export(utils_exports, {
|
|
|
34
34
|
FlowExitException: () => import_exceptions.FlowExitException,
|
|
35
35
|
MENU_KEYS: () => import_constants.MENU_KEYS,
|
|
36
36
|
RUNJS_OPEN_VIEW_ROUTE_STATE: () => import_openViewRouteState.RUNJS_OPEN_VIEW_ROUTE_STATE,
|
|
37
|
+
buildFlowModelResolveDescriptor: () => import_params_resolvers.buildFlowModelResolveDescriptor,
|
|
37
38
|
buildRecordMeta: () => import_variablesParams.buildRecordMeta,
|
|
38
39
|
clearAutoFlowError: () => import_autoFlowError.clearAutoFlowError,
|
|
39
40
|
collectContextParamsForTemplate: () => import_variablesParams.collectContextParamsForTemplate,
|
|
@@ -57,6 +58,7 @@ __export(utils_exports, {
|
|
|
57
58
|
extractUsedVariablePaths: () => import_variablesParams.extractUsedVariablePaths,
|
|
58
59
|
extractUsedVariablePathsFromRunJS: () => import_runjsValue.extractUsedVariablePathsFromRunJS,
|
|
59
60
|
formatPathToVariable: () => import_context.formatPathToVariable,
|
|
61
|
+
getAssociationFilterByTk: () => import_associationObjectVariable.getAssociationFilterByTk,
|
|
60
62
|
getAutoFlowError: () => import_autoFlowError.getAutoFlowError,
|
|
61
63
|
getT: () => import_translation.getT,
|
|
62
64
|
inferRecordRef: () => import_variablesParams.inferRecordRef,
|
|
@@ -127,6 +129,7 @@ var import_randomId = require("./randomId");
|
|
|
127
129
|
FlowExitException,
|
|
128
130
|
MENU_KEYS,
|
|
129
131
|
RUNJS_OPEN_VIEW_ROUTE_STATE,
|
|
132
|
+
buildFlowModelResolveDescriptor,
|
|
130
133
|
buildRecordMeta,
|
|
131
134
|
clearAutoFlowError,
|
|
132
135
|
collectContextParamsForTemplate,
|
|
@@ -150,6 +153,7 @@ var import_randomId = require("./randomId");
|
|
|
150
153
|
extractUsedVariablePaths,
|
|
151
154
|
extractUsedVariablePathsFromRunJS,
|
|
152
155
|
formatPathToVariable,
|
|
156
|
+
getAssociationFilterByTk,
|
|
153
157
|
getAutoFlowError,
|
|
154
158
|
getT,
|
|
155
159
|
inferRecordRef,
|
|
@@ -27,10 +27,12 @@ export type JSONValue = string | {
|
|
|
27
27
|
[key: string]: JSONValue;
|
|
28
28
|
} | JSONValue[];
|
|
29
29
|
type BatchPayload = {
|
|
30
|
+
rd?: string;
|
|
30
31
|
template: JSONValue;
|
|
31
32
|
contextParams?: ServerContextParams | undefined;
|
|
32
33
|
};
|
|
33
34
|
export declare function enqueueVariablesResolve(ctx: FlowRuntimeContext, payload: BatchPayload): Promise<unknown>;
|
|
35
|
+
export declare function buildFlowModelResolveDescriptor(ctx: Pick<FlowModelContext, 'api'>, flowModelUid?: string | number | null): any;
|
|
34
36
|
/**
|
|
35
37
|
* 解析参数中的 {{xxx}} 表达式,自动处理异步属性访问
|
|
36
38
|
*/
|
|
@@ -37,6 +37,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
37
37
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
38
38
|
var params_resolvers_exports = {};
|
|
39
39
|
__export(params_resolvers_exports, {
|
|
40
|
+
buildFlowModelResolveDescriptor: () => buildFlowModelResolveDescriptor,
|
|
40
41
|
enqueueVariablesResolve: () => enqueueVariablesResolve,
|
|
41
42
|
preprocessExpression: () => preprocessExpression,
|
|
42
43
|
resolveCreateModelOptions: () => resolveCreateModelOptions,
|
|
@@ -45,6 +46,7 @@ __export(params_resolvers_exports, {
|
|
|
45
46
|
});
|
|
46
47
|
module.exports = __toCommonJS(params_resolvers_exports);
|
|
47
48
|
var import_shared = require("@nocobase/shared");
|
|
49
|
+
var import_client = require("@nocobase/utils/client");
|
|
48
50
|
var import_lodash = __toESM(require("lodash"));
|
|
49
51
|
async function resolveDefaultParams(defaultParams, ctx) {
|
|
50
52
|
if (!defaultParams) {
|
|
@@ -144,6 +146,7 @@ function enqueueVariablesResolve(ctx, payload) {
|
|
|
144
146
|
try {
|
|
145
147
|
const batch = items.map((it) => ({
|
|
146
148
|
id: it.id,
|
|
149
|
+
rd: it.payload.rd,
|
|
147
150
|
template: it.payload.template,
|
|
148
151
|
contextParams: it.payload.contextParams || {}
|
|
149
152
|
}));
|
|
@@ -187,6 +190,11 @@ function enqueueVariablesResolve(ctx, payload) {
|
|
|
187
190
|
return p;
|
|
188
191
|
}
|
|
189
192
|
__name(enqueueVariablesResolve, "enqueueVariablesResolve");
|
|
193
|
+
function buildFlowModelResolveDescriptor(ctx, flowModelUid) {
|
|
194
|
+
var _a, _b;
|
|
195
|
+
return (0, import_client.generateFlowModelRdFromToken)(flowModelUid, (_b = (_a = ctx == null ? void 0 : ctx.api) == null ? void 0 : _a.auth) == null ? void 0 : _b.token);
|
|
196
|
+
}
|
|
197
|
+
__name(buildFlowModelResolveDescriptor, "buildFlowModelResolveDescriptor");
|
|
190
198
|
async function resolveExpressions(params, ctx) {
|
|
191
199
|
const compile = /* @__PURE__ */ __name(async (source) => {
|
|
192
200
|
if (typeof source === "string" && /\{\{.*?\}\}/.test(source)) {
|
|
@@ -308,6 +316,7 @@ async function compileExpression(expression, ctx) {
|
|
|
308
316
|
__name(compileExpression, "compileExpression");
|
|
309
317
|
// Annotate the CommonJS export names for ESM import in node:
|
|
310
318
|
0 && (module.exports = {
|
|
319
|
+
buildFlowModelResolveDescriptor,
|
|
311
320
|
enqueueVariablesResolve,
|
|
312
321
|
preprocessExpression,
|
|
313
322
|
resolveCreateModelOptions,
|
|
@@ -210,6 +210,11 @@ async function collectContextParamsForTemplate(ctx, template) {
|
|
|
210
210
|
input[key] = built;
|
|
211
211
|
}
|
|
212
212
|
}
|
|
213
|
+
const viewPaths = usage.view || [];
|
|
214
|
+
if (!input.view && viewPaths.some((path) => path === "record" || path.startsWith("record.") || path.startsWith("record["))) {
|
|
215
|
+
const recordRef = inferViewRecordRef(ctx);
|
|
216
|
+
if (recordRef) input.view = { record: recordRef };
|
|
217
|
+
}
|
|
213
218
|
return (0, import_serverContextParams.buildServerContextParams)(ctx, input);
|
|
214
219
|
}
|
|
215
220
|
__name(collectContextParamsForTemplate, "collectContextParamsForTemplate");
|
|
@@ -33,6 +33,13 @@ __export(createViewMeta_exports, {
|
|
|
33
33
|
});
|
|
34
34
|
module.exports = __toCommonJS(createViewMeta_exports);
|
|
35
35
|
var import_variablesParams = require("../utils/variablesParams");
|
|
36
|
+
function buildPopupSourceRecordRef(ref) {
|
|
37
|
+
if ((ref == null ? void 0 : ref.sourceId) == null || ref.sourceId === "" || typeof ref.associationName !== "string") return void 0;
|
|
38
|
+
const collection = ref.associationName.split(".")[0];
|
|
39
|
+
if (!collection) return void 0;
|
|
40
|
+
return { collection, dataSourceKey: ref.dataSourceKey || "main", filterByTk: ref.sourceId };
|
|
41
|
+
}
|
|
42
|
+
__name(buildPopupSourceRecordRef, "buildPopupSourceRecordRef");
|
|
36
43
|
function isDefined(value) {
|
|
37
44
|
return value !== void 0 && value !== null;
|
|
38
45
|
}
|
|
@@ -306,9 +313,11 @@ function createPopupMeta(ctx, anchorView) {
|
|
|
306
313
|
let level = 1;
|
|
307
314
|
let parentRef = await getParentRecordRef(level, c);
|
|
308
315
|
while (parentRef) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
316
|
+
const parent = { record: parentRef };
|
|
317
|
+
const sourceRecord = buildPopupSourceRecordRef(parentRef);
|
|
318
|
+
if (sourceRecord) parent.sourceRecord = sourceRecord;
|
|
319
|
+
cur.parent = parent;
|
|
320
|
+
cur = parent;
|
|
312
321
|
level += 1;
|
|
313
322
|
parentRef = await getParentRecordRef(level, c);
|
|
314
323
|
}
|
|
@@ -317,19 +326,12 @@ function createPopupMeta(ctx, anchorView) {
|
|
|
317
326
|
(_b = (_a = c.logger) == null ? void 0 : _a.debug) == null ? void 0 : _b.call(_a, { err }, "[FlowEngine] buildVariablesParams: build parent-chain failed");
|
|
318
327
|
}
|
|
319
328
|
try {
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
params.sourceRecord = {
|
|
327
|
-
collection: parentCollectionName,
|
|
328
|
-
dataSourceKey: dsKey,
|
|
329
|
-
filterByTk: srcId
|
|
330
|
-
};
|
|
331
|
-
}
|
|
332
|
-
}
|
|
329
|
+
const sourceRecord = buildPopupSourceRecordRef({
|
|
330
|
+
sourceId: inputArgs == null ? void 0 : inputArgs.sourceId,
|
|
331
|
+
associationName: inputArgs == null ? void 0 : inputArgs.associationName,
|
|
332
|
+
dataSourceKey: inputArgs == null ? void 0 : inputArgs.dataSourceKey
|
|
333
|
+
});
|
|
334
|
+
if (sourceRecord) params.sourceRecord = sourceRecord;
|
|
333
335
|
} catch (err) {
|
|
334
336
|
(_d = (_c = c.logger) == null ? void 0 : _c.debug) == null ? void 0 : _d.call(_c, { err }, "[FlowEngine] buildVariablesParams: infer sourceRecord failed");
|
|
335
337
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/flow-engine",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A standalone flow engine for NocoBase, managing workflows, models, and actions.",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
"dependencies": {
|
|
9
9
|
"@formily/antd-v5": "1.x",
|
|
10
10
|
"@formily/reactive": "2.x",
|
|
11
|
-
"@nocobase/sdk": "3.0.0-alpha.
|
|
12
|
-
"@nocobase/shared": "3.0.0-alpha.
|
|
11
|
+
"@nocobase/sdk": "3.0.0-alpha.7",
|
|
12
|
+
"@nocobase/shared": "3.0.0-alpha.7",
|
|
13
13
|
"ahooks": "^3.7.2",
|
|
14
14
|
"axios": "^1.7.0",
|
|
15
15
|
"dayjs": "^1.11.9",
|
|
@@ -37,5 +37,5 @@
|
|
|
37
37
|
],
|
|
38
38
|
"author": "NocoBase Team",
|
|
39
39
|
"license": "Apache-2.0",
|
|
40
|
-
"gitHead": "
|
|
40
|
+
"gitHead": "19cd28c710f3b3c614ebe2fd7d5a415d527ee559"
|
|
41
41
|
}
|
package/src/flowContext.ts
CHANGED
|
@@ -50,11 +50,11 @@ import {
|
|
|
50
50
|
resolveModuleUrl,
|
|
51
51
|
} from './utils';
|
|
52
52
|
import { FlowExitAllException } from './utils/exceptions';
|
|
53
|
-
import { enqueueVariablesResolve, JSONValue } from './utils/params-resolvers';
|
|
53
|
+
import { buildFlowModelResolveDescriptor, enqueueVariablesResolve, JSONValue } from './utils/params-resolvers';
|
|
54
54
|
import type { RecordRef } from './utils/serverContextParams';
|
|
55
55
|
import { buildServerContextParams as _buildServerContextParams } from './utils/serverContextParams';
|
|
56
56
|
import { getDirtyAwareApiClient } from './utils/dirtyAwareApiClient';
|
|
57
|
-
import { inferRecordRef } from './utils/variablesParams';
|
|
57
|
+
import { inferRecordRef, inferViewRecordRef } from './utils/variablesParams';
|
|
58
58
|
import { FlowView, FlowViewer } from './views/FlowView';
|
|
59
59
|
import { RunJSContextRegistry, getModelClassName, type RunJSVersion } from './runjs-context/registry';
|
|
60
60
|
import { createEphemeralContext } from './utils/createEphemeralContext';
|
|
@@ -3316,6 +3316,15 @@ export class FlowEngineContext extends BaseFlowEngineContext {
|
|
|
3316
3316
|
const inputFromMeta = await collectFromMeta();
|
|
3317
3317
|
const autoInput = { ...inputFromMeta } as Record<string, any>;
|
|
3318
3318
|
|
|
3319
|
+
const viewPaths = serverVarPaths.view || [];
|
|
3320
|
+
if (
|
|
3321
|
+
!autoInput.view &&
|
|
3322
|
+
viewPaths.some((path) => path === 'record' || path.startsWith('record.') || path.startsWith('record['))
|
|
3323
|
+
) {
|
|
3324
|
+
const recordRef = inferViewRecordRef(this);
|
|
3325
|
+
if (recordRef) autoInput.view = { record: recordRef };
|
|
3326
|
+
}
|
|
3327
|
+
|
|
3319
3328
|
// Special-case: formValues
|
|
3320
3329
|
// If server needs to resolve some formValues paths but meta params only cover association anchors
|
|
3321
3330
|
// (e.g. formValues.customer) and some top-level paths are missing (e.g. formValues.status),
|
|
@@ -3388,6 +3397,7 @@ export class FlowEngineContext extends BaseFlowEngineContext {
|
|
|
3388
3397
|
if (this.api) {
|
|
3389
3398
|
try {
|
|
3390
3399
|
serverResolved = await enqueueVariablesResolve(this as FlowRuntimeContext<FlowModel>, {
|
|
3400
|
+
rd: buildFlowModelResolveDescriptor(this as FlowRuntimeContext<FlowModel>, this.model?.uid),
|
|
3391
3401
|
template,
|
|
3392
3402
|
contextParams: autoContextParams || {},
|
|
3393
3403
|
});
|
|
@@ -34,7 +34,8 @@ describe('variablesParams helpers', () => {
|
|
|
34
34
|
|
|
35
35
|
it('inferRecordRef fallback to collection.getFilterByTK when resource has no filterByTk', () => {
|
|
36
36
|
const engine = new FlowEngine();
|
|
37
|
-
const ds = engine.context.dataSourceManager.getDataSource('main')
|
|
37
|
+
const ds = engine.context.dataSourceManager.getDataSource('main');
|
|
38
|
+
if (!ds) throw new Error('main data source is required');
|
|
38
39
|
ds.addCollection({
|
|
39
40
|
name: 'users',
|
|
40
41
|
filterTargetKey: 'id',
|
|
@@ -108,6 +109,32 @@ describe('variablesParams helpers', () => {
|
|
|
108
109
|
});
|
|
109
110
|
});
|
|
110
111
|
|
|
112
|
+
it('collectContextParamsForTemplate infers view.record when its meta has no descriptor', async () => {
|
|
113
|
+
const ctx: any = {
|
|
114
|
+
getPropertyOptions: () => undefined,
|
|
115
|
+
view: {
|
|
116
|
+
inputArgs: {
|
|
117
|
+
collectionName: 'posts',
|
|
118
|
+
dataSourceKey: 'main',
|
|
119
|
+
filterByTk: 3,
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const res = await collectContextParamsForTemplate(ctx, {
|
|
125
|
+
recordId: '{{ ctx.view.record.id }}',
|
|
126
|
+
viewType: '{{ ctx.view.type }}',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
expect(res).toEqual({
|
|
130
|
+
'view.record': {
|
|
131
|
+
collection: 'posts',
|
|
132
|
+
dataSourceKey: 'main',
|
|
133
|
+
filterByTk: 3,
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
111
138
|
it('createRecordResolveOnServerWithLocal: no local record => always use server', () => {
|
|
112
139
|
const resolver = createRecordResolveOnServerWithLocal(
|
|
113
140
|
() => ({ name: 'posts', dataSourceKey: 'main' }) as any,
|
|
@@ -51,13 +51,14 @@ function findFieldByName(collection: Collection | null | undefined, name?: strin
|
|
|
51
51
|
* @param primaryKey 主键字段名
|
|
52
52
|
* @returns 解析出的主键值,无法解析时返回 undefined
|
|
53
53
|
*/
|
|
54
|
-
function
|
|
54
|
+
export function getAssociationFilterByTk(value: unknown, primaryKey: string | string[]) {
|
|
55
55
|
if (value == null) return undefined;
|
|
56
56
|
if (Array.isArray(primaryKey)) {
|
|
57
57
|
if (typeof value !== 'object' || !value) return undefined;
|
|
58
|
-
const
|
|
58
|
+
const record = value as Record<string, unknown>;
|
|
59
|
+
const out: Record<string, unknown> = {};
|
|
59
60
|
for (const k of primaryKey) {
|
|
60
|
-
const v =
|
|
61
|
+
const v = record[k];
|
|
61
62
|
if (typeof v === 'undefined' || v === null) return undefined;
|
|
62
63
|
out[k] = v;
|
|
63
64
|
}
|
|
@@ -65,7 +66,7 @@ function toFilterByTk(value: unknown, primaryKey: string | string[]) {
|
|
|
65
66
|
}
|
|
66
67
|
if (typeof value === 'string' || typeof value === 'number') return value;
|
|
67
68
|
if (typeof value === 'object') {
|
|
68
|
-
return (value as
|
|
69
|
+
return (value as Record<string, unknown>)[primaryKey];
|
|
69
70
|
}
|
|
70
71
|
return undefined;
|
|
71
72
|
}
|
|
@@ -149,7 +150,9 @@ export function createAssociationAwareObjectMetaFactory(
|
|
|
149
150
|
if (associationValue == null) continue;
|
|
150
151
|
|
|
151
152
|
if (Array.isArray(associationValue)) {
|
|
152
|
-
const ids = associationValue
|
|
153
|
+
const ids = associationValue
|
|
154
|
+
.map((item) => getAssociationFilterByTk(item, primaryKey))
|
|
155
|
+
.filter((v) => v != null);
|
|
153
156
|
if (ids.length) {
|
|
154
157
|
params[name] = {
|
|
155
158
|
collection: target,
|
|
@@ -158,7 +161,7 @@ export function createAssociationAwareObjectMetaFactory(
|
|
|
158
161
|
};
|
|
159
162
|
}
|
|
160
163
|
} else {
|
|
161
|
-
const id =
|
|
164
|
+
const id = getAssociationFilterByTk(associationValue, primaryKey);
|
|
162
165
|
if (id != null) {
|
|
163
166
|
params[name] = {
|
|
164
167
|
collection: target,
|
package/src/utils/index.ts
CHANGED
|
@@ -31,7 +31,12 @@ export { defineAction } from './flow-definitions';
|
|
|
31
31
|
export { isInheritedFrom } from './inheritance';
|
|
32
32
|
|
|
33
33
|
// 参数解析器
|
|
34
|
-
export {
|
|
34
|
+
export {
|
|
35
|
+
buildFlowModelResolveDescriptor,
|
|
36
|
+
resolveCreateModelOptions,
|
|
37
|
+
resolveDefaultParams,
|
|
38
|
+
resolveExpressions,
|
|
39
|
+
} from './params-resolvers';
|
|
35
40
|
|
|
36
41
|
// Schema 工具
|
|
37
42
|
export {
|
|
@@ -48,7 +53,11 @@ export { setupRuntimeContextSteps } from './setupRuntimeContextSteps';
|
|
|
48
53
|
|
|
49
54
|
// Record Proxy 工具
|
|
50
55
|
export { createCollectionContextMeta } from './createCollectionContextMeta';
|
|
51
|
-
export {
|
|
56
|
+
export {
|
|
57
|
+
createAssociationAwareObjectMetaFactory,
|
|
58
|
+
createAssociationSubpathResolver,
|
|
59
|
+
getAssociationFilterByTk,
|
|
60
|
+
} from './associationObjectVariable';
|
|
52
61
|
export {
|
|
53
62
|
buildRecordMeta,
|
|
54
63
|
collectContextParamsForTemplate,
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { getValuesByPath } from '@nocobase/shared';
|
|
11
|
+
import { generateFlowModelRdFromToken } from '@nocobase/utils/client';
|
|
11
12
|
import _ from 'lodash';
|
|
12
13
|
import { FlowContext, FlowModelContext, FlowRuntimeContext } from '../flowContext';
|
|
13
14
|
import type { FlowModel } from '../models';
|
|
@@ -78,6 +79,7 @@ export type JSONValue = string | { [key: string]: JSONValue } | JSONValue[];
|
|
|
78
79
|
// =========================
|
|
79
80
|
|
|
80
81
|
type BatchPayload = {
|
|
82
|
+
rd?: string;
|
|
81
83
|
template: JSONValue;
|
|
82
84
|
contextParams?: ServerContextParams | undefined;
|
|
83
85
|
};
|
|
@@ -170,6 +172,7 @@ export function enqueueVariablesResolve(ctx: FlowRuntimeContext, payload: BatchP
|
|
|
170
172
|
try {
|
|
171
173
|
const batch = items.map((it) => ({
|
|
172
174
|
id: it.id,
|
|
175
|
+
rd: it.payload.rd,
|
|
173
176
|
template: it.payload.template,
|
|
174
177
|
contextParams: it.payload.contextParams || {},
|
|
175
178
|
}));
|
|
@@ -218,6 +221,13 @@ export function enqueueVariablesResolve(ctx: FlowRuntimeContext, payload: BatchP
|
|
|
218
221
|
return p;
|
|
219
222
|
}
|
|
220
223
|
|
|
224
|
+
export function buildFlowModelResolveDescriptor(
|
|
225
|
+
ctx: Pick<FlowModelContext, 'api'>,
|
|
226
|
+
flowModelUid?: string | number | null,
|
|
227
|
+
) {
|
|
228
|
+
return generateFlowModelRdFromToken(flowModelUid, ctx?.api?.auth?.token);
|
|
229
|
+
}
|
|
230
|
+
|
|
221
231
|
/**
|
|
222
232
|
* 解析参数中的 {{xxx}} 表达式,自动处理异步属性访问
|
|
223
233
|
*/
|
|
@@ -263,5 +263,15 @@ export async function collectContextParamsForTemplate(
|
|
|
263
263
|
input[key] = built;
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
|
+
|
|
267
|
+
const viewPaths = usage.view || [];
|
|
268
|
+
if (
|
|
269
|
+
!input.view &&
|
|
270
|
+
viewPaths.some((path) => path === 'record' || path.startsWith('record.') || path.startsWith('record['))
|
|
271
|
+
) {
|
|
272
|
+
const recordRef = inferViewRecordRef(ctx);
|
|
273
|
+
if (recordRef) input.view = { record: recordRef };
|
|
274
|
+
}
|
|
275
|
+
|
|
266
276
|
return buildServerContextParams(ctx, input);
|
|
267
277
|
}
|
|
@@ -15,6 +15,13 @@ import type { FlowView } from './FlowView';
|
|
|
15
15
|
|
|
16
16
|
type PopupModelLike = { getStepParams?: (a: string, b: string) => any } | undefined;
|
|
17
17
|
|
|
18
|
+
function buildPopupSourceRecordRef(ref?: Pick<RecordRef, 'associationName' | 'dataSourceKey' | 'sourceId'>) {
|
|
19
|
+
if (ref?.sourceId == null || ref.sourceId === '' || typeof ref.associationName !== 'string') return undefined;
|
|
20
|
+
const collection = ref.associationName.split('.')[0];
|
|
21
|
+
if (!collection) return undefined;
|
|
22
|
+
return { collection, dataSourceKey: ref.dataSourceKey || 'main', filterByTk: ref.sourceId };
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
function isDefined(value: any) {
|
|
19
26
|
return value !== undefined && value !== null;
|
|
20
27
|
}
|
|
@@ -327,13 +334,15 @@ export function createPopupMeta(ctx: FlowContext, anchorView?: FlowView): Proper
|
|
|
327
334
|
const stack = getViewStack(view);
|
|
328
335
|
const currentIndex = getAnchoredViewStackIndex(view, stack);
|
|
329
336
|
if (currentIndex >= 2) {
|
|
330
|
-
let cur
|
|
337
|
+
let cur = params;
|
|
331
338
|
let level = 1;
|
|
332
339
|
let parentRef = await getParentRecordRef(level, c);
|
|
333
340
|
while (parentRef) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
341
|
+
const parent: PopupVariableParams = { record: parentRef };
|
|
342
|
+
const sourceRecord = buildPopupSourceRecordRef(parentRef);
|
|
343
|
+
if (sourceRecord) parent.sourceRecord = sourceRecord;
|
|
344
|
+
cur.parent = parent;
|
|
345
|
+
cur = parent;
|
|
337
346
|
level += 1;
|
|
338
347
|
parentRef = await getParentRecordRef(level, c);
|
|
339
348
|
}
|
|
@@ -343,20 +352,12 @@ export function createPopupMeta(ctx: FlowContext, anchorView?: FlowView): Proper
|
|
|
343
352
|
}
|
|
344
353
|
|
|
345
354
|
try {
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
if (parentCollectionName) {
|
|
353
|
-
params.sourceRecord = {
|
|
354
|
-
collection: parentCollectionName,
|
|
355
|
-
dataSourceKey: dsKey,
|
|
356
|
-
filterByTk: srcId,
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
}
|
|
355
|
+
const sourceRecord = buildPopupSourceRecordRef({
|
|
356
|
+
sourceId: inputArgs?.sourceId,
|
|
357
|
+
associationName: inputArgs?.associationName,
|
|
358
|
+
dataSourceKey: inputArgs?.dataSourceKey,
|
|
359
|
+
});
|
|
360
|
+
if (sourceRecord) params.sourceRecord = sourceRecord;
|
|
360
361
|
} catch (err) {
|
|
361
362
|
c.logger?.debug?.({ err }, '[FlowEngine] buildVariablesParams: infer sourceRecord failed');
|
|
362
363
|
}
|