diff --git a/kogito-codegen-modules/kogito-codegen-decisions/src/main/java/org/kie/kogito/codegen/decision/CodegenUtils.java b/kogito-codegen-modules/kogito-codegen-decisions/src/main/java/org/kie/kogito/codegen/decision/CodegenUtils.java new file mode 100644 index 00000000000..52c03e7c315 --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-decisions/src/main/java/org/kie/kogito/codegen/decision/CodegenUtils.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.codegen.decision; + +import org.kie.dmn.api.core.DMNModel; + +public class CodegenUtils { + + private CodegenUtils() { + } + + public static String getDefinitionsFileFromModel(DMNModel dmnModel) { + String modelName = geNameForDefinitionsFile(dmnModel); + return modelName.replace(" ", "_").replace(".dmn", ".json"); + } + + static String geNameForDefinitionsFile(DMNModel dmnModel) { + if (dmnModel.getResource() != null && dmnModel.getResource().getSourcePath() != null) { + String resourcePath = dmnModel.getResource().getSourcePath().replace('\\', '/'); + return resourcePath.contains("/") ? resourcePath.substring(resourcePath.lastIndexOf('/') + 1) : resourcePath; + } else { + return dmnModel.getName() + ".dmn"; + } + + } + +} \ No newline at end of file diff --git a/kogito-codegen-modules/kogito-codegen-decisions/src/main/java/org/kie/kogito/codegen/decision/DecisionCodegen.java b/kogito-codegen-modules/kogito-codegen-decisions/src/main/java/org/kie/kogito/codegen/decision/DecisionCodegen.java index 08a6b97da71..b0944b54099 100644 --- a/kogito-codegen-modules/kogito-codegen-decisions/src/main/java/org/kie/kogito/codegen/decision/DecisionCodegen.java +++ b/kogito-codegen-modules/kogito-codegen-decisions/src/main/java/org/kie/kogito/codegen/decision/DecisionCodegen.java @@ -22,7 +22,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -66,6 +65,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import static java.util.stream.Collectors.toList; +import static org.kie.kogito.codegen.decision.CodegenUtils.getDefinitionsFileFromModel; public class DecisionCodegen extends AbstractGenerator { @@ -134,22 +134,11 @@ private void generateAndStoreRestResources() { List rgs = new ArrayList<>(); // REST resources List models = resources.stream().map(DMNResource::getDmnModel).collect(Collectors.toList()); - DMNOASResult oasResult = null; - try { - Comparator nsNameComparator = Comparator.comparing(DMNModel::getNamespace).thenComparing(DMNModel::getName); - List orderedModels = new ArrayList<>(models); - Collections.sort(orderedModels, nsNameComparator); - oasResult = DMNOASGeneratorFactory.generator(orderedModels).build(); - String jsonContent = new ObjectMapper().writeValueAsString(oasResult.getJsonSchemaNode()); - storeFile(GeneratedFileType.STATIC_HTTP_RESOURCE, "dmnDefinitions.json", jsonContent); - } catch (Exception e) { - LOGGER.error("Error while trying to generate OpenAPI specification for the DMN models", e); - } - for (DMNModel model : models) { if (model.getName() == null || model.getName().isEmpty()) { throw new RuntimeException("Model name should not be empty"); } + DMNOASResult oasResult = generateAndStoreDefinitionsJson(model); boolean stronglyTypedEnabled = Optional.ofNullable(context()) .flatMap(c -> c.getApplicationProperty(STRONGLY_TYPED_CONFIGURATION_KEY)) @@ -198,6 +187,19 @@ private void generateAndStoreRestResources() { } } + private DMNOASResult generateAndStoreDefinitionsJson(DMNModel dmnModel) { + DMNOASResult toReturn = null; + try { + toReturn = DMNOASGeneratorFactory.generator(Collections.singleton(dmnModel)).build(); + String jsonContent = new ObjectMapper().writeValueAsString(toReturn.getJsonSchemaNode()); + final String DMN_DEFINITIONS_JSON = getDefinitionsFileFromModel(dmnModel); + storeFile(GeneratedFileType.STATIC_HTTP_RESOURCE, DMN_DEFINITIONS_JSON, jsonContent); + } catch (Exception e) { + LOGGER.error("Error while trying to generate OpenAPI specification for the DMN models", e); + } + return toReturn; + } + private void generateAndStoreDecisionModelResourcesProvider() { final DecisionModelResourcesProviderGenerator generator = new DecisionModelResourcesProviderGenerator(context(), applicationCanonicalName(), diff --git a/kogito-codegen-modules/kogito-codegen-decisions/src/main/java/org/kie/kogito/codegen/decision/DecisionRestResourceGenerator.java b/kogito-codegen-modules/kogito-codegen-decisions/src/main/java/org/kie/kogito/codegen/decision/DecisionRestResourceGenerator.java index 89ddb88e6f2..93cbb98d324 100644 --- a/kogito-codegen-modules/kogito-codegen-decisions/src/main/java/org/kie/kogito/codegen/decision/DecisionRestResourceGenerator.java +++ b/kogito-codegen-modules/kogito-codegen-decisions/src/main/java/org/kie/kogito/codegen/decision/DecisionRestResourceGenerator.java @@ -62,6 +62,7 @@ import static com.github.javaparser.StaticJavaParser.parseStatement; import static java.util.function.Predicate.not; +import static org.kie.kogito.codegen.decision.CodegenUtils.getDefinitionsFileFromModel; public class DecisionRestResourceGenerator { @@ -203,7 +204,7 @@ private void processOASAnn(MethodDeclaration dmnMethod, DecisionService ds) { inputRef = withOASResult.getNamingPolicy().getRef(identifyInputSet); outputRef = withOASResult.getNamingPolicy().getRef(identifyOutputSet); } - final String DMN_DEFINITIONS_JSON = "/dmnDefinitions.json"; + final String DMN_DEFINITIONS_JSON = "/" + getDefinitionsFileFromModel(dmnModel); // MP / Quarkus final String Q_CTX_PATH = context.getApplicationProperty("quarkus.http.root-path").filter(not("/"::equals)).orElse(""); processAnnForRef(dmnMethod, diff --git a/kogito-codegen-modules/kogito-codegen-decisions/src/test/java/org/kie/kogito/codegen/decision/CodegenUtilsTest.java b/kogito-codegen-modules/kogito-codegen-decisions/src/test/java/org/kie/kogito/codegen/decision/CodegenUtilsTest.java new file mode 100644 index 00000000000..9accd555300 --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-decisions/src/test/java/org/kie/kogito/codegen/decision/CodegenUtilsTest.java @@ -0,0 +1,93 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.kie.kogito.codegen.decision; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.nio.charset.StandardCharsets; +import java.util.Collections; + +import org.drools.io.FileSystemResource; +import org.drools.util.FileUtils; +import org.junit.jupiter.api.Test; +import org.kie.api.io.Resource; +import org.kie.dmn.api.core.DMNModel; +import org.kie.dmn.api.core.DMNRuntime; +import org.kie.dmn.core.internal.utils.DMNRuntimeBuilder; +import org.kie.kogito.dmn.DMNKogito; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CodegenUtilsTest { + + @Test + void getDefinitionsFileFromModelWithSpace() { + File dmnFile = FileUtils.getFile("Traffic Violation.dmn"); + assertNotNull(dmnFile); + assertTrue(dmnFile.exists()); + Resource dmnResource = new FileSystemResource(dmnFile, StandardCharsets.UTF_8.name()); + + DMNRuntime dmnRuntime = DMNRuntimeBuilder.fromDefaults() + .setRootClassLoader(Thread.currentThread().getContextClassLoader()) + .buildConfiguration() + .fromResources(Collections.singleton(dmnResource)) + .getOrElseThrow(e -> new RuntimeException("Error compiling DMN model(s)", e)); + assertThat(dmnRuntime.getModels()).hasSize(1); + final DMNModel dmnModel = dmnRuntime.getModel("https://github.com/kiegroup/drools/kie-dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF", "Traffic Violation Model Name"); + assertNotNull(dmnModel); + String expected = "Traffic_Violation.json"; + assertEquals(expected, CodegenUtils.getDefinitionsFileFromModel(dmnModel)); + } + + @Test + void geNameForDefinitionsFileWithSourcePath() { + File dmnFile = FileUtils.getFile("Traffic Violation.dmn"); + assertNotNull(dmnFile); + assertTrue(dmnFile.exists()); + Resource dmnResource = new FileSystemResource(dmnFile, StandardCharsets.UTF_8.name()); + DMNRuntime dmnRuntime = DMNRuntimeBuilder.fromDefaults() + .setRootClassLoader(Thread.currentThread().getContextClassLoader()) + .buildConfiguration() + .fromResources(Collections.singleton(dmnResource)) + .getOrElseThrow(e -> new RuntimeException("Error compiling DMN model(s)", e)); + assertThat(dmnRuntime.getModels()).hasSize(1); + final DMNModel dmnModel = dmnRuntime.getModel("https://github.com/kiegroup/drools/kie-dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF", "Traffic Violation Model Name"); + assertNotNull(dmnModel); + String expected = "Traffic Violation.dmn"; + assertEquals(expected, CodegenUtils.geNameForDefinitionsFile(dmnModel)); + } + + @Test + void geNameForDefinitionsFileWithoutSourcePath() throws FileNotFoundException { + File dmnFile = FileUtils.getFile("Traffic Violation.dmn"); + assertNotNull(dmnFile); + assertTrue(dmnFile.exists()); + DMNRuntime dmnRuntime = DMNKogito.createGenericDMNRuntime(new FileReader(dmnFile)); + assertNotNull(dmnRuntime); + assertThat(dmnRuntime.getModels()).hasSize(1); + final DMNModel dmnModel = dmnRuntime.getModel("https://github.com/kiegroup/drools/kie-dmn/_A4BCA8B8-CF08-433F-93B2-A2598F19ECFF", "Traffic Violation Model Name"); + assertNotNull(dmnModel); + String expected = "Traffic Violation Model Name.dmn"; + assertEquals(expected, CodegenUtils.geNameForDefinitionsFile(dmnModel)); + } +} diff --git a/kogito-codegen-modules/kogito-codegen-decisions/src/test/resources/decision-model-space-name/Traffic Violation.dmn b/kogito-codegen-modules/kogito-codegen-decisions/src/test/resources/decision-model-space-name/Traffic Violation.dmn new file mode 100644 index 00000000000..7665bc2c908 --- /dev/null +++ b/kogito-codegen-modules/kogito-codegen-decisions/src/test/resources/decision-model-space-name/Traffic Violation.dmn @@ -0,0 +1,241 @@ + + + + + + string + + + number + + + string + + + string + + + number + + + + + string + + + date + + + string + + "speed", "parking", "driving under the influence" + + + + number + + + number + + + + + number + + + number + + + + + + + + + + + + + + Violation.Type + + + + + Violation.Actual Speed - Violation.Speed Limit + + + + + + + "speed" + + + [10..30) + + + 500 + + + 3 + + + + + "speed" + + + >= 30 + + + 1000 + + + 7 + + + + + "parking" + + + - + + + 100 + + + 1 + + + + + "driving under the influence" + + + - + + + 1000 + + + 5 + + + + + + + + + Should the driver be suspended due to points on his license? + "Yes", "No" + + + + + + + + + + + + Driver.Points + Fine.Points + + + + + if Total Points >= 20 then "Yes" else "No" + + + + + + + + + + 50.0 + 254.0 + 329.0 + 119.0 + 100.0 + 186.0 + + + 50.0 + 100.0 + 398.0 + + + 398.0 + + + 398.0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/quarkus/extensions/kogito-quarkus-decisions-extension/kogito-quarkus-decisions-integration-test/src/test/java/org/kie/kogito/quarkus/dmn/DMNIT.java b/quarkus/extensions/kogito-quarkus-decisions-extension/kogito-quarkus-decisions-integration-test/src/test/java/org/kie/kogito/quarkus/dmn/DMNIT.java index 8afe0a2f321..a931ee03972 100644 --- a/quarkus/extensions/kogito-quarkus-decisions-extension/kogito-quarkus-decisions-integration-test/src/test/java/org/kie/kogito/quarkus/dmn/DMNIT.java +++ b/quarkus/extensions/kogito-quarkus-decisions-extension/kogito-quarkus-decisions-integration-test/src/test/java/org/kie/kogito/quarkus/dmn/DMNIT.java @@ -38,7 +38,12 @@ public class DMNIT { @Test public void testOASdmnDefinitions() { RestAssured.given() - .get("/dmnDefinitions.json") + .get("/OneOfEachType.json") + .then() + .statusCode(200) + .body("definitions", aMapWithSize(greaterThan(0))); + RestAssured.given() + .get("/a.json") .then() .statusCode(200) .body("definitions", aMapWithSize(greaterThan(0))); diff --git a/quarkus/extensions/kogito-quarkus-extension/kogito-quarkus-integration-test-maven-devmode/src/test/java/io/quarkus/it/kogito/devmode/DevMojoIT.java b/quarkus/extensions/kogito-quarkus-extension/kogito-quarkus-integration-test-maven-devmode/src/test/java/io/quarkus/it/kogito/devmode/DevMojoIT.java index b206fab7b26..c8edef6f27c 100644 --- a/quarkus/extensions/kogito-quarkus-extension/kogito-quarkus-integration-test-maven-devmode/src/test/java/io/quarkus/it/kogito/devmode/DevMojoIT.java +++ b/quarkus/extensions/kogito-quarkus-extension/kogito-quarkus-integration-test-maven-devmode/src/test/java/io/quarkus/it/kogito/devmode/DevMojoIT.java @@ -448,7 +448,7 @@ public void testStaticResource() throws MavenInvocationException { // static resource given().baseUri("http://localhost:" + httpPort) - .get("/dmnDefinitions.json") + .get("/hello.json") .then() .statusCode(200) .body("definitions", aMapWithSize(greaterThan(0))); diff --git a/quarkus/integration-tests/integration-tests-quarkus-decisions/src/test/java/org/kie/kogito/integrationtests/quarkus/OASIT.java b/quarkus/integration-tests/integration-tests-quarkus-decisions/src/test/java/org/kie/kogito/integrationtests/quarkus/OASIT.java index 578c38aafe4..9ffd072596f 100644 --- a/quarkus/integration-tests/integration-tests-quarkus-decisions/src/test/java/org/kie/kogito/integrationtests/quarkus/OASIT.java +++ b/quarkus/integration-tests/integration-tests-quarkus-decisions/src/test/java/org/kie/kogito/integrationtests/quarkus/OASIT.java @@ -20,8 +20,6 @@ import java.net.URL; -import org.junit.jupiter.api.Test; - import io.quarkus.test.common.http.TestHTTPResource; import io.quarkus.test.junit.QuarkusIntegrationTest; import io.restassured.RestAssured; @@ -30,6 +28,9 @@ import io.swagger.v3.parser.OpenAPIV3Parser; import io.swagger.v3.parser.core.models.ParseOptions; import io.swagger.v3.parser.core.models.SwaggerParseResult; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.Matchers.aMapWithSize; @@ -77,14 +78,23 @@ public void testOASisSwaggerUICompatible() { OpenAPI openAPI = result.getOpenAPI(); PathItem p1 = openAPI.getPaths().get("/" + DMN_MODEL_NAME); assertThat(p1).isNotNull(); - assertThat(p1.getPost().getRequestBody().getContent().get("application/json").getSchema().get$ref()).startsWith("/dmnDefinitions.json#"); - assertThat(p1.getPost().getResponses().getDefault().getContent().get("application/json").getSchema().get$ref()).startsWith("/dmnDefinitions.json#"); + assertThat(p1.getPost().getRequestBody().getContent().get("application/json").getSchema().get$ref()).startsWith("/basicAdd.json#"); + assertThat(p1.getPost().getResponses().getDefault().getContent().get("application/json").getSchema().get$ref()).startsWith("/basicAdd.json#"); } - @Test - public void testOASdmnDefinitions() { + @ParameterizedTest + @ValueSource(strings = { "basicAdd", + "DScoercion", + "ElementAtIndex", + "FaceMask", + "Hospitals", + "HospitalStatus", + "java_function_context", + "OneOfEachType", + "StatusService" }) + public void testOASdmnDefinitions(String name) { RestAssured.given() - .get("/dmnDefinitions.json") + .get("/" + name + ".json") .then() .statusCode(200) .body("definitions", aMapWithSize(greaterThan(0))); diff --git a/quarkus/integration-tests/integration-tests-quarkus-resteasy-classic/src/test/java/org/kie/kogito/quarkus/EndpointsIT.java b/quarkus/integration-tests/integration-tests-quarkus-resteasy-classic/src/test/java/org/kie/kogito/quarkus/EndpointsIT.java index d064772adf4..97b06919524 100644 --- a/quarkus/integration-tests/integration-tests-quarkus-resteasy-classic/src/test/java/org/kie/kogito/quarkus/EndpointsIT.java +++ b/quarkus/integration-tests/integration-tests-quarkus-resteasy-classic/src/test/java/org/kie/kogito/quarkus/EndpointsIT.java @@ -70,7 +70,7 @@ public void testGeneratedResource() { given() .contentType(ContentType.JSON) .when() - .get("/dmnDefinitions.json") + .get("/a.json") .then() .statusCode(200) .body("definitions.tAddress.type", is("object")); diff --git a/quarkus/integration-tests/integration-tests-quarkus-resteasy-reactive/src/test/java/org/kie/kogito/quarkus/EndpointsIT.java b/quarkus/integration-tests/integration-tests-quarkus-resteasy-reactive/src/test/java/org/kie/kogito/quarkus/EndpointsIT.java index d064772adf4..97b06919524 100644 --- a/quarkus/integration-tests/integration-tests-quarkus-resteasy-reactive/src/test/java/org/kie/kogito/quarkus/EndpointsIT.java +++ b/quarkus/integration-tests/integration-tests-quarkus-resteasy-reactive/src/test/java/org/kie/kogito/quarkus/EndpointsIT.java @@ -70,7 +70,7 @@ public void testGeneratedResource() { given() .contentType(ContentType.JSON) .when() - .get("/dmnDefinitions.json") + .get("/a.json") .then() .statusCode(200) .body("definitions.tAddress.type", is("object")); diff --git a/springboot/integration-tests/src/it/integration-tests-springboot-decisions-it/src/test/java/org/kie/kogito/integrationtests/springboot/OASTest.java b/springboot/integration-tests/src/it/integration-tests-springboot-decisions-it/src/test/java/org/kie/kogito/integrationtests/springboot/OASTest.java index f748db2ad3b..6b1227ba188 100644 --- a/springboot/integration-tests/src/it/integration-tests-springboot-decisions-it/src/test/java/org/kie/kogito/integrationtests/springboot/OASTest.java +++ b/springboot/integration-tests/src/it/integration-tests-springboot-decisions-it/src/test/java/org/kie/kogito/integrationtests/springboot/OASTest.java @@ -28,6 +28,8 @@ import io.swagger.v3.parser.core.models.SwaggerParseResult; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -78,14 +80,23 @@ public void testOASisSwaggerUICompatible() { OpenAPI openAPI = result.getOpenAPI(); PathItem p1 = openAPI.getPaths().get("/" + DMN_MODEL_NAME); assertThat(p1).isNotNull(); - assertThat(p1.getPost().getRequestBody().getContent().get("application/json").getSchema().get$ref()).startsWith("/dmnDefinitions.json#"); - assertThat(p1.getPost().getResponses().getDefault().getContent().get("application/json").getSchema().get$ref()).startsWith("/dmnDefinitions.json#"); + assertThat(p1.getPost().getRequestBody().getContent().get("application/json").getSchema().get$ref()).startsWith("/basicAdd.json#"); + assertThat(p1.getPost().getResponses().getDefault().getContent().get("application/json").getSchema().get$ref()).startsWith("/basicAdd.json#"); } - @Test - public void testOASdmnDefinitions() { + @ParameterizedTest + @ValueSource(strings = {"basicAdd", + "DScoercion", + "ElementAtIndex", + "FaceMask", + "Hospitals", + "HospitalStatus", + "java_function_context", + "OneOfEachType", + "StatusService"}) + public void testOASdmnDefinitions(String name) { RestAssured.given() - .get("/dmnDefinitions.json") + .get("/" + name + ".json") .then() .statusCode(200) .body("definitions", aMapWithSize(greaterThan(0)));