ca.uhn.fhir.jpa.mdm.svc.BlockRuleEvaluationSvcImpl Maven / Gradle / Ivy
The newest version!
/*-
* #%L
* HAPI FHIR JPA Server - Master Data Management
* %%
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.fhirpath.FhirPathExecutionException;
import ca.uhn.fhir.fhirpath.IFhirPath;
import ca.uhn.fhir.mdm.blocklist.json.BlockListJson;
import ca.uhn.fhir.mdm.blocklist.json.BlockListRuleJson;
import ca.uhn.fhir.mdm.blocklist.json.BlockedFieldJson;
import ca.uhn.fhir.mdm.blocklist.svc.IBlockListRuleProvider;
import ca.uhn.fhir.mdm.blocklist.svc.IBlockRuleEvaluationSvc;
import ca.uhn.fhir.util.FhirTypeUtil;
import jakarta.annotation.Nullable;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.slf4j.Logger;
import java.util.List;
import static org.slf4j.LoggerFactory.getLogger;
/**
* An implementation of IBlockRuleEvaluationSvc.
* Evaluates whether or not a provided resource
* is blocked from mdm matching or not.
*/
public class BlockRuleEvaluationSvcImpl implements IBlockRuleEvaluationSvc {
private static final Logger ourLog = getLogger(BlockRuleEvaluationSvcImpl.class);
private final IFhirPath myFhirPath;
private final IBlockListRuleProvider myBlockListRuleProvider;
public BlockRuleEvaluationSvcImpl(
FhirContext theContext, @Nullable IBlockListRuleProvider theIBlockListRuleProvider) {
myFhirPath = theContext.newFhirPath();
myBlockListRuleProvider = theIBlockListRuleProvider;
}
private boolean hasBlockList() {
return myBlockListRuleProvider != null && myBlockListRuleProvider.getBlocklistRules() != null;
}
@Override
public boolean isMdmMatchingBlocked(IAnyResource theResource) {
if (hasBlockList()) {
return isMdmMatchingBlockedInternal(theResource);
}
return false;
}
private boolean isMdmMatchingBlockedInternal(IAnyResource theResource) {
BlockListJson blockListJson = myBlockListRuleProvider.getBlocklistRules();
String resourceType = theResource.fhirType();
// gather only applicable rules
// these rules are 'or''d, so if any match,
// mdm matching is blocked
return blockListJson.getBlockListItemJsonList().stream()
.filter(r -> r.getResourceType().equals(resourceType))
.anyMatch(rule -> isMdmBlockedForFhirPath(theResource, rule));
}
private boolean isMdmBlockedForFhirPath(IAnyResource theResource, BlockListRuleJson theRule) {
List blockedFields = theRule.getBlockedFields();
// rules are 'and'ed
// This means that if we detect any reason *not* to block
// we don't; only if all block rules pass do we block
for (BlockedFieldJson field : blockedFields) {
String path = field.getFhirPath();
String blockedValue = field.getBlockedValue();
List results;
try {
// can throw FhirPathExecutionException if path is incorrect
// or functions are invalid.
// single() explicitly throws this (but may be what is desired)
// so we'll catch and not block if this fails
results = myFhirPath.evaluate(theResource, path, IBase.class);
} catch (FhirPathExecutionException ex) {
ourLog.warn(
"FhirPath evaluation failed with an exception."
+ " No blocking will be applied and mdm matching will continue as before.",
ex);
return false;
}
// fhir path should return exact values
if (results.size() != 1) {
// no results means no blocking
// too many matches means no blocking
ourLog.trace("Too many values at field {}", path);
return false;
}
IBase first = results.get(0);
if (FhirTypeUtil.isPrimitiveType(first.fhirType())) {
IPrimitiveType> primitiveType = (IPrimitiveType>) first;
if (!primitiveType.getValueAsString().equalsIgnoreCase(blockedValue)) {
// doesn't match
// no block
ourLog.trace("Value at path {} does not match - mdm will not block.", path);
return false;
}
} else {
// blocking can only be done by evaluating primitive types
// additional fhirpath values required
ourLog.warn(
"FhirPath {} yields a non-primitive value; blocking is only supported on primitive field types.",
path);
return false;
}
}
// if we got here, all blocking rules evaluated to true
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy