Skip to content

Commit 7ff271b

Browse files
committed
feat: provide variables used in feel expressions
Related to camunda/camunda-modeler#5639
1 parent 4ca7017 commit 7ff271b

File tree

10 files changed

+1262
-40
lines changed

10 files changed

+1262
-40
lines changed

lib/base/VariableResolver.js

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,14 @@ import { getParents } from './util/elementsUtil';
2424
* Base Class that handles additional variable extractors, variable parsing and caching.
2525
*/
2626
export class BaseVariableResolver {
27+
2728
constructor(eventBus, bpmnjs) {
2829
this.providers = [];
2930
this._eventBus = eventBus;
3031
this._bpmnjs = bpmnjs;
3132

3233
this.rawVariables = new CachedValue(this._generateRawVariables.bind(this));
33-
this.parsedVariables = new CachedValue(async () => {
34+
this._allParsedVariables = new CachedValue(async () => {
3435

3536
const rawVariables = await this.getRawVariables();
3637
const context = { variables: rawVariables };
@@ -39,6 +40,17 @@ export class BaseVariableResolver {
3940

4041
return context.variables;
4142
});
43+
this.parsedVariables = new CachedValue(async () => {
44+
const allParsed = await this._allParsedVariables.get();
45+
46+
// Filter out variables with no scope (e.g. consumed variable markers)
47+
const filtered = {};
48+
for (const key in allParsed) {
49+
filtered[key] = allParsed[key].filter(v => v.scope);
50+
}
51+
52+
return filtered;
53+
});
4254

4355
eventBus.on([ 'commandStack.changed', 'diagram.clear', 'import.done', 'variables.changed' ], () => {
4456
this.invalidateCache();
@@ -100,6 +112,7 @@ export class BaseVariableResolver {
100112
*/
101113
invalidateCache() {
102114
this.rawVariables.invalidate();
115+
this._allParsedVariables.invalidate();
103116
this.parsedVariables.invalidate();
104117
}
105118

@@ -159,12 +172,26 @@ export class BaseVariableResolver {
159172
variables.forEach(variable => {
160173
const existingVariable = mergedVariables.find(v =>
161174
v.name === variable.name && v.scope === variable.scope
175+
&& (v.scope || !v.usedBy) && (variable.scope || !variable.usedBy)
162176
);
163177

164178
if (existingVariable) {
165179
merge('origin', existingVariable, variable);
166180
merge('provider', existingVariable, variable);
167181
mergeEntries(existingVariable, variable);
182+
183+
// Preserve usedBy from either side during merge
184+
if (variable.usedBy) {
185+
if (!existingVariable.usedBy) {
186+
existingVariable.usedBy = [ ...variable.usedBy ];
187+
} else {
188+
variable.usedBy.forEach(target => {
189+
if (!existingVariable.usedBy.includes(target)) {
190+
existingVariable.usedBy.push(target);
191+
}
192+
});
193+
}
194+
}
168195
} else {
169196
mergedVariables.push(variable);
170197
}
@@ -251,7 +278,7 @@ export class BaseVariableResolver {
251278
*
252279
* @async
253280
* @param {ModdleElement} element
254-
* @returns {Array<ProcessVariable>} variables
281+
* @returns {Promise<Array<ProcessVariable>>} variables
255282
*/
256283
async getVariablesForElement(element) {
257284
const bo = getBusinessObject(element);
@@ -261,14 +288,14 @@ export class BaseVariableResolver {
261288

262289
// (1) get variables for given scope
263290
var scopeVariables = allVariables.filter(function(variable) {
264-
return variable.scope.id === bo.id;
291+
return variable.scope && variable.scope.id === bo.id;
265292
});
266293

267294
// (2) get variables for parent scopes
268295
var parents = getParents(bo);
269296

270297
var parentsScopeVariables = allVariables.filter(function(variable) {
271-
return parents.find(function(parent) {
298+
return variable.scope && parents.find(function(parent) {
272299
return parent.id === variable.scope.id;
273300
});
274301
});
@@ -299,6 +326,31 @@ export class BaseVariableResolver {
299326
throw new Error('not implemented VariableResolver#_getScope');
300327
}
301328

329+
/**
330+
* Returns consumed variables for an element — variables
331+
* the element needs as input for its expressions and mappings.
332+
*
333+
* Uses `getVariables()` instead of `getVariablesForElement()` to
334+
* bypass the name-based deduplication that would drop requirement
335+
* entries for variables that also exist in ancestor scopes.
336+
*
337+
* @param {Object} element
338+
* @returns {Promise<Array<AvailableVariable>>}
339+
*/
340+
async getConsumedVariablesForElement(element) {
341+
const allVariablesByRoot = await this._allParsedVariables.get()
342+
.catch(() => {
343+
return {};
344+
});
345+
346+
const allVariables = Object.values(allVariablesByRoot).flat();
347+
348+
return allVariables.filter(v =>
349+
v.usedBy && v.usedBy.length > 0
350+
&& !v.scope
351+
&& v.origin.length === 1 && v.origin[0].id === element.id
352+
);
353+
}
302354
}
303355

304356
BaseVariableResolver.$inject = [ 'eventBus', 'bpmnjs' ];

lib/zeebe/VariableResolver.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ export default class ZeebeVariableResolver extends BaseVariableResolver {
100100

101101
for (const key in rawVariables) {
102102
const variables = rawVariables[key];
103-
const newVariables = parseVariables(variables);
103+
const { resolvedVariables, consumedVariables } = parseVariables(variables);
104104

105-
mappedVariables[key] = [ ...variables, ...newVariables ];
105+
mappedVariables[key] = [ ...variables, ...resolvedVariables, ...consumedVariables ];
106106
}
107107

108108
context.variables = mappedVariables;

0 commit comments

Comments
 (0)