Merge remote-tracking branch 'remotes/origin/master' into ks-20200302-near-chain
This commit is contained in:
commit
5b5ef08500
|
@ -0,0 +1,21 @@
|
||||||
|
package ca.uhn.fhir.rest.param;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
public class QualifierDetailsTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBlacklist() {
|
||||||
|
|
||||||
|
QualifierDetails details = new QualifierDetails();
|
||||||
|
details.setColonQualifier(":Patient");
|
||||||
|
assertFalse(details.passes(null, Sets.newHashSet(":Patient")));
|
||||||
|
assertTrue(details.passes(null, Sets.newHashSet(":Observation")));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 1742
|
||||||
|
title: "When validating a resource, the validator will now report an error if the resource declares conformance
|
||||||
|
to an unknown or invalid profile URL via the `Resource.meta.profile` declaration. Previously this was a warning
|
||||||
|
and did not block successful validation."
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 1742
|
||||||
|
title: "When performing a search in the JPA server where the only parameter was a `_has` parameter,
|
||||||
|
the server did not respect the resource typename being searched for, causing false positive
|
||||||
|
search results. This has been corrected."
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
type: add
|
||||||
|
issue: 1742
|
||||||
|
title: When performing a terminology delta ADD operation, if the number of codes being added is large
|
||||||
|
the codes will be added in small batches via an asynchronous scheduled task in order to avoid overwhelming
|
||||||
|
the database with a large operation.
|
|
@ -48,6 +48,11 @@
|
||||||
<artifactId>commons-csv</artifactId>
|
<artifactId>commons-csv</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>co.elastic.apm</groupId>
|
||||||
|
<artifactId>apm-agent-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||||
<artifactId>hapi-fhir-base</artifactId>
|
<artifactId>hapi-fhir-base</artifactId>
|
||||||
|
|
|
@ -102,7 +102,7 @@ public class DaoConfig {
|
||||||
/**
|
/**
|
||||||
* update setter javadoc if default changes
|
* update setter javadoc if default changes
|
||||||
*/
|
*/
|
||||||
private int myDeferIndexingForCodesystemsOfSize = 2000;
|
private int myDeferIndexingForCodesystemsOfSize = 100;
|
||||||
private boolean myDeleteStaleSearches = true;
|
private boolean myDeleteStaleSearches = true;
|
||||||
private boolean myEnforceReferentialIntegrityOnDelete = true;
|
private boolean myEnforceReferentialIntegrityOnDelete = true;
|
||||||
private boolean myUniqueIndexesEnabled = true;
|
private boolean myUniqueIndexesEnabled = true;
|
||||||
|
@ -392,7 +392,7 @@ public class DaoConfig {
|
||||||
* the code system will be indexed later in an incremental process in order to
|
* the code system will be indexed later in an incremental process in order to
|
||||||
* avoid overwhelming Lucene with a huge number of codes in a single operation.
|
* avoid overwhelming Lucene with a huge number of codes in a single operation.
|
||||||
* <p>
|
* <p>
|
||||||
* Defaults to 2000
|
* Defaults to 100
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public int getDeferIndexingForCodesystemsOfSize() {
|
public int getDeferIndexingForCodesystemsOfSize() {
|
||||||
|
@ -404,7 +404,7 @@ public class DaoConfig {
|
||||||
* the code system will be indexed later in an incremental process in order to
|
* the code system will be indexed later in an incremental process in order to
|
||||||
* avoid overwhelming Lucene with a huge number of codes in a single operation.
|
* avoid overwhelming Lucene with a huge number of codes in a single operation.
|
||||||
* <p>
|
* <p>
|
||||||
* Defaults to 2000
|
* Defaults to 100
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public void setDeferIndexingForCodesystemsOfSize(int theDeferIndexingForCodesystemsOfSize) {
|
public void setDeferIndexingForCodesystemsOfSize(int theDeferIndexingForCodesystemsOfSize) {
|
||||||
|
|
|
@ -50,4 +50,8 @@ public interface ITermConceptDao extends JpaRepository<TermConcept, Long> {
|
||||||
@Query("SELECT t FROM TermConcept t WHERE t.myIndexStatus = null")
|
@Query("SELECT t FROM TermConcept t WHERE t.myIndexStatus = null")
|
||||||
Page<TermConcept> findResourcesRequiringReindexing(Pageable thePageRequest);
|
Page<TermConcept> findResourcesRequiringReindexing(Pageable thePageRequest);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("DELETE FROM TermConcept t WHERE t.myId = :pid")
|
||||||
|
void deleteByPid(@Param("pid") Long theId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
|
||||||
import org.springframework.data.domain.Pageable;
|
import org.springframework.data.domain.Pageable;
|
||||||
import org.springframework.data.domain.Slice;
|
import org.springframework.data.domain.Slice;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
import org.springframework.data.jpa.repository.Query;
|
import org.springframework.data.jpa.repository.Query;
|
||||||
import org.springframework.data.repository.query.Param;
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
@ -39,4 +40,9 @@ public interface ITermConceptParentChildLinkDao extends JpaRepository<TermConcep
|
||||||
|
|
||||||
@Query("SELECT t.myPid FROM TermConceptParentChildLink t WHERE t.myCodeSystem.myId = :cs_pid")
|
@Query("SELECT t.myPid FROM TermConceptParentChildLink t WHERE t.myCodeSystem.myId = :cs_pid")
|
||||||
Slice<Long> findIdsByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid);
|
Slice<Long> findIdsByCodeSystemVersion(Pageable thePage, @Param("cs_pid") Long thePid);
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("DELETE FROM TermConceptParentChildLink t WHERE t.myChildPid = :pid OR t.myParentPid = :pid")
|
||||||
|
void deleteByConceptPid(@Param("pid") Long theId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -430,7 +430,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Constants.PARAM_HAS:
|
case Constants.PARAM_HAS:
|
||||||
addPredicateHas(theAndOrParams, theRequest);
|
addPredicateHas(theResourceName, theAndOrParams, theRequest);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Constants.PARAM_TAG:
|
case Constants.PARAM_TAG:
|
||||||
|
@ -756,7 +756,7 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPredicateHas(List<List<IQueryParameterType>> theHasParameters, RequestDetails theRequest) {
|
private void addPredicateHas(String theResourceType, List<List<IQueryParameterType>> theHasParameters, RequestDetails theRequest) {
|
||||||
|
|
||||||
for (List<? extends IQueryParameterType> nextOrList : theHasParameters) {
|
for (List<? extends IQueryParameterType> nextOrList : theHasParameters) {
|
||||||
|
|
||||||
|
@ -811,8 +811,9 @@ class PredicateBuilderReference extends BasePredicateBuilder {
|
||||||
|
|
||||||
Join<ResourceTable, ResourceLink> join = myQueryRoot.join("myResourceLinksAsTarget", JoinType.LEFT);
|
Join<ResourceTable, ResourceLink> join = myQueryRoot.join("myResourceLinksAsTarget", JoinType.LEFT);
|
||||||
Predicate pathPredicate = myPredicateBuilder.createResourceLinkPathPredicate(targetResourceType, paramReference, join);
|
Predicate pathPredicate = myPredicateBuilder.createResourceLinkPathPredicate(targetResourceType, paramReference, join);
|
||||||
Predicate pidPredicate = join.get("mySourceResourcePid").in(subQ);
|
Predicate sourceTypePredicate = myBuilder.equal(join.get("myTargetResourceType"), theResourceType);
|
||||||
Predicate andPredicate = myBuilder.and(pathPredicate, pidPredicate);
|
Predicate sourcePidPredicate = join.get("mySourceResourcePid").in(subQ);
|
||||||
|
Predicate andPredicate = myBuilder.and(pathPredicate, sourcePidPredicate, sourceTypePredicate);
|
||||||
myQueryRoot.addPredicate(andPredicate);
|
myQueryRoot.addPredicate(andPredicate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class TermCodeSystem implements Serializable {
|
||||||
@Column(name = "CODE_SYSTEM_URI", nullable = false, length = MAX_URL_LENGTH)
|
@Column(name = "CODE_SYSTEM_URI", nullable = false, length = MAX_URL_LENGTH)
|
||||||
private String myCodeSystemUri;
|
private String myCodeSystemUri;
|
||||||
|
|
||||||
@OneToOne()
|
@OneToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "CURRENT_VERSION_PID", referencedColumnName = "PID", nullable = true, foreignKey = @ForeignKey(name = "FK_TRMCODESYSTEM_CURVER"))
|
@JoinColumn(name = "CURRENT_VERSION_PID", referencedColumnName = "PID", nullable = true, foreignKey = @ForeignKey(name = "FK_TRMCODESYSTEM_CURVER"))
|
||||||
private TermCodeSystemVersion myCurrentVersion;
|
private TermCodeSystemVersion myCurrentVersion;
|
||||||
@Column(name = "CURRENT_VERSION_PID", nullable = true, insertable = false, updatable = false)
|
@Column(name = "CURRENT_VERSION_PID", nullable = true, insertable = false, updatable = false)
|
||||||
|
@ -57,7 +57,7 @@ public class TermCodeSystem implements Serializable {
|
||||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CODESYSTEM_PID")
|
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CODESYSTEM_PID")
|
||||||
@Column(name = "PID")
|
@Column(name = "PID")
|
||||||
private Long myPid;
|
private Long myPid;
|
||||||
@OneToOne()
|
@OneToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_TRMCODESYSTEM_RES"))
|
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_TRMCODESYSTEM_RES"))
|
||||||
private ResourceTable myResource;
|
private ResourceTable myResource;
|
||||||
@Column(name = "RES_ID", insertable = false, updatable = false)
|
@Column(name = "RES_ID", insertable = false, updatable = false)
|
||||||
|
|
|
@ -50,7 +50,7 @@ public class TermCodeSystemVersion implements Serializable {
|
||||||
@Column(name = "PID")
|
@Column(name = "PID")
|
||||||
private Long myId;
|
private Long myId;
|
||||||
|
|
||||||
@OneToOne()
|
@OneToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_CODESYSVER_RES_ID"))
|
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_CODESYSVER_RES_ID"))
|
||||||
private ResourceTable myResource;
|
private ResourceTable myResource;
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public class TermCodeSystemVersion implements Serializable {
|
||||||
* This was added in HAPI FHIR 3.3.0 and is nullable just to avoid migration
|
* This was added in HAPI FHIR 3.3.0 and is nullable just to avoid migration
|
||||||
* issued. It should be made non-nullable at some point.
|
* issued. It should be made non-nullable at some point.
|
||||||
*/
|
*/
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "CODESYSTEM_PID", referencedColumnName = "PID", nullable = true, foreignKey = @ForeignKey(name = "FK_CODESYSVER_CS_ID"))
|
@JoinColumn(name = "CODESYSTEM_PID", referencedColumnName = "PID", nullable = true, foreignKey = @ForeignKey(name = "FK_CODESYSVER_CS_ID"))
|
||||||
private TermCodeSystem myCodeSystem;
|
private TermCodeSystem myCodeSystem;
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ public class TermCodeSystemVersion implements Serializable {
|
||||||
private Long myCodeSystemPid;
|
private Long myCodeSystemPid;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@OneToOne(mappedBy = "myCurrentVersion", optional = true)
|
@OneToOne(mappedBy = "myCurrentVersion", optional = true, fetch = FetchType.LAZY)
|
||||||
private TermCodeSystem myCodeSystemHavingThisVersionAsCurrentVersionIfAny;
|
private TermCodeSystem myCodeSystemHavingThisVersionAsCurrentVersionIfAny;
|
||||||
|
|
||||||
@Column(name = "CS_DISPLAY", nullable = true, updatable = false, length = MAX_VERSION_LENGTH)
|
@Column(name = "CS_DISPLAY", nullable = true, updatable = false, length = MAX_VERSION_LENGTH)
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class TermConcept implements Serializable {
|
||||||
@Temporal(TemporalType.TIMESTAMP)
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
@Column(name = "CONCEPT_UPDATED", nullable = true)
|
@Column(name = "CONCEPT_UPDATED", nullable = true)
|
||||||
private Date myUpdated;
|
private Date myUpdated;
|
||||||
@ManyToOne()
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "CODESYSTEM_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPT_PID_CS_PID"))
|
@JoinColumn(name = "CODESYSTEM_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPT_PID_CS_PID"))
|
||||||
private TermCodeSystemVersion myCodeSystem;
|
private TermCodeSystemVersion myCodeSystem;
|
||||||
@Column(name = "CODESYSTEM_PID", insertable = false, updatable = false)
|
@Column(name = "CODESYSTEM_PID", insertable = false, updatable = false)
|
||||||
|
@ -79,11 +79,11 @@ public class TermConcept implements Serializable {
|
||||||
@Field(name = "myDisplayPhonetic", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompletePhoneticAnalyzer"))
|
@Field(name = "myDisplayPhonetic", index = org.hibernate.search.annotations.Index.YES, store = Store.NO, analyze = Analyze.YES, analyzer = @Analyzer(definition = "autocompletePhoneticAnalyzer"))
|
||||||
})
|
})
|
||||||
private String myDisplay;
|
private String myDisplay;
|
||||||
@OneToMany(mappedBy = "myConcept", orphanRemoval = false)
|
@OneToMany(mappedBy = "myConcept", orphanRemoval = false, fetch = FetchType.LAZY)
|
||||||
@Field(name = "PROPmyProperties", analyzer = @Analyzer(definition = "termConceptPropertyAnalyzer"))
|
@Field(name = "PROPmyProperties", analyzer = @Analyzer(definition = "termConceptPropertyAnalyzer"))
|
||||||
@FieldBridge(impl = TermConceptPropertyFieldBridge.class)
|
@FieldBridge(impl = TermConceptPropertyFieldBridge.class)
|
||||||
private Collection<TermConceptProperty> myProperties;
|
private Collection<TermConceptProperty> myProperties;
|
||||||
@OneToMany(mappedBy = "myConcept", orphanRemoval = false)
|
@OneToMany(mappedBy = "myConcept", orphanRemoval = false, fetch = FetchType.LAZY)
|
||||||
private Collection<TermConceptDesignation> myDesignations;
|
private Collection<TermConceptDesignation> myDesignations;
|
||||||
@Id()
|
@Id()
|
||||||
@SequenceGenerator(name = "SEQ_CONCEPT_PID", sequenceName = "SEQ_CONCEPT_PID")
|
@SequenceGenerator(name = "SEQ_CONCEPT_PID", sequenceName = "SEQ_CONCEPT_PID")
|
||||||
|
|
|
@ -39,7 +39,7 @@ public class TermConceptDesignation implements Serializable {
|
||||||
public static final int MAX_LENGTH = 500;
|
public static final int MAX_LENGTH = 500;
|
||||||
public static final int MAX_VAL_LENGTH = 2000;
|
public static final int MAX_VAL_LENGTH = 2000;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "CONCEPT_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTDESIG_CONCEPT"))
|
@JoinColumn(name = "CONCEPT_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTDESIG_CONCEPT"))
|
||||||
private TermConcept myConcept;
|
private TermConcept myConcept;
|
||||||
@Id()
|
@Id()
|
||||||
|
@ -62,7 +62,7 @@ public class TermConceptDesignation implements Serializable {
|
||||||
*
|
*
|
||||||
* @since 3.5.0
|
* @since 3.5.0
|
||||||
*/
|
*/
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "CS_VER_PID", nullable = true, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTDESIG_CSV"))
|
@JoinColumn(name = "CS_VER_PID", nullable = true, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTDESIG_CSV"))
|
||||||
private TermCodeSystemVersion myCodeSystemVersion;
|
private TermCodeSystemVersion myCodeSystemVersion;
|
||||||
|
|
||||||
|
|
|
@ -32,14 +32,14 @@ import java.io.Serializable;
|
||||||
public class TermConceptParentChildLink implements Serializable {
|
public class TermConceptParentChildLink implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
@ManyToOne()
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "CHILD_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CHILD"))
|
@JoinColumn(name = "CHILD_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CHILD"))
|
||||||
private TermConcept myChild;
|
private TermConcept myChild;
|
||||||
|
|
||||||
@Column(name = "CHILD_PID", insertable = false, updatable = false)
|
@Column(name = "CHILD_PID", insertable = false, updatable = false)
|
||||||
private Long myChildPid;
|
private Long myChildPid;
|
||||||
|
|
||||||
@ManyToOne()
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "CODESYSTEM_PID", nullable = false, foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CS"))
|
@JoinColumn(name = "CODESYSTEM_PID", nullable = false, foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CS"))
|
||||||
private TermCodeSystemVersion myCodeSystem;
|
private TermCodeSystemVersion myCodeSystem;
|
||||||
|
|
||||||
|
@ -47,7 +47,7 @@ public class TermConceptParentChildLink implements Serializable {
|
||||||
@Fields({@Field(name = "myCodeSystemVersionPid")})
|
@Fields({@Field(name = "myCodeSystemVersionPid")})
|
||||||
private long myCodeSystemVersionPid;
|
private long myCodeSystemVersionPid;
|
||||||
|
|
||||||
@ManyToOne(cascade = {})
|
@ManyToOne(fetch = FetchType.LAZY, cascade = {})
|
||||||
@JoinColumn(name = "PARENT_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_PARENT"))
|
@JoinColumn(name = "PARENT_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_PARENT"))
|
||||||
private TermConcept myParent;
|
private TermConcept myParent;
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ public class TermConceptProperty implements Serializable {
|
||||||
private static final int MAX_LENGTH = 500;
|
private static final int MAX_LENGTH = 500;
|
||||||
static final int MAX_PROPTYPE_ENUM_LENGTH = 6;
|
static final int MAX_PROPTYPE_ENUM_LENGTH = 6;
|
||||||
|
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "CONCEPT_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTPROP_CONCEPT"))
|
@JoinColumn(name = "CONCEPT_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTPROP_CONCEPT"))
|
||||||
private TermConcept myConcept;
|
private TermConcept myConcept;
|
||||||
/**
|
/**
|
||||||
|
@ -52,7 +52,7 @@ public class TermConceptProperty implements Serializable {
|
||||||
*
|
*
|
||||||
* @since 3.5.0
|
* @since 3.5.0
|
||||||
*/
|
*/
|
||||||
@ManyToOne
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "CS_VER_PID", nullable = true, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTPROP_CSV"))
|
@JoinColumn(name = "CS_VER_PID", nullable = true, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTPROP_CSV"))
|
||||||
private TermCodeSystemVersion myCodeSystemVersion;
|
private TermCodeSystemVersion myCodeSystemVersion;
|
||||||
@Id()
|
@Id()
|
||||||
|
|
|
@ -56,6 +56,9 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.util.ICachedSearchDetails;
|
import ca.uhn.fhir.rest.server.util.ICachedSearchDetails;
|
||||||
import ca.uhn.fhir.util.AsyncUtil;
|
import ca.uhn.fhir.util.AsyncUtil;
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
import ca.uhn.fhir.util.StopWatch;
|
||||||
|
import co.elastic.apm.api.ElasticApm;
|
||||||
|
import co.elastic.apm.api.Span;
|
||||||
|
import co.elastic.apm.api.Transaction;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||||
|
@ -601,7 +604,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
private List<ResourcePersistentId> myPreviouslyAddedResourcePids;
|
private List<ResourcePersistentId> myPreviouslyAddedResourcePids;
|
||||||
private Integer myMaxResultsToFetch;
|
private Integer myMaxResultsToFetch;
|
||||||
private SearchRuntimeDetails mySearchRuntimeDetails;
|
private SearchRuntimeDetails mySearchRuntimeDetails;
|
||||||
|
private Transaction myParentTransaction;
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
|
@ -614,6 +617,7 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
mySearchRuntimeDetails = new SearchRuntimeDetails(theRequest, mySearch.getUuid());
|
mySearchRuntimeDetails = new SearchRuntimeDetails(theRequest, mySearch.getUuid());
|
||||||
mySearchRuntimeDetails.setQueryString(theParams.toNormalizedQueryString(theCallingDao.getContext()));
|
mySearchRuntimeDetails.setQueryString(theParams.toNormalizedQueryString(theCallingDao.getContext()));
|
||||||
myRequest = theRequest;
|
myRequest = theRequest;
|
||||||
|
myParentTransaction = ElasticApm.currentTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -841,7 +845,8 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
@Override
|
@Override
|
||||||
public Void call() {
|
public Void call() {
|
||||||
StopWatch sw = new StopWatch();
|
StopWatch sw = new StopWatch();
|
||||||
|
Span span = myParentTransaction.startSpan("db", "query", "search");
|
||||||
|
span.setName("FHIR Database Search");
|
||||||
try {
|
try {
|
||||||
// Create an initial search in the DB and give it an ID
|
// Create an initial search in the DB and give it an ID
|
||||||
saveSearch();
|
saveSearch();
|
||||||
|
@ -897,7 +902,6 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
ourLog.error("Failed during search loading after {}ms", sw.getMillis(), t);
|
ourLog.error("Failed during search loading after {}ms", sw.getMillis(), t);
|
||||||
}
|
}
|
||||||
myUnsyncedPids.clear();
|
myUnsyncedPids.clear();
|
||||||
|
|
||||||
Throwable rootCause = ExceptionUtils.getRootCause(t);
|
Throwable rootCause = ExceptionUtils.getRootCause(t);
|
||||||
rootCause = defaultIfNull(rootCause, t);
|
rootCause = defaultIfNull(rootCause, t);
|
||||||
|
|
||||||
|
@ -924,12 +928,13 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
|
||||||
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FAILED, params);
|
JpaInterceptorBroadcaster.doCallHooks(myInterceptorBroadcaster, myRequest, Pointcut.JPA_PERFTRACE_SEARCH_FAILED, params);
|
||||||
|
|
||||||
saveSearch();
|
saveSearch();
|
||||||
|
span.captureException(t);
|
||||||
} finally {
|
} finally {
|
||||||
|
|
||||||
myIdToSearchTask.remove(mySearch.getUuid());
|
myIdToSearchTask.remove(mySearch.getUuid());
|
||||||
myInitialCollectionLatch.countDown();
|
myInitialCollectionLatch.countDown();
|
||||||
markComplete();
|
markComplete();
|
||||||
|
span.end();
|
||||||
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -1246,6 +1246,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public List<VersionIndependentConcept> findCodesAbove(String theSystem, String theCode) {
|
public List<VersionIndependentConcept> findCodesAbove(String theSystem, String theCode) {
|
||||||
TermCodeSystem cs = getCodeSystem(theSystem);
|
TermCodeSystem cs = getCodeSystem(theSystem);
|
||||||
|
@ -1277,6 +1278,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc, ApplicationCo
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
@Override
|
@Override
|
||||||
public List<VersionIndependentConcept> findCodesBelow(String theSystem, String theCode) {
|
public List<VersionIndependentConcept> findCodesBelow(String theSystem, String theCode) {
|
||||||
TermCodeSystem cs = getCodeSystem(theSystem);
|
TermCodeSystem cs = getCodeSystem(theSystem);
|
||||||
|
|
|
@ -223,8 +223,16 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
||||||
* save parent concepts first (it's way too slow to do that)
|
* save parent concepts first (it's way too slow to do that)
|
||||||
*/
|
*/
|
||||||
if (theConcept.getId() == null) {
|
if (theConcept.getId() == null) {
|
||||||
|
boolean needToSaveParents = false;
|
||||||
|
for (TermConceptParentChildLink next : theConcept.getParents()) {
|
||||||
|
if (next.getParent().getId() == null) {
|
||||||
|
needToSaveParents = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (needToSaveParents) {
|
||||||
retVal += ensureParentsSaved(theConcept.getParents());
|
retVal += ensureParentsSaved(theConcept.getParents());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (theConcept.getId() == null || theConcept.getIndexStatus() == null) {
|
if (theConcept.getId() == null || theConcept.getIndexStatus() == null) {
|
||||||
retVal++;
|
retVal++;
|
||||||
|
@ -485,7 +493,7 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
||||||
conceptToAdd.setCodeSystemVersion(theCsv);
|
conceptToAdd.setCodeSystemVersion(theCsv);
|
||||||
|
|
||||||
if (theStatisticsTracker.getUpdatedConceptCount() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) {
|
if (theStatisticsTracker.getUpdatedConceptCount() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) {
|
||||||
conceptToAdd = myConceptDao.save(conceptToAdd);
|
saveConcept(conceptToAdd);
|
||||||
Long nextConceptPid = conceptToAdd.getId();
|
Long nextConceptPid = conceptToAdd.getId();
|
||||||
Validate.notNull(nextConceptPid);
|
Validate.notNull(nextConceptPid);
|
||||||
} else {
|
} else {
|
||||||
|
@ -655,12 +663,15 @@ public class TermCodeSystemStorageSvcImpl implements ITermCodeSystemStorageSvc {
|
||||||
private void deleteConceptChildrenAndConcept(TermConcept theConcept, AtomicInteger theRemoveCounter) {
|
private void deleteConceptChildrenAndConcept(TermConcept theConcept, AtomicInteger theRemoveCounter) {
|
||||||
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
|
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
|
||||||
deleteConceptChildrenAndConcept(nextChildLink.getChild(), theRemoveCounter);
|
deleteConceptChildrenAndConcept(nextChildLink.getChild(), theRemoveCounter);
|
||||||
myConceptParentChildLinkDao.delete(nextChildLink);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
myConceptParentChildLinkDao.deleteByConceptPid(theConcept.getId());
|
||||||
|
|
||||||
myConceptDesignationDao.deleteAll(theConcept.getDesignations());
|
myConceptDesignationDao.deleteAll(theConcept.getDesignations());
|
||||||
myConceptPropertyDao.deleteAll(theConcept.getProperties());
|
myConceptPropertyDao.deleteAll(theConcept.getProperties());
|
||||||
myConceptDao.delete(theConcept);
|
|
||||||
|
ourLog.info("Deleting concept {} - Code {}", theConcept.getId(), theConcept.getCode());
|
||||||
|
myConceptDao.deleteByPid(theConcept.getId());
|
||||||
theRemoveCounter.incrementAndGet();
|
theRemoveCounter.incrementAndGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,13 @@ public class TermDeferredStorageSvcImpl implements ITermDeferredStorageSvc {
|
||||||
myDeferredValueSets.addAll(theValueSets);
|
myDeferredValueSets.addAll(theValueSets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveAllDeferred() {
|
||||||
|
while (!isStorageQueueEmpty()) {
|
||||||
|
saveDeferred();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setProcessDeferred(boolean theProcessDeferred) {
|
public void setProcessDeferred(boolean theProcessDeferred) {
|
||||||
myProcessDeferred = theProcessDeferred;
|
myProcessDeferred = theProcessDeferred;
|
||||||
|
|
|
@ -50,4 +50,9 @@ public interface ITermDeferredStorageSvc {
|
||||||
void addConceptMapsToStorageQueue(List<ConceptMap> theConceptMaps);
|
void addConceptMapsToStorageQueue(List<ConceptMap> theConceptMaps);
|
||||||
|
|
||||||
void addValueSetsToStorageQueue(List<ValueSet> theValueSets);
|
void addValueSetsToStorageQueue(List<ValueSet> theValueSets);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is mostly here for unit tests - Saves any and all deferred concepts and links
|
||||||
|
*/
|
||||||
|
void saveAllDeferred();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
|
||||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||||
import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl;
|
import ca.uhn.fhir.jpa.term.TermReindexingSvcImpl;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermDeferredStorageSvc;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||||
|
@ -134,8 +135,13 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION" , cs, table);
|
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION" , cs, table);
|
||||||
|
|
||||||
|
myTermDeferredStorageSvc.saveAllDeferred();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ITermDeferredStorageSvc myTermDeferredStorageSvc;
|
||||||
|
|
||||||
private void createExternalCsAndLocalVs() {
|
private void createExternalCsAndLocalVs() {
|
||||||
CodeSystem codeSystem = createExternalCs();
|
CodeSystem codeSystem = createExternalCs();
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,6 @@ import org.hl7.fhir.dstu3.hapi.validation.FhirInstanceValidator;
|
||||||
import org.hl7.fhir.dstu3.model.*;
|
import org.hl7.fhir.dstu3.model.*;
|
||||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||||
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
|
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
|
||||||
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.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r5.utils.IResourceValidator;
|
import org.hl7.fhir.r5.utils.IResourceValidator;
|
||||||
|
@ -323,12 +322,16 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
ValidationModeEnum mode = ValidationModeEnum.CREATE;
|
ValidationModeEnum mode = ValidationModeEnum.CREATE;
|
||||||
String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input);
|
String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input);
|
||||||
MethodOutcome output = myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
|
try {
|
||||||
OperationOutcome oo = (OperationOutcome) output.getOperationOutcome();
|
myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
|
||||||
|
fail();
|
||||||
|
} catch (PreconditionFailedException e) {
|
||||||
|
OperationOutcome oo = (OperationOutcome) e.getOperationOutcome();
|
||||||
String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo);
|
String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo);
|
||||||
ourLog.info(outputString);
|
ourLog.info(outputString);
|
||||||
assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked"));
|
assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked"));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testValidateForCreate() {
|
public void testValidateForCreate() {
|
||||||
|
|
|
@ -172,6 +172,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
|
||||||
@Qualifier("myDeviceDaoR4")
|
@Qualifier("myDeviceDaoR4")
|
||||||
protected IFhirResourceDao<Device> myDeviceDao;
|
protected IFhirResourceDao<Device> myDeviceDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
@Qualifier("myProvenanceDaoR4")
|
||||||
|
protected IFhirResourceDao<Provenance> myProvenanceDao;
|
||||||
|
@Autowired
|
||||||
@Qualifier("myDiagnosticReportDaoR4")
|
@Qualifier("myDiagnosticReportDaoR4")
|
||||||
protected IFhirResourceDao<DiagnosticReport> myDiagnosticReportDao;
|
protected IFhirResourceDao<DiagnosticReport> myDiagnosticReportDao;
|
||||||
@Autowired
|
@Autowired
|
||||||
|
|
|
@ -589,6 +589,41 @@ public class FhirResourceDaoR4SearchNoFtTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHasLimitsByType() {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setActive(true);
|
||||||
|
IIdType patientId = myPatientDao.create(patient).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Encounter encounter = new Encounter();
|
||||||
|
encounter.setStatus(Encounter.EncounterStatus.ARRIVED);
|
||||||
|
IIdType encounterId = myEncounterDao.create(encounter).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Device device = new Device();
|
||||||
|
device.setManufacturer("Acme");
|
||||||
|
IIdType deviceId = myDeviceDao.create(device).getId().toUnqualifiedVersionless();
|
||||||
|
|
||||||
|
Provenance provenance = new Provenance();
|
||||||
|
provenance.addTarget().setReferenceElement(patientId);
|
||||||
|
provenance.addTarget().setReferenceElement(encounterId);
|
||||||
|
provenance.addAgent().setWho(new Reference(deviceId));
|
||||||
|
myProvenanceDao.create(provenance);
|
||||||
|
|
||||||
|
String criteria = "_has:Provenance:target:agent=" + deviceId.getValue();
|
||||||
|
SearchParameterMap map = myMatchUrlService.translateMatchUrl(criteria, myFhirCtx.getResourceDefinition(Encounter.class));
|
||||||
|
|
||||||
|
map.setLoadSynchronous(true);
|
||||||
|
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
|
IBundleProvider results = myEncounterDao.search(map);
|
||||||
|
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||||
|
|
||||||
|
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
||||||
|
assertThat(ids, containsInAnyOrder(encounterId.getValue()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHasParameter() {
|
public void testHasParameter() {
|
||||||
IIdType pid0;
|
IIdType pid0;
|
||||||
|
|
|
@ -164,6 +164,8 @@ public class FhirResourceDaoR4TerminologyTest extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
|
|
||||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs, table);
|
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), URL_MY_CODE_SYSTEM, "SYSTEM NAME", "SYSTEM VERSION", cs, table);
|
||||||
|
|
||||||
|
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createLocalCsAndVs() {
|
private void createLocalCsAndVs() {
|
||||||
|
|
|
@ -147,6 +147,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
}
|
}
|
||||||
myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://loinc.org", codesToAdd);
|
myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://loinc.org", codesToAdd);
|
||||||
|
|
||||||
|
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||||
|
|
||||||
// Create a valueset
|
// Create a valueset
|
||||||
ValueSet vs = new ValueSet();
|
ValueSet vs = new ValueSet();
|
||||||
vs.setUrl("http://example.com/fhir/ValueSet/observation-vitalsignresult");
|
vs.setUrl("http://example.com/fhir/ValueSet/observation-vitalsignresult");
|
||||||
|
@ -395,12 +397,15 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
ValidationModeEnum mode = ValidationModeEnum.CREATE;
|
ValidationModeEnum mode = ValidationModeEnum.CREATE;
|
||||||
String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input);
|
String encoded = myFhirCtx.newJsonParser().encodeResourceToString(input);
|
||||||
|
|
||||||
MethodOutcome output = myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
|
try {
|
||||||
org.hl7.fhir.r4.model.OperationOutcome oo = (org.hl7.fhir.r4.model.OperationOutcome) output.getOperationOutcome();
|
myObservationDao.validate(input, null, encoded, EncodingEnum.JSON, mode, null, mySrd);
|
||||||
|
fail();
|
||||||
|
} catch (PreconditionFailedException e) {
|
||||||
|
org.hl7.fhir.r4.model.OperationOutcome oo = (org.hl7.fhir.r4.model.OperationOutcome) e.getOperationOutcome();
|
||||||
String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo);
|
String outputString = myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(oo);
|
||||||
ourLog.info(outputString);
|
ourLog.info(outputString);
|
||||||
assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked"));
|
assertThat(outputString, containsString("Profile reference 'http://example.com/StructureDefinition/testValidateResourceContainingProfileDeclarationInvalid' could not be resolved, so has not been checked"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -614,6 +619,8 @@ public class FhirResourceDaoR4ValidateTest extends BaseJpaR4Test {
|
||||||
upload("/r4/uscore/ValueSet-omb-race-category.json");
|
upload("/r4/uscore/ValueSet-omb-race-category.json");
|
||||||
upload("/r4/uscore/ValueSet-us-core-usps-state.json");
|
upload("/r4/uscore/ValueSet-us-core-usps-state.json");
|
||||||
|
|
||||||
|
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||||
|
|
||||||
{
|
{
|
||||||
String resource = loadResource("/r4/uscore/patient-resource-badcode.json");
|
String resource = loadResource("/r4/uscore/patient-resource-badcode.json");
|
||||||
IBaseResource parsedResource = myFhirCtx.newJsonParser().parseResource(resource);
|
IBaseResource parsedResource = myFhirCtx.newJsonParser().parseResource(resource);
|
||||||
|
|
|
@ -15,6 +15,7 @@ import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||||
import ca.uhn.fhir.rest.api.Constants;
|
import ca.uhn.fhir.rest.api.Constants;
|
||||||
import ca.uhn.fhir.rest.api.*;
|
import ca.uhn.fhir.rest.api.*;
|
||||||
|
import ca.uhn.fhir.rest.client.apache.ResourceEntity;
|
||||||
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
|
||||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||||
|
@ -26,6 +27,9 @@ import ca.uhn.fhir.rest.server.exceptions.*;
|
||||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||||
import ca.uhn.fhir.util.StopWatch;
|
import ca.uhn.fhir.util.StopWatch;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
|
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||||
|
import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||||
|
import ca.uhn.fhir.validation.ValidationResult;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
@ -52,6 +56,7 @@ import org.hl7.fhir.r4.model.Observation.ObservationStatus;
|
||||||
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
|
import org.hl7.fhir.r4.model.Questionnaire.QuestionnaireItemType;
|
||||||
import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
|
import org.hl7.fhir.r4.model.Subscription.SubscriptionChannelType;
|
||||||
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
|
import org.hl7.fhir.r4.model.Subscription.SubscriptionStatus;
|
||||||
|
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.test.util.AopTestUtils;
|
import org.springframework.test.util.AopTestUtils;
|
||||||
|
@ -2260,6 +2265,28 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateResourceContainingProfileDeclarationDoesntResolve() throws IOException {
|
||||||
|
Observation input = new Observation();
|
||||||
|
input.getText().setDiv(new XhtmlNode().setValue("<div>AA</div>")).setStatus(Narrative.NarrativeStatus.GENERATED);
|
||||||
|
input.getMeta().addProfile("http://foo/structuredefinition/myprofile");
|
||||||
|
|
||||||
|
input.getCode().setText("Hello");
|
||||||
|
input.setStatus(ObservationStatus.FINAL);
|
||||||
|
|
||||||
|
HttpPost post = new HttpPost(ourServerBase + "/Observation/$validate?_pretty=true");
|
||||||
|
post.setEntity(new ResourceEntity(myFhirCtx, input));
|
||||||
|
|
||||||
|
try (CloseableHttpResponse resp = ourHttpClient.execute(post)) {
|
||||||
|
String respString = IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8);
|
||||||
|
ourLog.info(respString);
|
||||||
|
assertEquals(412, resp.getStatusLine().getStatusCode());
|
||||||
|
assertThat(respString, containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@Test
|
@Test
|
||||||
public void testFullTextSearch() throws Exception {
|
public void testFullTextSearch() throws Exception {
|
||||||
|
|
|
@ -31,8 +31,10 @@ import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.countMatches;
|
||||||
import static org.apache.commons.lang3.StringUtils.leftPad;
|
import static org.apache.commons.lang3.StringUtils.leftPad;
|
||||||
import static org.hamcrest.CoreMatchers.containsString;
|
import static org.hamcrest.CoreMatchers.containsString;
|
||||||
|
import static org.hamcrest.Matchers.matchesPattern;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
@ -126,17 +128,23 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test {
|
||||||
ourLog.info("All concepts: {}", myTermConceptDao.findAll());
|
ourLog.info("All concepts: {}", myTermConceptDao.findAll());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
myCaptureQueriesListener.clear();
|
||||||
|
|
||||||
delta = new CustomTerminologySet();
|
delta = new CustomTerminologySet();
|
||||||
TermConcept root = delta.addRootConcept("RootA", "Root A");
|
TermConcept root = delta.addRootConcept("RootA", "Root A");
|
||||||
root.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAA").setDisplay("Child AA");
|
root.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAA").setDisplay("Child AA");
|
||||||
root.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAB").setDisplay("Child AB");
|
root.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA).setCode("ChildAB").setDisplay("Child AB");
|
||||||
myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", delta);
|
myTermCodeSystemStorageSvc.applyDeltaCodeSystemsAdd("http://foo/cs", delta);
|
||||||
|
|
||||||
|
myCaptureQueriesListener.logAllQueriesForCurrentThread();
|
||||||
|
|
||||||
assertHierarchyContains(
|
assertHierarchyContains(
|
||||||
"RootA seq=0",
|
"RootA seq=0",
|
||||||
" ChildAA seq=0",
|
" ChildAA seq=0",
|
||||||
" ChildAB seq=1",
|
" ChildAB seq=1",
|
||||||
"RootB seq=0"
|
"RootB seq=0"
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -175,6 +183,12 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test {
|
||||||
);
|
);
|
||||||
assertEquals(2, outcome.getUpdatedConceptCount());
|
assertEquals(2, outcome.getUpdatedConceptCount());
|
||||||
|
|
||||||
|
runInTransaction(() -> {
|
||||||
|
TermConcept concept = myTermSvc.findCode("http://foo/cs", "ChildAA").orElseThrow(() -> new IllegalStateException());
|
||||||
|
assertEquals(2, concept.getParents().size());
|
||||||
|
assertThat(concept.getParentPidsAsString(), matchesPattern("^[0-9]+ [0-9]+$"));
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -538,9 +552,15 @@ public class TerminologySvcDeltaR4Test extends BaseJpaR4Test {
|
||||||
assertEquals(true, runInTransaction(() -> myTermSvc.findCode("http://foo/cs", "codeAAA").isPresent()));
|
assertEquals(true, runInTransaction(() -> myTermSvc.findCode("http://foo/cs", "codeAAA").isPresent()));
|
||||||
|
|
||||||
// Remove CodeA
|
// Remove CodeA
|
||||||
delta = new CustomTerminologySet();
|
myCaptureQueriesListener.clear();
|
||||||
delta.addRootConcept("codeA");
|
runInTransaction(()->{
|
||||||
myTermCodeSystemStorageSvc.applyDeltaCodeSystemsRemove("http://foo/cs", delta);
|
CustomTerminologySet delta2 = new CustomTerminologySet();
|
||||||
|
delta2.addRootConcept("codeA");
|
||||||
|
myTermCodeSystemStorageSvc.applyDeltaCodeSystemsRemove("http://foo/cs", delta2);
|
||||||
|
});
|
||||||
|
myCaptureQueriesListener.logAllQueriesForCurrentThread();
|
||||||
|
|
||||||
|
ourLog.info("*** Done removing");
|
||||||
|
|
||||||
assertEquals(false, runInTransaction(() -> myTermSvc.findCode("http://foo/cs", "codeB").isPresent()));
|
assertEquals(false, runInTransaction(() -> myTermSvc.findCode("http://foo/cs", "codeB").isPresent()));
|
||||||
assertEquals(false, runInTransaction(() -> myTermSvc.findCode("http://foo/cs", "codeA").isPresent()));
|
assertEquals(false, runInTransaction(() -> myTermSvc.findCode("http://foo/cs", "codeA").isPresent()));
|
||||||
|
|
|
@ -100,6 +100,8 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
||||||
|
|
||||||
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs, table);
|
myTermCodeSystemStorageSvc.storeNewCodeSystemVersion(new ResourcePersistentId(table.getId()), CS_URL, "SYSTEM NAME", "SYSTEM VERSION", cs, table);
|
||||||
|
|
||||||
|
myTerminologyDeferredStorageSvc.saveAllDeferred();
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -171,11 +171,18 @@ public class ValidatorWrapper {
|
||||||
for (int i = 0; i < messages.size(); i++) {
|
for (int i = 0; i < messages.size(); i++) {
|
||||||
ValidationMessage next = messages.get(i);
|
ValidationMessage next = messages.get(i);
|
||||||
String message = next.getMessage();
|
String message = next.getMessage();
|
||||||
|
|
||||||
|
// TODO: are these still needed?
|
||||||
if ("Binding has no source, so can't be checked".equals(message) ||
|
if ("Binding has no source, so can't be checked".equals(message) ||
|
||||||
"ValueSet http://hl7.org/fhir/ValueSet/mimetypes not found".equals(message)) {
|
"ValueSet http://hl7.org/fhir/ValueSet/mimetypes not found".equals(message)) {
|
||||||
messages.remove(i);
|
messages.remove(i);
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.endsWith("' could not be resolved, so has not been checked") && next.getLevel() == ValidationMessage.IssueSeverity.WARNING) {
|
||||||
|
next.setLevel(ValidationMessage.IssueSeverity.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return messages;
|
return messages;
|
||||||
|
|
|
@ -66,10 +66,13 @@ import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.*;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.ArgumentMatchers.nullable;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class FhirInstanceValidatorR4Test extends BaseTest {
|
public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
|
|
||||||
|
@ -280,6 +283,33 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
assertEquals("Primitive types must have a value that is not empty", all.get(0).getMessage());
|
assertEquals("Primitive types must have a value that is not empty", all.get(0).getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See #1740
|
||||||
|
*/
|
||||||
|
@Ignore
|
||||||
|
@Test
|
||||||
|
public void testValidateScalarInRepeatableField() {
|
||||||
|
String operationDefinition = "{\n" +
|
||||||
|
" \"resourceType\": \"OperationDefinition\",\n" +
|
||||||
|
" \"name\": \"Questionnaire\",\n" +
|
||||||
|
" \"status\": \"draft\",\n" +
|
||||||
|
" \"kind\" : \"operation\",\n" +
|
||||||
|
" \"code\": \"populate\",\n" +
|
||||||
|
" \"resource\": \"Patient\",\n" + // should be array
|
||||||
|
" \"system\": false,\n" + " " +
|
||||||
|
" \"type\": false,\n" +
|
||||||
|
" \"instance\": true\n" +
|
||||||
|
"}";
|
||||||
|
|
||||||
|
FhirValidator val = ourCtx.newValidator();
|
||||||
|
val.registerValidatorModule(new FhirInstanceValidator(myDefaultValidationSupport));
|
||||||
|
|
||||||
|
ValidationResult result = val.validateWithResult(operationDefinition);
|
||||||
|
List<SingleValidationMessage> all = logResultsAndReturnAll(result);
|
||||||
|
assertFalse(result.isSuccessful());
|
||||||
|
assertEquals("Primitive types must have a value that is not empty", all.get(0).getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #1676 - We should ignore schema location
|
* See #1676 - We should ignore schema location
|
||||||
*/
|
*/
|
||||||
|
@ -400,7 +430,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||||
errors = errors
|
errors = errors
|
||||||
.stream()
|
.stream()
|
||||||
.filter(t->t.getMessage().contains("Bundle entry missing fullUrl"))
|
.filter(t -> t.getMessage().contains("Bundle entry missing fullUrl"))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
assertEquals(5, errors.size());
|
assertEquals(5, errors.size());
|
||||||
}
|
}
|
||||||
|
@ -658,7 +688,6 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
.setCode("acd");
|
.setCode("acd");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Should pass
|
// Should pass
|
||||||
ValidationResult output = val.validateWithResult(input);
|
ValidationResult output = val.validateWithResult(input);
|
||||||
List<SingleValidationMessage> all = logResultsAndReturnErrorOnes(output);
|
List<SingleValidationMessage> all = logResultsAndReturnErrorOnes(output);
|
||||||
|
@ -1038,7 +1067,10 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
myInstanceVal.setValidationSupport(myMockSupport);
|
myInstanceVal.setValidationSupport(myMockSupport);
|
||||||
ValidationResult output = myVal.validateWithResult(input);
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
List<SingleValidationMessage> errors = logResultsAndReturnNonInformationalOnes(output);
|
||||||
assertThat(errors.toString(), containsString("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked"));
|
|
||||||
|
assertEquals(1, errors.size());
|
||||||
|
assertEquals("Profile reference 'http://foo/structuredefinition/myprofile' could not be resolved, so has not been checked", errors.get(0).getMessage());
|
||||||
|
assertEquals(ResultSeverityEnum.ERROR, errors.get(0).getSeverity());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1110,7 +1142,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
" },\n" +
|
" },\n" +
|
||||||
" \"text\": \"210.0-925.\"\n" +
|
" \"text\": \"210.0-925.\"\n" +
|
||||||
" }\n" +
|
" }\n" +
|
||||||
" ]"+
|
" ]" +
|
||||||
"}";
|
"}";
|
||||||
ourLog.info(input);
|
ourLog.info(input);
|
||||||
ValidationResult output = myVal.validateWithResult(input);
|
ValidationResult output = myVal.validateWithResult(input);
|
||||||
|
@ -1263,7 +1295,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
|
||||||
String encoded = loadResource("/r4/r4-caredove-bundle.json");
|
String encoded = loadResource("/r4/r4-caredove-bundle.json");
|
||||||
|
|
||||||
IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class);
|
IResourceValidator.IValidatorResourceFetcher resourceFetcher = mock(IResourceValidator.IValidatorResourceFetcher.class);
|
||||||
when(resourceFetcher.validationPolicy(any(),anyString(), anyString())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS);
|
when(resourceFetcher.validationPolicy(any(), anyString(), anyString())).thenReturn(IResourceValidator.ReferenceValidationPolicy.CHECK_TYPE_IF_EXISTS);
|
||||||
myInstanceVal.setValidatorResourceFetcher(resourceFetcher);
|
myInstanceVal.setValidatorResourceFetcher(resourceFetcher);
|
||||||
myVal.validateWithResult(encoded);
|
myVal.validateWithResult(encoded);
|
||||||
|
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -679,6 +679,7 @@
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
<ebay_cors_filter_version>1.0.1</ebay_cors_filter_version>
|
<ebay_cors_filter_version>1.0.1</ebay_cors_filter_version>
|
||||||
|
|
||||||
|
<elastic_apm_version>1.13.0</elastic_apm_version>
|
||||||
<!-- Site properties -->
|
<!-- Site properties -->
|
||||||
<fontawesomeVersion>5.4.1</fontawesomeVersion>
|
<fontawesomeVersion>5.4.1</fontawesomeVersion>
|
||||||
</properties>
|
</properties>
|
||||||
|
@ -1036,6 +1037,11 @@
|
||||||
<artifactId>httpcore</artifactId>
|
<artifactId>httpcore</artifactId>
|
||||||
<version>${httpcore_version}</version>
|
<version>${httpcore_version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>co.elastic.apm</groupId>
|
||||||
|
<artifactId>apm-agent-api</artifactId>
|
||||||
|
<version>${elastic_apm_version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.jena</groupId>
|
<groupId>org.apache.jena</groupId>
|
||||||
<artifactId>apache-jena-libs</artifactId>
|
<artifactId>apache-jena-libs</artifactId>
|
||||||
|
|
Loading…
Reference in New Issue