This is an automated email from the ASF dual-hosted git repository. rikkola pushed a commit to branch YARD in repository https://gitbox.apache.org/repos/asf/incubator-kie-kogito-runtimes.git
commit aab2f86060527ca75fa99c3ed7cc030cca3e79e5 Author: Toni Rikkola <[email protected]> AuthorDate: Mon Jan 29 12:57:19 2024 +0000 yard --- kogito-bom/pom.xml | 22 +++ kogito-build/kogito-dependencies-bom/pom.xml | 18 ++ kogito-build/kogito-kie-bom/pom.xml | 5 + pom.xml | 1 + yard/pom.xml | 69 ++++++++ yard/yard-api/pom.xml | 61 +++++++ .../java/org/kie/yard/api/model/DecisionLogic.java | 31 ++++ .../java/org/kie/yard/api/model/DecisionTable.java | 70 ++++++++ .../main/java/org/kie/yard/api/model/Element.java | 64 +++++++ .../java/org/kie/yard/api/model/InlineRule.java | 45 +++++ .../main/java/org/kie/yard/api/model/Input.java | 43 +++++ .../org/kie/yard/api/model/LiteralExpression.java | 34 ++++ .../java/org/kie/yard/api/model/Operators.java | 49 ++++++ .../src/main/java/org/kie/yard/api/model/Rule.java | 23 +++ .../org/kie/yard/api/model/RuleDefSerializer.java | 88 ++++++++++ .../java/org/kie/yard/api/model/WhenThenRule.java | 53 ++++++ .../yard/api/model/WhenThenRuleThenSerializer.java | 56 ++++++ .../src/main/java/org/kie/yard/api/model/YaRD.java | 82 +++++++++ yard/yard-api/src/main/resources/YaRD-schema.json | 157 +++++++++++++++++ yard/yard-core/pom.xml | 75 ++++++++ .../java/org/kie/yard/core/DTableUnitBuilder.java | 195 +++++++++++++++++++++ .../src/main/java/org/kie/yard/core/Firable.java | 27 +++ .../kie/yard/core/LiteralExpressionBuilder.java | 39 +++++ .../yard/core/LiteralExpressionInterpreter.java | 73 ++++++++ .../java/org/kie/yard/core/QuotedExprParsed.java | 115 ++++++++++++ .../main/java/org/kie/yard/core/StoreHandle.java | 69 ++++++++ .../kie/yard/core/SyntheticRuleUnitWrapper.java | 39 +++++ .../java/org/kie/yard/core/YaRDDefinitions.java | 55 ++++++ .../main/java/org/kie/yard/core/YaRDParser.java | 85 +++++++++ .../kie/yard/core/DomesticPackagePricesTest.java | 59 +++++++ .../java/org/kie/yard/core/ExtraCostsTest.java | 45 +++++ .../org/kie/yard/core/InsuranceBasePriceTest.java | 58 ++++++ .../src/test/java/org/kie/yard/core/TestBase.java | 35 ++++ .../src/test/resources/domestic-package-prices.yml | 30 ++++ yard/yard-core/src/test/resources/extra-costs.yml | 47 +++++ .../src/test/resources/insurance-base-price.yml | 30 ++++ yard/yard-core/src/test/resources/logback-test.xml | 18 ++ 37 files changed, 2065 insertions(+) diff --git a/kogito-bom/pom.xml b/kogito-bom/pom.xml index d916bb9eee..ce84bc6b86 100755 --- a/kogito-bom/pom.xml +++ b/kogito-bom/pom.xml @@ -2749,6 +2749,28 @@ <version>${project.version}</version> <classifier>sources</classifier> </dependency> + <dependency> + <groupId>org.kie.kogito</groupId> + <artifactId>yard-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.kie.kogito</groupId> + <artifactId>yard-api</artifactId> + <version>${project.version}</version> + <classifier>sources</classifier> + </dependency> + <dependency> + <groupId>org.kie.kogito</groupId> + <artifactId>yard-core</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.kie.kogito</groupId> + <artifactId>yard-core</artifactId> + <version>${project.version}</version> + <classifier>sources</classifier> + </dependency> </dependencies> </dependencyManagement> diff --git a/kogito-build/kogito-dependencies-bom/pom.xml b/kogito-build/kogito-dependencies-bom/pom.xml index 6d38e1aafc..873e2906c0 100644 --- a/kogito-build/kogito-dependencies-bom/pom.xml +++ b/kogito-build/kogito-dependencies-bom/pom.xml @@ -132,6 +132,9 @@ <version.black.ninia>4.2.0</version.black.ninia> <version.com.google.guava>32.0.0-jre</version.com.google.guava> <version.apache.commons.commons-compress>1.24.0</version.apache.commons.commons-compress> + <version.yaml.mapper.common>0.4</version.yaml.mapper.common> + <version.yaml.mapper>0.4</version.yaml.mapper> + <version.jshell>1.1.0</version.jshell> </properties> <dependencyManagement> @@ -806,6 +809,21 @@ <artifactId>jep</artifactId> <version>${version.black.ninia}</version> </dependency> + <dependency> + <groupId>org.treblereel.gwt.yaml.mapper</groupId> + <artifactId>common</artifactId> + <version>${version.yaml.mapper}</version> + </dependency> + <dependency> + <groupId>org.treblereel.gwt.yaml.mapper</groupId> + <artifactId>processor</artifactId> + <version>${version.yaml.mapper}</version> + </dependency> + <dependency> + <groupId>ch.obermuhlner</groupId> + <artifactId>jshell-scriptengine</artifactId> + <version>${version.jshell}</version> + </dependency> </dependencies> </dependencyManagement> diff --git a/kogito-build/kogito-kie-bom/pom.xml b/kogito-build/kogito-kie-bom/pom.xml index 836f4c4cbd..e6333d4409 100644 --- a/kogito-build/kogito-kie-bom/pom.xml +++ b/kogito-build/kogito-kie-bom/pom.xml @@ -185,6 +185,11 @@ <artifactId>drools-ruleunits-api</artifactId> <version>${version.org.kie}</version> </dependency> + <dependency> + <groupId>org.drools</groupId> + <artifactId>drools-ruleunits-dsl</artifactId> + <version>${version.org.kie}</version> + </dependency> <dependency> <groupId>org.drools</groupId> <artifactId>drools-ruleunits-impl</artifactId> diff --git a/pom.xml b/pom.xml index fbe18c88c2..4366453db1 100644 --- a/pom.xml +++ b/pom.xml @@ -145,6 +145,7 @@ <module>addons</module> <module>kogito-workitems</module> <module>kogito-serverless-workflow</module> + <module>yard</module> <module>kogito-maven-plugin</module> <module>springboot</module> </modules> diff --git a/yard/pom.xml b/yard/pom.xml new file mode 100644 index 0000000000..5799645e5a --- /dev/null +++ b/yard/pom.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + + <parent> + <groupId>org.kie.kogito</groupId> + <artifactId>kogito-build-parent</artifactId> + <version>999-SNAPSHOT</version> + <relativePath>../kogito-build/kogito-build-parent/pom.xml</relativePath> + </parent> + + <artifactId>yard</artifactId> + <packaging>pom</packaging> + + <name>Kogito :: YARD</name> + <description> + A simple way to describe declarative Decisions and Rules in YAML. + </description> + + <properties> + <surefire.forkCount>1</surefire.forkCount> + <alphanetworkCompilerEnabled>false</alphanetworkCompilerEnabled> + </properties> + + <modules> + <module>yard-api</module> + <module>yard-core</module> + </modules> + + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <forkCount>${surefire.forkCount}</forkCount> + <reuseForks>true</reuseForks> + <runOrder>hourly</runOrder> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> + +</project> diff --git a/yard/yard-api/pom.xml b/yard/yard-api/pom.xml new file mode 100644 index 0000000000..cf91d94dd2 --- /dev/null +++ b/yard/yard-api/pom.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + 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. + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.kie.kogito</groupId> + <artifactId>yard</artifactId> + <version>999-SNAPSHOT</version> + </parent> + + <artifactId>yard-api</artifactId> + + <properties> + <java.module.name>org.kie.yard.api</java.module.name> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.kie.kogito</groupId> + <artifactId>kogito-kie-bom</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>org.treblereel.gwt.yaml.mapper</groupId> + <artifactId>common</artifactId> + </dependency> + + <dependency> + <groupId>org.treblereel.gwt.yaml.mapper</groupId> + <artifactId>processor</artifactId> + <scope>provided</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/DecisionLogic.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/DecisionLogic.java new file mode 100644 index 0000000000..52c622d867 --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/DecisionLogic.java @@ -0,0 +1,31 @@ +/* + * 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.api.model; + +import org.treblereel.gwt.yaml.api.annotation.YamlSubtype; +import org.treblereel.gwt.yaml.api.annotation.YamlTypeInfo; + +@YamlTypeInfo( + key = "type", + value = { + @YamlSubtype(alias = "DecisionTable", type = DecisionTable.class), + @YamlSubtype(alias = "LiteralExpression", type = LiteralExpression.class) + }) +public interface DecisionLogic { +} diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/DecisionTable.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/DecisionTable.java new file mode 100644 index 0000000000..b20c96db4b --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/DecisionTable.java @@ -0,0 +1,70 @@ +/* + * 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.api.model; + +import java.util.List; + +import org.treblereel.gwt.yaml.api.annotation.YAMLMapper; +import org.treblereel.gwt.yaml.api.annotation.YamlTypeDeserializer; +import org.treblereel.gwt.yaml.api.annotation.YamlTypeSerializer; + +@YAMLMapper +public class DecisionTable implements DecisionLogic { + private List<String> inputs; + private String hitPolicy = "ANY"; + @Deprecated + private List<String> outputComponents; + @YamlTypeSerializer(RuleDefSerializer.class) + @YamlTypeDeserializer(RuleDefSerializer.class) + private List rules; + + public void setInputs(List<String> inputs) { + this.inputs = inputs; + } + + public void setOutputComponents(List<String> outputComponents) { + this.outputComponents = outputComponents; + } + + public List<String> getInputs() { + return inputs; + } + + @Deprecated + public List<String> getOutputComponents() { + return outputComponents; + } + + public String getHitPolicy() { + return hitPolicy; + } + + public void setHitPolicy(String hitPolicy) { + this.hitPolicy = hitPolicy; + } + + public List getRules() { + return rules; + } + + public void setRules(List rules) { + this.rules = rules; + } + +} diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/Element.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/Element.java new file mode 100644 index 0000000000..910003090c --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/Element.java @@ -0,0 +1,64 @@ +/* + * 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.api.model; + +import java.util.List; + +import org.treblereel.gwt.yaml.api.annotation.YAMLMapper; + +@YAMLMapper +public class Element { + + private String name; + private String type; + private List<String> requirements; + private DecisionLogic logic; + + public void setName(String name) { + this.name = name; + } + + public void setType(String type) { + this.type = type; + } + + public void setRequirements(List<String> requirements) { + this.requirements = requirements; + } + + public void setLogic(DecisionLogic logic) { + this.logic = logic; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } + + public List<String> getRequirements() { + return requirements; + } + + public DecisionLogic getLogic() { + return logic; + } +} diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/InlineRule.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/InlineRule.java new file mode 100644 index 0000000000..330273bc69 --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/InlineRule.java @@ -0,0 +1,45 @@ +/* + * 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.api.model; + +import java.util.List; + +public class InlineRule implements Rule { + + private final int rowNumber; + public List def; + + public InlineRule(int rowNumber, List data) { + this.rowNumber = rowNumber; + this.def = data; + } + + @Override + public int getRowNumber() { + return rowNumber; + } + + public List getDef() { + return def; + } + + public void setDef(List def) { + this.def = def; + } +} diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/Input.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/Input.java new file mode 100644 index 0000000000..8b9862f2c4 --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/Input.java @@ -0,0 +1,43 @@ +/* + * 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.api.model; + +import org.treblereel.gwt.yaml.api.annotation.YAMLMapper; + +@YAMLMapper +public class Input { + private String name; + private String type; + + public void setName(String name) { + this.name = name; + } + + public void setType(String type) { + this.type = type; + } + + public String getName() { + return name; + } + + public String getType() { + return type; + } +} \ No newline at end of file diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/LiteralExpression.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/LiteralExpression.java new file mode 100644 index 0000000000..7a1ce500c7 --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/LiteralExpression.java @@ -0,0 +1,34 @@ +/* + * 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.api.model; + +import org.treblereel.gwt.yaml.api.annotation.YAMLMapper; + +@YAMLMapper +public class LiteralExpression implements DecisionLogic { + private String expression; + + public void setExpression(String expression) { + this.expression = expression; + } + + public String getExpression() { + return expression; + } +} \ No newline at end of file diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/Operators.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/Operators.java new file mode 100644 index 0000000000..56e20b1878 --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/Operators.java @@ -0,0 +1,49 @@ +/* + * 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.api.model; + +import java.util.Objects; + +/** + * Interface instead of enum due to possible custom operators. + */ +public interface Operators { + String NOT_EQUALS = "!="; + String EQUALS = "="; + String GREATER_OR_EQUAL = ">="; + String GREATER_THAN = ">"; + String LESS_OR_EQUAL = "<="; + String LESS_THAN = "<"; + + String[] ALL = { EQUALS, LESS_OR_EQUAL, LESS_THAN, GREATER_OR_EQUAL, GREATER_THAN, NOT_EQUALS }; + + static int compare(final String operator, + final String other) { + return getWeight(operator) - getWeight(other); + } + + static int getWeight(final String operator) { + for (int i = 0; i < ALL.length; i++) { + if (Objects.equals(operator, ALL[i])) { + return i; + } + } + return 0; + } +} diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/Rule.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/Rule.java new file mode 100644 index 0000000000..5dd5d380fb --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/Rule.java @@ -0,0 +1,23 @@ +/* + * 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.api.model; + +public interface Rule { + int getRowNumber(); +} diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/RuleDefSerializer.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/RuleDefSerializer.java new file mode 100644 index 0000000000..0304c2622d --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/RuleDefSerializer.java @@ -0,0 +1,88 @@ +/* + * 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.api.model; + +import java.util.ArrayList; +import java.util.List; + +import org.treblereel.gwt.yaml.api.YAMLDeserializer; +import org.treblereel.gwt.yaml.api.YAMLSerializer; +import org.treblereel.gwt.yaml.api.exception.YAMLDeserializationException; +import org.treblereel.gwt.yaml.api.internal.deser.YAMLDeserializationContext; +import org.treblereel.gwt.yaml.api.internal.ser.YAMLSerializationContext; +import org.treblereel.gwt.yaml.api.node.YamlMapping; +import org.treblereel.gwt.yaml.api.node.YamlNode; +import org.treblereel.gwt.yaml.api.node.YamlSequence; + +public class RuleDefSerializer + implements YAMLSerializer<Object>, YAMLDeserializer<Object> { + + private int rowNumber = 1; + + @Override + public Object deserialize(YamlMapping yamlMapping, + String s, + YAMLDeserializationContext yamlDeserializationContext) throws YAMLDeserializationException { + return deserialize(yamlMapping.getNode(s), yamlDeserializationContext); + } + + @Override + public Object deserialize(YamlNode yamlNode, + YAMLDeserializationContext yamlDeserializationContext) { + if (yamlNode instanceof YamlSequence) { + final List<Comparable> items = getItems(yamlNode); + return new InlineRule(rowNumber++, items); + + } else if (yamlNode instanceof YamlMapping) { + final WhenThenRule whenThenRule = new WhenThenRule(rowNumber++); + final YamlNode when = ((YamlMapping) yamlNode).getNode("when"); + final YamlNode then = ((YamlMapping) yamlNode).getNode("then"); + whenThenRule.setWhen(getItems(when)); + whenThenRule.setThen(then.asScalar().value()); + return whenThenRule; + } + return new IllegalArgumentException("Unknown rule format."); + } + + private List<Comparable> getItems(final YamlNode yamlNode) { + final List<Comparable> result = new ArrayList<>(); + if (yamlNode instanceof YamlSequence) { + ((YamlSequence) yamlNode).iterator().forEachRemaining(x -> { + final Comparable value = (Comparable) x.asScalar().value(); + result.add(value); + }); + } + return result; + } + + @Override + public void serialize(YamlMapping yamlMapping, + String s, + Object objects, + YAMLSerializationContext yamlSerializationContext) { + // Not needed, we never serialize. + } + + @Override + public void serialize(YamlSequence yamlSequence, + Object objects, + YAMLSerializationContext yamlSerializationContext) { + // Not needed, we never serialize. + } +} diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/WhenThenRule.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/WhenThenRule.java new file mode 100644 index 0000000000..bcd9f1aa04 --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/WhenThenRule.java @@ -0,0 +1,53 @@ +/* + * 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.api.model; + +import java.util.List; + +public class WhenThenRule implements Rule { + + private final int rowNumber; + private List when; + private Object then; + + public WhenThenRule(int rowNumber) { + this.rowNumber = rowNumber; + } + + @Override + public int getRowNumber() { + return rowNumber; + } + + public List getWhen() { + return when; + } + + public Object getThen() { + return then; + } + + public void setWhen(List when) { + this.when = when; + } + + public void setThen(Object then) { + this.then = then; + } +} diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/WhenThenRuleThenSerializer.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/WhenThenRuleThenSerializer.java new file mode 100644 index 0000000000..2ea51743fb --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/WhenThenRuleThenSerializer.java @@ -0,0 +1,56 @@ +/* + * 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.api.model; + +import java.util.Locale; + +import org.treblereel.gwt.yaml.api.YAMLDeserializer; +import org.treblereel.gwt.yaml.api.YAMLSerializer; +import org.treblereel.gwt.yaml.api.exception.YAMLDeserializationException; +import org.treblereel.gwt.yaml.api.internal.deser.YAMLDeserializationContext; +import org.treblereel.gwt.yaml.api.internal.ser.YAMLSerializationContext; +import org.treblereel.gwt.yaml.api.node.YamlMapping; +import org.treblereel.gwt.yaml.api.node.YamlNode; +import org.treblereel.gwt.yaml.api.node.YamlSequence; + +public class WhenThenRuleThenSerializer + implements YAMLSerializer<Object>, YAMLDeserializer<Object> { + @Override + public Object deserialize(YamlMapping yamlMapping, String key, YAMLDeserializationContext yamlDeserializationContext) throws YAMLDeserializationException { + return deserialize(yamlMapping.getNode(key), yamlDeserializationContext); + } + + @Override + public Object deserialize(YamlNode yamlNode, YAMLDeserializationContext yamlDeserializationContext) { + if (yamlNode == null || yamlNode.isEmpty()) { + return null; + } + return yamlNode.<String> asScalar().value().toLowerCase(Locale.ROOT); + } + + @Override + public void serialize(YamlMapping yamlMapping, String s, Object o, YAMLSerializationContext yamlSerializationContext) { + // Not needed, we never serialize. + } + + @Override + public void serialize(YamlSequence yamlSequence, Object o, YAMLSerializationContext yamlSerializationContext) { + // Not needed, we never serialize. + } +} diff --git a/yard/yard-api/src/main/java/org/kie/yard/api/model/YaRD.java b/yard/yard-api/src/main/java/org/kie/yard/api/model/YaRD.java new file mode 100644 index 0000000000..3d2d4fe1f7 --- /dev/null +++ b/yard/yard-api/src/main/java/org/kie/yard/api/model/YaRD.java @@ -0,0 +1,82 @@ +/* + * 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.api.model; + +import java.util.List; + +import org.treblereel.gwt.yaml.api.annotation.YAMLMapper; + +@YAMLMapper +public class YaRD { + + private String specVersion = "alpha"; + private String kind = "YaRD"; + private String name; + private String expressionLang; + private List<Input> inputs; + private List<Element> elements; + + public void setInputs(List<Input> inputs) { + this.inputs = inputs; + } + + public void setElements(List<Element> elements) { + this.elements = elements; + } + + public String getName() { + return name; + } + + public String getExpressionLang() { + return expressionLang; + } + + public void setExpressionLang(String expressionLang) { + this.expressionLang = expressionLang; + } + + public String getKind() { + return kind; + } + + public void setKind(String kind) { + this.kind = kind; + } + + public String getSpecVersion() { + return specVersion; + } + + public void setSpecVersion(String specVersion) { + this.specVersion = specVersion; + } + + public void setName(String name) { + this.name = name; + } + + public List<Input> getInputs() { + return inputs; + } + + public List<Element> getElements() { + return elements; + } +} diff --git a/yard/yard-api/src/main/resources/YaRD-schema.json b/yard/yard-api/src/main/resources/YaRD-schema.json new file mode 100644 index 0000000000..ef5cd6d91c --- /dev/null +++ b/yard/yard-api/src/main/resources/YaRD-schema.json @@ -0,0 +1,157 @@ +{ + "$schema" : "http://json-schema.org/draft-07/schema#", + "definitions" : { + "DecisionTable-1" : { + "type" : "object", + "properties" : { + "inputs" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "hitPolicy" : { + "type" : "string", + "default" : "ANY" + }, + "rules" : { + "type" : "array", + "items" : { + "anyOf" : [ { + "$ref" : "#/definitions/InlineRule" + }, { + "$ref" : "#/definitions/WhenThenRule" + } ] + } + }, + "outputComponents" : { + "description" : "deprecated", + "type" : "array", + "items" : { + "type" : "string" + } + } + }, + "required" : [ "inputs", "rules" ] + }, + "DecisionTable-2" : { + "allOf" : [ { + "$ref" : "#/definitions/DecisionTable-1" + }, { + "type" : "object", + "properties" : { + "type" : { + "const" : "DecisionTable" + } + }, + "required" : [ "type" ] + } ] + }, + "Element" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + }, + "requirements" : { + "type" : "array", + "items" : { + "type" : "string" + } + }, + "logic" : { + "anyOf" : [ { + "$ref" : "#/definitions/DecisionTable-2" + }, { + "$ref" : "#/definitions/LiteralExpression-2" + } ] + } + }, + "required" : [ "name", "type", "logic" ] + }, + "InlineRule" : { + "type" : "array", + "items" : { } + }, + "Input" : { + "type" : "object", + "properties" : { + "name" : { + "type" : "string" + }, + "type" : { + "type" : "string" + } + }, + "required" : [ "name", "type" ] + }, + "LiteralExpression-1" : { + "type" : "object", + "properties" : { + "expression" : { + "type" : "string" + } + }, + "required" : [ "expression" ] + }, + "LiteralExpression-2" : { + "allOf" : [ { + "$ref" : "#/definitions/LiteralExpression-1" + }, { + "type" : "object", + "properties" : { + "type" : { + "const" : "LiteralExpression" + } + }, + "required" : [ "type" ] + } ] + }, + "WhenThenRule" : { + "type" : "object", + "properties" : { + "when" : { + "type" : "array", + "items" : { } + }, + "then" : { } + }, + "required" : [ "when", "then" ] + } + }, + "type" : "object", + "properties" : { + "specVersion" : { + "type" : "string", + "default" : "alpha" + }, + "kind" : { + "type" : "string", + "default" : "YaRD" + }, + "name" : { + "type" : "string", + "description" : "when not provided explicitly, implementation will attempt to deduce the name from the runtime context; if a name cannot be deduced it is an error." + }, + "expressionLang" : { + "type" : "string", + "description" : "An implementation is free to assume a default expressionLang if not explicitly set. For the purpose of a User sharing a YaRD definition, is best to valorise this field explicit." + }, + "inputs" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/Input" + } + }, + "elements" : { + "type" : "array", + "items" : { + "$ref" : "#/definitions/Element" + } + } + }, + "required" : [ "inputs", "elements" ] +} \ No newline at end of file diff --git a/yard/yard-core/pom.xml b/yard/yard-core/pom.xml new file mode 100644 index 0000000000..009d9e89a1 --- /dev/null +++ b/yard/yard-core/pom.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.kie.kogito</groupId> + <artifactId>yard</artifactId> + <version>999-SNAPSHOT</version> + </parent> + + <artifactId>yard-core</artifactId> + + <properties> + <java.module.name>org.kie.yard.core</java.module.name> + </properties> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.kie.kogito</groupId> + <artifactId>kogito-kie-bom</artifactId> + <version>${project.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + + <dependencies> + <dependency> + <groupId>org.kie.kogito</groupId> + <artifactId>yard-api</artifactId> + </dependency> + <dependency> + <groupId>org.drools</groupId> + <artifactId>drools-canonical-model</artifactId> + </dependency> + <dependency> + <groupId>org.drools</groupId> + <artifactId>drools-ruleunits-api</artifactId> + </dependency> + <dependency> + <groupId>org.drools</groupId> + <artifactId>drools-ruleunits-dsl</artifactId> + </dependency> + <dependency> + <groupId>org.treblereel.gwt.yaml.mapper</groupId> + <artifactId>common</artifactId> + </dependency> + + <dependency> + <groupId>ch.obermuhlner</groupId> + <artifactId>jshell-scriptengine</artifactId> + </dependency> + <dependency> + <groupId>com.fasterxml.jackson.dataformat</groupId> + <artifactId>jackson-dataformat-yaml</artifactId> + </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-engine</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <scope>test</scope> + </dependency> + </dependencies> +</project> \ No newline at end of file diff --git a/yard/yard-core/src/main/java/org/kie/yard/core/DTableUnitBuilder.java b/yard/yard-core/src/main/java/org/kie/yard/core/DTableUnitBuilder.java new file mode 100644 index 0000000000..9036b66310 --- /dev/null +++ b/yard/yard-core/src/main/java/org/kie/yard/core/DTableUnitBuilder.java @@ -0,0 +1,195 @@ +/* + * 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 java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.drools.model.Index; +import org.drools.ruleunits.api.SingletonStore; +import org.drools.ruleunits.dsl.SyntheticRuleUnit; +import org.drools.ruleunits.dsl.SyntheticRuleUnitBuilder; +import org.kie.yard.api.model.InlineRule; +import org.kie.yard.api.model.Rule; +import org.kie.yard.api.model.WhenThenRule; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.json.JsonMapper; + +public class DTableUnitBuilder { + private final YaRDDefinitions definitions; + private final String name; + private final OnExecute executionAction; + private final List<Rule> rules; + private final List<String> inputs; + private JsonMapper jsonMapper = JsonMapper.builder().build(); + + public DTableUnitBuilder(final YaRDDefinitions definitions, + final String name, + final org.kie.yard.api.model.DecisionTable dtableDefinition) { + this.inputs = dtableDefinition.getInputs(); + if (inputs.isEmpty()) { + throw new IllegalStateException("Empty decision table?"); + } + this.definitions = definitions; + this.name = name; + this.rules = dtableDefinition.getRules(); + this.executionAction = getExecutionAction(dtableDefinition.getHitPolicy()); + } + + public SyntheticRuleUnit build() { + + final SyntheticRuleUnitBuilder unit = SyntheticRuleUnitBuilder.build(name); // TODO ensure unique key + for (Map.Entry<String, SingletonStore<Object>> e : definitions.inputs().entrySet()) { + unit.registerDataSource(e.getKey(), e.getValue(), Object.class); + } + final StoreHandle<Object> result = StoreHandle.empty(Object.class); + unit.registerGlobal(name, result); + definitions.outputs().put(name, result); + return unit.defineRules(rulesFactory -> { + for (Rule ruleDefinition : rules) { + var rule = rulesFactory.rule(); + for (int idx = 0; idx < inputs.size(); idx++) { + final RuleCell ruleCell = parseGenericRuleCell(ruleDefinition, idx); + if (ruleCell.value != null) { + final SingletonStore<Object> dataSource = definitions.inputs().get(inputs.get(idx)); + + rule.on(dataSource).filter(ruleCell.idxtype, ruleCell.value); + } + } + rule.execute(result, storeHandle -> executionAction.onExecute(ruleDefinition, storeHandle)); + } + }); + } + + private OnExecute getExecutionAction(String hitPolicy) { + if (hitPolicy == null || Objects.equals("ANY", hitPolicy)) { + return (ruleDefinition, storeHandle) -> { + final RuleCell rc = parseGenericRuleThen(ruleDefinition); + storeHandle.set(rc.value); + }; + } else if (Objects.equals("FIRST", hitPolicy)) { + return (ruleDefinition, storeHandle) -> { + if (!storeHandle.isValuePresent()) { + final RuleCell rc = parseGenericRuleThen(ruleDefinition); + storeHandle.set(rc.value); + } + }; + } else if (Objects.equals("COLLECT", hitPolicy)) { + return (ruleDefinition, storeHandle) -> { + if (!storeHandle.isValuePresent()) { + storeHandle.set(new ArrayList<>()); + } + final RuleCell rc = parseGenericRuleThen(ruleDefinition); + + if (storeHandle.get() instanceof List list) { + list.add(resolveValue(rc)); + } + + }; + } else { + throw new UnsupportedOperationException("Not implemented "); + } + } + + private Object resolveValue(final RuleCell rc) { + try { + if (rc.value instanceof String text) { + return jsonMapper.readValue(text, Map.class); + } + } catch (JsonProcessingException ignored) { + } + return rc.value; + } + + private RuleCell parseGenericRuleThen(Rule rule) { + if (rule instanceof InlineRule inlineRule) { + return parseRuleCell(inlineRule.getDef().get(inlineRule.getDef().size() - 1)); + } else if (rule instanceof WhenThenRule whenThenRule) { + return parseRuleCell((whenThenRule).getThen()); + } else { + throw new IllegalStateException("Unknown or unmanaged rule instance type?"); + } + } + + private RuleCell parseGenericRuleCell(Rule rule, int i) { + if (rule instanceof InlineRule inlineRule) { + return parseRuleCell((inlineRule).getDef().get(i)); + } else if (rule instanceof WhenThenRule whenThenRule) { + return parseRuleCell((whenThenRule).getWhen().get(i)); + } else { + throw new IllegalStateException("Unknown or unmanaged rule instance type?"); + } + } + + private RuleCell parseRuleCell(Object object) { + if (object instanceof Boolean) { + return new RuleCell(Index.ConstraintType.EQUAL, object); + } else if (object instanceof Number) { + return new RuleCell(Index.ConstraintType.EQUAL, object); + } else if (object instanceof String valueString) { + if (valueString.startsWith("<=")) { // pay attention to ordering when not using a parser like in this case. + return new RuleCell(Index.ConstraintType.LESS_OR_EQUAL, parseConstrainedCellString(valueString.substring(2))); + } else if (valueString.startsWith(">=")) { + return new RuleCell(Index.ConstraintType.GREATER_OR_EQUAL, parseConstrainedCellString(valueString.substring(2))); + } else if (valueString.startsWith("<")) { + return new RuleCell(Index.ConstraintType.LESS_THAN, parseConstrainedCellString(valueString.substring(1))); + } else if (valueString.startsWith(">")) { + return new RuleCell(Index.ConstraintType.GREATER_THAN, parseConstrainedCellString(valueString.substring(1))); + } else { + return new RuleCell(Index.ConstraintType.EQUAL, parseConstrainedCellString(valueString)); + } + } else { + throw new IllegalStateException("Unmanaged case, please report!"); + } + } + + private Object parseConstrainedCellString(String substring) { + if (Objects.equals("true", substring.trim().toLowerCase())) { + return true; + } + if (Objects.equals("false", substring.trim().toLowerCase())) { + return false; + } + try { + return Integer.parseInt(substring.trim()); + } catch (Exception e) { + + } + try { + return Long.parseLong(substring.trim()); + } catch (Exception e) { + + } + if (Objects.equals("-", substring.trim())) { + return null; // TODO while this was the way in DMN, should we go with the same now. What if you need to use - the character? + } + return substring; + } + + private interface OnExecute { + void onExecute(Rule ruleDefinition, StoreHandle<Object> storeHandle); + } + + public static record RuleCell(Index.ConstraintType idxtype, Object value) { + } + +} diff --git a/yard/yard-core/src/main/java/org/kie/yard/core/Firable.java b/yard/yard-core/src/main/java/org/kie/yard/core/Firable.java new file mode 100644 index 0000000000..52b4da9940 --- /dev/null +++ b/yard/yard-core/src/main/java/org/kie/yard/core/Firable.java @@ -0,0 +1,27 @@ +/* + * 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 java.util.Map; + +public interface Firable { + + int fire(Map<String, Object> context, YaRDDefinitions units); + +} \ No newline at end of file diff --git a/yard/yard-core/src/main/java/org/kie/yard/core/LiteralExpressionBuilder.java b/yard/yard-core/src/main/java/org/kie/yard/core/LiteralExpressionBuilder.java new file mode 100644 index 0000000000..508a23b041 --- /dev/null +++ b/yard/yard-core/src/main/java/org/kie/yard/core/LiteralExpressionBuilder.java @@ -0,0 +1,39 @@ +/* + * 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.kie.yard.api.model.LiteralExpression; + +public class LiteralExpressionBuilder { + private final YaRDDefinitions definitions; + private final String name; + private final LiteralExpression decisionLogic; + + public LiteralExpressionBuilder(YaRDDefinitions definitions, String name, LiteralExpression decisionLogic) { + this.definitions = definitions; + this.name = name; + this.decisionLogic = decisionLogic; + } + + public Firable build() { + String expr = decisionLogic.getExpression(); + definitions.outputs().put(name, StoreHandle.empty(Object.class)); + return new LiteralExpressionInterpreter(name, QuotedExprParsed.from(expr)); + } +} diff --git a/yard/yard-core/src/main/java/org/kie/yard/core/LiteralExpressionInterpreter.java b/yard/yard-core/src/main/java/org/kie/yard/core/LiteralExpressionInterpreter.java new file mode 100644 index 0000000000..f973ea6e0b --- /dev/null +++ b/yard/yard-core/src/main/java/org/kie/yard/core/LiteralExpressionInterpreter.java @@ -0,0 +1,73 @@ +/* + * 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 java.util.Map; +import java.util.Map.Entry; + +import javax.script.Bindings; +import javax.script.Compilable; +import javax.script.CompiledScript; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +public class LiteralExpressionInterpreter implements Firable { + private final String name; + private final QuotedExprParsed quoted; + private final ScriptEngine engine; + private final CompiledScript compiledScript; + + public LiteralExpressionInterpreter(String nameString, QuotedExprParsed quotedExprParsed) { + this.name = nameString; + this.quoted = quotedExprParsed; + try { + ScriptEngineManager manager = new ScriptEngineManager(); + engine = manager.getEngineByName("jshell"); + Compilable compiler = (Compilable) engine; + compiledScript = compiler.compile(quoted.getRewrittenExpression()); + } catch (Exception e) { + throw new IllegalArgumentException("parse error", e); + } + } + + @Override + public int fire(Map<String, Object> context, YaRDDefinitions units) { + Bindings bindings = engine.createBindings(); + // deliberately escape all symbols; a normal symbol will + // never be in the detected-by-unquoting set, so this + // set can't be used to selectively put in scope + for (Entry<String, Object> inKV : context.entrySet()) { + bindings.put(QuotedExprParsed.escapeIdentifier(inKV.getKey()), inKV.getValue()); + } + for (Entry<String, StoreHandle<Object>> outKV : units.outputs().entrySet()) { + if (!outKV.getValue().isValuePresent()) { + continue; + } + bindings.put(QuotedExprParsed.escapeIdentifier(outKV.getKey()), outKV.getValue().get()); + } + try { + var result = compiledScript.eval(bindings); + units.outputs().get(name).set(result); + return 1; + } catch (ScriptException e) { + throw new RuntimeException("interpretation failed at runtime", e); + } + } +} diff --git a/yard/yard-core/src/main/java/org/kie/yard/core/QuotedExprParsed.java b/yard/yard-core/src/main/java/org/kie/yard/core/QuotedExprParsed.java new file mode 100644 index 0000000000..593190ae69 --- /dev/null +++ b/yard/yard-core/src/main/java/org/kie/yard/core/QuotedExprParsed.java @@ -0,0 +1,115 @@ +/* + * 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 java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.PrimitiveIterator.OfInt; +import java.util.Set; +import java.util.stream.Collectors; + +import javax.lang.model.SourceVersion; + +public class QuotedExprParsed { + private static final int ESCAPE_CHAR = "`".codePointAt(0); + + private final Set<String> usedSymbols; + private final String rewrittenExpression; + + private QuotedExprParsed(List<String> usedSymbols, String rewrittenExpression) { + this.usedSymbols = usedSymbols.stream().collect(Collectors.toUnmodifiableSet()); + this.rewrittenExpression = rewrittenExpression; + } + + public String getRewrittenExpression() { + return rewrittenExpression; + } + + public Collection<String> getUsedSymbols() { + return usedSymbols; + } + + public static QuotedExprParsed from(String expr) { + StringBuilder rewittenExpr = new StringBuilder(); + StringBuilder quotedBuffer = new StringBuilder(); + // TODO consider including scope so to check the symbols are present + List<String> usedSymbols = new ArrayList<>(); + OfInt it = expr.codePoints().iterator(); + State state = State.UNQUOTED; + while (it.hasNext()) { + int c = it.nextInt(); + if (c == ESCAPE_CHAR) { + switch (state) { + case UNQUOTED: + state = State.QUOTED; + break; + case QUOTED: + state = State.UNQUOTED; + var originalSymbol = quotedBuffer.toString(); + usedSymbols.add(originalSymbol); + var escaped = escapeIdentifier(originalSymbol); + rewittenExpr.append(escaped); + quotedBuffer = new StringBuilder(); + break; + default: + throw new IllegalStateException(); + } + } else { + switch (state) { + case UNQUOTED: + rewittenExpr.appendCodePoint(c); + break; + case QUOTED: + quotedBuffer.appendCodePoint(c); + break; + default: + throw new IllegalStateException(); + } + } + } + return new QuotedExprParsed(usedSymbols, rewittenExpr.toString()); + } + + private static enum State { + UNQUOTED, + QUOTED + } + + public static String escapeIdentifier(String partOfIdentifier) { + String id = partOfIdentifier; + if (!Character.isJavaIdentifierStart(id.charAt(0))) { + id = "_" + id; + } + id = id.replaceAll("_", "__"); + if (SourceVersion.isKeyword(id)) { + id = "_" + id; + } + StringBuilder result = new StringBuilder(); + char[] cs = id.toCharArray(); + for (char c : cs) { + if (Character.isJavaIdentifierPart(c)) { + result.append(c); + } else { + result.append("_" + Integer.valueOf(c)); + } + } + return result.toString(); + } +} diff --git a/yard/yard-core/src/main/java/org/kie/yard/core/StoreHandle.java b/yard/yard-core/src/main/java/org/kie/yard/core/StoreHandle.java new file mode 100644 index 0000000000..ece69d7b73 --- /dev/null +++ b/yard/yard-core/src/main/java/org/kie/yard/core/StoreHandle.java @@ -0,0 +1,69 @@ +/* + * 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.drools.ruleunits.api.DataHandle; +import org.drools.ruleunits.api.DataSource; +import org.drools.ruleunits.api.SingletonStore; + +public class StoreHandle<T> { + private SingletonStore<T> wrapped; + private DataHandle datahandle; + + private StoreHandle(T value) { + wrapped = DataSource.createSingleton(); + datahandle = wrapped.set(value); + } + + private StoreHandle() { + wrapped = DataSource.createSingleton(); + datahandle = null; + } + + public static <T> StoreHandle<T> of(T value) { + return new StoreHandle<>(value); + } + + public static <T> StoreHandle<T> empty(Class<T> type) { + return new StoreHandle<>(); + } + + public DataHandle set(T value) { + datahandle = wrapped.set(value); + return datahandle; + } + + public void clear() { + datahandle = null; + wrapped.clear(); + } + + public boolean isValuePresent() { + return !(datahandle == null); + } + + public T get() { + if (datahandle == null) { + throw new IllegalStateException("was never set"); + } + @SuppressWarnings("unchecked") + T result = (T) datahandle.getObject(); + return result; + } +} diff --git a/yard/yard-core/src/main/java/org/kie/yard/core/SyntheticRuleUnitWrapper.java b/yard/yard-core/src/main/java/org/kie/yard/core/SyntheticRuleUnitWrapper.java new file mode 100644 index 0000000000..60f5e39df6 --- /dev/null +++ b/yard/yard-core/src/main/java/org/kie/yard/core/SyntheticRuleUnitWrapper.java @@ -0,0 +1,39 @@ +/* + * 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 java.util.Map; + +import org.drools.ruleunits.api.RuleUnitInstance; +import org.drools.ruleunits.api.RuleUnitProvider; +import org.drools.ruleunits.dsl.SyntheticRuleUnit; + +public class SyntheticRuleUnitWrapper implements Firable { + private final SyntheticRuleUnit wrapped; + + public SyntheticRuleUnitWrapper(SyntheticRuleUnit wrapped) { + this.wrapped = wrapped; + } + + @Override + public int fire(Map<String, Object> context, YaRDDefinitions units) { + RuleUnitInstance<SyntheticRuleUnit> unitInstance = RuleUnitProvider.get().createRuleUnitInstance(wrapped); + return unitInstance.fire(); + } +} diff --git a/yard/yard-core/src/main/java/org/kie/yard/core/YaRDDefinitions.java b/yard/yard-core/src/main/java/org/kie/yard/core/YaRDDefinitions.java new file mode 100644 index 0000000000..f683affa88 --- /dev/null +++ b/yard/yard-core/src/main/java/org/kie/yard/core/YaRDDefinitions.java @@ -0,0 +1,55 @@ +/* + * 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 java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.drools.ruleunits.api.SingletonStore; + +public record YaRDDefinitions(Map<String, SingletonStore<Object>> inputs, List<Firable> units, Map<String, StoreHandle<Object>> outputs) { + + public Map<String, Object> evaluate(Map<String, Object> context) { + Map<String, Object> results = new LinkedHashMap<>(context); + for (String inputKey : inputs.keySet()) { + if (!context.containsKey(inputKey)) { + throw new IllegalArgumentException("Missing input key in context: " + inputKey); + } + Object inputValue = context.get(inputKey); + inputs.get(inputKey).set(inputValue); + } + context.put("gg", 10); + for (Firable unit : units) { + unit.fire(context, this); + } + for (Entry<String, StoreHandle<Object>> outputSets : outputs.entrySet()) { + results.put(outputSets.getKey(), outputSets.getValue().get()); + } + reset(); + return results; + } + + // TODO to be revised for better concern separation + private void reset() { + inputs.forEach((k, v) -> v.clear()); + outputs.forEach((k, v) -> v.clear()); + } +} diff --git a/yard/yard-core/src/main/java/org/kie/yard/core/YaRDParser.java b/yard/yard-core/src/main/java/org/kie/yard/core/YaRDParser.java new file mode 100644 index 0000000000..0e6ca66390 --- /dev/null +++ b/yard/yard-core/src/main/java/org/kie/yard/core/YaRDParser.java @@ -0,0 +1,85 @@ +/* + * 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 java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.drools.ruleunits.api.DataSource; +import org.kie.yard.api.model.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class YaRDParser { + private static final Logger LOG = LoggerFactory.getLogger(YaRDParser.class); + private final YaRDDefinitions definitions = new YaRDDefinitions(new HashMap<>(), new ArrayList<>(), new HashMap<>()); + + public YaRDParser() { + } + + public YaRDDefinitions parse(String yaml) throws Exception { + final YaRD sd = new YaRD_YamlMapperImpl().read(yaml); + if (!sd.getExpressionLang().equals("jshell")) { + // TODO default to something, but what? + throw new IllegalArgumentException("Only `jshell` is supported as an expression language"); + } + appendInputs(sd.getInputs()); + appendUnits(sd.getElements()); + return definitions; + } + + private void appendUnits(List<Element> list) { + for (Element hi : list) { + String nameString = hi.getName(); + LOG.debug("parsing {}", nameString); + Firable decisionLogic = createDecisionLogic(nameString, hi.getLogic()); + definitions.units().add(decisionLogic); + } + } + + private Firable createDecisionLogic(String nameString, DecisionLogic decisionLogic) { + if (decisionLogic instanceof org.kie.yard.api.model.DecisionTable decisionTable) { + return new SyntheticRuleUnitWrapper(new DTableUnitBuilder(definitions, nameString, decisionTable).build()); + } else if (decisionLogic instanceof org.kie.yard.api.model.LiteralExpression literalExpression) { + return new LiteralExpressionBuilder(definitions, nameString, literalExpression).build(); + } else { + throw new UnsupportedOperationException("Not implemented."); + } + } + + private void appendInputs(List<Input> list) { + for (Input hi : list) { + String nameString = hi.getName(); + @SuppressWarnings("unused") + Class<?> typeRef = processType(hi.getType()); + definitions.inputs().put(nameString, DataSource.createSingleton()); + } + } + + private Class<?> processType(String string) { + switch (string) { + case "string": + case "number": + case "boolean": + default: + return Object.class; // TODO currently does not resolve external JSON Schemas + } + } +} diff --git a/yard/yard-core/src/test/java/org/kie/yard/core/DomesticPackagePricesTest.java b/yard/yard-core/src/test/java/org/kie/yard/core/DomesticPackagePricesTest.java new file mode 100644 index 0000000000..5cc3d21f82 --- /dev/null +++ b/yard/yard-core/src/test/java/org/kie/yard/core/DomesticPackagePricesTest.java @@ -0,0 +1,59 @@ +/* + * 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 java.util.Map; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DomesticPackagePricesTest + extends TestBase { + private static final String FILE_NAME = "/domestic-package-prices.yml"; + + @Test + public void testMPackage() throws Exception { + final String CTX = """ + { + "Height":10, + "Width":10, + "Length": 10, + "Weight":10 + } + """; + Map<String, Object> outputJSONasMap = evaluate(CTX, FILE_NAME); + assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Package", "{ \"Size\": \"M\", \"Cost\": 6.90 }"); + } + + @Test + public void testLPackage() throws Exception { + final String CTX = """ + { + "Height":12, + "Width":10, + "Length": 10, + "Weight":10 + } + """; + Map<String, Object> outputJSONasMap = evaluate(CTX, FILE_NAME); + assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Package", "{ \"Size\": \"L\", \"Cost\": 8.90 }"); + } + +} diff --git a/yard/yard-core/src/test/java/org/kie/yard/core/ExtraCostsTest.java b/yard/yard-core/src/test/java/org/kie/yard/core/ExtraCostsTest.java new file mode 100644 index 0000000000..55a02971ae --- /dev/null +++ b/yard/yard-core/src/test/java/org/kie/yard/core/ExtraCostsTest.java @@ -0,0 +1,45 @@ +/* + * 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 java.util.Map; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ExtraCostsTest + extends TestBase { + private static final String FILE_NAME = "/extra-costs.yml"; + + @Test + public void testMPackage() throws Exception { + final String CTX = """ + { + "Fragile":true, + "Package Tracking":true, + "Insurance":true, + "Package Type":"M" + } + """; + Map<String, Object> outputJSONasMap = evaluate(CTX, FILE_NAME); + assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Total cost of premiums", 40); + } + +} diff --git a/yard/yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java b/yard/yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java new file mode 100644 index 0000000000..22db5bd59c --- /dev/null +++ b/yard/yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java @@ -0,0 +1,58 @@ +/* + * 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 java.util.Map; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class InsuranceBasePriceTest + extends TestBase { + + private static final String FILE_NAME = "/insurance-base-price.yml"; + + @Test + public void testScenario1() throws Exception { + final String CTX = """ + { + "Age": 47, + "Previous incidents?": false + } + """; + Map<String, Object> outputJSONasMap = evaluate(CTX, FILE_NAME); + assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Base price", 500); + assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Downpayment", 50.0); + } + + @Test + public void testScenario2() throws Exception { + final String CTX = """ + { + "Age": 19, + "Previous incidents?": true + } + """; + Map<String, Object> outputJSONasMap = evaluate(CTX, FILE_NAME); + assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Base price", 1000); + assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Downpayment", 70.0); + } + +} diff --git a/yard/yard-core/src/test/java/org/kie/yard/core/TestBase.java b/yard/yard-core/src/test/java/org/kie/yard/core/TestBase.java new file mode 100644 index 0000000000..ce055e1517 --- /dev/null +++ b/yard/yard-core/src/test/java/org/kie/yard/core/TestBase.java @@ -0,0 +1,35 @@ +package org.kie.yard.core; + +import java.util.Map; + +import org.drools.util.IoUtils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.json.JsonMapper; + +public class TestBase { + + private JsonMapper jsonMapper = JsonMapper.builder().build(); + + protected Map<String, Object> evaluate(String jsonInputCxt, String file) throws Exception { + String yamlDecision = new String(IoUtils.readBytesFromInputStream(this.getClass().getResourceAsStream(file), true)); + + YaRDParser parser = new YaRDParser(); + YaRDDefinitions units = parser.parse(yamlDecision); + + Map<String, Object> inputContext = readJSON(jsonInputCxt); + + Map<String, Object> tempOutCtx = units.evaluate(inputContext); + final String OUTPUT_JSON = jsonMapper.writerWithDefaultPrettyPrinter().writeValueAsString(tempOutCtx); + Map<String, Object> outputJSONasMap = readJSON(OUTPUT_JSON); + + return outputJSONasMap; + } + + @SuppressWarnings("unchecked") + private Map<String, Object> readJSON(final String CONTEXT) throws JsonProcessingException, JsonMappingException { + return jsonMapper.readValue(CONTEXT, Map.class); + } + +} diff --git a/yard/yard-core/src/test/resources/domestic-package-prices.yml b/yard/yard-core/src/test/resources/domestic-package-prices.yml new file mode 100644 index 0000000000..4640a5586a --- /dev/null +++ b/yard/yard-core/src/test/resources/domestic-package-prices.yml @@ -0,0 +1,30 @@ +specVersion: alpha +kind: YaRD +name: "Traffic Violation" +expressionLang: jshell +inputs: + - name: "Length" + type: integer + - name: "Width" + type: number + - name: "Height" + type: number + - name: "Weight" + type: number +elements: + - name: Package + type: Decision + logic: + type: DecisionTable + # First matching result will be picked + hitPolicy: FIRST + inputs: ["Height", "Width", "Length", "Weight"] + rules: + - when: ['<= 3', '<= 25','<= 35', '<= 2'] + then: '{ "Size": "S", "Cost": 5.90 }' + - when: ['<= 11','<= 32','<= 42', '<=25'] + then: '{ "Size": "M", "Cost": 6.90 }' + - when: ['<= 19', '<= 36', '<= 60', '<= 25'] + then: '{ "Size": "L", "Cost": 8.90 }' + - when: ['<= 37', '<= 36', '<= 60', '<= 25'] + then: '{ "Size": "XL", "Cost": 10.90}' \ No newline at end of file diff --git a/yard/yard-core/src/test/resources/extra-costs.yml b/yard/yard-core/src/test/resources/extra-costs.yml new file mode 100644 index 0000000000..71bf208654 --- /dev/null +++ b/yard/yard-core/src/test/resources/extra-costs.yml @@ -0,0 +1,47 @@ +specVersion: alpha +kind: YaRD +name: "Traffic Violation" +expressionLang: jshell +inputs: + - name: Fragile + type: boolean + - name: Package Tracking + type: boolean + - name: Insurance + type: boolean + - name: Package Type + type: string +elements: + - name: Selected premiums + type: Decision + logic: + type: DecisionTable + # Collect all costs + hitPolicy: COLLECT + inputs: [Package Type, Fragile, Package Tracking, Insurance] + rules: + # Insurance for all packages, based on size and if the content is fragile + - when: [ 'S', true, "-", true] + then: '{ "Name": "Fragile insurance cost", "Price": 10}' + - when: [ 'M', true, "-", true] + then: '{ "Name": "Fragile insurance cost", "Price": 20}' + - when: [ 'L', true, "-", true] + then: '{ "Name": "Fragile insurance cost", "Price": 30}' + - when: [ 'XL', true, "-", true] + then: '{ "Name": "Fragile insurance cost", "Price": 40}' + - when: [ "-", false, "-", true] + then: '{ "Name": "Regular insurance cost", "Price": 5}' + # Tracking cost is same for all sizes + - when: ["-", "-", true, "-"] + then: '{ "Name": "Tracking cost", "Price": 5}' + # Fragile package extra care cost + - when: ["-", true, "-", "-"] + then: '{ "Name": "Fragile package shipping cost", "Price": 15}' + - name: "Total cost of premiums" + type: Decision + logic: + type: LiteralExpression + expression: | + ((java.util.List<java.util.Map<String,Integer>>)`Selected premiums`).stream().map(m -> m.get("Price")).mapToInt(Integer::valueOf).sum(); +# Feels filthy compared to FEEL below +# expression: 'sum( for item in Selected premiums return item.Price )' diff --git a/yard/yard-core/src/test/resources/insurance-base-price.yml b/yard/yard-core/src/test/resources/insurance-base-price.yml new file mode 100644 index 0000000000..be89f4477e --- /dev/null +++ b/yard/yard-core/src/test/resources/insurance-base-price.yml @@ -0,0 +1,30 @@ +specVersion: alpha +kind: YaRD +name: 'BasePrice' +expressionLang: jshell +inputs: +- name: 'Age' + type: number +- name: 'Previous incidents?' + type: boolean +elements: +- name: 'Base price' + type: Decision + logic: + type: DecisionTable + inputs: ['Age', 'Previous incidents?'] + rules: + - when: ['<21', false] + then: 800 + - when: ['<21', true] + then: 1000 + - when: ['>=21', false] + then: 500 + - when: ['>=21', true] + then: 600 +- name: 'Downpayment' + type: Decision + logic: + type: LiteralExpression + expression: | + Math.max(`Base price` * 0.07, 50) diff --git a/yard/yard-core/src/test/resources/logback-test.xml b/yard/yard-core/src/test/resources/logback-test.xml new file mode 100644 index 0000000000..1bdd160790 --- /dev/null +++ b/yard/yard-core/src/test/resources/logback-test.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<configuration> + + <appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender"> + <encoder> + <pattern>%date{HH:mm:ss.SSS} [%thread] %-5level %class{36}.%method:%line - %msg%n</pattern> + </encoder> + </appender> + + <logger name="org.drools" level="info"/> + <logger name="org.kie" level="info"/> + <logger name="org.kie.yard" level="info"/> + + <root level="warn"> + <appender-ref ref="consoleAppender" /> + </root> + +</configuration> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
