[3040] finish implementation (#3049)
* [3040] finish implementation * Update hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md Co-authored-by: Tadgh <tadgh@cs.toronto.edu> * Update hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md Co-authored-by: Tadgh <tadgh@cs.toronto.edu> * Update hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/docs/server_jpa_mdm/mdm_operations.md Co-authored-by: Tadgh <tadgh@cs.toronto.edu> * [3040] throw exception if link exists * [3040] add matchResult parameter/update documentation Co-authored-by: Tadgh <tadgh@cs.toronto.edu>
This commit is contained in:
parent
a474e88ed4
commit
3c80dba90e
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 3020
|
||||||
|
jira: SMILE-3192
|
||||||
|
title: "Added `$mdm-create-link` operation and documentation."
|
|
@ -396,6 +396,78 @@ Any supported MDM type can be used. The following request body shows how to upda
|
||||||
|
|
||||||
The operation returns the updated Golden Resource. For the query above `Patient` resource will be returned. Note that this is the only way to modify MDM-managed Golden Resources.
|
The operation returns the updated Golden Resource. For the query above `Patient` resource will be returned. Note that this is the only way to modify MDM-managed Golden Resources.
|
||||||
|
|
||||||
|
## Create Link
|
||||||
|
|
||||||
|
Use the `$mdm-create-link` operation to create an MDM link from a Golden Resource to a Target Resource without the need for any pre-existing matching data within the two resources. This operation takes the following parameters:
|
||||||
|
|
||||||
|
<table class="table table-striped table-condensed">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Cardinality</th>
|
||||||
|
<th>Description</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>goldenResourceId</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>1..1</td>
|
||||||
|
<td>
|
||||||
|
The id of the Golden Resource.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>resourceId</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>1..1</td>
|
||||||
|
<td>
|
||||||
|
The id of the target resource.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>matchResult</td>
|
||||||
|
<td>String</td>
|
||||||
|
<td>0..1</td>
|
||||||
|
<td>
|
||||||
|
Optional matchResult. If omitted, it automatically set the default to MATCH, otherwise the value should be
|
||||||
|
MATCH, POSSIBLE_MATCH or NO_MATCH.
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
MDM links created in this way will automatically have their `linkSource` set to `MANUAL`.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
Use an HTTP POST to the following URL to invoke this operation:
|
||||||
|
|
||||||
|
```url
|
||||||
|
http://example.com/$mdm-create-link
|
||||||
|
```
|
||||||
|
|
||||||
|
Any supported MDM type can be used. The following request body shows how to update link on the Patient resource type:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"resourceType": "Parameters",
|
||||||
|
"parameter": [ {
|
||||||
|
"name": "goldenResourceId",
|
||||||
|
"valueString": "Patient/123"
|
||||||
|
}, {
|
||||||
|
"name": "resourceId",
|
||||||
|
"valueString": "Patient/456"
|
||||||
|
}, {
|
||||||
|
"name": "matchResult",
|
||||||
|
"valueString": "MATCH"
|
||||||
|
} ]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The operation returns the Golden Resource. For the query above, `Patient` will be returned.
|
||||||
|
|
||||||
## Merge Golden Resources
|
## Merge Golden Resources
|
||||||
|
|
||||||
The `$mdm-merge-golden-resources` operation can be used to merge one Golden Resource with another. When doing this, you will need to decide which resource to merge from and which one to merge to.
|
The `$mdm-merge-golden-resources` operation can be used to merge one Golden Resource with another. When doing this, you will need to decide which resource to merge from and which one to merge to.
|
||||||
|
|
|
@ -35,6 +35,7 @@ import ca.uhn.fhir.jpa.mdm.svc.GoldenResourceMergerSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.IMdmModelConverterSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.IMdmModelConverterSvc;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.MdmControllerSvcImpl;
|
import ca.uhn.fhir.jpa.mdm.svc.MdmControllerSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.MdmEidUpdateService;
|
import ca.uhn.fhir.jpa.mdm.svc.MdmEidUpdateService;
|
||||||
|
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkCreateSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkQuerySvcImplSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkQuerySvcImplSvc;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkSvcImpl;
|
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkSvcImpl;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkUpdaterSvcImpl;
|
import ca.uhn.fhir.jpa.mdm.svc.MdmLinkUpdaterSvcImpl;
|
||||||
|
@ -55,6 +56,7 @@ import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
|
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmBatchJobSubmitterFactory;
|
import ca.uhn.fhir.mdm.api.IMdmBatchJobSubmitterFactory;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
|
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmLinkCreateSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
|
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
|
import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
|
||||||
|
@ -233,6 +235,12 @@ public class MdmConsumerConfig {
|
||||||
return new MdmLinkUpdaterSvcImpl();
|
return new MdmLinkUpdaterSvcImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
IMdmLinkCreateSvc mdmLinkCreateSvc() {
|
||||||
|
return new MdmLinkCreateSvcImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
MdmLoader mdmLoader() {
|
MdmLoader mdmLoader() {
|
||||||
return new MdmLoader();
|
return new MdmLoader();
|
||||||
|
|
|
@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
|
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmBatchJobSubmitterFactory;
|
import ca.uhn.fhir.mdm.api.IMdmBatchJobSubmitterFactory;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
|
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmLinkCreateSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
|
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
|
import ca.uhn.fhir.mdm.api.IMdmLinkUpdaterSvc;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkJson;
|
import ca.uhn.fhir.mdm.api.MdmLinkJson;
|
||||||
|
@ -65,6 +66,8 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
|
||||||
@Autowired
|
@Autowired
|
||||||
IMdmLinkUpdaterSvc myIMdmLinkUpdaterSvc;
|
IMdmLinkUpdaterSvc myIMdmLinkUpdaterSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
IMdmLinkCreateSvc myIMdmLinkCreateSvc;
|
||||||
|
@Autowired
|
||||||
IMdmBatchJobSubmitterFactory myMdmBatchJobSubmitterFactory;
|
IMdmBatchJobSubmitterFactory myMdmBatchJobSubmitterFactory;
|
||||||
|
|
||||||
public MdmControllerSvcImpl() {
|
public MdmControllerSvcImpl() {
|
||||||
|
@ -106,6 +109,17 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
|
||||||
return myIMdmLinkUpdaterSvc.updateLink(goldenResource, source, matchResult, theMdmTransactionContext);
|
return myIMdmLinkUpdaterSvc.updateLink(goldenResource, source, matchResult, theMdmTransactionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IAnyResource createLink(String theGoldenResourceId, String theSourceResourceId, @Nullable String theMatchResult, MdmTransactionContext theMdmTransactionContext) {
|
||||||
|
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
|
||||||
|
IAnyResource goldenResource = myMdmControllerHelper.getLatestGoldenResourceFromIdOrThrowException(ProviderConstants.MDM_CREATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
|
||||||
|
IAnyResource source = myMdmControllerHelper.getLatestSourceFromIdOrThrowException(ProviderConstants.MDM_CREATE_LINK_RESOURCE_ID, theSourceResourceId);
|
||||||
|
myMdmControllerHelper.validateSameVersion(goldenResource, theGoldenResourceId);
|
||||||
|
myMdmControllerHelper.validateSameVersion(source, theSourceResourceId);
|
||||||
|
|
||||||
|
return myIMdmLinkCreateSvc.createLink(goldenResource, source, matchResult, theMdmTransactionContext);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IBaseParameters submitMdmClearJob(List<String> theUrls, IPrimitiveType<BigDecimal> theBatchSize, ServletRequestDetails theRequestDetails) {
|
public IBaseParameters submitMdmClearJob(List<String> theUrls, IPrimitiveType<BigDecimal> theBatchSize, ServletRequestDetails theRequestDetails) {
|
||||||
MultiUrlProcessor multiUrlProcessor = new MultiUrlProcessor(myFhirContext, myMdmBatchJobSubmitterFactory.getClearJobSubmitter());
|
MultiUrlProcessor multiUrlProcessor = new MultiUrlProcessor(myFhirContext, myMdmBatchJobSubmitterFactory.getClearJobSubmitter());
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
package ca.uhn.fhir.jpa.mdm.svc;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||||
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmLinkCreateSvc;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
|
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||||
|
import ca.uhn.fhir.mdm.util.MessageHelper;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class MdmLinkCreateSvcImpl implements IMdmLinkCreateSvc {
|
||||||
|
private static final Logger ourLog = Logs.getMdmTroubleshootingLog();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
FhirContext myFhirContext;
|
||||||
|
@Autowired
|
||||||
|
IdHelperService myIdHelperService;
|
||||||
|
@Autowired
|
||||||
|
MdmLinkDaoSvc myMdmLinkDaoSvc;
|
||||||
|
@Autowired
|
||||||
|
IMdmSettings myMdmSettings;
|
||||||
|
@Autowired
|
||||||
|
MessageHelper myMessageHelper;
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public IAnyResource createLink(IAnyResource theGoldenResource, IAnyResource theSourceResource, MdmMatchResultEnum theMatchResult, MdmTransactionContext theMdmContext) {
|
||||||
|
String sourceType = myFhirContext.getResourceType(theSourceResource);
|
||||||
|
|
||||||
|
validateCreateLinkRequest(theGoldenResource, theSourceResource, sourceType);
|
||||||
|
|
||||||
|
Long goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource);
|
||||||
|
Long targetId = myIdHelperService.getPidOrThrowException(theSourceResource);
|
||||||
|
|
||||||
|
Optional<MdmLink> optionalMdmLink = myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, targetId);
|
||||||
|
if (optionalMdmLink.isPresent()) {
|
||||||
|
throw new InvalidRequestException(myMessageHelper.getMessageForPresentLink(theGoldenResource, theSourceResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
List<MdmLink> mdmLinks = myMdmLinkDaoSvc.getMdmLinksBySourcePidAndMatchResult(targetId, MdmMatchResultEnum.MATCH);
|
||||||
|
if (mdmLinks.size() > 0 && theMatchResult == MdmMatchResultEnum.MATCH) {
|
||||||
|
throw new InvalidRequestException(myMessageHelper.getMessageForMultipleGoldenRecords(theSourceResource));
|
||||||
|
}
|
||||||
|
|
||||||
|
MdmLink mdmLink = myMdmLinkDaoSvc.getOrCreateMdmLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, targetId);
|
||||||
|
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
|
||||||
|
if (theMatchResult == null) {
|
||||||
|
mdmLink.setMatchResult(MdmMatchResultEnum.MATCH);
|
||||||
|
} else {
|
||||||
|
mdmLink.setMatchResult(theMatchResult);
|
||||||
|
}
|
||||||
|
ourLog.info("Manually creating a " + theGoldenResource.getIdElement().toVersionless() + " to " + theSourceResource.getIdElement().toVersionless() + " mdm link.");
|
||||||
|
myMdmLinkDaoSvc.save(mdmLink);
|
||||||
|
|
||||||
|
return theGoldenResource;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateCreateLinkRequest(IAnyResource theGoldenRecord, IAnyResource theSourceResource, String theSourceType) {
|
||||||
|
String goldenRecordType = myFhirContext.getResourceType(theGoldenRecord);
|
||||||
|
|
||||||
|
if (!myMdmSettings.isSupportedMdmType(goldenRecordType)) {
|
||||||
|
throw new InvalidRequestException(myMessageHelper.getMessageForUnsupportedFirstArgumentTypeInUpdate(goldenRecordType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!myMdmSettings.isSupportedMdmType(theSourceType)) {
|
||||||
|
throw new InvalidRequestException(myMessageHelper.getMessageForUnsupportedSecondArgumentTypeInUpdate(theSourceType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Objects.equals(goldenRecordType, theSourceType)) {
|
||||||
|
throw new InvalidRequestException(myMessageHelper.getMessageForArgumentTypeMismatchInUpdate(goldenRecordType, theSourceType));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MdmResourceUtil.isMdmManaged(theGoldenRecord)) {
|
||||||
|
throw new InvalidRequestException(myMessageHelper.getMessageForUnmanagedResource());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MdmResourceUtil.isMdmAllowed(theSourceResource)) {
|
||||||
|
throw new InvalidRequestException(myMessageHelper.getMessageForUnsupportedSourceResource());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,175 @@
|
||||||
|
package ca.uhn.fhir.jpa.mdm.provider;
|
||||||
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
import ca.uhn.fhir.mdm.api.MdmConstants;
|
||||||
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
|
import ca.uhn.fhir.mdm.util.MessageHelper;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.endsWith;
|
||||||
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
public class MdmProviderCreateLinkR4Test extends BaseLinkR4Test {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessageHelper myMessageHelper;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateLinkWithMatchResult() {
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
Patient patient = createPatient(buildPatientWithNameAndId("PatientGiven", "ID.PatientGiven.123"), true, false);
|
||||||
|
StringType patientId = new StringType(patient.getIdElement().getValue());
|
||||||
|
|
||||||
|
Patient sourcePatient = createPatient(buildPatientWithNameAndId("SourcePatientGiven", "ID.SourcePatientGiven.123"), true, false);
|
||||||
|
StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue());
|
||||||
|
|
||||||
|
myMdmProvider.createLink(sourcePatientId, patientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
assertLinkCount(2);
|
||||||
|
|
||||||
|
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(patient);
|
||||||
|
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
|
||||||
|
assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateLinkWithNullMatchResult() {
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
Patient patient = createPatient(buildPatientWithNameAndId("PatientGiven", "ID.PatientGiven.123"), true, false);
|
||||||
|
StringType patientId = new StringType(patient.getIdElement().getValue());
|
||||||
|
|
||||||
|
Patient sourcePatient = createPatient(buildPatientWithNameAndId("SourcePatientGiven", "ID.SourcePatientGiven.123"), true, false);
|
||||||
|
StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue());
|
||||||
|
|
||||||
|
myMdmProvider.createLink(sourcePatientId, patientId, null, myRequestDetails);
|
||||||
|
assertLinkCount(2);
|
||||||
|
|
||||||
|
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(patient);
|
||||||
|
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
|
||||||
|
assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateLinkTwiceWithDifferentGoldenResourceAndMatchResult() {
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
Patient patient = createPatient(buildPatientWithNameAndId("PatientGiven", "ID.PatientGiven.123"), true, false);
|
||||||
|
StringType patientId = new StringType(patient.getIdElement().getValue());
|
||||||
|
|
||||||
|
Patient sourcePatient = createPatient(buildPatientWithNameAndId("SourcePatientGiven", "ID.SourcePatientGiven.123"), true, false);
|
||||||
|
StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue());
|
||||||
|
|
||||||
|
myMdmProvider.createLink(sourcePatientId, patientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
|
||||||
|
Patient sourcePatient2 = createPatient(buildPatientWithNameAndId("SourcePatientGiven2", "ID.SourcePatientGiven.123.2"), true, false);
|
||||||
|
StringType sourcePatientId2 = new StringType(sourcePatient2.getIdElement().getValue());
|
||||||
|
|
||||||
|
try {
|
||||||
|
myMdmProvider.createLink(sourcePatientId2, patientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), endsWith("Use $mdm-query-links to see more details."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateLinkTwiceWithDifferentGoldenResourceAndNoMatchResult() {
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
Patient patient = createPatient(buildPatientWithNameAndId("PatientGiven", "ID.PatientGiven.123"), true, false);
|
||||||
|
StringType patientId = new StringType(patient.getIdElement().getValue());
|
||||||
|
|
||||||
|
Patient sourcePatient = createPatient(buildPatientWithNameAndId("SourcePatientGiven", "ID.SourcePatientGiven.123"), true, false);
|
||||||
|
StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue());
|
||||||
|
|
||||||
|
myMdmProvider.createLink(sourcePatientId, patientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
|
||||||
|
Patient sourcePatient2 = createPatient(buildPatientWithNameAndId("SourcePatientGiven2", "ID.SourcePatientGiven.123.2"), true, false);
|
||||||
|
StringType sourcePatientId2 = new StringType(sourcePatient2.getIdElement().getValue());
|
||||||
|
|
||||||
|
myMdmProvider.createLink(sourcePatientId2, patientId, NO_MATCH_RESULT, myRequestDetails);
|
||||||
|
|
||||||
|
assertLinkCount(3);
|
||||||
|
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(patient);
|
||||||
|
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(1).getLinkSource());
|
||||||
|
assertEquals(MdmMatchResultEnum.NO_MATCH, links.get(1).getMatchResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateExistentLink() {
|
||||||
|
assertLinkCount(1);
|
||||||
|
try {
|
||||||
|
myMdmProvider.createLink(mySourcePatientId, myPatientId, MATCH_RESULT,myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), startsWith("Link already exists"));
|
||||||
|
}
|
||||||
|
assertLinkCount(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateIllegalResultPD() {
|
||||||
|
try {
|
||||||
|
myMdmProvider.createLink(mySourcePatientId, myPatientId, POSSIBLE_DUPLICATE_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("$mdm-create-link illegal matchResult value 'POSSIBLE_DUPLICATE'. Must be NO_MATCH, MATCH or POSSIBLE_MATCH", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateIllegalFirstArg() {
|
||||||
|
try {
|
||||||
|
myMdmProvider.createLink(new StringType(""), myPatientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), endsWith(" must have form <resourceType>/<id> where <id> is the id of the resource"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateIllegalSecondArg() {
|
||||||
|
try {
|
||||||
|
myMdmProvider.createLink(myPatientId, new StringType(""), MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), endsWith(" must have form <resourceType>/<id> where <id> is the id of the resource and <resourceType> is the type of the resource"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateStrangePatient() {
|
||||||
|
Patient patient = createPatient();
|
||||||
|
try {
|
||||||
|
myMdmProvider.createLink(new StringType(patient.getIdElement().getValue()), myPatientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
String expectedMessage = myMessageHelper.getMessageForUnmanagedResource();
|
||||||
|
assertEquals(expectedMessage, e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExcludedGoldenResource() {
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_NO_MDM_MANAGED);
|
||||||
|
createPatient(patient);
|
||||||
|
try {
|
||||||
|
myMdmProvider.createLink(mySourcePatientId, new StringType(patient.getIdElement().getValue()), MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals(myMessageHelper.getMessageForUnsupportedSourceResource(), e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,5 +44,7 @@ public interface IMdmControllerSvc {
|
||||||
|
|
||||||
IAnyResource updateLink(String theGoldenResourceId, String theSourceResourceId, String theMatchResult, MdmTransactionContext theMdmTransactionContext);
|
IAnyResource updateLink(String theGoldenResourceId, String theSourceResourceId, String theMatchResult, MdmTransactionContext theMdmTransactionContext);
|
||||||
|
|
||||||
|
IAnyResource createLink(String theGoldenResourceId, String theSourceResourceId, @Nullable String theMatchResult, MdmTransactionContext theMdmTransactionContext);
|
||||||
|
|
||||||
IBaseParameters submitMdmClearJob(List<String> theUrls, IPrimitiveType<BigDecimal> theBatchSize, ServletRequestDetails theRequestDetails);
|
IBaseParameters submitMdmClearJob(List<String> theUrls, IPrimitiveType<BigDecimal> theBatchSize, ServletRequestDetails theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package ca.uhn.fhir.mdm.api;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public interface IMdmLinkCreateSvc {
|
||||||
|
IAnyResource createLink(IAnyResource theGoldenResource, IAnyResource theSourceResource, MdmMatchResultEnum theMatchResult, MdmTransactionContext theMdmContext);
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ public class MdmTransactionContext {
|
||||||
SUBMIT_RESOURCE_TO_MDM,
|
SUBMIT_RESOURCE_TO_MDM,
|
||||||
QUERY_LINKS,
|
QUERY_LINKS,
|
||||||
UPDATE_LINK,
|
UPDATE_LINK,
|
||||||
|
CREATE_LINK,
|
||||||
DUPLICATE_GOLDEN_RESOURCES,
|
DUPLICATE_GOLDEN_RESOURCES,
|
||||||
NOT_DUPLICATE,
|
NOT_DUPLICATE,
|
||||||
MERGE_GOLDEN_RESOURCES,
|
MERGE_GOLDEN_RESOURCES,
|
||||||
|
|
|
@ -38,6 +38,8 @@ import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
|
||||||
public abstract class BaseMdmProvider {
|
public abstract class BaseMdmProvider {
|
||||||
|
|
||||||
|
@ -81,6 +83,22 @@ public abstract class BaseMdmProvider {
|
||||||
validateNotNull(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theResourceId);
|
validateNotNull(ProviderConstants.MDM_UPDATE_LINK_RESOURCE_ID, theResourceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void validateCreateLinkParameters(IPrimitiveType<String> theGoldenResourceId, IPrimitiveType<String> theResourceId, @Nullable IPrimitiveType<String> theMatchResult) {
|
||||||
|
validateNotNull(ProviderConstants.MDM_CREATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId);
|
||||||
|
validateNotNull(ProviderConstants.MDM_CREATE_LINK_RESOURCE_ID, theResourceId);
|
||||||
|
if (theMatchResult != null) {
|
||||||
|
MdmMatchResultEnum matchResult = MdmMatchResultEnum.valueOf(theMatchResult.getValue());
|
||||||
|
switch (matchResult) {
|
||||||
|
case NO_MATCH:
|
||||||
|
case POSSIBLE_MATCH:
|
||||||
|
case MATCH:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidRequestException(ProviderConstants.MDM_CREATE_LINK + " illegal " + ProviderConstants.MDM_CREATE_LINK_MATCH_RESULT +
|
||||||
|
" value '" + matchResult + "'. Must be " + MdmMatchResultEnum.NO_MATCH + ", " + MdmMatchResultEnum.MATCH + " or " + MdmMatchResultEnum.POSSIBLE_MATCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected MdmTransactionContext createMdmContext(RequestDetails theRequestDetails, MdmTransactionContext.OperationType theOperationType, String theResourceType) {
|
protected MdmTransactionContext createMdmContext(RequestDetails theRequestDetails, MdmTransactionContext.OperationType theOperationType, String theResourceType) {
|
||||||
TransactionLogMessages transactionLogMessages = TransactionLogMessages.createFromTransactionGuid(theRequestDetails.getTransactionGuid());
|
TransactionLogMessages transactionLogMessages = TransactionLogMessages.createFromTransactionGuid(theRequestDetails.getTransactionGuid());
|
||||||
|
|
|
@ -53,6 +53,7 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
@ -180,6 +181,18 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Operation(name = ProviderConstants.MDM_CREATE_LINK)
|
||||||
|
public IBaseResource createLink(@OperationParam(name = ProviderConstants.MDM_CREATE_LINK_GOLDEN_RESOURCE_ID, min = 1, max = 1) IPrimitiveType<String> theGoldenResourceId,
|
||||||
|
@OperationParam(name = ProviderConstants.MDM_CREATE_LINK_RESOURCE_ID, min = 1, max = 1) IPrimitiveType<String> theResourceId,
|
||||||
|
@OperationParam(name = ProviderConstants.MDM_CREATE_LINK_MATCH_RESULT, min = 0, max = 1) IPrimitiveType<String> theMatchResult,
|
||||||
|
ServletRequestDetails theRequestDetails) {
|
||||||
|
validateCreateLinkParameters(theGoldenResourceId, theResourceId, theMatchResult);
|
||||||
|
return myMdmControllerSvc.createLink(theGoldenResourceId.getValueAsString(), theResourceId.getValue(), extractStringOrNull(theMatchResult),
|
||||||
|
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.CREATE_LINK,
|
||||||
|
getResourceType(ProviderConstants.MDM_CREATE_LINK_GOLDEN_RESOURCE_ID, theGoldenResourceId))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.OPERATION_MDM_CLEAR, returnParameters = {
|
@Operation(name = ProviderConstants.OPERATION_MDM_CLEAR, returnParameters = {
|
||||||
@OperationParam(name = ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, typeName = "decimal")
|
@OperationParam(name = ProviderConstants.OPERATION_BATCH_RESPONSE_JOB_ID, typeName = "decimal")
|
||||||
})
|
})
|
||||||
|
|
|
@ -90,4 +90,21 @@ public class MessageHelper {
|
||||||
public String getMessageForNoLink(String theGoldenRecord, String theSourceResource) {
|
public String getMessageForNoLink(String theGoldenRecord, String theSourceResource) {
|
||||||
return "No link exists between " + theGoldenRecord + " and " + theSourceResource;
|
return "No link exists between " + theGoldenRecord + " and " + theSourceResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMessageForPresentLink(IAnyResource theGoldenRecord, IAnyResource theSourceResource) {
|
||||||
|
return getMessageForPresentLink(theGoldenRecord.getIdElement().toVersionless().toString(),
|
||||||
|
theSourceResource.getIdElement().toVersionless().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageForPresentLink(String theGoldenRecord, String theSourceResource) {
|
||||||
|
return "Link already exists between " + theGoldenRecord + " and " + theSourceResource + ". Use $mdm-update-link instead.";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageForMultipleGoldenRecords(IAnyResource theSourceResource) {
|
||||||
|
return getMessageForMultipleGoldenRecords(theSourceResource.getIdElement().toVersionless().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageForMultipleGoldenRecords(String theSourceResource) {
|
||||||
|
return theSourceResource + " already has matched golden resource. Use $mdm-query-links to see more details.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,6 +80,11 @@ public class ProviderConstants {
|
||||||
public static final String MDM_UPDATE_LINK_RESOURCE_ID = "resourceId";
|
public static final String MDM_UPDATE_LINK_RESOURCE_ID = "resourceId";
|
||||||
public static final String MDM_UPDATE_LINK_MATCH_RESULT = "matchResult";
|
public static final String MDM_UPDATE_LINK_MATCH_RESULT = "matchResult";
|
||||||
|
|
||||||
|
public static final String MDM_CREATE_LINK = "$mdm-create-link";
|
||||||
|
public static final String MDM_CREATE_LINK_GOLDEN_RESOURCE_ID = "goldenResourceId";
|
||||||
|
public static final String MDM_CREATE_LINK_RESOURCE_ID = "resourceId";
|
||||||
|
public static final String MDM_CREATE_LINK_MATCH_RESULT = "matchResult";
|
||||||
|
|
||||||
public static final String MDM_QUERY_LINKS = "$mdm-query-links";
|
public static final String MDM_QUERY_LINKS = "$mdm-query-links";
|
||||||
public static final String MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID = "goldenResourceId";
|
public static final String MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID = "goldenResourceId";
|
||||||
public static final String MDM_QUERY_LINKS_RESOURCE_ID = "resourceId";
|
public static final String MDM_QUERY_LINKS_RESOURCE_ID = "resourceId";
|
||||||
|
|
Loading…
Reference in New Issue