From 745ac21fbff05bded5d18e584d39bef26d1f1e5b Mon Sep 17 00:00:00 2001 From: ukrbublik Date: Thu, 23 May 2024 01:58:25 +0300 Subject: [PATCH] fixes fix . --- CHANGELOG.md | 2 + packages/core/modules/config/index.js | 5 +- packages/core/modules/export/jsonLogic.js | 73 ++++++++--------- packages/core/modules/index.d.ts | 1 + packages/core/modules/utils/export.js | 10 +++ packages/examples/demo_switch/config.tsx | 1 + packages/examples/demo_switch/index.tsx | 95 ++++++++++++++++++----- packages/tests/karma.conf.js | 2 +- packages/tests/specs/SwitchCase.test.ts | 24 ++++-- packages/tests/support/configs.js | 1 + packages/tests/support/inits.js | 2 +- packages/tests/support/utils.tsx | 7 +- 12 files changed, 156 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 267dc5949..d6eacfca9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ # Changelog +- 6.6.0 + - Add JsonLogic Export for SwitchCase (PR #1013) - 6.5.2 - Updated dependencies. `@babel/runtime` is now dep for core package (PR #1051) (issue #964) - 6.5.1 diff --git a/packages/core/modules/config/index.js b/packages/core/modules/config/index.js index b8cd1f171..02e8c6225 100644 --- a/packages/core/modules/config/index.js +++ b/packages/core/modules/config/index.js @@ -941,7 +941,10 @@ const widgets = { }, spelImportValue: (val) => { return [val.value, []]; - } + }, + jsonLogic: function (val) { + return val === "" ? null : val; + }, } }; diff --git a/packages/core/modules/export/jsonLogic.js b/packages/core/modules/export/jsonLogic.js index 0085bfba0..a5ea21553 100644 --- a/packages/core/modules/export/jsonLogic.js +++ b/packages/core/modules/export/jsonLogic.js @@ -70,7 +70,7 @@ const formatItem = (item, config, meta, isRoot, parentField = null) => { } else if (type === "rule") { ret = formatRule(item, config, meta, parentField); } else if (type == "switch_group") { - ret = formatSwitch(item, config, meta, parentField); + ret = formatSwitch(item, config, meta); } else if (type == "case_group") { ret = formatCase(item, config, meta, parentField); } @@ -207,73 +207,76 @@ const formatRule = (item, config, meta, parentField = null) => { return formatLogic(config, properties, formattedField, formattedValue, operator, operatorOptions, fieldDefinition, isRev); }; -const formatSwitch = (item, config, meta, parentField = null) => { - const properties = item.get("properties") || new Map(); +const formatSwitch = (item, config, meta) => { const children = item.get("children1"); - if (!children) return undefined; + if (!children) + return undefined; const cases = children .map((currentChild) => formatCase(currentChild, config, meta, null)) .filter((currentChild) => typeof currentChild !== "undefined") - .toArray(); + .valueSeq().toArray(); - if (!cases.length) return undefined; - - if (cases.length == 1 && !cases[0][0]) { - // only 1 case without condition - return cases[0][1]; - } let filteredCases = []; for (let i = 0 ; i < cases.length ; i++) { - if (i != (cases.length - 1) && !cases[i][0]) { + if (i !== (cases.length - 1) && !cases[i][0]) { meta.errors.push(`No condition for case ${i}`); } else { filteredCases.push(cases[i]); - if (i == (cases.length - 1) && cases[i][0]) { + if (i === (cases.length - 1) && cases[i][0]) { // no default - add null as default filteredCases.push([undefined, null]); } } } - let left = "", right = ""; - const ret = { - "IF": [], - }; - for (let i = 0 ; i < filteredCases.length ; i++) { + if (!filteredCases.length) + return undefined; + + if (filteredCases.length === 1) { + // only 1 case without condition + let [_cond, defVal] = filteredCases[0]; + if (defVal == undefined) + defVal = null; + return defVal; + } + + const ret = { if: [] }; + let ifArgs = ret.if; + const [_, defVal] = filteredCases[filteredCases.length - 1]; + for (let i = 0 ; i < filteredCases.length - 1 ; i++) { + const isLastIf = i === (filteredCases.length - 2); let [cond, value] = filteredCases[i]; if (value == undefined) - value = "null"; + value = null; if (cond == undefined) - cond = "true"; - if (i != (filteredCases.length - 1)) { - ret.IF.push(cond); + cond = true; + ifArgs.push(cond); // if + ifArgs.push(value); // then + if (isLastIf) { + ifArgs.push(defVal); // else + } else { + // elif.. + ifArgs.push({ if: [] }); + ifArgs = ifArgs[ifArgs.length - 1].if; } - ret.IF.push(value); } return ret; }; -const formatCase = (item, config, meta, isRoot, parentField = null) => { - const type = item.get('type'); - if (type != 'case_group') { +const formatCase = (item, config, meta, parentField = null) => { + const type = item.get("type"); + if (type != "case_group") { meta.errors.push(`Unexpected child of type ${type} inside switch`); return undefined; } - const properties = item.get('properties') || new Map(); + const properties = item.get("properties") || new Map(); const cond = formatGroup(item, config, meta, parentField); - // return [cond, formattedValue]; - const values = properties.get('value'); const formattedItem = formatItemValue( config, properties, meta, null, parentField, "!case_value" ); - if(typeof formattedItem == 'string'){ - return [cond, formattedItem]; - } else if (Array.isArray(formattedItem)) { - return [cond, formattedItem[0].value]; - } - return [cond, null]; + return [cond, formattedItem]; }; const formatItemValue = (config, properties, meta, operator, parentField, expectedValueType = null) => { diff --git a/packages/core/modules/index.d.ts b/packages/core/modules/index.d.ts index 6d6d05c4d..db094c3c6 100644 --- a/packages/core/modules/index.d.ts +++ b/packages/core/modules/index.d.ts @@ -527,6 +527,7 @@ interface ConfigUtils { interface ExportUtils { spelEscape(val: any): string; spelFormatConcat(parts: SpelConcatParts): string; + jsonLogicFormatConcat(parts: SpelConcatParts): any; spelImportConcat(val: SpelConcatValue): [SpelConcatParts | undefined, Array]; } interface ListUtils { diff --git a/packages/core/modules/utils/export.js b/packages/core/modules/utils/export.js index dea51fbe7..68cc338e4 100644 --- a/packages/core/modules/utils/export.js +++ b/packages/core/modules/utils/export.js @@ -129,6 +129,16 @@ export const spelEscape = (val, numberToFloat = false, arrayToArray = false) => } }; +export const jsonLogicFormatConcat = (parts) => { + if (parts && Array.isArray(parts) && parts.length) { + return parts + .map(part => part?.value ?? part) + .filter(r => r != undefined); + } else { + return undefined; + } +}; + export const spelFormatConcat = (parts) => { if (parts && Array.isArray(parts) && parts.length) { return parts diff --git a/packages/examples/demo_switch/config.tsx b/packages/examples/demo_switch/config.tsx index c229739fa..518352dda 100644 --- a/packages/examples/demo_switch/config.tsx +++ b/packages/examples/demo_switch/config.tsx @@ -15,6 +15,7 @@ export default (): Config => { ...InitialConfig.widgets.case_value, spelFormatValue: QbUtils.ExportUtils.spelFormatConcat, spelImportValue: QbUtils.ExportUtils.spelImportConcat, + jsonLogic: QbUtils.ExportUtils.jsonLogicFormatConcat, factory: ({value, setValue, id}: WidgetProps) => { /> ); - const renderSpelOutput = () => ( -
- Output SpEL: -
-        {QbUtils.spelFormat(state.tree, state.config)}
-      
- Values: -
-        {JSON.stringify(QbUtils.getSwitchValues(state.tree), undefined, 2)}
-      
-
-
-
- Tree: -
-        {JSON.stringify(QbUtils.getTree(state.tree), undefined, 2)}
-      
-
- ); - const renderSpelInput = () => (
Input SpEL: @@ -109,11 +89,84 @@ const Demo: React.FC = () => {
); + const renderSpelBlock = () => { + const [spel, spelErrors] = QbUtils._spelFormat(state.tree, state.config); + + return ( + <> +
+ spelFormat: + { spelErrors.length > 0 + &&
+              {JSON.stringify(spelErrors, undefined, 2)}
+            
+ } +
+            {JSON.stringify(spel, undefined, 2)}
+          
+ Values: +
+            {JSON.stringify(QbUtils.getSwitchValues(state.tree), undefined, 2)}
+          
+
+
+ + ); + }; + + const renderJsTreeBlock = () => { + const treeJs = QbUtils.getTree(state.tree); + + return ( + <> + Tree: +
+          {JSON.stringify(treeJs, undefined, 2)}
+        
+
+
+
+ + ); + }; + + const renderJsonLogicBlock = () => { + const {logic, data: logicData, errors: logicErrors} = QbUtils.jsonLogicFormat(state.tree, state.config); + + return ( + <> +
+ jsonLogicFormat: + { (logicErrors?.length || 0) > 0 + &&
+              {JSON.stringify(logicErrors, undefined, 2)}
+            
+ } + { !!logic + &&
+              {"// Rule"}:
+ {JSON.stringify(logic, undefined, 2)} +
+
+ {"// Data"}:
+ {JSON.stringify(logicData, undefined, 2)} +
+ } +
+
+ + ); + }; + return (
{renderSpelInput()} {renderQueryBuilder()} - {renderSpelOutput()} +
+ {renderSpelBlock()} + {renderJsonLogicBlock()} + {renderJsTreeBlock()} +
); }; diff --git a/packages/tests/karma.conf.js b/packages/tests/karma.conf.js index 79824acf5..9dfe7a7ca 100644 --- a/packages/tests/karma.conf.js +++ b/packages/tests/karma.conf.js @@ -19,7 +19,7 @@ let reporters; if (isCI) { reporters = ["mocha", "junit", "coverage"]; } else if (useCoverage) { - reporters = ["progress", "coverage"]; + reporters = ["coverage", "mocha"]; // "progress" } else { reporters = ["mocha"]; } diff --git a/packages/tests/specs/SwitchCase.test.ts b/packages/tests/specs/SwitchCase.test.ts index b6001c94e..5eba7c32c 100644 --- a/packages/tests/specs/SwitchCase.test.ts +++ b/packages/tests/specs/SwitchCase.test.ts @@ -5,18 +5,32 @@ import {export_checks} from "../support/utils"; describe("query with switch-case", () => { - describe("simple", () => { + describe("2 cases and 1 default", () => { export_checks([configs.with_all_types, configs.with_cases], inits.spel_with_cases, "SpEL", { "spel": "(str == '222' ? 'is_string' : (num == 4 ? 'is_number' : 'unknown'))", logic: - {"IF":[{"==":[{"var":"str"},"222"]},"is_string",{"==":[{"var":"num"},4]},"is_number","unknown"]} + {"if": [ + {"==":[{"var":"str"},"222"]}, + "is_string", + {"if": [ + {"==":[{"var":"num"},4]}, + "is_number", + "unknown" + ]} + ]} }); }); - describe("simple json", () => { - export_checks([configs.with_concat_case_value, configs.with_cases], inits.spel_with_cases_simple, "SpEL", { + describe("1 case and 1 default", () => { + export_checks([configs.with_all_types, configs.with_cases], inits.spel_with_cases_simple, "SpEL", { logic: - {"IF": [{"==": [{"var": "str"}, "222"]}, "foo", "bar"]} + { + "if": [ + {"==": [{"var": "str"}, "222"]}, + "foo", + "bar" + ] + } }); }); diff --git a/packages/tests/support/configs.js b/packages/tests/support/configs.js index d0574cf4e..1a46646a6 100644 --- a/packages/tests/support/configs.js +++ b/packages/tests/support/configs.js @@ -1547,6 +1547,7 @@ export const with_concat_case_value = (BasicConfig) => ({ ...BasicConfig.widgets.case_value, spelFormatValue: ExportUtils.spelFormatConcat, spelImportValue: ExportUtils.spelImportConcat, + jsonLogic: ExportUtils.jsonLogicFormatConcat, }, }, }); diff --git a/packages/tests/support/inits.js b/packages/tests/support/inits.js index c18a9fe52..d7fc5ca8d 100644 --- a/packages/tests/support/inits.js +++ b/packages/tests/support/inits.js @@ -1480,7 +1480,7 @@ export const spel_with_not_between = "(num < 1 || num > 2)"; export const spel_with_not = "!(num == 2)"; export const spel_with_not_not = "!(num == 2 || !(num == 3))"; export const spel_with_cases = "(str == '222' ? is_string : (num == 4 ? is_number : unknown))"; -export const spel_with_cases_simple = "(str == '222' ? foo : foo)"; +export const spel_with_cases_simple = "(str == '222' ? foo : bar)"; export const spel_with_cases_and_concat = "(str == '222' ? foo : foo + bar)"; export const spel_with_lhs_toLowerCase = "str.toLowerCase().startsWith('aaa')"; diff --git a/packages/tests/support/utils.tsx b/packages/tests/support/utils.tsx index c0deaaed8..86daa31f4 100644 --- a/packages/tests/support/utils.tsx +++ b/packages/tests/support/utils.tsx @@ -687,9 +687,10 @@ const expect_jlogic_before_and_after = (config: Config, tree: ImmutableTree, onC }; export const expect_objects_equal = (act: any, exp: any, actLabel?: string, expLabel?: string) => { - const expStr = JSON.stringify(exp); - const actStr = JSON.stringify(act); - expect(actStr).to.equal(expStr); + // const expStr = JSON.stringify(exp); + // const actStr = JSON.stringify(act); + // expect(actStr).to.equal(expStr); + expect(act).to.eql(exp); }; export function hexToRgb(hex: string) {