diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5986-fixing-bug-with-deserializing-mdmlink-json.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5986-fixing-bug-with-deserializing-mdmlink-json.yaml new file mode 100644 index 00000000000..7c7e320a338 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5986-fixing-bug-with-deserializing-mdmlink-json.yaml @@ -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. +" diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java index 775a9ac226a..ba16be6115f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/index/IdHelperService.java @@ -125,7 +125,7 @@ public class IdHelperService implements IIdHelperService { private boolean myDontCheckActiveTransactionForUnitTest; @VisibleForTesting - void setDontCheckActiveTransactionForUnitTest(boolean theDontCheckActiveTransactionForUnitTest) { + protected void setDontCheckActiveTransactionForUnitTest(boolean theDontCheckActiveTransactionForUnitTest) { myDontCheckActiveTransactionForUnitTest = theDontCheckActiveTransactionForUnitTest; } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java index 8d050055d65..a96a38325b3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/index/IdHelperServiceTest.java @@ -20,11 +20,20 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import java.util.List; 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.assertFalse; import static org.junit.jupiter.api.Assertions.assertNull; @@ -37,7 +46,7 @@ import static org.mockito.Mockito.when; public class IdHelperServiceTest { @InjectMocks - private final IdHelperService subject = new IdHelperService(); + private final IdHelperService myHelperSvc = new IdHelperService(); @Mock protected IResourceTableDao myResourceTableDao; @@ -45,8 +54,8 @@ public class IdHelperServiceTest { @Mock private JpaStorageSettings myStorageSettings; - @Mock - private FhirContext myFhirCtx; + @Spy + private FhirContext myFhirCtx = FhirContext.forR4Cached(); @Mock private MemoryCacheService myMemoryCacheService; @@ -59,10 +68,11 @@ public class IdHelperServiceTest { @BeforeEach void setUp() { - subject.setDontCheckActiveTransactionForUnitTest(true); + myHelperSvc.setDontCheckActiveTransactionForUnitTest(true); - when(myStorageSettings.isDeleteEnabled()).thenReturn(true); - when(myStorageSettings.getResourceClientIdStrategy()).thenReturn(JpaStorageSettings.ClientIdStrategyEnum.ANY); + // lenient because some tests require this setup, and others do not + lenient().doReturn(true).when(myStorageSettings).isDeleteEnabled(); + lenient().doReturn(JpaStorageSettings.ClientIdStrategyEnum.ANY).when(myStorageSettings).getResourceClientIdStrategy(); } @Test @@ -81,7 +91,7 @@ public class IdHelperServiceTest { // configure mock behaviour 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()); } @@ -102,13 +112,14 @@ public class IdHelperServiceTest { // configure mock behaviour when(myStorageSettings.isDeleteEnabled()).thenReturn(false); - Map actualIds = subject.resolveResourcePersistentIds(requestPartitionId, resourceType, ids, theExcludeDeleted); + Map actualIds = myHelperSvc.resolveResourcePersistentIds(requestPartitionId, resourceType, ids, theExcludeDeleted); //verifyResult assertFalse(actualIds.isEmpty()); assertNull(actualIds.get(ids.get(0))); } + private Root getMockedFrom() { @SuppressWarnings("unchecked") Path path = mock(Path.class); diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSurvivorshipConfig.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSurvivorshipConfig.java index 655565085a7..2e0e9b68e12 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSurvivorshipConfig.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/config/MdmSurvivorshipConfig.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.mdm.config; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.api.dao.DaoRegistry; 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.IMdmSettings; import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService; @@ -57,10 +58,18 @@ public class MdmSurvivorshipConfig { @Autowired private IIdHelperService myIIdHelperService; + @Autowired + private HapiTransactionService myTransactionService; + @Bean public IMdmSurvivorshipService mdmSurvivorshipService() { return new MdmSurvivorshipSvcImpl( - myFhirContext, goldenResourceHelper(), myDaoRegistry, myMdmLinkQuerySvc, myIIdHelperService); + myFhirContext, + goldenResourceHelper(), + myDaoRegistry, + myMdmLinkQuerySvc, + myIIdHelperService, + myTransactionService); } @Bean diff --git a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java index 89faf7f5550..150240ea71c 100644 --- a/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java +++ b/hapi-fhir-jpaserver-mdm/src/main/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImpl.java @@ -44,13 +44,17 @@ public class MdmModelConverterSvcImpl implements IMdmModelConverterSvc { .toVersionless() .getValue(); retVal.setSourceId(sourceId); - retVal.setSourcePid(theLink.getSourcePersistenceId()); + if (theLink.getSourcePersistenceId() != null) { + retVal.setSourcePid(theLink.getSourcePersistenceId()); + } String goldenResourceId = myIdHelperService .resourceIdFromPidOrThrowException(theLink.getGoldenResourcePersistenceId(), theLink.getMdmSourceType()) .toVersionless() .getValue(); retVal.setGoldenResourceId(goldenResourceId); - retVal.setGoldenPid(theLink.getGoldenResourcePersistenceId()); + if (theLink.getGoldenResourcePersistenceId() != null) { + retVal.setGoldenPid(theLink.getGoldenResourcePersistenceId()); + } retVal.setCreated(theLink.getCreated()); retVal.setEidMatch(theLink.getEidMatch()); retVal.setLinkSource(theLink.getLinkSource()); diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImplTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImplTest.java index 530ef90ee25..64a7e0d665d 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImplTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmModelConverterSvcImplTest.java @@ -50,8 +50,15 @@ public class MdmModelConverterSvcImplTest extends BaseMdmR4Test { ourLog.info("actualMdmLinkJson: {}", actualMdmLinkJson); - MdmLinkJson exepctedMdmLinkJson = getExepctedMdmLinkJson(mdmLink.getGoldenResourcePersistenceId().getId(), mdmLink.getSourcePersistenceId().getId(), MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.MANUAL, version, createTime, updateTime, isLinkCreatedResource, scoreRounded); - assertEquals(exepctedMdmLinkJson, actualMdmLinkJson); + MdmLinkJson expectedMdmLinkJson = getExepctedMdmLinkJson(mdmLink.getGoldenResourcePersistenceId().getId(), mdmLink.getSourcePersistenceId().getId(), MdmMatchResultEnum.MATCH, MdmLinkSourceEnum.MANUAL, version, createTime, updateTime, isLinkCreatedResource, scoreRounded); + 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 diff --git a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImplTest.java b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImplTest.java index c33b050f6c6..b9c6a266821 100644 --- a/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImplTest.java +++ b/hapi-fhir-jpaserver-mdm/src/test/java/ca/uhn/fhir/jpa/mdm/svc/MdmSurvivorshipSvcImplTest.java @@ -1,11 +1,12 @@ package ca.uhn.fhir.jpa.mdm.svc; 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.IFhirResourceDao; -import ca.uhn.fhir.jpa.api.svc.IIdHelperService; 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.mdm.api.IMdmLinkQuerySvc; 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.MdmResourceUtil; 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.r4.model.Patient; import org.junit.jupiter.api.BeforeEach; @@ -31,20 +33,23 @@ import org.mockito.Mock; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; 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.Collections; -import java.util.Date; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; 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.spy; import static org.mockito.Mockito.verify; @@ -69,12 +74,15 @@ public class MdmSurvivorshipSvcImplTest { @Mock private MdmPartitionHelper myMdmPartitionHelper; - @Spy - private IIdHelperService myIIdHelperService = new IdHelperService(); + @Mock + private IdHelperService myIIdHelperService; @Mock private IMdmLinkQuerySvc myMdmLinkQuerySvc; + @Mock + private HapiTransactionService myTransactionService; + private MdmSurvivorshipSvcImpl mySvc; @BeforeEach @@ -91,7 +99,8 @@ public class MdmSurvivorshipSvcImplTest { myGoldenResourceHelper, myDaoRegistry, myMdmLinkQuerySvc, - myIIdHelperService + myIIdHelperService, + myTransactionService ); } @@ -115,7 +124,7 @@ public class MdmSurvivorshipSvcImplTest { List resources = new ArrayList<>(); List links = new ArrayList<>(); - + Map sourceIdToPid = new HashMap<>(); for (int i = 0; i < 10; i++) { // we want our resources to be slightly different Patient patient = new Patient(); @@ -137,24 +146,34 @@ public class MdmSurvivorshipSvcImplTest { ); link.setSourcePid(JpaPid.fromId((long)i)); link.setGoldenPid(JpaPid.fromId((long)goldenId)); + link.setSourceId(patient.getId()); + link.setGoldenResourceId(goldenPatient.getId()); links.add(link); + sourceIdToPid.put(patient.getId(), link.getSourcePid()); } IFhirResourceDao resourceDao = mock(IFhirResourceDao.class); // 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"))) .thenReturn(resourceDao); AtomicInteger counter = new AtomicInteger(); when(resourceDao.readByPid(any())) .thenAnswer(params -> resources.get(counter.getAndIncrement())); - Page linkPage = mock(Page.class); + Page linkPage = new PageImpl<>(links); when(myMdmLinkQuerySvc.queryLinks(any(), any())) .thenReturn(linkPage); - when(linkPage.get()) - .thenReturn(links.stream()); when(myMdmSettings.getMdmRules()) .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 when(myEIDHelper.getExternalEid(any())) .thenReturn(Collections.singletonList(new CanonicalEID("example", "value", "use"))); @@ -178,19 +197,6 @@ public class MdmSurvivorshipSvcImplTest { .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() { MdmTransactionContext context = new MdmTransactionContext(); context.setRestOperation(MdmTransactionContext.OperationType.UPDATE_LINK); diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/mdmevents/MdmLinkJson.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/mdmevents/MdmLinkJson.java index c5f4fe364a6..20854c1903a 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/mdmevents/MdmLinkJson.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/model/mdmevents/MdmLinkJson.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum; import ca.uhn.fhir.mdm.api.MdmMatchResultEnum; import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.rest.api.server.storage.IResourcePersistentId; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Date; @@ -45,13 +46,13 @@ public class MdmLinkJson implements IModelJson { /** * Golden resource PID */ - @JsonProperty("goldenPid") + @JsonIgnore private IResourcePersistentId myGoldenPid; /** * Source PID */ - @JsonProperty("sourcePid") + @JsonIgnore private IResourcePersistentId mySourcePid; /** diff --git a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSurvivorshipSvcImpl.java b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSurvivorshipSvcImpl.java index 0ac8940a7c7..9c951b0ad01 100644 --- a/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSurvivorshipSvcImpl.java +++ b/hapi-fhir-server-mdm/src/main/java/ca/uhn/fhir/mdm/svc/MdmSurvivorshipSvcImpl.java @@ -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.IFhirResourceDao; 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.IMdmSurvivorshipService; 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.storage.IResourcePersistentId; 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.IBase; import org.hl7.fhir.instance.model.api.IBaseResource; 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.stream.Stream; @@ -58,17 +62,21 @@ public class MdmSurvivorshipSvcImpl implements IMdmSurvivorshipService { private final IIdHelperService myIIdHelperService; + private final HapiTransactionService myTransactionService; + public MdmSurvivorshipSvcImpl( FhirContext theFhirContext, GoldenResourceHelper theResourceHelper, DaoRegistry theDaoRegistry, IMdmLinkQuerySvc theLinkQuerySvc, - IIdHelperService theIIdHelperService) { + IIdHelperService theIIdHelperService, + HapiTransactionService theHapiTransactionService) { myFhirContext = theFhirContext; myGoldenResourceHelper = theResourceHelper; myDaoRegistry = theDaoRegistry; myMdmLinkQuerySvc = theLinkQuerySvc; myIIdHelperService = theIIdHelperService; + myTransactionService = theHapiTransactionService; } // this logic is custom in smile vs hapi @@ -132,6 +140,7 @@ public class MdmSurvivorshipSvcImpl implements IMdmSurvivorshipService { return (T) toSave; } + @SuppressWarnings("rawtypes") private Stream getMatchedSourceIdsByLinkUpdateDate( IBaseResource theGoldenResource, MdmTransactionContext theMdmTransactionContext) { String resourceType = theGoldenResource.fhirType(); @@ -143,18 +152,27 @@ public class MdmSurvivorshipSvcImpl implements IMdmSurvivorshipService { searchParameters.setMatchResult(MdmMatchResultEnum.MATCH); Page linksQuery = myMdmLinkQuerySvc.queryLinks(searchParameters, theMdmTransactionContext); - return linksQuery.get().map(link -> { - IResourcePersistentId pid = link.getSourcePid(); + // we want it ordered + List sourceIds = new ArrayList<>(); + linksQuery.forEach(link -> { + sourceIds.add(link.getSourceId()); + }); + Map 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 ids = + myIIdHelperService.resolveResourcePersistentIds( + RequestPartitionId.allPartitions(), resourceType, sourceIds); + sourceIdToPid.putAll(ids); + }); + } + + return sourceIds.stream().map(id -> { + IResourcePersistentId pid = sourceIdToPid.get(id); return dao.readByPid(pid); }); } - - private IResourcePersistentId getResourcePID(String theId, String theResourceType) { - return myIIdHelperService.newPidFromStringIdAndResourceName(theId, theResourceType); - } - - private boolean isNumericOrUuid(String theLongCandidate) { - return StringUtils.isNumeric(theLongCandidate) - || IS_UUID.matcher(theLongCandidate).matches(); - } } diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/model/.keep b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/model/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/model/mdmevents/MdmLinkJsonTest.java b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/model/mdmevents/MdmLinkJsonTest.java new file mode 100644 index 00000000000..ad1824a5c73 --- /dev/null +++ b/hapi-fhir-server-mdm/src/test/java/ca/uhn/fhir/mdm/model/mdmevents/MdmLinkJsonTest.java @@ -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 extends BaseResourcePersistentId { + + 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 golden = new TestPID<>("Patient", 1L); + golden.setAssociatedResourceId(new IdType("Patient/1")); + golden.setVersion(1L); + TestPID 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; + } +} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/IResourcePersistentId.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/IResourcePersistentId.java index cf66098daf4..3d9649ee94e 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/IResourcePersistentId.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/api/server/storage/IResourcePersistentId.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.api.server.storage; import org.hl7.fhir.instance.model.api.IIdType; public interface IResourcePersistentId { + IResourcePersistentId NOT_FOUND = new NotFoundPid(); IIdType getAssociatedResourceId(); @@ -31,6 +32,7 @@ public interface IResourcePersistentId { T getId(); Long getVersion(); + /** * @param theVersion This should only be populated if a specific version is needed. If you want the current version, * leave this as null