From d0bb0e348a90887e65f42454f7e8a4d0c1d4c4f1 Mon Sep 17 00:00:00 2001 From: rikkola Date: Tue, 7 May 2024 14:26:16 +0300 Subject: [PATCH] MVEL support --- .../org/kie/yard/core/DTableUnitBuilder.java | 14 +++-- .../MVELLiteralExpressionInterpreter.java | 18 +++++- .../yard/core/DomesticPackagePricesTest.java | 7 ++- .../kie/yard/core/InsuranceBasePriceTest.java | 1 + .../java/org/kie/yard/core/MVELJSONTest.java | 61 +++++++++++++++++++ .../test/resources/mvel-json-dot-access.yml | 57 +++++++++++++++++ 6 files changed, 147 insertions(+), 11 deletions(-) create mode 100644 kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/MVELJSONTest.java create mode 100644 kie-yard/kie-yard-core/src/test/resources/mvel-json-dot-access.yml diff --git a/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/DTableUnitBuilder.java b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/DTableUnitBuilder.java index 3bb1ba8628a..592d7e7a260 100644 --- a/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/DTableUnitBuilder.java +++ b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/DTableUnitBuilder.java @@ -85,13 +85,14 @@ private OnExecute getExecutionAction(String hitPolicy) { if (hitPolicy == null || Objects.equals("ANY", hitPolicy)) { return (ruleDefinition, storeHandle) -> { final RuleCell rc = parseGenericRuleThen(ruleDefinition); - storeHandle.set(rc.value); + Object value = resolveValue(rc.value); + storeHandle.set(value); }; } else if (Objects.equals("FIRST", hitPolicy)) { return (ruleDefinition, storeHandle) -> { if (!storeHandle.isValuePresent()) { final RuleCell rc = parseGenericRuleThen(ruleDefinition); - storeHandle.set(rc.value); + storeHandle.set(resolveValue(rc.value)); } }; } else if (Objects.equals("COLLECT", hitPolicy)) { @@ -102,22 +103,23 @@ private OnExecute getExecutionAction(String hitPolicy) { final RuleCell rc = parseGenericRuleThen(ruleDefinition); if (storeHandle.get() instanceof List list) { - list.add(resolveValue(rc)); + list.add(resolveValue(rc.value)); } + String remove = ""; }; } else { throw new UnsupportedOperationException("Not implemented "); } } - private Object resolveValue(final RuleCell rc) { + private Object resolveValue(Object value) { try { - if (rc.value instanceof String text) { + if (value instanceof String text) { return jsonMapper.readValue(text, Map.class); } } catch (JsonProcessingException ignored) { } - return rc.value; + return value; } private RuleCell parseGenericRuleThen(Rule rule) { diff --git a/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/MVELLiteralExpressionInterpreter.java b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/MVELLiteralExpressionInterpreter.java index 70bd6e3a152..3e9971fe1fb 100644 --- a/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/MVELLiteralExpressionInterpreter.java +++ b/kie-yard/kie-yard-core/src/main/java/org/kie/yard/core/MVELLiteralExpressionInterpreter.java @@ -1,9 +1,9 @@ package org.kie.yard.core; -import org.drools.base.util.MVELExecutor; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.json.JsonMapper; import org.mvel2.MVEL; -import javax.script.Bindings; import java.util.HashMap; import java.util.Map; @@ -11,6 +11,8 @@ public class MVELLiteralExpressionInterpreter implements Firable { private final String name; private final QuotedExprParsed expr; + private final JsonMapper jsonMapper = JsonMapper.builder().build(); + public MVELLiteralExpressionInterpreter(final String name, final QuotedExprParsed expr) { this.name = name; @@ -33,11 +35,21 @@ public int fire(final Map context, try { String rewrittenExpression = expr.getRewrittenExpression(); final Object result = MVEL.eval(rewrittenExpression, internalContext); - units.outputs().get(name).set(result); + units.outputs().get(name).set(resolveValue(result)); return 1; } catch (Exception e) { throw new RuntimeException("interpretation failed at runtime", e); // TODO why throw and not return 0? } } + + private Object resolveValue(Object value) { + try { + if (value instanceof String text) { + return jsonMapper.readValue(text, Map.class); + } + } catch (JsonProcessingException ignored) { + } + return value; + } } diff --git a/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/DomesticPackagePricesTest.java b/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/DomesticPackagePricesTest.java index ba94c9449c9..a7a94255c7a 100644 --- a/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/DomesticPackagePricesTest.java +++ b/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/DomesticPackagePricesTest.java @@ -24,6 +24,7 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; public class DomesticPackagePricesTest extends TestBase { @@ -41,7 +42,8 @@ public void testMPackage() throws Exception { } """; Map outputJSONasMap = evaluate(CTX, FILE_NAME); - assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Package", "{ \"Size\": \"M\", \"Cost\": 6.90 }"); + assertEquals(6.9, ((Map) outputJSONasMap.get("Package")).get("Cost")); + assertEquals("M", ((Map) outputJSONasMap.get("Package")).get("Size")); } @Test @@ -55,6 +57,7 @@ public void testLPackage() throws Exception { } """; Map outputJSONasMap = evaluate(CTX, FILE_NAME); - assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Package", "{ \"Size\": \"L\", \"Cost\": 8.90 }"); + assertEquals(8.9, ((Map) outputJSONasMap.get("Package")).get("Cost")); + assertEquals("L", ((Map) outputJSONasMap.get("Package")).get("Size")); } } diff --git a/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java b/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java index cfed2129397..86cc261fae0 100644 --- a/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java +++ b/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; public class InsuranceBasePriceTest extends TestBase { diff --git a/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/MVELJSONTest.java b/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/MVELJSONTest.java new file mode 100644 index 00000000000..c1921c2414f --- /dev/null +++ b/kie-yard/kie-yard-core/src/test/java/org/kie/yard/core/MVELJSONTest.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.yard.core; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MVELJSONTest + extends TestBase { + + private static final String FILE_NAME = "/mvel-json-dot-access.yml"; + + @Test + public void testMVELManagesJSONMaps() throws Exception { + final String CTX = """ + { + "Work Address":true + } + """; + final Map output = evaluate(CTX, FILE_NAME); + + final Map mailingAddress = (Map) output.get("Mailing Address"); + assertEquals("Work Street", mailingAddress.get("Street")); + assertEquals(23, mailingAddress.get("Number")); + + final List deliveryItemNames = (List) output.get("Delivery Item Names"); + assertEquals(3, deliveryItemNames.size()); + assertTrue(deliveryItemNames.contains("Work Shoes")); + assertTrue(deliveryItemNames.contains("Work Hat")); + assertTrue(deliveryItemNames.contains("Work Shirt")); + + final Map JSONTest= (Map) output.get("JSON Test"); + assertEquals("Best Company LTD", JSONTest.get("Company")); + + final Map mapTest = (Map) output.get("Map Test"); + assertEquals("Hello", mapTest.get("Map")); + } + // TODO Validate two elements can not have the same name +} diff --git a/kie-yard/kie-yard-core/src/test/resources/mvel-json-dot-access.yml b/kie-yard/kie-yard-core/src/test/resources/mvel-json-dot-access.yml new file mode 100644 index 00000000000..3bd073033dc --- /dev/null +++ b/kie-yard/kie-yard-core/src/test/resources/mvel-json-dot-access.yml @@ -0,0 +1,57 @@ +specVersion: alpha +kind: YaRD +name: "Testing MVEL" +inputs: + - name: Work Address + type: boolean +elements: + - name: JSON Test + type: Decision + logic: + type: LiteralExpression + expression: | + '{ "Company": "Best Company LTD" }' + - name: Map Test + type: Decision + logic: + type: LiteralExpression + expression: | + [ "Map": "Hello" ] + - name: Delivery Items + type: Decision + logic: + type: DecisionTable + hitPolicy: COLLECT + inputs: [ 'Work Address'] + rules: + - when: [ true ] + then: '{ "Item": { "Name": "Work Shoes" } }' + - when: [ true ] + then: '{ "Item": { "Name": "Work Hat" } }' + - when: [ true ] + then: '{ "Item": { "Name": "Work Shirt" } }' + - when: [ false ] + then: '{ "Item": { "Name": "Holiday Hat" } }' + - name: Customer + type: Decision + logic: + type: DecisionTable + hitPolicy: ANY + inputs: [ 'Work Address'] + rules: + - when: [ true ] + then: '{ "Address": { "Street": "Work Street", "Number": 23 } }' + - when: [ false ] + then: '{ "Address": { "Street": "Free Time Path", "Number": 123 } }' + - name: Mailing Address + type: Decision + logic: + type: LiteralExpression + expression: | + Customer.Address + - name: Delivery Item Names + type: Decision + logic: + type: LiteralExpression + expression: | + ( Item.Name in `Delivery Items` )