RED-9472: seperation of system rules

refactored two have a rulefileblueprint and a rulefileblueprint for validationresult

changed the flow of merging and seperating system and user rules

added functions and declarations to rule file parsing
This commit is contained in:
yhampe 2024-10-07 13:59:34 +02:00
parent 4978259fc6
commit ae38506809
17 changed files with 338 additions and 142 deletions

View File

@ -1,6 +1,7 @@
package com.iqser.red.service.redaction.v1.resources;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@ -20,6 +21,6 @@ public interface RuleBuilderResource {
@PostMapping(value = "/internal-api/rules/system-rules", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
RulesResponse getRuleFileWithSeperatedSystemRules(@RequestBody RulesUpdateRequest rulesUpdateRequest);
ResponseEntity mergeUserUpdateRules(@RequestBody RulesUpdateRequest rulesUpdateRequest);
}

View File

@ -1,13 +1,20 @@
package com.iqser.red.service.redaction.v1.server.controller;
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;
@ -36,28 +43,54 @@ public class RuleBuilderController implements RuleBuilderResource {
RulesResponse rulesResponse = new RulesResponse();
String filteredRules = this.ruleBuilderService.cleanRuleFileOfSystemRules(systemRulesSeperationRequest.getRules(), true);
try {
droolsValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), filteredRules));
} catch (Exception e) {
throw new RulesValidationException("Error trying to compile the cleaned rule file: " + filteredRules + " with message: {} " + e.getMessage(), e);
}
rulesResponse.setRules(filteredRules);
return rulesResponse;
}
@Override
public RulesResponse getRuleFileWithSeperatedSystemRules(RulesUpdateRequest rulesUpdateRequest) {
public ResponseEntity mergeUserUpdateRules(RulesUpdateRequest rulesUpdateRequest) {
RulesResponse rulesResponse = new RulesResponse();
String mergedRules = ruleBuilderService.getRuleFileWithSeperatedSystemRules(rulesUpdateRequest.getExistingRules(), rulesUpdateRequest.getUpdatedRules());
DroolsValidationResponse droolsValidationResponse = new DroolsValidationResponse();
RuleMergingResult mergingResult = ruleBuilderService.mergeUserRulesAndSystemRules(rulesUpdateRequest.getExistingRules(), rulesUpdateRequest.getUpdatedRules());
try {
droolsValidationService.testRules(new RuleValidationModel(RuleFileType.ENTITY.name(), mergedRules));
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("Error trying to compile the cleaned rule file: " + mergedRules + " with message: {} " + e.getMessage(), e);
throw new RulesValidationException("Could not test rules: " + e.getMessage(), e);
}
rulesResponse.setRules(mergedRules);
return rulesResponse;
return new ResponseEntity<>(rulesResponse, HttpStatus.OK);
}

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

@ -1,23 +0,0 @@
package com.iqser.red.service.redaction.v1.server.model;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Schema(description = "Object containing a string of Drools rules.")
public class RulesResponse {
@Schema(description = "The actual string of rules.")
private String rules;
@Schema(description = "The DossierTemplate Id for these rules")
private String dossierTemplateId;
@Schema(description = "Bad written rules can lead to timeouts or endless processing. This will be detected by the system and all analyse request for the rules will be rejected. This flag indicates that a timeout was detected and you need to fix the rules")
private boolean timeoutDetected;
}

View File

@ -26,7 +26,7 @@ import lombok.extern.slf4j.Slf4j;
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public final class RuleFileBluePrint {
public final class RuleCompilationResult {
String imports;
int importLine;

View File

@ -8,9 +8,11 @@ import java.util.List;
import org.springframework.stereotype.Service;
import com.iqser.red.service.redaction.v1.model.RuleBuilderModel;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleFileBluePrint;
import com.iqser.red.service.redaction.v1.server.model.drools.RuleType;
import com.iqser.red.service.redaction.v1.server.service.drools.RuleFileParser;
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 lombok.extern.slf4j.Slf4j;
@ -37,34 +39,32 @@ public class RuleBuilderService {
RuleFileBluePrint ruleFileBluePrint = RuleFileParser.buildBluePrintFromRulesString(rulesString);
log.info("Starting to remove system rules from ruleFile");
for (String systemRule : systemRules) {
ruleFileBluePrint.dropRulesByIdentifier(RuleType.fromString(systemRule));
}
ruleFileBluePrint.dropQueries();
if (removeImports) {
ruleFileBluePrint.dropImports();
ruleFileBluePrint.removeRule(RuleIdentifier.fromName(systemRule));
}
log.info("Finished removing system rules for ruleFile");
return RuleFileParser.buildRulesStringFromBluePrint(ruleFileBluePrint);
// removing imports if flagged and dropping queries when building result string
return RuleFileFactory.buildRuleString(ruleFileBluePrint, removeImports, true);
}
public String getRuleFileWithSeperatedSystemRules(String existingRules, String updatedRules) {
public RuleMergingResult mergeUserRulesAndSystemRules(String existingRules, String updatedRules) {
//todo: when returning validation result to frotnend substract the length of imports/globals
log.info("starting to merge user rules update with system rules");
RuleFileBluePrint ruleFileBluePrintExisting = RuleFileParser.buildBluePrintFromRulesString(existingRules);
ruleFileBluePrintExisting.dropAllRulesExceptSystemRules(systemRules);
RuleFileBluePrint ruleFileBluePrintUpdate = RuleFileParser.buildBluePrintFromRulesString(updatedRules);
for (String systemRule : systemRules) {
if (ruleFileBluePrintUpdate.countRuleOccurences(RuleType.fromString(systemRule)) > 0) {
throw new RuntimeException("System rules are not allowed in an update.");
RuleFileBluePrint ruleFileBluePrintExistingMerged = RuleFileParser.buildBluePrintFromRulesString(updatedRules);
// add imports from systemrules
ruleFileBluePrintExistingMerged.setImports(ruleFileBluePrintExisting.getImports() + ruleFileBluePrintExistingMerged.getImports());
//add globals from systemrules
ruleFileBluePrintExistingMerged.setGlobals(ruleFileBluePrintExisting.getGlobals() + ruleFileBluePrintExistingMerged.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();
}
}
ruleFileBluePrintExisting.setImports(ruleFileBluePrintUpdate.getImports());
ruleFileBluePrintUpdate.getRuleClasses()
.forEach(ruleFileBluePrintExisting::addRuleClass);
return RuleFileParser.buildRulesStringFromBluePrint(ruleFileBluePrintExisting);
return mergingResult;
}
}

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,38 @@ 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) {
DroolsValidation customDroolsValidation = DroolsValidation.builder().build();
DrlParser parser = new DrlParser(LanguageLevelOption.DRL6);
@ -65,26 +89,26 @@ public class RuleFileParser {
List<RuleClass> ruleClasses = buildRuleClasses(allRules);
return new RuleFileBluePrint(imports.trim(),
packageDescr.getImports()
return new RuleCompilationResult(imports.trim(),
packageDescr.getImports()
.stream()
.findFirst()
.map(ImportDescr::getLine)
.orElse(0),
globals.trim(),
packageDescr.getGlobals()
globals.trim(),
packageDescr.getGlobals()
.stream()
.findFirst()
.map(GlobalDescr::getLine)
.orElse(0),
allQueries,
ruleClasses,
customDroolsValidation);
allQueries,
ruleClasses,
customDroolsValidation);
}
@SneakyThrows
public String buildRulesStringFromBluePrint(RuleFileBluePrint bluePrint) {
public String buildRulesStringFromBluePrint(RuleCompilationResult bluePrint) {
StringBuilder ruleStringBuilder = new StringBuilder();
@ -179,6 +203,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

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

@ -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,31 @@ 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.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());
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 +109,9 @@ public class RuleFileFactory {
}
writeRuleClass(bluePrint, ruleBlockType, sb);
}
sb.append("//------------------------------------ functions ------------------------------------");
sb.append("\n\n");
sb.append(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;
@ -49,6 +53,8 @@ public class RuleFileParser {
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,6 +63,21 @@ public class RuleFileParser {
}
allRules.add(BasicRule.fromRuleDescr(rule, rulesString));
}
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 = rulesString.substring(0,
packageDescr.getImports()
.stream()
@ -70,7 +91,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);
}

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() {