diff --git a/redaction-service-v1/redaction-service-api-v1/build.gradle.kts b/redaction-service-v1/redaction-service-api-v1/build.gradle.kts index 96bccabd..0e52ff8c 100644 --- a/redaction-service-v1/redaction-service-api-v1/build.gradle.kts +++ b/redaction-service-v1/redaction-service-api-v1/build.gradle.kts @@ -9,6 +9,7 @@ val persistenceServiceVersion = "2.581.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 { diff --git a/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/resources/RuleBuilderResource.java b/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/resources/RuleBuilderResource.java index 3864ba9b..4683fd5f 100644 --- a/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/resources/RuleBuilderResource.java +++ b/redaction-service-v1/redaction-service-api-v1/src/main/java/com/iqser/red/service/redaction/v1/resources/RuleBuilderResource.java @@ -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); + } diff --git a/redaction-service-v1/redaction-service-server-v1/build.gradle.kts b/redaction-service-v1/redaction-service-server-v1/build.gradle.kts index 75fef208..013ad986 100644 --- a/redaction-service-v1/redaction-service-server-v1/build.gradle.kts +++ b/redaction-service-v1/redaction-service-server-v1/build.gradle.kts @@ -39,6 +39,7 @@ configurations.all { 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}") @@ -83,6 +84,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") diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/controller/RuleBuilderController.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/controller/RuleBuilderController.java index 306dccbe..23e5c9d2 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/controller/RuleBuilderController.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/controller/RuleBuilderController.java @@ -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 } } diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/RuleMergingResult.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/RuleMergingResult.java new file mode 100644 index 00000000..b2cb1b27 --- /dev/null +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/RuleMergingResult.java @@ -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; + +} diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/drools/RuleFileBluePrint.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/drools/RuleCompilationResult.java similarity index 62% rename from redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/drools/RuleFileBluePrint.java rename to redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/drools/RuleCompilationResult.java index 338e58a7..b69c629c 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/drools/RuleFileBluePrint.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/model/drools/RuleCompilationResult.java @@ -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 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 systemRules) { + + log.info("removing all rules except the system rules"); + + List 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 rulesToBeRemoved = ruleClasses.stream() + .filter(ruleClass -> Objects.equals(ruleClass.ruleType(), ruleType)) + .collect(Collectors.toList()); + return rulesToBeRemoved.size(); + } + + + private boolean filterOutRule(RuleType ruleType, List filteredRules) { + + return !filteredRules.contains(ruleType.name()); + } + + public Set 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); + } + } diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/RuleBuilderService.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/RuleBuilderService.java new file mode 100644 index 00000000..95cecf24 --- /dev/null +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/RuleBuilderService.java @@ -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 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 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 rules = new ArrayList(); + Set 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 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 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)); + } + } + +} diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/DroolsValidationService.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/DroolsValidationService.java index 6d98ae0c..df73f4b8 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/DroolsValidationService.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/DroolsValidationService.java @@ -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 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 getWarningsForDeprecatedRules(RuleFileBluePrint ruleFileBluePrint) { + private List getWarningsForDeprecatedRules(RuleCompilationResult ruleCompilationResult) { List warningMessages = new ArrayList<>(); @@ -141,7 +141,7 @@ public class DroolsValidationService { SearchImplementation methodsSearchImplementation = deprecatedElementsFinder.getMethodsSearchImplementation(); Map deprecatedMethodsSignatureMap = deprecatedElementsFinder.getDeprecatedMethodsSignaturesMap(); - for (RuleClass ruleClass : ruleFileBluePrint.getRuleClasses()) { + for (RuleClass ruleClass : ruleCompilationResult.getRuleClasses()) { for (RuleUnit ruleUnit : ruleClass.ruleUnits()) { for (BasicRule basicRule : ruleUnit.rules()) { List 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 baseImports = baseRuleFileBluePrint.getImportSplitByKeyword(); - Set imports = ruleFileBluePrint.getImportSplitByKeyword(); + Set baseImports = baseRuleCompilationResult.getImportSplitByKeyword(); + Set imports = ruleCompilationResult.getImportSplitByKeyword(); Set 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 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, diff --git a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/RuleFileParser.java b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/RuleCompilationResultParser.java similarity index 53% rename from redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/RuleFileParser.java rename to redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/RuleCompilationResultParser.java index 375dc0f2..2a5158f3 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/RuleFileParser.java +++ b/redaction-service-v1/redaction-service-server-v1/src/main/java/com/iqser/red/service/redaction/v1/server/service/drools/RuleCompilationResultParser.java @@ -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 allRules = new LinkedList<>(); + List allQueries = new LinkedList<>(); + PackageDescr packageDescr = new PackageDescr(); + String imports = ""; + String globals = ""; + List 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, - packageDescr.getImports() - .stream() - .mapToInt(ImportDescr::getEndCharacter) - .max() - .orElseThrow() + 1); + 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,21 +95,81 @@ public class RuleFileParser { List ruleClasses = buildRuleClasses(allRules); - return new RuleFileBluePrint(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); + 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 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(); } @@ -126,6 +216,7 @@ public class RuleFileParser { private List buildRuleClasses(List allRules) { + //todo: comments List ruleTypeOrder = allRules.stream() .map(BasicRule::getIdentifier) .map(RuleIdentifier::type) diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/RuleBuilderTest.java b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/RuleBuilderTest.java new file mode 100644 index 00000000..de4592ff --- /dev/null +++ b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/RuleBuilderTest.java @@ -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 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(); + } + +} diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/drools/files/management/services/DroolsValidationServiceTest.java b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/drools/files/management/services/DroolsValidationServiceTest.java index b8fd95fe..2e42db62 100644 --- a/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/drools/files/management/services/DroolsValidationServiceTest.java +++ b/redaction-service-v1/redaction-service-server-v1/src/test/java/com/iqser/red/service/redaction/v1/server/drools/files/management/services/DroolsValidationServiceTest.java @@ -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()); } } \ No newline at end of file diff --git a/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/user_rule_update.drl b/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/user_rule_update.drl new file mode 100644 index 00000000..16ea73d2 --- /dev/null +++ b/redaction-service-v1/redaction-service-server-v1/src/test/resources/drools/user_rule_update.drl @@ -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 + diff --git a/redaction-service-v1/rules-management/build.gradle.kts b/redaction-service-v1/rules-management/build.gradle.kts index 2aaea40c..b3ef9cfa 100644 --- a/redaction-service-v1/rules-management/build.gradle.kts +++ b/redaction-service-v1/rules-management/build.gradle.kts @@ -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") diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileFactory.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileFactory.java index 039664bf..31d653d1 100644 --- a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileFactory.java +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileFactory.java @@ -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 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 ruleOrder) { + private String buildBluePrintWithTemplateRuleOrder(RuleFileBluePrint bluePrint, List ruleOrder, boolean dropImports, boolean dropQueries) { - Set additionalRuleTypes = bluePrint.ruleClasses() + Set 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"); + } 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(bluePrint.globals()); + sb.append("//------------------------------------ declarations ------------------------------------"); sb.append("\n\n"); - sb.append("//------------------------------------ queries ------------------------------------"); - sb.append("\n\n"); - sb.append(bluePrint.queries()); + sb.append(bluePrint.getDeclarations().isEmpty() ? "" : bluePrint.getDeclarations()); sb.append("\n\n"); + if (!dropQueries) { + sb.append("//------------------------------------ queries ------------------------------------"); + sb.append("\n\n"); + 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"; } diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileParser.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileParser.java index 3e17b0db..36b206aa 100644 --- a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileParser.java +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/factory/RuleFileParser.java @@ -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 allRules = new LinkedList<>(); + List functions = new LinkedList<>(); + List 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, - packageDescr.getImports() - .stream() - .mapToInt(ImportDescr::getEndCharacter) - .max() - .orElseThrow() + 1); + 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 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,13 +118,18 @@ public class RuleFileParser { private List groupingByGroup(List rules) { - Map> rulesPerUnit = rules.stream() - .collect(groupingBy(rule -> rule.identifier().unit())); - return rulesPerUnit.keySet() - .stream() - .sorted() - .map(unit -> new RuleUnit(unit, rulesPerUnit.get(unit))) - .collect(Collectors.toList()); + try { + Map> rulesPerUnit = rules.stream() + .collect(groupingBy(rule -> rule.identifier().unit())); + return rulesPerUnit.keySet() + .stream() + .sorted() + .map(unit -> new RuleUnit(unit, rulesPerUnit.get(unit))) + .collect(Collectors.toList()); + } catch (NullPointerException e) { + System.out.println(rules); + throw e; + } } diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/migration/RuleFileMigrator.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/migration/RuleFileMigrator.java index 94975b8e..7a031490 100644 --- a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/migration/RuleFileMigrator.java +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/migration/RuleFileMigrator.java @@ -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)); diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/migration/RuleIdentifierMigrator.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/migration/RuleIdentifierMigrator.java index fc8e9026..20a0ab5f 100644 --- a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/migration/RuleIdentifierMigrator.java +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/migration/RuleIdentifierMigrator.java @@ -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 migratedRules = bluePrint.ruleClasses() + List 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; } diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/BasicDeclaration.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/BasicDeclaration.java new file mode 100644 index 00000000..5798601f --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/BasicDeclaration.java @@ -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); + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/BasicFunction.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/BasicFunction.java new file mode 100644 index 00000000..c707ba79 --- /dev/null +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/BasicFunction.java @@ -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); + } + +} diff --git a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleFileBluePrint.java b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleFileBluePrint.java index 2b2ae91f..50242175 100644 --- a/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleFileBluePrint.java +++ b/redaction-service-v1/rules-management/src/main/java/com/knecon/fforesight/utility/rules/management/models/RuleFileBluePrint.java @@ -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 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 ruleClasses; + List declarations; + List 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 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 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 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() diff --git a/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/RuleFileBluePrintMergingTest.java b/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/RuleCompilationResultMergingTest.java similarity index 93% rename from redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/RuleFileBluePrintMergingTest.java rename to redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/RuleCompilationResultMergingTest.java index 559101ac..f9fe18f4 100644 --- a/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/RuleFileBluePrintMergingTest.java +++ b/redaction-service-v1/rules-management/src/test/java/com/knecon/fforesight/utility/rules/management/RuleCompilationResultMergingTest.java @@ -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() {