Skip to content

Commit

Permalink
SpEL: backward compatibility support import of CollectionUtils.contai…
Browse files Browse the repository at this point in the history
…nsAny (#1174)

* backw support CollectionUtils.containsAny

* test

* chlog
  • Loading branch information
ukrbublik authored Jan 13, 2025
1 parent 2fda41a commit a0433c0
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog
- 6.6.8
- Support safe navigation operator in SpEL operators/functions (PR #1172) (issue #1010)
- SpEL: backward compatibility for import of `CollectionUtils.containsAny` (PR #1174) (issue #1007)
- 6.6.7
- Fix import of ambiguous operators (like, select_any_in) (PR #1168) (issue #1159)
- Allow import of epoch for date/datetime widgets from JsonLogic (PR #1171) (issue #1154)
Expand Down
14 changes: 14 additions & 0 deletions packages/core/modules/config/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,20 @@ const operators = {
}),
//spelOp: "${0}.containsAll(${1})",
spelOp: "T(CollectionUtils).containsAny(${0}, ${1})",
spelImportFuncs: [
// just for backward compatibility (issue #1007)
{
obj: {
type: "property",
val: "CollectionUtils"
},
methodName: "containsAny",
args: [
{var: "0"},
{var: "1"},
],
}
],
elasticSearchQueryType: "term",
mongoFormatOp: function(...args) { return this.utils.mongoFormatOp1("$in", v => v, false, ...args); },
},
Expand Down
53 changes: 44 additions & 9 deletions packages/core/modules/import/spel/convert.js
Original file line number Diff line number Diff line change
Expand Up @@ -523,12 +523,13 @@ const convertFuncToValue = (spel, conv, config, meta, parentSpel, fsigns, conver
};

const convertFuncToOp = (spel, conv, config, meta, parentSpel, fsigns, convertFuncArg) => {
let errs, opKey, foundSign;
const candidates = [];

for (const {s, params} of fsigns) {
const found = conv.opFuncs[s] || [];
for (const {op, argsOrder} of found) {
const argsArr = params.map(convertFuncArg);
opKey = op;
const errs = [];
if (op === "!compare") {
if (
parentSpel.type.startsWith("op-")
Expand All @@ -544,9 +545,7 @@ const convertFuncToOp = (spel, conv, config, meta, parentSpel, fsigns, convertFu
errs.push("Result of compareTo() should be compared to 0");
}
}
foundSign = s;
errs = [];
const opDef = config.operators[opKey];
const opDef = config.operators[op];
const {valueTypes} = opDef;
const argsObj = Object.fromEntries(
argsOrder.map((argKey, i) => [argKey, argsArr[i]])
Expand All @@ -557,14 +556,50 @@ const convertFuncToOp = (spel, conv, config, meta, parentSpel, fsigns, convertFu
if (valueTypes && valueType && !valueTypes.includes(valueType)) {
errs.push(`Op supports types ${valueTypes}, but got ${valueType}`);
}
if (!errs.length) {
return buildRule(config, meta, field, opKey, convertedArgs, spel);
candidates.push({
opKey: op, foundSign: s, field, convertedArgs, errs,
});
}
}

for (let op in config.operators) {
const opDef = config.operators[op];
const {spelImportFuncs, valueTypes} = opDef;
if (spelImportFuncs) {
for (let i = 0 ; i < spelImportFuncs.length ; i++) {
const fj = spelImportFuncs[i];
if (isObject(fj)) {
const argsObj = {};
if (isJsonCompatible(fj, spel, argsObj)) {
const errs = [];
for (const k in argsObj) {
argsObj[k] = convertFuncArg(argsObj[k]);
}
const field = argsObj["0"];
const convertedArgs = Object.keys(argsObj).filter(k => parseInt(k) > 0).map(k => argsObj[k]);
const valueType = argsObj["1"]?.valueType;
if (valueTypes && valueType && !valueTypes.includes(valueType)) {
errs.push(`Op supports types ${valueTypes}, but got ${valueType}`);
}
candidates.push({
opKey: op, foundSign: `spelImportFuncs[${i}]`, field, convertedArgs, errs,
});
}
}
}
}
}

if (opKey && errs.length) {
meta.errors.push(`Signature ${foundSign} - looks like convertable to ${opKey}, but: ${errs.join("; ")}`);
const bestCandidate = candidates.find(({errs}) => !errs.length);
if (bestCandidate) {
const {opKey, foundSign, field, convertedArgs, errs} = bestCandidate;
return buildRule(config, meta, field, opKey, convertedArgs, spel);
} else if (candidates.length) {
const allErrs = candidates.map(
({foundSign, opKey, errs}) =>
`Looks like convertable to ${opKey} with signature ${foundSign}, but: ${errs.join("; ")}`
).join(". ");
meta.errors.push(allErrs);
}

return undefined;
Expand Down
12 changes: 12 additions & 0 deletions packages/tests/specs/QueryWithOperators.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,18 @@ describe("query with ops", () => {
"sql": inits.with_ops_sql,
});
});

describe("@spel multiselect_contains import works", () => {
export_checks([configs.with_all_types], "T(CollectionUtils).containsAny(multicolor, {'yellow'})", "SpEL", {
spel: "T(CollectionUtils).containsAny(multicolor, {'yellow'})",
});
});

describe("@spel multiselect_contains import is backward compatible", () => {
export_checks([configs.with_all_types], "CollectionUtils.containsAny(multicolor, {'yellow'})", "SpEL", {
spel: "T(CollectionUtils).containsAny(multicolor, {'yellow'})",
});
});
});

describe("query with exclamation operators", () => {
Expand Down

0 comments on commit a0433c0

Please sign in to comment.