generated from CDCgov/template
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into story/650/results-metadata-new-issue
- Loading branch information
Showing
21 changed files
with
800 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/FhirResource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package gov.hhs.cdc.trustedintermediary.etor.ruleengine; | ||
|
||
/** | ||
* This interface represents a FHIR resource. It's used as a wrapper to decouple dependency on third | ||
* party libraries. | ||
* | ||
* @param <T> the type of the underlying resource | ||
*/ | ||
public interface FhirResource<T> { | ||
T getUnderlyingResource(); | ||
} |
28 changes: 28 additions & 0 deletions
28
etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/Rule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package gov.hhs.cdc.trustedintermediary.etor.ruleengine; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* The Rule interface defines the structure for a rule in the rule engine. Each rule has a name, | ||
* description, warning message, conditions, validations, and methods to check if a resource is | ||
* valid and if the rule applies to a resource. | ||
*/ | ||
public interface Rule { | ||
String getName(); | ||
|
||
String getDescription(); | ||
|
||
/** | ||
* Descriptive message when there's a rule violation Note: When implementing this method, make | ||
* sure that no PII or PHI is included in the message! | ||
*/ | ||
String getViolationMessage(); | ||
|
||
List<String> getConditions(); | ||
|
||
List<String> getValidations(); | ||
|
||
boolean isValid(FhirResource<?> resource); | ||
|
||
boolean appliesTo(FhirResource<?> resource); | ||
} |
59 changes: 59 additions & 0 deletions
59
etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleEngine.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package gov.hhs.cdc.trustedintermediary.etor.ruleengine; | ||
|
||
import gov.hhs.cdc.trustedintermediary.wrappers.Logger; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.nio.charset.StandardCharsets; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import javax.inject.Inject; | ||
|
||
/** Manages the application of rules loaded from a definitions file using the RuleLoader. */ | ||
public class RuleEngine { | ||
|
||
private static final RuleEngine INSTANCE = new RuleEngine(); | ||
|
||
final List<Rule> rules = new ArrayList<>(); | ||
|
||
@Inject Logger logger; | ||
@Inject RuleLoader ruleLoader; | ||
|
||
private RuleEngine() {} | ||
|
||
public static RuleEngine getInstance() { | ||
return INSTANCE; | ||
} | ||
|
||
public void unloadRules() { | ||
rules.clear(); | ||
} | ||
|
||
public void ensureRulesLoaded() { | ||
if (rules.isEmpty()) { | ||
loadRules(); | ||
} | ||
} | ||
|
||
private synchronized void loadRules() { | ||
String fileName = "rule_definitions.json"; | ||
try (InputStream ruleDefinitionStream = | ||
getClass().getClassLoader().getResourceAsStream(fileName)) { | ||
assert ruleDefinitionStream != null; | ||
var ruleStream = | ||
new String(ruleDefinitionStream.readAllBytes(), StandardCharsets.UTF_8); | ||
rules.addAll(ruleLoader.loadRules(ruleStream)); | ||
} catch (IOException | RuleLoaderException e) { | ||
logger.logError("Failed to load rules definitions from: " + fileName, e); | ||
} | ||
} | ||
|
||
public void validate(FhirResource<?> resource) { | ||
logger.logDebug("Validating FHIR resource"); | ||
ensureRulesLoaded(); | ||
for (Rule rule : rules) { | ||
if (rule.appliesTo(resource) && !rule.isValid(resource)) { | ||
logger.logWarning("Rule violation: " + rule.getViolationMessage()); | ||
} | ||
} | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleLoader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package gov.hhs.cdc.trustedintermediary.etor.ruleengine; | ||
|
||
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.Formatter; | ||
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.FormatterProcessingException; | ||
import gov.hhs.cdc.trustedintermediary.wrappers.formatter.TypeReference; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import javax.inject.Inject; | ||
|
||
/** Manages the loading of rules from a definitions file. */ | ||
public class RuleLoader { | ||
private static final RuleLoader INSTANCE = new RuleLoader(); | ||
@Inject Formatter formatter; | ||
|
||
private RuleLoader() {} | ||
|
||
public static RuleLoader getInstance() { | ||
return INSTANCE; | ||
} | ||
|
||
public List<ValidationRule> loadRules(String ruleStream) throws RuleLoaderException { | ||
try { | ||
Map<String, List<ValidationRule>> jsonObj = | ||
formatter.convertJsonToObject(ruleStream, new TypeReference<>() {}); | ||
return jsonObj.getOrDefault("rules", Collections.emptyList()); | ||
} catch (FormatterProcessingException e) { | ||
throw new RuleLoaderException( | ||
"Failed to load rules definitions for provided stream", e); | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/RuleLoaderException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package gov.hhs.cdc.trustedintermediary.etor.ruleengine; | ||
|
||
/** Custom exception class use to catch RuleLoader exceptions */ | ||
public class RuleLoaderException extends Exception { | ||
|
||
public RuleLoaderException(String message, Throwable cause) { | ||
super(message, cause); | ||
} | ||
} |
108 changes: 108 additions & 0 deletions
108
etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/ValidationRule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
package gov.hhs.cdc.trustedintermediary.etor.ruleengine; | ||
|
||
import gov.hhs.cdc.trustedintermediary.context.ApplicationContext; | ||
import gov.hhs.cdc.trustedintermediary.wrappers.HapiFhir; | ||
import gov.hhs.cdc.trustedintermediary.wrappers.Logger; | ||
import java.util.List; | ||
|
||
/** | ||
* Implements the Rule interface. It represents a rule with a name, description, warning message, | ||
* conditions, and validations. It uses the HapiFhir engine to evaluate the conditions and | ||
* validations. | ||
*/ | ||
public class ValidationRule implements Rule { | ||
|
||
private final Logger logger = ApplicationContext.getImplementation(Logger.class); | ||
private final HapiFhir fhirEngine = ApplicationContext.getImplementation(HapiFhir.class); | ||
private String name; | ||
private String description; | ||
private String violationMessage; | ||
private List<String> conditions; | ||
private List<String> validations; | ||
|
||
/** | ||
* Do not delete this constructor! It is used for JSON deserialization when loading rules from a | ||
* file. | ||
*/ | ||
public ValidationRule() {} | ||
|
||
public ValidationRule( | ||
String ruleName, | ||
String ruleDescription, | ||
String ruleWarningMessage, | ||
List<String> ruleConditions, | ||
List<String> ruleValidations) { | ||
name = ruleName; | ||
description = ruleDescription; | ||
violationMessage = ruleWarningMessage; | ||
conditions = ruleConditions; | ||
validations = ruleValidations; | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return name; | ||
} | ||
|
||
@Override | ||
public String getDescription() { | ||
return description; | ||
} | ||
|
||
@Override | ||
public String getViolationMessage() { | ||
return violationMessage; | ||
} | ||
|
||
@Override | ||
public List<String> getConditions() { | ||
return conditions; | ||
} | ||
|
||
@Override | ||
public List<String> getValidations() { | ||
return validations; | ||
} | ||
|
||
@Override | ||
public boolean isValid(FhirResource<?> resource) { | ||
return validations.stream() | ||
.allMatch( | ||
validation -> { | ||
try { | ||
return fhirEngine.evaluateCondition( | ||
resource.getUnderlyingResource(), validation); | ||
} catch (Exception e) { | ||
logger.logError( | ||
"Rule [" | ||
+ name | ||
+ "]: " | ||
+ "An error occurred while evaluating the validation: " | ||
+ validation, | ||
e); | ||
return false; | ||
} | ||
}); | ||
} | ||
|
||
@Override | ||
public boolean appliesTo(FhirResource<?> resource) { | ||
return conditions.stream() | ||
.allMatch( | ||
condition -> { | ||
try { | ||
return fhirEngine.evaluateCondition( | ||
resource.getUnderlyingResource(), condition); | ||
} catch (Exception e) { | ||
logger.logError( | ||
"Rule [" | ||
+ name | ||
+ "]: " | ||
+ "An error occurred while evaluating the condition: " | ||
+ condition, | ||
e); | ||
return false; | ||
} | ||
}); | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
etor/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiFhirResource.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package gov.hhs.cdc.trustedintermediary.external.hapi; | ||
|
||
import gov.hhs.cdc.trustedintermediary.etor.ruleengine.FhirResource; | ||
import org.hl7.fhir.instance.model.api.IBaseResource; | ||
|
||
/** An implementation of {@link FhirResource} to use as a wrapper around HAPI FHIR IBaseResource */ | ||
public class HapiFhirResource implements FhirResource<IBaseResource> { | ||
|
||
private final IBaseResource innerResource; | ||
|
||
public HapiFhirResource(IBaseResource innerResource) { | ||
this.innerResource = innerResource; | ||
} | ||
|
||
@Override | ||
public IBaseResource getUnderlyingResource() { | ||
return innerResource; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"rules": [ { | ||
"name": "requiredReceiverId", | ||
"description": "Message has required receiver id", | ||
"violationMessage": "Message doesn't have required receiver id", | ||
"conditions": [ ], | ||
"validations": [ | ||
"Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.value.exists()" | ||
] | ||
}, | ||
{ | ||
"name": "requiredReceiverId", | ||
"description": "ORM has required Card Number", | ||
"violationMessage": "ORM doesn't have required Card Number", | ||
"conditions": [ | ||
"Bundle.entry.resource.ofType(MessageHeader).event.code = 'O01'" | ||
], | ||
"validations": [ | ||
"Bundle.entry.resource.ofType(Observation).where(code.coding.code = '57723-9').value.coding.code.exists()" | ||
] | ||
} | ||
] | ||
} |
17 changes: 17 additions & 0 deletions
17
etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/FhirResourceMock.groovy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package gov.hhs.cdc.trustedintermediary | ||
|
||
import gov.hhs.cdc.trustedintermediary.etor.ruleengine.FhirResource | ||
|
||
class FhirResourceMock<T> implements FhirResource<T> { | ||
|
||
private T innerResource | ||
|
||
FhirResourceMock(T innerResource) { | ||
this.innerResource = innerResource | ||
} | ||
|
||
@Override | ||
public T getUnderlyingResource() { | ||
return innerResource | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.