mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-03-02 18:19:27 +00:00
all updated resources to link to existing resources (#5152)
updating mdm resources will update all links to them as well as any links that it would match after update
This commit is contained in:
parent
5eda18f500
commit
fd5c2c2862
@ -0,0 +1,10 @@
|
||||
---
|
||||
issue: 5141
|
||||
type: add
|
||||
title: "Previously, updating an existing resource (resource A) to match a resource that
|
||||
it didn't match to before (resource B) would update only the already
|
||||
existing links on resource A.
|
||||
This behaviour has been changed. Now such an update will also add additional links,
|
||||
if necessary, from resource A to resource B, including adding a POSSIBLE_DUPLICATE
|
||||
between golden resource A and golden resource B.
|
||||
"
|
@ -81,10 +81,10 @@ public class MdmMessageHandler implements MessageHandler {
|
||||
|
||||
ResourceModifiedMessage msg = ((ResourceModifiedJsonMessage) theMessage).getPayload();
|
||||
try {
|
||||
|
||||
IBaseResource sourceResource = msg.getNewPayload(myFhirContext);
|
||||
|
||||
if (myMdmResourceFilteringSvc.shouldBeProcessed((IAnyResource) sourceResource)) {
|
||||
boolean toProcess = myMdmResourceFilteringSvc.shouldBeProcessed((IAnyResource) sourceResource);
|
||||
if (toProcess) {
|
||||
matchMdmAndUpdateLinks(sourceResource, msg);
|
||||
}
|
||||
} catch (TooManyCandidatesException e) {
|
||||
|
@ -0,0 +1,30 @@
|
||||
package ca.uhn.fhir.jpa.mdm.models;
|
||||
|
||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
|
||||
public class FindGoldenResourceCandidatesParams {
|
||||
|
||||
/**
|
||||
* The resource to find matches for
|
||||
*/
|
||||
private final IAnyResource myResource;
|
||||
|
||||
/**
|
||||
* The mdm context
|
||||
*/
|
||||
private final MdmTransactionContext myContext;
|
||||
|
||||
public FindGoldenResourceCandidatesParams(IAnyResource theResource, MdmTransactionContext theContext) {
|
||||
myResource = theResource;
|
||||
myContext = theContext;
|
||||
}
|
||||
|
||||
public IAnyResource getResource() {
|
||||
return myResource;
|
||||
}
|
||||
|
||||
public MdmTransactionContext getContext() {
|
||||
return myContext;
|
||||
}
|
||||
}
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.mdm.svc;
|
||||
|
||||
import ca.uhn.fhir.jpa.mdm.models.FindGoldenResourceCandidatesParams;
|
||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateList;
|
||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateStrategyEnum;
|
||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.MatchedGoldenResourceCandidate;
|
||||
@ -93,7 +94,7 @@ public class MdmMatchLinkSvc {
|
||||
// we require a candidatestrategy, but it doesn't matter
|
||||
// because empty lists are effectively no matches
|
||||
// (and so the candidate strategy doesn't matter)
|
||||
CandidateList candidateList = new CandidateList(CandidateStrategyEnum.LINK);
|
||||
CandidateList candidateList = new CandidateList(CandidateStrategyEnum.ANY);
|
||||
|
||||
/*
|
||||
* If a resource is blocked, we will not conduct
|
||||
@ -103,7 +104,9 @@ public class MdmMatchLinkSvc {
|
||||
boolean isResourceBlocked = myBlockRuleEvaluationSvc.isMdmMatchingBlocked(theResource);
|
||||
|
||||
if (!isResourceBlocked) {
|
||||
candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(theResource);
|
||||
FindGoldenResourceCandidatesParams params =
|
||||
new FindGoldenResourceCandidatesParams(theResource, theMdmTransactionContext);
|
||||
candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(params);
|
||||
}
|
||||
|
||||
if (isResourceBlocked || candidateList.isEmpty()) {
|
||||
|
@ -35,7 +35,7 @@ public abstract class BaseCandidateFinder {
|
||||
|
||||
CandidateList findCandidates(IAnyResource theTarget) {
|
||||
CandidateList candidateList = new CandidateList(getStrategy());
|
||||
candidateList.addAll(findMatchGoldenResourceCandidates(theTarget));
|
||||
candidateList.addAll(getStrategy(), findMatchGoldenResourceCandidates(theTarget));
|
||||
return candidateList;
|
||||
}
|
||||
|
||||
|
@ -19,17 +19,29 @@
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.mdm.svc.candidate;
|
||||
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class CandidateList {
|
||||
private final CandidateStrategyEnum myStrategy;
|
||||
private final List<MatchedGoldenResourceCandidate> myList = new ArrayList<>();
|
||||
|
||||
// no multimap - ordering matters
|
||||
private final Map<CandidateStrategyEnum, List<MatchedGoldenResourceCandidate>> myStrategyToCandidateList =
|
||||
new HashMap<>();
|
||||
|
||||
public CandidateList(CandidateStrategyEnum theStrategy) {
|
||||
myStrategy = theStrategy;
|
||||
myStrategyToCandidateList.put(CandidateStrategyEnum.EID, new ArrayList<>());
|
||||
myStrategyToCandidateList.put(CandidateStrategyEnum.LINK, new ArrayList<>());
|
||||
myStrategyToCandidateList.put(CandidateStrategyEnum.SCORE, new ArrayList<>());
|
||||
}
|
||||
|
||||
public CandidateStrategyEnum getStrategy() {
|
||||
@ -37,32 +49,75 @@ public class CandidateList {
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return myList.isEmpty();
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
public void addAll(List<MatchedGoldenResourceCandidate> theList) {
|
||||
myList.addAll(theList);
|
||||
public void addAll(CandidateStrategyEnum theStrategy, List<MatchedGoldenResourceCandidate> theList) {
|
||||
switch (theStrategy) {
|
||||
case EID:
|
||||
case LINK:
|
||||
case SCORE:
|
||||
myStrategyToCandidateList.get(theStrategy).addAll(theList);
|
||||
break;
|
||||
default:
|
||||
throw new InternalErrorException(
|
||||
Msg.code(2424) + " Existing resources cannot be added for strategy " + theStrategy.name());
|
||||
}
|
||||
}
|
||||
|
||||
public MatchedGoldenResourceCandidate getOnlyMatch() {
|
||||
assert myList.size() == 1;
|
||||
return myList.get(0);
|
||||
assert size() == 1;
|
||||
return getCandidates().get(0);
|
||||
}
|
||||
|
||||
public boolean exactlyOneMatch() {
|
||||
return myList.size() == 1;
|
||||
return size() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream of all types.
|
||||
* If multiple streams are present,
|
||||
* they will be ordered by strategy type
|
||||
*/
|
||||
public Stream<MatchedGoldenResourceCandidate> stream() {
|
||||
return myList.stream();
|
||||
return Stream.concat(
|
||||
myStrategyToCandidateList.get(CandidateStrategyEnum.EID).stream(),
|
||||
Stream.concat(
|
||||
myStrategyToCandidateList.get(CandidateStrategyEnum.LINK).stream(),
|
||||
myStrategyToCandidateList.get(CandidateStrategyEnum.SCORE).stream()));
|
||||
}
|
||||
|
||||
public Stream<MatchedGoldenResourceCandidate> stream(CandidateStrategyEnum theStrategy) {
|
||||
return myStrategyToCandidateList.get(theStrategy).stream();
|
||||
}
|
||||
|
||||
public List<MatchedGoldenResourceCandidate> getCandidates() {
|
||||
return Collections.unmodifiableList(myList);
|
||||
switch (myStrategy) {
|
||||
case LINK:
|
||||
case EID:
|
||||
case SCORE:
|
||||
return new ArrayList<>(myStrategyToCandidateList.get(myStrategy));
|
||||
default:
|
||||
return Stream.of(
|
||||
myStrategyToCandidateList.get(CandidateStrategyEnum.EID),
|
||||
myStrategyToCandidateList.get(CandidateStrategyEnum.LINK),
|
||||
myStrategyToCandidateList.get(CandidateStrategyEnum.SCORE))
|
||||
.flatMap(Collection::stream)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
||||
public MatchedGoldenResourceCandidate getFirstMatch() {
|
||||
return myList.get(0);
|
||||
assert size() > 0;
|
||||
|
||||
switch (myStrategy) {
|
||||
case EID:
|
||||
case LINK:
|
||||
case SCORE:
|
||||
return myStrategyToCandidateList.get(myStrategy).get(0);
|
||||
default:
|
||||
return getCandidates().get(0);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEidMatch() {
|
||||
@ -70,6 +125,19 @@ public class CandidateList {
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return myList.size();
|
||||
switch (myStrategy) {
|
||||
case EID:
|
||||
case LINK:
|
||||
case SCORE:
|
||||
return myStrategyToCandidateList.get(myStrategy).size();
|
||||
default:
|
||||
return myStrategyToCandidateList.get(CandidateStrategyEnum.EID).size()
|
||||
+ myStrategyToCandidateList
|
||||
.get(CandidateStrategyEnum.LINK)
|
||||
.size()
|
||||
+ myStrategyToCandidateList
|
||||
.get(CandidateStrategyEnum.SCORE)
|
||||
.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,11 @@ public enum CandidateStrategyEnum {
|
||||
/** Find Golden Resource candidates based on a link already existing for the source resource */
|
||||
LINK,
|
||||
/** Find Golden Resource candidates based on other sources that match the incoming source using the MDM Matching rules */
|
||||
SCORE;
|
||||
SCORE,
|
||||
/**
|
||||
* Find golden resource candidates that are EID, LINK, or SCORE.
|
||||
*/
|
||||
ANY;
|
||||
|
||||
public boolean isEidMatch() {
|
||||
return this == EID;
|
||||
|
@ -19,8 +19,10 @@
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.mdm.svc.candidate;
|
||||
|
||||
import ca.uhn.fhir.jpa.mdm.models.FindGoldenResourceCandidatesParams;
|
||||
import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc;
|
||||
import ca.uhn.fhir.mdm.log.Logs;
|
||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
@ -56,23 +58,47 @@ public class MdmGoldenResourceFindingSvc {
|
||||
* 4. If none are found, attempt to find Golden Resources that are linked to sources that are similar to our incoming resource based on the MDM rules and
|
||||
* field matchers.
|
||||
*
|
||||
* @param theResource the {@link IBaseResource} we are attempting to find matching candidate Golden Resources for.
|
||||
* @param theParams Params hold the {@link IBaseResource} for which we are attempting to find matching candidate Golden Resources,
|
||||
* as well as the mdm context.
|
||||
* @return A list of {@link MatchedGoldenResourceCandidate} indicating all potential Golden Resource matches.
|
||||
*/
|
||||
public CandidateList findGoldenResourceCandidates(IAnyResource theResource) {
|
||||
CandidateList matchedGoldenResourceCandidates = myFindCandidateByEidSvc.findCandidates(theResource);
|
||||
public CandidateList findGoldenResourceCandidates(FindGoldenResourceCandidatesParams theParams) {
|
||||
IAnyResource resource = theParams.getResource();
|
||||
|
||||
if (matchedGoldenResourceCandidates.isEmpty()) {
|
||||
matchedGoldenResourceCandidates = myFindCandidateByLinkSvc.findCandidates(theResource);
|
||||
CandidateList eidGoldenResources = myFindCandidateByEidSvc.findCandidates(resource);
|
||||
|
||||
// if we have matches from eid, we'll return only these
|
||||
if (!eidGoldenResources.isEmpty()) {
|
||||
return eidGoldenResources;
|
||||
}
|
||||
|
||||
if (matchedGoldenResourceCandidates.isEmpty()) {
|
||||
// OK, so we have not found any links in the MdmLink table with us as a source. Next, let's find
|
||||
// possible Golden Resources matches by following MDM rules.
|
||||
matchedGoldenResourceCandidates = myFindCandidateByExampleSvc.findCandidates(theResource);
|
||||
boolean isUpdate =
|
||||
theParams.getContext().getRestOperation() == MdmTransactionContext.OperationType.UPDATE_RESOURCE;
|
||||
|
||||
// find MdmLinks that have theResource as the source
|
||||
// (these are current golden resources matching this resource)
|
||||
CandidateList linkGoldenResources = myFindCandidateByLinkSvc.findCandidates(resource);
|
||||
|
||||
if (!linkGoldenResources.isEmpty() && !isUpdate) {
|
||||
return linkGoldenResources;
|
||||
}
|
||||
|
||||
return matchedGoldenResourceCandidates;
|
||||
// if we're updating, we might have existing resources that could *also* match
|
||||
// find other golden resources that could be matching to this resource
|
||||
// (we only need to do this for updates because otherwise they would already have matching resources
|
||||
CandidateList anyGoldenResources = myFindCandidateByExampleSvc.findCandidates(resource);
|
||||
|
||||
if (linkGoldenResources.isEmpty()) {
|
||||
// only other resources are available - we'll return this
|
||||
return anyGoldenResources;
|
||||
}
|
||||
|
||||
// else, we will combine the lists
|
||||
CandidateList matches = new CandidateList(CandidateStrategyEnum.ANY);
|
||||
matches.addAll(CandidateStrategyEnum.LINK, linkGoldenResources.getCandidates());
|
||||
matches.addAll(CandidateStrategyEnum.SCORE, anyGoldenResources.getCandidates());
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
public IAnyResource getGoldenResourceFromMatchedGoldenResourceCandidate(
|
||||
|
@ -208,7 +208,7 @@ public class MdmLinkHelper {
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue(foundLink, String.format("State: %s - not found", stateExpression));
|
||||
assertTrue(foundLink, String.format("State: %s - not found", stateExpression.getLinkExpression()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||
import ca.uhn.fhir.jpa.interceptor.UserRequestRetryVersionConflictsInterceptor;
|
||||
import ca.uhn.fhir.jpa.mdm.provider.BaseLinkR4Test;
|
||||
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||
import ca.uhn.fhir.jpa.test.Batch2JobHelper;
|
||||
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
|
||||
import ca.uhn.fhir.mdm.api.MdmLinkJson;
|
||||
@ -18,6 +17,7 @@ import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
|
||||
import ca.uhn.fhir.mdm.batch2.clear.MdmClearStep;
|
||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||
import ca.uhn.fhir.mdm.rules.config.MdmSettings;
|
||||
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
@ -3,7 +3,10 @@ package ca.uhn.fhir.jpa.mdm.svc;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
||||
import ca.uhn.fhir.jpa.mdm.config.BaseTestMdmConfig;
|
||||
import ca.uhn.fhir.jpa.mdm.config.BlockListConfig;
|
||||
import ca.uhn.fhir.jpa.mdm.helper.MdmLinkHelper;
|
||||
import ca.uhn.fhir.jpa.mdm.helper.testmodels.MDMState;
|
||||
import ca.uhn.fhir.jpa.model.dao.JpaPid;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.mdm.api.IMdmLink;
|
||||
@ -31,14 +34,18 @@ import org.hl7.fhir.r4.model.Identifier;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Practitioner;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.RepeatedTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
@ -61,8 +68,17 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.slf4j.LoggerFactory.getLogger;
|
||||
|
||||
/**
|
||||
* These tests use the rules defined in mdm-rules.json
|
||||
* See {@link BaseTestMdmConfig}
|
||||
*/
|
||||
public class MdmMatchLinkSvcTest {
|
||||
|
||||
private static final Logger ourLog = getLogger(MdmMatchLinkSvcTest.class);
|
||||
|
||||
|
||||
@Nested
|
||||
public class NoBlockLinkTest extends BaseMdmR4Test {
|
||||
@Autowired
|
||||
@ -74,6 +90,9 @@ public class MdmMatchLinkSvcTest {
|
||||
@Autowired
|
||||
private IMdmLinkUpdaterSvc myMdmLinkUpdaterSvc;
|
||||
|
||||
@Autowired
|
||||
private MdmLinkHelper myLinkHelper;
|
||||
|
||||
@Test
|
||||
public void testAddPatientLinksToNewGoldenResourceIfNoneFound() {
|
||||
createPatientAndUpdateLinks(buildJanePatient());
|
||||
@ -98,6 +117,64 @@ public class MdmMatchLinkSvcTest {
|
||||
assertLinksMatchVector((Long) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RepeatedTest(20)
|
||||
public void testUpdatingAResourceToMatchACurrentlyUnmatchedResource_resultsInUpdatedLinksForBoth() {
|
||||
// setup
|
||||
MDMState<Patient, JpaPid> state = new MDMState<>();
|
||||
String startingState = """
|
||||
GP1, AUTO, MATCH, P1
|
||||
GP2, AUTO, MATCH, P2
|
||||
""";
|
||||
|
||||
Map<String, Patient> idToResource = new HashMap<>();
|
||||
|
||||
// we're creating our patients manually,
|
||||
// because we're testing mdm rules and how candidates are found
|
||||
// so the patient info matters
|
||||
Patient jane = buildJanePatient();
|
||||
Long id;
|
||||
{
|
||||
Patient p = createPatient(jane);
|
||||
idToResource.put("P1", p);
|
||||
Long patientId = p.getIdElement().getIdPartAsLong();
|
||||
state.addPID(patientId.toString(), JpaPid.fromIdAndResourceType(patientId, "Patient"));
|
||||
}
|
||||
{
|
||||
Patient yui = buildJanePatient();
|
||||
yui.setName(new ArrayList<>());
|
||||
yui.addName()
|
||||
.addGiven("Yui")
|
||||
.setFamily("Hirasawa");
|
||||
Patient retVal = createPatient(yui);
|
||||
id = retVal.getIdElement().getIdPartAsLong();
|
||||
idToResource.put("P2", retVal);
|
||||
state.addPID(String.valueOf(id), JpaPid.fromIdAndResourceType(id, "Patient"));
|
||||
}
|
||||
|
||||
// initialize our links
|
||||
state.setInputState(startingState);
|
||||
state.setParameterToValue(idToResource);
|
||||
myLinkHelper.setup(state);
|
||||
|
||||
// test
|
||||
Patient toUpdate = buildJanePatient();
|
||||
toUpdate.setId("Patient/" + id.longValue());
|
||||
updatePatientAndUpdateLinks(toUpdate);
|
||||
|
||||
// verify
|
||||
String endState = """
|
||||
GP1, AUTO, MATCH, P1
|
||||
GP2, AUTO, POSSIBLE_MATCH, P2
|
||||
GP1, AUTO, POSSIBLE_MATCH, P2
|
||||
GP2, AUTO, POSSIBLE_DUPLICATE, GP1
|
||||
""";
|
||||
state.setParameterToValue(idToResource);
|
||||
state.setOutputState(endState);
|
||||
myLinkHelper.validateResults(state);
|
||||
myLinkHelper.logMdmLinks();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddPatientLinksToNewlyCreatedResourceIfNoMatch() {
|
||||
Patient patient1 = createPatientAndUpdateLinks(buildJanePatient());
|
||||
|
@ -4,8 +4,8 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -0,0 +1,121 @@
|
||||
package ca.uhn.fhir.jpa.mdm.svc.candidate;
|
||||
|
||||
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
||||
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.EnumSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class CandidateListTest {
|
||||
|
||||
private List<MatchedGoldenResourceCandidate> getCandidatesList(int theSize) {
|
||||
List<MatchedGoldenResourceCandidate> candidatesToAdd = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < theSize; i++) {
|
||||
MatchedGoldenResourceCandidate candidate = new MatchedGoldenResourceCandidate(
|
||||
mock(IResourcePersistentId.class),
|
||||
MdmMatchOutcome.POSSIBLE_MATCH
|
||||
);
|
||||
candidatesToAdd.add(candidate);
|
||||
}
|
||||
|
||||
return candidatesToAdd;
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(CandidateStrategyEnum.class)
|
||||
public void addAll_withVariousStrategies_behaviourTest(CandidateStrategyEnum theStrategyEnum) {
|
||||
// setup
|
||||
int total = 3;
|
||||
List<MatchedGoldenResourceCandidate> candidatesToAdd = getCandidatesList(total);
|
||||
|
||||
// test
|
||||
CandidateList list = new CandidateList(theStrategyEnum);
|
||||
|
||||
// verify
|
||||
if (theStrategyEnum == CandidateStrategyEnum.ANY) {
|
||||
assertThrows(InternalErrorException.class, () -> {
|
||||
list.addAll(theStrategyEnum, candidatesToAdd);
|
||||
});
|
||||
} else {
|
||||
list.addAll(theStrategyEnum, candidatesToAdd);
|
||||
assertEquals(total, list.size());
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(CandidateStrategyEnum.class)
|
||||
public void stream_forVariousStrategies_returnsJointStream(CandidateStrategyEnum theStrategy) {
|
||||
// setup
|
||||
int size = 3;
|
||||
CandidateList candidateList = new CandidateList(theStrategy);
|
||||
|
||||
// we need some values first
|
||||
size = populateCandidateList(theStrategy, size, candidateList);
|
||||
|
||||
// test
|
||||
assertEquals(size, candidateList.stream().count());
|
||||
}
|
||||
|
||||
private int populateCandidateList(CandidateStrategyEnum theStrategy, int theSize, CandidateList theCandidateList) {
|
||||
if (theStrategy == CandidateStrategyEnum.ANY) {
|
||||
int realTotal = 0;
|
||||
for (CandidateStrategyEnum strat : CandidateStrategyEnum.values()) {
|
||||
if (strat == theStrategy) {
|
||||
continue;
|
||||
}
|
||||
|
||||
theCandidateList.addAll(strat, getCandidatesList(theSize));
|
||||
realTotal += theSize;
|
||||
}
|
||||
theSize = realTotal;
|
||||
} else {
|
||||
theCandidateList.addAll(theStrategy, getCandidatesList(theSize));
|
||||
}
|
||||
return theSize;
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(CandidateStrategyEnum.class)
|
||||
public void singleElement_CandidateList_Tests(CandidateStrategyEnum theStrategy) {
|
||||
// setup
|
||||
CandidateList candidate = new CandidateList(theStrategy);
|
||||
|
||||
if (theStrategy == CandidateStrategyEnum.ANY) {
|
||||
candidate.addAll(CandidateStrategyEnum.LINK, getCandidatesList(1));
|
||||
} else {
|
||||
candidate.addAll(theStrategy, getCandidatesList(1));
|
||||
}
|
||||
|
||||
// tests
|
||||
assertFalse(candidate.isEmpty());
|
||||
assertTrue(candidate.exactlyOneMatch());
|
||||
assertEquals(1, candidate.size());
|
||||
assertNotNull(candidate.getFirstMatch());
|
||||
assertNotNull(candidate.getOnlyMatch());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(CandidateStrategyEnum.class)
|
||||
public void getCandidates_variousStrategies_returnsExpectedResults(CandidateStrategyEnum theStrategy) {
|
||||
// setup
|
||||
CandidateList candidateList = new CandidateList(theStrategy);
|
||||
|
||||
int size = populateCandidateList(theStrategy, 10, candidateList);
|
||||
|
||||
// tests
|
||||
assertEquals(size, candidateList.size());
|
||||
List<MatchedGoldenResourceCandidate> candidates = candidateList.getCandidates();
|
||||
assertEquals(size, candidates.size());
|
||||
}
|
||||
}
|
@ -4,9 +4,12 @@ import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
||||
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
||||
import ca.uhn.fhir.jpa.mdm.models.FindGoldenResourceCandidatesParams;
|
||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@ -49,7 +52,8 @@ class MdmGoldenResourceFindingSvcIT extends BaseMdmR4Test {
|
||||
myMdmLinkDaoSvc.save(link);
|
||||
|
||||
// the NO_MATCH golden resource should not be a candidate
|
||||
CandidateList candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(jane);
|
||||
CandidateList candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(
|
||||
createFindGoldenResourceCandidateParams(jane));
|
||||
assertEquals(0, candidateList.size());
|
||||
}
|
||||
|
||||
@ -131,7 +135,8 @@ class MdmGoldenResourceFindingSvcIT extends BaseMdmR4Test {
|
||||
}
|
||||
|
||||
// test
|
||||
CandidateList candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(candidate);
|
||||
CandidateList candidateList = myMdmGoldenResourceFindingSvc.findGoldenResourceCandidates(
|
||||
createFindGoldenResourceCandidateParams(candidate));
|
||||
|
||||
// verify
|
||||
assertNotNull(candidateList);
|
||||
@ -154,4 +159,12 @@ class MdmGoldenResourceFindingSvcIT extends BaseMdmR4Test {
|
||||
|
||||
return (Patient) daoOutcome.getResource();
|
||||
}
|
||||
|
||||
private FindGoldenResourceCandidatesParams createFindGoldenResourceCandidateParams(IAnyResource theResource) {
|
||||
FindGoldenResourceCandidatesParams params = new FindGoldenResourceCandidatesParams(
|
||||
theResource,
|
||||
new MdmTransactionContext()
|
||||
);
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.test;
|
||||
|
||||
import ca.uhn.fhir.batch2.jobs.export.BulkDataExportProvider;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||
@ -37,7 +38,6 @@ import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
||||
import ca.uhn.fhir.jpa.binary.interceptor.BinaryStorageInterceptor;
|
||||
import ca.uhn.fhir.jpa.binary.provider.BinaryAccessProvider;
|
||||
import ca.uhn.fhir.jpa.bulk.export.api.IBulkDataExportJobSchedulingHelper;
|
||||
import ca.uhn.fhir.batch2.jobs.export.BulkDataExportProvider;
|
||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IMdmLinkJpaRepository;
|
||||
|
@ -260,7 +260,7 @@ public abstract class BaseJpaTest extends BaseTest {
|
||||
private IForcedIdDao myForcedIdDao;
|
||||
@Autowired
|
||||
private DaoRegistry myDaoRegistry;
|
||||
private List<Object> myRegisteredInterceptors = new ArrayList<>(1);
|
||||
private final List<Object> myRegisteredInterceptors = new ArrayList<>(1);
|
||||
|
||||
@SuppressWarnings("BusyWait")
|
||||
public static void waitForSize(int theTarget, List<?> theList) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user