RED-7493: provide error messages in testRules Endpoint #103

Merged
kilian.schuettler1 merged 1 commits from RED-7493 into master 2023-08-22 12:49:39 +02:00
7 changed files with 140 additions and 31 deletions

View File

@ -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;
}

View File

@ -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<DroolsSyntaxErrorMessage> droolsSyntaxErrorMessages;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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<FileAttribute> 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);
}
}

View File

@ -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<Message> errorMessages = kieBuilder.getResults().getMessages(Message.Level.ERROR);
List<DroolsSyntaxErrorMessage> 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();
}
}

View File

@ -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());
}
}