From a6a6ae0e2684b2d8681ec199ce1e89e942e1965f Mon Sep 17 00:00:00 2001 From: Kilian Schuettler Date: Tue, 22 Aug 2023 12:32:42 +0200 Subject: [PATCH] RED-7493: provide error messages in testRules Endpoint --- .../v1/model/DroolsSyntaxErrorMessage.java | 16 +++++ .../v1/model/DroolsSyntaxValidation.java | 17 ++++++ .../v1/resources/RedactionResource.java | 4 +- .../controller/RedactionController.java | 5 +- .../service/DroolsExecutionService.java | 40 ++++-------- .../DroolsSyntaxValidationFactory.java | 28 +++++++++ .../service/DroolsExecutionServiceTest.java | 61 +++++++++++++++++++ 7 files changed, 140 insertions(+), 31 deletions(-) create mode 100644 redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/model/DroolsSyntaxErrorMessage.java create mode 100644 redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/model/DroolsSyntaxValidation.java create mode 100644 redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsSyntaxValidationFactory.java create mode 100644 redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsExecutionServiceTest.java diff --git a/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/model/DroolsSyntaxErrorMessage.java b/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/model/DroolsSyntaxErrorMessage.java new file mode 100644 index 00000000..7c771d1c --- /dev/null +++ b/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/model/DroolsSyntaxErrorMessage.java @@ -0,0 +1,16 @@ +package com.iqser.red.service.redaction.v1.model; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.experimental.FieldDefaults; + +@Getter +@Builder +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class DroolsSyntaxErrorMessage { + + Integer line; + Integer column; + String message; +} diff --git a/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/model/DroolsSyntaxValidation.java b/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/model/DroolsSyntaxValidation.java new file mode 100644 index 00000000..ded4fe1c --- /dev/null +++ b/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/model/DroolsSyntaxValidation.java @@ -0,0 +1,17 @@ +package com.iqser.red.service.redaction.v1.model; + +import java.util.List; + +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.experimental.FieldDefaults; + +@Getter +@Builder +@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE) +public class DroolsSyntaxValidation { + + boolean compiled; + List droolsSyntaxErrorMessages; +} diff --git a/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/resources/RedactionResource.java b/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/resources/RedactionResource.java index 2c435cdd..177240a1 100644 --- a/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/resources/RedactionResource.java +++ b/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/resources/RedactionResource.java @@ -4,9 +4,11 @@ import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; +import com.iqser.red.service.redaction.v1.model.DroolsSyntaxValidation; + public interface RedactionResource { @PostMapping(value = "/rules/test", consumes = MediaType.APPLICATION_JSON_VALUE) - void testRules(@RequestBody String rules); + DroolsSyntaxValidation testRules(@RequestBody String rules); } diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/controller/RedactionController.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/controller/RedactionController.java index 28cc0cd9..4ab0be10 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/controller/RedactionController.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/controller/RedactionController.java @@ -3,6 +3,7 @@ package com.iqser.red.service.redaction.v1.server.controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import com.iqser.red.service.redaction.v1.model.DroolsSyntaxValidation; import com.iqser.red.service.redaction.v1.resources.RedactionResource; import com.iqser.red.service.redaction.v1.server.exception.RulesValidationException; import com.iqser.red.service.redaction.v1.server.redaction.service.DroolsExecutionService; @@ -19,10 +20,10 @@ public class RedactionController implements RedactionResource { @Override - public void testRules(@RequestBody String rules) { + public DroolsSyntaxValidation testRules(@RequestBody String rules) { try { - droolsExecutionService.testRules(rules); + return droolsExecutionService.testRules(rules); } catch (Exception e) { throw new RulesValidationException("Could not test rules: " + e.getMessage(), e); } diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsExecutionService.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsExecutionService.java index 1b0bf58e..d3e682a4 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsExecutionService.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsExecutionService.java @@ -7,17 +7,14 @@ import static java.util.stream.Collectors.toList; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.LinkedList; import java.util.List; -import java.util.UUID; import java.util.stream.Collector; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; -import org.drools.compiler.kie.builder.impl.InternalKieServices; import org.kie.api.KieServices; import org.kie.api.builder.KieBuilder; import org.kie.api.builder.KieFileSystem; @@ -28,24 +25,18 @@ import org.kie.api.runtime.rule.QueryResults; import org.kie.api.runtime.rule.QueryResultsRow; import org.springframework.stereotype.Service; -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hasher; -import com.google.common.hash.Hashing; -import com.google.common.io.ByteStreams; -import com.google.common.primitives.Bytes; -import com.google.common.primitives.Longs; import com.iqser.red.service.persistence.service.v1.api.shared.model.FileAttribute; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.ManualRedactions; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.BaseAnnotation; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.IdRemoval; import com.iqser.red.service.persistence.service.v1.api.shared.model.annotations.entitymapped.ManualForceRedaction; +import com.iqser.red.service.redaction.v1.model.DroolsSyntaxValidation; import com.iqser.red.service.redaction.v1.server.client.RulesClient; import com.iqser.red.service.redaction.v1.server.document.graph.nodes.Document; import com.iqser.red.service.redaction.v1.server.document.graph.nodes.SemanticNode; import com.iqser.red.service.redaction.v1.server.document.services.EntityCreationService; import com.iqser.red.service.redaction.v1.server.document.services.EntityEnrichmentService; import com.iqser.red.service.redaction.v1.server.document.services.ManualRedactionApplicationService; -import com.iqser.red.service.redaction.v1.server.exception.RulesValidationException; import com.iqser.red.service.redaction.v1.server.redaction.adapter.NerEntities; import com.iqser.red.service.redaction.v1.server.redaction.model.KieWrapper; import com.iqser.red.service.redaction.v1.server.redaction.model.dictionary.Dictionary; @@ -60,12 +51,15 @@ import lombok.extern.slf4j.Slf4j; @Slf4j @Service @RequiredArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = false) +@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true) public class DroolsExecutionService { - final RulesClient rulesClient; + RulesClient rulesClient; + + EntityEnrichmentService entityEnrichmentService; + + DroolsSyntaxValidationFactory droolsSyntaxValidationFactory; - final EntityEnrichmentService entityEnrichmentService; @Timed("redactmanager_executeRules") public List executeRules(KieContainer kieContainer, @@ -191,7 +185,7 @@ public class DroolsExecutionService { } - private void registerNewKieContainerVersion(String dossierTemplateId, long version, String rules) { + private KieBuilder registerNewKieContainerVersion(String dossierTemplateId, long version, String rules) { KieServices kieServices = KieServices.Factory.get(); KieFileSystem kieFileSystem = kieServices.newKieFileSystem(); @@ -199,26 +193,16 @@ public class DroolsExecutionService { InputStream input = new ByteArrayInputStream(rules.getBytes(StandardCharsets.UTF_8)); kieFileSystem.write("src/main/resources/drools/rules" + dossierTemplateId + ".drl", kieServices.getResources().newInputStreamResource(input)); KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem); - kieBuilder.buildAll(); + return kieBuilder.buildAll(); } - public void testRules(String rules) { + public DroolsSyntaxValidation testRules(String rules) { var versionId = System.currentTimeMillis(); var testRules = "test-rules"; - registerNewKieContainerVersion(testRules, versionId, rules); - var releaseId = getReleaseId(testRules, versionId); - try { - KieServices kieServices = KieServices.Factory.get(); - var containerId = UUID.randomUUID().toString(); - var container = kieServices.newKieContainer(containerId, releaseId); - container.newKieSession(); - container.dispose(); - - } catch (Exception e) { - throw new RulesValidationException("Could not update rules: " + e.getMessage(), e); - } + KieBuilder kieBuilder = registerNewKieContainerVersion(testRules, versionId, rules); + return droolsSyntaxValidationFactory.buildDroolsSyntaxValidation(kieBuilder); } } diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsSyntaxValidationFactory.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsSyntaxValidationFactory.java new file mode 100644 index 00000000..d8a769e3 --- /dev/null +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsSyntaxValidationFactory.java @@ -0,0 +1,28 @@ +package com.iqser.red.service.redaction.v1.server.redaction.service; + +import java.util.List; + +import org.kie.api.builder.KieBuilder; +import org.kie.api.builder.Message; +import org.springframework.stereotype.Service; + +import com.iqser.red.service.redaction.v1.model.DroolsSyntaxErrorMessage; +import com.iqser.red.service.redaction.v1.model.DroolsSyntaxValidation; + +@Service +public class DroolsSyntaxValidationFactory { + + public DroolsSyntaxValidation buildDroolsSyntaxValidation(KieBuilder kieBuilder) { + + List errorMessages = kieBuilder.getResults().getMessages(Message.Level.ERROR); + List droolsSyntaxErrorMessages = errorMessages.stream().map(this::buildDroolsSyntaxErrorMessage).toList(); + return DroolsSyntaxValidation.builder().compiled(droolsSyntaxErrorMessages.isEmpty()).droolsSyntaxErrorMessages(droolsSyntaxErrorMessages).build(); + } + + + public DroolsSyntaxErrorMessage buildDroolsSyntaxErrorMessage(Message message) { + + return DroolsSyntaxErrorMessage.builder().line(message.getLine()).column(message.getColumn()).message(message.getText()).build(); + } + +} diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsExecutionServiceTest.java b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsExecutionServiceTest.java new file mode 100644 index 00000000..d6fa2bd6 --- /dev/null +++ b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/redaction/service/DroolsExecutionServiceTest.java @@ -0,0 +1,61 @@ +package com.iqser.red.service.redaction.v1.server.redaction.service; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.MockitoAnnotations; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.core.io.ClassPathResource; + +import com.iqser.red.service.redaction.v1.model.DroolsSyntaxValidation; +import com.iqser.red.service.redaction.v1.server.client.RulesClient; +import com.iqser.red.service.redaction.v1.server.document.services.EntityEnrichmentService; + +import lombok.SneakyThrows; + +class DroolsExecutionServiceTest { + + @MockBean + RulesClient rulesClient; + @MockBean + EntityEnrichmentService entityEnrichmentService; + + + @BeforeEach + public void setupMocks() { + + MockitoAnnotations.openMocks(this); + } + + + @Test + @SneakyThrows + void testRules() { + + DroolsExecutionService droolsExecutionService = new DroolsExecutionService(rulesClient, entityEnrichmentService, new DroolsSyntaxValidationFactory()); + var rulesFile = new ClassPathResource("drools/rules.drl"); + + String rulesString = new String(rulesFile.getInputStream().readAllBytes()); + + DroolsSyntaxValidation droolsSyntaxValidation = droolsExecutionService.testRules(rulesString); + assertTrue(droolsSyntaxValidation.isCompiled()); + } + + + @Test + @SneakyThrows + void testCorruptedRules() { + + DroolsExecutionService droolsExecutionService = new DroolsExecutionService(rulesClient, entityEnrichmentService, new DroolsSyntaxValidationFactory()); + var rulesFile = new ClassPathResource("drools/rules.drl"); + + String rulesString = new String(rulesFile.getInputStream().readAllBytes()); + String corruptedRules = rulesString.replaceAll(";", ""); + + DroolsSyntaxValidation droolsSyntaxValidation = droolsExecutionService.testRules(corruptedRules); + assertFalse(droolsSyntaxValidation.isCompiled()); + } + +} \ No newline at end of file -- 2.47.2