2579 partitioned support for mdm (#3485)
* committed changes discussed at tasking * Changed the subscription message to store the request partition * modified mdm subscription loader to create a cross partition subscription in the default partition if multitenancy is enabled * Fix errors when running Multitenant IT for mdm * Added partition id to mdm golden resource search and creating/updating golden resource * added partition id column to the mdm link table * Added partition id of the source resource to partition id column of the mdm link table * broken build * working -links, missing hapi tests * Fixed the mdm link db table due to feedback * added IT for create link mdm operation, also added changes to pass the IT * Fixed test for create mdm links, added test for update mdm link on partitioned server * changed mdmLink search from searchbyExample to criteriaBuilder, added partitionHelperSvc to determine partition * added test for merge golden resource and search golden resource on partitioned server * added unit test for not duplicate on partitioned and non-partitioned server * added criteriabuilder search and tests * code cleanup * added partition support to -merge-golden-resources operation * test cleanup * fixed operations with multitenancy * added test for mdm clear operation * added waiting for batch job to finish in mdm clear operation test * added query to dao to support partitioned server for mdm clear operation * resolve mistake from merge conflict * added mdm-submit operations, also fixed paging for -query * added partitionIds constant * fixed build issue where there was no Code.msg in mdm partition exceptions * code review fixes * fix broken test, also fix a bug where query link could not query by linkSource properly * fix test stubbing * bump hapi version to 6.0.0-PRE10-SNAPSHOT Co-authored-by: Ken Stevens <khstevens@gmail.com> Co-authored-by: Steven Li <steven@smilecdr.com> Co-authored-by: Long Ma <long@smilecdr.com>
This commit is contained in:
parent
523fe95820
commit
01d6e15f90
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ public final class Msg {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IMPORTANT: Please update the following comment after you add a new code
|
* IMPORTANT: Please update the following comment after you add a new code
|
||||||
* Last code value: 2073
|
* Last code value: 2075
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private Msg() {}
|
private Msg() {}
|
||||||
|
|
|
@ -285,6 +285,7 @@ public class Constants {
|
||||||
* key will be of type {@link ca.uhn.fhir.interceptor.model.RequestPartitionId}.
|
* key will be of type {@link ca.uhn.fhir.interceptor.model.RequestPartitionId}.
|
||||||
*/
|
*/
|
||||||
public static final String RESOURCE_PARTITION_ID = Constants.class.getName() + "_RESOURCE_PARTITION_ID";
|
public static final String RESOURCE_PARTITION_ID = Constants.class.getName() + "_RESOURCE_PARTITION_ID";
|
||||||
|
public static final String PARTITION_IDS = "partitionIds";
|
||||||
public static final String CT_APPLICATION_GZIP = "application/gzip";
|
public static final String CT_APPLICATION_GZIP = "application/gzip";
|
||||||
public static final String[] EMPTY_STRING_ARRAY = new String[0];
|
public static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||||
public static final String SUBSCRIPTION_MULTITYPE_PREFIX = "[";
|
public static final String SUBSCRIPTION_MULTITYPE_PREFIX = "[";
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,14 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-bom</artifactId>
|
<artifactId>hapi-fhir-bom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>HAPI FHIR BOM</name>
|
<name>HAPI FHIR BOM</name>
|
||||||
|
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-cli</artifactId>
|
<artifactId>hapi-fhir-cli</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../../hapi-deployable-pom</relativePath>
|
<relativePath>../../hapi-deployable-pom</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.batch.mdm.job;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.batch.reader.BaseReverseCronologicalBatchPidReader;
|
import ca.uhn.fhir.jpa.batch.reader.BaseReverseCronologicalBatchPidReader;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
|
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
|
||||||
import ca.uhn.fhir.jpa.searchparam.ResourceSearch;
|
import ca.uhn.fhir.jpa.searchparam.ResourceSearch;
|
||||||
|
@ -28,6 +29,7 @@ import org.springframework.data.domain.PageRequest;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,9 +43,14 @@ public class ReverseCronologicalBatchMdmLinkPidReader extends BaseReverseCronolo
|
||||||
protected Set<Long> getNextPidBatch(ResourceSearch resourceSearch) {
|
protected Set<Long> getNextPidBatch(ResourceSearch resourceSearch) {
|
||||||
String resourceName = resourceSearch.getResourceName();
|
String resourceName = resourceSearch.getResourceName();
|
||||||
Pageable pageable = PageRequest.of(0, getBatchSize());
|
Pageable pageable = PageRequest.of(0, getBatchSize());
|
||||||
//Expand out the list to handle the REDIRECT/POSSIBLE DUPLICATE ones.
|
RequestPartitionId requestPartitionId = resourceSearch.getRequestPartitionId();
|
||||||
|
if (requestPartitionId.isAllPartitions()){
|
||||||
return new HashSet<>(myMdmLinkDao.findPidByResourceNameAndThreshold(resourceName, getCurrentHighThreshold(), pageable));
|
return new HashSet<>(myMdmLinkDao.findPidByResourceNameAndThreshold(resourceName, getCurrentHighThreshold(), pageable));
|
||||||
}
|
}
|
||||||
|
List<Integer> partitionIds = requestPartitionId.getPartitionIds();
|
||||||
|
//Expand out the list to handle the REDIRECT/POSSIBLE DUPLICATE ones.
|
||||||
|
return new HashSet<>(myMdmLinkDao.findPidByResourceNameAndThresholdAndPartitionId(resourceName, getCurrentHighThreshold(), partitionIds, pageable));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setDateFromPidFunction(ResourceSearch resourceSearch) {
|
protected void setDateFromPidFunction(ResourceSearch resourceSearch) {
|
||||||
|
|
|
@ -73,4 +73,7 @@ public interface IMdmLinkDao extends JpaRepository<MdmLink, Long>, IHapiFhirJpaR
|
||||||
|
|
||||||
@Query("SELECT ml.myId FROM MdmLink ml WHERE ml.myMdmSourceType = :resourceName AND ml.myCreated <= :highThreshold ORDER BY ml.myCreated DESC")
|
@Query("SELECT ml.myId FROM MdmLink ml WHERE ml.myMdmSourceType = :resourceName AND ml.myCreated <= :highThreshold ORDER BY ml.myCreated DESC")
|
||||||
List<Long> findPidByResourceNameAndThreshold(@Param("resourceName") String theResourceName, @Param("highThreshold") Date theHighThreshold, Pageable thePageable);
|
List<Long> findPidByResourceNameAndThreshold(@Param("resourceName") String theResourceName, @Param("highThreshold") Date theHighThreshold, Pageable thePageable);
|
||||||
|
|
||||||
|
@Query("SELECT ml.myId FROM MdmLink ml WHERE ml.myMdmSourceType = :resourceName AND ml.myCreated <= :highThreshold AND ml.myPartitionIdValue IN :partitionId ORDER BY ml.myCreated DESC")
|
||||||
|
List<Long> findPidByResourceNameAndThresholdAndPartitionId(@Param("resourceName") String theResourceName, @Param("highThreshold") Date theHighThreshold, @Param("partitionId") List<Integer> thePartitionIds, Pageable thePageable);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,10 +20,11 @@ package ca.uhn.fhir.jpa.entity;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.BasePartitionable;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmLink;
|
import ca.uhn.fhir.mdm.api.IMdmLink;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
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.jpa.model.entity.ResourceTable;
|
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
|
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
|
@ -52,7 +53,7 @@ import java.util.Date;
|
||||||
//TODO GGG revisit adding this: @UniqueConstraint(name = "IDX_EMPI_GR_TGT", columnNames = {"GOLDEN_RESOURCE_PID", "TARGET_PID"}),
|
//TODO GGG revisit adding this: @UniqueConstraint(name = "IDX_EMPI_GR_TGT", columnNames = {"GOLDEN_RESOURCE_PID", "TARGET_PID"}),
|
||||||
//TODO GGG Should i make individual indices for PERSON/TARGET?
|
//TODO GGG Should i make individual indices for PERSON/TARGET?
|
||||||
})
|
})
|
||||||
public class MdmLink implements IMdmLink {
|
public class MdmLink extends BasePartitionable implements IMdmLink {
|
||||||
public static final int VERSION_LENGTH = 16;
|
public static final int VERSION_LENGTH = 16;
|
||||||
private static final int MATCH_RESULT_LENGTH = 16;
|
private static final int MATCH_RESULT_LENGTH = 16;
|
||||||
private static final int LINK_SOURCE_LENGTH = 16;
|
private static final int LINK_SOURCE_LENGTH = 16;
|
||||||
|
@ -331,6 +332,7 @@ public class MdmLink implements IMdmLink {
|
||||||
.append("myHadToCreateNewResource", myHadToCreateNewGoldenResource)
|
.append("myHadToCreateNewResource", myHadToCreateNewGoldenResource)
|
||||||
.append("myScore", myScore)
|
.append("myScore", myScore)
|
||||||
.append("myRuleCount", myRuleCount)
|
.append("myRuleCount", myRuleCount)
|
||||||
|
.append("myPartitionId", getPartitionId())
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -49,10 +49,11 @@ import java.util.stream.Collectors;
|
||||||
@SuppressWarnings({"SqlNoDataSourceInspection", "SpellCheckingInspection"})
|
@SuppressWarnings({"SqlNoDataSourceInspection", "SpellCheckingInspection"})
|
||||||
public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
||||||
|
|
||||||
|
private final Set<FlagEnum> myFlags;
|
||||||
|
|
||||||
// H2, Derby, MariaDB, and MySql automatically add indexes to foreign keys
|
// H2, Derby, MariaDB, and MySql automatically add indexes to foreign keys
|
||||||
public static final DriverTypeEnum[] NON_AUTOMATIC_FK_INDEX_PLATFORMS = new DriverTypeEnum[]{
|
public static final DriverTypeEnum[] NON_AUTOMATIC_FK_INDEX_PLATFORMS = new DriverTypeEnum[]{
|
||||||
DriverTypeEnum.POSTGRES_9_4, DriverTypeEnum.ORACLE_12C, DriverTypeEnum.MSSQL_2012};
|
DriverTypeEnum.POSTGRES_9_4, DriverTypeEnum.ORACLE_12C, DriverTypeEnum.MSSQL_2012};
|
||||||
private final Set<FlagEnum> myFlags;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -494,6 +495,15 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
||||||
.nullable()
|
.nullable()
|
||||||
.type(ColumnTypeEnum.STRING, 4000);
|
.type(ColumnTypeEnum.STRING, 4000);
|
||||||
|
|
||||||
|
// Add partition id column for mdm
|
||||||
|
Builder.BuilderWithTableName empiLink = version.onTable("MPI_LINK");
|
||||||
|
|
||||||
|
empiLink.addColumn("20220324.1", "PARTITION_ID")
|
||||||
|
.nullable()
|
||||||
|
.type(ColumnTypeEnum.INT);
|
||||||
|
empiLink.addColumn("20220324.2", "PARTITION_DATE")
|
||||||
|
.nullable()
|
||||||
|
.type(ColumnTypeEnum.DATE_ONLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -580,7 +590,6 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
||||||
version.onTable("HFJ_SEARCH")
|
version.onTable("HFJ_SEARCH")
|
||||||
.migratePostgresTextClobToBinaryClob("20211003.3", "SEARCH_QUERY_STRING");
|
.migratePostgresTextClobToBinaryClob("20211003.3", "SEARCH_QUERY_STRING");
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init540() {
|
private void init540() {
|
||||||
|
|
|
@ -37,6 +37,8 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
import ca.uhn.fhir.util.StringUtil;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -249,7 +251,7 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
|
||||||
* If the partition has both, they are validated to ensure that they correspond.
|
* If the partition has both, they are validated to ensure that they correspond.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private RequestPartitionId validateNormalizeAndNotifyHooksForRead(@Nonnull RequestPartitionId theRequestPartitionId, RequestDetails theRequest, String theResourceType) {
|
private RequestPartitionId validateNormalizeAndNotifyHooksForRead(@Nonnull RequestPartitionId theRequestPartitionId, RequestDetails theRequest, @Nonnull String theResourceType) {
|
||||||
RequestPartitionId retVal = theRequestPartitionId;
|
RequestPartitionId retVal = theRequestPartitionId;
|
||||||
|
|
||||||
if (retVal.getPartitionNames() != null) {
|
if (retVal.getPartitionNames() != null) {
|
||||||
|
@ -260,18 +262,25 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
|
||||||
|
|
||||||
// Note: It's still possible that the partition only has a date but no name/id
|
// Note: It's still possible that the partition only has a date but no name/id
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(theResourceType)) {
|
||||||
|
validateHasPartitionPermissions(theRequest, theResourceType, retVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateHasPartitionPermissions(RequestDetails theRequest, String theResourceType, RequestPartitionId theRequestPartitionId) {
|
||||||
if (myInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_SELECTED)) {
|
if (myInterceptorBroadcaster.hasHooks(Pointcut.STORAGE_PARTITION_SELECTED)) {
|
||||||
RuntimeResourceDefinition runtimeResourceDefinition = myFhirContext.getResourceDefinition(theResourceType);
|
RuntimeResourceDefinition runtimeResourceDefinition;
|
||||||
|
runtimeResourceDefinition = myFhirContext.getResourceDefinition(theResourceType);
|
||||||
HookParams params = new HookParams()
|
HookParams params = new HookParams()
|
||||||
.add(RequestPartitionId.class, retVal)
|
.add(RequestPartitionId.class, theRequestPartitionId)
|
||||||
.add(RequestDetails.class, theRequest)
|
.add(RequestDetails.class, theRequest)
|
||||||
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
.addIfMatchesType(ServletRequestDetails.class, theRequest)
|
||||||
.add(RuntimeResourceDefinition.class, runtimeResourceDefinition);
|
.add(RuntimeResourceDefinition.class, runtimeResourceDefinition);
|
||||||
doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PARTITION_SELECTED, params);
|
doCallHooks(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PARTITION_SELECTED, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
return retVal;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private RequestPartitionId validateAndNormalizePartitionIds(RequestPartitionId theRequestPartitionId) {
|
private RequestPartitionId validateAndNormalizePartitionIds(RequestPartitionId theRequestPartitionId) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.validation;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||||
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.validation.IResourceLoader;
|
import ca.uhn.fhir.validation.IResourceLoader;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
@ -35,6 +36,7 @@ public class JpaResourceLoader implements IResourceLoader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IBaseResource> T load(Class<T> theType, IIdType theId) throws ResourceNotFoundException {
|
public <T extends IBaseResource> T load(Class<T> theType, IIdType theId) throws ResourceNotFoundException {
|
||||||
return myDaoRegistry.getResourceDao(theType).read(theId);
|
SystemRequestDetails systemRequestDetails = SystemRequestDetails.forAllPartitions();
|
||||||
|
return myDaoRegistry.getResourceDao(theType).read(theId, systemRequestDetails);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkEvent;
|
import ca.uhn.fhir.mdm.api.MdmLinkEvent;
|
||||||
import ca.uhn.fhir.mdm.log.Logs;
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.server.TransactionLogMessages;
|
import ca.uhn.fhir.rest.server.TransactionLogMessages;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage;
|
import ca.uhn.fhir.rest.server.messaging.ResourceOperationMessage;
|
||||||
|
@ -166,7 +167,9 @@ public class MdmMessageHandler implements MessageHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private IAnyResource getResourceFromPayload(ResourceModifiedMessage theMsg) {
|
private IAnyResource getResourceFromPayload(ResourceModifiedMessage theMsg) {
|
||||||
return (IAnyResource) theMsg.getNewPayload(myFhirContext);
|
IBaseResource newPayload = theMsg.getNewPayload(myFhirContext);
|
||||||
|
newPayload.setUserData(Constants.RESOURCE_PARTITION_ID, theMsg.getPartitionId());
|
||||||
|
return (IAnyResource) newPayload;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleUpdateResource(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) {
|
private void handleUpdateResource(ResourceModifiedMessage theMsg, MdmTransactionContext theMdmTransactionContext) {
|
||||||
|
|
|
@ -51,6 +51,8 @@ import ca.uhn.fhir.jpa.mdm.svc.candidate.FindCandidateByLinkSvc;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchCriteriaBuilderSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchCriteriaBuilderSvc;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
|
||||||
|
import ca.uhn.fhir.jpa.mdm.util.MdmPartitionHelper;
|
||||||
|
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
|
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
|
||||||
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.IMdmLinkCreateSvc;
|
||||||
|
@ -244,12 +246,14 @@ public class MdmConsumerConfig {
|
||||||
IResourceLoader theResourceLoader,
|
IResourceLoader theResourceLoader,
|
||||||
IMdmSettings theMdmSettings,
|
IMdmSettings theMdmSettings,
|
||||||
IMdmMatchFinderSvc theMdmMatchFinderSvc,
|
IMdmMatchFinderSvc theMdmMatchFinderSvc,
|
||||||
MessageHelper messageHelper) {
|
MessageHelper messageHelper,
|
||||||
|
IRequestPartitionHelperSvc partitionHelperSvc) {
|
||||||
return new MdmControllerHelper(theFhirContext,
|
return new MdmControllerHelper(theFhirContext,
|
||||||
theResourceLoader,
|
theResourceLoader,
|
||||||
theMdmMatchFinderSvc,
|
theMdmMatchFinderSvc,
|
||||||
theMdmSettings,
|
theMdmSettings,
|
||||||
messageHelper);
|
messageHelper,
|
||||||
|
partitionHelperSvc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -257,4 +261,6 @@ public class MdmConsumerConfig {
|
||||||
return new MdmControllerSvcImpl();
|
return new MdmControllerSvcImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
MdmPartitionHelper mdmPartitionHelper() {return new MdmPartitionHelper();}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
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.partition.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
||||||
import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings;
|
import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings;
|
||||||
import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer;
|
import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer;
|
||||||
|
@ -33,7 +34,9 @@ import ca.uhn.fhir.mdm.api.MdmConstants;
|
||||||
import ca.uhn.fhir.mdm.log.Logs;
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.Subscription;
|
import org.hl7.fhir.r4.model.Subscription;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -88,10 +91,10 @@ public class MdmSubscriptionLoader {
|
||||||
|
|
||||||
synchronized void updateIfNotPresent(IBaseResource theSubscription) {
|
synchronized void updateIfNotPresent(IBaseResource theSubscription) {
|
||||||
try {
|
try {
|
||||||
mySubscriptionDao.read(theSubscription.getIdElement());
|
mySubscriptionDao.read(theSubscription.getIdElement(), SystemRequestDetails.forAllPartitions());
|
||||||
} catch (ResourceNotFoundException | ResourceGoneException e) {
|
} catch (ResourceNotFoundException | ResourceGoneException e) {
|
||||||
ourLog.info("Creating subsription " + theSubscription.getIdElement());
|
ourLog.info("Creating subscription " + theSubscription.getIdElement());
|
||||||
mySubscriptionDao.update(theSubscription);
|
mySubscriptionDao.update(theSubscription, SystemRequestDetails.forAllPartitions());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +105,7 @@ public class MdmSubscriptionLoader {
|
||||||
retval.setStatus(org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus.REQUESTED);
|
retval.setStatus(org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus.REQUESTED);
|
||||||
retval.setCriteria(theCriteria);
|
retval.setCriteria(theCriteria);
|
||||||
retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED);
|
retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED);
|
||||||
|
retval.addExtension().setUrl(HapiExtensions.EXTENSION_SUBSCRIPTION_CROSS_PARTITION).setValue(new org.hl7.fhir.dstu3.model.BooleanType().setValue(true));
|
||||||
org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelComponent channel = retval.getChannel();
|
org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelComponent channel = retval.getChannel();
|
||||||
channel.setType(org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType.MESSAGE);
|
channel.setType(org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType.MESSAGE);
|
||||||
channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings()));
|
channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings()));
|
||||||
|
@ -116,6 +120,7 @@ public class MdmSubscriptionLoader {
|
||||||
retval.setStatus(Subscription.SubscriptionStatus.REQUESTED);
|
retval.setStatus(Subscription.SubscriptionStatus.REQUESTED);
|
||||||
retval.setCriteria(theCriteria);
|
retval.setCriteria(theCriteria);
|
||||||
retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED);
|
retval.getMeta().addTag().setSystem(MdmConstants.SYSTEM_MDM_MANAGED).setCode(MdmConstants.CODE_HAPI_MDM_MANAGED);
|
||||||
|
retval.addExtension().setUrl(HapiExtensions.EXTENSION_SUBSCRIPTION_CROSS_PARTITION).setValue(new BooleanType().setValue(true));
|
||||||
Subscription.SubscriptionChannelComponent channel = retval.getChannel();
|
Subscription.SubscriptionChannelComponent channel = retval.getChannel();
|
||||||
channel.setType(Subscription.SubscriptionChannelType.MESSAGE);
|
channel.setType(Subscription.SubscriptionChannelType.MESSAGE);
|
||||||
channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings()));
|
channel.setEndpoint("channel:" + myChannelNamer.getChannelName(IMdmSettings.EMPI_CHANNEL_NAME, new ChannelProducerSettings()));
|
||||||
|
|
|
@ -21,26 +21,42 @@ package ca.uhn.fhir.jpa.mdm.dao;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
|
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
|
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
|
||||||
import ca.uhn.fhir.mdm.log.Logs;
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Example;
|
import org.springframework.data.domain.Example;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageImpl;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.Expression;
|
||||||
|
import javax.persistence.criteria.Predicate;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -58,6 +74,8 @@ public class MdmLinkDaoSvc {
|
||||||
private IJpaIdHelperService myJpaIdHelperService;
|
private IJpaIdHelperService myJpaIdHelperService;
|
||||||
@Autowired
|
@Autowired
|
||||||
private FhirContext myFhirContext;
|
private FhirContext myFhirContext;
|
||||||
|
@Autowired
|
||||||
|
protected EntityManager myEntityManager;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public MdmLink createOrUpdateLinkEntity(IBaseResource theGoldenResource, IBaseResource theSourceResource, MdmMatchOutcome theMatchOutcome, MdmLinkSourceEnum theLinkSource, @Nullable MdmTransactionContext theMdmTransactionContext) {
|
public MdmLink createOrUpdateLinkEntity(IBaseResource theGoldenResource, IBaseResource theSourceResource, MdmMatchOutcome theMatchOutcome, MdmLinkSourceEnum theLinkSource, @Nullable MdmTransactionContext theMdmTransactionContext) {
|
||||||
|
@ -76,6 +94,11 @@ public class MdmLinkDaoSvc {
|
||||||
} else {
|
} else {
|
||||||
mdmLink.setScore(theMatchOutcome.score);
|
mdmLink.setScore(theMatchOutcome.score);
|
||||||
}
|
}
|
||||||
|
// Add partition for the mdm link if it's available in the source resource
|
||||||
|
RequestPartitionId partitionId = (RequestPartitionId) theSourceResource.getUserData(Constants.RESOURCE_PARTITION_ID);
|
||||||
|
if (partitionId != null && partitionId.getFirstPartitionIdOrNull() != null) {
|
||||||
|
mdmLink.setPartitionId(new PartitionablePartitionId(partitionId.getFirstPartitionIdOrNull(), partitionId.getPartitionDate()));
|
||||||
|
}
|
||||||
|
|
||||||
String message = String.format("Creating MdmLink from %s to %s -> %s", theGoldenResource.getIdElement().toUnqualifiedVersionless(), theSourceResource.getIdElement().toUnqualifiedVersionless(), theMatchOutcome);
|
String message = String.format("Creating MdmLink from %s to %s -> %s", theGoldenResource.getIdElement().toUnqualifiedVersionless(), theSourceResource.getIdElement().toUnqualifiedVersionless(), theMatchOutcome);
|
||||||
theMdmTransactionContext.addTransactionLogMessage(message);
|
theMdmTransactionContext.addTransactionLogMessage(message);
|
||||||
|
@ -253,13 +276,57 @@ public class MdmLinkDaoSvc {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an example {@link MdmLink}, return all links from the database which match the example.
|
* Given a list of criteria, return all links from the database which fits the criteria provided
|
||||||
*
|
*
|
||||||
* @param theExampleLink The MDM link containing the data we would like to search for.
|
* @param theGoldenResourceId The resource ID of the golden resource being searched.
|
||||||
|
* @param theSourceId The resource ID of the source resource being searched.
|
||||||
|
* @param theMatchResult the {@link MdmMatchResultEnum} being searched.
|
||||||
|
* @param theLinkSource the {@link MdmLinkSourceEnum} being searched.
|
||||||
|
* @param thePageRequest the {@link MdmPageRequest} paging information
|
||||||
|
* @param thePartitionId List of partitions ID being searched, where the link's partition must be in the list.
|
||||||
* @return a list of {@link MdmLink} entities which match the example.
|
* @return a list of {@link MdmLink} entities which match the example.
|
||||||
*/
|
*/
|
||||||
public Page<MdmLink> findMdmLinkByExample(Example<MdmLink> theExampleLink, MdmPageRequest thePageRequest) {
|
public PageImpl<MdmLink> executeTypedQuery(IIdType theGoldenResourceId, IIdType theSourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmPageRequest thePageRequest, List<Integer> thePartitionId) {
|
||||||
return myMdmLinkDao.findAll(theExampleLink, thePageRequest.toPageRequest());
|
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<MdmLink> criteriaQuery = criteriaBuilder.createQuery(MdmLink.class);
|
||||||
|
Root<MdmLink> from = criteriaQuery.from(MdmLink.class);
|
||||||
|
|
||||||
|
List<Predicate> andPredicates = new ArrayList<>();
|
||||||
|
|
||||||
|
if (theGoldenResourceId != null) {
|
||||||
|
Predicate goldenResourcePredicate = criteriaBuilder.equal(from.get("myGoldenResourcePid").as(Long.class), myJpaIdHelperService.getPidOrThrowException(theGoldenResourceId));
|
||||||
|
andPredicates.add(goldenResourcePredicate);
|
||||||
|
}
|
||||||
|
if (theSourceId != null) {
|
||||||
|
Predicate sourceIdPredicate = criteriaBuilder.equal(from.get("mySourcePid").as(Long.class), myJpaIdHelperService.getPidOrThrowException(theSourceId));
|
||||||
|
andPredicates.add(sourceIdPredicate);
|
||||||
|
}
|
||||||
|
if (theMatchResult != null) {
|
||||||
|
Predicate matchResultPredicate = criteriaBuilder.equal(from.get("myMatchResult").as(MdmMatchResultEnum.class), theMatchResult);
|
||||||
|
andPredicates.add(matchResultPredicate);
|
||||||
|
}
|
||||||
|
if (theLinkSource != null) {
|
||||||
|
Predicate linkSourcePredicate = criteriaBuilder.equal(from.get("myLinkSource").as(MdmLinkSourceEnum.class), theLinkSource);
|
||||||
|
andPredicates.add(linkSourcePredicate);
|
||||||
|
}
|
||||||
|
if (!CollectionUtils.isEmpty(thePartitionId)) {
|
||||||
|
Expression<Integer> exp = from.get("myPartitionId").get("myPartitionId").as(Integer.class);
|
||||||
|
Predicate linkSourcePredicate = exp.in(thePartitionId);
|
||||||
|
andPredicates.add(linkSourcePredicate);
|
||||||
|
}
|
||||||
|
|
||||||
|
Predicate finalQuery = criteriaBuilder.and(andPredicates.toArray(new Predicate[0]));
|
||||||
|
TypedQuery<MdmLink> typedQuery = myEntityManager.createQuery(criteriaQuery.where(finalQuery));
|
||||||
|
|
||||||
|
CriteriaQuery<Long> countQuery = criteriaBuilder.createQuery(Long.class);
|
||||||
|
countQuery.select(criteriaBuilder.count(countQuery.from(MdmLink.class)))
|
||||||
|
.where(finalQuery);
|
||||||
|
|
||||||
|
Long totalResults = myEntityManager.createQuery(countQuery).getSingleResult();
|
||||||
|
|
||||||
|
return new PageImpl<>(typedQuery.setFirstResult(thePageRequest.getOffset()).setMaxResults(thePageRequest.getCount()).getResultList(),
|
||||||
|
PageRequest.of(thePageRequest.getPage(), thePageRequest.getCount()),
|
||||||
|
totalResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,6 +24,7 @@ import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
||||||
|
import ca.uhn.fhir.jpa.mdm.util.MdmPartitionHelper;
|
||||||
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
|
import ca.uhn.fhir.mdm.api.IGoldenResourceMergerSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
|
@ -32,6 +33,7 @@ import ca.uhn.fhir.mdm.log.Logs;
|
||||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
|
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
|
||||||
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||||
|
import ca.uhn.fhir.mdm.util.MessageHelper;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -58,6 +60,10 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
|
||||||
IJpaIdHelperService myIdHelperService;
|
IJpaIdHelperService myIdHelperService;
|
||||||
@Autowired
|
@Autowired
|
||||||
MdmResourceDaoSvc myMdmResourceDaoSvc;
|
MdmResourceDaoSvc myMdmResourceDaoSvc;
|
||||||
|
@Autowired
|
||||||
|
MessageHelper myMessageHelper;
|
||||||
|
@Autowired
|
||||||
|
MdmPartitionHelper myMdmPartitionHelper;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
|
@ -82,6 +88,9 @@ public class GoldenResourceMergerSvcImpl implements IGoldenResourceMergerSvc {
|
||||||
myMdmResourceDaoSvc.upsertGoldenResource(theToGoldenResource, resourceType);
|
myMdmResourceDaoSvc.upsertGoldenResource(theToGoldenResource, resourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if the golden resource and the source resource are in the same partition, throw error if not
|
||||||
|
myMdmPartitionHelper.validateResourcesInSamePartition(theFromGoldenResource, theToGoldenResource);
|
||||||
|
|
||||||
//Merge the links from the FROM to the TO resource. Clean up dangling links.
|
//Merge the links from the FROM to the TO resource. Clean up dangling links.
|
||||||
mergeGoldenResourceLinks(theFromGoldenResource, theToGoldenResource, toGoldenResourcePid, theMdmTransactionContext);
|
mergeGoldenResourceLinks(theFromGoldenResource, theToGoldenResource, toGoldenResourcePid, theMdmTransactionContext);
|
||||||
|
|
||||||
|
|
|
@ -21,12 +21,14 @@ 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.subscription.channel.api.ChannelProducerSettings;
|
import ca.uhn.fhir.jpa.subscription.channel.api.ChannelProducerSettings;
|
||||||
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
|
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmChannelSubmitterSvc;
|
import ca.uhn.fhir.mdm.api.IMdmChannelSubmitterSvc;
|
||||||
import ca.uhn.fhir.mdm.log.Logs;
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -50,7 +52,7 @@ public class MdmChannelSubmitterSvcImpl implements IMdmChannelSubmitterSvc {
|
||||||
@Override
|
@Override
|
||||||
public void submitResourceToMdmChannel(IBaseResource theResource) {
|
public void submitResourceToMdmChannel(IBaseResource theResource) {
|
||||||
ResourceModifiedJsonMessage resourceModifiedJsonMessage = new ResourceModifiedJsonMessage();
|
ResourceModifiedJsonMessage resourceModifiedJsonMessage = new ResourceModifiedJsonMessage();
|
||||||
ResourceModifiedMessage resourceModifiedMessage = new ResourceModifiedMessage(myFhirContext, theResource, ResourceModifiedMessage.OperationTypeEnum.MANUALLY_TRIGGERED);
|
ResourceModifiedMessage resourceModifiedMessage = new ResourceModifiedMessage(myFhirContext, theResource, ResourceModifiedMessage.OperationTypeEnum.MANUALLY_TRIGGERED, null, (RequestPartitionId) theResource.getUserData(Constants.RESOURCE_PARTITION_ID));
|
||||||
resourceModifiedMessage.setOperationType(ResourceModifiedMessage.OperationTypeEnum.MANUALLY_TRIGGERED);
|
resourceModifiedMessage.setOperationType(ResourceModifiedMessage.OperationTypeEnum.MANUALLY_TRIGGERED);
|
||||||
resourceModifiedJsonMessage.setPayload(resourceModifiedMessage);
|
resourceModifiedJsonMessage.setPayload(resourceModifiedMessage);
|
||||||
boolean success = getMdmChannelProducer().send(resourceModifiedJsonMessage);
|
boolean success = getMdmChannelProducer().send(resourceModifiedJsonMessage);
|
||||||
|
|
|
@ -21,6 +21,8 @@ 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.partition.IRequestPartitionHelperSvc;
|
||||||
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;
|
||||||
|
@ -34,6 +36,8 @@ import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
|
||||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
import ca.uhn.fhir.mdm.provider.MdmControllerHelper;
|
import ca.uhn.fhir.mdm.provider.MdmControllerHelper;
|
||||||
import ca.uhn.fhir.mdm.provider.MdmControllerUtil;
|
import ca.uhn.fhir.mdm.provider.MdmControllerUtil;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.provider.MultiUrlProcessor;
|
import ca.uhn.fhir.rest.server.provider.MultiUrlProcessor;
|
||||||
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
@ -47,7 +51,9 @@ import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class acts as a layer between MdmProviders and MDM services to support a REST API that's not a FHIR Operation API.
|
* This class acts as a layer between MdmProviders and MDM services to support a REST API that's not a FHIR Operation API.
|
||||||
|
@ -69,6 +75,8 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
|
||||||
IMdmLinkCreateSvc myIMdmLinkCreateSvc;
|
IMdmLinkCreateSvc myIMdmLinkCreateSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
IMdmBatchJobSubmitterFactory myMdmBatchJobSubmitterFactory;
|
IMdmBatchJobSubmitterFactory myMdmBatchJobSubmitterFactory;
|
||||||
|
@Autowired
|
||||||
|
IRequestPartitionHelperSvc myRequestPartitionHelperSvc;
|
||||||
|
|
||||||
public MdmControllerSvcImpl() {
|
public MdmControllerSvcImpl() {
|
||||||
}
|
}
|
||||||
|
@ -86,11 +94,33 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest) {
|
public Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest) {
|
||||||
|
return queryLinksFromPartitionList(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, theMdmTransactionContext, thePageRequest, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, @Nullable RequestDetails theRequestDetails) {
|
||||||
|
RequestPartitionId theReadPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequest(theRequestDetails, null, null);
|
||||||
|
Page<MdmLinkJson> resultPage;
|
||||||
|
if (theReadPartitionId.hasPartitionIds()) {
|
||||||
|
resultPage = queryLinksFromPartitionList(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, theMdmTransactionContext, thePageRequest, theReadPartitionId.getPartitionIds());
|
||||||
|
validateMdmQueryPermissions(theReadPartitionId, resultPage.getContent(), theRequestDetails);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resultPage = queryLinksFromPartitionList(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, theMdmTransactionContext, thePageRequest, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<MdmLinkJson> queryLinksFromPartitionList(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, @Nullable List<Integer> thePartitionIds) {
|
||||||
IIdType goldenResourceId = MdmControllerUtil.extractGoldenResourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId);
|
IIdType goldenResourceId = MdmControllerUtil.extractGoldenResourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId);
|
||||||
IIdType sourceId = MdmControllerUtil.extractSourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, theSourceResourceId);
|
IIdType sourceId = MdmControllerUtil.extractSourceIdDtOrNull(ProviderConstants.MDM_QUERY_LINKS_RESOURCE_ID, theSourceResourceId);
|
||||||
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
|
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
|
||||||
MdmLinkSourceEnum linkSource = MdmControllerUtil.extractLinkSourceOrNull(theLinkSource);
|
MdmLinkSourceEnum linkSource = MdmControllerUtil.extractLinkSourceOrNull(theLinkSource);
|
||||||
return myMdmLinkQuerySvc.queryLinks(goldenResourceId, sourceId, matchResult, linkSource, theMdmTransactionContext, thePageRequest);
|
|
||||||
|
return myMdmLinkQuerySvc.queryLinks(goldenResourceId, sourceId, matchResult, linkSource, theMdmTransactionContext, thePageRequest, thePartitionIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -98,6 +128,22 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
|
||||||
return myMdmLinkQuerySvc.getDuplicateGoldenResources(theMdmTransactionContext, thePageRequest);
|
return myMdmLinkQuerySvc.getDuplicateGoldenResources(theMdmTransactionContext, thePageRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, RequestDetails theRequestDetails) {
|
||||||
|
Page<MdmLinkJson> resultPage;
|
||||||
|
RequestPartitionId readPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequest(theRequestDetails, null, null);
|
||||||
|
if (readPartitionId.isAllPartitions()){
|
||||||
|
resultPage = myMdmLinkQuerySvc.getDuplicateGoldenResources(theMdmTransactionContext, thePageRequest);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resultPage = myMdmLinkQuerySvc.getDuplicateGoldenResources(theMdmTransactionContext, thePageRequest, readPartitionId.getPartitionIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
validateMdmQueryPermissions(readPartitionId, resultPage.getContent(), theRequestDetails);
|
||||||
|
|
||||||
|
return resultPage;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IAnyResource updateLink(String theGoldenResourceId, String theSourceResourceId, String theMatchResult, MdmTransactionContext theMdmTransactionContext) {
|
public IAnyResource updateLink(String theGoldenResourceId, String theSourceResourceId, String theMatchResult, MdmTransactionContext theMdmTransactionContext) {
|
||||||
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
|
MdmMatchResultEnum matchResult = MdmControllerUtil.extractMatchResultOrNull(theMatchResult);
|
||||||
|
@ -133,4 +179,16 @@ public class MdmControllerSvcImpl implements IMdmControllerSvc {
|
||||||
|
|
||||||
myIMdmLinkUpdaterSvc.notDuplicateGoldenResource(goldenResource, target, theMdmTransactionContext);
|
myIMdmLinkUpdaterSvc.notDuplicateGoldenResource(goldenResource, target, theMdmTransactionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateMdmQueryPermissions(RequestPartitionId theRequestPartitionId, List<MdmLinkJson> theMdmLinkJsonList, RequestDetails theRequestDetails){
|
||||||
|
Set<String> seenResourceTypes = new HashSet<>();
|
||||||
|
for (MdmLinkJson mdmLinkJson : theMdmLinkJsonList){
|
||||||
|
IdDt idDt = new IdDt(mdmLinkJson.getSourceId());
|
||||||
|
|
||||||
|
if (!seenResourceTypes.contains(idDt.getResourceType())){
|
||||||
|
myRequestPartitionHelperSvc.validateHasPartitionPermissions(theRequestDetails, idDt.getResourceType(), theRequestPartitionId);
|
||||||
|
seenResourceTypes.add(idDt.getResourceType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,14 @@ 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.dao.index.IdHelperService;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
||||||
|
import ca.uhn.fhir.jpa.mdm.util.MdmPartitionHelper;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmLinkCreateSvc;
|
import ca.uhn.fhir.mdm.api.IMdmLinkCreateSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
|
@ -33,6 +37,7 @@ import ca.uhn.fhir.mdm.log.Logs;
|
||||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||||
import ca.uhn.fhir.mdm.util.MessageHelper;
|
import ca.uhn.fhir.mdm.util.MessageHelper;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -56,6 +61,8 @@ public class MdmLinkCreateSvcImpl implements IMdmLinkCreateSvc {
|
||||||
IMdmSettings myMdmSettings;
|
IMdmSettings myMdmSettings;
|
||||||
@Autowired
|
@Autowired
|
||||||
MessageHelper myMessageHelper;
|
MessageHelper myMessageHelper;
|
||||||
|
@Autowired
|
||||||
|
MdmPartitionHelper myMdmPartitionHelper;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
|
@ -67,6 +74,9 @@ public class MdmLinkCreateSvcImpl implements IMdmLinkCreateSvc {
|
||||||
Long goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource);
|
Long goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource);
|
||||||
Long targetId = myIdHelperService.getPidOrThrowException(theSourceResource);
|
Long targetId = myIdHelperService.getPidOrThrowException(theSourceResource);
|
||||||
|
|
||||||
|
// check if the golden resource and the source resource are in the same partition, throw error if not
|
||||||
|
myMdmPartitionHelper.validateResourcesInSamePartition(theGoldenResource, theSourceResource);
|
||||||
|
|
||||||
Optional<MdmLink> optionalMdmLink = myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, targetId);
|
Optional<MdmLink> optionalMdmLink = myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, targetId);
|
||||||
if (optionalMdmLink.isPresent()) {
|
if (optionalMdmLink.isPresent()) {
|
||||||
throw new InvalidRequestException(Msg.code(753) + myMessageHelper.getMessageForPresentLink(theGoldenResource, theSourceResource));
|
throw new InvalidRequestException(Msg.code(753) + myMessageHelper.getMessageForPresentLink(theGoldenResource, theSourceResource));
|
||||||
|
@ -84,6 +94,12 @@ public class MdmLinkCreateSvcImpl implements IMdmLinkCreateSvc {
|
||||||
} else {
|
} else {
|
||||||
mdmLink.setMatchResult(theMatchResult);
|
mdmLink.setMatchResult(theMatchResult);
|
||||||
}
|
}
|
||||||
|
// Add partition for the mdm link if it doesn't exist
|
||||||
|
RequestPartitionId goldenResourcePartitionId = (RequestPartitionId) theGoldenResource.getUserData(Constants.RESOURCE_PARTITION_ID);
|
||||||
|
if (goldenResourcePartitionId != null && goldenResourcePartitionId.hasPartitionIds() && goldenResourcePartitionId.getFirstPartitionIdOrNull() != null &&
|
||||||
|
(mdmLink.getPartitionId() == null || mdmLink.getPartitionId().getPartitionId() == null)) {
|
||||||
|
mdmLink.setPartitionId(new PartitionablePartitionId(goldenResourcePartitionId.getFirstPartitionIdOrNull(), goldenResourcePartitionId.getPartitionDate()));
|
||||||
|
}
|
||||||
ourLog.info("Manually creating a " + theGoldenResource.getIdElement().toVersionless() + " to " + theSourceResource.getIdElement().toVersionless() + " mdm link.");
|
ourLog.info("Manually creating a " + theGoldenResource.getIdElement().toVersionless() + " to " + theSourceResource.getIdElement().toVersionless() + " mdm link.");
|
||||||
myMdmLinkDaoSvc.save(mdmLink);
|
myMdmLinkDaoSvc.save(mdmLink);
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.mdm.svc;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
|
import ca.uhn.fhir.mdm.api.IMdmLinkQuerySvc;
|
||||||
|
@ -33,10 +32,11 @@ import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.domain.Example;
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class MdmLinkQuerySvcImplSvc implements IMdmLinkQuerySvc {
|
public class MdmLinkQuerySvcImplSvc implements IMdmLinkQuerySvc {
|
||||||
|
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkQuerySvcImplSvc.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(MdmLinkQuerySvcImplSvc.class);
|
||||||
|
@ -44,44 +44,33 @@ public class MdmLinkQuerySvcImplSvc implements IMdmLinkQuerySvc {
|
||||||
@Autowired
|
@Autowired
|
||||||
MdmLinkDaoSvc myMdmLinkDaoSvc;
|
MdmLinkDaoSvc myMdmLinkDaoSvc;
|
||||||
|
|
||||||
@Autowired
|
|
||||||
IJpaIdHelperService myIdHelperService;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
IMdmModelConverterSvc myMdmModelConverterSvc;
|
IMdmModelConverterSvc myMdmModelConverterSvc;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Deprecated
|
||||||
@Transactional
|
@Transactional
|
||||||
public Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) {
|
public Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) {
|
||||||
Example<MdmLink> exampleLink = exampleLinkFromParameters(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource);
|
return queryLinks(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, theMdmContext, thePageRequest, null);
|
||||||
Page<MdmLink> mdmLinkByExample = myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink, thePageRequest);
|
}
|
||||||
Page<MdmLinkJson> map = mdmLinkByExample.map(myMdmModelConverterSvc::toJson);
|
|
||||||
return map;
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest, List<Integer> thePartitionId) {
|
||||||
|
Page<MdmLink> mdmLinks = myMdmLinkDaoSvc.executeTypedQuery(theGoldenResourceId, theSourceResourceId, theMatchResult, theLinkSource, thePageRequest, thePartitionId);
|
||||||
|
return mdmLinks.map(myMdmModelConverterSvc::toJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) {
|
public Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest) {
|
||||||
Example<MdmLink> exampleLink = exampleLinkFromParameters(null, null, MdmMatchResultEnum.POSSIBLE_DUPLICATE, null);
|
return getDuplicateGoldenResources(theMdmContext, thePageRequest, null);
|
||||||
Page<MdmLink> mdmLinkPage = myMdmLinkDaoSvc.findMdmLinkByExample(exampleLink, thePageRequest);
|
|
||||||
Page<MdmLinkJson> map = mdmLinkPage.map(myMdmModelConverterSvc::toJson);
|
|
||||||
return map;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Example<MdmLink> exampleLinkFromParameters(IIdType theGoldenResourceId, IIdType theSourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource) {
|
@Override
|
||||||
MdmLink mdmLink = myMdmLinkDaoSvc.newMdmLink();
|
@Transactional
|
||||||
if (theGoldenResourceId != null) {
|
public Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest, List<Integer> thePartitionId) {
|
||||||
mdmLink.setGoldenResourcePid(myIdHelperService.getPidOrThrowException(theGoldenResourceId));
|
Page<MdmLink> mdmLinkPage = myMdmLinkDaoSvc.executeTypedQuery(null, null, MdmMatchResultEnum.POSSIBLE_DUPLICATE, null, thePageRequest, thePartitionId);
|
||||||
}
|
return mdmLinkPage.map(myMdmModelConverterSvc::toJson);
|
||||||
if (theSourceId != null) {
|
|
||||||
mdmLink.setSourcePid(myIdHelperService.getPidOrThrowException(theSourceId));
|
|
||||||
}
|
|
||||||
if (theMatchResult != null) {
|
|
||||||
mdmLink.setMatchResult(theMatchResult);
|
|
||||||
}
|
|
||||||
if (theLinkSource != null) {
|
|
||||||
mdmLink.setLinkSource(theLinkSource);
|
|
||||||
}
|
|
||||||
return Example.of(mdmLink);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,12 @@ 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.dao.index.IdHelperService;
|
||||||
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
||||||
|
import ca.uhn.fhir.jpa.mdm.util.MdmPartitionHelper;
|
||||||
|
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
@ -35,6 +41,7 @@ import ca.uhn.fhir.mdm.log.Logs;
|
||||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||||
import ca.uhn.fhir.mdm.util.MessageHelper;
|
import ca.uhn.fhir.mdm.util.MessageHelper;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
@ -67,6 +74,8 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
|
||||||
MessageHelper myMessageHelper;
|
MessageHelper myMessageHelper;
|
||||||
@Autowired
|
@Autowired
|
||||||
IMdmSurvivorshipService myMdmSurvivorshipService;
|
IMdmSurvivorshipService myMdmSurvivorshipService;
|
||||||
|
@Autowired
|
||||||
|
MdmPartitionHelper myMdmPartitionHelper;
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
|
@ -78,6 +87,9 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
|
||||||
Long goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource);
|
Long goldenResourceId = myIdHelperService.getPidOrThrowException(theGoldenResource);
|
||||||
Long targetId = myIdHelperService.getPidOrThrowException(theSourceResource);
|
Long targetId = myIdHelperService.getPidOrThrowException(theSourceResource);
|
||||||
|
|
||||||
|
// check if the golden resource and the source resource are in the same partition, throw error if not
|
||||||
|
myMdmPartitionHelper.validateResourcesInSamePartition(theGoldenResource, theSourceResource);
|
||||||
|
|
||||||
Optional<MdmLink> optionalMdmLink = myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, targetId);
|
Optional<MdmLink> optionalMdmLink = myMdmLinkDaoSvc.getLinkByGoldenResourcePidAndSourceResourcePid(goldenResourceId, targetId);
|
||||||
if (!optionalMdmLink.isPresent()) {
|
if (!optionalMdmLink.isPresent()) {
|
||||||
throw new InvalidRequestException(Msg.code(738) + myMessageHelper.getMessageForNoLink(theGoldenResource, theSourceResource));
|
throw new InvalidRequestException(Msg.code(738) + myMessageHelper.getMessageForNoLink(theGoldenResource, theSourceResource));
|
||||||
|
@ -92,6 +104,13 @@ public class MdmLinkUpdaterSvcImpl implements IMdmLinkUpdaterSvc {
|
||||||
ourLog.info("Manually updating MDM Link for " + theGoldenResource.getIdElement().toVersionless() + ", " + theSourceResource.getIdElement().toVersionless() + " from " + mdmLink.getMatchResult() + " to " + theMatchResult + ".");
|
ourLog.info("Manually updating MDM Link for " + theGoldenResource.getIdElement().toVersionless() + ", " + theSourceResource.getIdElement().toVersionless() + " from " + mdmLink.getMatchResult() + " to " + theMatchResult + ".");
|
||||||
mdmLink.setMatchResult(theMatchResult);
|
mdmLink.setMatchResult(theMatchResult);
|
||||||
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
|
mdmLink.setLinkSource(MdmLinkSourceEnum.MANUAL);
|
||||||
|
|
||||||
|
// Add partition for the mdm link if it doesn't exist
|
||||||
|
RequestPartitionId goldenResourcePartitionId = (RequestPartitionId) theGoldenResource.getUserData(Constants.RESOURCE_PARTITION_ID);
|
||||||
|
if (goldenResourcePartitionId != null && goldenResourcePartitionId.hasPartitionIds() && goldenResourcePartitionId.getFirstPartitionIdOrNull() != null &&
|
||||||
|
(mdmLink.getPartitionId() == null || mdmLink.getPartitionId().getPartitionId() == null)) {
|
||||||
|
mdmLink.setPartitionId(new PartitionablePartitionId(goldenResourcePartitionId.getFirstPartitionIdOrNull(), goldenResourcePartitionId.getPartitionDate()));
|
||||||
|
}
|
||||||
myMdmLinkDaoSvc.save(mdmLink);
|
myMdmLinkDaoSvc.save(mdmLink);
|
||||||
|
|
||||||
if (theMatchResult == MdmMatchResultEnum.MATCH) {
|
if (theMatchResult == MdmMatchResultEnum.MATCH) {
|
||||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.mdm.svc;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc;
|
import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc;
|
||||||
import ca.uhn.fhir.mdm.api.MatchedTarget;
|
import ca.uhn.fhir.mdm.api.MatchedTarget;
|
||||||
import ca.uhn.fhir.mdm.log.Logs;
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
|
@ -49,8 +50,8 @@ public class MdmMatchFinderSvcImpl implements IMdmMatchFinderSvc {
|
||||||
@Override
|
@Override
|
||||||
@Nonnull
|
@Nonnull
|
||||||
@Transactional
|
@Transactional
|
||||||
public List<MatchedTarget> getMatchedTargets(String theResourceType, IAnyResource theResource) {
|
public List<MatchedTarget> getMatchedTargets(String theResourceType, IAnyResource theResource, RequestPartitionId theRequestPartitionId) {
|
||||||
Collection<IAnyResource> targetCandidates = myMdmCandidateSearchSvc.findCandidates(theResourceType, theResource);
|
Collection<IAnyResource> targetCandidates = myMdmCandidateSearchSvc.findCandidates(theResourceType, theResource, theRequestPartitionId);
|
||||||
|
|
||||||
List<MatchedTarget> matches = targetCandidates.stream()
|
List<MatchedTarget> matches = targetCandidates.stream()
|
||||||
.map(candidate -> new MatchedTarget(candidate, myMdmResourceMatcherSvc.getMatchResult(theResource, candidate)))
|
.map(candidate -> new MatchedTarget(candidate, myMdmResourceMatcherSvc.getMatchResult(theResource, candidate)))
|
||||||
|
|
|
@ -20,16 +20,16 @@ package ca.uhn.fhir.jpa.mdm.svc;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateList;
|
||||||
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.MatchedGoldenResourceCandidate;
|
||||||
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
|
||||||
import ca.uhn.fhir.mdm.log.Logs;
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
|
||||||
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
|
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.CandidateList;
|
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmGoldenResourceFindingSvc;
|
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.MatchedGoldenResourceCandidate;
|
|
||||||
import ca.uhn.fhir.rest.server.TransactionLogMessages;
|
import ca.uhn.fhir.rest.server.TransactionLogMessages;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
|
|
@ -20,15 +20,19 @@ package ca.uhn.fhir.jpa.mdm.svc;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
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.model.DaoMethodOutcome;
|
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||||
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
import ca.uhn.fhir.jpa.model.entity.TagTypeEnum;
|
||||||
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
import ca.uhn.fhir.mdm.api.MdmConstants;
|
import ca.uhn.fhir.mdm.api.MdmConstants;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
@ -53,10 +57,11 @@ public class MdmResourceDaoSvc {
|
||||||
|
|
||||||
public DaoMethodOutcome upsertGoldenResource(IAnyResource theGoldenResource, String theResourceType) {
|
public DaoMethodOutcome upsertGoldenResource(IAnyResource theGoldenResource, String theResourceType) {
|
||||||
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType);
|
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType);
|
||||||
|
RequestDetails requestDetails = new SystemRequestDetails().setRequestPartitionId((RequestPartitionId) theGoldenResource.getUserData(Constants.RESOURCE_PARTITION_ID));
|
||||||
if (theGoldenResource.getIdElement().hasIdPart()) {
|
if (theGoldenResource.getIdElement().hasIdPart()) {
|
||||||
return resourceDao.update(theGoldenResource);
|
return resourceDao.update(theGoldenResource, requestDetails);
|
||||||
} else {
|
} else {
|
||||||
return resourceDao.create(theGoldenResource);
|
return resourceDao.create(theGoldenResource, requestDetails);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +73,8 @@ public class MdmResourceDaoSvc {
|
||||||
*/
|
*/
|
||||||
public void removeGoldenResourceTag(IAnyResource theGoldenResource, String theResourcetype) {
|
public void removeGoldenResourceTag(IAnyResource theGoldenResource, String theResourcetype) {
|
||||||
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourcetype);
|
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourcetype);
|
||||||
resourceDao.removeTag(theGoldenResource.getIdElement(), TagTypeEnum.TAG, MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD);
|
RequestDetails requestDetails = new SystemRequestDetails().setRequestPartitionId((RequestPartitionId) theGoldenResource.getUserData(Constants.RESOURCE_PARTITION_ID));
|
||||||
|
resourceDao.removeTag(theGoldenResource.getIdElement(), TagTypeEnum.TAG, MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, MdmConstants.CODE_GOLDEN_RECORD, requestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAnyResource readGoldenResourceByPid(ResourcePersistentId theGoldenResourcePid, String theResourceType) {
|
public IAnyResource readGoldenResourceByPid(ResourcePersistentId theGoldenResourcePid, String theResourceType) {
|
||||||
|
@ -76,12 +82,17 @@ public class MdmResourceDaoSvc {
|
||||||
return (IAnyResource) resourceDao.readByPid(theGoldenResourcePid);
|
return (IAnyResource) resourceDao.readByPid(theGoldenResourcePid);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO GGG MDM address this
|
|
||||||
public Optional<IAnyResource> searchGoldenResourceByEID(String theEid, String theResourceType) {
|
public Optional<IAnyResource> searchGoldenResourceByEID(String theEid, String theResourceType) {
|
||||||
|
return this.searchGoldenResourceByEID(theEid, theResourceType, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<IAnyResource> searchGoldenResourceByEID(String theEid, String theResourceType, RequestPartitionId thePartitionId) {
|
||||||
SearchParameterMap map = buildEidSearchParameterMap(theEid, theResourceType);
|
SearchParameterMap map = buildEidSearchParameterMap(theEid, theResourceType);
|
||||||
|
|
||||||
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType);
|
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theResourceType);
|
||||||
IBundleProvider search = resourceDao.search(map);
|
SystemRequestDetails systemRequestDetails = new SystemRequestDetails();
|
||||||
|
systemRequestDetails.setRequestPartitionId(thePartitionId);
|
||||||
|
IBundleProvider search = resourceDao.search(map, systemRequestDetails);
|
||||||
List<IBaseResource> resources = search.getResources(0, MAX_MATCHING_GOLDEN_RESOURCES);
|
List<IBaseResource> resources = search.getResources(0, MAX_MATCHING_GOLDEN_RESOURCES);
|
||||||
|
|
||||||
if (resources.isEmpty()) {
|
if (resources.isEmpty()) {
|
||||||
|
|
|
@ -27,11 +27,13 @@ import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.IResultIterator;
|
import ca.uhn.fhir.jpa.dao.IResultIterator;
|
||||||
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
import ca.uhn.fhir.jpa.dao.ISearchBuilder;
|
||||||
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
|
import ca.uhn.fhir.jpa.model.search.SearchRuntimeDetails;
|
||||||
|
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmChannelSubmitterSvc;
|
import ca.uhn.fhir.mdm.api.IMdmChannelSubmitterSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
|
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
|
||||||
import ca.uhn.fhir.mdm.log.Logs;
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
@ -42,6 +44,7 @@ import org.slf4j.Logger;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -66,6 +69,9 @@ public class MdmSubmitSvcImpl implements IMdmSubmitSvc {
|
||||||
@Autowired
|
@Autowired
|
||||||
private IMdmSettings myMdmSettings;
|
private IMdmSettings myMdmSettings;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IRequestPartitionHelperSvc myRequestPartitionHelperSvc;
|
||||||
|
|
||||||
public static final int DEFAULT_BUFFER_SIZE = 100;
|
public static final int DEFAULT_BUFFER_SIZE = 100;
|
||||||
|
|
||||||
private int myBufferSize = DEFAULT_BUFFER_SIZE;
|
private int myBufferSize = DEFAULT_BUFFER_SIZE;
|
||||||
|
@ -75,9 +81,9 @@ public class MdmSubmitSvcImpl implements IMdmSubmitSvc {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public long submitAllSourceTypesToMdm(@Nullable String theCriteria) {
|
public long submitAllSourceTypesToMdm(@Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) {
|
||||||
long submittedCount = myMdmSettings.getMdmRules().getMdmTypes().stream()
|
long submittedCount = myMdmSettings.getMdmRules().getMdmTypes().stream()
|
||||||
.mapToLong(type -> submitSourceResourceTypeToMdm(type, theCriteria))
|
.mapToLong(type -> submitSourceResourceTypeToMdm(type, theCriteria, theRequestDetails))
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
return submittedCount;
|
return submittedCount;
|
||||||
|
@ -85,7 +91,7 @@ public class MdmSubmitSvcImpl implements IMdmSubmitSvc {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public long submitSourceResourceTypeToMdm(String theSourceResourceType, @Nullable String theCriteria) {
|
public long submitSourceResourceTypeToMdm(String theSourceResourceType, @Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) {
|
||||||
if (theCriteria == null) {
|
if (theCriteria == null) {
|
||||||
ourLog.info("Submitting all resources of type {} to MDM", theSourceResourceType);
|
ourLog.info("Submitting all resources of type {} to MDM", theSourceResourceType);
|
||||||
} else {
|
} else {
|
||||||
|
@ -97,13 +103,15 @@ public class MdmSubmitSvcImpl implements IMdmSubmitSvc {
|
||||||
spMap.setLoadSynchronous(true);
|
spMap.setLoadSynchronous(true);
|
||||||
spMap.setCount(myBufferSize);
|
spMap.setCount(myBufferSize);
|
||||||
ISearchBuilder searchBuilder = myMdmSearchParamSvc.generateSearchBuilderForType(theSourceResourceType);
|
ISearchBuilder searchBuilder = myMdmSearchParamSvc.generateSearchBuilderForType(theSourceResourceType);
|
||||||
return submitAllMatchingResourcesToMdmChannel(spMap, searchBuilder);
|
|
||||||
|
RequestPartitionId requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequestForSearchType(theRequestDetails, theSourceResourceType, spMap, null);
|
||||||
|
return submitAllMatchingResourcesToMdmChannel(spMap, searchBuilder, requestPartitionId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private long submitAllMatchingResourcesToMdmChannel(SearchParameterMap theSpMap, ISearchBuilder theSearchBuilder) {
|
private long submitAllMatchingResourcesToMdmChannel(SearchParameterMap theSpMap, ISearchBuilder theSearchBuilder, RequestPartitionId theRequestPartitionId) {
|
||||||
SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(null, UUID.randomUUID().toString());
|
SearchRuntimeDetails searchRuntimeDetails = new SearchRuntimeDetails(null, UUID.randomUUID().toString());
|
||||||
long total = 0;
|
long total = 0;
|
||||||
try (IResultIterator query = theSearchBuilder.createQuery(theSpMap, searchRuntimeDetails, null, RequestPartitionId.defaultPartition())) {
|
try (IResultIterator query = theSearchBuilder.createQuery(theSpMap, searchRuntimeDetails, null, theRequestPartitionId)) {
|
||||||
Collection<ResourcePersistentId> pidBatch;
|
Collection<ResourcePersistentId> pidBatch;
|
||||||
do {
|
do {
|
||||||
pidBatch = query.getNextResultBatch(myBufferSize);
|
pidBatch = query.getNextResultBatch(myBufferSize);
|
||||||
|
@ -136,22 +144,22 @@ public class MdmSubmitSvcImpl implements IMdmSubmitSvc {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public long submitPractitionerTypeToMdm(@Nullable String theCriteria) {
|
public long submitPractitionerTypeToMdm(@Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) {
|
||||||
return submitSourceResourceTypeToMdm("Practitioner", theCriteria);
|
return submitSourceResourceTypeToMdm("Practitioner", theCriteria, theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public long submitPatientTypeToMdm(@Nullable String theCriteria) {
|
public long submitPatientTypeToMdm(@Nullable String theCriteria, @Nonnull RequestDetails theRequestDetails) {
|
||||||
return submitSourceResourceTypeToMdm("Patient", theCriteria);
|
return submitSourceResourceTypeToMdm("Patient", theCriteria, theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public long submitSourceResourceToMdm(IIdType theId) {
|
public long submitSourceResourceToMdm(IIdType theId, RequestDetails theRequestDetails) {
|
||||||
validateSourceType(theId.getResourceType());
|
validateSourceType(theId.getResourceType());
|
||||||
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType());
|
IFhirResourceDao resourceDao = myDaoRegistry.getResourceDao(theId.getResourceType());
|
||||||
IBaseResource read = resourceDao.read(theId);
|
IBaseResource read = resourceDao.read(theId, theRequestDetails);
|
||||||
myMdmChannelSubmitterSvc.submitResourceToMdmChannel(read);
|
myMdmChannelSubmitterSvc.submitResourceToMdmChannel(read);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,11 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
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.mdm.svc.MdmSearchParamSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.MdmSearchParamSvc;
|
||||||
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
|
@ -50,20 +52,35 @@ public class CandidateSearcher {
|
||||||
*
|
*
|
||||||
* @param theResourceType the type of resources searched on
|
* @param theResourceType the type of resources searched on
|
||||||
* @param theResourceCriteria the criteria used to search for the candidates
|
* @param theResourceCriteria the criteria used to search for the candidates
|
||||||
|
* @param partitionId the partition for the search
|
||||||
* @return Optional.empty() if >= IMdmSettings.getCandidateSearchLimit() candidates are found, otherwise
|
* @return Optional.empty() if >= IMdmSettings.getCandidateSearchLimit() candidates are found, otherwise
|
||||||
* return the bundle provider for the search results.
|
* return the bundle provider for the search results.
|
||||||
*/
|
*/
|
||||||
public Optional<IBundleProvider> search(String theResourceType, String theResourceCriteria) {
|
public Optional<IBundleProvider> search(String theResourceType, String theResourceCriteria, RequestPartitionId partitionId) {
|
||||||
SearchParameterMap searchParameterMap = myMdmSearchParamSvc.mapFromCriteria(theResourceType, theResourceCriteria);
|
SearchParameterMap searchParameterMap = myMdmSearchParamSvc.mapFromCriteria(theResourceType, theResourceCriteria);
|
||||||
|
|
||||||
searchParameterMap.setLoadSynchronousUpTo(myMdmSettings.getCandidateSearchLimit());
|
searchParameterMap.setLoadSynchronousUpTo(myMdmSettings.getCandidateSearchLimit());
|
||||||
|
|
||||||
IFhirResourceDao<?> resourceDao = myDaoRegistry.getResourceDao(theResourceType);
|
IFhirResourceDao<?> resourceDao = myDaoRegistry.getResourceDao(theResourceType);
|
||||||
IBundleProvider retval = resourceDao.search(searchParameterMap);
|
SystemRequestDetails systemRequestDetails = new SystemRequestDetails();
|
||||||
|
systemRequestDetails.setRequestPartitionId(partitionId);
|
||||||
|
IBundleProvider retval = resourceDao.search(searchParameterMap, systemRequestDetails);
|
||||||
|
|
||||||
if (retval.size() != null && retval.size() >= myMdmSettings.getCandidateSearchLimit()) {
|
if (retval.size() != null && retval.size() >= myMdmSettings.getCandidateSearchLimit()) {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
return Optional.of(retval);
|
return Optional.of(retval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform a search for mdm candidates.
|
||||||
|
*
|
||||||
|
* @param theResourceType the type of resources searched on
|
||||||
|
* @param theResourceCriteria the criteria used to search for the candidates
|
||||||
|
* @return Optional.empty() if >= IMdmSettings.getCandidateSearchLimit() candidates are found, otherwise
|
||||||
|
* return the bundle provider for the search results.
|
||||||
|
*/
|
||||||
|
public Optional<IBundleProvider> search(String theResourceType, String theResourceCriteria) {
|
||||||
|
return this.search(theResourceType, theResourceCriteria, RequestPartitionId.allPartitions());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,11 +20,13 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
|
import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
||||||
import ca.uhn.fhir.mdm.log.Logs;
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
import ca.uhn.fhir.mdm.model.CanonicalEID;
|
import ca.uhn.fhir.mdm.model.CanonicalEID;
|
||||||
import ca.uhn.fhir.mdm.util.EIDHelper;
|
import ca.uhn.fhir.mdm.util.EIDHelper;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.MdmResourceDaoSvc;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -52,7 +54,7 @@ public class FindCandidateByEidSvc extends BaseCandidateFinder {
|
||||||
List<CanonicalEID> eidFromResource = myEIDHelper.getExternalEid(theBaseResource);
|
List<CanonicalEID> eidFromResource = myEIDHelper.getExternalEid(theBaseResource);
|
||||||
if (!eidFromResource.isEmpty()) {
|
if (!eidFromResource.isEmpty()) {
|
||||||
for (CanonicalEID eid : eidFromResource) {
|
for (CanonicalEID eid : eidFromResource) {
|
||||||
Optional<IAnyResource> oFoundGoldenResource = myMdmResourceDaoSvc.searchGoldenResourceByEID(eid.getValue(), theBaseResource.getIdElement().getResourceType());
|
Optional<IAnyResource> oFoundGoldenResource = myMdmResourceDaoSvc.searchGoldenResourceByEID(eid.getValue(), theBaseResource.getIdElement().getResourceType(), (RequestPartitionId) theBaseResource.getUserData(Constants.RESOURCE_PARTITION_ID));
|
||||||
if (oFoundGoldenResource.isPresent()) {
|
if (oFoundGoldenResource.isPresent()) {
|
||||||
IAnyResource foundGoldenResource = oFoundGoldenResource.get();
|
IAnyResource foundGoldenResource = oFoundGoldenResource.get();
|
||||||
Long pidOrNull = myIdHelperService.getPidOrNull(foundGoldenResource);
|
Long pidOrNull = myIdHelperService.getPidOrNull(foundGoldenResource);
|
||||||
|
|
|
@ -21,6 +21,8 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
|
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
import ca.uhn.fhir.jpa.mdm.dao.MdmLinkDaoSvc;
|
||||||
|
@ -28,6 +30,7 @@ import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc;
|
||||||
import ca.uhn.fhir.mdm.api.MatchedTarget;
|
import ca.uhn.fhir.mdm.api.MatchedTarget;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
import ca.uhn.fhir.mdm.log.Logs;
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
@ -66,7 +69,7 @@ public class FindCandidateByExampleSvc extends BaseCandidateFinder {
|
||||||
|
|
||||||
List<Long> goldenResourcePidsToExclude = getNoMatchGoldenResourcePids(theTarget);
|
List<Long> goldenResourcePidsToExclude = getNoMatchGoldenResourcePids(theTarget);
|
||||||
|
|
||||||
List<MatchedTarget> matchedCandidates = myMdmMatchFinderSvc.getMatchedTargets(myFhirContext.getResourceType(theTarget), theTarget);
|
List<MatchedTarget> matchedCandidates = myMdmMatchFinderSvc.getMatchedTargets(myFhirContext.getResourceType(theTarget), theTarget, (RequestPartitionId) theTarget.getUserData(Constants.RESOURCE_PARTITION_ID));
|
||||||
|
|
||||||
// Convert all possible match targets to their equivalent Golden Resources by looking up in the MdmLink table,
|
// Convert all possible match targets to their equivalent Golden Resources by looking up in the MdmLink table,
|
||||||
// while ensuring that the matches aren't in our NO_MATCH list.
|
// while ensuring that the matches aren't in our NO_MATCH list.
|
||||||
|
|
|
@ -20,6 +20,8 @@ package ca.uhn.fhir.jpa.mdm.svc.candidate;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
|
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
|
@ -64,12 +66,14 @@ public class MdmCandidateSearchSvc {
|
||||||
/**
|
/**
|
||||||
* Given a source resource, search for all resources that are considered an MDM match based on defined MDM rules.
|
* Given a source resource, search for all resources that are considered an MDM match based on defined MDM rules.
|
||||||
*
|
*
|
||||||
* @param theResourceType
|
* @param theResourceType the resource type of the resource being matched
|
||||||
* @param theResource the {@link IBaseResource} we are attempting to match.
|
* @param theResource the {@link IBaseResource} we are attempting to match.
|
||||||
|
* @param theRequestPartitionId the {@link RequestPartitionId} representation of the partitions we are limited to when attempting to match
|
||||||
|
*
|
||||||
* @return the list of candidate {@link IBaseResource} which could be matches to theResource
|
* @return the list of candidate {@link IBaseResource} which could be matches to theResource
|
||||||
*/
|
*/
|
||||||
@Transactional
|
@Transactional
|
||||||
public Collection<IAnyResource> findCandidates(String theResourceType, IAnyResource theResource) {
|
public Collection<IAnyResource> findCandidates(String theResourceType, IAnyResource theResource, RequestPartitionId theRequestPartitionId) {
|
||||||
Map<Long, IAnyResource> matchedPidsToResources = new HashMap<>();
|
Map<Long, IAnyResource> matchedPidsToResources = new HashMap<>();
|
||||||
List<MdmFilterSearchParamJson> filterSearchParams = myMdmSettings.getMdmRules().getCandidateFilterSearchParams();
|
List<MdmFilterSearchParamJson> filterSearchParams = myMdmSettings.getMdmRules().getCandidateFilterSearchParams();
|
||||||
List<String> filterCriteria = buildFilterQuery(filterSearchParams, theResourceType);
|
List<String> filterCriteria = buildFilterQuery(filterSearchParams, theResourceType);
|
||||||
|
@ -78,7 +82,7 @@ public class MdmCandidateSearchSvc {
|
||||||
//If there are zero MdmResourceSearchParamJson, we end up only making a single search, otherwise we
|
//If there are zero MdmResourceSearchParamJson, we end up only making a single search, otherwise we
|
||||||
//must perform one search per MdmResourceSearchParamJson.
|
//must perform one search per MdmResourceSearchParamJson.
|
||||||
if (candidateSearchParams.isEmpty()) {
|
if (candidateSearchParams.isEmpty()) {
|
||||||
searchForIdsAndAddToMap(theResourceType, theResource, matchedPidsToResources, filterCriteria, null);
|
searchForIdsAndAddToMap(theResourceType, theResource, matchedPidsToResources, filterCriteria, null, theRequestPartitionId);
|
||||||
} else {
|
} else {
|
||||||
for (MdmResourceSearchParamJson resourceSearchParam : candidateSearchParams) {
|
for (MdmResourceSearchParamJson resourceSearchParam : candidateSearchParams) {
|
||||||
|
|
||||||
|
@ -86,7 +90,7 @@ public class MdmCandidateSearchSvc {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
searchForIdsAndAddToMap(theResourceType, theResource, matchedPidsToResources, filterCriteria, resourceSearchParam);
|
searchForIdsAndAddToMap(theResourceType, theResource, matchedPidsToResources, filterCriteria, resourceSearchParam, theRequestPartitionId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Obviously we don't want to consider the freshly added resource as a potential candidate.
|
//Obviously we don't want to consider the freshly added resource as a potential candidate.
|
||||||
|
@ -113,7 +117,7 @@ public class MdmCandidateSearchSvc {
|
||||||
* 4. Store all results in `theMatchedPidsToResources`
|
* 4. Store all results in `theMatchedPidsToResources`
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
private void searchForIdsAndAddToMap(String theResourceType, IAnyResource theResource, Map<Long, IAnyResource> theMatchedPidsToResources, List<String> theFilterCriteria, MdmResourceSearchParamJson resourceSearchParam) {
|
private void searchForIdsAndAddToMap(String theResourceType, IAnyResource theResource, Map<Long, IAnyResource> theMatchedPidsToResources, List<String> theFilterCriteria, MdmResourceSearchParamJson resourceSearchParam, RequestPartitionId theRequestPartitionId) {
|
||||||
//1.
|
//1.
|
||||||
Optional<String> oResourceCriteria = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString(theResourceType, theResource, theFilterCriteria, resourceSearchParam);
|
Optional<String> oResourceCriteria = myMdmCandidateSearchCriteriaBuilderSvc.buildResourceQueryString(theResourceType, theResource, theFilterCriteria, resourceSearchParam);
|
||||||
if (!oResourceCriteria.isPresent()) {
|
if (!oResourceCriteria.isPresent()) {
|
||||||
|
@ -123,7 +127,7 @@ public class MdmCandidateSearchSvc {
|
||||||
ourLog.debug("Searching for {} candidates with {}", theResourceType, resourceCriteria);
|
ourLog.debug("Searching for {} candidates with {}", theResourceType, resourceCriteria);
|
||||||
|
|
||||||
//2.
|
//2.
|
||||||
Optional<IBundleProvider> bundleProvider = myCandidateSearcher.search(theResourceType, resourceCriteria);
|
Optional<IBundleProvider> bundleProvider = myCandidateSearcher.search(theResourceType, resourceCriteria, theRequestPartitionId);
|
||||||
if (!bundleProvider.isPresent()) {
|
if (!bundleProvider.isPresent()) {
|
||||||
throw new TooManyCandidatesException(Msg.code(762) + "More than " + myMdmSettings.getCandidateSearchLimit() + " candidate matches found for " + resourceCriteria + ". Aborting mdm matching.");
|
throw new TooManyCandidatesException(Msg.code(762) + "More than " + myMdmSettings.getCandidateSearchLimit() + " candidate matches found for " + resourceCriteria + ". Aborting mdm matching.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package ca.uhn.fhir.jpa.mdm.util;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
|
import ca.uhn.fhir.mdm.util.MessageHelper;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MdmPartitionHelper {
|
||||||
|
@Autowired
|
||||||
|
MessageHelper myMessageHelper;
|
||||||
|
|
||||||
|
public void validateResourcesInSamePartition(IAnyResource theFromResource, IAnyResource theToResource){
|
||||||
|
RequestPartitionId fromGoldenResourcePartitionId = (RequestPartitionId) theFromResource.getUserData(Constants.RESOURCE_PARTITION_ID);
|
||||||
|
RequestPartitionId toGoldenPartitionId = (RequestPartitionId) theToResource.getUserData(Constants.RESOURCE_PARTITION_ID);
|
||||||
|
if (fromGoldenResourcePartitionId != null && toGoldenPartitionId != null && fromGoldenResourcePartitionId.hasPartitionIds() && toGoldenPartitionId.hasPartitionIds() &&
|
||||||
|
!fromGoldenResourcePartitionId.hasPartitionId(toGoldenPartitionId.getFirstPartitionIdOrNull())) {
|
||||||
|
throw new InvalidRequestException(Msg.code(2075) + myMessageHelper.getMessageForMismatchPartition(theFromResource, theToResource));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,12 @@
|
||||||
package ca.uhn.fhir.jpa.mdm;
|
package ca.uhn.fhir.jpa.mdm;
|
||||||
|
|
||||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||||
|
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.model.DaoMethodOutcome;
|
import ca.uhn.fhir.jpa.api.model.DaoMethodOutcome;
|
||||||
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
|
import ca.uhn.fhir.jpa.dao.data.IMdmLinkDao;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IJpaIdHelperService;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
|
||||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
import ca.uhn.fhir.jpa.mdm.config.MdmConsumerConfig;
|
import ca.uhn.fhir.jpa.mdm.config.MdmConsumerConfig;
|
||||||
|
@ -20,6 +20,9 @@ import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleLinkedTo;
|
||||||
import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleMatchWith;
|
import ca.uhn.fhir.jpa.mdm.matcher.IsPossibleMatchWith;
|
||||||
import ca.uhn.fhir.jpa.mdm.matcher.IsSameGoldenResourceAs;
|
import ca.uhn.fhir.jpa.mdm.matcher.IsSameGoldenResourceAs;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.MdmMatchLinkSvc;
|
||||||
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
|
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
|
||||||
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
|
import ca.uhn.fhir.jpa.searchparam.registry.SearchParamRegistryImpl;
|
||||||
import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig;
|
import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig;
|
||||||
|
@ -32,6 +35,7 @@ import ca.uhn.fhir.mdm.rules.svc.MdmResourceMatcherSvc;
|
||||||
import ca.uhn.fhir.mdm.util.EIDHelper;
|
import ca.uhn.fhir.mdm.util.EIDHelper;
|
||||||
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
@ -74,6 +78,8 @@ import static org.slf4j.LoggerFactory.getLogger;
|
||||||
@ContextConfiguration(classes = {MdmSubmitterConfig.class, MdmConsumerConfig.class, TestMdmConfigR4.class, SubscriptionProcessorConfig.class})
|
@ContextConfiguration(classes = {MdmSubmitterConfig.class, MdmConsumerConfig.class, TestMdmConfigR4.class, SubscriptionProcessorConfig.class})
|
||||||
abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
|
|
||||||
|
protected static final String PARTITION_1 = "PART-1";
|
||||||
|
protected static final String PARTITION_2 = "PART-2";
|
||||||
public static final String NAME_GIVEN_JANE = "Jane";
|
public static final String NAME_GIVEN_JANE = "Jane";
|
||||||
public static final String NAME_GIVEN_PAUL = "Paul";
|
public static final String NAME_GIVEN_PAUL = "Paul";
|
||||||
public static final String TEST_NAME_FAMILY = "Doe";
|
public static final String TEST_NAME_FAMILY = "Doe";
|
||||||
|
@ -118,6 +124,10 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||||
@Autowired
|
@Autowired
|
||||||
private DaoRegistry myDaoRegistry;
|
private DaoRegistry myDaoRegistry;
|
||||||
|
@Autowired
|
||||||
|
protected PartitionSettings myPartitionSettings;
|
||||||
|
@Autowired
|
||||||
|
protected IPartitionLookupSvc myPartitionLookupSvc;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void beforeSetRequestDetails() {
|
public void beforeSetRequestDetails() {
|
||||||
|
@ -183,8 +193,40 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
protected Medication createMedication(Medication theMedication) {
|
protected Patient createPatientOnPartition(Patient thePatient, boolean theMdmManaged, boolean isRedirect, RequestPartitionId theRequestPartitionId) {
|
||||||
|
if (theMdmManaged) {
|
||||||
|
MdmResourceUtil.setMdmManaged(thePatient);
|
||||||
|
if (isRedirect) {
|
||||||
|
MdmResourceUtil.setGoldenResourceRedirected(thePatient);
|
||||||
|
} else {
|
||||||
|
MdmResourceUtil.setGoldenResource(thePatient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemRequestDetails systemRequestDetails = new SystemRequestDetails();
|
||||||
|
systemRequestDetails.setRequestPartitionId(theRequestPartitionId);
|
||||||
|
DaoMethodOutcome outcome = myPatientDao.create(thePatient, systemRequestDetails);
|
||||||
|
Patient patient = (Patient) outcome.getResource();
|
||||||
|
patient.setId(outcome.getId());
|
||||||
|
patient.setUserData(Constants.RESOURCE_PARTITION_ID, theRequestPartitionId);
|
||||||
|
return patient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
protected Patient createPatientOnPartition(Patient thePatient, RequestPartitionId theRequestPartitionId) {
|
||||||
//Note that since our mdm-rules block on active=true, all patients must be active.
|
//Note that since our mdm-rules block on active=true, all patients must be active.
|
||||||
|
thePatient.setActive(true);
|
||||||
|
|
||||||
|
SystemRequestDetails systemRequestDetails = new SystemRequestDetails();
|
||||||
|
systemRequestDetails.setRequestPartitionId(theRequestPartitionId);
|
||||||
|
DaoMethodOutcome outcome = myPatientDao.create(thePatient, systemRequestDetails);
|
||||||
|
Patient patient = (Patient) outcome.getResource();
|
||||||
|
patient.setId(outcome.getId());
|
||||||
|
return patient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
protected Medication createMedication(Medication theMedication) {
|
||||||
DaoMethodOutcome outcome = myMedicationDao.create(theMedication);
|
DaoMethodOutcome outcome = myMedicationDao.create(theMedication);
|
||||||
Medication medication = (Medication) outcome.getResource();
|
Medication medication = (Medication) outcome.getResource();
|
||||||
medication.setId(outcome.getId());
|
medication.setId(outcome.getId());
|
||||||
|
@ -337,6 +379,13 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
return thePatient;
|
return thePatient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Patient createPatientAndUpdateLinksOnPartition(Patient thePatient, RequestPartitionId theRequestPartitionId) {
|
||||||
|
thePatient = createPatientOnPartition(thePatient, theRequestPartitionId);
|
||||||
|
thePatient.setUserData(Constants.RESOURCE_PARTITION_ID, theRequestPartitionId);
|
||||||
|
myMdmMatchLinkSvc.updateMdmLinksForMdmSource(thePatient, createContextForCreate("Patient"));
|
||||||
|
return thePatient;
|
||||||
|
}
|
||||||
|
|
||||||
protected Medication buildMedication(String theManufacturerReference) {
|
protected Medication buildMedication(String theManufacturerReference) {
|
||||||
Medication medication = new Medication();
|
Medication medication = new Medication();
|
||||||
medication.setManufacturer(new Reference(theManufacturerReference));
|
medication.setManufacturer(new Reference(theManufacturerReference));
|
||||||
|
@ -387,6 +436,17 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
return thePractitioner;
|
return thePractitioner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Practitioner createPractitionerAndUpdateLinksOnPartition(Practitioner thePractitioner, RequestPartitionId theRequestPartitionId) {
|
||||||
|
thePractitioner.setActive(true);
|
||||||
|
SystemRequestDetails systemRequestDetails = new SystemRequestDetails();
|
||||||
|
systemRequestDetails.setRequestPartitionId(theRequestPartitionId);
|
||||||
|
DaoMethodOutcome daoMethodOutcome = myPractitionerDao.create(thePractitioner, systemRequestDetails);
|
||||||
|
thePractitioner.setId(daoMethodOutcome.getId());
|
||||||
|
thePractitioner.setUserData(Constants.RESOURCE_PARTITION_ID, theRequestPartitionId);
|
||||||
|
myMdmMatchLinkSvc.updateMdmLinksForMdmSource(thePractitioner, createContextForCreate("Practitioner"));
|
||||||
|
return thePractitioner;
|
||||||
|
}
|
||||||
|
|
||||||
private Matcher<IAnyResource> wrapMatcherInTransaction(Supplier<Matcher<IAnyResource>> theFunction) {
|
private Matcher<IAnyResource> wrapMatcherInTransaction(Supplier<Matcher<IAnyResource>> theFunction) {
|
||||||
return new Matcher<IAnyResource>() {
|
return new Matcher<IAnyResource>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -458,7 +518,8 @@ abstract public class BaseMdmR4Test extends BaseJpaR4Test {
|
||||||
map.setLoadSynchronous(true);
|
map.setLoadSynchronous(true);
|
||||||
//TODO GGG ensure that this tag search works effectively.
|
//TODO GGG ensure that this tag search works effectively.
|
||||||
map.add("_tag", new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, theCode));
|
map.add("_tag", new TokenParam(MdmConstants.SYSTEM_GOLDEN_RECORD_STATUS, theCode));
|
||||||
IBundleProvider bundle = myPatientDao.search(map);
|
SystemRequestDetails systemRequestDetails = SystemRequestDetails.forAllPartitions();
|
||||||
|
IBundleProvider bundle = myPatientDao.search(map, systemRequestDetails);
|
||||||
return bundle.getResources(0, 999);
|
return bundle.getResources(0, 999);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,40 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.config;
|
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.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.channel.subscription.IChannelNamer;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
|
import ca.uhn.fhir.mdm.rules.json.MdmRulesJson;
|
||||||
|
import ca.uhn.fhir.model.api.IFhirVersion;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import ca.uhn.fhir.util.ExtensionUtil;
|
||||||
|
import ca.uhn.fhir.util.HapiExtensions;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
|
import org.hl7.fhir.r4.model.Extension;
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
import org.hl7.fhir.r4.model.Subscription;
|
import org.hl7.fhir.r4.model.Subscription;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
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 org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
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.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
|
@ -24,6 +45,18 @@ class MdmSubscriptionLoaderTest {
|
||||||
@Mock
|
@Mock
|
||||||
IFhirResourceDao<IBaseResource> mySubscriptionDao;
|
IFhirResourceDao<IBaseResource> mySubscriptionDao;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
DaoRegistry myDaoRegistry;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
IMdmSettings myMdmSettings;
|
||||||
|
|
||||||
|
@Spy
|
||||||
|
FhirContext myFhirContext = FhirContext.forR4Cached();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
IChannelNamer myChannelNamer;
|
||||||
|
|
||||||
@InjectMocks
|
@InjectMocks
|
||||||
MdmSubscriptionLoader mySvc = new MdmSubscriptionLoader();
|
MdmSubscriptionLoader mySvc = new MdmSubscriptionLoader();
|
||||||
|
|
||||||
|
@ -37,9 +70,9 @@ class MdmSubscriptionLoaderTest {
|
||||||
Subscription subscription = new Subscription();
|
Subscription subscription = new Subscription();
|
||||||
IdType id = new IdType("2401");
|
IdType id = new IdType("2401");
|
||||||
subscription.setIdElement(id);
|
subscription.setIdElement(id);
|
||||||
when(mySubscriptionDao.read(id)).thenThrow(new ResourceGoneException(""));
|
when(mySubscriptionDao.read(eq(id), any())).thenThrow(new ResourceGoneException(""));
|
||||||
mySvc.updateIfNotPresent(subscription);
|
mySvc.updateIfNotPresent(subscription);
|
||||||
verify(mySubscriptionDao).update(subscription);
|
verify(mySubscriptionDao).update(eq(subscription), any(RequestDetails.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -47,9 +80,9 @@ class MdmSubscriptionLoaderTest {
|
||||||
Subscription subscription = new Subscription();
|
Subscription subscription = new Subscription();
|
||||||
IdType id = new IdType("2401");
|
IdType id = new IdType("2401");
|
||||||
subscription.setIdElement(id);
|
subscription.setIdElement(id);
|
||||||
when(mySubscriptionDao.read(id)).thenThrow(new ResourceNotFoundException(""));
|
when(mySubscriptionDao.read(eq(id), any())).thenThrow(new ResourceNotFoundException(""));
|
||||||
mySvc.updateIfNotPresent(subscription);
|
mySvc.updateIfNotPresent(subscription);
|
||||||
verify(mySubscriptionDao).update(subscription);
|
verify(mySubscriptionDao).update(eq(subscription), any(RequestDetails.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -57,8 +90,29 @@ class MdmSubscriptionLoaderTest {
|
||||||
Subscription subscription = new Subscription();
|
Subscription subscription = new Subscription();
|
||||||
IdType id = new IdType("2401");
|
IdType id = new IdType("2401");
|
||||||
subscription.setIdElement(id);
|
subscription.setIdElement(id);
|
||||||
when(mySubscriptionDao.read(id)).thenReturn(subscription);
|
when(mySubscriptionDao.read(eq(id), any())).thenReturn(subscription);
|
||||||
mySvc.updateIfNotPresent(subscription);
|
mySvc.updateIfNotPresent(subscription);
|
||||||
verify(mySubscriptionDao, never()).update(any());
|
verify(mySubscriptionDao, never()).update(any(), any(RequestDetails.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDaoUpdateMdmSubscriptions() {
|
||||||
|
MdmRulesJson mdmRulesJson = new MdmRulesJson();
|
||||||
|
mdmRulesJson.setMdmTypes(Arrays.asList("Patient"));
|
||||||
|
when(myMdmSettings.getMdmRules()).thenReturn(mdmRulesJson);
|
||||||
|
when(myChannelNamer.getChannelName(any(), any())).thenReturn("Test");
|
||||||
|
when(myDaoRegistry.getResourceDao(eq("Subscription"))).thenReturn(mySubscriptionDao);
|
||||||
|
when(mySubscriptionDao.read(any(), any())).thenThrow(new ResourceGoneException(""));
|
||||||
|
|
||||||
|
mySvc.daoUpdateMdmSubscriptions();
|
||||||
|
|
||||||
|
ArgumentCaptor<Subscription> subscriptionCaptor = ArgumentCaptor.forClass(Subscription.class);
|
||||||
|
verify(mySubscriptionDao).update(subscriptionCaptor.capture(), any(RequestDetails.class));
|
||||||
|
|
||||||
|
IBaseExtension extension = ExtensionUtil.getExtensionByUrl(subscriptionCaptor.getValue(), HapiExtensions.EXTENSION_SUBSCRIPTION_CROSS_PARTITION);
|
||||||
|
|
||||||
|
assertNotNull(extension);
|
||||||
|
|
||||||
|
assertTrue(((BooleanType)extension.getValue()).booleanValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.mdm.provider;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
@ -59,6 +60,7 @@ public abstract class BaseLinkR4Test extends BaseProviderR4Test {
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void after() throws IOException {
|
public void after() throws IOException {
|
||||||
myDaoConfig.setExpungeEnabled(new DaoConfig().isExpungeEnabled());
|
myDaoConfig.setExpungeEnabled(new DaoConfig().isExpungeEnabled());
|
||||||
|
myPartitionSettings.setPartitioningEnabled(new PartitionSettings().isPartitioningEnabled());
|
||||||
super.after();
|
super.after();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public abstract class BaseProviderR4Test extends BaseMdmR4Test {
|
public abstract class BaseProviderR4Test extends BaseMdmR4Test {
|
||||||
MdmProviderDstu3Plus myMdmProvider;
|
protected MdmProviderDstu3Plus myMdmProvider;
|
||||||
@Autowired
|
@Autowired
|
||||||
private IMdmControllerSvc myMdmControllerSvc;
|
private IMdmControllerSvc myMdmControllerSvc;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.provider;
|
package ca.uhn.fhir.jpa.mdm.provider;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||||
import ca.uhn.fhir.mdm.api.MdmConstants;
|
import ca.uhn.fhir.mdm.api.MdmConstants;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
|
@ -17,7 +20,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.endsWith;
|
import static org.hamcrest.Matchers.endsWith;
|
||||||
import static org.hamcrest.Matchers.startsWith;
|
import static org.hamcrest.Matchers.startsWith;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
public class MdmProviderCreateLinkR4Test extends BaseLinkR4Test {
|
public class MdmProviderCreateLinkR4Test extends BaseLinkR4Test {
|
||||||
|
@ -43,6 +46,53 @@ public class MdmProviderCreateLinkR4Test extends BaseLinkR4Test {
|
||||||
assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult());
|
assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateLinkWithMatchResultOnSamePartition() {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
|
||||||
|
Patient patient = createPatientOnPartition(buildPatientWithNameAndId("PatientGiven", "ID.PatientGiven.123"), true, false, requestPartitionId);
|
||||||
|
StringType patientId = new StringType(patient.getIdElement().getValue());
|
||||||
|
|
||||||
|
Patient sourcePatient = createPatientOnPartition(buildPatientWithNameAndId("SourcePatientGiven", "ID.SourcePatientGiven.123"), true, false, requestPartitionId);
|
||||||
|
StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue());
|
||||||
|
|
||||||
|
myMdmProvider.createLink(sourcePatientId, patientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
assertLinkCount(2);
|
||||||
|
|
||||||
|
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(patient);
|
||||||
|
assertEquals(links.size(), 1);
|
||||||
|
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
|
||||||
|
assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult());
|
||||||
|
assertNotNull(links.get(0).getPartitionId());
|
||||||
|
assertEquals(1, links.get(0).getPartitionId().getPartitionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateLinkWithMatchResultOnDifferentPartitions() {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1);
|
||||||
|
Patient patient = createPatientOnPartition(buildPatientWithNameAndId("PatientGiven", "ID.PatientGiven.123"), true, false, requestPartitionId1);
|
||||||
|
StringType patientId = new StringType(patient.getIdElement().getValue());
|
||||||
|
|
||||||
|
RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2);
|
||||||
|
Patient sourcePatient = createPatientOnPartition(buildPatientWithNameAndId("SourcePatientGiven", "ID.SourcePatientGiven.123"), true, false, requestPartitionId2);
|
||||||
|
StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue());
|
||||||
|
|
||||||
|
try {
|
||||||
|
myMdmProvider.createLink(sourcePatientId, patientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), endsWith("This operation is only available for resources on the same partition."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateLinkWithNullMatchResult() {
|
public void testCreateLinkWithNullMatchResult() {
|
||||||
assertLinkCount(1);
|
assertLinkCount(1);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.provider;
|
package ca.uhn.fhir.jpa.mdm.provider;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.mdm.api.MdmConstants;
|
import ca.uhn.fhir.mdm.api.MdmConstants;
|
||||||
import com.google.common.collect.Ordering;
|
import com.google.common.collect.Ordering;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
@ -40,7 +41,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
|
||||||
Patient createdJane = createPatient(jane);
|
Patient createdJane = createPatient(jane);
|
||||||
Patient newJane = buildJanePatient();
|
Patient newJane = buildJanePatient();
|
||||||
|
|
||||||
Bundle result = (Bundle) myMdmProvider.match(newJane);
|
Bundle result = (Bundle) myMdmProvider.match(newJane, new SystemRequestDetails());
|
||||||
assertEquals(1, result.getEntry().size());
|
assertEquals(1, result.getEntry().size());
|
||||||
|
|
||||||
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
|
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
|
||||||
|
@ -64,7 +65,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
|
||||||
Medication createdMedication = createMedication(medication);
|
Medication createdMedication = createMedication(medication);
|
||||||
Medication newMedication = buildMedication("Organization/mfr");
|
Medication newMedication = buildMedication("Organization/mfr");
|
||||||
|
|
||||||
Bundle result = (Bundle) myMdmProvider.serverMatch(newMedication, new StringType("Medication"));
|
Bundle result = (Bundle) myMdmProvider.serverMatch(newMedication, new StringType("Medication"), new SystemRequestDetails());
|
||||||
assertEquals(1, result.getEntry().size());
|
assertEquals(1, result.getEntry().size());
|
||||||
|
|
||||||
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
|
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
|
||||||
|
@ -89,7 +90,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
|
||||||
Patient createdJane = createPatient(jane);
|
Patient createdJane = createPatient(jane);
|
||||||
Patient newJane = buildJanePatient();
|
Patient newJane = buildJanePatient();
|
||||||
|
|
||||||
Bundle result = (Bundle) myMdmProvider.serverMatch(newJane, new StringType("Patient"));
|
Bundle result = (Bundle) myMdmProvider.serverMatch(newJane, new StringType("Patient"), new SystemRequestDetails());
|
||||||
assertEquals(1, result.getEntry().size());
|
assertEquals(1, result.getEntry().size());
|
||||||
|
|
||||||
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
|
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
|
||||||
|
@ -115,7 +116,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
|
||||||
|
|
||||||
Patient newJane = buildJanePatient();
|
Patient newJane = buildJanePatient();
|
||||||
|
|
||||||
Bundle result = (Bundle) myMdmProvider.match(newJane);
|
Bundle result = (Bundle) myMdmProvider.match(newJane, new SystemRequestDetails());
|
||||||
assertEquals(2, result.getEntry().size());
|
assertEquals(2, result.getEntry().size());
|
||||||
|
|
||||||
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
|
Bundle.BundleEntryComponent entry0 = result.getEntry().get(0);
|
||||||
|
@ -139,7 +140,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
|
||||||
Patient paul = buildPaulPatient();
|
Patient paul = buildPaulPatient();
|
||||||
paul.setActive(true);
|
paul.setActive(true);
|
||||||
|
|
||||||
Bundle result = (Bundle) myMdmProvider.match(paul);
|
Bundle result = (Bundle) myMdmProvider.match(paul, new SystemRequestDetails());
|
||||||
assertEquals(0, result.getEntry().size());
|
assertEquals(0, result.getEntry().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,7 +152,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
|
||||||
Patient createdJane = createPatient(jane);
|
Patient createdJane = createPatient(jane);
|
||||||
Patient newJane = buildJanePatient();
|
Patient newJane = buildJanePatient();
|
||||||
|
|
||||||
Bundle result = (Bundle) myMdmProvider.match(newJane);
|
Bundle result = (Bundle) myMdmProvider.match(newJane, new SystemRequestDetails());
|
||||||
assertEquals(1, result.getEntry().size());
|
assertEquals(1, result.getEntry().size());
|
||||||
assertEquals(createdJane.getId(), result.getEntryFirstRep().getResource().getId());
|
assertEquals(createdJane.getId(), result.getEntryFirstRep().getResource().getId());
|
||||||
}
|
}
|
||||||
|
@ -221,7 +222,7 @@ public class MdmProviderMatchR4Test extends BaseProviderR4Test {
|
||||||
"}";
|
"}";
|
||||||
|
|
||||||
IBaseResource coarseResource = myFhirContext.newJsonParser().parseResource(coarsePatient);
|
IBaseResource coarseResource = myFhirContext.newJsonParser().parseResource(coarsePatient);
|
||||||
Bundle result = (Bundle) myMdmProvider.match((Patient) coarseResource);
|
Bundle result = (Bundle) myMdmProvider.match((Patient) coarseResource, new SystemRequestDetails());
|
||||||
assertEquals(1, result.getEntry().size());
|
assertEquals(1, result.getEntry().size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.provider;
|
package ca.uhn.fhir.jpa.mdm.provider;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||||
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
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.mdm.util.MdmResourceUtil;
|
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||||
|
@ -10,15 +13,19 @@ import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.util.TerserUtil;
|
import ca.uhn.fhir.util.TerserUtil;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.endsWith;
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
import static org.hamcrest.Matchers.hasSize;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
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.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
@ -40,6 +47,11 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
|
||||||
myToGoldenPatientId = new StringType(myToGoldenPatient.getIdElement().getValue());
|
myToGoldenPatientId = new StringType(myToGoldenPatient.getIdElement().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void after() throws IOException {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(new PartitionSettings().isPartitioningEnabled());
|
||||||
|
super.after();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMergeWithOverride() {
|
public void testMergeWithOverride() {
|
||||||
|
@ -69,7 +81,7 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
|
||||||
|
|
||||||
// we do not check setActive anymore - as not all types support that
|
// we do not check setActive anymore - as not all types support that
|
||||||
assertTrue(MdmResourceUtil.isGoldenRecord(mergedSourcePatient));
|
assertTrue(MdmResourceUtil.isGoldenRecord(mergedSourcePatient));
|
||||||
assertTrue(!MdmResourceUtil.isGoldenRecordRedirected(mergedSourcePatient));
|
assertFalse(MdmResourceUtil.isGoldenRecordRedirected(mergedSourcePatient));
|
||||||
|
|
||||||
assertEquals(myToGoldenPatient.getIdElement(), mergedSourcePatient.getIdElement());
|
assertEquals(myToGoldenPatient.getIdElement(), mergedSourcePatient.getIdElement());
|
||||||
assertThat(mergedSourcePatient, is(sameGoldenResourceAs(myToGoldenPatient)));
|
assertThat(mergedSourcePatient, is(sameGoldenResourceAs(myToGoldenPatient)));
|
||||||
|
@ -78,7 +90,7 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
|
||||||
|
|
||||||
Patient fromSourcePatient = myPatientDao.read(myFromGoldenPatient.getIdElement().toUnqualifiedVersionless());
|
Patient fromSourcePatient = myPatientDao.read(myFromGoldenPatient.getIdElement().toUnqualifiedVersionless());
|
||||||
|
|
||||||
assertTrue(!MdmResourceUtil.isGoldenRecord(fromSourcePatient));
|
assertFalse(MdmResourceUtil.isGoldenRecord(fromSourcePatient));
|
||||||
assertTrue(MdmResourceUtil.isGoldenRecordRedirected(fromSourcePatient));
|
assertTrue(MdmResourceUtil.isGoldenRecordRedirected(fromSourcePatient));
|
||||||
|
|
||||||
//TODO GGG eventually this will need to check a redirect... this is a hack which doesnt work
|
//TODO GGG eventually this will need to check a redirect... this is a hack which doesnt work
|
||||||
|
@ -95,6 +107,58 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
|
||||||
assertEquals(link.getLinkSource(), MdmLinkSourceEnum.MANUAL);
|
assertEquals(link.getLinkSource(), MdmLinkSourceEnum.MANUAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMergeOnSamePartition() {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||||
|
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
|
||||||
|
Patient fromGoldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId);
|
||||||
|
StringType fromGoldenPatientId = new StringType(fromGoldenPatient.getIdElement().getValue());
|
||||||
|
Patient toGoldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId);
|
||||||
|
StringType toGoldenPatientId = new StringType(toGoldenPatient.getIdElement().getValue());
|
||||||
|
|
||||||
|
Patient mergedSourcePatient = (Patient) myMdmProvider.mergeGoldenResources(fromGoldenPatientId,
|
||||||
|
toGoldenPatientId, null, myRequestDetails);
|
||||||
|
|
||||||
|
assertTrue(MdmResourceUtil.isGoldenRecord(mergedSourcePatient));
|
||||||
|
assertFalse(MdmResourceUtil.isGoldenRecordRedirected(mergedSourcePatient));
|
||||||
|
|
||||||
|
assertEquals(toGoldenPatient.getIdElement(), mergedSourcePatient.getIdElement());
|
||||||
|
assertThat(mergedSourcePatient, is(sameGoldenResourceAs(toGoldenPatient)));
|
||||||
|
assertEquals(1, getAllRedirectedGoldenPatients().size());
|
||||||
|
// 2 from the set-up and only one from this test should be golden resource
|
||||||
|
assertEquals(3, getAllGoldenPatients().size());
|
||||||
|
|
||||||
|
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(fromGoldenPatient);
|
||||||
|
assertThat(links, hasSize(1));
|
||||||
|
|
||||||
|
MdmLink link = links.get(0);
|
||||||
|
assertEquals(link.getSourcePid(), fromGoldenPatient.getIdElement().toUnqualifiedVersionless().getIdPartAsLong());
|
||||||
|
assertEquals(link.getGoldenResourcePid(), toGoldenPatient.getIdElement().toUnqualifiedVersionless().getIdPartAsLong());
|
||||||
|
assertEquals(link.getMatchResult(), MdmMatchResultEnum.REDIRECT);
|
||||||
|
assertEquals(link.getLinkSource(), MdmLinkSourceEnum.MANUAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMergeOnDifferentPartitions() {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||||
|
RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
|
||||||
|
RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2);
|
||||||
|
Patient fromGoldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId1);
|
||||||
|
StringType fromGoldenPatientId = new StringType(fromGoldenPatient.getIdElement().getValue());
|
||||||
|
Patient toGoldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId2);
|
||||||
|
StringType toGoldenPatientId = new StringType(toGoldenPatient.getIdElement().getValue());
|
||||||
|
|
||||||
|
try {
|
||||||
|
myMdmProvider.mergeGoldenResources(fromGoldenPatientId, toGoldenPatientId, null, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), endsWith("This operation is only available for resources on the same partition."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMergeWithManualOverride() {
|
public void testMergeWithManualOverride() {
|
||||||
Patient patient = TerserUtil.clone(myFhirContext, myFromGoldenPatient);
|
Patient patient = TerserUtil.clone(myFhirContext, myFromGoldenPatient);
|
||||||
|
@ -109,7 +173,7 @@ public class MdmProviderMergeGoldenResourcesR4Test extends BaseProviderR4Test {
|
||||||
assertEquals(1, getAllGoldenPatients().size());
|
assertEquals(1, getAllGoldenPatients().size());
|
||||||
|
|
||||||
Patient fromSourcePatient = myPatientDao.read(myFromGoldenPatient.getIdElement().toUnqualifiedVersionless());
|
Patient fromSourcePatient = myPatientDao.read(myFromGoldenPatient.getIdElement().toUnqualifiedVersionless());
|
||||||
assertTrue(!MdmResourceUtil.isGoldenRecord(fromSourcePatient));
|
assertFalse(MdmResourceUtil.isGoldenRecord(fromSourcePatient));
|
||||||
assertTrue(MdmResourceUtil.isGoldenRecordRedirected(fromSourcePatient));
|
assertTrue(MdmResourceUtil.isGoldenRecordRedirected(fromSourcePatient));
|
||||||
|
|
||||||
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(myFromGoldenPatient);
|
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(myFromGoldenPatient);
|
||||||
|
|
|
@ -0,0 +1,125 @@
|
||||||
|
package ca.uhn.fhir.jpa.mdm.provider;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
||||||
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
|
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
||||||
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
|
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.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
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.fail;
|
||||||
|
|
||||||
|
public class MdmProviderNotDuplicateGoldenResourceR4Test extends BaseProviderR4Test {
|
||||||
|
@Autowired
|
||||||
|
IMdmLinkSvc myMdmLinkSvc;
|
||||||
|
private Patient myGoldenPatient;
|
||||||
|
private StringType myGoldenPatientId;
|
||||||
|
private Patient myTargetPatient;
|
||||||
|
private StringType myTargetPatientId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
super.before();
|
||||||
|
|
||||||
|
myGoldenPatient = createGoldenPatient();
|
||||||
|
myGoldenPatientId = new StringType(myGoldenPatient.getIdElement().getValue());
|
||||||
|
myTargetPatient = createGoldenPatient();
|
||||||
|
myTargetPatientId = new StringType(myTargetPatient.getIdElement().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void after() throws IOException {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(false);
|
||||||
|
super.after();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotDuplicateGoldenResource() {
|
||||||
|
myMdmLinkSvc.updateLink(myGoldenPatient, myTargetPatient, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
|
||||||
|
assertLinkCount(1);
|
||||||
|
myMdmProvider.notDuplicate(myGoldenPatientId, myTargetPatientId, myRequestDetails);
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(myTargetPatient);
|
||||||
|
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
|
||||||
|
assertEquals(MdmMatchResultEnum.NO_MATCH, links.get(0).getMatchResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotDuplicateGoldenResourceNoLinkBetweenResources() {
|
||||||
|
try {
|
||||||
|
myMdmProvider.notDuplicate(myGoldenPatientId, myTargetPatientId, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), startsWith("HAPI-0745: No link exists between"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotDuplicateGoldenResourceNotPossibleDuplicate() {
|
||||||
|
myMdmLinkSvc.updateLink(myGoldenPatient, myTargetPatient, MdmMatchOutcome.POSSIBLE_MATCH, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
|
||||||
|
assertLinkCount(1);
|
||||||
|
try {
|
||||||
|
myMdmProvider.notDuplicate(myGoldenPatientId, myTargetPatientId, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), endsWith("are not linked as POSSIBLE_DUPLICATE."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotDuplicateGoldenResourceOnSamePartition() {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||||
|
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
|
||||||
|
Patient goldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId);
|
||||||
|
StringType goldenPatientId = new StringType(goldenPatient.getIdElement().getValue());
|
||||||
|
Patient targetPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId);
|
||||||
|
StringType targetPatientId = new StringType(targetPatient.getIdElement().getValue());
|
||||||
|
|
||||||
|
myMdmLinkSvc.updateLink(goldenPatient, targetPatient, MdmMatchOutcome.POSSIBLE_DUPLICATE, MdmLinkSourceEnum.AUTO, createContextForCreate("Patient"));
|
||||||
|
assertLinkCount(1);
|
||||||
|
myMdmProvider.notDuplicate(goldenPatientId, targetPatientId, myRequestDetails);
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
List<MdmLink> links = myMdmLinkDaoSvc.findMdmLinksBySourceResource(targetPatient);
|
||||||
|
assertEquals(MdmLinkSourceEnum.MANUAL, links.get(0).getLinkSource());
|
||||||
|
assertEquals(MdmMatchResultEnum.NO_MATCH, links.get(0).getMatchResult());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotDuplicateGoldenResourceOnDifferentPartitions() {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||||
|
RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
|
||||||
|
RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2);
|
||||||
|
Patient goldenPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId1);
|
||||||
|
StringType goldenPatientId = new StringType(goldenPatient.getIdElement().getValue());
|
||||||
|
Patient targetPatient = createPatientOnPartition(new Patient(), true, false, requestPartitionId2);
|
||||||
|
StringType targetPatientId = new StringType(targetPatient.getIdElement().getValue());
|
||||||
|
|
||||||
|
try {
|
||||||
|
myMdmProvider.notDuplicate(goldenPatientId, targetPatientId, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), startsWith("HAPI-0745: No link exists between"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,9 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.provider;
|
package ca.uhn.fhir.jpa.mdm.provider;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||||
import ca.uhn.fhir.mdm.api.MdmConstants;
|
import ca.uhn.fhir.mdm.api.MdmConstants;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
|
@ -54,6 +56,52 @@ public class MdmProviderUpdateLinkR4Test extends BaseLinkR4Test {
|
||||||
assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult());
|
assertEquals(MdmMatchResultEnum.MATCH, links.get(0).getMatchResult());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateLinkMatchOnSamePartition() {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||||
|
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
|
||||||
|
Patient patient = createPatientAndUpdateLinksOnPartition(buildFrankPatient(), requestPartitionId);
|
||||||
|
StringType patientId = new StringType(patient.getIdElement().getValue());
|
||||||
|
|
||||||
|
Patient sourcePatient = getGoldenResourceFromTargetResource(patient);
|
||||||
|
StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue());
|
||||||
|
MdmLink link = myMdmLinkDaoSvc.findMdmLinkBySource(patient).get();
|
||||||
|
link.setMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH);
|
||||||
|
saveLink(link);
|
||||||
|
assertEquals(MdmLinkSourceEnum.AUTO, link.getLinkSource());
|
||||||
|
assertLinkCount(2);
|
||||||
|
myMdmProvider.updateLink(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());
|
||||||
|
assertNotNull(links.get(0).getPartitionId());
|
||||||
|
assertEquals(1, links.get(0).getPartitionId().getPartitionId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateLinkMatchOnDifferentPartitions() {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
|
||||||
|
RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1);
|
||||||
|
RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2);
|
||||||
|
Patient patient = createPatientOnPartition(buildFrankPatient(), true, false, requestPartitionId1);
|
||||||
|
StringType patientId = new StringType(patient.getIdElement().getValue());
|
||||||
|
|
||||||
|
Patient sourcePatient = createPatientOnPartition(buildJanePatient(), true, false, requestPartitionId2);
|
||||||
|
StringType sourcePatientId = new StringType(sourcePatient.getIdElement().getValue());
|
||||||
|
assertLinkCount(1);
|
||||||
|
try {
|
||||||
|
myMdmProvider.updateLink(sourcePatientId, patientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), endsWith("This operation is only available for resources on the same partition."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateLinkTwiceFailsDueToWrongVersion() {
|
public void testUpdateLinkTwiceFailsDueToWrongVersion() {
|
||||||
myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
|
myMdmProvider.updateLink(mySourcePatientId, myPatientId, MATCH_RESULT, myRequestDetails);
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.mdm.svc;
|
||||||
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||||
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
||||||
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
|
import ca.uhn.fhir.mdm.api.IMdmSubmitSvc;
|
||||||
import ca.uhn.test.concurrency.PointcutLatch;
|
import ca.uhn.test.concurrency.PointcutLatch;
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
@ -56,7 +57,7 @@ class MdmBatchSvcImplIT extends BaseMdmR4Test {
|
||||||
assertLinkCount(0);
|
assertLinkCount(0);
|
||||||
|
|
||||||
//SUT
|
//SUT
|
||||||
afterMdmLatch.runWithExpectedCount(30, () -> myMdmSubmitSvc.submitAllSourceTypesToMdm(null));
|
afterMdmLatch.runWithExpectedCount(30, () -> myMdmSubmitSvc.submitAllSourceTypesToMdm(null, SystemRequestDetails.forAllPartitions()));
|
||||||
|
|
||||||
assertLinkCount(30);
|
assertLinkCount(30);
|
||||||
}
|
}
|
||||||
|
@ -72,7 +73,7 @@ class MdmBatchSvcImplIT extends BaseMdmR4Test {
|
||||||
|
|
||||||
//SUT
|
//SUT
|
||||||
myMdmSubmitSvc.setBufferSize(5);
|
myMdmSubmitSvc.setBufferSize(5);
|
||||||
afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Patient", null));
|
afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Patient", null, SystemRequestDetails.newSystemRequestAllPartitions()));
|
||||||
|
|
||||||
assertLinkCount(10);
|
assertLinkCount(10);
|
||||||
}
|
}
|
||||||
|
@ -89,7 +90,7 @@ class MdmBatchSvcImplIT extends BaseMdmR4Test {
|
||||||
assertLinkCount(0);
|
assertLinkCount(0);
|
||||||
|
|
||||||
//SUT
|
//SUT
|
||||||
afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Medication", null));
|
afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Medication", null, SystemRequestDetails.newSystemRequestAllPartitions()));
|
||||||
|
|
||||||
assertLinkCount(10);
|
assertLinkCount(10);
|
||||||
}
|
}
|
||||||
|
@ -104,7 +105,7 @@ class MdmBatchSvcImplIT extends BaseMdmR4Test {
|
||||||
assertLinkCount(0);
|
assertLinkCount(0);
|
||||||
|
|
||||||
//SUT
|
//SUT
|
||||||
afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitAllSourceTypesToMdm(null));
|
afterMdmLatch.runWithExpectedCount(10, () -> myMdmSubmitSvc.submitAllSourceTypesToMdm(null, SystemRequestDetails.newSystemRequestAllPartitions()));
|
||||||
|
|
||||||
assertLinkCount(10);
|
assertLinkCount(10);
|
||||||
}
|
}
|
||||||
|
@ -117,7 +118,7 @@ class MdmBatchSvcImplIT extends BaseMdmR4Test {
|
||||||
assertLinkCount(0);
|
assertLinkCount(0);
|
||||||
|
|
||||||
//SUT
|
//SUT
|
||||||
afterMdmLatch.runWithExpectedCount(1, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Patient", "Patient?name=gary"));
|
afterMdmLatch.runWithExpectedCount(1, () -> myMdmSubmitSvc.submitSourceResourceTypeToMdm("Patient", "Patient?name=gary", SystemRequestDetails.newSystemRequestAllPartitions()));
|
||||||
|
|
||||||
assertLinkCount(1);
|
assertLinkCount(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.svc;
|
package ca.uhn.fhir.jpa.mdm.svc;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.MdmCandidateSearchSvc;
|
||||||
import ca.uhn.fhir.jpa.mdm.svc.candidate.TooManyCandidatesException;
|
import ca.uhn.fhir.jpa.mdm.svc.candidate.TooManyCandidatesException;
|
||||||
|
@ -38,7 +39,7 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test {
|
||||||
createActivePatient();
|
createActivePatient();
|
||||||
Patient newJane = buildJanePatient();
|
Patient newJane = buildJanePatient();
|
||||||
|
|
||||||
Collection<IAnyResource> result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane);
|
Collection<IAnyResource> result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions());
|
||||||
assertEquals(1, result.size());
|
assertEquals(1, result.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test {
|
||||||
|
|
||||||
Patient newJane = buildJaneWithBirthday(today);
|
Patient newJane = buildJaneWithBirthday(today);
|
||||||
|
|
||||||
Collection<IAnyResource> result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane);
|
Collection<IAnyResource> result = myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions());
|
||||||
assertEquals(1, result.size());
|
assertEquals(1, result.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +72,7 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test {
|
||||||
incomingPatient.setActive(true);
|
incomingPatient.setActive(true);
|
||||||
incomingPatient.setGeneralPractitioner(Collections.singletonList(new Reference(practitionerAndUpdateLinks.getId())));
|
incomingPatient.setGeneralPractitioner(Collections.singletonList(new Reference(practitionerAndUpdateLinks.getId())));
|
||||||
|
|
||||||
Collection<IAnyResource> patient = myMdmCandidateSearchSvc.findCandidates("Patient", incomingPatient);
|
Collection<IAnyResource> patient = myMdmCandidateSearchSvc.findCandidates("Patient", incomingPatient, RequestPartitionId.allPartitions());
|
||||||
assertThat(patient, hasSize(1));
|
assertThat(patient, hasSize(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,13 +83,13 @@ public class MdmCandidateSearchSvcIT extends BaseMdmR4Test {
|
||||||
Patient newJane = buildJanePatient();
|
Patient newJane = buildJanePatient();
|
||||||
|
|
||||||
createActivePatient();
|
createActivePatient();
|
||||||
assertEquals(1, runInTransaction(()->myMdmCandidateSearchSvc.findCandidates("Patient", newJane).size()));
|
assertEquals(1, runInTransaction(()->myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions()).size()));
|
||||||
createActivePatient();
|
createActivePatient();
|
||||||
assertEquals(2, runInTransaction(()->myMdmCandidateSearchSvc.findCandidates("Patient", newJane).size()));
|
assertEquals(2, runInTransaction(()->myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions()).size()));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
createActivePatient();
|
createActivePatient();
|
||||||
myMdmCandidateSearchSvc.findCandidates("Patient", newJane);
|
myMdmCandidateSearchSvc.findCandidates("Patient", newJane, RequestPartitionId.allPartitions());
|
||||||
fail();
|
fail();
|
||||||
} catch (TooManyCandidatesException e) {
|
} catch (TooManyCandidatesException e) {
|
||||||
assertEquals("More than 3 candidate matches found for Patient?identifier=http%3A%2F%2Fa.tv%2F%7CID.JANE.123&active=true. Aborting mdm matching.", e.getMessage());
|
assertEquals("More than 3 candidate matches found for Patient?identifier=http%3A%2F%2Fa.tv%2F%7CID.JANE.123&active=true. Aborting mdm matching.", e.getMessage());
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
package ca.uhn.fhir.jpa.mdm.svc;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.api.IInterceptorService;
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||||
|
import ca.uhn.fhir.jpa.mdm.provider.BaseLinkR4Test;
|
||||||
|
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||||
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmControllerSvc;
|
||||||
|
import ca.uhn.fhir.mdm.api.MdmLinkJson;
|
||||||
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
|
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
|
||||||
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
|
import ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor;
|
||||||
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
|
import ca.uhn.fhir.test.utilities.BatchJobHelper;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
import org.hl7.fhir.r4.model.DecimalType;
|
||||||
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentMatcher;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.springframework.batch.core.ExitStatus;
|
||||||
|
import org.springframework.batch.core.JobExecution;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.test.mock.mockito.SpyBean;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.mdm.provider.MdmProviderDstu3Plus.DEFAULT_PAGE_SIZE;
|
||||||
|
import static ca.uhn.fhir.mdm.provider.MdmProviderDstu3Plus.MAX_PAGE_SIZE;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
|
||||||
|
public class MdmControllerSvcImplTest extends BaseLinkR4Test {
|
||||||
|
@Autowired
|
||||||
|
IMdmControllerSvc myMdmControllerSvc;
|
||||||
|
|
||||||
|
@SpyBean
|
||||||
|
@Autowired
|
||||||
|
IRequestPartitionHelperSvc myRequestPartitionHelperSvc;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IInterceptorService myInterceptorService;
|
||||||
|
@Autowired
|
||||||
|
private BatchJobHelper myBatchJobHelper;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void before() {
|
||||||
|
super.before();
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
|
||||||
|
myInterceptorService.registerInterceptor(new RequestTenantPartitionInterceptor());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSurvivorshipIsCalledOnMatchingToTheSameGoldenResource() {
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
|
||||||
|
|
||||||
|
Patient patient = createPatientAndUpdateLinksOnPartition(buildFrankPatient(), requestPartitionId);
|
||||||
|
|
||||||
|
getGoldenResourceFromTargetResource(patient);
|
||||||
|
|
||||||
|
MdmLink link = myMdmLinkDaoSvc.findMdmLinkBySource(patient).get();
|
||||||
|
link.setMatchResult(MdmMatchResultEnum.POSSIBLE_MATCH);
|
||||||
|
saveLink(link);
|
||||||
|
assertEquals(MdmLinkSourceEnum.AUTO, link.getLinkSource());
|
||||||
|
assertLinkCount(2);
|
||||||
|
|
||||||
|
Page<MdmLinkJson> resultPage = myMdmControllerSvc.queryLinks(null, myPatientId.getIdElement().getValue(), null, null,
|
||||||
|
new MdmTransactionContext(MdmTransactionContext.OperationType.QUERY_LINKS),
|
||||||
|
new MdmPageRequest((Integer) null, null, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE),
|
||||||
|
new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.fromPartitionId(1)));
|
||||||
|
|
||||||
|
assertEquals(resultPage.getContent().size(), 1);
|
||||||
|
|
||||||
|
assertEquals(resultPage.getContent().get(0).getSourceId(), patient.getIdElement().getResourceType() + "/" + patient.getIdElement().getIdPart());
|
||||||
|
|
||||||
|
Mockito.verify(myRequestPartitionHelperSvc, Mockito.atLeastOnce()).validateHasPartitionPermissions(any(), eq("Patient"), argThat(new PartitionIdMatcher(requestPartitionId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMdmDuplicateGoldenResource() {
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
|
||||||
|
|
||||||
|
Patient patient = createPatientAndUpdateLinksOnPartition(buildFrankPatient(), requestPartitionId);
|
||||||
|
|
||||||
|
getGoldenResourceFromTargetResource(patient);
|
||||||
|
|
||||||
|
MdmLink link = myMdmLinkDaoSvc.findMdmLinkBySource(patient).get();
|
||||||
|
link.setMatchResult(MdmMatchResultEnum.POSSIBLE_DUPLICATE);
|
||||||
|
saveLink(link);
|
||||||
|
assertEquals(MdmLinkSourceEnum.AUTO, link.getLinkSource());
|
||||||
|
assertLinkCount(2);
|
||||||
|
|
||||||
|
Page<MdmLinkJson> resultPage = myMdmControllerSvc.getDuplicateGoldenResources(null,
|
||||||
|
new MdmPageRequest((Integer) null, null, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE),
|
||||||
|
new SystemRequestDetails().setRequestPartitionId(RequestPartitionId.fromPartitionId(1)));
|
||||||
|
|
||||||
|
assertEquals(resultPage.getContent().size(), 1);
|
||||||
|
|
||||||
|
assertEquals(resultPage.getContent().get(0).getSourceId(), patient.getIdElement().getResourceType() + "/" + patient.getIdElement().getIdPart());
|
||||||
|
|
||||||
|
Mockito.verify(myRequestPartitionHelperSvc, Mockito.atLeastOnce()).validateHasPartitionPermissions(any(), eq("Patient"), argThat(new PartitionIdMatcher(requestPartitionId)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMdmClearWithProvidedResources() {
|
||||||
|
assertLinkCount(1);
|
||||||
|
|
||||||
|
RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1);
|
||||||
|
RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2);
|
||||||
|
createPractitionerAndUpdateLinksOnPartition(buildJanePractitioner(), requestPartitionId1);
|
||||||
|
createPractitionerAndUpdateLinksOnPartition(buildJanePractitioner(), requestPartitionId2);
|
||||||
|
assertLinkCount(3);
|
||||||
|
|
||||||
|
List<String> urls = new ArrayList<>();
|
||||||
|
urls.add("Practitioner?");
|
||||||
|
IPrimitiveType<BigDecimal> batchSize = new DecimalType(new BigDecimal(100));
|
||||||
|
ServletRequestDetails details = new ServletRequestDetails();
|
||||||
|
details.setTenantId(PARTITION_2);
|
||||||
|
IBaseParameters clearJob = myMdmControllerSvc.submitMdmClearJob(urls, batchSize, details);
|
||||||
|
Long jobId = Long.valueOf(((DecimalType) ((Parameters) clearJob).getParameter("jobId")).getValueAsString());
|
||||||
|
JobExecution jobExecution = myBatchJobHelper.awaitJobExecution(jobId);
|
||||||
|
assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
|
||||||
|
|
||||||
|
assertLinkCount(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PartitionIdMatcher implements ArgumentMatcher<RequestPartitionId> {
|
||||||
|
private RequestPartitionId myRequestPartitionId;
|
||||||
|
|
||||||
|
PartitionIdMatcher(RequestPartitionId theRequestPartitionId) {
|
||||||
|
myRequestPartitionId = theRequestPartitionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(RequestPartitionId theRequestPartitionId) {
|
||||||
|
return myRequestPartitionId.getPartitionIds().equals(theRequestPartitionId.getPartitionIds());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.svc;
|
package ca.uhn.fhir.jpa.mdm.svc;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.dao.expunge.ExpungeEverythingService;
|
import ca.uhn.fhir.jpa.dao.expunge.ExpungeEverythingService;
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
import ca.uhn.fhir.jpa.entity.MdmLink;
|
||||||
|
@ -8,6 +9,7 @@ import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
|
@ -182,4 +184,19 @@ public class MdmLinkSvcTest extends BaseMdmR4Test {
|
||||||
|
|
||||||
assertThat(actual, Matchers.containsInAnyOrder(expected.toArray()));
|
assertThat(actual, Matchers.containsInAnyOrder(expected.toArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMdmLinksHasPartitionIdForResourceOnNonDefaultPartition() {
|
||||||
|
Patient goldenPatient = createGoldenPatient(buildJanePatient());
|
||||||
|
Patient patient1 = createPatient(buildJanePatient());
|
||||||
|
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
|
||||||
|
patient1.setUserData(Constants.RESOURCE_PARTITION_ID, requestPartitionId);
|
||||||
|
assertEquals(0, myMdmLinkDao.count());
|
||||||
|
|
||||||
|
myMdmLinkDaoSvc.createOrUpdateLinkEntity(goldenPatient, patient1, MdmMatchOutcome.NEW_GOLDEN_RESOURCE_MATCH, MdmLinkSourceEnum.MANUAL, createContextForCreate("Patient"));
|
||||||
|
List<MdmLink> targets = myMdmLinkDaoSvc.findMdmLinksByGoldenResource(goldenPatient);
|
||||||
|
assertFalse(targets.isEmpty());
|
||||||
|
assertEquals(1, targets.size());
|
||||||
|
assertEquals(requestPartitionId.getFirstPartitionIdOrNull(), targets.get(0).getPartitionId().getPartitionId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,10 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.svc;
|
package ca.uhn.fhir.jpa.mdm.svc;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.entity.MdmLink;
|
|
||||||
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
|
||||||
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
import ca.uhn.fhir.mdm.api.IMdmLinkSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService;
|
import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService;
|
||||||
import ca.uhn.fhir.mdm.api.MdmConstants;
|
|
||||||
import ca.uhn.fhir.mdm.api.MdmLinkSourceEnum;
|
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchOutcome;
|
|
||||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
|
import ca.uhn.fhir.mdm.util.GoldenResourceHelper;
|
||||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
|
||||||
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.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
@ -23,11 +14,6 @@ import org.slf4j.Logger;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.mock.mockito.SpyBean;
|
import org.springframework.boot.test.mock.mockito.SpyBean;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static ca.uhn.fhir.mdm.api.MdmMatchResultEnum.*;
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.hamcrest.Matchers.*;
|
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.slf4j.LoggerFactory.getLogger;
|
import static org.slf4j.LoggerFactory.getLogger;
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,23 @@
|
||||||
package ca.uhn.fhir.jpa.mdm.svc;
|
package ca.uhn.fhir.jpa.mdm.svc;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
|
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||||
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
import ca.uhn.fhir.jpa.mdm.BaseMdmR4Test;
|
||||||
|
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||||
|
import ca.uhn.fhir.jpa.partition.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
public class MdmResourceDaoSvcTest extends BaseMdmR4Test {
|
public class MdmResourceDaoSvcTest extends BaseMdmR4Test {
|
||||||
|
@ -18,6 +25,12 @@ public class MdmResourceDaoSvcTest extends BaseMdmR4Test {
|
||||||
@Autowired
|
@Autowired
|
||||||
MdmResourceDaoSvc myResourceDaoSvc;
|
MdmResourceDaoSvc myResourceDaoSvc;
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void after() throws IOException {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(new PartitionSettings().isPartitioningEnabled());
|
||||||
|
super.after();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchPatientByEidExcludesNonGoldenPatients() {
|
public void testSearchPatientByEidExcludesNonGoldenPatients() {
|
||||||
Patient goodSourcePatient = addExternalEID(createGoldenPatient(), TEST_EID);
|
Patient goodSourcePatient = addExternalEID(createGoldenPatient(), TEST_EID);
|
||||||
|
@ -35,7 +48,7 @@ public class MdmResourceDaoSvcTest extends BaseMdmR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearcGoldenResourceByEidExcludesNonMdmManaged() {
|
public void testSearchGoldenResourceByEidExcludesNonMdmManaged() {
|
||||||
Patient goodSourcePatient = addExternalEID(createGoldenPatient(), TEST_EID);
|
Patient goodSourcePatient = addExternalEID(createGoldenPatient(), TEST_EID);
|
||||||
myPatientDao.update(goodSourcePatient);
|
myPatientDao.update(goodSourcePatient);
|
||||||
|
|
||||||
|
@ -46,4 +59,37 @@ public class MdmResourceDaoSvcTest extends BaseMdmR4Test {
|
||||||
assertTrue(foundSourcePatient.isPresent());
|
assertTrue(foundSourcePatient.isPresent());
|
||||||
assertThat(foundSourcePatient.get().getIdElement().toUnqualifiedVersionless().getValue(), is(goodSourcePatient.getIdElement().toUnqualifiedVersionless().getValue()));
|
assertThat(foundSourcePatient.get().getIdElement().toUnqualifiedVersionless().getValue(), is(goodSourcePatient.getIdElement().toUnqualifiedVersionless().getValue()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchGoldenResourceOnSamePartition() {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||||
|
RequestPartitionId requestPartitionId = RequestPartitionId.fromPartitionId(1);
|
||||||
|
Patient patientOnPartition = createPatientOnPartition(new Patient(), true, false, requestPartitionId);
|
||||||
|
Patient goodSourcePatient = addExternalEID(patientOnPartition, TEST_EID);
|
||||||
|
SystemRequestDetails systemRequestDetails = new SystemRequestDetails();
|
||||||
|
systemRequestDetails.setRequestPartitionId(requestPartitionId);
|
||||||
|
myPatientDao.update(goodSourcePatient, systemRequestDetails);
|
||||||
|
|
||||||
|
Optional<IAnyResource> foundSourcePatient = myResourceDaoSvc.searchGoldenResourceByEID(TEST_EID, "Patient", requestPartitionId);
|
||||||
|
assertTrue(foundSourcePatient.isPresent());
|
||||||
|
assertThat(foundSourcePatient.get().getIdElement().toUnqualifiedVersionless().getValue(), is(goodSourcePatient.getIdElement().toUnqualifiedVersionless().getValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchGoldenResourceOnDifferentPartitions() {
|
||||||
|
myPartitionSettings.setPartitioningEnabled(true);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||||
|
RequestPartitionId requestPartitionId1 = RequestPartitionId.fromPartitionId(1);
|
||||||
|
myPartitionLookupSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
|
||||||
|
RequestPartitionId requestPartitionId2 = RequestPartitionId.fromPartitionId(2);
|
||||||
|
Patient patientOnPartition = createPatientOnPartition(new Patient(), true, false, requestPartitionId1);
|
||||||
|
Patient goodSourcePatient = addExternalEID(patientOnPartition, TEST_EID);
|
||||||
|
SystemRequestDetails systemRequestDetails = new SystemRequestDetails();
|
||||||
|
systemRequestDetails.setRequestPartitionId(requestPartitionId1);
|
||||||
|
myPatientDao.update(goodSourcePatient, systemRequestDetails);
|
||||||
|
|
||||||
|
Optional<IAnyResource> foundSourcePatient = myResourceDaoSvc.searchGoldenResourceByEID(TEST_EID, "Patient", requestPartitionId2);
|
||||||
|
assertFalse(foundSourcePatient.isPresent());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import java.util.Optional;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
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.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ -54,7 +56,7 @@ class CandidateSearcherTest {
|
||||||
SimpleBundleProvider bundleProvider = new SimpleBundleProvider();
|
SimpleBundleProvider bundleProvider = new SimpleBundleProvider();
|
||||||
|
|
||||||
bundleProvider.setSize(candidateSearchLimit + offset);
|
bundleProvider.setSize(candidateSearchLimit + offset);
|
||||||
when(dao.search(map)).thenReturn(bundleProvider);
|
when(dao.search(eq(map), any())).thenReturn(bundleProvider);
|
||||||
|
|
||||||
Optional<IBundleProvider> result = myCandidateSearcher.search(resourceType, criteria);
|
Optional<IBundleProvider> result = myCandidateSearcher.search(resourceType, criteria);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.subscription.match.config;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
|
||||||
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelRegistry;
|
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionChannelRegistry;
|
||||||
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryChannelNamer;
|
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryChannelNamer;
|
||||||
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory;
|
import ca.uhn.fhir.jpa.subscription.channel.subscription.SubscriptionDeliveryHandlerFactory;
|
||||||
|
@ -38,6 +39,7 @@ import ca.uhn.fhir.jpa.subscription.match.matcher.subscriber.SubscriptionRegiste
|
||||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader;
|
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionLoader;
|
||||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
|
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.config.SubscriptionModelConfig;
|
import ca.uhn.fhir.jpa.subscription.model.config.SubscriptionModelConfig;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
@ -103,8 +105,8 @@ public class SubscriptionProcessorConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
public SubscriptionDeliveringMessageSubscriber subscriptionDeliveringMessageSubscriber() {
|
public SubscriptionDeliveringMessageSubscriber subscriptionDeliveringMessageSubscriber(IChannelFactory theChannelFactory) {
|
||||||
return new SubscriptionDeliveringMessageSubscriber();
|
return new SubscriptionDeliveringMessageSubscriber(theChannelFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|
|
@ -32,6 +32,7 @@ import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -44,16 +45,16 @@ import java.net.URISyntaxException;
|
||||||
|
|
||||||
@Scope("prototype")
|
@Scope("prototype")
|
||||||
public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDeliverySubscriber {
|
public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDeliverySubscriber {
|
||||||
private static Logger ourLog = LoggerFactory.getLogger(SubscriptionDeliveringMessageSubscriber.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(SubscriptionDeliveringMessageSubscriber.class);
|
||||||
|
|
||||||
@Autowired
|
private final IChannelFactory myChannelFactory;
|
||||||
private IChannelFactory myChannelFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
public SubscriptionDeliveringMessageSubscriber() {
|
public SubscriptionDeliveringMessageSubscriber(IChannelFactory theChannelFactory) {
|
||||||
super();
|
super();
|
||||||
|
myChannelFactory = theChannelFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void deliverPayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, IChannelProducer theChannelProducer) {
|
protected void deliverPayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, IChannelProducer theChannelProducer) {
|
||||||
|
@ -67,6 +68,7 @@ public class SubscriptionDeliveringMessageSubscriber extends BaseSubscriptionDel
|
||||||
ResourceModifiedMessage payload = new ResourceModifiedMessage(myFhirContext, thePayloadResource, theMsg.getOperationType());
|
ResourceModifiedMessage payload = new ResourceModifiedMessage(myFhirContext, thePayloadResource, theMsg.getOperationType());
|
||||||
payload.setMessageKey(theMsg.getMessageKeyOrNull());
|
payload.setMessageKey(theMsg.getMessageKeyOrNull());
|
||||||
payload.setTransactionId(theMsg.getTransactionId());
|
payload.setTransactionId(theMsg.getTransactionId());
|
||||||
|
payload.setPartitionId(theMsg.getRequestPartitionId());
|
||||||
ResourceModifiedJsonMessage message = new ResourceModifiedJsonMessage(payload);
|
ResourceModifiedJsonMessage message = new ResourceModifiedJsonMessage(payload);
|
||||||
theChannelProducer.send(message);
|
theChannelProducer.send(message);
|
||||||
ourLog.debug("Delivering {} message payload {} for {}", theMsg.getOperationType(), theMsg.getPayloadId(), theSubscription.getIdElement(myFhirContext).toUnqualifiedVersionless().getValue());
|
ourLog.debug("Delivering {} message payload {} for {}", theMsg.getOperationType(), theMsg.getPayloadId(), theSubscription.getIdElement(myFhirContext).toUnqualifiedVersionless().getValue());
|
||||||
|
|
|
@ -118,13 +118,13 @@ public class SubscriptionActivatingSubscriber extends BaseSubscriberForSubscript
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private boolean activateSubscription(final IBaseResource theSubscription) {
|
private boolean activateSubscription(final IBaseResource theSubscription) {
|
||||||
IFhirResourceDao subscriptionDao = myDaoRegistry.getSubscriptionDao();
|
IFhirResourceDao subscriptionDao = myDaoRegistry.getSubscriptionDao();
|
||||||
SystemRequestDetails srd = SystemRequestDetails.forAllPartition();
|
SystemRequestDetails srd = SystemRequestDetails.forAllPartitions();
|
||||||
|
|
||||||
IBaseResource subscription = null;
|
IBaseResource subscription = null;
|
||||||
try {
|
try {
|
||||||
// read can throw ResourceGoneException
|
// read can throw ResourceGoneException
|
||||||
// if this happens, we will treat this as a failure to activate
|
// if this happens, we will treat this as a failure to activate
|
||||||
subscription = subscriptionDao.read(theSubscription.getIdElement(), SystemRequestDetails.forAllPartition());
|
subscription = subscriptionDao.read(theSubscription.getIdElement(), SystemRequestDetails.forAllPartitions());
|
||||||
subscription.setId(subscription.getIdElement().toVersionless());
|
subscription.setId(subscription.getIdElement().toVersionless());
|
||||||
|
|
||||||
ourLog.info("Activating subscription {} from status {} to {}", subscription.getIdElement().toUnqualified().getValue(), SubscriptionConstants.REQUESTED_STATUS, SubscriptionConstants.ACTIVE_STATUS);
|
ourLog.info("Activating subscription {} from status {} to {}", subscription.getIdElement().toUnqualified().getValue(), SubscriptionConstants.REQUESTED_STATUS, SubscriptionConstants.ACTIVE_STATUS);
|
||||||
|
|
|
@ -90,7 +90,7 @@ public class SubscriptionLoader implements IResourceChangeListener {
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void registerListener() {
|
public void registerListener() {
|
||||||
mySearchParameterMap = getSearchParameterMap();
|
mySearchParameterMap = getSearchParameterMap();
|
||||||
mySystemRequestDetails = SystemRequestDetails.forAllPartition();
|
mySystemRequestDetails = SystemRequestDetails.forAllPartitions();
|
||||||
|
|
||||||
IResourceChangeListenerCache subscriptionCache = myResourceChangeListenerRegistry.registerResourceResourceChangeListener("Subscription", mySearchParameterMap, this, REFRESH_INTERVAL);
|
IResourceChangeListenerCache subscriptionCache = myResourceChangeListenerRegistry.registerResourceResourceChangeListener("Subscription", mySearchParameterMap, this, REFRESH_INTERVAL);
|
||||||
subscriptionCache.forceRefresh();
|
subscriptionCache.forceRefresh();
|
||||||
|
@ -252,7 +252,7 @@ public class SubscriptionLoader implements IResourceChangeListener {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
IFhirResourceDao<?> subscriptionDao = getSubscriptionDao();
|
IFhirResourceDao<?> subscriptionDao = getSubscriptionDao();
|
||||||
SystemRequestDetails systemRequestDetails = SystemRequestDetails.forAllPartition();
|
SystemRequestDetails systemRequestDetails = SystemRequestDetails.forAllPartitions();
|
||||||
List<IBaseResource> resourceList = theResourceIds.stream().map(n -> subscriptionDao.read(n, systemRequestDetails)).collect(Collectors.toList());
|
List<IBaseResource> resourceList = theResourceIds.stream().map(n -> subscriptionDao.read(n, systemRequestDetails)).collect(Collectors.toList());
|
||||||
updateSubscriptionRegistry(resourceList);
|
updateSubscriptionRegistry(resourceList);
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
||||||
if (!subscriptionId.hasResourceType()) {
|
if (!subscriptionId.hasResourceType()) {
|
||||||
subscriptionId = subscriptionId.withResourceType(ResourceTypeEnum.SUBSCRIPTION.getCode());
|
subscriptionId = subscriptionId.withResourceType(ResourceTypeEnum.SUBSCRIPTION.getCode());
|
||||||
}
|
}
|
||||||
subscriptionDao.read(subscriptionId, SystemRequestDetails.forAllPartition());
|
subscriptionDao.read(subscriptionId, SystemRequestDetails.forAllPartitions());
|
||||||
}
|
}
|
||||||
|
|
||||||
List<IPrimitiveType<String>> resourceIds = ObjectUtils.defaultIfNull(theResourceIds, Collections.emptyList());
|
List<IPrimitiveType<String>> resourceIds = ObjectUtils.defaultIfNull(theResourceIds, Collections.emptyList());
|
||||||
|
@ -300,7 +300,7 @@ public class SubscriptionTriggeringSvcImpl implements ISubscriptionTriggeringSvc
|
||||||
private Future<Void> submitResource(String theSubscriptionId, String theResourceIdToTrigger) {
|
private Future<Void> submitResource(String theSubscriptionId, String theResourceIdToTrigger) {
|
||||||
org.hl7.fhir.r4.model.IdType resourceId = new org.hl7.fhir.r4.model.IdType(theResourceIdToTrigger);
|
org.hl7.fhir.r4.model.IdType resourceId = new org.hl7.fhir.r4.model.IdType(theResourceIdToTrigger);
|
||||||
IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceId.getResourceType());
|
IFhirResourceDao dao = myDaoRegistry.getResourceDao(resourceId.getResourceType());
|
||||||
IBaseResource resourceToTrigger = dao.read(resourceId, SystemRequestDetails.forAllPartition());
|
IBaseResource resourceToTrigger = dao.read(resourceId, SystemRequestDetails.forAllPartitions());
|
||||||
|
|
||||||
return submitResource(theSubscriptionId, resourceToTrigger);
|
return submitResource(theSubscriptionId, resourceToTrigger);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,15 @@ import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelFactory;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.channel.api.IChannelProducer;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.match.deliver.message.SubscriptionDeliveringMessageSubscriber;
|
||||||
import ca.uhn.fhir.jpa.subscription.match.deliver.resthook.SubscriptionDeliveringRestHookSubscriber;
|
import ca.uhn.fhir.jpa.subscription.match.deliver.resthook.SubscriptionDeliveringRestHookSubscriber;
|
||||||
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
|
import ca.uhn.fhir.jpa.subscription.match.registry.SubscriptionRegistry;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
|
import ca.uhn.fhir.jpa.subscription.model.CanonicalSubscription;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryJsonMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryJsonMessage;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceDeliveryMessage;
|
||||||
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedJsonMessage;
|
||||||
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
import ca.uhn.fhir.jpa.subscription.model.ResourceModifiedMessage;
|
||||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
|
@ -23,6 +27,7 @@ import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.mockito.Answers;
|
import org.mockito.Answers;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -31,7 +36,9 @@ import org.springframework.messaging.Message;
|
||||||
import org.springframework.messaging.MessagingException;
|
import org.springframework.messaging.MessagingException;
|
||||||
import org.springframework.messaging.support.GenericMessage;
|
import org.springframework.messaging.support.GenericMessage;
|
||||||
|
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
@ -49,12 +56,18 @@ public class BaseSubscriptionDeliverySubscriberTest {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(BaseSubscriptionDeliverySubscriberTest.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(BaseSubscriptionDeliverySubscriberTest.class);
|
||||||
|
|
||||||
private SubscriptionDeliveringRestHookSubscriber mySubscriber;
|
private SubscriptionDeliveringRestHookSubscriber mySubscriber;
|
||||||
|
private SubscriptionDeliveringMessageSubscriber myMessageSubscriber;
|
||||||
private final FhirContext myCtx = FhirContext.forR4();
|
private final FhirContext myCtx = FhirContext.forR4();
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||||
@Mock
|
@Mock
|
||||||
protected SubscriptionRegistry mySubscriptionRegistry;
|
protected SubscriptionRegistry mySubscriptionRegistry;
|
||||||
|
@Mock
|
||||||
|
private IChannelFactory myChannelFactory;
|
||||||
|
@Mock
|
||||||
|
private IChannelProducer myChannelProducer;
|
||||||
|
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
private IRestfulClientFactory myRestfulClientFactory;
|
private IRestfulClientFactory myRestfulClientFactory;
|
||||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||||
|
@ -67,6 +80,11 @@ public class BaseSubscriptionDeliverySubscriberTest {
|
||||||
mySubscriber.setInterceptorBroadcasterForUnitTest(myInterceptorBroadcaster);
|
mySubscriber.setInterceptorBroadcasterForUnitTest(myInterceptorBroadcaster);
|
||||||
mySubscriber.setSubscriptionRegistryForUnitTest(mySubscriptionRegistry);
|
mySubscriber.setSubscriptionRegistryForUnitTest(mySubscriptionRegistry);
|
||||||
|
|
||||||
|
myMessageSubscriber = new SubscriptionDeliveringMessageSubscriber(myChannelFactory);
|
||||||
|
myMessageSubscriber.setFhirContextForUnitTest(myCtx);
|
||||||
|
myMessageSubscriber.setInterceptorBroadcasterForUnitTest(myInterceptorBroadcaster);
|
||||||
|
myMessageSubscriber.setSubscriptionRegistryForUnitTest(mySubscriptionRegistry);
|
||||||
|
|
||||||
myCtx.setRestfulClientFactory(myRestfulClientFactory);
|
myCtx.setRestfulClientFactory(myRestfulClientFactory);
|
||||||
when(myRestfulClientFactory.newGenericClient(any())).thenReturn(myGenericClient);
|
when(myRestfulClientFactory.newGenericClient(any())).thenReturn(myGenericClient);
|
||||||
}
|
}
|
||||||
|
@ -194,7 +212,6 @@ public class BaseSubscriptionDeliverySubscriberTest {
|
||||||
ourLog.info(jsonString);
|
ourLog.info(jsonString);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Assert that the partitionID is being serialized in JSON
|
// Assert that the partitionID is being serialized in JSON
|
||||||
assertThat(jsonString, containsString("\"partitionDate\":[2020,1,1]"));
|
assertThat(jsonString, containsString("\"partitionDate\":[2020,1,1]"));
|
||||||
assertThat(jsonString, containsString("\"partitionIds\":[123]"));
|
assertThat(jsonString, containsString("\"partitionIds\":[123]"));
|
||||||
|
@ -223,6 +240,30 @@ public class BaseSubscriptionDeliverySubscriberTest {
|
||||||
assertThat(jsonString, containsString("\"partitionIds\":[null]"));
|
assertThat(jsonString, containsString("\"partitionIds\":[null]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeliveryMessageWithPartition() throws URISyntaxException {
|
||||||
|
RequestPartitionId thePartitionId = RequestPartitionId.fromPartitionId(123, LocalDate.of(2020, 1, 1));
|
||||||
|
when(myInterceptorBroadcaster.callHooks(eq(Pointcut.SUBSCRIPTION_BEFORE_MESSAGE_DELIVERY), any())).thenReturn(true);
|
||||||
|
when(myInterceptorBroadcaster.callHooks(eq(Pointcut.SUBSCRIPTION_AFTER_MESSAGE_DELIVERY), any())).thenReturn(false);
|
||||||
|
when(myChannelFactory.getOrCreateProducer(any(), any(), any())).thenReturn(myChannelProducer);
|
||||||
|
|
||||||
|
CanonicalSubscription subscription = generateSubscription();
|
||||||
|
Patient patient = generatePatient();
|
||||||
|
|
||||||
|
ResourceDeliveryMessage payload = new ResourceDeliveryMessage();
|
||||||
|
payload.setSubscription(subscription);
|
||||||
|
payload.setPayload(myCtx, patient, EncodingEnum.JSON);
|
||||||
|
payload.setOperationType(ResourceModifiedMessage.OperationTypeEnum.CREATE);
|
||||||
|
payload.setPartitionId(thePartitionId);
|
||||||
|
|
||||||
|
myMessageSubscriber.handleMessage(payload);
|
||||||
|
verify(myChannelFactory).getOrCreateProducer(any(), any(), any());
|
||||||
|
ArgumentCaptor<ResourceModifiedJsonMessage> captor = ArgumentCaptor.forClass(ResourceModifiedJsonMessage.class);
|
||||||
|
verify(myChannelProducer).send(captor.capture());
|
||||||
|
final List<ResourceModifiedJsonMessage> params = captor.getAllValues();
|
||||||
|
assertEquals(thePartitionId, params.get(0).getPayload().getPartitionId());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSerializeLegacyDeliveryMessage() throws JsonProcessingException {
|
public void testSerializeLegacyDeliveryMessage() throws JsonProcessingException {
|
||||||
String legacyDeliveryMessageJson = "{\"headers\":{\"retryCount\":0,\"customHeaders\":{}},\"payload\":{\"operationType\":\"CREATE\",\"canonicalSubscription\":{\"id\":\"Subscription/123\",\"endpointUrl\":\"http://example.com/fhir\",\"payload\":\"application/fhir+json\"},\"payload\":\"{\\\"resourceType\\\":\\\"Patient\\\",\\\"active\\\":true}\"}}";
|
String legacyDeliveryMessageJson = "{\"headers\":{\"retryCount\":0,\"customHeaders\":{}},\"payload\":{\"operationType\":\"CREATE\",\"canonicalSubscription\":{\"id\":\"Subscription/123\",\"endpointUrl\":\"http://example.com/fhir\",\"payload\":\"application/fhir+json\"},\"payload\":\"{\\\"resourceType\\\":\\\"Patient\\\",\\\"active\\\":true}\"}}";
|
||||||
|
@ -250,4 +291,5 @@ public class BaseSubscriptionDeliverySubscriberTest {
|
||||||
subscription.setPayloadString("application/fhir+json");
|
subscription.setPayloadString("application/fhir+json");
|
||||||
return subscription;
|
return subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.mdm.api;
|
||||||
|
|
||||||
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
|
import ca.uhn.fhir.mdm.api.paging.MdmPageRequest;
|
||||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
|
@ -34,10 +35,17 @@ import java.util.List;
|
||||||
|
|
||||||
public interface IMdmControllerSvc {
|
public interface IMdmControllerSvc {
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest);
|
Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest);
|
||||||
|
|
||||||
|
Page<MdmLinkJson> queryLinks(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, RequestDetails theRequestDetails);
|
||||||
|
|
||||||
|
Page<MdmLinkJson> queryLinksFromPartitionList(@Nullable String theGoldenResourceId, @Nullable String theSourceResourceId, @Nullable String theMatchResult, @Nullable String theLinkSource, MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, List<Integer> thePartitionIds);
|
||||||
|
|
||||||
Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest);
|
Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest);
|
||||||
|
|
||||||
|
Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmTransactionContext, MdmPageRequest thePageRequest, RequestDetails theRequestDetails);
|
||||||
|
|
||||||
void notDuplicateGoldenResource(String theGoldenResourceId, String theTargetGoldenResourceId, MdmTransactionContext theMdmTransactionContext);
|
void notDuplicateGoldenResource(String theGoldenResourceId, String theTargetGoldenResourceId, MdmTransactionContext theMdmTransactionContext);
|
||||||
|
|
||||||
IAnyResource mergeGoldenResources(String theFromGoldenResourceId, String theToGoldenResourceId, IAnyResource theManuallyMergedGoldenResource, MdmTransactionContext theMdmTransactionContext);
|
IAnyResource mergeGoldenResources(String theFromGoldenResourceId, String theToGoldenResourceId, IAnyResource theManuallyMergedGoldenResource, MdmTransactionContext theMdmTransactionContext);
|
||||||
|
|
|
@ -25,10 +25,14 @@ import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This service supports the MDM operation providers for those services that return multiple MDM links.
|
* This service supports the MDM operation providers for those services that return multiple MDM links.
|
||||||
*/
|
*/
|
||||||
public interface IMdmLinkQuerySvc {
|
public interface IMdmLinkQuerySvc {
|
||||||
Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest);
|
Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest);
|
||||||
|
Page<MdmLinkJson> queryLinks(IIdType theGoldenResourceId, IIdType theSourceResourceId, MdmMatchResultEnum theMatchResult, MdmLinkSourceEnum theLinkSource, MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest, List<Integer> thePartitionId);
|
||||||
Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest);
|
Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest);
|
||||||
|
Page<MdmLinkJson> getDuplicateGoldenResources(MdmTransactionContext theMdmContext, MdmPageRequest thePageRequest, List<Integer> thePartitionId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.mdm.api;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
@ -36,5 +37,5 @@ public interface IMdmMatchFinderSvc {
|
||||||
* @return a List of {@link MatchedTarget} representing POSSIBLE_MATCH and MATCH outcomes.
|
* @return a List of {@link MatchedTarget} representing POSSIBLE_MATCH and MATCH outcomes.
|
||||||
*/
|
*/
|
||||||
@Nonnull
|
@Nonnull
|
||||||
List<MatchedTarget> getMatchedTargets(String theResourceType, IAnyResource theResource);
|
List<MatchedTarget> getMatchedTargets(String theResourceType, IAnyResource theResource, RequestPartitionId theRequestPartitionId);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.mdm.api;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
@ -37,7 +38,7 @@ public interface IMdmSubmitSvc {
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
long submitAllSourceTypesToMdm(@Nullable String theCriteria);
|
long submitAllSourceTypesToMdm(@Nullable String theCriteria, RequestDetails theRequestDetails);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a type and a search criteria, submit all found resources for MDM processing.
|
* Given a type and a search criteria, submit all found resources for MDM processing.
|
||||||
|
@ -46,7 +47,7 @@ public interface IMdmSubmitSvc {
|
||||||
* @param theCriteria The FHIR search critieria for filtering the resources to be submitted for MDM processing..
|
* @param theCriteria The FHIR search critieria for filtering the resources to be submitted for MDM processing..
|
||||||
* @return the number of resources submitted for MDM processing.
|
* @return the number of resources submitted for MDM processing.
|
||||||
*/
|
*/
|
||||||
long submitSourceResourceTypeToMdm(String theSourceResourceType, String theCriteria);
|
long submitSourceResourceTypeToMdm(String theSourceResourceType, String theCriteria, RequestDetails theRequestDetails);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method that calls {@link #submitSourceResourceTypeToMdm(String, String)} with the type pre-populated.
|
* Convenience method that calls {@link #submitSourceResourceTypeToMdm(String, String)} with the type pre-populated.
|
||||||
|
@ -54,7 +55,7 @@ public interface IMdmSubmitSvc {
|
||||||
* @param theCriteria The FHIR search critieria for filtering the resources to be submitted for MDM processing.
|
* @param theCriteria The FHIR search critieria for filtering the resources to be submitted for MDM processing.
|
||||||
* @return the number of resources submitted for MDM processing.
|
* @return the number of resources submitted for MDM processing.
|
||||||
*/
|
*/
|
||||||
long submitPractitionerTypeToMdm(String theCriteria);
|
long submitPractitionerTypeToMdm(String theCriteria, RequestDetails theRequestDetails);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience method that calls {@link #submitSourceResourceTypeToMdm(String, String)} with the type pre-populated.
|
* Convenience method that calls {@link #submitSourceResourceTypeToMdm(String, String)} with the type pre-populated.
|
||||||
|
@ -62,7 +63,7 @@ public interface IMdmSubmitSvc {
|
||||||
* @param theCriteria The FHIR search critieria for filtering the resources to be submitted for MDM processing.
|
* @param theCriteria The FHIR search critieria for filtering the resources to be submitted for MDM processing.
|
||||||
* @return the number of resources submitted for MDM processing.
|
* @return the number of resources submitted for MDM processing.
|
||||||
*/
|
*/
|
||||||
long submitPatientTypeToMdm(String theCriteria);
|
long submitPatientTypeToMdm(String theCriteria, RequestDetails theRequestDetails);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given an ID and a source resource type valid for MDM, manually submit the given ID for MDM processing.
|
* Given an ID and a source resource type valid for MDM, manually submit the given ID for MDM processing.
|
||||||
|
@ -70,7 +71,7 @@ public interface IMdmSubmitSvc {
|
||||||
* @param theId the ID of the resource to process for MDM.
|
* @param theId the ID of the resource to process for MDM.
|
||||||
* @return the constant `1`, as if this function returns successfully, it will have processed one resource for MDM.
|
* @return the constant `1`, as if this function returns successfully, it will have processed one resource for MDM.
|
||||||
*/
|
*/
|
||||||
long submitSourceResourceToMdm(IIdType theId);
|
long submitSourceResourceToMdm(IIdType theId, RequestDetails theRequestDetails);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This setter exists to allow imported modules to override settings.
|
* This setter exists to allow imported modules to override settings.
|
||||||
|
|
|
@ -118,7 +118,7 @@ public abstract class BaseMdmProvider {
|
||||||
protected IBaseParameters parametersFromMdmLinks(Page<MdmLinkJson> theMdmLinkStream, boolean includeResultAndSource, ServletRequestDetails theServletRequestDetails, MdmPageRequest thePageRequest) {
|
protected IBaseParameters parametersFromMdmLinks(Page<MdmLinkJson> theMdmLinkStream, boolean includeResultAndSource, ServletRequestDetails theServletRequestDetails, MdmPageRequest thePageRequest) {
|
||||||
IBaseParameters retval = ParametersUtil.newInstance(myFhirContext);
|
IBaseParameters retval = ParametersUtil.newInstance(myFhirContext);
|
||||||
addPagingParameters(retval, theMdmLinkStream, theServletRequestDetails, thePageRequest);
|
addPagingParameters(retval, theMdmLinkStream, theServletRequestDetails, thePageRequest);
|
||||||
theMdmLinkStream.forEach(mdmLink -> {
|
theMdmLinkStream.getContent().forEach(mdmLink -> {
|
||||||
IBase resultPart = ParametersUtil.addParameterToParameters(myFhirContext, retval, "link");
|
IBase resultPart = ParametersUtil.addParameterToParameters(myFhirContext, retval, "link");
|
||||||
ParametersUtil.addPartString(myFhirContext, resultPart, "goldenResourceId", mdmLink.getGoldenResourceId());
|
ParametersUtil.addPartString(myFhirContext, resultPart, "goldenResourceId", mdmLink.getGoldenResourceId());
|
||||||
ParametersUtil.addPartString(myFhirContext, resultPart, "sourceResourceId", mdmLink.getSourceId());
|
ParametersUtil.addPartString(myFhirContext, resultPart, "sourceResourceId", mdmLink.getSourceId());
|
||||||
|
|
|
@ -22,6 +22,8 @@ package ca.uhn.fhir.mdm.provider;
|
||||||
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||||
|
import ca.uhn.fhir.jpa.partition.IRequestPartitionHelperSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc;
|
import ca.uhn.fhir.mdm.api.IMdmMatchFinderSvc;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
import ca.uhn.fhir.mdm.api.MatchedTarget;
|
import ca.uhn.fhir.mdm.api.MatchedTarget;
|
||||||
|
@ -29,6 +31,7 @@ import ca.uhn.fhir.mdm.api.MdmConstants;
|
||||||
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
import ca.uhn.fhir.mdm.util.MdmResourceUtil;
|
||||||
import ca.uhn.fhir.mdm.util.MessageHelper;
|
import ca.uhn.fhir.mdm.util.MessageHelper;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||||
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||||
|
@ -60,18 +63,21 @@ public class MdmControllerHelper {
|
||||||
private final IMdmSettings myMdmSettings;
|
private final IMdmSettings myMdmSettings;
|
||||||
private final MessageHelper myMessageHelper;
|
private final MessageHelper myMessageHelper;
|
||||||
private final IMdmMatchFinderSvc myMdmMatchFinderSvc;
|
private final IMdmMatchFinderSvc myMdmMatchFinderSvc;
|
||||||
|
private final IRequestPartitionHelperSvc myRequestPartitionHelperSvc;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public MdmControllerHelper(FhirContext theFhirContext,
|
public MdmControllerHelper(FhirContext theFhirContext,
|
||||||
IResourceLoader theResourceLoader,
|
IResourceLoader theResourceLoader,
|
||||||
IMdmMatchFinderSvc theMdmMatchFinderSvc,
|
IMdmMatchFinderSvc theMdmMatchFinderSvc,
|
||||||
IMdmSettings theMdmSettings,
|
IMdmSettings theMdmSettings,
|
||||||
MessageHelper theMessageHelper) {
|
MessageHelper theMessageHelper,
|
||||||
|
IRequestPartitionHelperSvc theRequestPartitionHelperSvc) {
|
||||||
myFhirContext = theFhirContext;
|
myFhirContext = theFhirContext;
|
||||||
myResourceLoader = theResourceLoader;
|
myResourceLoader = theResourceLoader;
|
||||||
myMdmSettings = theMdmSettings;
|
myMdmSettings = theMdmSettings;
|
||||||
myMdmMatchFinderSvc = theMdmMatchFinderSvc;
|
myMdmMatchFinderSvc = theMdmMatchFinderSvc;
|
||||||
myMessageHelper = theMessageHelper;
|
myMessageHelper = theMessageHelper;
|
||||||
|
myRequestPartitionHelperSvc = theRequestPartitionHelperSvc;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateSameVersion(IAnyResource theResource, String theResourceId) {
|
public void validateSameVersion(IAnyResource theResource, String theResourceId) {
|
||||||
|
@ -130,8 +136,9 @@ public class MdmControllerHelper {
|
||||||
/**
|
/**
|
||||||
* Helper method which will return a bundle of all Matches and Possible Matches.
|
* Helper method which will return a bundle of all Matches and Possible Matches.
|
||||||
*/
|
*/
|
||||||
public IBaseBundle getMatchesAndPossibleMatchesForResource(IAnyResource theResource, String theResourceType) {
|
public IBaseBundle getMatchesAndPossibleMatchesForResource(IAnyResource theResource, String theResourceType, RequestDetails theRequestDetails) {
|
||||||
List<MatchedTarget> matches = myMdmMatchFinderSvc.getMatchedTargets(theResourceType, theResource);
|
RequestPartitionId requestPartitionId = myRequestPartitionHelperSvc.determineReadPartitionForRequest(theRequestDetails, theResourceType, null);
|
||||||
|
List<MatchedTarget> matches = myMdmMatchFinderSvc.getMatchedTargets(theResourceType, theResource, requestPartitionId);
|
||||||
matches.sort(Comparator.comparing((MatchedTarget m) -> m.getMatchResult().getNormalizedScore()).reversed());
|
matches.sort(Comparator.comparing((MatchedTarget m) -> m.getMatchResult().getNormalizedScore()).reversed());
|
||||||
|
|
||||||
BundleBuilder builder = new BundleBuilder(myFhirContext);
|
BundleBuilder builder = new BundleBuilder(myFhirContext);
|
||||||
|
|
|
@ -87,21 +87,23 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.EMPI_MATCH, typeName = "Patient")
|
@Operation(name = ProviderConstants.EMPI_MATCH, typeName = "Patient")
|
||||||
public IBaseBundle match(@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1, typeName = "Patient") IAnyResource thePatient) {
|
public IBaseBundle match(@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1, typeName = "Patient") IAnyResource thePatient,
|
||||||
|
RequestDetails theRequestDetails) {
|
||||||
if (thePatient == null) {
|
if (thePatient == null) {
|
||||||
throw new InvalidRequestException(Msg.code(1498) + "resource may not be null");
|
throw new InvalidRequestException(Msg.code(1498) + "resource may not be null");
|
||||||
}
|
}
|
||||||
return myMdmControllerHelper.getMatchesAndPossibleMatchesForResource(thePatient, "Patient");
|
return myMdmControllerHelper.getMatchesAndPossibleMatchesForResource(thePatient, "Patient", theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.MDM_MATCH)
|
@Operation(name = ProviderConstants.MDM_MATCH)
|
||||||
public IBaseBundle serverMatch(@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1) IAnyResource theResource,
|
public IBaseBundle serverMatch(@OperationParam(name = ProviderConstants.MDM_MATCH_RESOURCE, min = 1, max = 1) IAnyResource theResource,
|
||||||
@OperationParam(name = ProviderConstants.MDM_RESOURCE_TYPE, min = 1, max = 1, typeName = "string") IPrimitiveType<String> theResourceType
|
@OperationParam(name = ProviderConstants.MDM_RESOURCE_TYPE, min = 1, max = 1, typeName = "string") IPrimitiveType<String> theResourceType,
|
||||||
|
RequestDetails theRequestDetails
|
||||||
) {
|
) {
|
||||||
if (theResource == null) {
|
if (theResource == null) {
|
||||||
throw new InvalidRequestException(Msg.code(1499) + "resource may not be null");
|
throw new InvalidRequestException(Msg.code(1499) + "resource may not be null");
|
||||||
}
|
}
|
||||||
return myMdmControllerHelper.getMatchesAndPossibleMatchesForResource(theResource, theResourceType.getValueAsString());
|
return myMdmControllerHelper.getMatchesAndPossibleMatchesForResource(theResource, theResourceType.getValueAsString(), theRequestDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operation(name = ProviderConstants.MDM_MERGE_GOLDEN_RESOURCES)
|
@Operation(name = ProviderConstants.MDM_MERGE_GOLDEN_RESOURCES)
|
||||||
|
@ -184,13 +186,13 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
|
||||||
@Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
|
@Description(formalDefinition = "Results from this method are returned across multiple pages. This parameter controls the size of those pages.")
|
||||||
@OperationParam(name = Constants.PARAM_COUNT, min = 0, max = 1, typeName = "integer")
|
@OperationParam(name = Constants.PARAM_COUNT, min = 0, max = 1, typeName = "integer")
|
||||||
IPrimitiveType<Integer> theCount,
|
IPrimitiveType<Integer> theCount,
|
||||||
|
|
||||||
ServletRequestDetails theRequestDetails) {
|
ServletRequestDetails theRequestDetails) {
|
||||||
MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
|
MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
|
||||||
Page<MdmLinkJson> mdmLinkJson = myMdmControllerSvc.queryLinks(extractStringOrNull(theGoldenResourceId),
|
Page<MdmLinkJson> mdmLinkJson = myMdmControllerSvc.queryLinks(extractStringOrNull(theGoldenResourceId),
|
||||||
extractStringOrNull(theResourceId), extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource),
|
extractStringOrNull(theResourceId), extractStringOrNull(theMatchResult), extractStringOrNull(theLinkSource),
|
||||||
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.QUERY_LINKS,
|
createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.QUERY_LINKS,
|
||||||
getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId)), mdmPageRequest);
|
getResourceType(ProviderConstants.MDM_QUERY_LINKS_GOLDEN_RESOURCE_ID, theGoldenResourceId)),
|
||||||
|
mdmPageRequest, theRequestDetails);
|
||||||
|
|
||||||
return parametersFromMdmLinks(mdmLinkJson, true, theRequestDetails, mdmPageRequest);
|
return parametersFromMdmLinks(mdmLinkJson, true, theRequestDetails, mdmPageRequest);
|
||||||
}
|
}
|
||||||
|
@ -207,7 +209,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
|
||||||
|
|
||||||
MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
|
MdmPageRequest mdmPageRequest = new MdmPageRequest(theOffset, theCount, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
|
||||||
|
|
||||||
Page<MdmLinkJson> possibleDuplicates = myMdmControllerSvc.getDuplicateGoldenResources(createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.DUPLICATE_GOLDEN_RESOURCES, null), mdmPageRequest);
|
Page<MdmLinkJson> possibleDuplicates = myMdmControllerSvc.getDuplicateGoldenResources(createMdmContext(theRequestDetails, MdmTransactionContext.OperationType.DUPLICATE_GOLDEN_RESOURCES, null), mdmPageRequest, theRequestDetails);
|
||||||
|
|
||||||
return parametersFromMdmLinks(possibleDuplicates, false, theRequestDetails, mdmPageRequest);
|
return parametersFromMdmLinks(possibleDuplicates, false, theRequestDetails, mdmPageRequest);
|
||||||
}
|
}
|
||||||
|
@ -239,9 +241,9 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
|
||||||
String resourceType = convertStringTypeToString(theResourceType);
|
String resourceType = convertStringTypeToString(theResourceType);
|
||||||
long submittedCount;
|
long submittedCount;
|
||||||
if (resourceType != null) {
|
if (resourceType != null) {
|
||||||
submittedCount = myMdmSubmitSvc.submitSourceResourceTypeToMdm(resourceType, criteria);
|
submittedCount = myMdmSubmitSvc.submitSourceResourceTypeToMdm(resourceType, criteria, theRequestDetails);
|
||||||
} else {
|
} else {
|
||||||
submittedCount = myMdmSubmitSvc.submitAllSourceTypesToMdm(criteria);
|
submittedCount = myMdmSubmitSvc.submitAllSourceTypesToMdm(criteria, theRequestDetails);
|
||||||
}
|
}
|
||||||
return buildMdmOutParametersWithCount(submittedCount);
|
return buildMdmOutParametersWithCount(submittedCount);
|
||||||
}
|
}
|
||||||
|
@ -257,7 +259,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
|
||||||
public IBaseParameters mdmBatchPatientInstance(
|
public IBaseParameters mdmBatchPatientInstance(
|
||||||
@IdParam IIdType theIdParam,
|
@IdParam IIdType theIdParam,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
long submittedCount = myMdmSubmitSvc.submitSourceResourceToMdm(theIdParam);
|
long submittedCount = myMdmSubmitSvc.submitSourceResourceToMdm(theIdParam, theRequest);
|
||||||
return buildMdmOutParametersWithCount(submittedCount);
|
return buildMdmOutParametersWithCount(submittedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +270,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
|
||||||
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA, typeName = "string") IPrimitiveType<String> theCriteria,
|
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA, typeName = "string") IPrimitiveType<String> theCriteria,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
String criteria = convertStringTypeToString(theCriteria);
|
String criteria = convertStringTypeToString(theCriteria);
|
||||||
long submittedCount = myMdmSubmitSvc.submitPatientTypeToMdm(criteria);
|
long submittedCount = myMdmSubmitSvc.submitPatientTypeToMdm(criteria, theRequest);
|
||||||
return buildMdmOutParametersWithCount(submittedCount);
|
return buildMdmOutParametersWithCount(submittedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,7 +280,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
|
||||||
public IBaseParameters mdmBatchPractitionerInstance(
|
public IBaseParameters mdmBatchPractitionerInstance(
|
||||||
@IdParam IIdType theIdParam,
|
@IdParam IIdType theIdParam,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
long submittedCount = myMdmSubmitSvc.submitSourceResourceToMdm(theIdParam);
|
long submittedCount = myMdmSubmitSvc.submitSourceResourceToMdm(theIdParam, theRequest);
|
||||||
return buildMdmOutParametersWithCount(submittedCount);
|
return buildMdmOutParametersWithCount(submittedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -289,7 +291,7 @@ public class MdmProviderDstu3Plus extends BaseMdmProvider {
|
||||||
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA, typeName = "string") IPrimitiveType<String> theCriteria,
|
@OperationParam(name = ProviderConstants.MDM_BATCH_RUN_CRITERIA, typeName = "string") IPrimitiveType<String> theCriteria,
|
||||||
RequestDetails theRequest) {
|
RequestDetails theRequest) {
|
||||||
String criteria = convertStringTypeToString(theCriteria);
|
String criteria = convertStringTypeToString(theCriteria);
|
||||||
long submittedCount = myMdmSubmitSvc.submitPractitionerTypeToMdm(criteria);
|
long submittedCount = myMdmSubmitSvc.submitPractitionerTypeToMdm(criteria, theRequest);
|
||||||
return buildMdmOutParametersWithCount(submittedCount);
|
return buildMdmOutParametersWithCount(submittedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@ import ca.uhn.fhir.mdm.api.IMdmSurvivorshipService;
|
||||||
import ca.uhn.fhir.mdm.log.Logs;
|
import ca.uhn.fhir.mdm.log.Logs;
|
||||||
import ca.uhn.fhir.mdm.model.CanonicalEID;
|
import ca.uhn.fhir.mdm.model.CanonicalEID;
|
||||||
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
import ca.uhn.fhir.mdm.model.MdmTransactionContext;
|
||||||
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.util.FhirTerser;
|
import ca.uhn.fhir.util.FhirTerser;
|
||||||
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;
|
||||||
|
@ -98,6 +99,9 @@ public class GoldenResourceHelper {
|
||||||
MdmResourceUtil.setMdmManaged(newGoldenResource);
|
MdmResourceUtil.setMdmManaged(newGoldenResource);
|
||||||
MdmResourceUtil.setGoldenResource(newGoldenResource);
|
MdmResourceUtil.setGoldenResource(newGoldenResource);
|
||||||
|
|
||||||
|
// add the partition id to the new resource
|
||||||
|
newGoldenResource.setUserData(Constants.RESOURCE_PARTITION_ID, theIncomingResource.getUserData(Constants.RESOURCE_PARTITION_ID));
|
||||||
|
|
||||||
return (T) newGoldenResource;
|
return (T) newGoldenResource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,9 @@ package ca.uhn.fhir.mdm.util;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
||||||
import ca.uhn.fhir.mdm.api.MdmConstants;
|
import ca.uhn.fhir.mdm.api.MdmConstants;
|
||||||
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
import ca.uhn.fhir.mdm.api.MdmMatchResultEnum;
|
||||||
import ca.uhn.fhir.mdm.api.IMdmSettings;
|
|
||||||
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
import ca.uhn.fhir.rest.server.provider.ProviderConstants;
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
@ -111,4 +111,13 @@ public class MessageHelper {
|
||||||
public String getMessageForFailedGoldenResourceLoad(String theParamName, String theGoldenResourceId) {
|
public String getMessageForFailedGoldenResourceLoad(String theParamName, String theGoldenResourceId) {
|
||||||
return theGoldenResourceId + " used as parameter [" + theParamName + "] could not be loaded as a golden resource, as it appears to be lacking the golden resource meta tags.";
|
return theGoldenResourceId + " used as parameter [" + theParamName + "] could not be loaded as a golden resource, as it appears to be lacking the golden resource meta tags.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMessageForMismatchPartition(IAnyResource theGoldenRecord, IAnyResource theSourceResource) {
|
||||||
|
return getMessageForMismatchPartition(theGoldenRecord.getIdElement().toVersionless().toString(),
|
||||||
|
theSourceResource.getIdElement().toVersionless().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageForMismatchPartition(String theGoldenRecord, String theSourceResource) {
|
||||||
|
return theGoldenRecord + " and " + theSourceResource + " are stored in different partitions. This operation is only available for resources on the same partition.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -88,6 +88,7 @@ public class ProviderConstants {
|
||||||
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";
|
||||||
|
public static final String MDM_QUERY_PARTITION_IDS = "partitionIds";
|
||||||
public static final String MDM_QUERY_LINKS_MATCH_RESULT = "matchResult";
|
public static final String MDM_QUERY_LINKS_MATCH_RESULT = "matchResult";
|
||||||
public static final String MDM_QUERY_LINKS_LINK_SOURCE = "linkSource";
|
public static final String MDM_QUERY_LINKS_LINK_SOURCE = "linkSource";
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>
|
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hapi-fhir-spring-boot-sample-client-okhttp</artifactId>
|
<artifactId>hapi-fhir-spring-boot-sample-client-okhttp</artifactId>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId>
|
<artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-spring-boot</artifactId>
|
<artifactId>hapi-fhir-spring-boot</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir</artifactId>
|
<artifactId>hapi-fhir</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../pom.xml</relativePath>
|
<relativePath>../pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-deployable-pom</artifactId>
|
<artifactId>hapi-deployable-pom</artifactId>
|
||||||
<version>6.0.0-PRE9-SNAPSHOT</version>
|
<version>6.0.0-PRE10-SNAPSHOT</version>
|
||||||
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ import java.util.Set;
|
||||||
public interface IRequestPartitionHelperSvc {
|
public interface IRequestPartitionHelperSvc {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
RequestPartitionId determineReadPartitionForRequest(@Nullable RequestDetails theRequest, String theResourceType, @Nonnull ReadPartitionIdRequestDetails theDetails);
|
RequestPartitionId determineReadPartitionForRequest(@Nullable RequestDetails theRequest, String theResourceType, ReadPartitionIdRequestDetails theDetails);
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
default RequestPartitionId determineReadPartitionForRequestForRead(RequestDetails theRequest, String theResourceType, IIdType theId) {
|
default RequestPartitionId determineReadPartitionForRequestForRead(RequestDetails theRequest, String theResourceType, IIdType theId) {
|
||||||
|
@ -55,6 +55,9 @@ public interface IRequestPartitionHelperSvc {
|
||||||
return determineReadPartitionForRequest(theRequest, theResourceType, details);
|
return determineReadPartitionForRequest(theRequest, theResourceType, details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
default void validateHasPartitionPermissions(RequestDetails theRequest, String theResourceType, RequestPartitionId theRequestPartitionId){}
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
RequestPartitionId determineCreatePartitionForRequest(@Nullable RequestDetails theRequest, @Nonnull IBaseResource theResource, @Nonnull String theResourceType);
|
RequestPartitionId determineCreatePartitionForRequest(@Nullable RequestDetails theRequest, @Nonnull IBaseResource theResource, @Nonnull String theResourceType);
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue