fixed mdmjsonlink serialization issues (#5987)

* fixed mdmjsonlink serialization issues

* spotless

* test fixing

* spotless

* review fixes

* spotless

* fixing

* spotless

---------

Co-authored-by: leif stawnyczy <leifstawnyczy@leifs-mbp.home>
This commit is contained in:
TipzCM 2024-06-07 11:45:39 -04:00 committed by GitHub
parent 5b75639718
commit 1437c356ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 190 additions and 54 deletions

View File

@ -0,0 +1,8 @@
---
type: fix
issue: 5985
title: "Fixed an issue where MDM json links were unserializable
due to using the IResourcePersistenceId objects.
This has been fixed, and IResourcePersistenceId objects
will not be serialized or returned to users.
"

View File

@ -125,7 +125,7 @@ public class IdHelperService implements IIdHelperService<JpaPid> {
private boolean myDontCheckActiveTransactionForUnitTest; private boolean myDontCheckActiveTransactionForUnitTest;
@VisibleForTesting @VisibleForTesting
void setDontCheckActiveTransactionForUnitTest(boolean theDontCheckActiveTransactionForUnitTest) { protected void setDontCheckActiveTransactionForUnitTest(boolean theDontCheckActiveTransactionForUnitTest) {
myDontCheckActiveTransactionForUnitTest = theDontCheckActiveTransactionForUnitTest; myDontCheckActiveTransactionForUnitTest = theDontCheckActiveTransactionForUnitTest;
} }

View File

@ -20,11 +20,20 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentMatchers; import org.mockito.ArgumentMatchers;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
@ -37,7 +46,7 @@ import static org.mockito.Mockito.when;
public class IdHelperServiceTest { public class IdHelperServiceTest {
@InjectMocks @InjectMocks
private final IdHelperService subject = new IdHelperService(); private final IdHelperService myHelperSvc = new IdHelperService();
@Mock @Mock
protected IResourceTableDao myResourceTableDao; protected IResourceTableDao myResourceTableDao;
@ -45,8 +54,8 @@ public class IdHelperServiceTest {
@Mock @Mock
private JpaStorageSettings myStorageSettings; private JpaStorageSettings myStorageSettings;
@Mock @Spy
private FhirContext myFhirCtx; private FhirContext myFhirCtx = FhirContext.forR4Cached();
@Mock @Mock
private MemoryCacheService myMemoryCacheService; private MemoryCacheService myMemoryCacheService;
@ -59,10 +68,11 @@ public class IdHelperServiceTest {
@BeforeEach @BeforeEach
void setUp() { void setUp() {
subject.setDontCheckActiveTransactionForUnitTest(true); myHelperSvc.setDontCheckActiveTransactionForUnitTest(true);
when(myStorageSettings.isDeleteEnabled()).thenReturn(true); // lenient because some tests require this setup, and others do not
when(myStorageSettings.getResourceClientIdStrategy()).thenReturn(JpaStorageSettings.ClientIdStrategyEnum.ANY); lenient().doReturn(true).when(myStorageSettings).isDeleteEnabled();
lenient().doReturn(JpaStorageSettings.ClientIdStrategyEnum.ANY).when(myStorageSettings).getResourceClientIdStrategy();
} }
@Test @Test
@ -81,7 +91,7 @@ public class IdHelperServiceTest {
// configure mock behaviour // configure mock behaviour
when(myStorageSettings.isDeleteEnabled()).thenReturn(true); when(myStorageSettings.isDeleteEnabled()).thenReturn(true);
final ResourceNotFoundException resourceNotFoundException = assertThrows(ResourceNotFoundException.class, () -> subject.resolveResourcePersistentIds(requestPartitionId, resourceType, ids, theExcludeDeleted)); final ResourceNotFoundException resourceNotFoundException = assertThrows(ResourceNotFoundException.class, () -> myHelperSvc.resolveResourcePersistentIds(requestPartitionId, resourceType, ids, theExcludeDeleted));
assertEquals("HAPI-2001: Resource Patient/123 is not known", resourceNotFoundException.getMessage()); assertEquals("HAPI-2001: Resource Patient/123 is not known", resourceNotFoundException.getMessage());
} }
@ -102,13 +112,14 @@ public class IdHelperServiceTest {
// configure mock behaviour // configure mock behaviour
when(myStorageSettings.isDeleteEnabled()).thenReturn(false); when(myStorageSettings.isDeleteEnabled()).thenReturn(false);
Map<String, JpaPid> actualIds = subject.resolveResourcePersistentIds(requestPartitionId, resourceType, ids, theExcludeDeleted); Map<String, JpaPid> actualIds = myHelperSvc.resolveResourcePersistentIds(requestPartitionId, resourceType, ids, theExcludeDeleted);
//verifyResult //verifyResult
assertFalse(actualIds.isEmpty()); assertFalse(actualIds.isEmpty());
assertNull(actualIds.get(ids.get(0))); assertNull(actualIds.get(ids.get(0)));
} }
private Root<ResourceTable> getMockedFrom() { private Root<ResourceTable> getMockedFrom() {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Path<Object> path = mock(Path.class); Path<Object> path = mock(Path.class);

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.mdm.config;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSettings;
import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService;
@ -57,10 +58,18 @@ public class MdmSurvivorshipConfig {
@Autowired @Autowired
private IIdHelperService<?> myIIdHelperService; private IIdHelperService<?> myIIdHelperService;
@Autowired
private HapiTransactionService myTransactionService;
@Bean @Bean
public IMdmSurvivorshipService mdmSurvivorshipService() { public IMdmSurvivorshipService mdmSurvivorshipService() {
return new MdmSurvivorshipSvcImpl( return new MdmSurvivorshipSvcImpl(
myFhirContext, goldenResourceHelper(), myDaoRegistry, myMdmLinkQuerySvc, myIIdHelperService); myFhirContext,
goldenResourceHelper(),
myDaoRegistry,
myMdmLinkQuerySvc,
myIIdHelperService,
myTransactionService);
} }
@Bean @Bean

View File

@ -44,13 +44,17 @@ public class MdmModelConverterSvcImpl implements IMdmModelConverterSvc {
.toVersionless() .toVersionless()
.getValue(); .getValue();
retVal.setSourceId(sourceId); retVal.setSourceId(sourceId);
if (theLink.getSourcePersistenceId() != null) {
retVal.setSourcePid(theLink.getSourcePersistenceId()); retVal.setSourcePid(theLink.getSourcePersistenceId());
}
String goldenResourceId = myIdHelperService String goldenResourceId = myIdHelperService
.resourceIdFromPidOrThrowException(theLink.getGoldenResourcePersistenceId(), theLink.getMdmSourceType()) .resourceIdFromPidOrThrowException(theLink.getGoldenResourcePersistenceId(), theLink.getMdmSourceType())
.toVersionless() .toVersionless()
.getValue(); .getValue();
retVal.setGoldenResourceId(goldenResourceId); retVal.setGoldenResourceId(goldenResourceId);
if (theLink.getGoldenResourcePersistenceId() != null) {
retVal.setGoldenPid(theLink.getGoldenResourcePersistenceId()); retVal.setGoldenPid(theLink.getGoldenResourcePersistenceId());
}
retVal.setCreated(theLink.getCreated()); retVal.setCreated(theLink.getCreated());
retVal.setEidMatch(theLink.getEidMatch()); retVal.setEidMatch(theLink.getEidMatch());
retVal.setLinkSource(theLink.getLinkSource()); retVal.setLinkSource(theLink.getLinkSource());

View File

@ -50,8 +50,15 @@ public class MdmModelConverterSvcImplTest extends BaseMdmR4Test {
ourLog.info("actualMdmLinkJson: {}", actualMdmLinkJson); ourLog.info("actualMdmLinkJson: {}", actualMdmLinkJson);
MdmLinkJson exepctedMdmLinkJson = getExepctedMdmLinkJson(mdmLink.getGoldenResourcePersistenceId().getId(), mdmLink.getSourcePersistenceId().getId(), MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.MANUAL, version, createTime, updateTime, isLinkCreatedResource, scoreRounded); MdmLinkJson expectedMdmLinkJson = getExepctedMdmLinkJson(mdmLink.getGoldenResourcePersistenceId().getId(), mdmLink.getSourcePersistenceId().getId(), MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.MANUAL, version, createTime, updateTime, isLinkCreatedResource, scoreRounded);
assertEquals(exepctedMdmLinkJson, actualMdmLinkJson); assertEquals(expectedMdmLinkJson.getSourceId(), actualMdmLinkJson.getSourceId());
assertEquals(expectedMdmLinkJson.getGoldenResourceId(), actualMdmLinkJson.getGoldenResourceId());
assertEquals(expectedMdmLinkJson.getGoldenPid().getId(), actualMdmLinkJson.getGoldenPid().getId());
assertEquals(expectedMdmLinkJson.getSourcePid().getId(), actualMdmLinkJson.getSourcePid().getId());
assertEquals(expectedMdmLinkJson.getVector(), actualMdmLinkJson.getVector());
assertEquals(expectedMdmLinkJson.getScore(), actualMdmLinkJson.getScore());
assertEquals(expectedMdmLinkJson.getMatchResult(), actualMdmLinkJson.getMatchResult());
assertEquals(expectedMdmLinkJson.getLinkSource(), actualMdmLinkJson.getLinkSource());
} }
@Test @Test

View File

@ -1,11 +1,12 @@
package ca.uhn.fhir.jpa.mdm.svc; package ca.uhn.fhir.jpa.mdm.svc;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.dao.index.IdHelperService; import ca.uhn.fhir.jpa.dao.index.IdHelperService;
import ca.uhn.fhir.jpa.entity.MdmLink; import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.jpa.dao.tx.IHapiTransactionService;
import ca.uhn.fhir.jpa.model.dao.JpaPid; import ca.uhn.fhir.jpa.model.dao.JpaPid;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
import ca.uhn.fhir.mdm.api.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSettings;
@ -21,6 +22,7 @@ import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
import ca.uhn.fhir.mdm.util.MdmPartitionHelper; import ca.uhn.fhir.mdm.util.MdmPartitionHelper;
import ca.uhn.fhir.mdm.util.MdmResourceUtil; import ca.uhn.fhir.mdm.util.MdmResourceUtil;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@ -31,20 +33,23 @@ import org.mockito.Mock;
import org.mockito.Spy; import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -69,12 +74,15 @@ public class MdmSurvivorshipSvcImplTest {
@Mock @Mock
private MdmPartitionHelper myMdmPartitionHelper; private MdmPartitionHelper myMdmPartitionHelper;
@Spy @Mock
private IIdHelperService<?> myIIdHelperService = new IdHelperService(); private IdHelperService myIIdHelperService;
@Mock @Mock
private IMdmLinkQuerySvc myMdmLinkQuerySvc; private IMdmLinkQuerySvc myMdmLinkQuerySvc;
@Mock
private HapiTransactionService myTransactionService;
private MdmSurvivorshipSvcImpl mySvc; private MdmSurvivorshipSvcImpl mySvc;
@BeforeEach @BeforeEach
@ -91,7 +99,8 @@ public class MdmSurvivorshipSvcImplTest {
myGoldenResourceHelper, myGoldenResourceHelper,
myDaoRegistry, myDaoRegistry,
myMdmLinkQuerySvc, myMdmLinkQuerySvc,
myIIdHelperService myIIdHelperService,
myTransactionService
); );
} }
@ -115,7 +124,7 @@ public class MdmSurvivorshipSvcImplTest {
List<IBaseResource> resources = new ArrayList<>(); List<IBaseResource> resources = new ArrayList<>();
List<MdmLinkJson> links = new ArrayList<>(); List<MdmLinkJson> links = new ArrayList<>();
Map<String, IResourcePersistentId> sourceIdToPid = new HashMap<>();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
// we want our resources to be slightly different // we want our resources to be slightly different
Patient patient = new Patient(); Patient patient = new Patient();
@ -137,24 +146,34 @@ public class MdmSurvivorshipSvcImplTest {
); );
link.setSourcePid(JpaPid.fromId((long)i)); link.setSourcePid(JpaPid.fromId((long)i));
link.setGoldenPid(JpaPid.fromId((long)goldenId)); link.setGoldenPid(JpaPid.fromId((long)goldenId));
link.setSourceId(patient.getId());
link.setGoldenResourceId(goldenPatient.getId());
links.add(link); links.add(link);
sourceIdToPid.put(patient.getId(), link.getSourcePid());
} }
IFhirResourceDao resourceDao = mock(IFhirResourceDao.class); IFhirResourceDao resourceDao = mock(IFhirResourceDao.class);
// when // when
IHapiTransactionService.IExecutionBuilder executionBuilder = mock(IHapiTransactionService.IExecutionBuilder.class);
when(myTransactionService.withRequest(any())).thenReturn(executionBuilder);
doAnswer(a -> {
Runnable callback = a.getArgument(0);
callback.run();
return 0;
}).when(executionBuilder).execute(any(Runnable.class));
when(myDaoRegistry.getResourceDao(eq("Patient"))) when(myDaoRegistry.getResourceDao(eq("Patient")))
.thenReturn(resourceDao); .thenReturn(resourceDao);
AtomicInteger counter = new AtomicInteger(); AtomicInteger counter = new AtomicInteger();
when(resourceDao.readByPid(any())) when(resourceDao.readByPid(any()))
.thenAnswer(params -> resources.get(counter.getAndIncrement())); .thenAnswer(params -> resources.get(counter.getAndIncrement()));
Page<MdmLinkJson> linkPage = mock(Page.class); Page<MdmLinkJson> linkPage = new PageImpl<>(links);
when(myMdmLinkQuerySvc.queryLinks(any(), any())) when(myMdmLinkQuerySvc.queryLinks(any(), any()))
.thenReturn(linkPage); .thenReturn(linkPage);
when(linkPage.get())
.thenReturn(links.stream());
when(myMdmSettings.getMdmRules()) when(myMdmSettings.getMdmRules())
.thenReturn(new MdmRulesJson()); .thenReturn(new MdmRulesJson());
doReturn(sourceIdToPid).when(myIIdHelperService)
.resolveResourcePersistentIds(any(RequestPartitionId.class), anyString(), any(List.class));
// we will return a non-empty list to reduce mocking // we will return a non-empty list to reduce mocking
when(myEIDHelper.getExternalEid(any())) when(myEIDHelper.getExternalEid(any()))
.thenReturn(Collections.singletonList(new CanonicalEID("example", "value", "use"))); .thenReturn(Collections.singletonList(new CanonicalEID("example", "value", "use")));
@ -178,19 +197,6 @@ public class MdmSurvivorshipSvcImplTest {
.update(eq(goldenPatientRebuilt), any(RequestDetails.class)); .update(eq(goldenPatientRebuilt), any(RequestDetails.class));
} }
private MdmLink createLinkWithoutUpdateDate(Patient theSource, Patient theGoldenResource) {
MdmLink link = new MdmLink();
link.setCreated(Date.from(
Instant.now().minus(2, ChronoUnit.DAYS)
));
link.setLinkSource(MdmLinkSourceEnum.AUTO);
link.setMatchResult(MdmMatchResultEnum.MATCH);
link.setSourcePersistenceId(JpaPid.fromId(theSource.getIdElement().getIdPartAsLong()));
link.setGoldenResourcePersistenceId(JpaPid.fromId(theGoldenResource.getIdElement().getIdPartAsLong()));
return link;
}
private MdmTransactionContext createTransactionContext() { private MdmTransactionContext createTransactionContext() {
MdmTransactionContext context = new MdmTransactionContext(); MdmTransactionContext context = new MdmTransactionContext();
context.setRestOperation(MdmTransactionContext.OperationType.UPDATE_LINK); context.setRestOperation(MdmTransactionContext.OperationType.UPDATE_LINK);

View File

@ -23,6 +23,7 @@ import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.model.api.IModelJson;
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.Date; import java.util.Date;
@ -45,13 +46,13 @@ public class MdmLinkJson implements IModelJson {
/** /**
* Golden resource PID * Golden resource PID
*/ */
@JsonProperty("goldenPid") @JsonIgnore
private IResourcePersistentId<?> myGoldenPid; private IResourcePersistentId<?> myGoldenPid;
/** /**
* Source PID * Source PID
*/ */
@JsonProperty("sourcePid") @JsonIgnore
private IResourcePersistentId<?> mySourcePid; private IResourcePersistentId<?> mySourcePid;
/** /**

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.api.svc.IIdHelperService; import ca.uhn.fhir.jpa.api.svc.IIdHelperService;
import ca.uhn.fhir.jpa.dao.tx.HapiTransactionService;
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc; import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
@ -36,12 +37,15 @@ import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails; import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId;
import ca.uhn.fhir.util.TerserUtil; import ca.uhn.fhir.util.TerserUtil;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -58,17 +62,21 @@ public class MdmSurvivorshipSvcImpl implements IMdmSurvivorshipService {
private final IIdHelperService<?> myIIdHelperService; private final IIdHelperService<?> myIIdHelperService;
private final HapiTransactionService myTransactionService;
public MdmSurvivorshipSvcImpl( public MdmSurvivorshipSvcImpl(
FhirContext theFhirContext, FhirContext theFhirContext,
GoldenResourceHelper theResourceHelper, GoldenResourceHelper theResourceHelper,
DaoRegistry theDaoRegistry, DaoRegistry theDaoRegistry,
IMdmLinkQuerySvc theLinkQuerySvc, IMdmLinkQuerySvc theLinkQuerySvc,
IIdHelperService<?> theIIdHelperService) { IIdHelperService<?> theIIdHelperService,
HapiTransactionService theHapiTransactionService) {
myFhirContext = theFhirContext; myFhirContext = theFhirContext;
myGoldenResourceHelper = theResourceHelper; myGoldenResourceHelper = theResourceHelper;
myDaoRegistry = theDaoRegistry; myDaoRegistry = theDaoRegistry;
myMdmLinkQuerySvc = theLinkQuerySvc; myMdmLinkQuerySvc = theLinkQuerySvc;
myIIdHelperService = theIIdHelperService; myIIdHelperService = theIIdHelperService;
myTransactionService = theHapiTransactionService;
} }
// this logic is custom in smile vs hapi // this logic is custom in smile vs hapi
@ -132,6 +140,7 @@ public class MdmSurvivorshipSvcImpl implements IMdmSurvivorshipService {
return (T) toSave; return (T) toSave;
} }
@SuppressWarnings("rawtypes")
private Stream<IBaseResource> getMatchedSourceIdsByLinkUpdateDate( private Stream<IBaseResource> getMatchedSourceIdsByLinkUpdateDate(
IBaseResource theGoldenResource, MdmTransactionContext theMdmTransactionContext) { IBaseResource theGoldenResource, MdmTransactionContext theMdmTransactionContext) {
String resourceType = theGoldenResource.fhirType(); String resourceType = theGoldenResource.fhirType();
@ -143,18 +152,27 @@ public class MdmSurvivorshipSvcImpl implements IMdmSurvivorshipService {
searchParameters.setMatchResult(MdmMatchResultEnum.MATCH); searchParameters.setMatchResult(MdmMatchResultEnum.MATCH);
Page<MdmLinkJson> linksQuery = myMdmLinkQuerySvc.queryLinks(searchParameters, theMdmTransactionContext); Page<MdmLinkJson> linksQuery = myMdmLinkQuerySvc.queryLinks(searchParameters, theMdmTransactionContext);
return linksQuery.get().map(link -> { // we want it ordered
IResourcePersistentId<?> pid = link.getSourcePid(); List<String> sourceIds = new ArrayList<>();
return dao.readByPid(pid); linksQuery.forEach(link -> {
sourceIds.add(link.getSourceId());
});
Map<String, IResourcePersistentId> sourceIdToPid = new HashMap<>();
if (!sourceIds.isEmpty()) {
// we cannot call resolveResourcePersistentIds if there are no ids to call it with
myTransactionService
.withRequest(new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.allPartitions()))
.execute(() -> {
Map<String, ? extends IResourcePersistentId> ids =
myIIdHelperService.resolveResourcePersistentIds(
RequestPartitionId.allPartitions(), resourceType, sourceIds);
sourceIdToPid.putAll(ids);
}); });
} }
private IResourcePersistentId<?> getResourcePID(String theId, String theResourceType) { return sourceIds.stream().map(id -> {
return myIIdHelperService.newPidFromStringIdAndResourceName(theId, theResourceType); IResourcePersistentId<?> pid = sourceIdToPid.get(id);
} return dao.readByPid(pid);
});
private boolean isNumericOrUuid(String theLongCandidate) {
return StringUtils.isNumeric(theLongCandidate)
|| IS_UUID.matcher(theLongCandidate).matches();
} }
} }

View File

@ -0,0 +1,70 @@
package ca.uhn.fhir.mdm.model.mdmevents;
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
import ca.uhn.fhir.rest.api.server.storage.BaseResourcePersistentId;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.hl7.fhir.r4.model.IdType;
import org.junit.jupiter.api.Test;
import java.util.Date;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
public class MdmLinkJsonTest {
private static class TestPID<T> extends BaseResourcePersistentId<T> {
private final T myId;
protected TestPID(String theResourceType, T theId) {
super(theResourceType);
myId = theId;
}
@Override
public T getId() {
return myId;
}
}
private final ObjectMapper myObjectMapper = new ObjectMapper();
@Test
public void serializeDeserialize_longId_works() throws JsonProcessingException {
// setup
MdmLinkJson json = createLinkJson();
TestPID<Long> golden = new TestPID<>("Patient", 1L);
golden.setAssociatedResourceId(new IdType("Patient/1"));
golden.setVersion(1L);
TestPID<Long> source = new TestPID<>("Patient", 2L);
source.setAssociatedResourceId(new IdType("Patient/2"));
source.setVersion(1L);
json.setGoldenPid(golden);
json.setSourcePid(source);
// test
String strVal = myObjectMapper.writeValueAsString(json);
assertFalse(isBlank(strVal));
MdmLinkJson deserialized = myObjectMapper.readValue(strVal, MdmLinkJson.class);
assertNotNull(deserialized);
assertNull(deserialized.getSourcePid());
assertNull(deserialized.getGoldenPid());
}
private MdmLinkJson createLinkJson() {
MdmLinkJson json = new MdmLinkJson();
json.setGoldenResourceId("Patient/1");
json.setSourceId("Patient/2");
json.setMatchResult(MdmMatchResultEnum.MATCH);
json.setLinkSource(MdmLinkSourceEnum.MANUAL);
json.setCreated(new Date());
json.setUpdated(new Date());
return json;
}
}

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.api.server.storage;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
public interface IResourcePersistentId<T> { public interface IResourcePersistentId<T> {
IResourcePersistentId NOT_FOUND = new NotFoundPid(); IResourcePersistentId NOT_FOUND = new NotFoundPid();
IIdType getAssociatedResourceId(); IIdType getAssociatedResourceId();
@ -31,6 +32,7 @@ public interface IResourcePersistentId<T> {
T getId(); T getId();
Long getVersion(); Long getVersion();
/** /**
* @param theVersion This should only be populated if a specific version is needed. If you want the current version, * @param theVersion This should only be populated if a specific version is needed. If you want the current version,
* leave this as <code>null</code> * leave this as <code>null</code>