├── .gitignore
├── LICENSE
├── NOTICE
├── README.md
├── dmnmgr-test-driver
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── de
│ │ └── lv1871
│ │ └── oss
│ │ └── dmnmgr
│ │ └── test
│ │ └── driver
│ │ ├── DecisionRunner.java
│ │ ├── DmnTestCreator.java
│ │ ├── DmnTestExecutor.java
│ │ ├── DmnTester.java
│ │ ├── VariableMapperService.java
│ │ └── model
│ │ ├── DecisionSimulationResponse.java
│ │ ├── DmnProject.java
│ │ ├── DmnProjectTestContainer.java
│ │ ├── DmnProjectTestDefinition.java
│ │ ├── DmnTest.java
│ │ ├── DmnTestSuite.java
│ │ └── TestSuiteTestResult.java
│ └── test
│ ├── java
│ └── de
│ │ └── lv1871
│ │ └── oss
│ │ └── dmnmgr
│ │ └── test
│ │ └── driver
│ │ ├── IntegrationTest.java
│ │ └── VariableMapperServiceTest.java
│ └── resources
│ ├── a_test.dmn
│ └── a_test.dmnapp.json
├── dmnmgr-tester
├── pom.xml
└── src
│ ├── main
│ └── java
│ │ └── de
│ │ └── lv1871
│ │ └── oss
│ │ └── tester
│ │ └── test
│ │ ├── dmnassert
│ │ ├── DmnAssert.java
│ │ ├── DmnTest.java
│ │ ├── DmnVariableAssert.java
│ │ └── model
│ │ │ └── DecisionSimulationResponse.java
│ │ ├── domain
│ │ └── DecisionEngine.java
│ │ └── function
│ │ ├── ExtendedBiFunction.java
│ │ ├── ExtendedBiPredicate.java
│ │ └── LambdaExtension.java
│ └── test
│ ├── java
│ └── de
│ │ └── lv1871
│ │ └── oss
│ │ └── tester
│ │ └── test
│ │ ├── dmnassert
│ │ ├── DmnAssertTest.java
│ │ ├── DmnTestTest.java
│ │ └── DmnVariableAssertTest.java
│ │ ├── domain
│ │ └── DecisionEngineTest.java
│ │ └── function
│ │ └── LambdaExtensionTest.java
│ └── resources
│ ├── testDrgTwoDecisions.dmn
│ └── testDrgTwoDecisions.dmnapp.json
├── dmnmgr
├── pom.xml
└── src
│ ├── main
│ ├── java
│ │ └── de
│ │ │ └── lv1871
│ │ │ └── oss
│ │ │ └── dmnmgr
│ │ │ ├── App.java
│ │ │ ├── api
│ │ │ ├── DecisionResource.java
│ │ │ ├── UtilityResource.java
│ │ │ ├── documentation
│ │ │ │ └── DecisionApi.java
│ │ │ └── model
│ │ │ │ ├── DecisionRequest.java
│ │ │ │ ├── DecisionSimulationRequest.java
│ │ │ │ ├── DecisionTestRequest.java
│ │ │ │ ├── DecisionTestResponse.java
│ │ │ │ ├── DmnValidationResponse.java
│ │ │ │ ├── DmnValidationResult.java
│ │ │ │ ├── ErrorSeverity.java
│ │ │ │ ├── OpenApiDefinitionResponse.java
│ │ │ │ └── ServiceResult.java
│ │ │ ├── controller
│ │ │ └── HomeController.java
│ │ │ ├── domain
│ │ │ ├── CheckerFunctions.java
│ │ │ ├── InputEntryValidator.java
│ │ │ ├── InputExpressionRequiredValidator.java
│ │ │ ├── OutputEntryValidator.java
│ │ │ └── OutputNameRequiredValidator.java
│ │ │ └── service
│ │ │ ├── AdvancedDmnCheckService.java
│ │ │ ├── DecisionTestService.java
│ │ │ ├── DownloadService.java
│ │ │ ├── GenericDecisionService.java
│ │ │ └── VariableMapperService.java
│ └── resources
│ │ ├── META-INF
│ │ ├── processes.xml
│ │ └── services
│ │ │ └── javax.ws.rs.ext.RuntimeDelegate
│ │ └── application.yml
│ └── test
│ ├── java
│ └── de
│ │ └── lv1871
│ │ └── oss
│ │ └── dmnmgr
│ │ ├── domain
│ │ └── CheckerFunctionsTest.java
│ │ └── service
│ │ └── AdvancedDmnCheckServiceTest.java
│ └── resources
│ ├── juelfeel.dmn
│ ├── juelfeel.dmnapp.json
│ ├── meineTestDecision.dmn
│ ├── meineTestDecision.dmnapp.json
│ ├── requiredInputExpressionTest.dmn
│ ├── requiredInputExpressionTest.dmnapp.json
│ ├── requiredOutputNameTest.dmn
│ ├── requiredOutputNameTest.dmnapp.json
│ ├── testTokenizedExpression.dmn
│ ├── testTokenizedExpression.dmnapp.json
│ ├── testTokenizedWithErrorExpression.dmn
│ └── testTokenizedWithErrorExpression.dmnapp.json
├── pom.xml
└── readme_assets
└── general_architecture.png
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/maven,eclipse
3 | # Edit at https://www.gitignore.io/?templates=maven,eclipse
4 |
5 | ### Eclipse ###
6 |
7 | .metadata
8 | bin/
9 | tmp/
10 | *.tmp
11 | *.bak
12 | *.swp
13 | *~.nib
14 | local.properties
15 | .settings/
16 | .loadpath
17 | .recommenders
18 |
19 | # External tool builders
20 | .externalToolBuilders/
21 |
22 | # Locally stored "Eclipse launch configurations"
23 | *.launch
24 |
25 | # PyDev specific (Python IDE for Eclipse)
26 | *.pydevproject
27 |
28 | # CDT-specific (C/C++ Development Tooling)
29 | .cproject
30 |
31 | # CDT- autotools
32 | .autotools
33 |
34 | # Java annotation processor (APT)
35 | .factorypath
36 |
37 | # PDT-specific (PHP Development Tools)
38 | .buildpath
39 |
40 | # sbteclipse plugin
41 | .target
42 |
43 | # Tern plugin
44 | .tern-project
45 |
46 | # TeXlipse plugin
47 | .texlipse
48 |
49 | # STS (Spring Tool Suite)
50 | .springBeans
51 |
52 | # Code Recommenders
53 | .recommenders/
54 |
55 | # Annotation Processing
56 | .apt_generated/
57 |
58 | # Scala IDE specific (Scala & Java development for Eclipse)
59 | .cache-main
60 | .scala_dependencies
61 | .worksheet
62 |
63 | ### Eclipse Patch ###
64 | # Eclipse Core
65 | .project
66 |
67 | # JDT-specific (Eclipse Java Development Tools)
68 | .classpath
69 |
70 | # Annotation Processing
71 | .apt_generated
72 |
73 | .sts4-cache/
74 |
75 | ### Maven ###
76 | target/
77 | pom.xml.tag
78 | pom.xml.releaseBackup
79 | pom.xml.versionsBackup
80 | pom.xml.next
81 | release.properties
82 | dependency-reduced-pom.xml
83 | buildNumber.properties
84 | .mvn/timing.properties
85 | .mvn/wrapper/maven-wrapper.jar
86 |
87 | *.log
88 |
89 | db
90 |
91 | settings.json
92 | .vscode/
93 |
94 | log*
95 |
96 | # End of https://www.gitignore.io/api/maven,eclipse
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | https://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | Copyright 2020 David Ibl
180 |
181 | Licensed under the Apache License, Version 2.0 (the "License");
182 | you may not use this file except in compliance with the License.
183 | You may obtain a copy of the License at
184 |
185 | https://www.apache.org/licenses/LICENSE-2.0
186 |
187 | Unless required by applicable law or agreed to in writing, software
188 | distributed under the License is distributed on an "AS IS" BASIS,
189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
190 | See the License for the specific language governing permissions and
191 | limitations under the License.
192 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright 2020 David Ibl
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # dmnmgr-server
2 |
3 | The DMN Manager is a toolkit incoperating the Camunda DMN implementation and providing
4 | tools to develop DMN based applications in cross functional teams.
5 |
6 | ## General architecture
7 |
8 | 
9 |
10 | - The client application is used to visually define business rules, simulate decision models and write tests. (https://github.com/davidibl/dmnmgr-client)
11 | - The server-side application provides web services to simulate decisions, run tests and validate DMN definitions. (This repository)
12 | - The test driver is a java library to run tests defined in the DMN Manager client during CI/CD processes automatically. (This repository)
13 |
14 | ## Use
15 |
16 | The DMN Manager server-side component is a java spring boot based webservice. To run this service, just build the project and run the jar file inside the target folder of the child project 'dmnmgr'.
17 |
18 | To use the test driver, provision any artifactory or any java project with the jar library from the target folder of the 'dmnmgr-test-driver' child project.
19 |
20 | ## Automatic dmn test execution
21 |
22 | To enable automatic test execution during build process of any java project, import a dmn file in the DMN Manager client tool, define tests and save the project into the resources folder of the java project.
23 |
24 | After saving a DMN Manager project a new file with the extension *.dmnapp.json will appear next to the original dmn file. This file contains all the testcases and test expectations.
25 |
26 | Now, just create a Java class with the following annotation:
27 |
28 | `
29 | @RunWith(DmnTester.class)
30 | `
31 |
32 | All testcases defined in the dmnapp.json file will now be executed during normal test phase of the build process.
33 |
34 | ## Customization
35 |
36 | The web service to validate, simulate and test decisions uses a specific version of the Camunda feel-scala engine to fulfill all requests. To ensure production near behaviour during development time it is recommended to build the webservice with a decision engine as near as possible to the production scenario.
37 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 |
5 | de.lv1871.oss
6 | dmnmgr-project
7 | 1.3.0-SNAPSHOT
8 |
9 |
10 | dmnmgr-test-driver
11 | jar
12 |
13 | dmnmgr-test-driver
14 |
15 |
16 |
17 | de.lv1871.oss
18 | dmnmgr-tester
19 | ${project.version}
20 | compile
21 |
22 |
23 | org.camunda.bpm.extension.feel.scala
24 | feel-engine-plugin
25 | provided
26 |
27 |
28 | org.camunda.bpm.extension.feel.scala
29 | feel-engine-factory
30 | ${feel.scala.version}
31 |
32 |
33 | org.camunda.bpm.extension.feel.scala
34 | feel-engine
35 | ${feel.scala.version}
36 |
37 |
38 | junit
39 | junit
40 |
41 |
42 | com.fasterxml.jackson.core
43 | jackson-annotations
44 | provided
45 |
46 |
47 | com.fasterxml.jackson.core
48 | jackson-core
49 | provided
50 |
51 |
52 | com.fasterxml.jackson.core
53 | jackson-databind
54 | provided
55 |
56 |
57 |
58 |
59 |
60 |
61 | org.apache.maven.plugins
62 | maven-assembly-plugin
63 | 3.1.1
64 |
65 |
66 |
67 | jar-with-dependencies
68 |
69 |
70 |
71 |
72 |
73 | make-assembly
74 | package
75 |
76 | single
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/DecisionRunner.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver;
2 |
3 | import com.fasterxml.jackson.databind.node.ObjectNode;
4 |
5 | import de.lv1871.oss.tester.test.dmnassert.model.DecisionSimulationResponse.DecisionTestCaseResponseBuilder;
6 | import de.lv1871.oss.tester.test.domain.DecisionEngine;
7 | import de.lv1871.oss.tester.test.dmnassert.model.DecisionSimulationResponse;
8 |
9 | public class DecisionRunner {
10 |
11 | private static VariableMapperService MAPPER = new VariableMapperService();
12 |
13 | public static DecisionSimulationResponse decide(DecisionEngine engine, String decisionKey,
14 | ObjectNode variablesNode) {
15 |
16 | try {
17 | var variables = MAPPER.getVariablesFromJsonAsMap(variablesNode);
18 |
19 | var decisionResult = engine.evaluateDecisionByKey(decisionKey, variables);
20 |
21 | if (decisionResult
22 | .getResultList()
23 | .stream()
24 | .filter(result -> result.get(null) != null)
25 | .count() > 0) {
26 | return DecisionTestCaseResponseBuilder.create()
27 | .withMessage("Ein oder meherere Output-Felder haben keinen Namen.").build();
28 | }
29 |
30 | return DecisionTestCaseResponseBuilder
31 | .create()
32 | .withResult(decisionResult.getResultList())
33 | .build();
34 |
35 | } catch (Exception exception) {
36 | if (exception.getCause() != null) {
37 | return DecisionTestCaseResponseBuilder
38 | .create()
39 | .withMessage(exception.getCause().getMessage())
40 | .build();
41 | }
42 | return DecisionTestCaseResponseBuilder
43 | .create()
44 | .withMessage(exception.getMessage())
45 | .build();
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/DmnTestCreator.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver;
2 |
3 | import java.io.File;
4 | import java.io.IOException;
5 | import java.nio.charset.StandardCharsets;
6 | import java.nio.file.Files;
7 | import java.nio.file.Path;
8 | import java.nio.file.Paths;
9 | import java.util.ArrayList;
10 | import java.util.Arrays;
11 | import java.util.List;
12 | import java.util.Map.Entry;
13 | import java.util.UUID;
14 | import java.util.stream.Collectors;
15 |
16 | import com.fasterxml.jackson.databind.ObjectMapper;
17 |
18 | import de.lv1871.oss.dmnmgr.test.driver.model.DmnProject;
19 | import de.lv1871.oss.dmnmgr.test.driver.model.DmnProjectTestContainer;
20 | import de.lv1871.oss.dmnmgr.test.driver.model.DmnProjectTestDefinition;
21 | import de.lv1871.oss.dmnmgr.test.driver.model.DmnTest;
22 | import de.lv1871.oss.dmnmgr.test.driver.model.DmnTestSuite;
23 | import de.lv1871.oss.tester.test.domain.DecisionEngine;
24 | import junit.framework.Test;
25 | import junit.framework.TestSuite;
26 |
27 | public class DmnTestCreator {
28 |
29 | private static String PROJECT_FILE_EXTENSION = "dmnapp.json";
30 | private static ObjectMapper MAPPER = new ObjectMapper();
31 |
32 | public static Test suite() {
33 | var suite = new TestSuite();
34 | new DmnTestCreator().createDmnTests(null).forEach(suite::addTest);
35 | return suite;
36 | }
37 |
38 | public static Test getSuiteParameterized(Path basePath) {
39 | var suite = new TestSuite();
40 | new DmnTestCreator().createDmnTests(basePath).forEach(suite::addTest);
41 | return suite;
42 | }
43 |
44 | public List createDmnTests(Path basePath) {
45 | return findTests(basePath)
46 | .stream()
47 | .map(this::toJunitTestSuite)
48 | .collect(Collectors.toList());
49 | }
50 |
51 | public List findTests(Path basePath) {
52 | return getResourceFiles(basePath)
53 | .stream()
54 | .filter(file -> file.endsWith(PROJECT_FILE_EXTENSION))
55 | .map(file -> Paths.get(file).toString())
56 | .map(this::loadTests)
57 | .collect(Collectors.toList());
58 | }
59 |
60 | private DmnTestSuite loadTests(String projectFilePath) {
61 | try {
62 | var projectFile = new File(projectFilePath);
63 |
64 | var dmnProject = MAPPER.readValue(new File(projectFilePath), DmnProject.class);
65 | var suite = new DmnTestSuite();
66 |
67 | suite.setTest(dmnProject.getTestsuite()
68 | .entrySet()
69 | .stream()
70 | .map(this::toDmnTest)
71 | .flatMap(List::stream)
72 | .collect(Collectors.toList()));
73 |
74 | suite.setXml(readXmlFile(projectFile, dmnProject.getDmnPath()));
75 | suite.setName(dmnProject.getDmnPath());
76 | return suite;
77 | } catch (Exception e) {
78 | throw new RuntimeException(e);
79 | }
80 | }
81 |
82 | private String readXmlFile(File projectFile, String dmnPath) throws IOException {
83 |
84 | var dmnFilePath = getResourceRelativToBasePath(projectFile.getParentFile(), dmnPath);
85 |
86 | var xmlFile = Paths.get(dmnFilePath);
87 | return new String(Files.readAllBytes(xmlFile), StandardCharsets.UTF_8);
88 | }
89 |
90 | private List toDmnTest(Entry testsRaw) {
91 | return testsRaw
92 | .getValue()
93 | .getTests()
94 | .stream()
95 | .map(testRaw -> this.testRawToTest(testRaw, testsRaw.getKey()))
96 | .collect(Collectors.toList());
97 | }
98 |
99 | private DmnTest testRawToTest(DmnProjectTestDefinition testRaw, String tableId) {
100 | var test = new DmnTest();
101 | test.setData(testRaw.getData());
102 | test.setExpectedData(testRaw.getExpectedData());
103 | var testName = (testRaw.getName() != null && !testRaw.getName().trim().equals("")) ? testRaw.getName()
104 | : UUID.randomUUID().toString();
105 | test.setName(testName);
106 | test.setTableId(tableId);
107 | return test;
108 | }
109 |
110 | private TestSuite toJunitTestSuite(DmnTestSuite dmnSuite) {
111 | var suite = new TestSuite(dmnSuite.getName());
112 |
113 | var processEngine = DecisionEngine.createEngine().parseDecision(dmnSuite.getXml());
114 |
115 | dmnSuite
116 | .getTest()
117 | .stream()
118 | .collect(Collectors.groupingBy(DmnTest::getTableId))
119 | .entrySet()
120 | .stream()
121 | .map(entry -> this.entrySetToJunitTestSuite(entry, processEngine))
122 | .forEach(suite::addTest);
123 | return suite;
124 | }
125 |
126 | private TestSuite entrySetToJunitTestSuite(Entry> entry, DecisionEngine engine) {
127 | TestSuite suite = new TestSuite(entry.getKey());
128 | entry
129 | .getValue()
130 | .stream()
131 | .map(dmnTest -> new DmnTestExecutor(dmnTest, engine))
132 | .forEach(suite::addTest);
133 | return suite;
134 | }
135 |
136 | private String getResourceRelativToBasePath(File baseDirectory, String filename) {
137 | return getResources(baseDirectory)
138 | .stream()
139 | .filter(file -> file.endsWith(filename))
140 | .findFirst()
141 | .orElseThrow(() -> new RuntimeException(String.format("DMN File \"%s\" not found", filename)));
142 | }
143 |
144 | private List getResourceFiles(Path basePath) {
145 | if (basePath != null) {
146 | return getDmnProjectFilesByPath(basePath);
147 | }
148 |
149 | var files = new ArrayList();
150 | files.addAll(getResources(Paths.get("src", "main", "resources").toFile()));
151 | files.addAll(getResources(Paths.get("src", "test", "resources").toFile()));
152 | return files;
153 | }
154 |
155 | private List getDmnProjectFilesByPath(Path basePath) {
156 | if (basePath.toFile().isFile()) {
157 | return Arrays.asList(basePath.toFile().getAbsolutePath());
158 | }
159 |
160 | return getResourceFiles(basePath);
161 | }
162 |
163 | private List getResources(File base) {
164 | if (base == null || !base.exists() || !base.isDirectory()) {
165 | return new ArrayList<>();
166 | }
167 |
168 | var files = Arrays.asList(base.list())
169 | .stream()
170 | .map(file -> Paths.get(base.getPath(), file).toString())
171 | .collect(Collectors.toList());
172 |
173 | files.addAll(files.stream()
174 | .filter(path -> Files.isDirectory(Paths.get(path)))
175 | .map(File::new)
176 | .map(this::getResources)
177 | .flatMap(List::stream)
178 | .collect(Collectors.toList()));
179 |
180 | return files;
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/DmnTestExecutor.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver;
2 |
3 | import java.util.ArrayList;
4 | import java.util.HashMap;
5 | import java.util.List;
6 | import java.util.Map.Entry;
7 |
8 | import org.junit.Assert;
9 |
10 | import com.fasterxml.jackson.databind.node.ObjectNode;
11 |
12 | import de.lv1871.oss.dmnmgr.test.driver.model.DmnTest;
13 | import junit.framework.TestCase;
14 | import static de.lv1871.oss.tester.test.dmnassert.DmnTest.testExpectation;
15 | import de.lv1871.oss.tester.test.dmnassert.model.DecisionSimulationResponse;
16 | import de.lv1871.oss.tester.test.domain.DecisionEngine;
17 |
18 | public class DmnTestExecutor extends TestCase {
19 |
20 | private static String ASSERTION_ERROR_MESSAGE_TEMPLATE =
21 | "\n--------------------------------------------\n" +
22 | "Expected Data:\n" +
23 | "%s\n\n" +
24 | "--------------------------------------------\n" +
25 | "Result:\n" +
26 | "%s";
27 |
28 | private static VariableMapperService MAPPER = new VariableMapperService();
29 |
30 | private final DmnTest test;
31 | private DecisionEngine dmnEngine;
32 |
33 | public DmnTestExecutor(DmnTest test, DecisionEngine engine) {
34 | super(test.getName());
35 | this.test = test;
36 | this.dmnEngine = engine;
37 | }
38 |
39 | protected void runTest() throws Throwable {
40 | testDecision();
41 | }
42 |
43 | public void testDecision() {
44 | var decisionSimulationResponse = DecisionRunner.decide(
45 | dmnEngine,
46 | this.test.getTableId(),
47 | this.test.getData());
48 |
49 | if (decisionSimulationResponse.getResult() == null) {
50 | fail(decisionSimulationResponse.getMessage());
51 | }
52 |
53 | try {
54 |
55 | List> expectedDataAssertionFailed = new ArrayList<>();
56 |
57 | for (ObjectNode expectedNode : this.test.getExpectedData()) {
58 | HashMap expectedObjectMap = MAPPER.getVariablesFromJsonAsMap(expectedNode);
59 | expectedDataAssertionFailed.addAll(testExpectation(decisionSimulationResponse, expectedObjectMap));
60 | }
61 |
62 | var testFailed = expectedDataAssertionFailed.size() > 0;
63 |
64 | var resultMessage = testFailed ? getFailureMessage(decisionSimulationResponse) : "";
65 |
66 | Assert.assertFalse(resultMessage, testFailed);
67 |
68 | } catch (Exception e) {
69 | fail(e.getMessage());
70 | }
71 | }
72 |
73 | private String getFailureMessage(DecisionSimulationResponse decisionSimulationResponse) {
74 | var expectedData = MAPPER.writeJson(this.test.getExpectedData());
75 | var result = MAPPER.writeJson(decisionSimulationResponse.getResult());
76 |
77 | return decisionSimulationResponse.getMessage() == null
78 | ? String.format(ASSERTION_ERROR_MESSAGE_TEMPLATE, expectedData, result)
79 | : decisionSimulationResponse.getMessage();
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/DmnTester.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver;
2 |
3 | import org.junit.runners.AllTests;
4 |
5 | public class DmnTester extends AllTests {
6 |
7 | public DmnTester(Class> klass) throws Throwable {
8 | super(DmnTestCreator.class);
9 | }
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/VariableMapperService.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver;
2 |
3 | import java.io.IOException;
4 | import java.util.HashMap;
5 |
6 | import com.fasterxml.jackson.core.JsonProcessingException;
7 | import com.fasterxml.jackson.databind.ObjectMapper;
8 | import com.fasterxml.jackson.databind.SerializationFeature;
9 | import com.fasterxml.jackson.databind.node.ObjectNode;
10 |
11 | public class VariableMapperService {
12 |
13 | private static ObjectMapper MAPPER;
14 |
15 | public VariableMapperService() {
16 | this.initMapper();
17 | }
18 |
19 | @SuppressWarnings("unchecked")
20 | public HashMap getVariablesFromJsonAsMap(ObjectNode json) {
21 | var valueAsString = json.toString();
22 | try {
23 | return MAPPER.readValue(valueAsString, HashMap.class);
24 | } catch (IOException e) {
25 | throw new RuntimeException(e.getMessage());
26 | }
27 | }
28 |
29 | public String writeJson(Object value) {
30 | try {
31 | return MAPPER.writeValueAsString(value);
32 | } catch (JsonProcessingException e) {
33 | throw new RuntimeException(e);
34 | }
35 | }
36 |
37 | private void initMapper() {
38 | MAPPER = new ObjectMapper();
39 | MAPPER.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/model/DecisionSimulationResponse.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver.model;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 |
6 | public class DecisionSimulationResponse {
7 |
8 | private final String message;
9 | private final List> result;
10 |
11 | private DecisionSimulationResponse(String message, List> result) {
12 | this.message = message;
13 | this.result = result;
14 | }
15 |
16 | public List> getResult() {
17 | return result;
18 | }
19 |
20 | public String getMessage() {
21 | return message;
22 | }
23 |
24 | public static class DecisionTestCaseResponseBuilder {
25 |
26 | private String message = null;
27 | private List> result = null;
28 |
29 | public static DecisionTestCaseResponseBuilder create() {
30 | return new DecisionTestCaseResponseBuilder();
31 | }
32 |
33 | public DecisionTestCaseResponseBuilder withMessage(String message) {
34 | this.message = message;
35 | return this;
36 | }
37 |
38 | public DecisionTestCaseResponseBuilder withResult(List> result) {
39 | this.result = result;
40 | return this;
41 | }
42 |
43 | public DecisionSimulationResponse build() {
44 | return new DecisionSimulationResponse(message, result);
45 | }
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/model/DmnProject.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver.model;
2 |
3 | import java.util.Map;
4 |
5 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
6 |
7 | @JsonIgnoreProperties(ignoreUnknown = true)
8 | public class DmnProject {
9 |
10 | private String dmnPath;
11 | private Map testsuite;
12 |
13 | public String getDmnPath() {
14 | return dmnPath;
15 | }
16 |
17 | public void setDmnPath(String dmnPath) {
18 | this.dmnPath = dmnPath;
19 | }
20 |
21 | public Map getTestsuite() {
22 | return testsuite;
23 | }
24 |
25 | public void setTestsuite(Map testsuite) {
26 | this.testsuite = testsuite;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/model/DmnProjectTestContainer.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver.model;
2 |
3 | import java.util.List;
4 |
5 | public class DmnProjectTestContainer {
6 |
7 | private List tests;
8 |
9 | public List getTests() {
10 | return tests;
11 | }
12 |
13 | public void setTests(List tests) {
14 | this.tests = tests;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/model/DmnProjectTestDefinition.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver.model;
2 |
3 | import java.util.List;
4 |
5 | import com.fasterxml.jackson.databind.node.ObjectNode;
6 |
7 | public class DmnProjectTestDefinition {
8 |
9 | private String name;
10 | private ObjectNode data;
11 | private List expectedData;
12 |
13 | public String getName() {
14 | return name;
15 | }
16 |
17 | public void setName(String name) {
18 | this.name = name;
19 | }
20 |
21 | public ObjectNode getData() {
22 | return data;
23 | }
24 |
25 | public void setData(ObjectNode data) {
26 | this.data = data;
27 | }
28 |
29 | public List getExpectedData() {
30 | return expectedData;
31 | }
32 |
33 | public void setExpectedData(List expectedData) {
34 | this.expectedData = expectedData;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/model/DmnTest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver.model;
2 |
3 | import java.util.List;
4 |
5 | import com.fasterxml.jackson.databind.node.ObjectNode;
6 |
7 | public class DmnTest {
8 |
9 | private String name;
10 | private String tableId;
11 | private ObjectNode data;
12 | private List expectedData;
13 |
14 | public List getExpectedData() {
15 | return expectedData;
16 | }
17 |
18 | public void setExpectedData(List expectedData) {
19 | this.expectedData = expectedData;
20 | }
21 |
22 | public ObjectNode getData() {
23 | return data;
24 | }
25 |
26 | public void setData(ObjectNode data) {
27 | this.data = data;
28 | }
29 |
30 | public String getName() {
31 | return name;
32 | }
33 |
34 | public void setName(String name) {
35 | this.name = name;
36 | }
37 |
38 | public String getTableId() {
39 | return tableId;
40 | }
41 |
42 | public void setTableId(String tableId) {
43 | this.tableId = tableId;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/model/DmnTestSuite.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver.model;
2 |
3 | import java.util.List;
4 |
5 | public class DmnTestSuite {
6 |
7 | private String xml;
8 | private String name;
9 | private List test;
10 |
11 | public List getTest() {
12 | return test;
13 | }
14 |
15 | public void setTest(List test) {
16 | this.test = test;
17 | }
18 |
19 | public String getXml() {
20 | return xml;
21 | }
22 |
23 | public void setXml(String xml) {
24 | this.xml = xml;
25 | }
26 |
27 | public String getName() {
28 | return name;
29 | }
30 |
31 | public void setName(String name) {
32 | this.name = name;
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/main/java/de/lv1871/oss/dmnmgr/test/driver/model/TestSuiteTestResult.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver.model;
2 |
3 | public class TestSuiteTestResult {
4 |
5 | private Boolean testFailed;
6 |
7 | public Boolean getTestFailed() {
8 | return testFailed;
9 | }
10 |
11 | public void setTestFailed(Boolean testFailed) {
12 | this.testFailed = testFailed;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/test/java/de/lv1871/oss/dmnmgr/test/driver/IntegrationTest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertTrue;
5 |
6 | import java.nio.file.Paths;
7 |
8 | import org.junit.Test;
9 |
10 | import junit.framework.TestResult;
11 |
12 | public class IntegrationTest {
13 |
14 | // @formatter:off
15 | private static final String ASSERTION_ERROR_TEST_A =
16 | "--------------------------------------------\n" +
17 | "Expected Data:\n" +
18 | "[{\"res\":\"We\"}]\n" +
19 | "\n" +
20 | "--------------------------------------------\n" +
21 | "Result:\n" +
22 | "[{\"res\":\"Welt\"}]";
23 | // @formatter:on
24 |
25 | @Test
26 | public void testAssertFailesWhenDataNotEqual() {
27 | junit.framework.Test testsuite = DmnTestCreator
28 | .getSuiteParameterized(Paths.get("src", "test", "resources", "a_test.dmnapp.json"));
29 | TestResult result = new TestResult();
30 | testsuite.run(result);
31 |
32 | assertEquals(1, result.errorCount());
33 | assertTrue(result.errors().nextElement().exceptionMessage().contains(ASSERTION_ERROR_TEST_A));
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/test/java/de/lv1871/oss/dmnmgr/test/driver/VariableMapperServiceTest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.test.driver;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertTrue;
5 |
6 | import java.math.BigDecimal;
7 | import java.text.ParseException;
8 | import java.text.SimpleDateFormat;
9 | import java.util.Date;
10 | import java.util.HashMap;
11 |
12 | import org.junit.Test;
13 |
14 | import com.fasterxml.jackson.databind.node.JsonNodeFactory;
15 | import com.fasterxml.jackson.databind.node.ObjectNode;
16 |
17 | public class VariableMapperServiceTest {
18 |
19 | private VariableMapperService cut = new VariableMapperService();
20 |
21 | @Test
22 | public void testDeserializesObjectNodeToMapCorrect() {
23 | ObjectNode node = new ObjectNode(JsonNodeFactory.instance);
24 | node.put("testDatum", "2019-12-20T12:12:00Z");
25 | node.put("Hallo", "Welt");
26 | node.put("zahl", 1);
27 | node.put("bool", true);
28 |
29 | HashMap jsonAsMap = cut.getVariablesFromJsonAsMap(node);
30 |
31 | assertEquals("Welt", jsonAsMap.get("Hallo"));
32 | assertEquals("2019-12-20T12:12:00Z", jsonAsMap.get("testDatum"));
33 | assertEquals(1, jsonAsMap.get("zahl"));
34 | assertTrue((boolean) jsonAsMap.get("bool"));
35 | }
36 |
37 | @Test
38 | public void testSerializesDatesAsJavascriptDates() {
39 | TestObject testObject = new TestObject(createDateFromString("2012-12-20:12"), BigDecimal.valueOf(12.12));
40 |
41 | String jsonString = cut.writeJson(testObject);
42 |
43 | assertTrue("Big Decimal fehlerhaft " + jsonString, jsonString.contains("\"zahl\":12.12"));
44 | assertTrue("Datum fehlerhaft " + jsonString, jsonString.contains("\"datum\":\"2012-12-20T11:00:00.000+0000\""));
45 | }
46 |
47 | private Date createDateFromString(String dateString) {
48 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd:HH");
49 | try {
50 | return format.parse(dateString);
51 | } catch (ParseException e) {
52 | throw new RuntimeException(e);
53 | }
54 | }
55 |
56 | public static class TestObject {
57 |
58 | private Date datum;
59 | private BigDecimal zahl;
60 |
61 | public TestObject(Date datum, BigDecimal zahl) {
62 | this.setDatum(datum);
63 | this.setZahl(zahl);
64 | }
65 |
66 | public Date getDatum() {
67 | return datum;
68 | }
69 |
70 | public void setDatum(Date datum) {
71 | this.datum = datum;
72 | }
73 |
74 | public BigDecimal getZahl() {
75 | return zahl;
76 | }
77 |
78 | public void setZahl(BigDecimal zahl) {
79 | this.zahl = zahl;
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/test/resources/a_test.dmn:
--------------------------------------------------------------------------------
1 |
2 | test "Hallo" "Welt"
--------------------------------------------------------------------------------
/dmnmgr-test-driver/src/test/resources/a_test.dmnapp.json:
--------------------------------------------------------------------------------
1 | {"dmnPath":"a_test.dmn","testsuite":{"decision":{"tests":[{"name":"Test A","data":{"test":"Hallo"},"expectedData":[{"res":"Welt"}]},{"name":"Test B","data":{"test":"Hallo"},"expectedData":[{"res":"We"}]}]}},"definitions":{"decision":{"requestModel":{"type":"object","properties":[{"type":"string","name":"test"}]}}}}
--------------------------------------------------------------------------------
/dmnmgr-tester/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 |
5 | de.lv1871.oss
6 | dmnmgr-project
7 | 1.3.0-SNAPSHOT
8 |
9 |
10 | dmnmgr-tester
11 | jar
12 |
13 | dmnmgr-tester
14 |
15 |
16 |
17 | org.camunda.bpm.dmn
18 | camunda-engine-dmn
19 |
20 |
21 | org.camunda.bpm.extension.feel.scala
22 | feel-engine-plugin
23 |
24 |
25 | junit
26 | junit
27 |
28 |
29 | com.fasterxml.jackson.core
30 | jackson-annotations
31 |
32 |
33 | com.fasterxml.jackson.core
34 | jackson-core
35 |
36 |
37 | com.fasterxml.jackson.core
38 | jackson-databind
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/dmnmgr-tester/src/main/java/de/lv1871/oss/tester/test/dmnassert/DmnAssert.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.dmnassert;
2 |
3 | import java.util.Map.Entry;
4 |
5 | import de.lv1871.oss.tester.test.dmnassert.model.DecisionSimulationResponse;
6 | import static de.lv1871.oss.tester.test.dmnassert.DmnVariableAssert.equal;
7 |
8 | public class DmnAssert {
9 |
10 | public static > boolean assertEqual(
11 | final DecisionSimulationResponse decisionSimulationResponse,
12 | final T expectedEntry
13 | ) {
14 |
15 | if (expectedEntry.getValue() == null) {
16 | return decisionSimulationResponse
17 | .getResult()
18 | .stream()
19 | .flatMap(map -> map.entrySet().stream())
20 | .filter(entry -> entry.getKey() == expectedEntry.getKey() && entry.getValue() != null)
21 | .count() < 1;
22 | }
23 |
24 | return decisionSimulationResponse
25 | .getResult()
26 | .stream()
27 | .flatMap(map -> map.entrySet().stream())
28 | .filter(resultEntry -> equal(resultEntry, expectedEntry))
29 | .count() >= 1;
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/dmnmgr-tester/src/main/java/de/lv1871/oss/tester/test/dmnassert/DmnTest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.dmnassert;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 | import java.util.Map.Entry;
6 | import java.util.stream.Collectors;
7 |
8 | import de.lv1871.oss.tester.test.dmnassert.model.DecisionSimulationResponse;
9 | import de.lv1871.oss.tester.test.function.ExtendedBiPredicate;
10 |
11 | public class DmnTest {
12 |
13 | public static List> testExpectation(
14 | DecisionSimulationResponse decisionSimulationResponse,
15 | Map expectedMap
16 | ) {
17 |
18 | return expectedMap.entrySet()
19 | .stream()
20 | .filter(assertEntry.curryWith(decisionSimulationResponse).negate())
21 | .collect(Collectors.toList());
22 | }
23 |
24 | private static ExtendedBiPredicate> assertEntry =
25 | DmnAssert::assertEqual;
26 | }
--------------------------------------------------------------------------------
/dmnmgr-tester/src/main/java/de/lv1871/oss/tester/test/dmnassert/DmnVariableAssert.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.dmnassert;
2 |
3 | import java.math.BigDecimal;
4 | import java.util.Map.Entry;
5 |
6 | public class DmnVariableAssert {
7 |
8 | public static boolean equal(Entry resultEntry, Entry expectedEntry) {
9 | if (!resultEntry.getKey().equals(expectedEntry.getKey())) {
10 | return false;
11 | }
12 | return equalValue(resultEntry.getValue(), expectedEntry.getValue());
13 | }
14 |
15 | public static boolean equalValue(Object result, Object expected) {
16 | if (result == null && expected == null) {
17 | return true;
18 | }
19 | if (result == null || expected == null) {
20 | return false;
21 | }
22 | if (result instanceof Number && expected instanceof Number) {
23 | return numbersEqual((Number) result, (Number) expected);
24 | }
25 | return result.equals(expected);
26 | }
27 |
28 | public static boolean numbersEqual(Number n1, Number n2) {
29 | var b1 = new BigDecimal(n1.doubleValue());
30 | var b2 = new BigDecimal(n2.doubleValue());
31 | return b1.compareTo(b2) == 0;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/dmnmgr-tester/src/main/java/de/lv1871/oss/tester/test/dmnassert/model/DecisionSimulationResponse.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.dmnassert.model;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 |
6 | public class DecisionSimulationResponse {
7 |
8 | private final String message;
9 | private final List> result;
10 | private final List resultRuleIds;
11 | private final Map> resultTableRuleIds;
12 |
13 | private DecisionSimulationResponse(String message, List> result, List resultRuleIds,
14 | Map> resultTableRuleIds) {
15 | this.message = message;
16 | this.result = result;
17 | this.resultRuleIds = resultRuleIds;
18 | this.resultTableRuleIds = resultTableRuleIds;
19 | }
20 |
21 | public Map> getResultTableRuleIds() {
22 | return resultTableRuleIds;
23 | }
24 |
25 | public List> getResult() {
26 | return result;
27 | }
28 |
29 | public String getMessage() {
30 | return message;
31 | }
32 |
33 | public List getResultRuleIds() {
34 | return resultRuleIds;
35 | }
36 |
37 | public static class DecisionTestCaseResponseBuilder {
38 |
39 | private String message = null;
40 | private List> result = null;
41 | private List resultRuleIds = null;
42 | private Map> resultTableRuleIds = null;
43 |
44 | public static DecisionTestCaseResponseBuilder create() {
45 | return new DecisionTestCaseResponseBuilder();
46 | }
47 |
48 | public DecisionTestCaseResponseBuilder withMessage(String message) {
49 | this.message = message;
50 | return this;
51 | }
52 |
53 | public DecisionTestCaseResponseBuilder withResult(List> result) {
54 | this.result = result;
55 | return this;
56 | }
57 |
58 | public DecisionSimulationResponse build() {
59 | return new DecisionSimulationResponse(message, result, resultRuleIds, resultTableRuleIds);
60 | }
61 |
62 | public DecisionTestCaseResponseBuilder withResultRuleIds(List resultRuleIds) {
63 | this.resultRuleIds = resultRuleIds;
64 | return this;
65 | }
66 |
67 | public DecisionTestCaseResponseBuilder withResultTableRuleIds(Map> resultTableRuleIds) {
68 | this.resultTableRuleIds = resultTableRuleIds;
69 | return this;
70 | }
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/dmnmgr-tester/src/main/java/de/lv1871/oss/tester/test/domain/DecisionEngine.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.domain;
2 |
3 | import java.io.ByteArrayInputStream;
4 | import java.nio.charset.StandardCharsets;
5 | import java.util.ArrayList;
6 | import java.util.Arrays;
7 | import java.util.HashMap;
8 | import java.util.List;
9 | import java.util.Map;
10 | import java.util.Map.Entry;
11 | import java.util.stream.Collectors;
12 |
13 | import org.camunda.bpm.dmn.engine.DmnDecision;
14 | import org.camunda.bpm.dmn.engine.DmnDecisionRequirementsGraph;
15 | import org.camunda.bpm.dmn.engine.DmnDecisionTableResult;
16 | import org.camunda.bpm.dmn.engine.DmnEngine;
17 | import org.camunda.bpm.dmn.engine.DmnEngineConfiguration;
18 | import org.camunda.bpm.dmn.engine.delegate.DmnDecisionTableEvaluationEvent;
19 | import org.camunda.bpm.dmn.engine.delegate.DmnDecisionTableEvaluationListener;
20 | import org.camunda.bpm.dmn.engine.delegate.DmnEvaluatedDecisionRule;
21 | import org.camunda.bpm.dmn.engine.impl.DefaultDmnEngineConfiguration;
22 | import org.camunda.feel.integration.CamundaFeelEngineFactory;
23 |
24 | public class DecisionEngine {
25 |
26 | private DmnEngine dmnEngine;
27 | private Map decisions;
28 | private Map decisionRequirementsGraphs;
29 | private MyTableEvaluationListener tableEvaluationListener = new MyTableEvaluationListener();
30 |
31 | public static DecisionEngine createEngine() {
32 | var engine = new DecisionEngine();
33 | engine.init();
34 | return engine;
35 | }
36 |
37 | private DecisionEngine() {}
38 |
39 | public DecisionEngine parseDecision(String dmnXml) {
40 | var drg = dmnEngine
41 | .parseDecisionRequirementsGraph(new ByteArrayInputStream(dmnXml.getBytes(StandardCharsets.UTF_8)));
42 | decisionRequirementsGraphs.put(drg.getKey(), drg);
43 | drg.getDecisionKeys().stream().forEach(key -> {
44 | this.decisions.put(key, drg.getDecision(key));
45 | });
46 | return this;
47 | }
48 |
49 | public DmnDecisionTableResult evaluateDecisionByKey(String key, Map variables) {
50 | return dmnEngine.evaluateDecisionTable(getDecisionByKey(key), variables);
51 | }
52 |
53 | public List getDecisionKeyByDrgKey(String key) {
54 | return decisionRequirementsGraphs.get(key).getDecisionKeys().stream().collect(Collectors.toList());
55 | }
56 |
57 | public List getResultRules(String dmnTableId) {
58 | return tableEvaluationListener.getLatestMatchedRules(dmnTableId);
59 | }
60 |
61 | public Map> getResultRulesAllTables() {
62 | return tableEvaluationListener.getLatestMatchedRulesToAllMatchedTables();
63 | }
64 |
65 | private void init() {
66 |
67 | decisions = new HashMap<>();
68 | decisionRequirementsGraphs = new HashMap<>();
69 |
70 | var dmnEngineConfig = (DefaultDmnEngineConfiguration) DmnEngineConfiguration
71 | .createDefaultDmnEngineConfiguration();
72 | dmnEngineConfig.setDefaultInputEntryExpressionLanguage("feel-scala-unary-tests");
73 | dmnEngineConfig.setDefaultOutputEntryExpressionLanguage("feel-scala");
74 | dmnEngineConfig.setDefaultLiteralExpressionLanguage("feel-scala");
75 |
76 | dmnEngineConfig.customPostDecisionTableEvaluationListeners(
77 | Arrays.asList(tableEvaluationListener)
78 | );
79 |
80 | dmnEngine = dmnEngineConfig.feelEngineFactory(new CamundaFeelEngineFactory()).buildEngine();
81 | }
82 |
83 | private static class MyTableEvaluationListener implements DmnDecisionTableEvaluationListener {
84 |
85 | private Map> latestMatchedRules = new HashMap<>();
86 |
87 | @Override
88 | public void notify(DmnDecisionTableEvaluationEvent evaluationEvent) {
89 | latestMatchedRules.put(
90 | evaluationEvent.getDecisionTable().getKey(),
91 | evaluationEvent.getMatchingRules()
92 | .stream()
93 | .map(DmnEvaluatedDecisionRule::getId)
94 | .collect(Collectors.toList())
95 | );
96 | }
97 |
98 | public List getLatestMatchedRules(String tableId) {
99 | if (!latestMatchedRules.containsKey(tableId)) {
100 | return new ArrayList<>();
101 | }
102 | return latestMatchedRules.get(tableId);
103 | }
104 |
105 | public Map> getLatestMatchedRulesToAllMatchedTables() {
106 | return latestMatchedRules
107 | .entrySet()
108 | .stream()
109 | .filter(matchedTable -> matchedTable.getValue().size() > 0)
110 | .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
111 | }
112 |
113 | }
114 |
115 | public DmnDecision getDecisionByKey(String key) {
116 | assertDecisionAvailable(key);
117 | return decisions.get(key);
118 | }
119 |
120 | private void assertDecisionAvailable(String key) {
121 | if (!decisions.containsKey(key)) {
122 | throw new IllegalArgumentException(String.format("Decision with key %s not found.", key));
123 | }
124 | }
125 |
126 | }
--------------------------------------------------------------------------------
/dmnmgr-tester/src/main/java/de/lv1871/oss/tester/test/function/ExtendedBiFunction.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.function;
2 |
3 | import java.util.function.BiFunction;
4 | import java.util.function.Function;
5 |
6 | @FunctionalInterface
7 | public interface ExtendedBiFunction extends BiFunction {
8 |
9 | default Function> curry() {
10 | return a -> b -> this.apply(a, b);
11 | }
12 |
13 | default Function curryWith(T value) {
14 | return curry().apply(value);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/dmnmgr-tester/src/main/java/de/lv1871/oss/tester/test/function/ExtendedBiPredicate.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.function;
2 |
3 | import java.util.function.BiPredicate;
4 | import java.util.function.Function;
5 | import java.util.function.Predicate;
6 |
7 | @FunctionalInterface
8 | public interface ExtendedBiPredicate extends BiPredicate {
9 |
10 | default Function> curry() {
11 | return a -> b -> this.test(a, b);
12 | }
13 |
14 | default Predicate curryWith(T value) {
15 | return curry().apply(value);
16 | }
17 | }
--------------------------------------------------------------------------------
/dmnmgr-tester/src/main/java/de/lv1871/oss/tester/test/function/LambdaExtension.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.function;
2 |
3 | import java.util.function.Function;
4 | import java.util.function.Predicate;
5 |
6 | public class LambdaExtension {
7 |
8 | public static Predicate notNull(Function supplier) {
9 | return (value) -> notNull().test(supplier.apply(value));
10 | }
11 |
12 | public static Predicate notNull() {
13 | return (value) -> value != null;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/dmnmgr-tester/src/test/java/de/lv1871/oss/tester/test/dmnassert/DmnAssertTest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.dmnassert;
2 |
3 | import static org.junit.Assert.assertFalse;
4 | import static org.junit.Assert.assertTrue;
5 |
6 | import java.util.AbstractMap;
7 | import java.util.Arrays;
8 | import java.util.HashMap;
9 | import java.util.List;
10 | import java.util.Map;
11 | import java.util.Map.Entry;
12 |
13 | import org.junit.Test;
14 |
15 | import de.lv1871.oss.tester.test.dmnassert.model.DecisionSimulationResponse;
16 |
17 | public class DmnAssertTest {
18 |
19 | private static final int VALUE_3 = 42;
20 | private static final double VALUE_2 = 2.23;
21 | private static final int VALUE_1 = 1;
22 | private static final String FIELDNAME = "result";
23 |
24 | @Test
25 | public void assertsFoundEntryAsEqual() {
26 | DecisionSimulationResponse decisionSimulationResponse = createDecisionResult(Arrays.asList(
27 | createResult(FIELDNAME, VALUE_1),
28 | createResult(FIELDNAME, VALUE_2)));
29 |
30 | boolean assertionResult = DmnAssert.assertEqual(decisionSimulationResponse, getEntry(FIELDNAME, VALUE_1));
31 |
32 | assertTrue(assertionResult);
33 | }
34 |
35 | @Test
36 | public void assertsNullNotEqualSomeExpectedNumber() {
37 | DecisionSimulationResponse decisionSimulationResponse = createDecisionResult(Arrays.asList(
38 | createResult(FIELDNAME, null)));
39 |
40 | boolean assertionResult = DmnAssert.assertEqual(decisionSimulationResponse, getEntry(FIELDNAME, VALUE_1));
41 |
42 | assertFalse(assertionResult);
43 | }
44 |
45 | @Test
46 | public void assertsNullNotEqualSomeFoundNumber() {
47 | DecisionSimulationResponse decisionSimulationResponse = createDecisionResult(Arrays.asList(
48 | createResult(FIELDNAME, VALUE_1)));
49 |
50 | boolean assertionResult = DmnAssert.assertEqual(decisionSimulationResponse, getEntry(FIELDNAME, null));
51 |
52 | assertFalse(assertionResult);
53 | }
54 |
55 | @Test
56 | public void assertsNullValueEqualNoFoundNumber() {
57 | DecisionSimulationResponse decisionSimulationResponse = createDecisionResult(Arrays.asList());
58 |
59 | boolean assertionResult = DmnAssert.assertEqual(decisionSimulationResponse, getEntry(FIELDNAME, null));
60 |
61 | assertTrue(assertionResult);
62 | }
63 |
64 | @Test
65 | public void assertsNumberValueNotEqualNoFoundNumber() {
66 | DecisionSimulationResponse decisionSimulationResponse = createDecisionResult(Arrays.asList());
67 |
68 | boolean assertionResult = DmnAssert.assertEqual(decisionSimulationResponse, getEntry(FIELDNAME, VALUE_1));
69 |
70 | assertFalse(assertionResult);
71 | }
72 |
73 | @Test
74 | public void assertsEntryNotFoundAsNotEqual() {
75 | DecisionSimulationResponse decisionSimulationResponse = createDecisionResult(Arrays.asList(
76 | createResult(FIELDNAME, VALUE_1),
77 | createResult(FIELDNAME, VALUE_2)));
78 |
79 | boolean assertionResult = DmnAssert.assertEqual(decisionSimulationResponse, getEntry(FIELDNAME, VALUE_3));
80 |
81 | assertFalse(assertionResult);
82 | }
83 |
84 | @Test
85 | public void assertsEntryFoundTwiceAsEqual() {
86 | DecisionSimulationResponse decisionSimulationResponse = createDecisionResult(Arrays.asList(
87 | createResult(FIELDNAME, VALUE_1),
88 | createResult(FIELDNAME, VALUE_2),
89 | createResult(FIELDNAME, VALUE_2)));
90 |
91 | boolean assertionResult = DmnAssert.assertEqual(decisionSimulationResponse, getEntry(FIELDNAME, VALUE_2));
92 |
93 | assertTrue(assertionResult);
94 | }
95 |
96 | private DecisionSimulationResponse createDecisionResult(List> result) {
97 | return DecisionSimulationResponse.DecisionTestCaseResponseBuilder
98 | .create()
99 | .withResult(result)
100 | .build();
101 | }
102 |
103 | private Map createResult(String key, Object value) {
104 | Map row = new HashMap<>();
105 | row.put(key, value);
106 | return row;
107 | }
108 |
109 | private Entry getEntry(String key, Object value) {
110 | return new AbstractMap.SimpleEntry(key, value);
111 | }
112 |
113 | }
--------------------------------------------------------------------------------
/dmnmgr-tester/src/test/java/de/lv1871/oss/tester/test/dmnassert/DmnTestTest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.dmnassert;
2 |
3 | import static org.junit.Assert.assertEquals;
4 |
5 | import java.util.Arrays;
6 | import java.util.HashMap;
7 | import java.util.List;
8 | import java.util.Map;
9 | import java.util.Map.Entry;
10 |
11 | import org.junit.Test;
12 |
13 | import de.lv1871.oss.tester.test.dmnassert.model.DecisionSimulationResponse;
14 |
15 | public class DmnTestTest {
16 |
17 | @Test
18 | public void testTestExpactationAllEqual() {
19 | DecisionSimulationResponse decisionSimulationResponse = createResponse(Arrays.asList(
20 | createResponseEntry("test", 1),
21 | createResponseEntry("zweitesAttribut", 3)
22 | ));
23 | Map expectedMap = createResponseEntry("test", 1);
24 |
25 | List> testExpectation = DmnTest.testExpectation(decisionSimulationResponse, expectedMap);
26 |
27 | assertEquals(0, testExpectation.size());
28 | }
29 |
30 | @Test
31 | public void testTestFailsWhenValueNotContained() {
32 | DecisionSimulationResponse decisionSimulationResponse = createResponse(Arrays.asList(
33 | createResponseEntry("test", 1),
34 | createResponseEntry("zweitesAttribut", 3)
35 | ));
36 | Map expectedMap = createResponseEntry("test", 2);
37 |
38 | List> testExpectation = DmnTest.testExpectation(decisionSimulationResponse, expectedMap);
39 |
40 | assertEquals(1, testExpectation.size());
41 | assertEquals("test", testExpectation.get(0).getKey());
42 | assertEquals(2, testExpectation.get(0).getValue());
43 | }
44 |
45 | @Test
46 | public void testTestWorksWhenExpectedValueIsNullAndNotContained() {
47 | DecisionSimulationResponse decisionSimulationResponse = createResponse(Arrays.asList(
48 | createResponseEntry("test", 1)
49 | ));
50 | Map expectedMap = createResponseEntry("nullValue", null);
51 |
52 | List> testExpectation = DmnTest.testExpectation(decisionSimulationResponse, expectedMap);
53 |
54 | assertEquals(0, testExpectation.size());
55 | }
56 |
57 | @Test
58 | public void testFailesWhenNotAllExpectedValuesContained() {
59 | DecisionSimulationResponse decisionSimulationResponse = createResponse(Arrays.asList(
60 | createResponseEntry("test", 1),
61 | createResponseEntry("test2", "Hallo"),
62 | createResponseEntry("test3", "Welt")
63 | ));
64 | Map expectedMap = new HashMap<>();
65 | expectedMap.put("test", 1);
66 | expectedMap.put("test2", "Hallo");
67 | expectedMap.put("test3", "Welt");
68 | expectedMap.put("test4", 1);
69 |
70 | List> testExpectation = DmnTest.testExpectation(decisionSimulationResponse, expectedMap);
71 |
72 | assertEquals(1, testExpectation.size());
73 | assertEquals("test4", testExpectation.get(0).getKey());
74 | }
75 |
76 | @Test
77 | public void testRunsWhenAllExpectedValuesContained() {
78 | DecisionSimulationResponse decisionSimulationResponse = createResponse(Arrays.asList(
79 | createResponseEntry("test", 1),
80 | createResponseEntry("test2", "Hallo"),
81 | createResponseEntry("test3", "Welt"),
82 | createResponseEntry("test4", 1)
83 | ));
84 | Map expectedMap = new HashMap<>();
85 | expectedMap.put("test", 1);
86 | expectedMap.put("test2", "Hallo");
87 | expectedMap.put("test3", "Welt");
88 | expectedMap.put("test4", 1);
89 |
90 | List> testExpectation = DmnTest.testExpectation(decisionSimulationResponse, expectedMap);
91 |
92 | assertEquals(0, testExpectation.size());
93 | }
94 |
95 | private DecisionSimulationResponse createResponse(List> results) {
96 | return DecisionSimulationResponse.DecisionTestCaseResponseBuilder
97 | .create()
98 | .withResult(results)
99 | .build();
100 | }
101 |
102 | private Map createResponseEntry(String key, Object value) {
103 | Map entry = new HashMap<>();
104 | entry.put(key, value);
105 | return entry;
106 | }
107 | }
--------------------------------------------------------------------------------
/dmnmgr-tester/src/test/java/de/lv1871/oss/tester/test/dmnassert/DmnVariableAssertTest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.dmnassert;
2 |
3 | import static org.junit.Assert.assertFalse;
4 | import static org.junit.Assert.assertTrue;
5 |
6 | import java.math.BigDecimal;
7 |
8 | import org.junit.Test;
9 |
10 | public class DmnVariableAssertTest {
11 |
12 | @Test
13 | public void testIntegerCompareCorrectNotEqual() {
14 | assertFalse(DmnVariableAssert.numbersEqual(1, 2));
15 | }
16 |
17 | @Test
18 | public void testIntegerCompareCorrectEqual() {
19 | assertTrue(DmnVariableAssert.numbersEqual(1, 1));
20 | }
21 |
22 | @Test
23 | public void testDoubleCompareCorrectNotEqual() {
24 | assertFalse(DmnVariableAssert.numbersEqual(1.3, 2.5));
25 | }
26 |
27 | @Test
28 | public void testDoubleCompareCorrectEqual() {
29 | assertTrue(DmnVariableAssert.numbersEqual(1.5, 1.5));
30 | }
31 |
32 | @Test
33 | public void testDoubleCompareCorrectToIntegerValueEqual() {
34 | assertTrue(DmnVariableAssert.numbersEqual(1.0, 1));
35 | }
36 |
37 | @Test
38 | public void testDoubleCompareCorrectToIntegerValueNotEqual() {
39 | assertFalse(DmnVariableAssert.numbersEqual(1.0, 2));
40 | }
41 |
42 | @Test
43 | public void testNullValueComparisonBothNull() {
44 | assertTrue(DmnVariableAssert.equalValue(null, null));
45 | }
46 |
47 | @Test
48 | public void testNullValueComparisonLeftNull() {
49 | assertFalse(DmnVariableAssert.equalValue(null, new Object()));
50 | }
51 |
52 | @Test
53 | public void testNullValueComparisonRightNull() {
54 | assertFalse(DmnVariableAssert.equalValue(new Object(), null));
55 | }
56 |
57 | @Test
58 | public void testNumberComparisonPassToNumberComparison() {
59 | assertTrue(DmnVariableAssert.equalValue(new BigDecimal(12.0), 12L));
60 | }
61 |
62 | @Test
63 | public void testStringsEqual() {
64 | assertTrue(DmnVariableAssert.equalValue(new String("Hallo".getBytes()), new String("Hallo".getBytes())));
65 | }
66 |
67 | @Test
68 | public void testObjectComparisonWithEqualIsFalseWhenUnequal() {
69 | TestObject a = new TestObject(1, 3);
70 | TestObject b = new TestObject(1, 7);
71 | assertFalse(DmnVariableAssert.equalValue(a, b));
72 | }
73 |
74 | @Test
75 | public void testObjectComparisonWithEqualIsTrueWhenEqual() {
76 | TestObject a = new TestObject(1, 3);
77 | TestObject b = new TestObject(1, 3);
78 | assertTrue(DmnVariableAssert.equalValue(a, b));
79 | }
80 |
81 | private class TestObject {
82 |
83 | private int a;
84 | private int b;
85 |
86 | public TestObject(int a, int b) {
87 | this.a = a;
88 | this.b = b;
89 | }
90 |
91 | public int getB() {
92 | return b;
93 | }
94 |
95 | public int getA() {
96 | return a;
97 | }
98 |
99 | @Override
100 | public boolean equals(Object obj) {
101 | if (this == obj)
102 | return true;
103 | if (obj == null)
104 | return false;
105 | if (getClass() != obj.getClass())
106 | return false;
107 | TestObject other = (TestObject) obj;
108 | return this.getA() == other.getA() && this.getB() == other.getB();
109 | }
110 |
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/dmnmgr-tester/src/test/java/de/lv1871/oss/tester/test/domain/DecisionEngineTest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.domain;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertNotNull;
5 |
6 | import java.io.IOException;
7 | import java.util.HashMap;
8 | import java.util.Map;
9 |
10 | import org.camunda.bpm.dmn.engine.DmnDecisionTableResult;
11 | import org.camunda.bpm.model.xml.impl.util.IoUtil;
12 | import org.junit.Test;
13 |
14 | public class DecisionEngineTest {
15 |
16 | @Test
17 | public void testReadsAllDecisionsWithKeys() throws IOException {
18 | DecisionEngine engine = DecisionEngine.createEngine()
19 | .parseDecision(getDmnXml("testDrgTwoDecisions.dmn"));
20 |
21 | assertNotNull(engine.getDecisionByKey("decisionA"));
22 | assertNotNull(engine.getDecisionByKey("decisionB"));
23 | }
24 |
25 | @Test(expected = IllegalArgumentException.class)
26 | public void testReadsUnknownKeyCausesIllegalArgumentException() throws IOException {
27 | DecisionEngine engine = DecisionEngine.createEngine()
28 | .parseDecision(getDmnXml("testDrgTwoDecisions.dmn"));
29 |
30 | engine.getDecisionByKey("decisionC");
31 | }
32 |
33 | @Test()
34 | public void testExecutesDecisionAndReturnsResult() throws IOException {
35 | DecisionEngine engine = DecisionEngine.createEngine()
36 | .parseDecision(getDmnXml("testDrgTwoDecisions.dmn"));
37 |
38 | DmnDecisionTableResult result = engine.evaluateDecisionByKey("decisionA", getVariables("input", "a"));
39 |
40 | assertEquals("b", result.get(0).get("result"));
41 | }
42 |
43 | private String getDmnXml(String filename) throws IOException {
44 | return IoUtil.getStringFromInputStream(this.getClass().getResourceAsStream("/" + filename));
45 | }
46 |
47 | private Map getVariables(String key, Object value) {
48 | Map variables = new HashMap<>();
49 | variables.put(key, value);
50 | return variables;
51 | }
52 |
53 | }
--------------------------------------------------------------------------------
/dmnmgr-tester/src/test/java/de/lv1871/oss/tester/test/function/LambdaExtensionTest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.tester.test.function;
2 |
3 | import static org.junit.Assert.assertFalse;
4 |
5 | import org.junit.Test;
6 |
7 | public class LambdaExtensionTest {
8 |
9 | @Test
10 | public void testNotNullWithSupplierWhenNull() {
11 | TestObject testObject = new TestObject(null);
12 |
13 | assertFalse(LambdaExtension.notNull(TestObject::getValue).test(testObject));
14 | }
15 |
16 | @Test
17 | public void testNotNullWithSupplierWhenNotNull() {
18 | TestObject testObject = new TestObject(null);
19 |
20 | assertFalse(LambdaExtension.notNull(TestObject::getValue).test(testObject));
21 | }
22 |
23 | private static class TestObject {
24 |
25 | public TestObject(String value) {
26 | this.value = value;
27 | }
28 |
29 | private String value;
30 |
31 | public String getValue() {
32 | return value;
33 | }
34 |
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/dmnmgr-tester/src/test/resources/testDrgTwoDecisions.dmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | input
11 |
12 |
13 |
14 |
15 |
16 | "a"
17 |
18 |
19 | "b"
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/dmnmgr-tester/src/test/resources/testDrgTwoDecisions.dmnapp.json:
--------------------------------------------------------------------------------
1 | {"dmnPath":"testDrgTwoDecisions.dmn","testsuite":{"decisionA":{"tests":[]},"definitions_09waf08":{"tests":[]},"decisionB":{"tests":[]}},"definitions":{},"plugins":[]}
--------------------------------------------------------------------------------
/dmnmgr/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 |
5 | de.lv1871.oss
6 | dmnmgr-project
7 | 1.3.0-SNAPSHOT
8 |
9 |
10 | dmnmgr
11 | jar
12 |
13 | dmnmgr
14 | http://maven.apache.org
15 |
16 |
17 |
18 |
19 | org.springframework.boot
20 | spring-boot-dependencies
21 | ${springframework.boot.version}
22 | pom
23 | import
24 |
25 |
26 |
27 |
28 |
29 |
30 | de.lv1871.oss
31 | dmnmgr-tester
32 | ${project.version}
33 | compile
34 |
35 |
36 | org.camunda.bpm
37 | camunda-engine
38 |
39 |
40 | org.camunda.bpm.dmn
41 | camunda-engine-dmn
42 |
43 |
44 | org.camunda.bpm.extension.dmn.scala
45 | dmn-engine-camunda-plugin
46 |
47 |
48 | org.camunda.bpm.extension.feel.scala
49 | feel-engine-plugin
50 |
51 |
52 | org.camunda.bpm.extension.feel.scala
53 | feel-engine-factory
54 | ${feel.scala.version}
55 |
56 |
57 | org.camunda.bpm.extension.feel.scala
58 | feel-engine
59 | ${feel.scala.version}
60 |
61 |
62 | org.springframework.boot
63 | spring-boot-starter-web
64 |
65 |
66 | org.springframework.boot
67 | spring-boot-starter-logging
68 |
69 |
70 | org.springframework.boot
71 | spring-boot-configuration-processor
72 | true
73 |
74 |
75 | de.redsix
76 | dmn-check-core
77 |
78 |
79 | de.redsix
80 | dmn-check-validators
81 |
82 |
83 | org.springframework.boot
84 | spring-boot-starter-actuator
85 |
86 |
87 | io.springfox
88 | springfox-swagger-ui
89 |
90 |
91 | io.springfox
92 | springfox-swagger2
93 |
94 |
95 | com.fasterxml.jackson.datatype
96 | jackson-datatype-jsr310
97 |
98 |
99 |
100 |
101 |
102 | junit
103 | junit
104 | test
105 |
106 |
107 |
108 |
109 |
110 | org.springframework.boot
111 | spring-boot-maven-plugin
112 | ${spring.boot.maven.plugin}
113 |
114 |
115 |
116 | repackage
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/App.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.context.annotation.Bean;
6 | import org.springframework.context.annotation.ComponentScan;
7 | import org.springframework.context.annotation.Configuration;
8 | import org.springframework.web.bind.annotation.CrossOrigin;
9 |
10 | import de.lv1871.oss.dmnmgr.api.documentation.DecisionApi;
11 | import springfox.documentation.spring.web.plugins.Docket;
12 | import springfox.documentation.swagger2.annotations.EnableSwagger2;
13 |
14 | @SpringBootApplication
15 | @ComponentScan(value = { "de.lv1871.oss" })
16 | @CrossOrigin
17 | @EnableSwagger2
18 | @Configuration
19 | public class App {
20 | public static void main(String[] args) {
21 | SpringApplication.run(App.class, args);
22 | }
23 |
24 | @Bean
25 | public Docket api() {
26 | return DecisionApi.createApiInfo();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/DecisionResource.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.web.bind.annotation.CrossOrigin;
6 | import org.springframework.web.bind.annotation.RequestBody;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RequestMethod;
9 | import org.springframework.web.bind.annotation.ResponseBody;
10 |
11 | import de.lv1871.oss.dmnmgr.api.model.DecisionRequest;
12 | import de.lv1871.oss.dmnmgr.api.model.DecisionSimulationRequest;
13 | import de.lv1871.oss.dmnmgr.api.model.DecisionTestRequest;
14 | import de.lv1871.oss.dmnmgr.api.model.DecisionTestResponse;
15 | import de.lv1871.oss.dmnmgr.api.model.DmnValidationResponse;
16 | import de.lv1871.oss.dmnmgr.service.AdvancedDmnCheckService;
17 | import de.lv1871.oss.dmnmgr.service.DecisionTestService;
18 | import de.lv1871.oss.dmnmgr.service.GenericDecisionService;
19 | import de.lv1871.oss.tester.test.dmnassert.model.DecisionSimulationResponse;
20 | import io.swagger.annotations.ApiResponse;
21 | import io.swagger.annotations.ApiResponses;
22 |
23 | @Controller
24 | @CrossOrigin
25 | public class DecisionResource {
26 |
27 | @Autowired
28 | private GenericDecisionService decisionService;
29 |
30 | @Autowired
31 | private DecisionTestService testService;
32 |
33 | @Autowired
34 | private AdvancedDmnCheckService advancedDmnCheckService;
35 |
36 | @RequestMapping(value = "/api/decision/simulation", method = RequestMethod.POST)
37 | @ApiResponses(value = { @ApiResponse(code = 500, message = "Internal Server Error"),
38 | @ApiResponse(code = 406, message = "Data Not Acceptable"),
39 | @ApiResponse(code = 403, message = "Not Authorized") })
40 | public @ResponseBody DecisionSimulationResponse evaluateTestCase(
41 | @RequestBody DecisionSimulationRequest decisionRequest) {
42 | return decisionService.decide(decisionRequest);
43 | }
44 |
45 | @RequestMapping(value = "/api/decision/test", method = RequestMethod.POST)
46 | @ApiResponses(value = { @ApiResponse(code = 500, message = "Internal Server Error"),
47 | @ApiResponse(code = 406, message = "Data Not Acceptable"),
48 | @ApiResponse(code = 403, message = "Not Authorized") })
49 | public @ResponseBody DecisionTestResponse test(@RequestBody DecisionTestRequest decisionRequest) {
50 | return testService.testDecision(decisionRequest);
51 | }
52 |
53 | @RequestMapping(value = "/api/decision/validate", method = RequestMethod.POST)
54 | @ApiResponses(value = { @ApiResponse(code = 500, message = "Internal Server Error"),
55 | @ApiResponse(code = 406, message = "Data Not Acceptable"),
56 | @ApiResponse(code = 403, message = "Not Authorized") })
57 | public @ResponseBody DmnValidationResponse validate(@RequestBody DecisionRequest decisionRequest) {
58 | return advancedDmnCheckService.validateDecision(decisionRequest);
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/UtilityResource.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.stereotype.Controller;
5 | import org.springframework.web.bind.annotation.CrossOrigin;
6 | import org.springframework.web.bind.annotation.RequestMapping;
7 | import org.springframework.web.bind.annotation.RequestMethod;
8 | import org.springframework.web.bind.annotation.RequestParam;
9 | import org.springframework.web.bind.annotation.ResponseBody;
10 |
11 | import de.lv1871.oss.dmnmgr.api.model.OpenApiDefinitionResponse;
12 | import de.lv1871.oss.dmnmgr.service.DownloadService;
13 | import io.swagger.annotations.ApiResponse;
14 | import io.swagger.annotations.ApiResponses;
15 |
16 | @Controller
17 | @CrossOrigin
18 | public class UtilityResource {
19 |
20 | @Autowired
21 | private DownloadService downloadService;
22 |
23 | @RequestMapping(value = "/api/open-api-definition", method = RequestMethod.GET)
24 | @ApiResponses(value = { @ApiResponse(code = 500, message = "Internal Server Error"),
25 | @ApiResponse(code = 406, message = "Data Not Acceptable"),
26 | @ApiResponse(code = 403, message = "Not Authorized") })
27 | public @ResponseBody OpenApiDefinitionResponse deploy(@RequestParam("api-url") String url) {
28 | return downloadService.downloadOpenApiDefinition(url);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/documentation/DecisionApi.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api.documentation;
2 |
3 | import static springfox.documentation.builders.PathSelectors.regex;
4 |
5 | import com.google.common.base.Predicate;
6 |
7 | import springfox.documentation.builders.ApiInfoBuilder;
8 | import springfox.documentation.service.ApiInfo;
9 | import springfox.documentation.spi.DocumentationType;
10 | import springfox.documentation.spring.web.plugins.Docket;
11 |
12 | public class DecisionApi {
13 |
14 | private static final String API_TITLE = "DMN API";
15 | private static final String API_NAME = "dmn-api";
16 | private static final String API_DESCRIPTION = "Dieses API dient dazu Entscheidungen zu triggern";
17 |
18 | private static ApiInfo apiInfo() {
19 | return new ApiInfoBuilder().title(API_TITLE).description(API_DESCRIPTION).build();
20 | }
21 |
22 | public static Docket createApiInfo() {
23 | // @formatter:off
24 | return new Docket(DocumentationType.SWAGGER_2)
25 | .groupName(API_NAME)
26 | .useDefaultResponseMessages(false)
27 | .apiInfo(apiInfo())
28 | .select()
29 | .paths(dokumentDecisionPath())
30 | .build();
31 | // @formatter:on
32 | }
33 |
34 | private static Predicate dokumentDecisionPath() {
35 | return regex("/api/.*");
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/model/DecisionRequest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api.model;
2 |
3 | public class DecisionRequest {
4 |
5 | private String xml;
6 |
7 | public String getXml() {
8 | return xml;
9 | }
10 |
11 | public void setXml(String xml) {
12 | this.xml = xml;
13 | }
14 |
15 | }
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/model/DecisionSimulationRequest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api.model;
2 |
3 | import com.fasterxml.jackson.databind.node.ObjectNode;
4 |
5 | public class DecisionSimulationRequest extends DecisionRequest {
6 |
7 | private String dmnTableId;
8 | private ObjectNode variables;
9 |
10 | public ObjectNode getVariables() {
11 | return variables;
12 | }
13 |
14 | public void setVariables(ObjectNode variables) {
15 | this.variables = variables;
16 | }
17 |
18 | public String getDmnTableId() {
19 | return dmnTableId;
20 | }
21 |
22 | public void setDmnTableId(String dmnTableId) {
23 | this.dmnTableId = dmnTableId;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/model/DecisionTestRequest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api.model;
2 |
3 | import java.util.List;
4 |
5 | import com.fasterxml.jackson.databind.node.ObjectNode;
6 |
7 | public class DecisionTestRequest extends DecisionSimulationRequest {
8 |
9 | private String dmnTableId;
10 | private List expectedData;
11 |
12 | public List getExpectedData() {
13 | return expectedData;
14 | }
15 |
16 | public void setExpectedData(List expectedData) {
17 | this.expectedData = expectedData;
18 | }
19 |
20 | public String getDmnTableId() {
21 | return dmnTableId;
22 | }
23 |
24 | public void setDmnTableId(String dmnTableId) {
25 | this.dmnTableId = dmnTableId;
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/model/DecisionTestResponse.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api.model;
2 |
3 | import java.util.List;
4 | import java.util.Map;
5 | import java.util.Map.Entry;
6 |
7 | public class DecisionTestResponse {
8 |
9 | private final String message;
10 | private final List> result;
11 | private final List resultRuleIds;
12 | private final Boolean testSucceded;
13 | private final List> expectedDataAssertionFailed;
14 |
15 | private DecisionTestResponse(String message, List> result, List resultRuleIds,
16 | Boolean testSucceded, List> expectedDataAssertionFailed) {
17 | this.message = message;
18 | this.result = result;
19 | this.resultRuleIds = resultRuleIds;
20 | this.testSucceded = testSucceded;
21 | this.expectedDataAssertionFailed = expectedDataAssertionFailed;
22 | }
23 |
24 | public String getMessage() {
25 | return message;
26 | }
27 |
28 | public List> getResult() {
29 | return result;
30 | }
31 |
32 | public List getResultRuleIds() {
33 | return resultRuleIds;
34 | }
35 |
36 | public Boolean getTestSucceded() {
37 | return testSucceded;
38 | }
39 |
40 | public List> getExpectedDataAssertionFailed() {
41 | return expectedDataAssertionFailed;
42 | }
43 |
44 | public static class DecisionTestResponseBuilder {
45 |
46 | private String message = null;
47 | private List> result = null;
48 | private List resultRuleIds = null;
49 | private List> expectedDataAssertionFailed;
50 | private Boolean testSucceded = false;
51 |
52 | public static DecisionTestResponseBuilder create() {
53 | return new DecisionTestResponseBuilder();
54 | }
55 |
56 | public DecisionTestResponseBuilder withMessage(String message) {
57 | this.message = message;
58 | return this;
59 | }
60 |
61 | public DecisionTestResponseBuilder withResultRuleIds(List resultRuleIds) {
62 | this.resultRuleIds = resultRuleIds;
63 | return this;
64 | }
65 |
66 | public DecisionTestResponseBuilder withResult(List> result) {
67 | this.result = result;
68 | return this;
69 | }
70 |
71 | public DecisionTestResponseBuilder withExpectedDataAssertionFailed(
72 | List> expectedDataAssertionFailed) {
73 | this.expectedDataAssertionFailed = expectedDataAssertionFailed;
74 | return this;
75 | }
76 |
77 | public DecisionTestResponseBuilder withTestSucceeded(Boolean testSucceded) {
78 | this.testSucceded = testSucceded;
79 | return this;
80 | }
81 |
82 | public DecisionTestResponse build() {
83 | return new DecisionTestResponse(message, result, resultRuleIds, testSucceded, expectedDataAssertionFailed);
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/model/DmnValidationResponse.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api.model;
2 |
3 | import java.util.List;
4 |
5 | public class DmnValidationResponse {
6 |
7 | private List errors;
8 | private List warnings;
9 |
10 | public List getWarnings() {
11 | return warnings;
12 | }
13 |
14 | public List getErrors() {
15 | return errors;
16 | }
17 |
18 | public void setErrors(List errors) {
19 | this.errors = errors;
20 | }
21 |
22 | public void setWarnings(List warnings) {
23 | this.warnings = warnings;
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/model/DmnValidationResult.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api.model;
2 |
3 | public class DmnValidationResult {
4 |
5 | private final String tableId;
6 | private final String ruleId;
7 | private final String cellId;
8 | private final String counterRuleId;
9 | private final String message;
10 | private final ErrorSeverity severity;
11 |
12 | private DmnValidationResult(String tableId, String ruleId, String cellId, String counterRuleId, String message,
13 | ErrorSeverity severety) {
14 | this.tableId = tableId;
15 | this.ruleId = ruleId;
16 | this.message = message;
17 | this.severity = severety;
18 | this.counterRuleId = counterRuleId;
19 | this.cellId = cellId;
20 | }
21 |
22 | public String getCellId() {
23 | return cellId;
24 | }
25 |
26 | public String getCounterRuleId() {
27 | return counterRuleId;
28 | }
29 |
30 | public String getTableId() {
31 | return tableId;
32 | }
33 |
34 | public String getRuleId() {
35 | return ruleId;
36 | }
37 |
38 | public String getMessage() {
39 | return message;
40 | }
41 |
42 | public ErrorSeverity getSeverity() {
43 | return severity;
44 | }
45 |
46 | public static class DmnValidationResultBuilder {
47 |
48 | private String tableId;
49 | private String ruleId;
50 | private String cellId;
51 | private String counterRuleId;
52 | private String message;
53 | private ErrorSeverity severity;
54 |
55 | public static DmnValidationResultBuilder create() {
56 | return new DmnValidationResultBuilder();
57 | }
58 |
59 | public DmnValidationResultBuilder withTableId(String tableId) {
60 | this.tableId = tableId;
61 | return this;
62 | }
63 |
64 | public DmnValidationResultBuilder withRuleId(String ruleId) {
65 | this.ruleId = ruleId;
66 | return this;
67 | }
68 |
69 | public DmnValidationResultBuilder withCounterRuleId(String counterRuleId) {
70 | this.counterRuleId = counterRuleId;
71 | return this;
72 | }
73 |
74 | public DmnValidationResultBuilder withMessage(String message) {
75 | this.message = message;
76 | return this;
77 | }
78 |
79 | public DmnValidationResultBuilder withSeverity(ErrorSeverity severity) {
80 | this.severity = severity;
81 | return this;
82 | }
83 |
84 | public DmnValidationResultBuilder withCellId(String cellId) {
85 | this.cellId = cellId;
86 | return this;
87 | }
88 |
89 | public DmnValidationResult build() {
90 | return new DmnValidationResult(tableId, ruleId, cellId, counterRuleId, message, severity);
91 | }
92 | }
93 |
94 | }
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/model/ErrorSeverity.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api.model;
2 |
3 | import de.redsix.dmncheck.result.Severity;
4 |
5 | public enum ErrorSeverity {
6 | ERROR, WARNING;
7 |
8 | public static ErrorSeverity ofSeverity(Severity severity) {
9 | switch(severity) {
10 | case ERROR:
11 | return ErrorSeverity.ERROR;
12 | case WARNING:
13 | return ErrorSeverity.WARNING;
14 | default:
15 | return ErrorSeverity.ERROR;
16 |
17 | }
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/model/OpenApiDefinitionResponse.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api.model;
2 |
3 | public class OpenApiDefinitionResponse {
4 |
5 | private String apiUrl;
6 | private String swaggerDefinition;
7 |
8 | public String getApiUrl() {
9 | return apiUrl;
10 | }
11 |
12 | public void setApiUrl(String apiUrl) {
13 | this.apiUrl = apiUrl;
14 | }
15 |
16 | public String getSwaggerDefinition() {
17 | return swaggerDefinition;
18 | }
19 |
20 | public void setSwaggerDefinition(String swaggerDefinition) {
21 | this.swaggerDefinition = swaggerDefinition;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/api/model/ServiceResult.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.api.model;
2 |
3 | public class ServiceResult {
4 |
5 | private boolean success;
6 |
7 | public boolean isSuccess() {
8 | return success;
9 | }
10 |
11 | public void setSuccess(boolean success) {
12 | this.success = success;
13 | }
14 |
15 | public static class ServiceResultBuilder {
16 | private boolean success;
17 |
18 | public static ServiceResultBuilder create() {
19 | return new ServiceResultBuilder();
20 | }
21 |
22 | public ServiceResultBuilder withResult(boolean success) {
23 | this.success = success;
24 | return this;
25 | }
26 |
27 | public ServiceResult build() {
28 | ServiceResult result = new ServiceResult();
29 | result.setSuccess(success);
30 | return result;
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/controller/HomeController.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.controller;
2 |
3 | import org.springframework.stereotype.Controller;
4 | import org.springframework.web.bind.annotation.RequestMapping;
5 |
6 | @Controller
7 | public class HomeController {
8 |
9 | @RequestMapping("/api")
10 | public String home() {
11 | return "redirect:swagger-ui.html";
12 | }
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/domain/CheckerFunctions.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.domain;
2 |
3 | import java.util.Arrays;
4 |
5 | import org.camunda.commons.utils.StringUtil;
6 |
7 | public class CheckerFunctions {
8 |
9 | public static boolean containesUnquotedtQuotationMark(String trimmedText) {
10 | if (trimmedText == null || trimmedText.length() < 1) {
11 | return false;
12 | }
13 | String textWithoutMarks = trimmedText;
14 | if (trimmedText.startsWith("\"")) {
15 | textWithoutMarks = trimmedText.substring(1);
16 | }
17 | if (textWithoutMarks.endsWith("\"")) {
18 | textWithoutMarks = textWithoutMarks.substring(0, textWithoutMarks.length() - 1);
19 | }
20 |
21 | return Arrays.asList(textWithoutMarks.split("\\\\"))
22 | .stream()
23 | .map(value -> value.substring(1))
24 | .filter(value -> value.indexOf("\"") > -1)
25 | .count() > 0;
26 | }
27 | }
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/domain/InputEntryValidator.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.domain;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.Collections;
6 | import java.util.List;
7 | import java.util.Optional;
8 | import java.util.Scanner;
9 |
10 | import org.camunda.bpm.engine.impl.el.ExpressionManager;
11 | import org.camunda.bpm.model.dmn.instance.InputEntry;
12 |
13 | import de.redsix.dmncheck.result.Severity;
14 | import de.redsix.dmncheck.result.ValidationResult;
15 | import de.redsix.dmncheck.validators.core.SimpleValidator;
16 | import de.redsix.dmncheck.validators.core.ValidationContext;
17 |
18 | import de.lv1871.oss.tester.test.function.ExtendedBiFunction;
19 | import static de.lv1871.oss.dmnmgr.domain.CheckerFunctions.containesUnquotedtQuotationMark;
20 |
21 | public class InputEntryValidator extends SimpleValidator {
22 |
23 | @Override
24 | protected boolean isApplicable(InputEntry arg0, ValidationContext arg1) {
25 | return true;
26 | }
27 |
28 | @Override
29 | protected List validate(InputEntry element, ValidationContext arg1) {
30 | var expressionLanguage = Optional.ofNullable(element).map(InputEntry::getExpressionLanguage).orElse("feel")
31 | .toLowerCase();
32 | switch (expressionLanguage) {
33 | case "feel":
34 | return checkExpression(element, this::checkFeelExpression);
35 | case "juel":
36 | return checkExpression(element, this::checkJuelExpression);
37 | default:
38 | return null;
39 | }
40 | }
41 |
42 | private List checkExpression(InputEntry element,
43 | ExtendedBiFunction> validator) {
44 | return Optional.ofNullable(element)
45 | .map(InputEntry::getTextContent)
46 | .map(validator.curryWith(element))
47 | .orElse(Collections.emptyList());
48 | }
49 |
50 | private List checkJuelExpression(InputEntry entry, String text) {
51 | if (text == null) {
52 | return Collections.emptyList();
53 | }
54 |
55 | if (text.startsWith("${") && !text.endsWith("}")) {
56 | return Arrays.asList(ValidationResult.init
57 | .message(String.format("Error in Juel Expression: (%s) Must start with '${'", text))
58 | .severity(Severity.ERROR).element(entry).build());
59 | }
60 |
61 | if (!text.startsWith("${") && text.endsWith("}")) {
62 | return Arrays.asList(ValidationResult.init
63 | .message(String.format("Error in Juel Expression: (%s) Must end with '}'", text))
64 | .severity(Severity.ERROR).element(entry).build());
65 | }
66 |
67 | List result = new ArrayList<>();
68 | try {
69 | if (!text.startsWith("$") && text.length() > 0) {
70 | text = "${" + text + "}";
71 | }
72 | new ExpressionManager().createExpression(text);
73 | } catch (Exception exception) {
74 | result.add(ValidationResult.init.message("Error in Juel Expression: " + exception.getMessage())
75 | .severity(Severity.ERROR).element(entry).build());
76 | }
77 | return result;
78 | }
79 |
80 | private List checkFeelExpression(InputEntry entry, String text) {
81 | if (text == null) {
82 | return Collections.emptyList();
83 | }
84 |
85 | List result = new ArrayList<>();
86 | var scanner = new Scanner(text);
87 | try {
88 | scanner.useDelimiter(",");
89 | while(scanner.hasNext()) {
90 | var value = scanner.next();
91 | if (value == null) {
92 | continue;
93 | }
94 | var valueTrimmed = value.trim();
95 | if (valueTrimmed.startsWith("\"") && !valueTrimmed.endsWith("\"")) {
96 | return Arrays.asList(ValidationResult.init
97 | .message(String.format("Error in Feel Expression: (%s) Must end with '\"'", text))
98 | .severity(Severity.ERROR).element(entry).build());
99 | }
100 | if (!valueTrimmed.startsWith("\"") && valueTrimmed.endsWith("\"")) {
101 | return Arrays.asList(ValidationResult.init
102 | .message(String.format("Error in Feel Expression: (%s) Must start with '\"'", text))
103 | .severity(Severity.ERROR).element(entry).build());
104 | }
105 |
106 | if (containesUnquotedtQuotationMark(valueTrimmed)) {
107 | result.add(ValidationResult.init
108 | .message(String.format("Error in Feel Expression: (%s) Must not contain a '\"'", valueTrimmed))
109 | .severity(Severity.ERROR).element(entry).build());
110 | }
111 | }
112 | } catch (Exception exception) {
113 | result.add(ValidationResult.init.message("Error in Juel Expression: " + exception.getMessage())
114 | .severity(Severity.ERROR).element(entry).build());
115 | } finally {
116 | scanner.close();
117 | }
118 | return result;
119 | }
120 |
121 | @Override
122 | public Class getClassUnderValidation() {
123 | return InputEntry.class;
124 | }
125 |
126 |
127 | }
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/domain/InputExpressionRequiredValidator.java:
--------------------------------------------------------------------------------
1 |
2 | package de.lv1871.oss.dmnmgr.domain;
3 |
4 | import de.redsix.dmncheck.result.Severity;
5 | import de.redsix.dmncheck.result.ValidationResult;
6 | import de.redsix.dmncheck.validators.core.SimpleValidator;
7 | import de.redsix.dmncheck.validators.core.ValidationContext;
8 |
9 | import java.util.Collections;
10 | import java.util.List;
11 | import java.util.Optional;
12 |
13 | import org.camunda.bpm.model.dmn.instance.InputExpression;
14 | import org.camunda.bpm.model.dmn.instance.Text;
15 | import org.springframework.util.StringUtils;
16 |
17 | public class InputExpressionRequiredValidator extends SimpleValidator {
18 |
19 | @Override
20 | protected boolean isApplicable(InputExpression arg0, ValidationContext arg1) {
21 | return true;
22 | }
23 |
24 | @Override
25 | protected List validate(InputExpression expression, ValidationContext arg1) {
26 | try {
27 | final var expressionText = Optional
28 | .ofNullable(expression)
29 | .map(InputExpression::getText)
30 | .map(Text::getTextContent)
31 | .orElse(null);
32 |
33 | if (StringUtils.isEmpty(expressionText)) {
34 | return Collections.singletonList(ValidationResult.init
35 | .message(getClassUnderValidation().getSimpleName() + " has no expressiontext")
36 | .severity(Severity.ERROR)
37 | .element(expression)
38 | .build());
39 | }
40 | return Collections.emptyList();
41 | } catch (Exception ex) {
42 | return Collections.emptyList();
43 | }
44 | }
45 |
46 | @Override
47 | public Class getClassUnderValidation() {
48 | return InputExpression.class;
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/domain/OutputEntryValidator.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.domain;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Arrays;
5 | import java.util.Collections;
6 | import java.util.List;
7 | import java.util.Optional;
8 |
9 | import org.camunda.bpm.engine.impl.el.ExpressionManager;
10 | import org.camunda.bpm.model.dmn.instance.OutputEntry;
11 |
12 | import de.redsix.dmncheck.result.Severity;
13 | import de.redsix.dmncheck.result.ValidationResult;
14 | import de.redsix.dmncheck.validators.core.SimpleValidator;
15 | import de.redsix.dmncheck.validators.core.ValidationContext;
16 |
17 | import de.lv1871.oss.tester.test.function.ExtendedBiFunction;
18 | import static de.lv1871.oss.dmnmgr.domain.CheckerFunctions.containesUnquotedtQuotationMark;
19 |
20 | public class OutputEntryValidator extends SimpleValidator {
21 |
22 | @Override
23 | protected boolean isApplicable(OutputEntry arg0, ValidationContext arg1) {
24 | return true;
25 | }
26 |
27 | @Override
28 | protected List validate(OutputEntry element, ValidationContext arg1) {
29 | var expressionLanguage = Optional.ofNullable(element).map(OutputEntry::getExpressionLanguage).orElse("feel")
30 | .toLowerCase();
31 | switch (expressionLanguage) {
32 | case "feel":
33 | return checkExpression(element, this::checkFeelExpression);
34 | case "juel":
35 | return checkExpression(element, this::checkJuelExpression);
36 | default:
37 | return null;
38 | }
39 | }
40 |
41 | private List checkExpression(OutputEntry element,
42 | ExtendedBiFunction> validator) {
43 | return Optional.ofNullable(element)
44 | .map(OutputEntry::getTextContent)
45 | .map(validator.curryWith(element))
46 | .orElse(Collections.emptyList());
47 | }
48 |
49 | private List checkJuelExpression(OutputEntry entry, String text) {
50 | if (text == null) {
51 | return Collections.emptyList();
52 | }
53 |
54 | if (text.startsWith("${") && !text.endsWith("}")) {
55 | return Arrays.asList(ValidationResult.init
56 | .message(String.format("Error in Juel Expression: (%s) Must start with '${'", text))
57 | .severity(Severity.ERROR).element(entry).build());
58 | }
59 |
60 | if (!text.startsWith("${") && text.endsWith("}")) {
61 | return Arrays.asList(ValidationResult.init
62 | .message(String.format("Error in Juel Expression: (%s) Must end with '}'", text))
63 | .severity(Severity.ERROR).element(entry).build());
64 | }
65 |
66 | List result = new ArrayList<>();
67 | try {
68 | if (!text.startsWith("$") && text.length() > 0) {
69 | text = "${" + text + "}";
70 | }
71 | new ExpressionManager().createExpression(text);
72 | } catch (Exception exception) {
73 | result.add(ValidationResult.init.message("Error in Juel Expression: " + exception.getMessage())
74 | .severity(Severity.ERROR).element(entry).build());
75 | }
76 | return result;
77 | }
78 |
79 | private List checkFeelExpression(OutputEntry entry, String text) {
80 | if (text == null) {
81 | return Collections.emptyList();
82 | }
83 |
84 | List result = new ArrayList<>();
85 | String trimmedText = text.trim();
86 | try {
87 | if (trimmedText.startsWith("\"") && !trimmedText.endsWith("\"")) {
88 | return Arrays.asList(ValidationResult.init
89 | .message(String.format("Error in Feel Expression: (%s) Must end with '\"'", trimmedText))
90 | .severity(Severity.ERROR).element(entry).build());
91 | }
92 | if (!trimmedText.startsWith("\"") && trimmedText.endsWith("\"")) {
93 | return Arrays.asList(ValidationResult.init
94 | .message(String.format("Error in Feel Expression: (%s) Must start with '\"'", trimmedText))
95 | .severity(Severity.ERROR).element(entry).build());
96 | }
97 |
98 | if (containesUnquotedtQuotationMark(trimmedText)) {
99 | result.add(ValidationResult.init
100 | .message(String.format("Error in Feel Expression: (%s) Must not contain a '\"'", trimmedText))
101 | .severity(Severity.ERROR).element(entry).build());
102 | }
103 | } catch (Exception exception) {
104 | result.add(ValidationResult.init.message("Error in Feel Expression: " + exception.getMessage())
105 | .severity(Severity.ERROR).element(entry).build());
106 | }
107 | return result;
108 | }
109 |
110 | @Override
111 | public Class getClassUnderValidation() {
112 | return OutputEntry.class;
113 | }
114 |
115 |
116 | }
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/domain/OutputNameRequiredValidator.java:
--------------------------------------------------------------------------------
1 |
2 | package de.lv1871.oss.dmnmgr.domain;
3 |
4 | import de.redsix.dmncheck.result.Severity;
5 | import de.redsix.dmncheck.result.ValidationResult;
6 | import de.redsix.dmncheck.validators.core.SimpleValidator;
7 | import de.redsix.dmncheck.validators.core.ValidationContext;
8 |
9 | import java.util.Collections;
10 | import java.util.List;
11 |
12 | import org.camunda.bpm.model.dmn.instance.Output;
13 | import org.springframework.util.StringUtils;
14 |
15 | public class OutputNameRequiredValidator extends SimpleValidator {
16 |
17 | @Override
18 | protected boolean isApplicable(Output arg0, ValidationContext arg1) {
19 | return true;
20 | }
21 |
22 | @Override
23 | protected List validate(Output expression, ValidationContext arg1) {
24 | final var outputName = expression.getName();
25 | if (StringUtils.isEmpty(outputName)) {
26 | return Collections.singletonList(
27 | ValidationResult
28 | .init
29 | .message(getClassUnderValidation().getSimpleName() + " has no name")
30 | .severity(Severity.ERROR)
31 | .element(expression)
32 | .build());
33 | }
34 | return Collections.emptyList();
35 | }
36 |
37 | @Override
38 | public Class getClassUnderValidation() {
39 | return Output.class;
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/service/AdvancedDmnCheckService.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.service;
2 |
3 | import java.io.ByteArrayInputStream;
4 | import java.io.InputStream;
5 | import java.lang.reflect.InvocationTargetException;
6 | import java.lang.reflect.Modifier;
7 | import java.nio.charset.StandardCharsets;
8 | import java.util.ArrayList;
9 | import java.util.Arrays;
10 | import java.util.List;
11 | import java.util.Optional;
12 | import java.util.stream.Collectors;
13 |
14 | import javax.annotation.PostConstruct;
15 |
16 | import org.camunda.bpm.model.dmn.Dmn;
17 | import org.camunda.bpm.model.dmn.DmnModelInstance;
18 | import org.camunda.bpm.model.xml.instance.ModelElementInstance;
19 | import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
20 | import org.springframework.stereotype.Service;
21 |
22 | import de.lv1871.oss.dmnmgr.api.model.DecisionRequest;
23 | import de.lv1871.oss.dmnmgr.api.model.DmnValidationResponse;
24 | import de.lv1871.oss.dmnmgr.api.model.DmnValidationResult;
25 | import de.lv1871.oss.dmnmgr.api.model.ErrorSeverity;
26 | import de.lv1871.oss.dmnmgr.api.model.DmnValidationResult.DmnValidationResultBuilder;
27 | import de.redsix.dmncheck.result.ValidationResult;
28 | import de.redsix.dmncheck.validators.core.Validator;
29 | import io.github.classgraph.ClassGraph;
30 | import io.github.classgraph.ClassInfoList;
31 | import io.github.classgraph.ScanResult;
32 |
33 | @Service
34 | public class AdvancedDmnCheckService {
35 |
36 | private static final String MESSAGE_NOT_CONNECTED_TABLE = "Element is not connected to requirement graph";
37 | private static final String ATTRIBUTE_NAME_RULE_ID = "id";
38 | private static final String SEQUEL_CORRESPONDING_RULE = "by rule ";
39 | private static final String VALIDATOR_PACKAGE = "de.redsix.dmncheck.validators";
40 | private static final String VALIDATOR_CORE_PACKAGE = "de.redsix.dmncheck.validators.core";
41 |
42 | private @MonotonicNonNull List validators;
43 |
44 | private String[] validatorPackages = new String[0];
45 |
46 | private String[] validatorClasses = {
47 | "de.redsix.dmncheck.validators.ConflictingRuleValidator",
48 | "de.redsix.dmncheck.validators.ShadowedRuleValidator",
49 | "de.redsix.dmncheck.validators.AggregationOutputTypeValidator",
50 | "de.redsix.dmncheck.validators.ConnectedRequirementGraphValidator",
51 | "de.redsix.dmncheck.validators.DuplicateRuleValidator",
52 | "de.redsix.dmncheck.validators.InputTypeDeclarationValidator",
53 | "de.redsix.dmncheck.validators.OutputTypeDeclarationValidator",
54 | "de.lv1871.oss.dmnmgr.domain.InputExpressionRequiredValidator",
55 | "de.lv1871.oss.dmnmgr.domain.OutputNameRequiredValidator",
56 | "de.lv1871.oss.dmnmgr.domain.InputEntryValidator",
57 | "de.lv1871.oss.dmnmgr.domain.OutputEntryValidator"
58 | };
59 |
60 | @PostConstruct
61 | public void init() {
62 | initValidators();
63 | }
64 |
65 | public DmnValidationResponse validateDecision(DecisionRequest decisionRequest) {
66 | var stream = new ByteArrayInputStream(decisionRequest.getXml().getBytes(StandardCharsets.UTF_8));
67 | return validateDecision(stream);
68 | }
69 |
70 | public DmnValidationResponse validateDecision(final InputStream file) {
71 | var response = new DmnValidationResponse();
72 | response.setErrors(new ArrayList<>());
73 | response.setWarnings(new ArrayList<>());
74 | try {
75 | final var dmnModelInstance = Dmn.readModelFromStream(file);
76 | final var validationResults = runValidators(dmnModelInstance);
77 |
78 | var results = validationResults
79 | .stream()
80 | .filter(result -> !result.getMessage().contains("parse"))
81 | .map(this::mapModel)
82 | .collect(Collectors.toList());
83 |
84 | response.setErrors(results
85 | .stream()
86 | .filter(result -> result.getSeverity() == ErrorSeverity.ERROR)
87 | .collect(Collectors.toList()));
88 |
89 | response.setWarnings(results
90 | .stream()
91 | .filter(result -> result.getSeverity() == ErrorSeverity.WARNING)
92 | .collect(Collectors.toList()));
93 |
94 | return response;
95 | }
96 | catch (Exception e) {
97 | var errors = Arrays.asList(
98 | DmnValidationResultBuilder
99 | .create()
100 | .withMessage(e.getMessage())
101 | .withSeverity(ErrorSeverity.ERROR)
102 | .build()
103 | );
104 | response.setErrors(errors);
105 | return response;
106 | }
107 | }
108 |
109 | private DmnValidationResult mapModel(ValidationResult result) {
110 | return DmnValidationResultBuilder
111 | .create()
112 | .withTableId(getTableId(result.getElement()))
113 | .withRuleId(getRuleId(result.getElement()))
114 | .withCellId(getCellId(result.getElement()))
115 | .withCounterRuleId(tryFindCounter(result.getMessage()))
116 | .withMessage(result.getMessage())
117 | .withSeverity(correctSeverity(result))
118 | .build();
119 | }
120 |
121 | public ErrorSeverity correctSeverity(ValidationResult result) {
122 | if (MESSAGE_NOT_CONNECTED_TABLE.equals(result.getMessage())) {
123 | return ErrorSeverity.WARNING;
124 | }
125 | return ErrorSeverity.ofSeverity(result.getSeverity());
126 | }
127 |
128 | private String tryFindCounter(String message) {
129 | if (message.contains(SEQUEL_CORRESPONDING_RULE)) {
130 | return Optional
131 | .ofNullable(message.split(SEQUEL_CORRESPONDING_RULE))
132 | .filter(arr -> arr.length > 1)
133 | .map(arr -> arr[arr.length - 1])
134 | .map(value -> value.trim())
135 | .orElse(null);
136 | }
137 | return null;
138 | }
139 |
140 | private String getTableId(ModelElementInstance instance) {
141 | if (isDecisionRule(instance)) {
142 | return Optional.ofNullable(instance)
143 | .map(ModelElementInstance::getParentElement)
144 | .map(ModelElementInstance::getParentElement)
145 | .map(element -> element.getAttributeValue(ATTRIBUTE_NAME_RULE_ID))
146 | .orElse(null);
147 | } else if(isLiteralExpression(instance)) {
148 | return Optional.ofNullable(instance)
149 | .map(ModelElementInstance::getParentElement)
150 | .map(ModelElementInstance::getParentElement)
151 | .map(ModelElementInstance::getParentElement)
152 | .map(element -> element.getAttributeValue(ATTRIBUTE_NAME_RULE_ID))
153 | .orElse(null);
154 | } else if(isOutputClause(instance)) {
155 | return Optional.ofNullable(instance)
156 | .map(ModelElementInstance::getParentElement)
157 | .map(ModelElementInstance::getParentElement)
158 | .map(element -> element.getAttributeValue(ATTRIBUTE_NAME_RULE_ID))
159 | .orElse(null);
160 | } else if(isInputEntry(instance) || isOutputEntry(instance)) {
161 | return Optional.ofNullable(instance)
162 | .map(ModelElementInstance::getParentElement)
163 | .map(ModelElementInstance::getParentElement)
164 | .map(ModelElementInstance::getParentElement)
165 | .map(element -> element.getAttributeValue(ATTRIBUTE_NAME_RULE_ID))
166 | .orElse(null);
167 | }
168 |
169 | return Optional.ofNullable(instance)
170 | .map(element -> element.getAttributeValue(ATTRIBUTE_NAME_RULE_ID))
171 | .orElse(null);
172 | }
173 |
174 | private boolean isInputEntry(ModelElementInstance instance) {
175 | return "inputEntry".equals(instance.getElementType().getTypeName());
176 | }
177 |
178 | private boolean isOutputEntry(ModelElementInstance instance) {
179 | return "outputEntry".equals(instance.getElementType().getTypeName());
180 | }
181 |
182 | private String getRuleId(ModelElementInstance instance) {
183 | if (isDecisionRule(instance)) {
184 | return instance.getAttributeValue(ATTRIBUTE_NAME_RULE_ID);
185 | } else if (isInputEntry(instance) || isOutputEntry(instance)) {
186 | return instance.getParentElement().getAttributeValue(ATTRIBUTE_NAME_RULE_ID);
187 | }
188 |
189 | return null;
190 | }
191 |
192 | private String getCellId(ModelElementInstance instance) {
193 | if (isInputEntry(instance) || isOutputEntry(instance)) {
194 | return instance.getAttributeValue(ATTRIBUTE_NAME_RULE_ID);
195 | }
196 | return null;
197 | }
198 |
199 | private boolean isDecisionRule(ModelElementInstance instance) {
200 | return "decisionRule".equals(instance.getElementType().getBaseType().getTypeName());
201 | }
202 |
203 | private boolean isLiteralExpression(ModelElementInstance instance) {
204 | return "literalExpression".equals(instance.getElementType().getBaseType().getTypeName());
205 | }
206 |
207 | private boolean isOutputClause(ModelElementInstance instance) {
208 | return "outputClause".equals(instance.getElementType().getBaseType().getTypeName());
209 | }
210 |
211 | private List runValidators(final DmnModelInstance dmnModelInstance) {
212 | return getValidators().stream()
213 | .flatMap(validator -> validator.apply(dmnModelInstance).stream())
214 | .collect(Collectors.toList());
215 | }
216 |
217 | private List getValidators() {
218 | return validators;
219 | }
220 |
221 | private void initValidators() {
222 |
223 | if (validatorPackages == null) {
224 | validatorPackages = new String[] {VALIDATOR_PACKAGE, VALIDATOR_PACKAGE + ".core"};
225 | }
226 |
227 | if (validatorClasses == null) {
228 | validatorClasses = new String[] { };
229 | }
230 |
231 | final ScanResult scanResult = new ClassGraph()
232 | .whitelistClasses(Validator.class.getName())
233 | .whitelistPackages(VALIDATOR_CORE_PACKAGE)
234 | .whitelistPackagesNonRecursive(validatorPackages)
235 | .whitelistClasses(validatorClasses)
236 | .scan();
237 |
238 | final ClassInfoList allValidatorClasses = scanResult.getClassesImplementing(Validator.class.getName());
239 |
240 | validators = allValidatorClasses.loadClasses(Validator.class).stream()
241 | .filter(validatorClass -> !Modifier.isAbstract(validatorClass.getModifiers()))
242 | .filter(validatorClass -> !Modifier.isInterface(validatorClass.getModifiers()))
243 | .map(this::instantiateValidator)
244 | .collect(Collectors.toList());
245 | }
246 |
247 | private Validator instantiateValidator(final Class extends Validator> validator) {
248 | try {
249 | return validator.getDeclaredConstructor().newInstance();
250 | }
251 | catch (IllegalAccessException | InstantiationException |
252 | NoSuchMethodException | InvocationTargetException e) {
253 | throw new RuntimeException("Failed to load validator " + validator, e);
254 | }
255 | }
256 |
257 | }
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/service/DecisionTestService.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.service;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.Optional;
6 | import java.util.Map.Entry;
7 |
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.stereotype.Service;
10 |
11 | import com.fasterxml.jackson.databind.node.ObjectNode;
12 |
13 | import de.lv1871.oss.dmnmgr.api.model.DecisionTestRequest;
14 | import de.lv1871.oss.dmnmgr.api.model.DecisionTestResponse;
15 | import de.lv1871.oss.dmnmgr.api.model.DecisionTestResponse.DecisionTestResponseBuilder;
16 |
17 | import static de.lv1871.oss.tester.test.dmnassert.DmnTest.testExpectation;
18 |
19 | @Service
20 | public class DecisionTestService {
21 |
22 | private static final String ERROR_SIZE_NOT_CORRECT = "Die Anzahl der Treffer weicht von der Erwartung ab.";
23 |
24 | @Autowired
25 | private GenericDecisionService decisionService;
26 |
27 | @Autowired
28 | private VariableMapperService mapperService;
29 |
30 | public DecisionTestResponse testDecision(DecisionTestRequest request) {
31 |
32 | var engine = decisionService.deployAndCreateEngine(request.getXml());
33 | var decisionSimulationResponse = decisionService
34 | .decide(engine, request.getDmnTableId(), request.getVariables());
35 |
36 | if (decisionSimulationResponse.getResult() == null) {
37 | // @formatter:off
38 | return DecisionTestResponseBuilder
39 | .create()
40 | .withMessage(decisionSimulationResponse.getMessage())
41 | .build();
42 | // @formatter:on
43 | }
44 |
45 | try {
46 |
47 | List> expectedDataAssertionFailed = new ArrayList<>();
48 | var message = decisionSimulationResponse.getMessage();
49 |
50 | for (ObjectNode expectedNode : request.getExpectedData()) {
51 | var expectedObjectMap = mapperService.getVariablesFromJsonAsMap(expectedNode);
52 | expectedDataAssertionFailed.addAll(testExpectation(decisionSimulationResponse, expectedObjectMap));
53 | }
54 |
55 | if (request.getExpectedData().size() != decisionSimulationResponse.getResult().size()) {
56 | message = Optional
57 | .ofNullable(message)
58 | .map(value -> String.format("%s; %s", value, ERROR_SIZE_NOT_CORRECT))
59 | .orElse(ERROR_SIZE_NOT_CORRECT);
60 | }
61 | // @formatter:off
62 | return DecisionTestResponseBuilder
63 | .create()
64 | .withResult(decisionSimulationResponse.getResult())
65 | .withResultRuleIds(decisionSimulationResponse.getResultRuleIds())
66 | .withExpectedDataAssertionFailed(expectedDataAssertionFailed)
67 | .withTestSucceeded(expectedDataAssertionFailed.size() < 1 &&
68 | !Optional.ofNullable(message).isPresent())
69 | .withMessage(message)
70 | .build();
71 | // @formatter:on
72 | } catch (Exception e) {
73 | return DecisionTestResponseBuilder.create().withMessage(e.getMessage()).build();
74 | }
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/service/DownloadService.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.service;
2 |
3 | import java.io.InputStreamReader;
4 | import java.net.URL;
5 |
6 | import org.springframework.stereotype.Service;
7 |
8 | import com.google.common.base.Charsets;
9 | import com.google.common.io.CharStreams;
10 |
11 | import de.lv1871.oss.dmnmgr.api.model.OpenApiDefinitionResponse;
12 |
13 | @Service
14 | public class DownloadService {
15 |
16 | public OpenApiDefinitionResponse downloadOpenApiDefinition(String urlString) {
17 | try {
18 | var url = new URL(urlString);
19 |
20 | String readUrlContents;
21 | try (InputStreamReader reader = new InputStreamReader(url.openStream(), Charsets.UTF_8)) {
22 | readUrlContents = CharStreams.toString(reader);
23 | }
24 |
25 | var response = new OpenApiDefinitionResponse();
26 | response.setApiUrl(urlString);
27 | response.setSwaggerDefinition(readUrlContents);
28 |
29 | return response;
30 | } catch (Exception exception) {
31 | throw new RuntimeException(exception);
32 | }
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/service/GenericDecisionService.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.service;
2 |
3 | import org.springframework.beans.factory.annotation.Autowired;
4 | import org.springframework.stereotype.Service;
5 |
6 | import com.fasterxml.jackson.databind.node.ObjectNode;
7 |
8 | import de.lv1871.oss.dmnmgr.api.model.DecisionSimulationRequest;
9 | import de.lv1871.oss.tester.test.dmnassert.model.DecisionSimulationResponse;
10 | import de.lv1871.oss.tester.test.dmnassert.model.DecisionSimulationResponse.DecisionTestCaseResponseBuilder;
11 | import de.lv1871.oss.tester.test.domain.DecisionEngine;
12 |
13 | @Service
14 | public class GenericDecisionService {
15 |
16 | @Autowired
17 | private VariableMapperService mapperService;
18 |
19 | public DecisionSimulationResponse decide(DecisionSimulationRequest decisionRequest) {
20 | try {
21 | var engine = deployAndCreateEngine(decisionRequest.getXml());
22 | return decide(engine, decisionRequest.getDmnTableId(), decisionRequest.getVariables());
23 | } catch (Exception exception) {
24 | exception.printStackTrace();
25 | if (exception.getCause() != null) {
26 | return DecisionTestCaseResponseBuilder.create().withMessage(exception.getCause().getMessage()).build();
27 | }
28 | return DecisionTestCaseResponseBuilder.create().withMessage(exception.getMessage()).build();
29 | }
30 | }
31 |
32 | public DecisionSimulationResponse decide(DecisionEngine engine, String dmnTableId, ObjectNode variablesNode) {
33 |
34 | try {
35 | var variables = mapperService.getVariablesFromJsonAsMap(variablesNode);
36 | var decisionResult = engine.evaluateDecisionByKey(dmnTableId, variables);
37 |
38 | if (decisionResult.getResultList().stream().filter(result -> result.get(null) != null).count() > 0) {
39 | return DecisionTestCaseResponseBuilder.create()
40 | .withMessage("Ein oder meherere Output-Felder haben keinen Namen.").build();
41 | }
42 |
43 | return DecisionTestCaseResponseBuilder.create().withResultRuleIds(engine.getResultRules(dmnTableId))
44 | .withResultTableRuleIds(engine.getResultRulesAllTables()).withResult(decisionResult.getResultList())
45 | .build();
46 | } catch (Exception exception) {
47 | exception.printStackTrace();
48 | if (exception.getCause() != null) {
49 | return DecisionTestCaseResponseBuilder.create().withMessage(exception.getCause().getMessage()).build();
50 | }
51 | return DecisionTestCaseResponseBuilder.create().withMessage(exception.getMessage()).build();
52 | }
53 | }
54 |
55 | public DecisionEngine deployAndCreateEngine(String dmnXml) {
56 | return DecisionEngine
57 | .createEngine()
58 | .parseDecision(dmnXml);
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/java/de/lv1871/oss/dmnmgr/service/VariableMapperService.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.service;
2 |
3 | import java.io.IOException;
4 | import java.util.HashMap;
5 |
6 | import javax.annotation.PostConstruct;
7 |
8 | import org.springframework.stereotype.Service;
9 |
10 | import com.fasterxml.jackson.databind.ObjectMapper;
11 | import com.fasterxml.jackson.databind.SerializationFeature;
12 | import com.fasterxml.jackson.databind.node.ObjectNode;
13 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
14 |
15 | @Service
16 | public class VariableMapperService {
17 |
18 | private static ObjectMapper mapper;
19 |
20 | @SuppressWarnings("unchecked")
21 | public HashMap getVariablesFromJsonAsMap(ObjectNode json) {
22 | var valueAsString = json.toString();
23 | try {
24 | return mapper.readValue(valueAsString, HashMap.class);
25 | } catch (IOException e) {
26 | throw new RuntimeException(e.getMessage());
27 | }
28 | }
29 |
30 | @PostConstruct
31 | private static void initMapper() {
32 | mapper = new ObjectMapper();
33 | mapper.registerModule(new JavaTimeModule());
34 | mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/dmnmgr/src/main/resources/META-INF/processes.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidibl/dmnmgr-server/1e4871a09d23f8e1d9fae6261bf4ad98ee941c9e/dmnmgr/src/main/resources/META-INF/processes.xml
--------------------------------------------------------------------------------
/dmnmgr/src/main/resources/META-INF/services/javax.ws.rs.ext.RuntimeDelegate:
--------------------------------------------------------------------------------
1 | org.jboss.resteasy.spi.ResteasyProviderFactory
--------------------------------------------------------------------------------
/dmnmgr/src/main/resources/application.yml:
--------------------------------------------------------------------------------
1 | spring:
2 | profiles:
3 | active: development
4 |
5 | ---
6 |
7 | spring:
8 | profiles: development
9 | server:
10 | port: 11401
11 | logging:
12 | file: log.log
13 | level:
14 | org:
15 | springframework:
16 | web: INFO
17 |
18 | ---
19 |
20 | spring:
21 | profiles: test
22 | server:
23 | port: 11401
24 | logging:
25 | file: log.log
26 | level:
27 | org:
28 | springframework:
29 | web: INFO
30 |
31 | ---
32 |
33 | spring:
34 | profiles: consolidation
35 | server:
36 | port: 11401
37 | logging:
38 | file: log.log
39 | level:
40 | org:
41 | springframework:
42 | web: INFO
43 |
44 | ---
45 |
46 | spring:
47 | profiles: production
48 | server:
49 | port: 11401
50 | logging:
51 | file: log.log
52 | level:
53 | org:
54 | springframework:
55 | web: INFO
--------------------------------------------------------------------------------
/dmnmgr/src/test/java/de/lv1871/oss/dmnmgr/domain/CheckerFunctionsTest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.domain;
2 |
3 | import static org.junit.Assert.assertFalse;
4 | import static org.junit.Assert.assertTrue;
5 |
6 | import org.junit.Test;
7 |
8 | public class CheckerFunctionsTest {
9 |
10 | @Test
11 | public void testFindsUnescapedQuotationMark() {
12 | String value = "Hallo\" Welt";
13 | Boolean result = CheckerFunctions.containesUnquotedtQuotationMark(value);
14 | assertTrue(result);
15 | }
16 |
17 | @Test
18 | public void testFindsUnescapedQuotationMarkWithOneCorrect() {
19 | String value = "Hal\\\"lo\" Welt";
20 | Boolean result = CheckerFunctions.containesUnquotedtQuotationMark(value);
21 | assertTrue(result);
22 | }
23 |
24 | @Test
25 | public void testFindsEscapedQuotationMark() {
26 | String value = "Hallo\\\" Welt";
27 | Boolean result = CheckerFunctions.containesUnquotedtQuotationMark(value);
28 | assertFalse(result);
29 | }
30 | }
--------------------------------------------------------------------------------
/dmnmgr/src/test/java/de/lv1871/oss/dmnmgr/service/AdvancedDmnCheckServiceTest.java:
--------------------------------------------------------------------------------
1 | package de.lv1871.oss.dmnmgr.service;
2 |
3 | import static org.junit.Assert.assertEquals;
4 | import static org.junit.Assert.assertNotNull;
5 | import static org.junit.Assert.assertTrue;
6 |
7 | import java.util.Arrays;
8 | import java.util.List;
9 | import java.util.stream.Collectors;
10 |
11 | import org.junit.Before;
12 | import org.junit.Test;
13 |
14 | import de.lv1871.oss.dmnmgr.api.model.DmnValidationResponse;
15 | import de.lv1871.oss.dmnmgr.api.model.DmnValidationResult;
16 |
17 | public class AdvancedDmnCheckServiceTest {
18 |
19 | private AdvancedDmnCheckService cut;
20 |
21 | @Before
22 | public void init() {
23 | cut = new AdvancedDmnCheckService();
24 | cut.init();
25 | }
26 |
27 | @Test
28 | public void testCheckHiddenRule() {
29 | DmnValidationResponse results = cut.validateDecision(this.getClass().getResourceAsStream("/meineTestDecision.dmn"));
30 |
31 | List decisionTable = results.getErrors().stream().map(DmnValidationResult::getTableId).distinct().collect(Collectors.toList());
32 | List decisionRules = results.getErrors().stream().map(DmnValidationResult::getRuleId).distinct().collect(Collectors.toList());
33 |
34 | assertNotNull(results);
35 |
36 | assertEquals(1, decisionTable.size());
37 | assertEquals("decision", decisionTable.get(0));
38 |
39 | assertEquals(2, decisionRules.size());
40 | assertTrue(allContained(decisionRules, Arrays.asList("DecisionRule_0fuuqy7", "DecisionRule_1op4sed")));
41 | }
42 |
43 | @Test
44 | public void testInputExpressionRequired() {
45 | DmnValidationResponse results = cut.validateDecision(this.getClass().getResourceAsStream("/requiredInputExpressionTest.dmn"));
46 |
47 | List decisionTable = results.getErrors().stream().map(DmnValidationResult::getTableId).distinct().collect(Collectors.toList());
48 |
49 | assertNotNull(results);
50 | assertEquals(0, results.getWarnings().size());
51 | assertEquals(1, results.getErrors().size());
52 | assertEquals("decision", decisionTable.get(0));
53 | assertEquals("InputExpression has no expressiontext", results.getErrors().get(0).getMessage());
54 |
55 | }
56 |
57 | @Test
58 | public void testInputExpressionTokenizedHasnoError() {
59 | DmnValidationResponse results = cut.validateDecision(this.getClass().getResourceAsStream("/testTokenizedExpression.dmn"));
60 |
61 | assertNotNull(results);
62 | assertEquals(0, results.getWarnings().size());
63 | assertEquals(0, results.getErrors().size());
64 | }
65 |
66 | @Test
67 | public void testInputExpressionTokenizedWithError() {
68 | DmnValidationResponse results = cut.validateDecision(this.getClass().getResourceAsStream("/testTokenizedWithErrorExpression.dmn"));
69 |
70 | assertNotNull(results);
71 | assertEquals(0, results.getWarnings().size());
72 | assertEquals(1, results.getErrors().size());
73 |
74 | }
75 |
76 | @Test
77 | public void testOutputNameRequired() {
78 | DmnValidationResponse results = cut.validateDecision(this.getClass().getResourceAsStream("/requiredOutputNameTest.dmn"));
79 |
80 | List decisionTable = results.getErrors().stream().map(DmnValidationResult::getTableId).distinct().collect(Collectors.toList());
81 |
82 | assertNotNull(results);
83 | assertEquals(0, results.getWarnings().size());
84 | assertEquals(1, results.getErrors().size());
85 | assertEquals("decision", decisionTable.get(0));
86 | assertEquals("Output has no name", results.getErrors().get(0).getMessage());
87 |
88 | }
89 |
90 | @Test
91 | public void testJuelGetsBasicValidation() {
92 | DmnValidationResponse results = cut.validateDecision(this.getClass().getResourceAsStream("/juelfeel.dmn"));
93 |
94 | List decisionTable = results.getErrors().stream().map(DmnValidationResult::getTableId).distinct().collect(Collectors.toList());
95 |
96 | assertNotNull(results);
97 | assertEquals(0, results.getWarnings().size());
98 | assertEquals(3, results.getErrors().size());
99 | assertEquals("decision", decisionTable.get(0));
100 | }
101 |
102 | private boolean allContained(List decisionRules, List expectedToBeContained) {
103 | return expectedToBeContained
104 | .stream()
105 | .filter(value -> !decisionRules.contains(value))
106 | .count() < 1;
107 | }
108 | }
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/juelfeel.dmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | test
8 |
9 |
10 |
11 |
12 |
13 | test = "hallo"
14 |
15 |
16 | "Welt"
17 |
18 |
19 |
20 |
21 | "hallo
22 |
23 |
24 | "Huhu"
25 |
26 |
27 |
28 |
29 | Welt"
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | "hui", "bui"
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | "krass"
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/juelfeel.dmnapp.json:
--------------------------------------------------------------------------------
1 | {
2 | "dmnPath": "juelfeel.dmn",
3 | "testsuite": {
4 | "decision": {
5 | "tests": []
6 | },
7 | "null": {
8 | "tests": []
9 | }
10 | },
11 | "definitions": {
12 | "decision": {
13 | "requestModel": {
14 | "type": "object",
15 | "properties": [
16 | {
17 | "type": "string",
18 | "name": "test"
19 | }
20 | ]
21 | }
22 | }
23 | },
24 | "plugins": []
25 | }
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/meineTestDecision.dmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | input
11 |
12 |
13 |
14 |
15 |
16 | "Hallo"
17 |
18 |
19 | "Welt"
20 |
21 |
22 |
23 |
24 | "Wow"
25 |
26 |
27 | "Cool"
28 |
29 |
30 |
31 |
32 | "Hallo"
33 |
34 |
35 | "zwei"
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/meineTestDecision.dmnapp.json:
--------------------------------------------------------------------------------
1 | {"dmnPath":"meineTestDecision.dmn","testsuite":{"null":{"tests":[]},"definitions_09waf08":{"tests":[]},"decision":{"tests":[]}},"definitions":{},"plugins":[]}
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/requiredInputExpressionTest.dmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | "huch"
14 |
15 |
16 | "Welt"
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/requiredInputExpressionTest.dmnapp.json:
--------------------------------------------------------------------------------
1 | {"dmnPath":"requiredInputExpressionTest.dmn","testsuite":{"null":{"tests":[]},"decision":{"tests":[]}},"definitions":{},"plugins":[]}
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/requiredOutputNameTest.dmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | "eins"
8 |
9 |
10 |
11 |
12 |
13 | "Hallo"
14 |
15 |
16 | "Welt"
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/requiredOutputNameTest.dmnapp.json:
--------------------------------------------------------------------------------
1 | {"dmnPath":"requiredOutputNameTest.dmn","testsuite":{"null":{"tests":[]},"decision":{"tests":[]}},"definitions":{},"plugins":[]}
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/testTokenizedExpression.dmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | test
8 |
9 |
10 |
11 |
12 |
13 | "Hallo", null
14 |
15 |
16 | "Welt"
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/testTokenizedExpression.dmnapp.json:
--------------------------------------------------------------------------------
1 | {
2 | "dmnPath": "testTokenizedExpression.dmn",
3 | "testsuite": {
4 | "decision": {
5 | "tests": []
6 | }
7 | },
8 | "definitions": {},
9 | "plugins": []
10 | }
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/testTokenizedWithErrorExpression.dmn:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | input
8 |
9 |
10 |
11 |
12 |
13 | "Hallo, "Hier"
14 |
15 |
16 | "Welt"
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/dmnmgr/src/test/resources/testTokenizedWithErrorExpression.dmnapp.json:
--------------------------------------------------------------------------------
1 | {
2 | "dmnPath": "testTokenizedWithErrorExpression.dmn",
3 | "testsuite": {
4 | "null": {
5 | "tests": []
6 | },
7 | "decision": {
8 | "tests": []
9 | }
10 | },
11 | "definitions": {},
12 | "plugins": []
13 | }
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 | 4.0.0
3 |
4 | de.lv1871.oss
5 | dmnmgr-project
6 | 1.3.0-SNAPSHOT
7 | pom
8 |
9 | dmnmgr-project
10 | http://maven.apache.org
11 |
12 |
13 | dmnmgr-tester
14 | dmnmgr-test-driver
15 | dmnmgr
16 |
17 |
18 |
19 | UTF-8
20 | 3.1
21 | 11
22 | 11
23 | 2.5.3
24 | 7.11.0
25 | 1.3.0
26 | 2.2.5.RELEASE
27 | 1.3.1
28 | 2.1
29 | 1.2
30 | 2.2.0.RELEASE
31 | 4.12
32 | 2.10.0
33 | 2.5.0
34 | 1.4.193
35 | 1.10.1
36 | 1.1.4
37 |
38 |
39 |
40 | https://github.com/davidibl/dmnmgr-server
41 | scm:git:git://github.com:davidibl/dmnmgr-server.git
42 | scm:git:git@github.com:davidibl/dmnmgr-server.git
43 | HEAD
44 |
45 |
46 |
47 | LV 1871
48 | https://lv1871.de/
49 |
50 |
51 |
52 |
53 | David Ibl
54 |
55 |
56 |
57 |
58 |
59 | The Apache Software License, Version 2.0
60 | http://www.apache.org/licenses/LICENSE-2.0.txt
61 | repo
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | org.camunda.bpm.dmn
71 | camunda-engine-dmn
72 | ${camunda.version}
73 |
74 |
75 | org.camunda.bpm
76 | camunda-engine
77 | ${camunda.version}
78 |
79 |
80 | org.camunda.bpm.extension.dmn.scala
81 | dmn-engine-camunda-plugin
82 | ${camunda.dmn.plugin.version}
83 |
84 |
85 | org.camunda.bpm.extension.feel.scala
86 | feel-engine-plugin
87 | ${feel.scala.version}
88 |
89 |
90 | org.camunda.bpm.extension.feel.scala
91 | feel-engine-factory
92 | ${feel.scala.version}
93 |
94 |
95 | org.camunda.bpm.extension.feel.scala
96 | feel-engine
97 | ${feel.scala.version}
98 |
99 |
100 |
101 |
102 | org.springframework.boot
103 | spring-boot-configuration-processor
104 | true
105 | ${springframework.boot.version}
106 |
107 |
108 | org.springframework.boot
109 | spring-boot-starter-web
110 | ${springframework.boot.version}
111 |
112 |
113 | org.springframework.boot
114 | spring-boot-starter-logging
115 | ${springframework.boot.version}
116 |
117 |
118 | org.springframework.boot
119 | spring-boot-starter-actuator
120 | ${springframework.boot.version}
121 |
122 |
123 |
124 |
125 | com.fasterxml.jackson.datatype
126 | jackson-datatype-jsr310
127 | ${jackson.version}
128 |
129 |
130 | com.fasterxml.jackson.core
131 | jackson-annotations
132 | ${jackson.version}
133 |
134 |
135 | com.fasterxml.jackson.core
136 | jackson-core
137 | ${jackson.version}
138 |
139 |
140 | com.fasterxml.jackson.core
141 | jackson-databind
142 | ${jackson.version}
143 |
144 |
145 |
146 |
147 | com.h2database
148 | h2
149 | ${h2.version}
150 |
151 |
152 | io.springfox
153 | springfox-swagger-ui
154 | ${swagger.version}
155 |
156 |
157 | io.springfox
158 | springfox-swagger2
159 | ${swagger.version}
160 |
161 |
162 | de.redsix
163 | dmn-check
164 | ${redsix.dmn-check.version}
165 |
166 |
167 | de.redsix
168 | dmn-check-core
169 | ${redsix.dmn-check.version}
170 |
171 |
172 | de.redsix
173 | dmn-check-validators
174 | ${redsix.dmn-check.version}
175 |
176 |
177 |
178 |
179 | junit
180 | junit
181 | ${junit.version}
182 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/readme_assets/general_architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidibl/dmnmgr-server/1e4871a09d23f8e1d9fae6261bf4ad98ee941c9e/readme_assets/general_architecture.png
--------------------------------------------------------------------------------