Skip to content

Commit

Permalink
Merge pull request #2 from tarilabs/akoufoudakis-DROOLS-1676+mmortari2
Browse files Browse the repository at this point in the history
Fixing the remainder of the code and providing test case.
  • Loading branch information
akoufoudakis committed Sep 8, 2017
2 parents 4497e46 + be2acca commit d83d6b3
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 7 deletions.
Expand Up @@ -25,7 +25,9 @@
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.kie.dmn.core.util.DynamicTypeUtils.entry;
import static org.kie.dmn.core.util.DynamicTypeUtils.prototype;
import static org.mockito.Matchers.any;
Expand All @@ -48,6 +50,7 @@
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.junit.Ignore;
import org.junit.Test;
import org.kie.dmn.api.core.DMNContext;
Expand Down Expand Up @@ -1322,5 +1325,62 @@ public void testArtificialAttributes() {

assertThat( result.get( "Greeting Message" ), is( "Hello John Doe" ) );
}

@Test
public void testCallDecisionFunction() {
DMNRuntime runtime = DMNRuntimeUtil.createRuntimeWithAdditionalResources( "Caller.dmn", this.getClass(), "Calling.dmn" );
DMNModel dmnModel = runtime.getModel( "http://www.trisotech.com/definitions/_b0a696d6-3d57-4e97-b5d4-b44a63909d67", "Caller" );
assertThat( dmnModel, notNullValue() );

DMNContext context = DMNFactory.newContext();
context.set( "My Name", "John Doe" );
context.set( "My Number", 3 );
context.set( "Call ns", "http://www.trisotech.com/definitions/_88156d21-3acc-43b6-8b81-385caf0bb6ca" );
context.set( "Call name", "Calling" );
context.set( "Call decision", "Final Result" );

// Good case.
{
DMNResult dmnResult = runtime.evaluateAll( dmnModel, context );
assertThat( DMNRuntimeUtil.formatMessages( dmnResult.getMessages() ), dmnResult.hasErrors(), is( false ) );

DMNContext result = dmnResult.getContext();
assertThat( result.get( "Final decision" ), is( "The final decision is: Hello, John Doe your number once double is equal to: 6" ) );
}

// Error if wrong namespace/name
{
DMNContext wrongNsContext = context.clone();
wrongNsContext.set("Call ns", "http://www.acme.com/a-wrong-namespace");

DMNResult wrongNsResult = runtime.evaluateAll( dmnModel, wrongNsContext );
assertThat( DMNRuntimeUtil.formatMessages( wrongNsResult.getMessages() ), wrongNsResult.hasErrors(), is( true ) );
// total of: 2. x1 error in calling external decision, and x1 error in making final decision as it depends on the former.
assertThat( wrongNsResult.getMessages().size(), is( 2 ) );
}

// Error if wrong decision name in model
{
DMNContext wrongDecisionContext = context.clone();
wrongDecisionContext.set("Call decision", "<unexistent decision>");

DMNResult wrongDecisionResult = runtime.evaluateAll( dmnModel, wrongDecisionContext );
assertThat( DMNRuntimeUtil.formatMessages( wrongDecisionResult.getMessages() ), wrongDecisionResult.hasErrors(), is( true ) );
// total of: 2. x1 error in calling external decision, and x1 error in making final decision as it depends on the former.
assertThat( wrongDecisionResult.getMessages().size(), is( 2 ) );
}

// Error if model and decision is found, but problem in external invocation.
{
DMNContext wrongContext = context.clone();
wrongContext.set("My Number", "<not a number>");

DMNResult wrongResult = runtime.evaluateAll( dmnModel, wrongContext );
assertThat( DMNRuntimeUtil.formatMessages( wrongResult.getMessages() ), wrongResult.hasErrors(), is( true ) );
// total of: 2. x1 error in calling external decision, and x1 error in making final decision as it depends on the former.
// please notice it will print 4 lines in the log, 2x are the "external invocation" and then 2x are the one by the caller, checked herebelow:
assertThat( wrongResult.getMessages().size(), is( 2 ) );
}
}
}

Expand Up @@ -16,12 +16,14 @@

package org.kie.dmn.core.util;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

import org.junit.Assert;
import org.kie.api.KieServices;
import org.kie.api.io.Resource;
import org.kie.api.runtime.KieContainer;
import org.kie.dmn.api.core.DMNMessage;
import org.kie.dmn.api.core.DMNRuntime;
Expand All @@ -48,6 +50,23 @@ public static DMNRuntime createRuntime(final String resourceName, final Class te
Assert.assertNotNull(runtime);
return runtime;
}

public static DMNRuntime createRuntimeWithAdditionalResources(final String resourceName, final Class testClass, final String... additionalResources) {
final KieServices ks = KieServices.Factory.get();
Resource mainResource = ks.getResources().newClassPathResource(resourceName, testClass);
List<Resource> totalResources = new ArrayList<>();
totalResources.add(mainResource);
for ( String add : additionalResources ) {
totalResources.add( ks.getResources().newClassPathResource(add, testClass) );
}
final KieContainer kieContainer = KieHelper.getKieContainer(
ks.newReleaseId("org.kie", "dmn-test-"+UUID.randomUUID(), "1.0"),
totalResources.toArray(new Resource[] {}));

final DMNRuntime runtime = kieContainer.newKieSession().getKieRuntime(DMNRuntime.class);
Assert.assertNotNull(runtime);
return runtime;
}

public static DMNRuntimeEventListener createListener() {
return new DefaultDMNRuntimeEventListener() {
Expand Down
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<semantic:definitions xmlns:semantic="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns="http://www.trisotech.com/definitions/_b0a696d6-3d57-4e97-b5d4-b44a63909d67" xmlns:feel="http://www.omg.org/spec/FEEL/20140401" xmlns:tc="http://www.omg.org/spec/DMN/20160719/testcase" xmlns:triso="http://www.trisotech.com/2015/triso/modeling" xmlns:trisofeed="http://trisotech.com/feed" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exporter="DMN Modeler" exporterVersion="5.2.4.1" id="_b0a696d6-3d57-4e97-b5d4-b44a63909d67" name="Caller" namespace="http://www.trisotech.com/definitions/_b0a696d6-3d57-4e97-b5d4-b44a63909d67" triso:logoChoice="Default">
<semantic:extensionElements/>
<semantic:decision id="_ffca21a7-67c7-4333-9c12-59a35c5075e2" name="Calling outside" triso:displayName="Calling outside">
<semantic:variable id="_8ed989c1-5cc0-4a9e-9401-c000a65d8190" name="Calling outside"/>
<semantic:informationRequirement>
<semantic:requiredInput href="#_f9072420-5236-4af9-b7a5-fd96f677a07b"/>
</semantic:informationRequirement>
<semantic:informationRequirement>
<semantic:requiredInput href="#_228cd72c-c735-4ed0-a635-035b898cbbe0"/>
</semantic:informationRequirement>
<semantic:informationRequirement>
<semantic:requiredInput href="#_d9170e9f-0842-418a-aab0-ade249b38da8"/>
</semantic:informationRequirement>
<semantic:informationRequirement>
<semantic:requiredInput href="#_52890fab-4767-4d6b-988b-dafed026d0b2"/>
</semantic:informationRequirement>
<semantic:informationRequirement>
<semantic:requiredInput href="#_9f9d607e-beb0-4d88-a881-c412ef1cee20"/>
</semantic:informationRequirement>
<semantic:invocation id="_6f46e094-d6b5-4dad-b0c6-05b96456b6b9">
<semantic:literalExpression id="literal__6f46e094-d6b5-4dad-b0c6-05b96456b6b9">
<semantic:text>call decision</semantic:text>
</semantic:literalExpression>
<semantic:binding>
<semantic:parameter id="_75a5d504-3ef7-42fb-9c9b-d28d1c198577" name="namespace"/>
<semantic:literalExpression id="_d9124ce8-42b5-4440-bc64-c014b1736468">
<semantic:text>Call ns</semantic:text>
</semantic:literalExpression>
</semantic:binding>
<semantic:binding>
<semantic:parameter id="_3969a316-e3d4-45a1-a681-784c993848bf" name="model name"/>
<semantic:literalExpression id="_343880cf-f513-4e31-924b-f3ef23766a66">
<semantic:text>Call name</semantic:text>
</semantic:literalExpression>
</semantic:binding>
<semantic:binding>
<semantic:parameter id="_ac744d85-3a93-433c-943a-b320d0a9afcf" name="decision name"/>
<semantic:literalExpression id="_98f0f611-520d-4a65-839d-b422909e9cf0">
<semantic:text>Call decision</semantic:text>
</semantic:literalExpression>
</semantic:binding>
<semantic:binding>
<semantic:parameter id="_96a5ada5-ecf8-4403-af22-85f999f86f03" name="invocation context"/>
<semantic:context id="_bcfff8ac-26f6-4946-bfc5-6b77101b55d5">
<semantic:contextEntry>
<semantic:variable id="_e3585247-a5ae-4441-bd47-d3be92d232d7" name="Caller name"/>
<semantic:literalExpression id="_8a804606-f815-46f2-80af-d9a2d7e8074d">
<semantic:text>My Name</semantic:text>
</semantic:literalExpression>
</semantic:contextEntry>
<semantic:contextEntry>
<semantic:variable id="_4b2fe0a2-b7a1-4e67-896e-6008e3df9ba9" name="A number"/>
<semantic:literalExpression id="_15d55230-cb0b-4875-ac30-24bdedec7490">
<semantic:text>My Number</semantic:text>
</semantic:literalExpression>
</semantic:contextEntry>
</semantic:context>
</semantic:binding>
</semantic:invocation>
</semantic:decision>
<semantic:inputData id="_f9072420-5236-4af9-b7a5-fd96f677a07b" name="My Name" triso:displayName="My Name">
<semantic:variable id="_7d1127b0-c2a1-45f5-9fca-a151cff8a0b0" name="My Name" typeRef="feel:string"/>
</semantic:inputData>
<semantic:inputData id="_228cd72c-c735-4ed0-a635-035b898cbbe0" name="My Number" triso:displayName="My Number">
<semantic:variable id="_19d776ec-cfcf-45bf-8027-83d58a3ec816" name="My Number" typeRef="feel:any"/> <!-- feel:any is intentional here. -->
</semantic:inputData>
<semantic:decision id="_998f547f-dcac-448e-953e-c8c9aff68e81" name="Final decision" triso:displayName="Final decision">
<semantic:variable id="_705ca19d-d732-4936-90a9-c941689952e5" name="Final decision"/>
<semantic:informationRequirement>
<semantic:requiredDecision href="#_ffca21a7-67c7-4333-9c12-59a35c5075e2"/>
</semantic:informationRequirement>
<semantic:literalExpression id="_65368486-732e-4523-b1b7-23ab02bcba66">
<semantic:text>"The final decision is: " + Calling outside</semantic:text>
</semantic:literalExpression>
</semantic:decision>
<semantic:inputData id="_d9170e9f-0842-418a-aab0-ade249b38da8" name="Call ns" triso:displayName="Call ns">
<semantic:variable id="_e2c56a0e-b061-43ee-8d3e-d811fc2f9f1d" name="Call ns" typeRef="feel:string"/>
</semantic:inputData>
<semantic:inputData id="_52890fab-4767-4d6b-988b-dafed026d0b2" name="Call name" triso:displayName="Call name">
<semantic:variable id="_4f253c86-0bdd-4fa6-9a7e-fcfff16ec95f" name="Call name" typeRef="feel:string"/>
</semantic:inputData>
<semantic:inputData id="_9f9d607e-beb0-4d88-a881-c412ef1cee20" name="Call decision" triso:displayName="Call decision">
<semantic:variable id="_3afdf84a-ecce-4ab8-a7e4-cbe2c0f66f10" name="Call decision" typeRef="feel:string"/>
</semantic:inputData>
</semantic:definitions>
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<semantic:definitions xmlns:semantic="http://www.omg.org/spec/DMN/20151101/dmn.xsd" xmlns="http://www.trisotech.com/definitions/_88156d21-3acc-43b6-8b81-385caf0bb6ca" xmlns:feel="http://www.omg.org/spec/FEEL/20140401" xmlns:tc="http://www.omg.org/spec/DMN/20160719/testcase" xmlns:triso="http://www.trisotech.com/2015/triso/modeling" xmlns:trisofeed="http://trisotech.com/feed" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" exporter="DMN Modeler" exporterVersion="5.2.4.1" id="_88156d21-3acc-43b6-8b81-385caf0bb6ca" name="Calling" namespace="http://www.trisotech.com/definitions/_88156d21-3acc-43b6-8b81-385caf0bb6ca" triso:logoChoice="Default">
<semantic:extensionElements/>
<semantic:inputData id="_8d5c3681-9616-4834-ad63-2a466eb91c5d" name="Caller name" triso:displayName="Caller name">
<semantic:variable id="_73528854-f705-4baf-9e23-72d9238b8832" name="Caller name" typeRef="feel:string"/>
</semantic:inputData>
<semantic:inputData id="_49dd2ea2-657f-4df0-bcd4-bdca0f84bf85" name="A number" triso:displayName="A number">
<semantic:variable id="_bae21883-ec73-40a0-a8a9-3dde50268495" name="A number" typeRef="feel:number"/>
</semantic:inputData>
<semantic:decision id="_5bcaa0b8-b476-46d2-bcbc-23e9f8fb4662" name="Greet caller" triso:displayName="Greet caller">
<semantic:variable id="_37e49c9c-432b-4a9a-8f94-d54ad937bd61" name="Greet caller"/>
<semantic:informationRequirement>
<semantic:requiredInput href="#_8d5c3681-9616-4834-ad63-2a466eb91c5d"/>
</semantic:informationRequirement>
<semantic:literalExpression id="_ee9beafd-b311-43e0-bacd-0eefdb295cac">
<semantic:text>"Hello, " + Caller name</semantic:text>
</semantic:literalExpression>
</semantic:decision>
<semantic:decision id="_ca1f35a9-34e1-4165-9cfa-d69802c5d6fe" name="Duoble the number" triso:displayName="Duoble the number">
<semantic:variable id="_b9114e44-24e2-4014-b578-354c7f61238d" name="Duoble the number"/>
<semantic:informationRequirement>
<semantic:requiredInput href="#_49dd2ea2-657f-4df0-bcd4-bdca0f84bf85"/>
</semantic:informationRequirement>
<semantic:context id="_20676122-3bac-4115-9f90-061fba334bef">
<semantic:contextEntry>
<semantic:variable id="_82a8fb04-7f1f-470a-a211-7fd676f5201a" name="left"/>
<semantic:literalExpression id="_18ac29f7-3283-41af-ac11-dfe7e11f115d">
<semantic:text>A number</semantic:text>
</semantic:literalExpression>
</semantic:contextEntry>
<semantic:contextEntry>
<semantic:variable id="_30b7ac11-88a7-4cf3-a861-dcae4d285bcd" name="right"/>
<semantic:literalExpression id="_574b6c02-a3da-4bd6-9012-e3059e350762">
<semantic:text>A number</semantic:text>
</semantic:literalExpression>
</semantic:contextEntry>
<semantic:contextEntry>
<semantic:variable id="_fa68a577-2a79-48a5-b8e6-99a03d53cbfe" name="sum left and right"/>
<semantic:literalExpression id="_ac1c79cf-ceb6-49fe-99e1-a8ea530a0668">
<semantic:text>left + right</semantic:text>
</semantic:literalExpression>
</semantic:contextEntry>
<semantic:contextEntry>
<semantic:literalExpression id="_ea6503be-c231-4666-801c-0ab23437fad1">
<semantic:text>sum left and right</semantic:text>
</semantic:literalExpression>
</semantic:contextEntry>
</semantic:context>
</semantic:decision>
<semantic:decision id="_2337d288-1ec7-457d-8ecc-63ecbdfe6f99" name="Final Result" triso:displayName="Final Result">
<semantic:variable id="_e3125b8d-6364-4450-a3d6-f4767bbae8d4" name="Final Result"/>
<semantic:informationRequirement>
<semantic:requiredDecision href="#_ca1f35a9-34e1-4165-9cfa-d69802c5d6fe"/>
</semantic:informationRequirement>
<semantic:informationRequirement>
<semantic:requiredDecision href="#_5bcaa0b8-b476-46d2-bcbc-23e9f8fb4662"/>
</semantic:informationRequirement>
<semantic:literalExpression id="_fc9f266a-5504-4453-9fad-8814fe505bf9">
<semantic:text>Greet caller + " your number once double is equal to: " + string(Duoble the number)</semantic:text>
</semantic:literalExpression>
</semantic:decision>
</semantic:definitions>
Expand Up @@ -16,10 +16,10 @@

package org.kie.dmn.feel.runtime.functions;

import org.kie.dmn.feel.runtime.FEELFunction;

import java.util.stream.Stream;

import org.kie.dmn.feel.runtime.FEELFunction;

public class BuiltInFunctions {

protected final static FEELFunction[] FUNCTIONS = new FEELFunction[]{
Expand Down Expand Up @@ -69,7 +69,8 @@ public class BuiltInFunctions {
// additional functions not part of the spec version 1.1
new NowFunction(),
new TodayFunction(),
new CodeFunction()
new CodeFunction(),
new CallDecisionFunction()
};

public static FEELFunction[] getFunctions() {
Expand Down
Expand Up @@ -16,6 +16,8 @@

package org.kie.dmn.feel.runtime.functions;

import java.util.Map;

import org.kie.dmn.api.core.DMNContext;
import org.kie.dmn.api.core.DMNModel;
import org.kie.dmn.api.core.DMNResult;
Expand All @@ -25,8 +27,6 @@
import org.kie.dmn.feel.runtime.events.FEELEventBase;
import org.kie.dmn.feel.runtime.events.InvalidParametersEvent;

import java.util.Map;

public class CallDecisionFunction extends BaseFEELFunction {

public CallDecisionFunction() {
Expand All @@ -38,6 +38,7 @@ public FEELFnResult<Object> invoke(@ParameterName("ctx") EvaluationContext ctx,
@ParameterName("decision name") String decisionName, @ParameterName("invocation context") Map<String, Object> invocationContext) {

DMNRuntime dmnRuntime = ctx.getDMNRuntime();

if(namespace == null) {
return FEELFnResult.ofError(new InvalidParametersEvent(FEELEvent.Severity.ERROR, "namespace", "cannot be null"));
}
Expand All @@ -53,15 +54,32 @@ public FEELFnResult<Object> invoke(@ParameterName("ctx") EvaluationContext ctx,
if(invocationContext == null) {
return FEELFnResult.ofError(new InvalidParametersEvent(FEELEvent.Severity.ERROR, "invocation context", "cannot be null"));
}

FEELEvent capturedException = null;
try {
ctx.enterFrame();
DMNModel dmnModel = dmnRuntime.getModel(namespace, modelName);
if (dmnModel == null) {
return FEELFnResult.ofError(
new FEELEventBase(FEELEvent.Severity.ERROR, "Cannot find model '"+modelName+"' in namespace "+namespace, null)
);
}
if (dmnModel.getDecisionByName(decisionName) == null) {
return FEELFnResult.ofError(
new FEELEventBase(FEELEvent.Severity.ERROR, "Cannot find decision '"+decisionName+"' in the model", null)
);
}

DMNContext dmnContext = dmnRuntime.newContext();
dmnContext.getAll().putAll(invocationContext);

DMNResult requiredDecisionResult = dmnRuntime.evaluateDecisionByName(dmnModel, decisionName, dmnContext);
if (requiredDecisionResult.hasErrors()) {
return FEELFnResult.ofError(
new FEELEventBase(FEELEvent.Severity.ERROR, "Errors occurred while invoking the external decision: " + requiredDecisionResult.getMessages(), null)
);
}

return FEELFnResult.ofResult(requiredDecisionResult.getContext().get(decisionName));
} catch(Exception e) {
capturedException = new FEELEventBase(FEELEvent.Severity.ERROR, "Error invoking function", new RuntimeException("Error invoking function " + getName() + ".", e));
Expand All @@ -71,5 +89,4 @@ public FEELFnResult<Object> invoke(@ParameterName("ctx") EvaluationContext ctx,

return FEELFnResult.ofError(capturedException);
}

}

0 comments on commit d83d6b3

Please sign in to comment.