RED-9472: seperation of system rules #516

Closed
yannik.hampe wants to merge 9 commits from RED-9472 into master
21 changed files with 887 additions and 119 deletions

View File

@ -9,6 +9,7 @@ val persistenceServiceVersion = "2.545.0"
dependencies {
implementation("org.springframework:spring-web:6.0.12")
implementation("com.iqser.red.service:persistence-service-internal-api-v1:${persistenceServiceVersion}")
api("com.knecon.fforesight:swagger-commons:0.7.0")
}
publishing {

View File

@ -1,13 +1,26 @@
package com.iqser.red.service.redaction.v1.resources;
import com.iqser.red.service.redaction.v1.model.RuleBuilderModel;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUpdateRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.SystemRulesSeperationRequest;
import com.iqser.red.service.redaction.v1.model.RuleBuilderModel;
public interface RuleBuilderResource {
@PostMapping(value = "/rule-builder-model", produces = MediaType.APPLICATION_JSON_VALUE)
RuleBuilderModel getRuleBuilderModel();
@PostMapping(value = "/internal-api/rules/user-rules", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
RulesResponse getRuleFileWithoutSystemRules(@RequestBody SystemRulesSeperationRequest systemRulesSeperationRequest);
@PostMapping(value = "/internal-api/rules/system-rules", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity mergeUserUpdateRules(@RequestBody RulesUpdateRequest rulesUpdateRequest);
}

View File

@ -33,6 +33,7 @@ configurations {
dependencies {
implementation(project(":rules-management"))
implementation(project(":redaction-service-api-v1")) { exclude(group = "com.iqser.red.service", module = "persistence-service-internal-api-v1") }
implementation("com.iqser.red.service:persistence-service-internal-api-v1:${persistenceServiceVersion}") { exclude(group = "org.springframework.boot") }
implementation("com.iqser.red.service:persistence-service-shared-mongo-v1:${persistenceServiceVersion}")
@ -76,6 +77,7 @@ dependencies {
implementation("net.logstash.logback:logstash-logback-encoder:7.4")
api("ch.qos.logback:logback-classic")
api("com.knecon.fforesight:swagger-commons:0.7.0")
implementation("org.reflections:reflections:0.10.2")

View File

@ -1,27 +1,105 @@
package com.iqser.red.service.redaction.v1.server.controller;
import java.util.Collections;
import org.junit.Assert;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.DroolsValidationResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RuleBlacklistErrorMessage;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RuleSyntaxErrorMessage;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RuleSyntaxWarningMessage;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesResponse;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.RulesUpdateRequest;
import com.iqser.red.service.persistence.service.v1.api.shared.model.dossiertemplate.rules.SystemRulesSeperationRequest;
import com.iqser.red.service.redaction.v1.model.RuleBuilderModel;
import com.iqser.red.service.redaction.v1.model.RuleValidationModel;
import com.iqser.red.service.redaction.v1.resources.RuleBuilderResource;
import com.iqser.red.service.redaction.v1.server.model.RuleMergingResult;
import com.iqser.red.service.redaction.v1.server.service.RuleBuilderService;
import com.iqser.red.service.redaction.v1.server.service.drools.DroolsValidationService;
import com.iqser.red.service.redaction.v1.server.utils.exception.RulesValidationException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@RestController
@Slf4j
@RequiredArgsConstructor
public class RuleBuilderController implements RuleBuilderResource {
private final RuleBuilderService ruleBuilderService;
private final DroolsValidationService droolsValidationService;
@Override
public RuleBuilderModel getRuleBuilderModel() {
RuleBuilderModel ruleBuilderModel = new RuleBuilderModel();
return this.ruleBuilderService.getRuleBuilderModel();
}
ruleBuilderModel.setWhenClauses(Collections.emptyList());
ruleBuilderModel.setThenConditions(Collections.emptyList());
return ruleBuilderModel;
@Override
public RulesResponse getRuleFileWithoutSystemRules(SystemRulesSeperationRequest systemRulesSeperationRequest) {
RulesResponse rulesResponse = new RulesResponse();
String filteredRules = this.ruleBuilderService.cleanRuleFileOfSystemRules(systemRulesSeperationRequest.getRules(), true);
Assert.assertFalse("There was an error when cleaning the rulefile of system rules", filteredRules.isEmpty());
rulesResponse.setRules(filteredRules);
return rulesResponse;
}
@Override
public ResponseEntity mergeUserUpdateRules(RulesUpdateRequest rulesUpdateRequest) {
RulesResponse rulesResponse = new RulesResponse();
DroolsValidationResponse droolsValidationResponse;
RuleMergingResult mergingResult = ruleBuilderService.mergeUserRulesAndSystemRules(rulesUpdateRequest.getExistingRules(), rulesUpdateRequest.getUpdatedRules());
Assert.assertFalse("There was an error when merging the user rule update", mergingResult.getMergedRules().isEmpty());
try {
var droolsValidation = droolsValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), mergingResult.getMergedRules()));
droolsValidationResponse = DroolsValidationResponse.builder()
.syntaxErrorMessages(droolsValidation.getSyntaxErrorMessages()
.stream()
.map(droolsSyntaxErrorMessage -> new RuleSyntaxErrorMessage(droolsSyntaxErrorMessage.getLine()
- (mergingResult.getAddedImportsOffset()
+ mergingResult.getAddedGlobalsOffset()),
droolsSyntaxErrorMessage.getColumn(),
droolsSyntaxErrorMessage.getMessage()))
.toList())
.deprecatedWarnings(droolsValidation.getDeprecatedWarnings()
.stream()
.map(droolsSyntaxDeprecatedWarnings -> new RuleSyntaxWarningMessage(droolsSyntaxDeprecatedWarnings.getLine()
- (mergingResult.getAddedImportsOffset()
+ mergingResult.getAddedGlobalsOffset()),
droolsSyntaxDeprecatedWarnings.getColumn(),
droolsSyntaxDeprecatedWarnings.getMessage()))
.toList())
.blacklistErrorMessages(droolsValidation.getBlacklistErrorMessages()
.stream()
.map(droolsBlacklistErrorMessage -> new RuleBlacklistErrorMessage(droolsBlacklistErrorMessage.getLine()
- (mergingResult.getAddedImportsOffset()
+ mergingResult.getAddedGlobalsOffset()),
droolsBlacklistErrorMessage.getColumn(),
droolsBlacklistErrorMessage.getMessage()))
.toList())
.build();
if (!droolsValidation.isCompiled()) {
return new ResponseEntity<>(droolsValidationResponse, HttpStatus.UNPROCESSABLE_ENTITY);
} else {
rulesResponse.setRules(mergingResult.getMergedRules());
}
} catch (Exception e) {
throw new RulesValidationException("Could not test rules: " + e.getMessage(), e);
}
return new ResponseEntity<>(rulesResponse, HttpStatus.OK);
}
private enum RuleFileType {
ENTITY,
COMPONENT
}
}

View File

@ -0,0 +1,24 @@
package com.iqser.red.service.redaction.v1.server.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Schema(description = "Object containing a string of Drools rules.")
public class RuleMergingResult {
@Schema(description = "The merged rules.")
private String mergedRules;
@Schema(description = "the length of added imports from sytemRules")
private int addedImportsOffset;
@Schema(description = "the length of added globals from sytemRules")
private int addedGlobalsOffset;
}

View File

@ -18,13 +18,15 @@ import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@Data
@Slf4j
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public final class RuleFileBluePrint {
public final class RuleCompilationResult {
String imports;
int importLine;
@ -43,6 +45,59 @@ public final class RuleFileBluePrint {
}
public void dropRulesByIdentifier(RuleType ruleType) {
log.info("removing rule with identifier {}", ruleType.name());
List<RuleClass> rulesToBeRemoved = ruleClasses.stream()
.filter(ruleClass -> Objects.equals(ruleClass.ruleType(), ruleType))
.collect(Collectors.toList());
log.info("rules to be removed {}", rulesToBeRemoved);
ruleClasses.removeAll(rulesToBeRemoved);
}
public void dropAllRulesExceptSystemRules(List<String> systemRules) {
log.info("removing all rules except the system rules");
List<RuleClass> rulesToBeRemoved = ruleClasses.stream()
.filter(ruleClass -> filterOutRule(ruleClass.ruleType(), systemRules))
.collect(Collectors.toList());
log.info("rules to be removed {}", rulesToBeRemoved);
ruleClasses.removeAll(rulesToBeRemoved);
}
public void dropImports() {
this.imports = "";
}
public void dropQueries() {
this.queries.clear();
}
public int countRuleOccurences(RuleType ruleType) {
log.info("counting occurences of files {}", ruleType.name());
List<RuleClass> rulesToBeRemoved = ruleClasses.stream()
.filter(ruleClass -> Objects.equals(ruleClass.ruleType(), ruleType))
.collect(Collectors.toList());
return rulesToBeRemoved.size();
}
private boolean filterOutRule(RuleType ruleType, List<String> filteredRules) {
return !filteredRules.contains(ruleType.name());
}
public Set<String> getImportSplitByKeyword() {
return Arrays.stream(imports.replaceAll("\n", "").split("import"))
@ -94,4 +149,10 @@ public final class RuleFileBluePrint {
return "RuleFileBluePrint[imports=" + imports + ", globals=" + globals + ", queries=" + queries + ", ruleClasses=" + ruleClasses + ']';
}
public void addRuleClass(RuleClass ruleClass) {
this.ruleClasses.add(ruleClass);
}
}

View File

@ -0,0 +1,145 @@
package com.iqser.red.service.redaction.v1.server.service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.Assert;
import org.springframework.stereotype.Service;
import com.iqser.red.service.redaction.v1.model.RuleBuilderModel;
import com.iqser.red.service.redaction.v1.server.model.RuleMergingResult;
import com.knecon.fforesight.utility.rules.management.factory.RuleFileFactory;
import com.knecon.fforesight.utility.rules.management.factory.RuleFileParser;
import com.knecon.fforesight.utility.rules.management.models.RuleFileBluePrint;
import com.knecon.fforesight.utility.rules.management.models.RuleIdentifier;
import com.knecon.fforesight.utility.rules.management.models.RuleType;
import com.knecon.fforesight.utility.rules.management.models.RuleUnit;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class RuleBuilderService {
//todo: make this configurable
private final List<RuleType> systemRules = new ArrayList<>(Arrays.asList(new RuleType("AI"),
new RuleType("MAN"),
new RuleType("X"),
new RuleType("DICT"),
new RuleType("FA"),
new RuleType("LDS")));
public RuleBuilderModel getRuleBuilderModel() {
RuleBuilderModel ruleBuilderModel = new RuleBuilderModel();
ruleBuilderModel.setWhenClauses(Collections.emptyList());
ruleBuilderModel.setThenConditions(Collections.emptyList());
return ruleBuilderModel;
}
public String cleanRuleFileOfSystemRules(String rulesString, boolean removeImports) {
RuleFileBluePrint ruleFileBluePrint = RuleFileParser.buildBluePrintFromRulesString(rulesString);
log.info("Starting to remove system rules from ruleFile");
removeSystemRulesAndCheckIfAnyRemain(ruleFileBluePrint);
log.info("Finished removing system rules for ruleFile");
return RuleFileFactory.buildRuleString(ruleFileBluePrint, removeImports, true);
}
public RuleMergingResult mergeUserRulesAndSystemRules(String existingRules, String userUpdatedRules) {
log.info("starting to merge user rules update with system rules");
RuleFileBluePrint ruleFileBluePrintExisting = RuleFileParser.buildBluePrintFromRulesString(existingRules);
RuleFileBluePrint mergedRuleFileBlueprint = RuleFileParser.buildBluePrintFromRulesString(userUpdatedRules, true);
removeAllRulesExceptSystemRulesAndCheck(ruleFileBluePrintExisting);
ruleFileBluePrintExisting.getRuleClasses()
.stream()
.flatMap(ruleClass -> ruleClass.ruleUnits()
.stream()
.flatMap(ruleUnit -> ruleUnit.rules()
.stream()))
.forEach(mergedRuleFileBlueprint::addRule);
mergedRuleFileBlueprint.setImports(ruleFileBluePrintExisting.getImports() + mergedRuleFileBlueprint.getImports());
mergedRuleFileBlueprint.setGlobals(ruleFileBluePrintExisting.getGlobals() + mergedRuleFileBlueprint.getGlobals());
log.info("finished merging user rules update with system rules");
RuleMergingResult mergingResult = RuleMergingResult.builder()
.mergedRules(RuleFileFactory.buildRuleString(ruleFileBluePrintExisting, false, false))
.addedGlobalsOffset(ruleFileBluePrintExisting.getGlobals().length())
.addedImportsOffset(ruleFileBluePrintExisting.getImports().length())
.build();
return mergingResult;
}
private void removeAllRulesExceptSystemRulesAndCheck(RuleFileBluePrint ruleFileBluePrint) {
Set<String> systemRuleNames = systemRules.stream()
.map(RuleType::name)
.collect(Collectors.toSet());
removeAllRulesExceptSystemRules(ruleFileBluePrint);
ruleFileBluePrint.getRuleClasses()
.forEach(ruleClass -> Assert.assertTrue("there was an error removing all rules except system rules", systemRuleNames.contains(ruleClass.ruleType().name())));
}
private void removeAllRulesExceptSystemRules(RuleFileBluePrint ruleFileBluePrint) {
List<RuleIdentifier> rules = new ArrayList();
Set<String> systemRuleNames = systemRules.stream()
.map(RuleType::name)
.collect(Collectors.toSet());
ruleFileBluePrint.getRuleClasses()
.stream()
.filter(ruleClass -> !systemRuleNames.contains(ruleClass.ruleType().name()))
.flatMap(ruleClass -> ruleClass.ruleUnits()
.stream()
.map(ruleUnit -> new RuleIdentifier(ruleClass.ruleType(), ruleUnit.unit(), null)))
.forEach(rule -> rules.add(rule));
rules.forEach(ruleIdentifier -> {
ruleFileBluePrint.removeRule(ruleIdentifier);
});
}
private void removeSystemRulesAndCheckIfAnyRemain(RuleFileBluePrint ruleFileBluePrint) {
removeSystemRules(ruleFileBluePrint);
for (RuleType systemRule : systemRules) {
List<RuleUnit> remainingSystemRules = new ArrayList();
ruleFileBluePrint.findRuleClassByType(systemRule)
.ifPresent(ruleClass -> ruleClass.ruleUnits()
.stream()
.forEach(remainingSystemRules::add));
Assert.assertTrue("There was an error removing the system rules from the file", remainingSystemRules.isEmpty());
}
}
private void removeSystemRules(RuleFileBluePrint ruleFileBluePrintExisting) {
for (RuleType systemRule : systemRules) {
List<RuleUnit> rules = new ArrayList();
ruleFileBluePrintExisting.findRuleClassByType(systemRule)
.ifPresent(ruleClass -> ruleClass.ruleUnits()
.stream()
.forEach(rules::add));
rules.forEach(ruleUnit -> {
ruleFileBluePrintExisting.removeRule(new RuleIdentifier(systemRule, ruleUnit.unit(), null));
});
ruleFileBluePrintExisting.removeRule(RuleIdentifier.fromRuleType(systemRule));
}
}
}

View File

@ -28,7 +28,7 @@ import com.iqser.red.service.redaction.v1.server.model.dictionary.SearchImplemen
import com.iqser.red.service.redaction.v1.server.model.drools.BasicQuery;
import com.iqser.red.service.redaction.v1.server.model.drools.BasicRule;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleClass;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleFileBluePrint;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleCompilationResult;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleUnit;
import com.iqser.red.service.redaction.v1.server.storage.RuleManagementResources;
@ -69,70 +69,70 @@ public class DroolsValidationService {
private DroolsValidation buildCustomDroolsValidation(String ruleString, RuleFileType ruleFileType) throws DroolsParserException {
RuleFileBluePrint ruleFileBluePrint = RuleFileParser.buildBluePrintFromRulesString(ruleString);
RuleCompilationResult ruleCompilationResult = RuleCompilationResultParser.buildRuleCompilationResultFromRuleString(ruleString);
DroolsValidation customValidation = ruleFileBluePrint.getDroolsValidation();
DroolsValidation customValidation = ruleCompilationResult.getDroolsValidation();
addSyntaxDeprecatedWarnings(ruleFileType, ruleFileBluePrint, customValidation);
addSyntaxDeprecatedWarnings(ruleFileType, ruleCompilationResult, customValidation);
addSyntaxErrorMessages(ruleFileType, ruleFileBluePrint, customValidation);
addSyntaxErrorMessages(ruleFileType, ruleCompilationResult, customValidation);
if (redactionServiceSettings.isRuleExecutionSecured()) {
addBlacklistErrorMessages(ruleFileBluePrint, customValidation);
addBlacklistErrorMessages(ruleCompilationResult, customValidation);
}
return customValidation;
}
private void addSyntaxDeprecatedWarnings(RuleFileType ruleFileType, RuleFileBluePrint ruleFileBluePrint, DroolsValidation customValidation) {
private void addSyntaxDeprecatedWarnings(RuleFileType ruleFileType, RuleCompilationResult ruleCompilationResult, DroolsValidation customValidation) {
// find deprecated elements in the ruleFileBluePrint
DroolsSyntaxDeprecatedWarnings warningMessageForImports = getWarningsForDeprecatedImports(ruleFileBluePrint);
DroolsSyntaxDeprecatedWarnings warningMessageForImports = getWarningsForDeprecatedImports(ruleCompilationResult);
if (warningMessageForImports != null) {
customValidation.getDeprecatedWarnings().add(warningMessageForImports);
}
customValidation.getDeprecatedWarnings().addAll(getWarningsForDeprecatedRules(ruleFileBluePrint));
customValidation.getDeprecatedWarnings().addAll(getWarningsForDeprecatedRules(ruleCompilationResult));
if (ruleFileType.equals(RuleFileType.COMPONENT)) {
if (!ruleFileBluePrint.getGlobals().contains(ComponentDroolsExecutionService.COMPONENT_MAPPING_SERVICE_GLOBAL)) {
customValidation.getDeprecatedWarnings().add(buildComponentMappingServiceMissingMessage(ruleFileBluePrint));
if (!ruleCompilationResult.getGlobals().contains(ComponentDroolsExecutionService.COMPONENT_MAPPING_SERVICE_GLOBAL)) {
customValidation.getDeprecatedWarnings().add(buildComponentMappingServiceMissingMessage(ruleCompilationResult));
}
}
}
private static DroolsSyntaxDeprecatedWarnings buildComponentMappingServiceMissingMessage(RuleFileBluePrint ruleFileBluePrint) {
private static DroolsSyntaxDeprecatedWarnings buildComponentMappingServiceMissingMessage(RuleCompilationResult ruleCompilationResult) {
return DroolsSyntaxDeprecatedWarnings.builder()
.message("global ComponentMappingService "
+ ComponentDroolsExecutionService.COMPONENT_MAPPING_SERVICE_GLOBAL
+ "\n is missing from the rules, consider adding it, as it will be required in future versions!")
.line(ruleFileBluePrint.getGlobalsLine())
.line(ruleCompilationResult.getGlobalsLine())
.column(0)
.build();
}
private DroolsSyntaxDeprecatedWarnings getWarningsForDeprecatedImports(RuleFileBluePrint ruleFileBluePrint) {
private DroolsSyntaxDeprecatedWarnings getWarningsForDeprecatedImports(RuleCompilationResult ruleCompilationResult) {
if (!deprecatedElementsFinder.getDeprecatedClasses().isEmpty()) {
String imports = ruleFileBluePrint.getImports();
String imports = ruleCompilationResult.getImports();
SearchImplementation classesSearchImplementation = deprecatedElementsFinder.getClassesSearchImplementation();
List<SearchImplementation.MatchPosition> matches = classesSearchImplementation.getMatches(imports);
if (!matches.isEmpty()) {
String sb = "Following imports are deprecated: \n" + matches.stream()
.map(m -> imports.substring(m.startIndex(), m.endIndex()))
.collect(Collectors.joining("\n"));
return DroolsSyntaxDeprecatedWarnings.builder().line(ruleFileBluePrint.getImportLine()).column(0).message(sb).build();
return DroolsSyntaxDeprecatedWarnings.builder().line(ruleCompilationResult.getImportLine()).column(0).message(sb).build();
}
}
return null;
}
private List<DroolsSyntaxDeprecatedWarnings> getWarningsForDeprecatedRules(RuleFileBluePrint ruleFileBluePrint) {
private List<DroolsSyntaxDeprecatedWarnings> getWarningsForDeprecatedRules(RuleCompilationResult ruleCompilationResult) {
List<DroolsSyntaxDeprecatedWarnings> warningMessages = new ArrayList<>();
@ -141,7 +141,7 @@ public class DroolsValidationService {
SearchImplementation methodsSearchImplementation = deprecatedElementsFinder.getMethodsSearchImplementation();
Map<String, String> deprecatedMethodsSignatureMap = deprecatedElementsFinder.getDeprecatedMethodsSignaturesMap();
for (RuleClass ruleClass : ruleFileBluePrint.getRuleClasses()) {
for (RuleClass ruleClass : ruleCompilationResult.getRuleClasses()) {
for (RuleUnit ruleUnit : ruleClass.ruleUnits()) {
for (BasicRule basicRule : ruleUnit.rules()) {
List<SearchImplementation.MatchPosition> matches = methodsSearchImplementation.getMatches(basicRule.getCode());
@ -165,32 +165,32 @@ public class DroolsValidationService {
}
private void addSyntaxErrorMessages(RuleFileType ruleFileType, RuleFileBluePrint ruleFileBluePrint, DroolsValidation customValidation) {
private void addSyntaxErrorMessages(RuleFileType ruleFileType, RuleCompilationResult ruleCompilationResult, DroolsValidation customValidation) {
RuleFileBluePrint baseRuleFileBluePrint = switch (ruleFileType) {
case ENTITY -> RuleFileParser.buildBluePrintFromRulesString(RuleManagementResources.getBaseRuleFileString());
case COMPONENT -> RuleFileParser.buildBluePrintFromRulesString(RuleManagementResources.getBaseComponentRuleFileString());
RuleCompilationResult baseRuleCompilationResult = switch (ruleFileType) {
case ENTITY -> RuleCompilationResultParser.buildRuleCompilationResultFromRuleString(RuleManagementResources.getBaseRuleFileString());
case COMPONENT -> RuleCompilationResultParser.buildRuleCompilationResultFromRuleString(RuleManagementResources.getBaseComponentRuleFileString());
};
if (!importsAreValid(baseRuleFileBluePrint, ruleFileBluePrint)) {
if (!importsAreValid(baseRuleCompilationResult, ruleCompilationResult)) {
customValidation.getSyntaxErrorMessages()
.add(DroolsSyntaxErrorMessage.builder()
.line(ruleFileBluePrint.getImportLine())
.line(ruleCompilationResult.getImportLine())
.column(0)
.message(String.format("Changing the imports is not allowed! Must be: %n%s", baseRuleFileBluePrint.getImports()))
.message(String.format("Changing the imports is not allowed! Must be: %n%s", baseRuleCompilationResult.getImports()))
.build());
}
if (!ruleFileBluePrint.getGlobals().contains(baseRuleFileBluePrint.getGlobals())) {
if (!ruleCompilationResult.getGlobals().contains(baseRuleCompilationResult.getGlobals())) {
customValidation.getSyntaxErrorMessages()
.add(DroolsSyntaxErrorMessage.builder()
.line(ruleFileBluePrint.getGlobalsLine())
.line(ruleCompilationResult.getGlobalsLine())
.column(0)
.message(String.format("Removing the globals is not allowed! Must be: %n%s", baseRuleFileBluePrint.getGlobals()))
.message(String.format("Removing the globals is not allowed! Must be: %n%s", baseRuleCompilationResult.getGlobals()))
.build());
}
baseRuleFileBluePrint.getQueries()
baseRuleCompilationResult.getQueries()
.forEach(basicQuery -> {
if (!validateQueryIsPresent(basicQuery, ruleFileBluePrint)) {
if (!validateQueryIsPresent(basicQuery, ruleCompilationResult)) {
customValidation.getSyntaxErrorMessages()
.add(DroolsSyntaxErrorMessage.builder()
.line(basicQuery.getLine())
@ -201,7 +201,7 @@ public class DroolsValidationService {
});
if (ruleFileType.equals(RuleFileType.ENTITY)) {
String requiredAgendaGroup = "LOCAL_DICTIONARY_ADDS";
if (!validateAgendaGroupIsPresent(ruleFileBluePrint, requiredAgendaGroup)) {
if (!validateAgendaGroupIsPresent(ruleCompilationResult, requiredAgendaGroup)) {
customValidation.getSyntaxErrorMessages()
.add(DroolsSyntaxErrorMessage.builder()
.line(0)
@ -213,18 +213,18 @@ public class DroolsValidationService {
}
private boolean validateAgendaGroupIsPresent(RuleFileBluePrint ruleFileBluePrint, String agendaGroupName) {
private boolean validateAgendaGroupIsPresent(RuleCompilationResult ruleCompilationResult, String agendaGroupName) {
return ruleFileBluePrint.streamAllRules()
return ruleCompilationResult.streamAllRules()
.anyMatch(basicRule -> basicRule.getAgendaGroup().equals(agendaGroupName));
}
private boolean importsAreValid(RuleFileBluePrint baseRuleFileBluePrint, RuleFileBluePrint ruleFileBluePrint) {
private boolean importsAreValid(RuleCompilationResult baseRuleCompilationResult, RuleCompilationResult ruleCompilationResult) {
// imports may shrink, but not add anything new!
Set<String> baseImports = baseRuleFileBluePrint.getImportSplitByKeyword();
Set<String> imports = ruleFileBluePrint.getImportSplitByKeyword();
Set<String> baseImports = baseRuleCompilationResult.getImportSplitByKeyword();
Set<String> imports = ruleCompilationResult.getImportSplitByKeyword();
Set<String> additionalImports = Sets.difference(imports, baseImports);
@ -233,15 +233,15 @@ public class DroolsValidationService {
}
private static boolean validateQueryIsPresent(BasicQuery queryToCheckFor, RuleFileBluePrint ruleFileBluePrint) {
private static boolean validateQueryIsPresent(BasicQuery queryToCheckFor, RuleCompilationResult ruleCompilationResult) {
return ruleFileBluePrint.getQueries()
return ruleCompilationResult.getQueries()
.stream()
.anyMatch(query -> query.getName().equals(queryToCheckFor.getName()) && query.getCode().equals(queryToCheckFor.getCode()));
}
private void addBlacklistErrorMessages(RuleFileBluePrint ruleFileBluePrint, DroolsValidation customValidation) {
private void addBlacklistErrorMessages(RuleCompilationResult ruleCompilationResult, DroolsValidation customValidation) {
List<DroolsBlacklistErrorMessage> blacklistErrorMessages = new ArrayList<>();
@ -253,14 +253,14 @@ public class DroolsValidationService {
// check also the imports
DroolsBlacklistErrorMessage blacklistErrorMessage = checkAndGetBlackListedMessages(blacklistedKeywordSearchImplementation,
ruleFileBluePrint.getImports(),
ruleFileBluePrint.getImportLine());
ruleCompilationResult.getImports(),
ruleCompilationResult.getImportLine());
if (blacklistErrorMessage != null) {
blacklistErrorMessages.add(blacklistErrorMessage);
}
// check the rules
for (RuleClass ruleClass : ruleFileBluePrint.getRuleClasses()) {
for (RuleClass ruleClass : ruleCompilationResult.getRuleClasses()) {
for (RuleUnit ruleUnit : ruleClass.ruleUnits()) {
for (BasicRule basicRule : ruleUnit.rules()) {
DroolsBlacklistErrorMessage ruleBlacklistErrorMessage = checkAndGetBlackListedMessages(blacklistedKeywordSearchImplementation,

View File

@ -20,7 +20,7 @@ import com.iqser.red.service.redaction.v1.model.DroolsValidation;
import com.iqser.red.service.redaction.v1.server.model.drools.BasicQuery;
import com.iqser.red.service.redaction.v1.server.model.drools.BasicRule;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleClass;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleFileBluePrint;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleCompilationResult;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleIdentifier;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleType;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleUnit;
@ -29,14 +29,41 @@ import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
@UtilityClass
public class RuleFileParser {
public class RuleCompilationResultParser {
private final static Pattern ruleIdentifierInCodeFinder = Pattern.compile(
"\\b(?:redact|apply|skip|remove|ignore|applyWithLineBreaks|applyWithReferences|skipWithReferences)\\s*\\(\\s*\"([a-zA-Z0-9]+.\\d+.\\d+)\"\\s*,\\s*.*(?:\\s*,\\s*.*)\\s*?\\)");
public RuleCompilationResult buildBluePrintFromUserRulesString(String userRulesString) {
DroolsValidation customDroolsValidation = DroolsValidation.builder().build();
List<BasicRule> allRules = new LinkedList<>();
List<BasicQuery> allQueries = new LinkedList<>();
PackageDescr packageDescr = new PackageDescr();
String imports = "";
String globals = "";
List<RuleClass> ruleClasses = buildRuleClasses(allRules);
return new RuleCompilationResult(imports.trim(),
packageDescr.getImports()
.stream()
.findFirst()
.map(ImportDescr::getLine)
.orElse(0),
globals.trim(),
packageDescr.getGlobals()
.stream()
.findFirst()
.map(GlobalDescr::getLine)
.orElse(0),
allQueries,
ruleClasses,
customDroolsValidation);
}
@SneakyThrows
public RuleFileBluePrint buildBluePrintFromRulesString(String ruleString) {
public RuleCompilationResult buildRuleCompilationResultFromRuleString(String ruleString, boolean removedImports) {
DroolsValidation customDroolsValidation = DroolsValidation.builder().build();
DrlParser parser = new DrlParser(LanguageLevelOption.DRL6);
@ -52,12 +79,15 @@ public class RuleFileParser {
}
}
String imports = ruleString.substring(0,
String imports = "";
if (!removedImports) {
imports = ruleString.substring(0,
packageDescr.getImports()
.stream()
.mapToInt(ImportDescr::getEndCharacter)
.max()
.orElseThrow() + 1);
}
String globals = packageDescr.getGlobals()
.stream()
.map(globalDescr -> ruleString.substring(globalDescr.getStartCharacter(), globalDescr.getEndCharacter()))
@ -65,7 +95,7 @@ public class RuleFileParser {
List<RuleClass> ruleClasses = buildRuleClasses(allRules);
return new RuleFileBluePrint(imports.trim(),
return new RuleCompilationResult(imports.trim(),
packageDescr.getImports()
.stream()
.findFirst()
@ -83,6 +113,66 @@ public class RuleFileParser {
}
@SneakyThrows
public RuleCompilationResult buildRuleCompilationResultFromRuleString(String ruleString) {
return buildRuleCompilationResultFromRuleString(ruleString,false);
}
@SneakyThrows
public String buildRulesStringFromBluePrint(RuleCompilationResult bluePrint) {
StringBuilder ruleStringBuilder = new StringBuilder();
// Append imports
ruleStringBuilder.append(bluePrint.getImports()).append("\n\n");
// Append globals
if (!bluePrint.getGlobals().isEmpty()) {
ruleStringBuilder.append(bluePrint.getGlobals()).append("\n\n");
}
// Append queries
for (BasicQuery query : bluePrint.getQueries()) {
ruleStringBuilder.append(buildQueryString(query)).append("\n\n");
}
// Append rules
for (RuleClass ruleClass : bluePrint.getRuleClasses()) {
ruleStringBuilder.append(buildRuleString(ruleClass)).append("\n\n");
}
// Return the final rule string
return ruleStringBuilder.toString().trim();
}
private String buildQueryString(BasicQuery query) {
return "query \"" + query.getName() + "\"\n" + query.getCode() + "\n" + "end";
}
private String buildRuleString(RuleClass ruleClass) {
StringBuilder ruleBuilder = new StringBuilder();
// Use RuleType to distinguish between different rule types, if needed
ruleBuilder.append("rule \"").append(ruleClass.ruleType().name()).append("\"\n");
for (RuleUnit ruleUnit : ruleClass.ruleUnits()) {
for (BasicRule rule : ruleUnit.rules()) {
// Assuming BasicRule has a method to retrieve the condition as a string
ruleBuilder.append(rule.getCode()).append("\n");
}
}
ruleBuilder.append("end");
return ruleBuilder.toString();
}
private static void validateRule(String ruleString, RuleDescr rule, DroolsValidation customDroolsValidation, List<BasicRule> allRules) {
BasicRule basicRule;
@ -126,6 +216,7 @@ public class RuleFileParser {
private List<RuleClass> buildRuleClasses(List<BasicRule> allRules) {
//todo: comments
List<RuleType> ruleTypeOrder = allRules.stream()
.map(BasicRule::getIdentifier)
.map(RuleIdentifier::type)

View File

@ -0,0 +1,149 @@
package com.iqser.red.service.redaction.v1.server;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Assert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import com.iqser.red.commons.jackson.ObjectMapperFactory;
import com.iqser.red.service.persistence.service.v1.api.shared.model.RuleFileType;
import com.iqser.red.service.persistence.service.v1.api.shared.model.common.JSONPrimitive;
import com.iqser.red.service.redaction.v1.server.model.RuleMergingResult;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleCompilationResult;
import com.iqser.red.service.redaction.v1.server.service.RuleBuilderService;
import com.iqser.red.service.redaction.v1.server.service.drools.RuleCompilationResultParser;
import com.iqser.red.storage.commons.StorageAutoConfiguration;
import com.iqser.red.storage.commons.service.StorageService;
import com.iqser.red.storage.commons.utils.FileSystemBackedStorageService;
import com.knecon.fforesight.service.layoutparser.processor.LayoutParsingServiceProcessorConfiguration;
import com.knecon.fforesight.tenantcommons.TenantContext;
import com.knecon.fforesight.utility.rules.management.factory.RuleFileParser;
import com.knecon.fforesight.utility.rules.management.models.RuleFileBluePrint;
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Import(RuleBuilderTest.RuleBuilderTestConfiguration.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class RuleBuilderTest extends AbstractRedactionIntegrationTest {
private static final String RULES = loadFromClassPath("drools/rules_v2.drl");
private static final String USER_RULES = loadFromClassPath("drools/user_rule_update.drl");
private final List<String> systemRules = new ArrayList<>(Arrays.asList("AI", "MAN", "X", "DICT", "FA", "LDS"));
@Autowired
RuleBuilderService ruleBuilderService;
@Configuration
@EnableAutoConfiguration(exclude = {RabbitAutoConfiguration.class})
@Import(LayoutParsingServiceProcessorConfiguration.class)
@ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = StorageAutoConfiguration.class)})
public static class RuleBuilderTestConfiguration {
@Bean
@Primary
public StorageService inmemoryStorage() {
return new FileSystemBackedStorageService(ObjectMapperFactory.create());
}
}
@BeforeEach
public void stubClients() {
TenantContext.setTenantId("redaction");
when(rulesClient.getVersion(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(System.currentTimeMillis());
when(rulesClient.getRules(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.ENTITY)).thenReturn(JSONPrimitive.of(RULES));
when(rulesClient.getVersion(TEST_DOSSIER_TEMPLATE_ID, RuleFileType.COMPONENT)).thenReturn(-1L);
loadDictionaryForTest();
loadTypeForTest();
loadNerForTest();
when(dictionaryClient.getVersion(TEST_DOSSIER_TEMPLATE_ID)).thenReturn(0L);
when(dictionaryClient.getAllTypesForDossierTemplate(TEST_DOSSIER_TEMPLATE_ID, null, true)).thenReturn(getTemplateDictionaryTypeResponse());
when(dictionaryClient.getVersion(TEST_DOSSIER_ID)).thenReturn(0L);
when(dictionaryClient.getAllTypesForDossier(TEST_DOSSIER_ID, null, true)).thenReturn(getDossierDictionaryTypeResponse());
mockDictionaryCalls(null);
when(dictionaryClient.getColors(TEST_DOSSIER_TEMPLATE_ID)).thenReturn(colors);
}
@Test
public void removeSystemRulesTest() {
String cleanedRulesWithImports = this.ruleBuilderService.cleanRuleFileOfSystemRules(RULES, false);
String cleanedRulesWithoutImports = this.ruleBuilderService.cleanRuleFileOfSystemRules(RULES, true);
RuleCompilationResult ruleCompilationResultWithImports = RuleCompilationResultParser.buildRuleCompilationResultFromRuleString(cleanedRulesWithImports, false);
RuleCompilationResult ruleCompilationResultWithoutImports = RuleCompilationResultParser.buildRuleCompilationResultFromRuleString(cleanedRulesWithoutImports, true);
Assert.assertFalse(checkIfImportsDontExist(ruleCompilationResultWithImports));
Assert.assertTrue(checkIfSystemRulesDontExist(ruleCompilationResultWithImports));
Assert.assertTrue(checkIfQueriesDontExist(ruleCompilationResultWithImports));
Assert.assertTrue(checkIfImportsDontExist(ruleCompilationResultWithoutImports));
Assert.assertTrue(checkIfSystemRulesDontExist(ruleCompilationResultWithoutImports));
Assert.assertTrue(checkIfQueriesDontExist(ruleCompilationResultWithoutImports));
}
@Test
public void mergeUserRulesUpdateTest() {
RuleMergingResult mergingResult = this.ruleBuilderService.mergeUserRulesAndSystemRules(RULES, USER_RULES);
String mergedRules = mergingResult.getMergedRules();
RuleFileBluePrint ruleFileBluePrintUserRulesUpdate = RuleFileParser.buildBluePrintFromRulesString(mergedRules, true);
RuleFileBluePrint ruleFileBluePrint = RuleFileParser.buildBluePrintFromRulesString(mergedRules);
ruleFileBluePrintUserRulesUpdate.getRuleClasses()
.forEach(ruleClass -> {
Assert.assertTrue(ruleFileBluePrint.getRuleClasses().contains(ruleClass));
});
}
private boolean checkIfSystemRulesDontExist(RuleCompilationResult ruleCompilationResult) {
return ruleCompilationResult.getRuleClasses()
.stream()
.filter(ruleClass -> this.systemRules.contains(ruleClass.ruleType().name()))
.collect(Collectors.toList()).size() == 0;
}
private boolean checkIfImportsDontExist(RuleCompilationResult ruleCompilationResult) {
return ruleCompilationResult.getImports().isEmpty();
}
private boolean checkIfQueriesDontExist(RuleCompilationResult ruleCompilationResult) {
return ruleCompilationResult.getQueries().isEmpty();
}
}

View File

@ -22,11 +22,11 @@ import com.iqser.red.service.redaction.v1.model.RuleValidationModel;
import com.iqser.red.service.redaction.v1.server.DeprecatedElementsFinder;
import com.iqser.red.service.redaction.v1.server.RedactionServiceSettings;
import com.iqser.red.service.redaction.v1.server.client.RulesClient;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleFileBluePrint;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleCompilationResult;
import com.iqser.red.service.redaction.v1.server.service.document.EntityEnrichmentService;
import com.iqser.red.service.redaction.v1.server.service.drools.DroolsValidationService;
import com.iqser.red.service.redaction.v1.server.service.drools.KieContainerCreationService;
import com.iqser.red.service.redaction.v1.server.service.drools.RuleFileParser;
import com.iqser.red.service.redaction.v1.server.service.drools.RuleCompilationResultParser;
import com.iqser.red.service.redaction.v1.server.storage.RuleManagementResources;
import lombok.SneakyThrows;
@ -235,11 +235,11 @@ class DroolsValidationServiceTest {
if (droolsValidation.isCompiled()) {
continue;
}
RuleFileBluePrint ruleFileBluePrint = RuleFileParser.buildBluePrintFromRulesString(rulesString);
RuleFileBluePrint baseRuleFileBluePrint = RuleFileParser.buildBluePrintFromRulesString(RuleManagementResources.getBaseRuleFileString());
RuleCompilationResult ruleCompilationResult = RuleCompilationResultParser.buildRuleCompilationResultFromRuleString(rulesString);
RuleCompilationResult baseRuleCompilationResult = RuleCompilationResultParser.buildRuleCompilationResultFromRuleString(RuleManagementResources.getBaseRuleFileString());
rulesString = rulesString.replace(ruleFileBluePrint.getImports(), baseRuleFileBluePrint.getImports());
rulesString = rulesString.replace(ruleFileBluePrint.getGlobals(), baseRuleFileBluePrint.getGlobals());
rulesString = rulesString.replace(ruleCompilationResult.getImports(), baseRuleCompilationResult.getImports());
rulesString = rulesString.replace(ruleCompilationResult.getGlobals(), baseRuleCompilationResult.getGlobals());
try (OutputStream outStream = new FileOutputStream(rulesFile.getFile().getAbsolutePath().replace("/test", "").replace("build", "src/test"))) {
outStream.write(rulesString.getBytes(StandardCharsets.UTF_8));
@ -460,9 +460,9 @@ class DroolsValidationServiceTest {
end
""";
RuleFileBluePrint ruleFileBluePrint = RuleFileParser.buildBluePrintFromRulesString(ruleString);
RuleCompilationResult ruleCompilationResult = RuleCompilationResultParser.buildRuleCompilationResultFromRuleString(ruleString);
assertFalse(ruleFileBluePrint.getDroolsValidation().isCompiled());
assertFalse(ruleCompilationResult.getDroolsValidation().isCompiled());
}
}

View File

@ -0,0 +1,50 @@
package drools
//------------------------------------ Table extraction rules ------------------------------------
// Rule unit: TAB.0
rule "TAB.0.0: Changed Study Type File Attribute"
when
not FileAttribute(label == "OECD Number", valueEqualsAnyOf("402","403","404","405","425","429","436","438","439","471","487"))
$section: Section(containsAnyString("DATA REQUIREMENT", "TEST GUIDELINE", "MÉTODO(S) DE REFERÊNCIA(S):")
&& containsAnyString("OECD", "EPA", "OPPTS"))
then
RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|(?:.{5,40}(?:Number |Procedure |Guideline )))(4[\\d]{2})", 1 ,$section.getTextBlock()).stream()
.map(boundary -> $section.getTextBlock().subSequence(boundary).toString())
.map(value -> FileAttribute.builder().label("OECD Number").value(value).build())
.forEach(fileAttribute -> insert(fileAttribute));
RedactionSearchUtility.findTextRangesByRegexIgnoreCase("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", 1, $section.getTextBlock()).stream()
.map(boundary -> $section.getTextBlock().subSequence(boundary).toString())
.map(value -> FileAttribute.builder().label("OECD Number").value(value).build())
.forEach(fileAttribute -> insert(fileAttribute));
end
rule "TAB.0.1: Changed Guidelines"
when
$section: Section(containsAnyString("DATA REQUIREMENT", "TEST GUIDELINE", "MÉTODO(S) DE REFERÊNCIA(S):") && containsAnyString("OECD", "EPA", "OPPTS"))
then
entityCreationService.byRegex("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2})", "oecd_guideline_number", EntityType.ENTITY, 1, $section)
.forEach(guideline -> guideline.apply("TAB.0.1", "OECD Guideline no. found"));
entityCreationService.byRegex("(?<=OECD)(?:[\\w\\s,\\[\\]\\(\\)\\.]{1,10}|.{5,40}(?:Number |Procedure |Guideline ))(4[\\d]{2}),?\\s\\(?(\\d{4})\\)?", "oecd_guideline_year", EntityType.ENTITY, 2, $section)
.forEach(guideline -> guideline.apply("TAB.0.1", "OECD Guideline year found"));
entityCreationService.byRegex("(?<=OECD)[\\w\\s,\\[\\]]{1,10}\\((\\d{4})\\)\\s(4[\\d]{2})", "oecd_guideline_year", EntityType.ENTITY, 1, $section)
.forEach(guideline -> guideline.apply("TAB.0.1", "OECD Guideline year found"));
entityCreationService.byRegex("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", "oecd_guideline_number", EntityType.ENTITY, 1, $section)
.forEach(guideline -> guideline.apply("TAB.0.1", "OECD Guideline number found"));
entityCreationService.byRegex("(?<=OECD).{5,40}Method (4[\\d]{2}).{1,65}(\\d{4})\\)", "oecd_guideline_year", EntityType.ENTITY, 2, $section)
.forEach(guideline -> guideline.apply("TAB.0.1", "OECD Guideline year found"));
end
// Rule unit: TAB.6
rule "TAB.6.1: Changed Targeted cell extraction (Experimental Stop date)"
when
$section: Section(getHeadline().containsString("Advanced Table Extraction"), containsAllStrings("female", "Female", "Survived", "Group 2"))
$table: Table(hasHeader("Group 2")) from $section.streamChildren().toList()
TableCell(containsWordIgnoreCase("Female"), $row: row) from $table.streamTableCellsWithHeader("Group 2").toList()
TableCell($row == row, containsStringIgnoreCase("Survived")) from $table.streamTableCellsWithHeader("Group 2").toList()
$femaleSurvived: TableCell($row == row) from $table.streamTableCellsWithHeader("Group 2").toList()
then
entityCreationService.bySemanticNode($femaleSurvived, "experiment_female_survived", EntityType.ENTITY)
.ifPresent(entity -> entity.apply("TAB.6.0", "Female in group to experimental start date"));
end

View File

@ -27,7 +27,6 @@ sourceSets {
}
dependencies {
implementation(project(":redaction-service-server-v1"))
testImplementation(platform("org.junit:junit-bom:5.10.0"))
testImplementation("org.junit.jupiter:junit-jupiter")

View File

@ -48,17 +48,17 @@ public class RuleFileFactory {
}
RuleFileBluePrint bluePrint = RuleFileParser.buildBluePrintFromAllRulesFile(applicationType);
RuleFileBluePrint filteredBluePrint = bluePrint.buildFilteredBluePrintByRuleIdentifiers(identifiers);
return buildRuleString(filteredBluePrint);
return buildRuleString(filteredBluePrint, false, false);
}
@SneakyThrows
public String buildRuleString(RuleFileBluePrint bluePrint) {
public String buildRuleString(RuleFileBluePrint bluePrint, boolean dropImports, boolean dropQueries) {
try (var templateInputStream = RuleManagementResources.getTemplateInputStream()) {
String template = new String(templateInputStream.readAllBytes(), StandardCharsets.UTF_8);
List<RuleType> templateRuleOrder = parseRuleOrder(template);
return buildBluePrintWithTemplateRuleOrder(bluePrint, templateRuleOrder);
return buildBluePrintWithTemplateRuleOrder(bluePrint, templateRuleOrder, dropImports, dropQueries);
}
}
@ -72,23 +72,35 @@ public class RuleFileFactory {
}
private String buildBluePrintWithTemplateRuleOrder(RuleFileBluePrint bluePrint, List<RuleType> ruleOrder) {
private String buildBluePrintWithTemplateRuleOrder(RuleFileBluePrint bluePrint, List<RuleType> ruleOrder, boolean dropImports, boolean dropQueries) {
Set<RuleType> additionalRuleTypes = bluePrint.ruleClasses()
Set<RuleType> additionalRuleTypes = bluePrint.getRuleClasses()
.stream()
.map(RuleClass::ruleType)
.filter(ruleType -> !ruleOrder.contains(ruleType))
.collect(Collectors.toSet());
StringBuilder sb = new StringBuilder();
sb.append(bluePrint.imports());
if (!dropImports) {
sb.append(bluePrint.getImports());
sb.append("\n\n");
sb.append(bluePrint.globals());
} else {
//todo: this is hacked to enable compiling the rules without imports
sb.append("package drools");
sb.append("\n\n");
}
sb.append(bluePrint.getGlobals());
sb.append("\n\n");
sb.append("//------------------------------------ declarations ------------------------------------");
sb.append("\n\n");
sb.append(bluePrint.getDeclarations().isEmpty() ? "" : bluePrint.getDeclarations());
sb.append("\n\n");
if (!dropQueries) {
sb.append("//------------------------------------ queries ------------------------------------");
sb.append("\n\n");
sb.append(bluePrint.queries());
sb.append(bluePrint.getQueries());
sb.append("\n\n");
}
for (RuleType ruleBlockType : ruleOrder) {
if (ruleBlockType.isWildCard()) {
additionalRuleTypes.stream()
@ -101,6 +113,9 @@ public class RuleFileFactory {
}
writeRuleClass(bluePrint, ruleBlockType, sb);
}
sb.append("//------------------------------------ functions ------------------------------------");
sb.append("\n\n");
sb.append(bluePrint.getFunctions().isEmpty() ? "" : bluePrint.getFunctions());
return sb.toString().trim() + "\n";
}

View File

@ -10,6 +10,8 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.drools.drl.ast.descr.AbstractClassTypeDeclarationDescr;
import org.drools.drl.ast.descr.FunctionDescr;
import org.drools.drl.ast.descr.ImportDescr;
import org.drools.drl.ast.descr.PackageDescr;
import org.drools.drl.ast.descr.RuleDescr;
@ -18,6 +20,8 @@ import org.kie.internal.builder.conf.LanguageLevelOption;
import com.knecon.fforesight.utility.rules.management.RuleManagementResources;
import com.knecon.fforesight.utility.rules.management.models.ApplicationType;
import com.knecon.fforesight.utility.rules.management.models.BasicDeclaration;
import com.knecon.fforesight.utility.rules.management.models.BasicFunction;
import com.knecon.fforesight.utility.rules.management.models.BasicRule;
import com.knecon.fforesight.utility.rules.management.models.RuleClass;
import com.knecon.fforesight.utility.rules.management.models.RuleFileBluePrint;
@ -45,10 +49,19 @@ public class RuleFileParser {
@SneakyThrows
public RuleFileBluePrint buildBluePrintFromRulesString(String rulesString) {
return buildBluePrintFromRulesString(rulesString, false);
}
@SneakyThrows
public RuleFileBluePrint buildBluePrintFromRulesString(String rulesString, boolean isUserRuleUpdate) {
DrlParser parser = new DrlParser(LanguageLevelOption.DRL6);
PackageDescr packageDescr = parser.parse(false, rulesString);
StringBuilder queryBuilder = new StringBuilder();
List<BasicRule> allRules = new LinkedList<>();
List<BasicFunction> functions = new LinkedList<>();
List<BasicDeclaration> declarations = new LinkedList<>();
for (RuleDescr rule : packageDescr.getRules()) {
if (rule.isQuery()) {
queryBuilder.append(rulesString, rule.getStartCharacter(), rule.getEndCharacter());
@ -57,12 +70,30 @@ public class RuleFileParser {
}
allRules.add(BasicRule.fromRuleDescr(rule, rulesString));
}
String imports = rulesString.substring(0,
for (FunctionDescr function : packageDescr.getFunctions()) {
functions.add(BasicFunction.fromFunctionDescr(function, rulesString));
}
for (AbstractClassTypeDeclarationDescr declaration : packageDescr.getTypeDeclarations()) {
declarations.add(BasicDeclaration.fromDeclarationDescription(declaration, rulesString));
}
for (AbstractClassTypeDeclarationDescr declaration : packageDescr.getEnumDeclarations()) {
declarations.add(BasicDeclaration.fromDeclarationDescription(declaration, rulesString));
}
for (AbstractClassTypeDeclarationDescr declaration : packageDescr.getClassAndEnumDeclarationDescrs()) {
declarations.add(BasicDeclaration.fromDeclarationDescription(declaration, rulesString));
}
for (AbstractClassTypeDeclarationDescr declaration : packageDescr.getTypeDeclarations()) {
declarations.add(BasicDeclaration.fromDeclarationDescription(declaration, rulesString));
}
String imports = "";
if (isUserRuleUpdate && !packageDescr.getImports().isEmpty()) {
imports = rulesString.substring(0,
packageDescr.getImports()
.stream()
.mapToInt(ImportDescr::getEndCharacter)
.max()
.orElseThrow() + 1);
}
String globals = packageDescr.getGlobals()
.stream()
.map(globalDescr -> rulesString.substring(globalDescr.getStartCharacter(), globalDescr.getEndCharacter()))
@ -70,7 +101,7 @@ public class RuleFileParser {
List<RuleClass> ruleClasses = buildRuleClasses(allRules);
return new RuleFileBluePrint(imports.trim(), globals.trim(), queryBuilder.toString().trim(), ruleClasses);
return new RuleFileBluePrint(imports.trim(), globals.trim(), queryBuilder.toString().trim(), ruleClasses, declarations, functions);
}
@ -87,6 +118,7 @@ public class RuleFileParser {
private List<RuleUnit> groupingByGroup(List<BasicRule> rules) {
try {
Map<Integer, List<BasicRule>> rulesPerUnit = rules.stream()
.collect(groupingBy(rule -> rule.identifier().unit()));
return rulesPerUnit.keySet()
@ -94,6 +126,10 @@ public class RuleFileParser {
.sorted()
.map(unit -> new RuleUnit(unit, rulesPerUnit.get(unit)))
.collect(Collectors.toList());
} catch (NullPointerException e) {
System.out.println(rules);
throw e;
}
}

View File

@ -33,7 +33,7 @@ public class RuleFileMigrator {
//replaceRules(ruleFileBluePrint, combinedBluePrint);
replaceRuleIdentifiers(combinedBluePrint, ruleFileBluePrint);
String migratedRulesString = RuleFileFactory.buildRuleString(ruleFileBluePrint);
String migratedRulesString = RuleFileFactory.buildRuleString(ruleFileBluePrint,false, false);
String migratedFilePath = ruleFile.getAbsolutePath();
try (var out = new FileOutputStream(migratedFilePath)) {
out.write(migratedRulesString.getBytes(StandardCharsets.UTF_8));

View File

@ -46,7 +46,7 @@ public class RuleIdentifierMigrator {
bluePrint = migrateMatchedRuleForAllRules(bluePrint);
String ruleString = RuleFileFactory.buildRuleString(bluePrint);
String ruleString = RuleFileFactory.buildRuleString(bluePrint, false, false);
try (var out = new FileOutputStream("/tmp/all_redact_manager_rules.drl")) {
out.write(ruleString.getBytes(StandardCharsets.UTF_8));
}
@ -57,9 +57,10 @@ public class RuleIdentifierMigrator {
}
//todo: introduce functions and declarations
private static RuleFileBluePrint migrateMatchedRuleForAllRules(RuleFileBluePrint bluePrint) {
List<BasicRule> migratedRules = bluePrint.ruleClasses()
List<BasicRule> migratedRules = bluePrint.getRuleClasses()
.stream()
.map(RuleClass::ruleUnits)
.flatMap(Collection::stream)
@ -67,7 +68,12 @@ public class RuleIdentifierMigrator {
.flatMap(Collection::stream)
.map(RuleIdentifierMigrator::migrateMatchedRule)
.toList();
RuleFileBluePrint migratedBluePrint = new RuleFileBluePrint(bluePrint.imports(), bluePrint.globals(), bluePrint.queries(), new LinkedList<>());
RuleFileBluePrint migratedBluePrint = new RuleFileBluePrint(bluePrint.getImports(),
bluePrint.getGlobals(),
bluePrint.getQueries(),
new LinkedList<>(),
new LinkedList<>(),
new LinkedList<>());
migratedRules.forEach(migratedBluePrint::addRule);
return migratedBluePrint;
}

View File

@ -0,0 +1,34 @@
package com.knecon.fforesight.utility.rules.management.models;
import org.drools.drl.ast.descr.AbstractClassTypeDeclarationDescr;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
@Getter
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class BasicDeclaration {
@EqualsAndHashCode.Include
String declarationFullTypeName;
String body;
int line;
public static BasicDeclaration fromDeclarationDescription(AbstractClassTypeDeclarationDescr declaration, String rulesString) {
String declarationFullTypeName = declaration.getFullTypeName();
String body = rulesString.substring(declaration.getStartCharacter(), declaration.getEndCharacter());
int line = declaration.getLine();
return new BasicDeclaration(declarationFullTypeName, body, line);
}
}

View File

@ -0,0 +1,36 @@
package com.knecon.fforesight.utility.rules.management.models;
import org.drools.drl.ast.descr.FunctionDescr;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import lombok.experimental.FieldDefaults;
@Getter
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@ToString
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
public class BasicFunction {
@EqualsAndHashCode.Include
String functionName;
String returnType;
String body;
int line;
public static BasicFunction fromFunctionDescr(FunctionDescr function, String rulesString) {
String functionName = function.getName();
String returnType = function.getReturnType();
String body = rulesString.substring(function.getStartCharacter(), function.getEndCharacter());
int line = function.getLine();
return new BasicFunction(functionName, returnType, body, line);
}
}

View File

@ -10,7 +10,29 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
public record RuleFileBluePrint(String imports, String globals, String queries, List<RuleClass> ruleClasses) {
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
@Data
@Slf4j
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class RuleFileBluePrint {
String imports;
String globals;
String queries;
List<RuleClass> ruleClasses;
List<BasicDeclaration> declarations;
List<BasicFunction> functions;
public boolean removeRule(RuleIdentifier ruleIdentifier) {
@ -24,7 +46,7 @@ public record RuleFileBluePrint(String imports, String globals, String queries,
ruleClass.ruleUnits().remove(ruleUnit);
}
if (ruleClass.ruleUnits().isEmpty()) {
ruleClasses().remove(ruleClass);
this.ruleClasses.remove(ruleClass);
}
}));
return wasRemoved.get();
@ -41,7 +63,7 @@ public record RuleFileBluePrint(String imports, String globals, String queries,
public Set<BasicRule> getAllRules() {
return ruleClasses().stream()
return this.ruleClasses.stream()
.map(RuleClass::ruleUnits)
.flatMap(Collection::stream)
.map(RuleUnit::rules)
@ -101,7 +123,7 @@ public record RuleFileBluePrint(String imports, String globals, String queries,
public Set<RuleIdentifier> getAllRuleIdentifiers() {
return ruleClasses().stream()
return ruleClasses.stream()
.map(RuleClass::ruleUnits)
.flatMap(Collection::stream)
.map(RuleUnit::rules)
@ -122,10 +144,16 @@ public record RuleFileBluePrint(String imports, String globals, String queries,
}
public void setImports(String newImports) {
this.imports = newImports;
}
public RuleFileBluePrint buildFilteredBluePrintByRuleIdentifiers(Set<RuleIdentifier> identifiers) {
RuleFileBluePrint filteredBluePrint = new RuleFileBluePrint(imports(), globals(), queries(), new LinkedList<>());
ruleClasses().stream()
RuleFileBluePrint filteredBluePrint = new RuleFileBluePrint(imports, globals, queries, new LinkedList<>(), new LinkedList<>(), new LinkedList<>());
ruleClasses.stream()
.flatMap(ruleClass -> ruleClass.ruleUnits()
.stream())
.flatMap(ruleUnit -> ruleUnit.rules()

View File

@ -8,7 +8,7 @@ import com.knecon.fforesight.utility.rules.management.factory.RuleFileParser;
import com.knecon.fforesight.utility.rules.management.models.RuleFileBluePrint;
import com.knecon.fforesight.utility.rules.management.models.RuleIdentifier;
public class RuleFileBluePrintMergingTest {
public class RuleCompilationResultMergingTest {
@Test
public void testBothRuleFilesCanBeMerged() {