diff --git a/yard-api/src/main/java/org/kie/yard/api/model/Accumulator.java b/yard-api/src/main/java/org/kie/yard/api/model/Accumulator.java new file mode 100644 index 0000000..a794f45 --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/Accumulator.java @@ -0,0 +1,51 @@ +/* + * 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 class Accumulator { + private String function; + + private String parameter; + + private String as; + + public String getFunction() { + return function; + } + + public void setFunction(String function) { + this.function = function; + } + + public String getParameter() { + return parameter; + } + + public void setParameter(String parameter) { + this.parameter = parameter; + } + + public String getAs() { + return as; + } + + public void setAs(String as) { + this.as = as; + } +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/DecisionLogic.java b/yard-api/src/main/java/org/kie/yard/api/model/DecisionLogic.java index b6fa47e..81817ad 100644 --- a/yard-api/src/main/java/org/kie/yard/api/model/DecisionLogic.java +++ b/yard-api/src/main/java/org/kie/yard/api/model/DecisionLogic.java @@ -27,7 +27,8 @@ key = "type", value = { @YamlSubtype(alias = "DecisionTable", type = DecisionTable.class), - @YamlSubtype(alias = "LiteralExpression", type = LiteralExpression.class) + @YamlSubtype(alias = "LiteralExpression", type = LiteralExpression.class), + @YamlSubtype(alias = "Rules", type = RuleExpression.class) }) @JsonbTypeInfo( key = "type", diff --git a/yard-api/src/main/java/org/kie/yard/api/model/Given.java b/yard-api/src/main/java/org/kie/yard/api/model/Given.java new file mode 100644 index 0000000..458cc8c --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/Given.java @@ -0,0 +1,51 @@ +/* + * 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 Given implements Pattern { + private String given; + private String from; + private List having; + + public String getGiven() { + return given; + } + + public void setGiven(String given) { + this.given = given; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public List getHaving() { + return having; + } + + public void setHaving(List having) { + this.having = having; + } +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/GroupBy.java b/yard-api/src/main/java/org/kie/yard/api/model/GroupBy.java new file mode 100644 index 0000000..222328c --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/GroupBy.java @@ -0,0 +1,51 @@ +/* + * 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 class GroupBy implements Pattern { + private Given given; + + private Grouping grouping; + + private Accumulator accumulator; + + public Given getGiven() { + return given; + } + + public void setGiven(Given given) { + this.given = given; + } + + public Grouping getGrouping() { + return grouping; + } + + public void setGrouping(Grouping grouping) { + this.grouping = grouping; + } + + public Accumulator getAccumulators() { + return accumulator; + } + + public void setAccumulators(Accumulator accumulator) { + this.accumulator = accumulator; + } +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/Grouping.java b/yard-api/src/main/java/org/kie/yard/api/model/Grouping.java new file mode 100644 index 0000000..10d1c12 --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/Grouping.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.api.model; +public class Grouping { + private String by; + private String as; + + public String getBy() { + return by; + } + + public void setBy(String by) { + this.by = by; + } + + public String getAs() { + return as; + } + + public void setAs(String as) { + this.as = as; + } +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/ListResult.java b/yard-api/src/main/java/org/kie/yard/api/model/ListResult.java new file mode 100644 index 0000000..9ed3d03 --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/ListResult.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; + +public class ListResult { + private String add; + + public String getAdd() { + return add; + } + + public void setAdd(String add) { + this.add = add; + } +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/Pattern.java b/yard-api/src/main/java/org/kie/yard/api/model/Pattern.java new file mode 100644 index 0000000..55540e7 --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/Pattern.java @@ -0,0 +1,22 @@ +/* + * 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 Pattern { +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/Pattern_YamlDeserializerImpl.java b/yard-api/src/main/java/org/kie/yard/api/model/Pattern_YamlDeserializerImpl.java new file mode 100644 index 0000000..da0bee1 --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/Pattern_YamlDeserializerImpl.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.api.model; + +import org.kie.j2cl.tools.yaml.mapper.api.YAMLDeserializer; +import org.kie.j2cl.tools.yaml.mapper.api.exception.YAMLDeserializationException; +import org.kie.j2cl.tools.yaml.mapper.api.internal.deser.YAMLDeserializationContext; +import org.kie.j2cl.tools.yaml.mapper.api.node.NodeType; +import org.kie.j2cl.tools.yaml.mapper.api.node.YamlMapping; +import org.kie.j2cl.tools.yaml.mapper.api.node.YamlNode; + +import java.util.ArrayList; +import java.util.Objects; + +public class Pattern_YamlDeserializerImpl + implements YAMLDeserializer { + + @Override + public Pattern deserialize(YamlMapping yamlMapping, String s, YAMLDeserializationContext yamlDeserializationContext) throws YAMLDeserializationException { + return deserialize(yamlMapping.getNode(s), yamlDeserializationContext); + } + + @Override + public Pattern deserialize(YamlNode yamlNode, YAMLDeserializationContext yamlDeserializationContext) { + final YamlMapping mapping = yamlNode.asMapping(); + if (mapping.keys().contains("given")) { + return createGiven(mapping); + } else if (mapping.keys().contains("groupBy")) { + final GroupBy groupBy = new GroupBy(); + groupBy.setGiven(createGiven(mapping.getNode("groupBy").asMapping())); + groupBy.setGrouping(createGrouping(mapping.getNode("grouping").asMapping())); + groupBy.setAccumulators(createAccumulator(mapping.getNode("accumulator").asMapping())); + return groupBy; + } + throw new IllegalStateException("Unknown element, should be given or groupBy"); + } + + private Accumulator createAccumulator(final YamlMapping groupBy) { + final Accumulator accumulator = new Accumulator(); + accumulator.setFunction((String) groupBy.getNode("function").asScalar().value()); + accumulator.setAs((String) groupBy.getNode("as").asScalar().value()); + if (groupBy.keys().contains("parameter")) { + accumulator.setParameter((String) groupBy.getNode("parameter").asScalar().value()); + } + // TODO All are cast to Strings, check what happens with numbers + return accumulator; + } + + private static Given createGiven(YamlMapping mapping) { + final Given given = new Given(); + given.setGiven((String) mapping.getNode("given").asScalar().value()); + given.setFrom((String) mapping.getNode("from").asScalar().value()); + given.setHaving(new ArrayList<>()); + if (mapping.keys().contains("having") + && Objects.equals(mapping.getNode("having").type(), NodeType.SEQUENCE)) { + for (YamlNode having : mapping.getNode("having").asSequence()) { + given.getHaving().add((String) having.asScalar().value()); + } + } + return given; + } + + private Grouping createGrouping(YamlMapping groupingNode) { + final Grouping grouping = new Grouping(); + grouping.setBy((String) groupingNode.getNode("by").asScalar().value()); + grouping.setAs((String) groupingNode.getNode("as").asScalar().value()); + return grouping; + } +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/Pattern_YamlSerializerImpl.java b/yard-api/src/main/java/org/kie/yard/api/model/Pattern_YamlSerializerImpl.java new file mode 100644 index 0000000..114468e --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/Pattern_YamlSerializerImpl.java @@ -0,0 +1,37 @@ +/* + * 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.kie.j2cl.tools.yaml.mapper.api.YAMLSerializer; +import org.kie.j2cl.tools.yaml.mapper.api.internal.ser.YAMLSerializationContext; +import org.kie.j2cl.tools.yaml.mapper.api.node.YamlMapping; +import org.kie.j2cl.tools.yaml.mapper.api.node.YamlSequence; + +public class Pattern_YamlSerializerImpl + implements YAMLSerializer { + @Override + public void serialize(YamlMapping yamlMapping, String s, Pattern when, YAMLSerializationContext yamlSerializationContext) { + + } + + @Override + public void serialize(YamlSequence yamlSequence, Pattern when, YAMLSerializationContext yamlSerializationContext) { + + } +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/RuleExpression.java b/yard-api/src/main/java/org/kie/yard/api/model/RuleExpression.java new file mode 100644 index 0000000..d516ad8 --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/RuleExpression.java @@ -0,0 +1,47 @@ +/* + * 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.kie.j2cl.tools.yaml.mapper.api.annotation.YAMLMapper; + +import java.util.List; + +@YAMLMapper +public class RuleExpression implements DecisionLogic { + + private String result; + + private List rules; + + public List getRules() { + return rules; + } + + public void setRules(List rules) { + this.rules = rules; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/YamlRule.java b/yard-api/src/main/java/org/kie/yard/api/model/YamlRule.java new file mode 100644 index 0000000..597c055 --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/YamlRule.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 org.kie.j2cl.tools.yaml.mapper.api.annotation.YAMLMapper; +import org.kie.j2cl.tools.yaml.mapper.api.annotation.YamlTypeDeserializer; +import org.kie.j2cl.tools.yaml.mapper.api.annotation.YamlTypeSerializer; + +import java.util.List; + +@YAMLMapper +public class YamlRule { + + @YamlTypeDeserializer(Pattern_YamlDeserializerImpl.class) + @YamlTypeSerializer(Pattern_YamlSerializerImpl.class) + private List when; + + @YamlTypeDeserializer(YamlRuleThen_YamlDeserializerImpl.class) + @YamlTypeSerializer(YamlRuleThen_YamlSerializerImpl.class) + private YamlRuleThen then; + + public List getWhen() { + return when; + } + + public void setWhen(List when) { + this.when = when; + } + + public YamlRuleThen getThen() { + return then; + } + + public void setThen(YamlRuleThen then) { + this.then = then; + } +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThen.java b/yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThen.java new file mode 100644 index 0000000..9bdafce --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThen.java @@ -0,0 +1,22 @@ +/* + * 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 YamlRuleThen { +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThenListImpl.java b/yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThenListImpl.java new file mode 100644 index 0000000..f5d340d --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThenListImpl.java @@ -0,0 +1,37 @@ +/* + * 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.Map; + +public class YamlRuleThenListImpl implements YamlRuleThen { + private Map functions; + + public YamlRuleThenListImpl(Map functions) { + this.functions = functions; + } + + public Map getFunctions() { + return functions; + } + + public void setFunctions(Map functions) { + this.functions = functions; + } +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThen_YamlDeserializerImpl.java b/yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThen_YamlDeserializerImpl.java new file mode 100644 index 0000000..92b9213 --- /dev/null +++ b/yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThen_YamlDeserializerImpl.java @@ -0,0 +1,60 @@ +/* + * 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.kie.j2cl.tools.yaml.mapper.api.YAMLDeserializer; +import org.kie.j2cl.tools.yaml.mapper.api.exception.YAMLDeserializationException; +import org.kie.j2cl.tools.yaml.mapper.api.internal.deser.YAMLDeserializationContext; +import org.kie.j2cl.tools.yaml.mapper.api.node.NodeType; +import org.kie.j2cl.tools.yaml.mapper.api.node.YamlMapping; +import org.kie.j2cl.tools.yaml.mapper.api.node.YamlNode; +import org.kie.j2cl.tools.yaml.mapper.api.node.YamlSequence; + +import java.util.HashMap; +import java.util.Map; + +public class YamlRuleThen_YamlDeserializerImpl + implements YAMLDeserializer { + + @Override + public YamlRuleThen deserialize(YamlMapping yamlMapping, String s, YAMLDeserializationContext yamlDeserializationContext) throws YAMLDeserializationException { + return deserialize(yamlMapping.getNode(s), yamlDeserializationContext); + } + + @Override + public YamlRuleThen deserialize(YamlNode yamlNode, YAMLDeserializationContext yamlDeserializationContext) { + final Map functions = new HashMap<>(); + final YamlSequence result = yamlNode.asMapping().getSequenceNode("result"); + result.iterator().forEachRemaining(x -> { + for (String key : x.asMapping().keys()) { + final YamlNode node = x.asMapping().getNode(key); + if(node.type() == NodeType.SCALAR){ + functions.put(key, node.asScalar().value()); + } else if( node.type() == NodeType.MAPPING){ + final YamlMapping mapping = node.asMapping(); + final Map map = new HashMap<>(); + map.put("key", mapping.getNode("key").asScalar().value()); + map.put("value", mapping.getNode("value").asScalar().value()); + functions.put(key, map); + } + } + }); + return new YamlRuleThenListImpl(functions); + } +} diff --git a/yard-api/src/main/java/org/kie/yard/api/model/WhenThenRuleThenSerializer.java b/yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThen_YamlSerializerImpl.java similarity index 56% rename from yard-api/src/main/java/org/kie/yard/api/model/WhenThenRuleThenSerializer.java rename to yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThen_YamlSerializerImpl.java index 63d2dcd..aeaec39 100644 --- a/yard-api/src/main/java/org/kie/yard/api/model/WhenThenRuleThenSerializer.java +++ b/yard-api/src/main/java/org/kie/yard/api/model/YamlRuleThen_YamlSerializerImpl.java @@ -18,41 +18,19 @@ */ package org.kie.yard.api.model; -import java.util.Locale; - -import org.kie.j2cl.tools.yaml.mapper.api.YAMLDeserializer; import org.kie.j2cl.tools.yaml.mapper.api.YAMLSerializer; -import org.kie.j2cl.tools.yaml.mapper.api.exception.YAMLDeserializationException; -import org.kie.j2cl.tools.yaml.mapper.api.internal.deser.YAMLDeserializationContext; import org.kie.j2cl.tools.yaml.mapper.api.internal.ser.YAMLSerializationContext; import org.kie.j2cl.tools.yaml.mapper.api.node.YamlMapping; -import org.kie.j2cl.tools.yaml.mapper.api.node.YamlNode; import org.kie.j2cl.tools.yaml.mapper.api.node.YamlSequence; -public class WhenThenRuleThenSerializer - implements YAMLSerializer, - YAMLDeserializer { - - @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.asScalar().value().toLowerCase(Locale.ROOT); - } - +public class YamlRuleThen_YamlSerializerImpl implements YAMLSerializer { @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-core/src/main/java/org/kie/yard/core/DTableUnitBuilder.java b/yard-core/src/main/java/org/kie/yard/core/DTableUnitBuilder.java index a32e320..abe8dcf 100644 --- a/yard-core/src/main/java/org/kie/yard/core/DTableUnitBuilder.java +++ b/yard-core/src/main/java/org/kie/yard/core/DTableUnitBuilder.java @@ -26,6 +26,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.json.JsonMapper; import org.drools.model.Index; +import org.drools.ruleunits.api.DataSource; import org.drools.ruleunits.api.SingletonStore; import org.drools.ruleunits.dsl.SyntheticRuleUnit; import org.drools.ruleunits.dsl.SyntheticRuleUnitBuilder; @@ -59,7 +60,7 @@ public DTableUnitBuilder( public SyntheticRuleUnit build() { final SyntheticRuleUnitBuilder unit = SyntheticRuleUnitBuilder.build(name); - for (Map.Entry> e : definitions.inputs().entrySet()) { + for (Map.Entry> e : definitions.inputs().entrySet()) { unit.registerDataSource(e.getKey(), e.getValue(), Object.class); } final StoreHandle result = StoreHandle.empty(Object.class); @@ -71,7 +72,7 @@ public SyntheticRuleUnit build() { for (int idx = 0; idx < inputs.size(); idx++) { final RuleCell ruleCell = parseGenericRuleCell(ruleDefinition, idx); if (ruleCell.value != null) { - final SingletonStore dataSource = definitions.inputs().get(inputs.get(idx)); + final DataSource dataSource = definitions.inputs().get(inputs.get(idx)); rule.on(dataSource).filter(ruleCell.idxtype, ruleCell.value); } diff --git a/yard-core/src/main/java/org/kie/yard/core/DoThen.java b/yard-core/src/main/java/org/kie/yard/core/DoThen.java new file mode 100644 index 0000000..ba1173d --- /dev/null +++ b/yard-core/src/main/java/org/kie/yard/core/DoThen.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 org.kie.yard.api.model.YamlRule; +import org.kie.yard.api.model.YamlRuleThenListImpl; + +import java.util.List; +import java.util.Map; + +public class DoThen { + private final YaRDDefinitions definitions; + + public DoThen(final YaRDDefinitions definitions) { + this.definitions = definitions; + } + + public void doThen(final YamlRule ruleDefinition, + final StoreHandle storeHandle, + final Map context) { + + if (storeHandle.get() instanceof List list) { + if (ruleDefinition.getThen() instanceof YamlRuleThenListImpl thenList) { + if (thenList.getFunctions().containsKey("add")) { + list.add(context.get(thenList.getFunctions().get("add"))); + } + } + } else if (storeHandle.get() instanceof Map map) { + if (ruleDefinition.getThen() instanceof YamlRuleThenListImpl thenList) { + if (thenList.getFunctions().containsKey("put")) { + final Map o = (Map) thenList.getFunctions().get("put"); + final Object key = new MVELLER(QuotedExprParsed.from(o.get("key"))).doTheMVEL(context, definitions); + final Object value = new MVELLER(QuotedExprParsed.from(o.get("value"))).doTheMVEL(context, definitions); + map.put(key, value); + } + } + } + } +} diff --git a/yard-core/src/main/java/org/kie/yard/core/DoWhen.java b/yard-core/src/main/java/org/kie/yard/core/DoWhen.java new file mode 100644 index 0000000..dafa96e --- /dev/null +++ b/yard-core/src/main/java/org/kie/yard/core/DoWhen.java @@ -0,0 +1,142 @@ +/* + * 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.model.functions.Predicate1; +import org.drools.ruleunits.api.DataSource; +import org.drools.ruleunits.dsl.RuleFactory; +import org.drools.ruleunits.dsl.accumulate.Accumulator1; +import org.drools.ruleunits.dsl.patterns.Pattern1Def; +import org.drools.ruleunits.dsl.patterns.PatternDef; +import org.kie.yard.api.model.*; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import static org.drools.ruleunits.dsl.Accumulators.*; + +public class DoWhen { + private final YaRDDefinitions definitions; + + public DoWhen(final YaRDDefinitions definitions) { + + this.definitions = definitions; + } + + public PatternDef doWhen(final YamlRule ruleDefinition, + final RuleFactory rule) { + final Iterator iterator = ruleDefinition.getWhen().iterator(); + while (iterator.hasNext()) { + final Pattern when = iterator.next(); + if (when instanceof Given given) { + final Pattern1Def on = getObjectPattern(rule, given); + if (!iterator.hasNext()) { + return on; + } + } + if (when instanceof GroupBy groupBy) { + final Given given = groupBy.getGiven(); + return rule.groupBy( + r -> formPattern(r, given), + o -> getGroupingFunction(o, groupBy, definitions), + getAccumulator(groupBy, definitions) + + ); + } + throw new IllegalStateException("Unknown when section."); + } + throw new IllegalStateException("Rule had no constraints"); + } + + + private static Accumulator1 getAccumulator(final GroupBy groupBy, YaRDDefinitions definitions) { + final Accumulator accumulator = groupBy.getAccumulators(); + final String function = accumulator.getFunction(); + final String functionParameter = accumulator.getParameter(); + if (functionParameter == null || functionParameter.trim().isEmpty()) { + return switch (function) { + case "count" -> count(); + case "collect" -> collect(); + default -> throw new IllegalStateException("Could not find function " + function); + }; + } else { + return switch (function) { + case "sum" -> sum((a) -> { + final Map context = new HashMap<>(); + context.put(groupBy.getGiven().getGiven(), a); + return Integer.parseInt((String) new MVELLER(QuotedExprParsed.from(functionParameter)).doTheMVEL(context, definitions)); + }); + default -> + throw new IllegalStateException("Could not find function " + function + " with a parameter."); + }; + } + } + + private Object getGroupingFunction(final Object o, + final GroupBy groupBy, + final YaRDDefinitions definitions) { + final Grouping grouping = groupBy.getGrouping(); + final Map context = new HashMap<>(); + context.put(groupBy.getGiven().getGiven(), o); + + final MVELLER mveller = new MVELLER(QuotedExprParsed.from(grouping.getBy())); + return mveller.doTheMVEL(context, definitions); + } + + private Pattern1Def getObjectPattern(final RuleFactory rule, + final Given given) { + final Pattern1Def on = rule.on(from(given.getFrom())); + final String varName = given.getGiven(); + + for (String expression : given.getHaving()) { + + on.filter((Predicate1) o -> { + final Map context = new HashMap<>(); + if(o instanceof Map map){ + for (Object object : map.keySet()) { + context.put((String)object,((Map) o).get(object)); + } + } + context.put(varName, o); + return toBoolean(new MVELLER(QuotedExprParsed.from(expression)).doTheMVEL(context, definitions)); + }); + } + return on; + } + + + private PatternDef formPattern(final RuleFactory rule, + final Given given) { + return getObjectPattern(rule, given); + } + + private DataSource from(final String from) { + return definitions.inputs().get(from); + } + + private boolean toBoolean(Object o) { + if (o instanceof Boolean) { + return (Boolean) o; + } else { + return false; + } + + } +} diff --git a/yard-core/src/main/java/org/kie/yard/core/MVELLER.java b/yard-core/src/main/java/org/kie/yard/core/MVELLER.java new file mode 100644 index 0000000..5bc31b1 --- /dev/null +++ b/yard-core/src/main/java/org/kie/yard/core/MVELLER.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.core; + +import org.mvel2.MVEL; + +import java.util.HashMap; +import java.util.Map; + +public class MVELLER { + + private final String DATE_FUNCTION = """ + import java.time.Duration; + import java.time.LocalDate; + def date(text) { + java.time.format.DateTimeFormatter format = java.time.format.DateTimeFormatter.ofPattern(org.drools.util.DateUtils.getDateFormatMask()); + return LocalDate.parse(text, format); + } + """; + + private final QuotedExprParsed expr; + + public MVELLER(final QuotedExprParsed expr) { + this.expr = expr; + } + + public Object doTheMVEL(final Map context, + final YaRDDefinitions units) { + + final Map internalContext = new HashMap<>(); + internalContext.putAll(context); + + for (Map.Entry rawInput: units.rawInputs().entrySet()) { + internalContext.put(QuotedExprParsed.escapeIdentifier(rawInput.getKey()),rawInput.getValue()); + } + + for (Map.Entry> outKV : units.outputs().entrySet()) { + if (!outKV.getValue().isValuePresent()) { + continue; + } + internalContext.put(QuotedExprParsed.escapeIdentifier(outKV.getKey()), outKV.getValue().get()); + } + + try { + final String expression = new StringBuilder() + .append(DATE_FUNCTION) + .append(expr.getRewrittenExpression()).toString(); + final Object eval = MVEL.eval(expression, internalContext); + return eval; + } catch (Exception e) { + throw new RuntimeException("interpretation failed at runtime", e); + } + } +} diff --git a/yard-core/src/main/java/org/kie/yard/core/MVELLiteralExpressionInterpreter.java b/yard-core/src/main/java/org/kie/yard/core/MVELLiteralExpressionInterpreter.java index 1951df7..1bec1d4 100644 --- a/yard-core/src/main/java/org/kie/yard/core/MVELLiteralExpressionInterpreter.java +++ b/yard-core/src/main/java/org/kie/yard/core/MVELLiteralExpressionInterpreter.java @@ -20,9 +20,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.json.JsonMapper; -import org.mvel2.MVEL; -import java.util.HashMap; import java.util.Map; public class MVELLiteralExpressionInterpreter implements Firable { @@ -40,24 +38,9 @@ public MVELLiteralExpressionInterpreter(final String name, @Override public int fire(final Map context, final YaRDDefinitions units) { - final Map internalContext = new HashMap<>(); - internalContext.putAll(context); - - for (Map.Entry> outKV : units.outputs().entrySet()) { - if (!outKV.getValue().isValuePresent()) { - continue; - } - internalContext.put(QuotedExprParsed.escapeIdentifier(outKV.getKey()), outKV.getValue().get()); - } - - try { - String rewrittenExpression = expr.getRewrittenExpression(); - final Object result = MVEL.eval(rewrittenExpression, internalContext); - units.outputs().get(name).set(resolveValue(result)); - return 1; - } catch (Exception e) { - throw new RuntimeException("interpretation failed at runtime", e); - } + final Object result = new MVELLER(expr).doTheMVEL(context, units); + units.outputs().get(name).set(resolveValue(result)); + return 1; } private Object resolveValue(Object value) { diff --git a/yard-core/src/main/java/org/kie/yard/core/RuleExpressionBuilder.java b/yard-core/src/main/java/org/kie/yard/core/RuleExpressionBuilder.java new file mode 100644 index 0000000..5f5d2a5 --- /dev/null +++ b/yard-core/src/main/java/org/kie/yard/core/RuleExpressionBuilder.java @@ -0,0 +1,124 @@ +/* + * 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.DataSource; +import org.drools.ruleunits.dsl.SyntheticRuleUnit; +import org.drools.ruleunits.dsl.SyntheticRuleUnitBuilder; +import org.drools.ruleunits.dsl.accumulate.GroupByPattern1; +import org.drools.ruleunits.dsl.patterns.Pattern1Def; +import org.drools.ruleunits.dsl.patterns.PatternDef; +import org.kie.yard.api.model.Given; +import org.kie.yard.api.model.GroupBy; +import org.kie.yard.api.model.RuleExpression; +import org.kie.yard.api.model.YamlRule; + +import java.util.*; + +public class RuleExpressionBuilder { + private final YaRDDefinitions definitions; + private final List rules; + private final String name; + private final String resultType; + private final DoThen doThen; + private final DoWhen doWhen; + + public RuleExpressionBuilder(final YaRDDefinitions definitions, + final String name, + final RuleExpression ruleExpression) { + this.definitions = definitions; + this.name = name; + this.resultType = ruleExpression.getResult(); + this.rules = ruleExpression.getRules(); + this.doThen = new DoThen(definitions); + this.doWhen = new DoWhen(definitions); + } + + public SyntheticRuleUnit build() { + + final SyntheticRuleUnitBuilder unit = SyntheticRuleUnitBuilder.build(name); + + for (Map.Entry> e : definitions.inputs().entrySet()) { + unit.registerDataSource(e.getKey(), e.getValue(), Object.class); + } + + final StoreHandle result = getResult(); + + unit.registerGlobal(name, result); + definitions.outputs().put(name, result); + + return unit.defineRules(rulesFactory -> { + for (final YamlRule ruleDefinition : rules) { + final PatternDef on = doWhen.doWhen(ruleDefinition, rulesFactory.rule()); + + switch (ruleDefinition.getWhen().size()) { + case 1: + if (on instanceof Pattern1Def on1) { + execute(ruleDefinition, (Pattern1Def) on1, result); + } else if (on instanceof GroupByPattern1 on1) { + final GroupBy groupBy = (GroupBy) ruleDefinition.getWhen().get(0); // TODO 0? + execute(ruleDefinition, (GroupByPattern1) on1, result, groupBy); + } + // TODO 2 and 3 + } + } + }); + } + + private void execute(final YamlRule ruleDefinition, + final GroupByPattern1 on, + final StoreHandle result, + final GroupBy groupBy) { + on.execute(result, (storeHandle, grouper, group) -> { + final Map context = new HashMap<>(); + context.put(groupBy.getGrouping().getAs(), grouper); + context.put(groupBy.getAccumulators().getAs(), group); + doThen.doThen(ruleDefinition, storeHandle, context); + }); + } + + private void execute(final YamlRule ruleDefinition, + final Pattern1Def on, + final StoreHandle result) { + on.execute(result, (storeHandle, a) -> { + final Map context = new HashMap<>(); + if (ruleDefinition.getWhen().get(0) instanceof Given given) { + context.put(given.getGiven(), a); + } + doThen.doThen(ruleDefinition, storeHandle, context); + }); + } + + private StoreHandle getResult() { + final StoreHandle result = StoreHandle.empty(Object.class); + switch (resultType) { + case "List": + result.set(new ArrayList<>()); + return result; + case "Map": + result.set(new HashMap<>()); + return result; + case "TreeMap": + result.set(new TreeMap<>()); + return result; + default: + throw new IllegalStateException("Result type is not set correctly"); + } + } +} diff --git a/yard-core/src/main/java/org/kie/yard/core/YaRDDefinitions.java b/yard-core/src/main/java/org/kie/yard/core/YaRDDefinitions.java index 730953b..94442fa 100644 --- a/yard-core/src/main/java/org/kie/yard/core/YaRDDefinitions.java +++ b/yard-core/src/main/java/org/kie/yard/core/YaRDDefinitions.java @@ -18,15 +18,19 @@ */ package org.kie.yard.core; +import org.drools.ruleunits.api.DataSource; +import org.drools.ruleunits.api.DataStore; +import org.drools.ruleunits.api.SingletonStore; + +import java.util.Collection; 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> inputs, + Map> inputs, + Map rawInputs, List units, Map> outputs) { @@ -36,8 +40,19 @@ public Map evaluate(Map context) { if (!context.containsKey(inputKey)) { throw new IllegalArgumentException("Missing input key in context: " + inputKey); } + rawInputs.putAll(context); Object inputValue = context.get(inputKey); - inputs.get(inputKey).set(inputValue); + if (inputs.get(inputKey) instanceof SingletonStore singletonStore) { + singletonStore.set(inputValue); + } else if (inputs.get(inputKey) instanceof DataStore dataStore) { + if (inputValue instanceof Collection collection) { + for (Object o : collection) { + dataStore.add(o); + } + } else { + throw new IllegalArgumentException("Store needs to be a collection."); + } + } } for (Firable unit : units) { unit.fire(context, this); @@ -50,7 +65,13 @@ public Map evaluate(Map context) { } private void reset() { - inputs.forEach((k, v) -> v.clear()); + inputs.forEach((k, v) -> { + if (v instanceof SingletonStore vv) { + vv.clear(); + } + // TODO multiple + }); + rawInputs.clear(); outputs.forEach((k, v) -> v.clear()); } } diff --git a/yard-core/src/main/java/org/kie/yard/core/YaRDParser.java b/yard-core/src/main/java/org/kie/yard/core/YaRDParser.java index 7d6584b..7e66f92 100644 --- a/yard-core/src/main/java/org/kie/yard/core/YaRDParser.java +++ b/yard-core/src/main/java/org/kie/yard/core/YaRDParser.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Objects; public class YaRDParser { static YaRDParser fromModel(final YaRD model) throws IOException { @@ -64,7 +65,7 @@ private static String read(Reader reader) throws IOException { } private static final Logger LOG = LoggerFactory.getLogger(YaRDParser.class); - private final YaRDDefinitions definitions = new YaRDDefinitions(new HashMap<>(), new ArrayList<>(), new HashMap<>()); + private final YaRDDefinitions definitions = new YaRDDefinitions(new HashMap<>(), new HashMap<>(), new ArrayList<>(), new HashMap<>()); private final YaRD model; private YaRDParser(final YaRD model) { @@ -97,11 +98,14 @@ private void appendUnits() { } } - private Firable createDecisionLogic(String nameString, DecisionLogic decisionLogic) { + private Firable createDecisionLogic(final String name, + final DecisionLogic decisionLogic) { if (decisionLogic instanceof org.kie.yard.api.model.DecisionTable decisionTable) { - return new SyntheticRuleUnitWrapper(new DTableUnitBuilder(definitions, nameString, decisionTable).build()); + return new SyntheticRuleUnitWrapper(new DTableUnitBuilder(definitions, name, decisionTable).build()); } else if (decisionLogic instanceof org.kie.yard.api.model.LiteralExpression literalExpression) { - return new LiteralExpressionBuilder(model.getExpressionLang(), definitions, nameString, literalExpression).build(); + return new LiteralExpressionBuilder(model.getExpressionLang(), definitions, name, literalExpression).build(); + } else if (decisionLogic instanceof RuleExpression ruleExpression) { + return new SyntheticRuleUnitWrapper(new RuleExpressionBuilder(definitions, name, ruleExpression).build()); } else { throw new UnsupportedOperationException("Not implemented."); } @@ -113,7 +117,11 @@ private void appendInputs() { String nameString = hi.getName(); @SuppressWarnings("unused") Class typeRef = processType(hi.getType()); - definitions.inputs().put(nameString, DataSource.createSingleton()); + if(Objects.equals("Store", hi.getType())) { + definitions.inputs().put(nameString, DataSource.createStore()); + } else { + definitions.inputs().put(nameString, DataSource.createSingleton()); + } } } diff --git a/yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java b/yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java index cfed212..ed0195e 100644 --- a/yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java +++ b/yard-core/src/test/java/org/kie/yard/core/InsuranceBasePriceTest.java @@ -39,7 +39,7 @@ public void testScenario1() throws Exception { """; Map outputJSONasMap = evaluate(CTX, FILE_NAME); assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Base price", 500); - assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Downpayment", 50); + assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Downpayment", 50.0); } @Test @@ -52,6 +52,6 @@ public void testScenario2() throws Exception { """; Map outputJSONasMap = evaluate(CTX, FILE_NAME); assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Base price", 1000); - assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Downpayment", 70); + assertThat(outputJSONasMap).hasFieldOrPropertyWithValue("Downpayment", 70.0); } } diff --git a/yard-core/src/test/java/org/kie/yard/core/MVELLERTest.java b/yard-core/src/test/java/org/kie/yard/core/MVELLERTest.java new file mode 100644 index 0000000..1152c27 --- /dev/null +++ b/yard-core/src/test/java/org/kie/yard/core/MVELLERTest.java @@ -0,0 +1,71 @@ +/* + * 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.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class MVELLERTest { + + @Test + void testSimple() { + final Map context = new HashMap<>(); + final Map value = new HashMap<>(); + value.put("name", "Ticket 3"); + value.put("status", "Critical"); + context.put("$ticket", value); + final String expr = "$ticket.status == \"Blocking\""; + final Object o = new MVELLER(QuotedExprParsed.from(expr)).doTheMVEL(context, new YaRDDefinitions(new HashMap<>(), new HashMap<>(), null, new HashMap<>())); + assertEquals(false, o); + } + + @Test + void testFirstLetter() { + final Map context = new HashMap<>(); + final Map value = new HashMap<>(); + value.put("name", "Lars"); + value.put("age", "24"); + context.put("$p", value); + final String expr = "$p.age >= 18"; + final Object o = new MVELLER(QuotedExprParsed.from(expr)).doTheMVEL(context, new YaRDDefinitions(new HashMap<>(), new HashMap<>(), null, new HashMap<>())); + assertEquals(true, o); + } + + @Test + void testDate() { + final Map context = new HashMap<>(); + final String expr = "date('20-Dec-2024').month"; + final Object o = new MVELLER(QuotedExprParsed.from(expr)).doTheMVEL(context, new YaRDDefinitions(new HashMap<>(), new HashMap<>(), null, new HashMap<>())); + assertEquals("DECEMBER", o.toString()); + } + + @Test + void testDuration() { + final Map context = new HashMap<>(); + final String expr = "Duration.between(" + + " date('10-Jan-2022').atStartOfDay(), " + + " date('15-Jan-2022').atStartOfDay()).toDays()"; + final Object o = new MVELLER(QuotedExprParsed.from(expr)).doTheMVEL(context, new YaRDDefinitions(new HashMap<>(), new HashMap<>(), null, new HashMap<>())); + assertEquals("5", o.toString()); + } +} \ No newline at end of file diff --git a/yard-core/src/test/java/org/kie/yard/core/MVELLiteralExpressionInterpreterTest.java b/yard-core/src/test/java/org/kie/yard/core/MVELLiteralExpressionInterpreterTest.java index b7cd80f..e73c39d 100644 --- a/yard-core/src/test/java/org/kie/yard/core/MVELLiteralExpressionInterpreterTest.java +++ b/yard-core/src/test/java/org/kie/yard/core/MVELLiteralExpressionInterpreterTest.java @@ -38,7 +38,7 @@ void setUp(final String expression) { final Map> outputs = new HashMap<>(); outputs.put("C", StoreHandle.of(5)); outputs.put("sum", StoreHandle.empty(Object.class)); - yardDefinitions = new YaRDDefinitions(Collections.emptyMap(), Collections.EMPTY_LIST, outputs); + yardDefinitions = new YaRDDefinitions(Collections.emptyMap(), Collections.emptyMap(), Collections.EMPTY_LIST, outputs); } diff --git a/yard-core/src/test/java/org/kie/yard/core/RulesGroupByTest.java b/yard-core/src/test/java/org/kie/yard/core/RulesGroupByTest.java new file mode 100644 index 0000000..3b1c72e --- /dev/null +++ b/yard-core/src/test/java/org/kie/yard/core/RulesGroupByTest.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.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class RulesGroupByTest + extends TestBase { + + private static final String FILE_NAME = "/rules/groupby.yml"; + private String data = """ + { + "Persons": + [ + {"name":"Toni", "age":"12", "kids": "0"}, + {"name":"Lars", "age":"24", "kids": "0"}, + {"name":"David", "age":"29", "kids": "2"}, + {"name":"Eric", "age":"21", "kids": "1"}, + {"name":"Jens", "age":"40", "kids": "2"}, + {"name":"Jens", "age":"44", "kids": "2"}, + {"name":"John", "age":"104", "kids": "3"} + ] + } + """; + + @Test + public void testGroupByAndSumByInitial() throws Exception { + final Map outputJSONasMap = evaluate(data, FILE_NAME); + final Map o = (Map) outputJSONasMap.get("Kids by parent initials"); + + assertThat(o.size()).isEqualTo(4); + assertEquals(7,o.get("J")); + assertEquals(0,o.get("L")); + assertEquals(2,o.get("D")); + assertEquals(1,o.get("E")); + } + + @Test + public void testCountByDecade() throws Exception { + final Map outputJSONasMap = evaluate(data, FILE_NAME); + final Map o = (Map) outputJSONasMap.get("Parent count by decade"); + + assertThat(o.size()).isEqualTo(3); + assertEquals(2,o.get("20")); + assertEquals(1,o.get("100")); + assertEquals(2,o.get("40")); + } +} diff --git a/yard-core/src/test/java/org/kie/yard/core/RulesGroupDateFunctionsByTest.java b/yard-core/src/test/java/org/kie/yard/core/RulesGroupDateFunctionsByTest.java new file mode 100644 index 0000000..de541e8 --- /dev/null +++ b/yard-core/src/test/java/org/kie/yard/core/RulesGroupDateFunctionsByTest.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 org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +public class RulesGroupDateFunctionsByTest + extends TestBase { + + private static final String FILE_NAME = "/rules/groupby-functions.yml"; + private String data = """ + { + "Current Time": "12-Dec-2024", + "Tickets": + [ + {"name":"Bug 1", "createdAt":"11-Dec-2024", "priority": "Critical"}, + {"name":"Bug 2", "createdAt":"11-Dec-2024", "priority": "Major"}, + {"name":"Bug 3", "createdAt":"5-Dec-2024", "priority": "Minor"}, + {"name":"Bug 4", "createdAt":"10-Dec-2024", "priority": "Critical"}, + {"name":"Bug 5", "createdAt":"1-Dec-2024", "priority": "Critical"} + ] + } + """; + + @Test + public void testGroupByAndSumByInitial() throws Exception { + final Map outputJSONasMap = evaluate(data, FILE_NAME); + final Map o = (Map) outputJSONasMap.get("Group week old tickets by type"); + + assertThat(o.size()).isEqualTo(3); + assertEquals(1,o.get("Major")); + assertEquals(2,o.get("Critical")); + assertEquals(1,o.get("Minor")); + } + + @Test + public void testGroupByAndSumByDate() throws Exception { + final Map outputJSONasMap = evaluate(data, FILE_NAME); + final Map o = (Map) outputJSONasMap.get("All Tickets group by Date"); + + assertThat(o.size()).isEqualTo(4); + assertThat(o.keySet()).containsExactly( + "2024-12-01", + "2024-12-05", + "2024-12-10", + "2024-12-11" + ); + assertEquals(1,o.get("2024-12-01")); + assertEquals(1,o.get("2024-12-05")); + assertEquals(1,o.get("2024-12-10")); + assertEquals(2,o.get("2024-12-11")); + } +} diff --git a/yard-core/src/test/java/org/kie/yard/core/RulesMergeTest.java b/yard-core/src/test/java/org/kie/yard/core/RulesMergeTest.java new file mode 100644 index 0000000..7ee596e --- /dev/null +++ b/yard-core/src/test/java/org/kie/yard/core/RulesMergeTest.java @@ -0,0 +1,65 @@ +/* + * 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.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +public class RulesMergeTest + extends TestBase { + + private static final String FILE_NAME = "/rules/merge.yml"; + + @Test + public void testMerge() throws Exception { + final String CTX = """ + { + "Jira 1":[ + {"name":"Ticket 1", "status":"Blocking", "createdAt":"12-12-2000"}, + {"name":"Ticket 2", "status":"Minor", "createdAt":"12-12-2000"} + ], + "Jira 2":[ + {"name":"Ticket 3", "status":"Critical", "createdAt":"12-12-2000"}, + {"name":"Ticket 4", "status":"Blocking", "createdAt":"12-12-2000"} + ] + } + """; + final Map outputJSONasMap = evaluate(CTX, FILE_NAME); + final List> o = (List>) outputJSONasMap.get("Merged data from two ticket streams"); + + assertThat(o.size()).isEqualTo(2); + assertThat(o).contains(toTicketMap("Ticket 1")); + assertThat(o).contains(toTicketMap("Ticket 4")); + // 1 and 3 + } + + private Map toTicketMap(final String name) { + final Map map = new HashMap<>(); + map.put("name", name); + map.put("status", "Blocking"); + map.put("createdAt", "12-12-2000"); + + return map; + } +} diff --git a/yard-core/src/test/resources/domestic-package-prices.yml b/yard-core/src/test/resources/domestic-package-prices.yml index 7f52576..e6627fb 100644 --- a/yard-core/src/test/resources/domestic-package-prices.yml +++ b/yard-core/src/test/resources/domestic-package-prices.yml @@ -26,4 +26,4 @@ elements: - 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 + then: '{ "Size": "XL", "Cost": 10.90}' diff --git a/yard-core/src/test/resources/rules/groupby-functions.yml b/yard-core/src/test/resources/rules/groupby-functions.yml new file mode 100644 index 0000000..9049e0e --- /dev/null +++ b/yard-core/src/test/resources/rules/groupby-functions.yml @@ -0,0 +1,57 @@ +specVersion: alpha +kind: YaRD +name: 'BasePrice' +inputs: + - name: Current Time + type: String + - name: Tickets + type: Store +elements: + - name: Group week old tickets by type + type: Decision + logic: + type: Rules + result: Map + rules: + - when: + - groupBy: + given: $t + from: Tickets + having: + - | + Duration.between( + date(createdAt).atStartOfDay(), + date(`Current Time`).atStartOfDay()).toDays() + <= 7 + grouping: + by: $t.priority + as: $priority + accumulator: + function: count + as: $count + then: + result: + - put: + key: $priority + value: $count + - name: All Tickets group by Date + type: Decision + logic: + type: Rules + result: TreeMap + rules: + - when: + - groupBy: + given: $t + from: Tickets + grouping: + by: date($t.createdAt) + as: $createdAtDate + accumulator: + function: count + as: $count + then: + result: + - put: + key: $createdAtDate + value: $count diff --git a/yard-core/src/test/resources/rules/groupby.yml b/yard-core/src/test/resources/rules/groupby.yml new file mode 100644 index 0000000..eaa195e --- /dev/null +++ b/yard-core/src/test/resources/rules/groupby.yml @@ -0,0 +1,55 @@ +specVersion: alpha +kind: YaRD +name: 'BasePrice' +inputs: + - name: Persons + type: Store +elements: + - name: Kids by parent initials + type: Decision + logic: + type: Rules + result: Map + rules: + - when: + - groupBy: + given: $p + from: Persons + having: + - age >= 18 + grouping: + by: $p.name[0] + as: $initial + accumulator: + function: sum + parameter: $p.kids + as: $kidCount + filter: $kidCount == 0 + then: + result: + - put: + key: $initial + value: $kidCount + - name: Parent count by decade + type: Decision + logic: + type: Rules + result: Map + rules: + - when: + - groupBy: + given: $p + from: Persons + having: + - kids > 0 + grouping: + by: $p.age - ( $p.age % 10 ) + as: $decade + accumulator: + function: count + as: $amountOfParentsForDecade + then: + result: + - put: + key: $decade + value: $amountOfParentsForDecade diff --git a/yard-core/src/test/resources/rules/merge.yml b/yard-core/src/test/resources/rules/merge.yml new file mode 100644 index 0000000..48b0538 --- /dev/null +++ b/yard-core/src/test/resources/rules/merge.yml @@ -0,0 +1,31 @@ +specVersion: alpha +kind: YaRD +name: 'Merge' +inputs: + - name: Jira 1 + type: Store + - name: Jira 2 + type: Store +elements: + - name: Merged data from two ticket streams + type: Decision + logic: + type: Rules + result: List + rules: + - when: + - given: $ticket + from: Jira 1 + having: + - status == "Blocking" + then: + result: + - add: $ticket + - when: + - given: $ticket2 + from: Jira 2 + having: + - status == "Blocking" + then: + result: + - add: $ticket2 \ No newline at end of file