Resolve 5262 adding new parameter strictmatch to the mdm history link operation 1 (#5282)

* implemented the strictMatch parameter, added corresponding test

* implemented the strictMatch parameter, added corresponding test

* updated mdm link history documentation
created changelog

* reverted previous changes. Made the default behaviour of history link as AND instead of OR

* optimized imports

* modified changelog

* modified tests
This commit is contained in:
TynerGjs 2023-09-19 12:15:47 -04:00 committed by GitHub
parent 7eb3150644
commit 8d4f07c56e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 65 additions and 41 deletions

View File

@ -0,0 +1,5 @@
---
type: change
issue: 5262
title: "Previously, when both resourceId and goldenResourceId are provided to the mdm link history operation, they will
be treated as an OR. This is now changed to AND in order to comply with REST conventions."

View File

@ -380,15 +380,8 @@ public class MdmLinkDaoJpaImpl implements IMdmLinkDao<JpaPid, MdmLink> {
if (!theMdmHistorySearchParameters.getGoldenResourceIds().isEmpty() if (!theMdmHistorySearchParameters.getGoldenResourceIds().isEmpty()
&& !theMdmHistorySearchParameters.getSourceIds().isEmpty()) { && !theMdmHistorySearchParameters.getSourceIds().isEmpty()) {
if (theMdmHistorySearchParameters.getParameterJoinType() == MdmHistorySearchParameters.JoinType.AND) {
// 'and' the source and golden ids
goldenResourceAndOrResourceIdCriterion = goldenResourceAndOrResourceIdCriterion =
AuditEntity.and(goldenResourceIdCriterion, resourceIdCriterion); AuditEntity.and(goldenResourceIdCriterion, resourceIdCriterion);
} else {
// default is 'or'
goldenResourceAndOrResourceIdCriterion =
AuditEntity.or(goldenResourceIdCriterion, resourceIdCriterion);
}
} else if (!theMdmHistorySearchParameters.getGoldenResourceIds().isEmpty()) { } else if (!theMdmHistorySearchParameters.getGoldenResourceIds().isEmpty()) {
goldenResourceAndOrResourceIdCriterion = goldenResourceIdCriterion; goldenResourceAndOrResourceIdCriterion = goldenResourceIdCriterion;
} else if (!theMdmHistorySearchParameters.getSourceIds().isEmpty()) { } else if (!theMdmHistorySearchParameters.getSourceIds().isEmpty()) {

View File

@ -13,6 +13,7 @@ import ca.uhn.fhir.mdm.api.MdmLinkWithRevision;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.mdm.model.MdmPidTuple; import ca.uhn.fhir.mdm.model.MdmPidTuple;
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson; import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
import ca.uhn.fhir.model.primitive.BooleanDt;
import org.hibernate.envers.RevisionType; import org.hibernate.envers.RevisionType;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.Enumerations;
@ -26,7 +27,10 @@ import javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@ -114,12 +118,19 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test {
final List<MdmLink> mdmLinksWithLinkedPatients3 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 2); final List<MdmLink> mdmLinksWithLinkedPatients3 = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 2);
flipLinksTo(mdmLinksWithLinkedPatients3, MdmMatchResultEnum.NO_MATCH); flipLinksTo(mdmLinksWithLinkedPatients3, MdmMatchResultEnum.NO_MATCH);
final MdmHistorySearchParameters mdmHistorySearchParameters = final MdmHistorySearchParameters mdmHistorySearchParametersResourceIds =
new MdmHistorySearchParameters()
.setGoldenResourceIds(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients3.get(0)));
final List<MdmLinkWithRevision<MdmLink>> actualMdmLinkRevisionsResourceIds = myMdmLinkDaoSvc.findMdmLinkHistory(mdmHistorySearchParametersResourceIds);
final MdmHistorySearchParameters mdmHistorySearchParametersGoldenResourceIds =
new MdmHistorySearchParameters() new MdmHistorySearchParameters()
.setGoldenResourceIds(getIdsFromMdmLinks(MdmLink::getGoldenResourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients3.get(0)))
.setSourceIds(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0))); .setSourceIds(getIdsFromMdmLinks(MdmLink::getSourcePersistenceId, mdmLinksWithLinkedPatients1.get(0), mdmLinksWithLinkedPatients2.get(0)));
final List<MdmLinkWithRevision<MdmLink>> actualMdmLinkRevisions = myMdmLinkDaoSvc.findMdmLinkHistory(mdmHistorySearchParameters); final List<MdmLinkWithRevision<MdmLink>> actualMdmLinkRevisionsGoldenResourceIds = myMdmLinkDaoSvc.findMdmLinkHistory(mdmHistorySearchParametersGoldenResourceIds);
final List<MdmLinkWithRevision<MdmLink>> actualMdmLinkRevisionsJoined = joinAndHandleRepetitiveLinks(actualMdmLinkRevisionsResourceIds, actualMdmLinkRevisionsGoldenResourceIds);
final JpaPid goldenResourceId1 = mdmLinksWithLinkedPatients1.get(0).getGoldenResourcePersistenceId(); final JpaPid goldenResourceId1 = mdmLinksWithLinkedPatients1.get(0).getGoldenResourcePersistenceId();
final JpaPid goldenResourceId2 = mdmLinksWithLinkedPatients2.get(0).getGoldenResourcePersistenceId(); final JpaPid goldenResourceId2 = mdmLinksWithLinkedPatients2.get(0).getGoldenResourcePersistenceId();
@ -138,14 +149,14 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test {
buildMdmLinkWithRevision(1, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_1), buildMdmLinkWithRevision(1, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_1),
buildMdmLinkWithRevision(2, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_2), buildMdmLinkWithRevision(2, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_2),
buildMdmLinkWithRevision(3, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_3), buildMdmLinkWithRevision(3, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId1, sourceId1_3),
buildMdmLinkWithRevision(4, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_1),
buildMdmLinkWithRevision(10, RevisionType.MOD, MdmMatchResultEnum.NO_MATCH, goldenResourceId3, sourceId3_1), buildMdmLinkWithRevision(10, RevisionType.MOD, MdmMatchResultEnum.NO_MATCH, goldenResourceId3, sourceId3_1),
buildMdmLinkWithRevision(8, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId3, sourceId3_1), buildMdmLinkWithRevision(8, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId3, sourceId3_1),
buildMdmLinkWithRevision(11, RevisionType.MOD, MdmMatchResultEnum.NO_MATCH, goldenResourceId3, sourceId3_2), buildMdmLinkWithRevision(11, RevisionType.MOD, MdmMatchResultEnum.NO_MATCH, goldenResourceId3, sourceId3_2),
buildMdmLinkWithRevision(9, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId3, sourceId3_2) buildMdmLinkWithRevision(9, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId3, sourceId3_2),
buildMdmLinkWithRevision(4, RevisionType.ADD, MdmMatchResultEnum.MATCH, goldenResourceId2, sourceId2_1)
); );
assertMdmRevisionsEqual(expectedMdLinkRevisions, actualMdmLinkRevisions); assertMdmRevisionsEqual(expectedMdLinkRevisions, actualMdmLinkRevisionsJoined);
} }
@Test @Test
@ -212,6 +223,29 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test {
assertMdmRevisionsEqual(expectedMdLinkRevisions, actualMdmLinkRevisions); assertMdmRevisionsEqual(expectedMdLinkRevisions, actualMdmLinkRevisions);
} }
@Test
public void testHistoryForBothSourceAndGoldenResourceIds(){
// setup
MdmLink targetMdmLink = createMdmLinksWithLinkedPatients(MdmMatchResultEnum.MATCH, 2).get(0);
// link both patient to the GR
String goldenPatientId = targetMdmLink.getGoldenResourcePersistenceId().getId().toString();
String sourcePatientId = targetMdmLink.getSourcePersistenceId().getId().toString();
// execute
MdmHistorySearchParameters mdmHistorySearchParameters = new MdmHistorySearchParameters()
.setSourceIds(List.of(sourcePatientId))
.setGoldenResourceIds(List.of(goldenPatientId));
List<MdmLinkWithRevision<MdmLink>> actualMdmLinkRevisions = myMdmLinkDaoSvc.findMdmLinkHistory(mdmHistorySearchParameters);
// verify
assertEquals(1, actualMdmLinkRevisions.size());
MdmLink actualMdmLink = actualMdmLinkRevisions.get(0).getMdmLink();
assertEquals(goldenPatientId, actualMdmLink.getGoldenResourcePersistenceId().getId().toString());
assertEquals(sourcePatientId, actualMdmLink.getSourcePersistenceId().getId().toString());
}
@Test @Test
public void testHistoryForNoIdsOnly() { public void testHistoryForNoIdsOnly() {
assertThrows(IllegalArgumentException.class, () -> myMdmLinkDaoSvc.findMdmLinkHistory(new MdmHistorySearchParameters())); assertThrows(IllegalArgumentException.class, () -> myMdmLinkDaoSvc.findMdmLinkHistory(new MdmHistorySearchParameters()));
@ -313,4 +347,17 @@ public class MdmLinkDaoSvcTest extends BaseMdmR4Test {
mdmLink.setSourcePersistenceId(runInTransaction(() -> myIdHelperService.getPidOrNull(RequestPartitionId.allPartitions(), theTargetResource))); mdmLink.setSourcePersistenceId(runInTransaction(() -> myIdHelperService.getPidOrNull(RequestPartitionId.allPartitions(), theTargetResource)));
return myMdmLinkDao.save(mdmLink); return myMdmLinkDao.save(mdmLink);
} }
private List<MdmLinkWithRevision<MdmLink>> joinAndHandleRepetitiveLinks(List<MdmLinkWithRevision<MdmLink>> toLinks, List<MdmLinkWithRevision<MdmLink>> fromLinks){
List<MdmLinkWithRevision<MdmLink>> joinedLinks = new ArrayList<>(toLinks);
Set<String> joinedLinkIds = new HashSet<>();
toLinks.forEach(link -> joinedLinkIds.add(link.getMdmLink().getId().toString()));
for (MdmLinkWithRevision<MdmLink> link : fromLinks){
if (!joinedLinkIds.contains(link.getMdmLink().getId().toString())){
joinedLinks.add(link);
}
}
return joinedLinks;
}
} }

View File

@ -33,24 +33,9 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class MdmHistorySearchParameters { public class MdmHistorySearchParameters {
public enum JoinType {
AND,
OR
}
private List<IIdType> myGoldenResourceIds = new ArrayList<>(); private List<IIdType> myGoldenResourceIds = new ArrayList<>();
private List<IIdType> mySourceIds = new ArrayList<>(); private List<IIdType> mySourceIds = new ArrayList<>();
/**
* When both myGoldenResourceIds and mySourceIds are provided,
* this parameter determines whether to 'and' or 'or' the query
* that fetches links by these values.
*
* Default (legacy) functionality is 'or'.
*/
private JoinType myParameterJoinType = JoinType.OR;
public MdmHistorySearchParameters() {} public MdmHistorySearchParameters() {}
public List<IIdType> getGoldenResourceIds() { public List<IIdType> getGoldenResourceIds() {
@ -61,10 +46,6 @@ public class MdmHistorySearchParameters {
return mySourceIds; return mySourceIds;
} }
public JoinType getParameterJoinType() {
return myParameterJoinType;
}
public MdmHistorySearchParameters setGoldenResourceIds(List<String> theGoldenResourceIds) { public MdmHistorySearchParameters setGoldenResourceIds(List<String> theGoldenResourceIds) {
myGoldenResourceIds = extractId(theGoldenResourceIds); myGoldenResourceIds = extractId(theGoldenResourceIds);
return this; return this;
@ -75,11 +56,6 @@ public class MdmHistorySearchParameters {
return this; return this;
} }
public MdmHistorySearchParameters setJoinType(JoinType theJoinType) {
myParameterJoinType = theJoinType;
return this;
}
@Override @Override
public boolean equals(Object theO) { public boolean equals(Object theO) {
if (this == theO) return true; if (this == theO) return true;

View File

@ -28,6 +28,7 @@ import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters; import ca.uhn.fhir.mdm.api.params.MdmHistorySearchParameters;
import ca.uhn.fhir.mdm.model.mdmevents.MdmHistoryEvent; import ca.uhn.fhir.mdm.model.mdmevents.MdmHistoryEvent;
import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson; import ca.uhn.fhir.mdm.model.mdmevents.MdmLinkWithRevisionJson;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
@ -62,12 +63,14 @@ public class MdmLinkHistoryProviderDstu3Plus extends BaseMdmProvider {
@Operation(name = ProviderConstants.MDM_LINK_HISTORY, idempotent = true) @Operation(name = ProviderConstants.MDM_LINK_HISTORY, idempotent = true)
public IBaseParameters historyLinks( public IBaseParameters historyLinks(
@Description(value = "The id of the Golden Resource (e.g. Golden Patient Resource).")
@OperationParam( @OperationParam(
name = ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, name = ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID,
min = 0, min = 0,
max = OperationParam.MAX_UNLIMITED, max = OperationParam.MAX_UNLIMITED,
typeName = "string") typeName = "string")
List<IPrimitiveType<String>> theMdmGoldenResourceIds, List<IPrimitiveType<String>> theMdmGoldenResourceIds,
@Description(value = "The id of the source resource (e.g. Patient resource).")
@OperationParam( @OperationParam(
name = ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, name = ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID,
min = 0, min = 0,