From 16b5967411713d41e932db3c93aaa2bb5e91a9ed Mon Sep 17 00:00:00 2001 From: Denis Oblogin Date: Thu, 27 Jul 2023 14:16:54 +0300 Subject: [PATCH 1/2] Fixed import of fields with dot from JsonLogic --- CHANGELOG.md | 1 + packages/core/modules/import/jsonLogic.js | 34 ++++++++++--------- .../specs/QueryWithGroupsAndStructs.test.js | 22 ++++++++++++ packages/tests/support/configs.js | 11 ++++++ packages/tests/support/inits.js | 12 +++++-- 5 files changed, 62 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe706b53f..0b219ebce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Updated type `ItemBuilderProps` (PR #959) - Fixed drag-n-drop to respect `maxNesting` when moving group into group (PR #959) - Fix: allow custom conjuction in JsonLogic (issue #317) (PR #959) + - Fixed import of fields with dot from JsonLogic (issue #786) (PR #xxx) - 6.4.0 - Functions can be used in LHS with `fieldSources: ["field", "func"]` in `settings` Thanks @rhallerman1 (PR #900, #896) (issues #287, #250, #344, #336) diff --git a/packages/core/modules/import/jsonLogic.js b/packages/core/modules/import/jsonLogic.js index fa3b9e1e3..83d9578f8 100644 --- a/packages/core/modules/import/jsonLogic.js +++ b/packages/core/modules/import/jsonLogic.js @@ -430,7 +430,7 @@ const convertConj = (op, vals, conv, config, not, meta, parentField = null, isRu parts.slice(0, -1) .map((f, i, parts) => [...parts.slice(0, i), f]) .map(fp => [fp.join(fieldSeparator), getFieldConfig(config, fp)]) - .filter(([_f, fc]) => fc.type == "!group") + .filter(([_f, fc]) => fc?.type == "!group") ); return [f, Object.keys(ancs)]; }) @@ -451,7 +451,7 @@ const convertConj = (op, vals, conv, config, not, meta, parentField = null, isRu let children1 = {}; let groupToId = {}; Object.entries(children).map(([k, v]) => { - if (v.type == "group" || v.type == "rule_group") { + if (v?.type == "group" || v?.type == "rule_group") { // put as-is children1[k] = v; } else { @@ -460,7 +460,9 @@ const convertConj = (op, vals, conv, config, not, meta, parentField = null, isRu const groupField = groupAncestors[groupAncestors.length - 1]; if (!groupField) { // not in rule_group (can be simple field or in struct) - put as-is - children1[k] = v; + if (v) { + children1[k] = v; + } } else { // wrap field in rule_group (with creating hierarchy if need) let ch = children1; @@ -514,19 +516,19 @@ const convertConj = (op, vals, conv, config, not, meta, parentField = null, isRu }; -const topLevelFieldsFilter = (fields) => { - let arr = [...fields].sort((a, b) => (a.length - b.length)); - for (let i = 0 ; i < arr.length ; i++) { - for (let j = i + 1 ; j < arr.length ; j++) { - if (arr[j].indexOf(arr[i]) == 0) { - // arr[j] is inside arr[i] (eg. "a.b" inside "a") - arr.splice(j, 1); - j--; - } - } - } - return arr; -}; +// const topLevelFieldsFilter = (fields) => { +// let arr = [...fields].sort((a, b) => (a.length - b.length)); +// for (let i = 0 ; i < arr.length ; i++) { +// for (let j = i + 1 ; j < arr.length ; j++) { +// if (arr[j].indexOf(arr[i]) == 0) { +// // arr[j] is inside arr[i] (eg. "a.b" inside "a") +// arr.splice(j, 1); +// j--; +// } +// } +// } +// return arr; +// }; const wrapInDefaultConjRuleGroup = (rule, parentField, parentFieldConfig, config, conj) => { if (!rule) return undefined; diff --git a/packages/tests/specs/QueryWithGroupsAndStructs.test.js b/packages/tests/specs/QueryWithGroupsAndStructs.test.js index ff1004da3..581d47a93 100644 --- a/packages/tests/specs/QueryWithGroupsAndStructs.test.js +++ b/packages/tests/specs/QueryWithGroupsAndStructs.test.js @@ -822,3 +822,25 @@ describe("query with !group mode array", () => { }); }); + +////////////////////////////////////////////////////////////////////////////////////////// + +describe("query with dot but without !struct", () => { + + describe("should handle dot notation when importing from JsonLogic", () => { + export_checks(configs.with_dot_in_field, inits.with_dot_in_field, "JsonLogic", { + "logic": inits.with_dot_in_field, + "spel": inits.spel_with_dot_in_field, + }); + }); + + describe("should handle dot notation when importing from SpEL", () => { + export_checks(configs.with_dot_in_field, inits.spel_with_dot_in_field, "SpEL", { + "logic": inits.with_dot_in_field, + "spel": inits.spel_with_dot_in_field, + }); + }); + +}); + +////////////////////////////////////////////////////////////////////////////////////////// diff --git a/packages/tests/support/configs.js b/packages/tests/support/configs.js index bf2781fc8..839bff770 100644 --- a/packages/tests/support/configs.js +++ b/packages/tests/support/configs.js @@ -1256,3 +1256,14 @@ export const with_keepInputOnChangeFieldSrc = (BasicConfig) => ({ keepInputOnChangeFieldSrc: true, } }); + +export const with_dot_in_field = (BasicConfig) => ({ + ...BasicConfig, + fields: { + "number.one": { + label: "Number", + type: "number", + preferWidgets: ["number"], + }, + }, +}); diff --git a/packages/tests/support/inits.js b/packages/tests/support/inits.js index 901fcbd49..62b64213a 100644 --- a/packages/tests/support/inits.js +++ b/packages/tests/support/inits.js @@ -158,7 +158,7 @@ export const with_struct_and_group = { { "!!": { "var": "user.login" } } ] }; - + export const with_struct_and_group_mixed_obsolete = { "and": [ { "==": [ { "var": "results.slider" }, 22 ] }, @@ -1067,4 +1067,12 @@ export const tree_with_lhs_toLowerCase2 = { "conjunction": "AND", "not": false } -}; \ No newline at end of file +}; + +export const with_dot_in_field = { + "and": [ + { "==": [ { "var": "number.one" }, 11 ] }, + ] +}; + +export const spel_with_dot_in_field = "number.one == 11"; From 2197418cf91622e786a30e4c39859f666380f780 Mon Sep 17 00:00:00 2001 From: Denis Oblogin Date: Thu, 27 Jul 2023 14:28:38 +0300 Subject: [PATCH 2/2] Fixes export to SpEL to respect `fieldSeparator` --- CHANGELOG.md | 3 ++- packages/core/modules/config/index.js | 6 +++--- packages/core/modules/export/mongoDb.js | 5 +++-- packages/core/modules/export/spel.js | 7 +++++-- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b219ebce..c6c31bfc0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ - Updated type `ItemBuilderProps` (PR #959) - Fixed drag-n-drop to respect `maxNesting` when moving group into group (PR #959) - Fix: allow custom conjuction in JsonLogic (issue #317) (PR #959) - - Fixed import of fields with dot from JsonLogic (issue #786) (PR #xxx) + - Fixed import of fields with dot from JsonLogic (issue #786) (PR #960) + - Fixes export to SpEL to respect `fieldSeparator` (issue #958) (PR #960) - 6.4.0 - Functions can be used in LHS with `fieldSources: ["field", "func"]` in `settings` Thanks @rhallerman1 (PR #900, #896) (issues #287, #250, #344, #336) diff --git a/packages/core/modules/config/index.js b/packages/core/modules/config/index.js index fd9ee3191..b36668c36 100644 --- a/packages/core/modules/config/index.js +++ b/packages/core/modules/config/index.js @@ -1236,7 +1236,7 @@ const settings = { }, formatSpelField: function (field, parentField, parts, partsExt, fieldDefinition, config) { - let fieldName = partsExt.map(({key, parent}, ind) => { + let fieldName = partsExt.map(({key, parent, fieldSeparator: sep}, ind) => { if (ind == 0) { if (parent == "[map]") return `#this[${this.utils.spelEscape(key)}]`; @@ -1248,9 +1248,9 @@ const settings = { if (parent == "map" || parent == "[map]") return `[${this.utils.spelEscape(key)}]`; else if (parent == "class" || parent == "[class]") - return `.${key}`; + return `${sep}${key}`; else - return `.${key}`; + return `${sep}${key}`; } }).join(""); if (fieldDefinition.fieldName) { diff --git a/packages/core/modules/export/mongoDb.js b/packages/core/modules/export/mongoDb.js index 394418c3e..3c455c36c 100644 --- a/packages/core/modules/export/mongoDb.js +++ b/packages/core/modules/export/mongoDb.js @@ -53,7 +53,8 @@ const formatGroup = (parents, item, config, meta, _not = false, _canWrapExpr = t const type = item.get("type"); const properties = item.get("properties") || new Map(); const children = item.get("children1") || new List(); - const {canShortMongoQuery} = config.settings; + const {canShortMongoQuery, fieldSeparator} = config.settings; + const sep = fieldSeparator; const hasParentRuleGroup = parents.filter(it => it.get("type") == "rule_group").length > 0; const parentPath = parents @@ -71,7 +72,7 @@ const formatGroup = (parents, item, config, meta, _not = false, _canWrapExpr = t const not = _not ? !(properties.get("not")) : (properties.get("not")); const list = children .map((currentChild) => formatItem( - [...parents, item], currentChild, config, meta, not, mode != "array", mode == "array" ? (f => `$$el.${f}`) : undefined) + [...parents, item], currentChild, config, meta, not, mode != "array", mode == "array" ? (f => `$$el${sep}${f}`) : undefined) ) .filter((currentChild) => typeof currentChild !== "undefined"); if (!canHaveEmptyChildren && !list.size) diff --git a/packages/core/modules/export/spel.js b/packages/core/modules/export/spel.js index 815860e02..5b7509821 100644 --- a/packages/core/modules/export/spel.js +++ b/packages/core/modules/export/spel.js @@ -140,6 +140,7 @@ const formatGroup = (item, config, meta, parentField = null) => { const groupField = isRuleGroupArray ? field : parentField; const groupFieldDef = getFieldConfig(config, groupField) || {}; const isSpelArray = groupFieldDef.isSpelArray; + const {fieldSeparator} = config.settings; // check op for reverse let groupOperator = properties.get("operator"); @@ -177,7 +178,8 @@ const formatGroup = (item, config, meta, parentField = null) => { let ret; if (isRuleGroupArray) { const formattedField = formatField(meta, config, field, parentField); - const getSize = isSpelArray ? ".length" : ".size()"; + const sep = fieldSeparator || "."; + const getSize = sep + (isSpelArray ? "length" : "size()"); const fullSize = `${formattedField}${getSize}`; // https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/expressions.html#expressions-collection-selection const filteredSize = filter ? `${formattedField}.?[${filter}]${getSize}` : fullSize; @@ -431,7 +433,8 @@ const formatField = (meta, config, field, parentField = null) => { return { key, parent, - isSpelVariable + isSpelVariable, + fieldSeparator }; }); const formattedField = formatFieldFn.call(config.ctx, fieldName, parentField, fieldParts, fieldPartsMeta, fieldDefinition, config);