Merge branch 'master' into jd-20240905-add-batch-and-chunk-ids-to-logging-context

This commit is contained in:
jdar 2024-09-25 16:20:32 -07:00
commit e148c76a09
118 changed files with 1399 additions and 1043 deletions

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -136,6 +136,7 @@ public enum VersionEnum {
V6_8_5, V6_8_5,
V6_8_6, V6_8_6,
V6_8_7, V6_8_7,
V6_8_8,
V6_9_0, V6_9_0,

View File

@ -4,7 +4,7 @@
<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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>HAPI FHIR BOM</name> <name>HAPI FHIR BOM</name>
@ -12,7 +12,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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 6218
jira: SMILE-8370
title: "Previously, when disabling the 'Enforce Referential Integrity on Write' setting, referential integrity was still
partially enforced when posting a resource with an invalid reference. This has now been fixed."

View File

@ -0,0 +1,4 @@
---
type: fix
issue: 6262
title: "Previously, when a `extension` was passed in as a part of the CDS hooks request, it would result in a `400 service not found`. This behaviour has now been fixed."

View File

@ -0,0 +1,10 @@
---
type: remove
issue: 6283
title: "Hibernate Search Fulltext fields which were unused
have been removed from indexing.
This will reduce storage usage in Lucene and Elasticsearch.
The fields that were removed are: `myNarrativeTextEdgeNGram`,
`myNarrativeTextNGram`, `myNarrativeTextPhonetic`, `myContentTextEdgeNGram`,
`myContentTextNGram`, `myContentTextPhonetic`.
"

View File

@ -0,0 +1,5 @@
---
type: fix
issue: 6290
title: "Previously, a specific migration task was using the `TRIM()` function, which does not exist in MSSQL 2012. This was causing migrations targeting MSSQL 2012 to fail.
This has been corrected and replaced with usage of a combination of LTRIM() and RTRIM(). Thanks to Primož Delopst at Better for the contribution!"

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 6305
title: "Previously, when having StorageSettings#getIndexMissingFields() == IndexEnabledEnum.DISABLED (default value)
and attempting to search with the missing qualifier against a resource type with multiple search parameters of type reference,
the returned results would be incorrect. This has been fixed."

View File

@ -4,6 +4,7 @@
title: "The version of a few dependencies have been bumped to more recent versions title: "The version of a few dependencies have been bumped to more recent versions
(dependent HAPI modules listed in brackets): (dependent HAPI modules listed in brackets):
<ul> <ul>
<li>org.hl7.fhir.core (Base): 6.3.18 -&gt; 6.3.23</li>
<li>Bower/Moment.js (hapi-fhir-testpage-overlay): 2.27.0 -&gt; 2.29.4</li> <li>Bower/Moment.js (hapi-fhir-testpage-overlay): 2.27.0 -&gt; 2.29.4</li>
<li>htmlunit (Base): 3.9.0 -&gt; 3.11.0</li> <li>htmlunit (Base): 3.9.0 -&gt; 3.11.0</li>
<li>Elasticsearch (Base): 8.11.1 -&gt; 8.14.3</li> <li>Elasticsearch (Base): 8.11.1 -&gt; 8.14.3</li>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -588,8 +588,8 @@ public class PackageInstallerSvcImpl implements IPackageInstallerSvc {
if (statusTypes.isEmpty()) { if (statusTypes.isEmpty()) {
return true; return true;
} }
// Resource has a null status field // Resource has no status field, or an explicitly null one
if (statusTypes.get(0).getValue() == null) { if (!statusTypes.get(0).hasValue() || statusTypes.get(0).getValue() == null) {
return false; return false;
} }
// Resource has a status, and we need to check based on type // Resource has a status, and we need to check based on type

View File

@ -800,14 +800,19 @@ public class ResourceLinkPredicateBuilder extends BaseJoiningPredicateBuilder im
subquery.addCustomColumns(1); subquery.addCustomColumns(1);
subquery.addFromTable(getTable()); subquery.addFromTable(getTable());
String resourceType = theParams.getResourceTablePredicateBuilder().getResourceType();
RuntimeSearchParam paramDefinition =
mySearchParamRegistry.getRuntimeSearchParam(resourceType, theParams.getParamName());
List<String> pathList = paramDefinition.getPathsSplitForResourceType(resourceType);
Condition subQueryCondition = ComboCondition.and( Condition subQueryCondition = ComboCondition.and(
BinaryCondition.equalTo( BinaryCondition.equalTo(
getResourceIdColumn(), getResourceIdColumn(),
theParams.getResourceTablePredicateBuilder().getResourceIdColumn()), theParams.getResourceTablePredicateBuilder().getResourceIdColumn()),
BinaryCondition.equalTo( BinaryCondition.equalTo(getResourceTypeColumn(), generatePlaceholder(resourceType)),
getResourceTypeColumn(), ComboCondition.or(pathList.stream()
generatePlaceholder( .map(path -> BinaryCondition.equalTo(getColumnSourcePath(), generatePlaceholder(path)))
theParams.getResourceTablePredicateBuilder().getResourceType()))); .toArray(BinaryCondition[]::new)));
subquery.addCondition(subQueryCondition); subquery.addCondition(subQueryCondition);

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -3,7 +3,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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -3,7 +3,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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -34,6 +34,7 @@ public class PartitionSettings {
private boolean myAlwaysOpenNewTransactionForDifferentPartition; private boolean myAlwaysOpenNewTransactionForDifferentPartition;
private boolean myConditionalCreateDuplicateIdentifiersEnabled = false; private boolean myConditionalCreateDuplicateIdentifiersEnabled = false;
public PartitionSettings() {}
/** /**
* Should we always open a new database transaction if the partition context changes * Should we always open a new database transaction if the partition context changes
* *

View File

@ -119,21 +119,6 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
searchable = Searchable.YES, searchable = Searchable.YES,
projectable = Projectable.YES, projectable = Projectable.YES,
analyzer = "standardAnalyzer") analyzer = "standardAnalyzer")
@FullTextField(
name = "myContentTextEdgeNGram",
searchable = Searchable.YES,
projectable = Projectable.NO,
analyzer = "autocompleteEdgeAnalyzer")
@FullTextField(
name = "myContentTextNGram",
searchable = Searchable.YES,
projectable = Projectable.NO,
analyzer = "autocompleteNGramAnalyzer")
@FullTextField(
name = "myContentTextPhonetic",
searchable = Searchable.YES,
projectable = Projectable.NO,
analyzer = "autocompletePhoneticAnalyzer")
@OptimisticLock(excluded = true) @OptimisticLock(excluded = true)
@IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myVersion"))) @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myVersion")))
private String myContentText; private String myContentText;
@ -171,21 +156,6 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
searchable = Searchable.YES, searchable = Searchable.YES,
projectable = Projectable.YES, projectable = Projectable.YES,
analyzer = "standardAnalyzer") analyzer = "standardAnalyzer")
@FullTextField(
name = "myNarrativeTextEdgeNGram",
searchable = Searchable.YES,
projectable = Projectable.NO,
analyzer = "autocompleteEdgeAnalyzer")
@FullTextField(
name = "myNarrativeTextNGram",
searchable = Searchable.YES,
projectable = Projectable.NO,
analyzer = "autocompleteNGramAnalyzer")
@FullTextField(
name = "myNarrativeTextPhonetic",
searchable = Searchable.YES,
projectable = Projectable.NO,
analyzer = "autocompletePhoneticAnalyzer")
@OptimisticLock(excluded = true) @OptimisticLock(excluded = true)
@IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myVersion"))) @IndexingDependency(derivedFrom = @ObjectPath(@PropertyValue(propertyName = "myVersion")))
private String myNarrativeText; private String myNarrativeText;

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -44,13 +44,13 @@ public interface ISearchParamExtractor {
/** /**
* Constant for the {@literal theSearchParamFilter} parameters on this interface * Constant for the {@literal theSearchParamFilter} parameters on this interface
* indicating that all search parameters should be indexed. * indicating that all search parameters should be extracted.
*/ */
ISearchParamFilter ALL_PARAMS = t -> t; ISearchParamFilter ALL_PARAMS = t -> t;
/** /**
* Constant for the {@literal theSearchParamFilter} parameters on this interface * Constant for the {@literal theSearchParamFilter} parameters on this interface
* indicating that no search parameters should be indexed. * indicating that no search parameters should be extracted.
*/ */
ISearchParamFilter NO_PARAMS = t -> Collections.emptyList(); ISearchParamFilter NO_PARAMS = t -> Collections.emptyList();
@ -155,7 +155,7 @@ public interface ISearchParamExtractor {
interface ISearchParamFilter { interface ISearchParamFilter {
/** /**
* Given the list of search parameters for indexing, an implementation of this * Given the list of search parameters for extracting, an implementation of this
* interface may selectively remove any that it wants to remove (or can add if desired). * interface may selectively remove any that it wants to remove (or can add if desired).
* <p> * <p>
* Implementations must not modify the list that is passed in. If changes are * Implementations must not modify the list that is passed in. If changes are

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -1,19 +1,26 @@
package ca.uhn.fhir.jpa.dao.r4; package ca.uhn.fhir.jpa.dao.r4;
import static org.junit.jupiter.api.Assertions.assertEquals;
import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
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.exceptions.UnprocessableEntityException;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Reference; import org.hl7.fhir.r4.model.Reference;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import static org.assertj.core.api.Assertions.assertThat; import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
public class FhirResourceDaoR4ReferentialIntegrityTest extends BaseJpaR4Test { public class FhirResourceDaoR4ReferentialIntegrityTest extends BaseJpaR4Test {
@ -23,51 +30,168 @@ public class FhirResourceDaoR4ReferentialIntegrityTest extends BaseJpaR4Test {
myStorageSettings.setEnforceReferentialIntegrityOnWrite(new JpaStorageSettings().isEnforceReferentialIntegrityOnWrite()); myStorageSettings.setEnforceReferentialIntegrityOnWrite(new JpaStorageSettings().isEnforceReferentialIntegrityOnWrite());
myStorageSettings.setEnforceReferentialIntegrityOnDelete(new JpaStorageSettings().isEnforceReferentialIntegrityOnDelete()); myStorageSettings.setEnforceReferentialIntegrityOnDelete(new JpaStorageSettings().isEnforceReferentialIntegrityOnDelete());
myStorageSettings.setEnforceReferenceTargetTypes(new JpaStorageSettings().isEnforceReferenceTargetTypes()); myStorageSettings.setEnforceReferenceTargetTypes(new JpaStorageSettings().isEnforceReferenceTargetTypes());
myStorageSettings.setResourceClientIdStrategy(new JpaStorageSettings().getResourceClientIdStrategy());
} }
@Test @ParameterizedTest
public void testCreateUnknownReferenceFail() throws Exception { @MethodSource("paramsProvider_withResourceType")
public void referentialIntegrityOnWriteSetting_unknownIds_fullScopeTest(boolean theIsEnforceRefIntegrityEnabled,
JpaStorageSettings.ClientIdStrategyEnum theClientIdStrategy,
String theReferenceId) {
// Given
myStorageSettings.setEnforceReferentialIntegrityOnWrite(theIsEnforceRefIntegrityEnabled);
myStorageSettings.setEnforceReferenceTargetTypes(theIsEnforceRefIntegrityEnabled);
myStorageSettings.setResourceClientIdStrategy(theClientIdStrategy);
Patient p = new Patient(); Patient p = new Patient();
p.setManagingOrganization(new Reference("Organization/AAA")); p.setManagingOrganization(new Reference(theReferenceId));
if (theIsEnforceRefIntegrityEnabled) {
try { try {
// When
myPatientDao.create(p); myPatientDao.create(p);
fail(); fail();
} catch (InvalidRequestException e) { } catch (InvalidRequestException e) {
assertEquals(Msg.code(1094) + "Resource Organization/AAA not found, specified in path: Patient.managingOrganization", e.getMessage()); // Then
assertEquals(Msg.code(1094) + "Resource " + theReferenceId + " not found, specified in path: Patient.managingOrganization", e.getMessage());
}
} else {
// Disabled ref integrity on write case: all POSTs should succeed
// When
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
// Then
p = myPatientDao.read(id);
assertEquals(theReferenceId, p.getManagingOrganization().getReference());
}
} }
private static Stream<Arguments> paramsProvider_withResourceType() {
// theIsEnforceRefIntegrityEnabled, theClientIdStrategy, theReferenceId
// note: client ID is tested since resolving the resource reference is different depending on the strategy
return Stream.of(
Arguments.of(true, JpaStorageSettings.ClientIdStrategyEnum.ALPHANUMERIC, "Organization/123"),
Arguments.of(true, JpaStorageSettings.ClientIdStrategyEnum.ALPHANUMERIC, "Organization/A123"),
Arguments.of(true, JpaStorageSettings.ClientIdStrategyEnum.ANY, "Organization/123"),
Arguments.of(true, JpaStorageSettings.ClientIdStrategyEnum.ANY, "Organization/A123"),
Arguments.of(false, JpaStorageSettings.ClientIdStrategyEnum.ALPHANUMERIC, "Organization/123"),
Arguments.of(false, JpaStorageSettings.ClientIdStrategyEnum.ALPHANUMERIC, "Organization/A123"),
Arguments.of(false, JpaStorageSettings.ClientIdStrategyEnum.ANY, "Organization/123"),
Arguments.of(false, JpaStorageSettings.ClientIdStrategyEnum.ANY, "Organization/A123")
);
} }
@Test @Test
public void testCreateUnknownReferenceAllowed() { public void testRefIntegrityOnWrite_withReferenceIdOfAnotherResourceType() {
// Given
myStorageSettings.setResourceClientIdStrategy(JpaStorageSettings.ClientIdStrategyEnum.ALPHANUMERIC);
// Create Observation with some ID...
Observation obs = new Observation();
IIdType obsId = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
// And reference a non-existing Organization resource with the same ID as the Observation
Patient p = new Patient();
p.setManagingOrganization(new Reference(new IdType("Organization", obsId.getIdPart())));
try {
// When
myPatientDao.create(p);
fail();
} catch (UnprocessableEntityException e) {
// Then: identify that it is the wrong resource type, since ref integrity is enabled
assertEquals(Msg.code(1095) + "Resource contains reference to unknown resource ID Organization/" + obsId.getIdPart(), e.getMessage());
}
// Given: now disable referential integrity on write
myStorageSettings.setEnforceReferentialIntegrityOnWrite(false); myStorageSettings.setEnforceReferentialIntegrityOnWrite(false);
// When
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
// Then: this should now succeed
p = myPatientDao.read(id);
assertEquals("Organization/" + obsId.getIdPart(), p.getManagingOrganization().getReference());
}
@ParameterizedTest
@MethodSource("paramsProvider_noResourceType")
public void testRefIntegrityOnWrite_withValidReferenceId_shouldAlwaysSucceed(boolean theIsEnforceRefIntegrityEnabled,
JpaStorageSettings.ClientIdStrategyEnum theClientIdStrategy,
String theReferenceId) {
// Given
myStorageSettings.setEnforceReferentialIntegrityOnWrite(theIsEnforceRefIntegrityEnabled);
myStorageSettings.setEnforceReferenceTargetTypes(theIsEnforceRefIntegrityEnabled);
myStorageSettings.setResourceClientIdStrategy(theClientIdStrategy);
Organization obs = new Organization();
obs.setId(theReferenceId);
myOrganizationDao.update(obs, mySrd);
String qualifiedReferenceId = "Organization/" + theReferenceId;
Patient p = new Patient(); Patient p = new Patient();
p.setManagingOrganization(new Reference("Organization/AAA")); p.setManagingOrganization(new Reference(qualifiedReferenceId));
// When
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless(); IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
// Then
p = myPatientDao.read(id); p = myPatientDao.read(id);
assertEquals("Organization/AAA", p.getManagingOrganization().getReference()); assertEquals(qualifiedReferenceId, p.getManagingOrganization().getReference());
} }
@Test private static Stream<Arguments> paramsProvider_noResourceType() {
public void testCreateUnknownReferenceAllowed_NumericId() { // theIsEnforceRefIntegrityEnabled, theClientIdStrategy, theReferenceId
myStorageSettings.setEnforceReferentialIntegrityOnWrite(false); // note: client ID is tested since resolving the resource reference is different depending on the strategy
myStorageSettings.setEnforceReferenceTargetTypes(false); return Stream.of(
Arguments.of(true, JpaStorageSettings.ClientIdStrategyEnum.ALPHANUMERIC, "A123"),
Arguments.of(true, JpaStorageSettings.ClientIdStrategyEnum.ANY, "123"),
Arguments.of(true, JpaStorageSettings.ClientIdStrategyEnum.ANY, "A123"),
Arguments.of(false, JpaStorageSettings.ClientIdStrategyEnum.ALPHANUMERIC, "A123"),
Arguments.of(false, JpaStorageSettings.ClientIdStrategyEnum.ANY, "123"),
Arguments.of(false, JpaStorageSettings.ClientIdStrategyEnum.ANY, "A123")
);
}
@ParameterizedTest
@MethodSource("paramsProvider_noResourceType")
public void testRefIntegrityOnWrite_withReferenceIdOfDeletedResource(boolean theIsEnforceRefIntegrityEnabled,
JpaStorageSettings.ClientIdStrategyEnum theClientIdStrategy,
String theReferenceId) {
// Given
myStorageSettings.setEnforceReferentialIntegrityOnWrite(theIsEnforceRefIntegrityEnabled);
myStorageSettings.setEnforceReferenceTargetTypes(theIsEnforceRefIntegrityEnabled);
myStorageSettings.setResourceClientIdStrategy(theClientIdStrategy);
Organization obs = new Organization();
obs.setId(theReferenceId);
IIdType obsId = myOrganizationDao.update(obs, mySrd).getId();
String qualifiedReferenceId = "Organization/" + theReferenceId;
myOrganizationDao.delete(obsId, mySrd);
Patient p = new Patient(); Patient p = new Patient();
p.setManagingOrganization(new Reference("Organization/123")); p.setManagingOrganization(new Reference(qualifiedReferenceId));
if (theIsEnforceRefIntegrityEnabled) {
try {
// When
myPatientDao.create(p);
fail();
} catch (InvalidRequestException e) {
// Then
assertEquals(Msg.code(1096) + "Resource " + qualifiedReferenceId + " is deleted, specified in path: Patient.managingOrganization", e.getMessage());
}
} else {
// Disabled ref integrity on write case: all POSTs should succeed
// When
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless(); IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless();
// Then
p = myPatientDao.read(id); p = myPatientDao.read(id);
assertEquals("Organization/123", p.getManagingOrganization().getReference()); assertEquals(qualifiedReferenceId, p.getManagingOrganization().getReference());
}
} }
@Test @Test
public void testDeleteFail() throws Exception { public void testDeleteFail() {
Organization o = new Organization(); Organization o = new Organization();
o.setName("FOO"); o.setName("FOO");
IIdType oid = myOrganizationDao.create(o).getId().toUnqualifiedVersionless(); IIdType oid = myOrganizationDao.create(o).getId().toUnqualifiedVersionless();
@ -89,7 +213,7 @@ public class FhirResourceDaoR4ReferentialIntegrityTest extends BaseJpaR4Test {
} }
@Test @Test
public void testDeleteAllow() throws Exception { public void testDeleteAllow() {
myStorageSettings.setEnforceReferentialIntegrityOnDelete(false); myStorageSettings.setEnforceReferentialIntegrityOnDelete(false);
Organization o = new Organization(); Organization o = new Organization();

View File

@ -1,9 +1,10 @@
package ca.uhn.fhir.jpa.dao.r4; package ca.uhn.fhir.jpa.dao.r4;
import static org.junit.jupiter.api.Assertions.assertEquals;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel; import ca.uhn.fhir.jpa.model.entity.NormalizedQuantitySearchLevel;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantity;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamQuantityNormalized;
import ca.uhn.fhir.jpa.model.entity.StorageSettings;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.test.BaseJpaR4Test; import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.api.server.IBundleProvider;
@ -12,7 +13,6 @@ import ca.uhn.fhir.rest.param.QuantityParam;
import ca.uhn.fhir.rest.param.ReferenceParam; import ca.uhn.fhir.rest.param.ReferenceParam;
import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.DecimalType; import org.hl7.fhir.r4.model.DecimalType;
@ -26,57 +26,25 @@ import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Task; import org.hl7.fhir.r4.model.Task;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test { public class FhirResourceDaoR4SearchMissingTest {
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4SearchMissingTest.class);
@Nested
class IndexMissingDisabledTests extends MissingTests {
@BeforeEach @BeforeEach
public void beforeResetMissing() { public void before() {
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.ENABLED); myStorageSettings.setIndexMissingFields(StorageSettings.IndexEnabledEnum.DISABLED);
}
@AfterEach
public void afterResetSearch() {
myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_NOT_SUPPORTED);
}
@Test
public void testIndexMissingFieldsDisabledDontAllowInSearch_NonReference() {
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
SearchParameterMap params = new SearchParameterMap();
params.add(Patient.SP_ACTIVE, new StringParam().setMissing(true));
try {
myPatientDao.search(params);
} catch (MethodNotAllowedException e) {
assertEquals(Msg.code(985) + ":missing modifier is disabled on this server", e.getMessage());
}
}
@Test
public void testIndexMissingFieldsDisabledDontAllowInSearch_Reference() {
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
SearchParameterMap params = new SearchParameterMap();
params.add(Patient.SP_ORGANIZATION, new StringParam().setMissing(true));
try {
myPatientDao.search(params);
} catch (MethodNotAllowedException e) {
assertEquals(Msg.code(985) + ":missing modifier is disabled on this server", e.getMessage());
}
} }
@Test @Test
public void testIndexMissingFieldsDisabledDontCreateIndexes() { public void testIndexMissingFieldsDisabledDontCreateIndexes() {
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
Organization org = new Organization(); Organization org = new Organization();
org.setActive(true); org.setActive(true);
myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
@ -93,8 +61,6 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
@Test @Test
public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantitySearchSupported() { public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantitySearchSupported() {
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED); myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_SEARCH_SUPPORTED);
Organization org = new Organization(); Organization org = new Organization();
org.setActive(true); org.setActive(true);
@ -112,8 +78,6 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
@Test @Test
public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantityStorageSupported() { public void testIndexMissingFieldsDisabledDontCreateIndexesWithNormalizedQuantityStorageSupported() {
myStorageSettings.setIndexMissingFields(JpaStorageSettings.IndexEnabledEnum.DISABLED);
myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED); myStorageSettings.setNormalizedQuantitySearchLevel(NormalizedQuantitySearchLevel.NORMALIZED_QUANTITY_STORAGE_SUPPORTED);
Organization org = new Organization(); Organization org = new Organization();
org.setActive(true); org.setActive(true);
@ -128,6 +92,15 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
}); });
} }
}
@Nested
class IndexMissingEnabledTests extends MissingTests {
@BeforeEach
public void before() {
myStorageSettings.setIndexMissingFields(StorageSettings.IndexEnabledEnum.ENABLED);
}
@SuppressWarnings("unused") @SuppressWarnings("unused")
@Test @Test
@ -144,11 +117,10 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
task.setRequester(new Reference(oid1)); task.setRequester(new Reference(oid1));
tid1 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless(); tid1 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless();
} }
IIdType tid2;
{ {
Task task = new Task(); Task task = new Task();
task.setOwner(new Reference(oid1)); task.setOwner(new Reference(oid1));
tid2 = myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless(); myTaskDao.create(task, mySrd).getId().toUnqualifiedVersionless();
} }
IIdType oid2; IIdType oid2;
@ -170,27 +142,36 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
map = new SearchParameterMap(); map = new SearchParameterMap();
map.add(Organization.SP_NAME, new StringParam().setMissing(true)); map.add(Organization.SP_NAME, new StringParam().setMissing(true));
ids = toUnqualifiedVersionlessIds(myOrganizationDao.search(map)); ids = toUnqualifiedVersionlessIds(myOrganizationDao.search(map, mySrd));
assertThat(ids).containsExactly(oid1); assertThat(ids).containsExactly(oid1);
ourLog.info("Starting Search 2"); ourLog.info("Starting Search 2");
map = new SearchParameterMap(); map = new SearchParameterMap();
map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "true")); map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "true"));
ids = toUnqualifiedVersionlessIds(myTaskDao.search(map)); ids = toUnqualifiedVersionlessIds(myTaskDao.search(map, mySrd));
assertThat(ids).containsExactly(tid1); // NOT tid2 assertThat(ids).containsExactly(tid1); // NOT tid2
map = new SearchParameterMap(); map = new SearchParameterMap();
map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "false")); map.add(Task.SP_REQUESTER, new ReferenceParam("Organization", "name:missing", "false"));
ids = toUnqualifiedVersionlessIds(myTaskDao.search(map)); ids = toUnqualifiedVersionlessIds(myTaskDao.search(map, mySrd));
assertThat(ids).containsExactly(tid3); assertThat(ids).containsExactly(tid3);
map = new SearchParameterMap(); map = new SearchParameterMap();
map.add(Patient.SP_ORGANIZATION, new ReferenceParam("Organization", "name:missing", "true")); map.add(Patient.SP_ORGANIZATION, new ReferenceParam("Organization", "name:missing", "true"));
ids = toUnqualifiedVersionlessIds(myPatientDao.search(map)); ids = toUnqualifiedVersionlessIds(myPatientDao.search(map, mySrd));
assertThat(ids).isEmpty(); assertThat(ids).isEmpty();
} }
}
static class MissingTests extends BaseJpaR4Test {
@AfterEach
public void after() {
myStorageSettings.setIndexMissingFields(new StorageSettings().getIndexMissingFields());
myStorageSettings.setNormalizedQuantitySearchLevel(new StorageSettings().getNormalizedQuantitySearchLevel());
}
@Test @Test
public void testSearchWithMissingDate() { public void testSearchWithMissingDate() {
@ -217,7 +198,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
DateParam param = new DateParam(); DateParam param = new DateParam();
param.setMissing(false); param.setMissing(false);
params.add(Patient.SP_BIRTHDATE, param); params.add(Patient.SP_BIRTHDATE, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
assertThat(patients).containsSubsequence(notMissing); assertThat(patients).containsSubsequence(notMissing);
assertThat(patients).doesNotContainSubsequence(missing); assertThat(patients).doesNotContainSubsequence(missing);
} }
@ -227,7 +208,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
DateParam param = new DateParam(); DateParam param = new DateParam();
param.setMissing(true); param.setMissing(true);
params.add(Patient.SP_BIRTHDATE, param); params.add(Patient.SP_BIRTHDATE, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
assertThat(patients).containsSubsequence(missing); assertThat(patients).containsSubsequence(missing);
assertThat(patients).doesNotContainSubsequence(notMissing); assertThat(patients).doesNotContainSubsequence(notMissing);
} }
@ -238,9 +219,11 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
String locId = myLocationDao.create(new Location(), mySrd).getId().toUnqualifiedVersionless().getValue(); String locId = myLocationDao.create(new Location(), mySrd).getId().toUnqualifiedVersionless().getValue();
String locId2 = myLocationDao.create(new Location().setPosition(new Location.LocationPositionComponent(new DecimalType(10), new DecimalType(10))), mySrd).getId().toUnqualifiedVersionless().getValue(); String locId2 = myLocationDao.create(new Location().setPosition(new Location.LocationPositionComponent(new DecimalType(10), new DecimalType(10))), mySrd).getId().toUnqualifiedVersionless().getValue();
runInTransaction(() -> { runInTransaction(() -> ourLog.info("Coords:\n * {}",
ourLog.info("Coords:\n * {}", myResourceIndexedSearchParamCoordsDao.findAll().stream().map(t -> t.toString()).collect(Collectors.joining("\n * "))); myResourceIndexedSearchParamCoordsDao.findAll().stream()
}); .map(ResourceIndexedSearchParamCoords::toString).collect(Collectors.joining("\n * "))
)
);
{ {
SearchParameterMap params = new SearchParameterMap(); SearchParameterMap params = new SearchParameterMap();
@ -249,7 +232,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
param.setMissing(true); param.setMissing(true);
params.add(Location.SP_NEAR, param); params.add(Location.SP_NEAR, param);
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
List<String> patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params)); List<String> patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params, mySrd));
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0); myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
assertThat(patients).containsSubsequence(locId); assertThat(patients).containsSubsequence(locId);
assertThat(patients).doesNotContainSubsequence(locId2); assertThat(patients).doesNotContainSubsequence(locId2);
@ -260,7 +243,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
TokenParam param = new TokenParam(); TokenParam param = new TokenParam();
param.setMissing(false); param.setMissing(false);
params.add(Location.SP_NEAR, param); params.add(Location.SP_NEAR, param);
List<String> patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params)); List<String> patients = toUnqualifiedVersionlessIdValues(myLocationDao.search(params, mySrd));
assertThat(patients).containsSubsequence(locId2); assertThat(patients).containsSubsequence(locId2);
assertThat(patients).doesNotContainSubsequence(locId); assertThat(patients).doesNotContainSubsequence(locId);
} }
@ -271,15 +254,15 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
MedicationRequest mr1 = new MedicationRequest(); MedicationRequest mr1 = new MedicationRequest();
mr1.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral"); mr1.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral");
mr1.addDosageInstruction().getTiming().addEventElement().setValueAsString("2017-01-01"); mr1.addDosageInstruction().getTiming().addEventElement().setValueAsString("2017-01-01");
IIdType id1 = myMedicationRequestDao.create(mr1).getId().toUnqualifiedVersionless(); myMedicationRequestDao.create(mr1, mySrd).getId().toUnqualifiedVersionless();
MedicationRequest mr2 = new MedicationRequest(); MedicationRequest mr2 = new MedicationRequest();
mr2.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral"); mr2.addCategory().addCoding().setSystem("urn:medicationroute").setCode("oral");
IIdType id2 = myMedicationRequestDao.create(mr2).getId().toUnqualifiedVersionless(); IIdType id2 = myMedicationRequestDao.create(mr2, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap map = new SearchParameterMap(); SearchParameterMap map = new SearchParameterMap();
map.add(MedicationRequest.SP_DATE, new DateParam().setMissing(true)); map.add(MedicationRequest.SP_DATE, new DateParam().setMissing(true));
IBundleProvider results = myMedicationRequestDao.search(map); IBundleProvider results = myMedicationRequestDao.search(map, mySrd);
List<String> ids = toUnqualifiedVersionlessIdValues(results); List<String> ids = toUnqualifiedVersionlessIdValues(results);
assertThat(ids).containsExactly(id2.getValue()); assertThat(ids).containsExactly(id2.getValue());
@ -308,7 +291,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
QuantityParam param = new QuantityParam(); QuantityParam param = new QuantityParam();
param.setMissing(false); param.setMissing(false);
params.add(Observation.SP_VALUE_QUANTITY, param); params.add(Observation.SP_VALUE_QUANTITY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
assertThat(patients).doesNotContainSubsequence(missing); assertThat(patients).doesNotContainSubsequence(missing);
assertThat(patients).containsSubsequence(notMissing); assertThat(patients).containsSubsequence(notMissing);
} }
@ -318,7 +301,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
QuantityParam param = new QuantityParam(); QuantityParam param = new QuantityParam();
param.setMissing(true); param.setMissing(true);
params.add(Observation.SP_VALUE_QUANTITY, param); params.add(Observation.SP_VALUE_QUANTITY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
assertThat(patients).containsSubsequence(missing); assertThat(patients).containsSubsequence(missing);
assertThat(patients).doesNotContainSubsequence(notMissing); assertThat(patients).doesNotContainSubsequence(notMissing);
} }
@ -343,8 +326,16 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
} }
runInTransaction(() -> { runInTransaction(() -> {
ourLog.info("Quantity Indexes:\n * {}", myResourceIndexedSearchParamQuantityDao.findAll().stream().filter(t -> t.getParamName().equals("value-quantity")).map(t -> t.toString()).collect(Collectors.joining("\n * "))); ourLog.info("Quantity Indexes:\n * {}",
ourLog.info("Normalized Quantity Indexes:\n * {}", myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().filter(t -> t.getParamName().equals("value-quantity")).map(t -> t.toString()).collect(Collectors.joining("\n * "))); myResourceIndexedSearchParamQuantityDao.findAll().stream()
.filter(t -> t.getParamName().equals("value-quantity")).map(ResourceIndexedSearchParamQuantity::toString).collect(Collectors.joining("\n * ")
)
);
ourLog.info("Normalized Quantity Indexes:\n * {}",
myResourceIndexedSearchParamQuantityNormalizedDao.findAll().stream().
filter(t -> t.getParamName().equals("value-quantity")).map(ResourceIndexedSearchParamQuantityNormalized::toString).collect(Collectors.joining("\n * ")
)
);
}); });
// Quantity Param // Quantity Param
@ -354,7 +345,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
QuantityParam param = new QuantityParam(); QuantityParam param = new QuantityParam();
param.setMissing(false); param.setMissing(false);
params.add(Observation.SP_VALUE_QUANTITY, param); params.add(Observation.SP_VALUE_QUANTITY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
assertThat(patients).doesNotContainSubsequence(missing); assertThat(patients).doesNotContainSubsequence(missing);
assertThat(patients).containsSubsequence(notMissing); assertThat(patients).containsSubsequence(notMissing);
} }
@ -365,7 +356,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
param.setMissing(true); param.setMissing(true);
params.add(Observation.SP_VALUE_QUANTITY, param); params.add(Observation.SP_VALUE_QUANTITY, param);
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
myCaptureQueriesListener.logSelectQueries(); myCaptureQueriesListener.logSelectQueries();
assertThat(patients).containsSubsequence(missing); assertThat(patients).containsSubsequence(missing);
assertThat(patients).doesNotContainSubsequence(notMissing); assertThat(patients).doesNotContainSubsequence(notMissing);
@ -397,7 +388,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
QuantityParam param = new QuantityParam(); QuantityParam param = new QuantityParam();
param.setMissing(false); param.setMissing(false);
params.add(Observation.SP_VALUE_QUANTITY, param); params.add(Observation.SP_VALUE_QUANTITY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
assertThat(patients).doesNotContainSubsequence(missing); assertThat(patients).doesNotContainSubsequence(missing);
assertThat(patients).containsSubsequence(notMissing); assertThat(patients).containsSubsequence(notMissing);
} }
@ -407,7 +398,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
QuantityParam param = new QuantityParam(); QuantityParam param = new QuantityParam();
param.setMissing(true); param.setMissing(true);
params.add(Observation.SP_VALUE_QUANTITY, param); params.add(Observation.SP_VALUE_QUANTITY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
assertThat(patients).containsSubsequence(missing); assertThat(patients).containsSubsequence(missing);
assertThat(patients).doesNotContainSubsequence(notMissing); assertThat(patients).doesNotContainSubsequence(notMissing);
} }
@ -440,7 +431,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
ReferenceParam param = new ReferenceParam(); ReferenceParam param = new ReferenceParam();
param.setMissing(false); param.setMissing(false);
params.add(Patient.SP_ORGANIZATION, param); params.add(Patient.SP_ORGANIZATION, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
assertThat(patients).doesNotContainSubsequence(missing); assertThat(patients).doesNotContainSubsequence(missing);
assertThat(patients).containsSubsequence(notMissing); assertThat(patients).containsSubsequence(notMissing);
} }
@ -450,13 +441,36 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
ReferenceParam param = new ReferenceParam(); ReferenceParam param = new ReferenceParam();
param.setMissing(true); param.setMissing(true);
params.add(Patient.SP_ORGANIZATION, param); params.add(Patient.SP_ORGANIZATION, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
assertThat(patients).containsSubsequence(missing); assertThat(patients).containsSubsequence(missing);
assertThat(patients).doesNotContainSubsequence(notMissing); assertThat(patients).doesNotContainSubsequence(notMissing);
assertThat(patients).doesNotContainSubsequence(orgId); assertThat(patients).doesNotContainSubsequence(orgId);
} }
} }
@Test
public void testSearchWithMissingReference_resourceTypeWithMultipleReferences() {
IIdType patientId = createPatient();
IIdType observationId = createObservation(withSubject(patientId));
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_PERFORMER, new ReferenceParam().setMissing(true));
IBundleProvider bundleProvider = myObservationDao.search(params, mySrd);
assertThat(bundleProvider.getAllResourceIds()).containsExactly(observationId.getIdPart());
}
@Test
public void testSearchWithMissingReference_searchParamMultiplePaths() {
IIdType encounterId = createEncounter();
createObservation(withEncounter(encounterId.getValue()));
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_ENCOUNTER, new ReferenceParam().setMissing(true));
IBundleProvider bundleProvider = myObservationDao.search(params, mySrd);
assertThat(bundleProvider.getAllResourceIds()).isEmpty();
}
@Test @Test
public void testSearchWithMissingString() { public void testSearchWithMissingString() {
IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId(); IIdType orgId = myOrganizationDao.create(new Organization(), mySrd).getId();
@ -482,7 +496,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
StringParam param = new StringParam(); StringParam param = new StringParam();
param.setMissing(false); param.setMissing(false);
params.add(Patient.SP_FAMILY, param); params.add(Patient.SP_FAMILY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
assertThat(patients).doesNotContainSubsequence(missing); assertThat(patients).doesNotContainSubsequence(missing);
assertThat(patients).containsSubsequence(notMissing); assertThat(patients).containsSubsequence(notMissing);
} }
@ -492,7 +506,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
StringParam param = new StringParam(); StringParam param = new StringParam();
param.setMissing(true); param.setMissing(true);
params.add(Patient.SP_FAMILY, param); params.add(Patient.SP_FAMILY, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params, mySrd));
assertThat(patients).containsSubsequence(missing); assertThat(patients).containsSubsequence(missing);
assertThat(patients).doesNotContainSubsequence(notMissing); assertThat(patients).doesNotContainSubsequence(notMissing);
} }
@ -520,7 +534,7 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
TokenParam param = new TokenParam(); TokenParam param = new TokenParam();
param.setMissing(false); param.setMissing(false);
params.add(Observation.SP_CODE, param); params.add(Observation.SP_CODE, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
assertThat(patients).doesNotContainSubsequence(missing); assertThat(patients).doesNotContainSubsequence(missing);
assertThat(patients).containsSubsequence(notMissing); assertThat(patients).containsSubsequence(notMissing);
} }
@ -530,10 +544,11 @@ public class FhirResourceDaoR4SearchMissingTest extends BaseJpaR4Test {
TokenParam param = new TokenParam(); TokenParam param = new TokenParam();
param.setMissing(true); param.setMissing(true);
params.add(Observation.SP_CODE, param); params.add(Observation.SP_CODE, param);
List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params)); List<IIdType> patients = toUnqualifiedVersionlessIds(myObservationDao.search(params, mySrd));
assertThat(patients).containsSubsequence(missing); assertThat(patients).containsSubsequence(missing);
assertThat(patients).doesNotContainSubsequence(notMissing); assertThat(patients).doesNotContainSubsequence(notMissing);
} }
} }
}
} }

View File

@ -217,7 +217,7 @@ import static org.mockito.Mockito.when;
public class ResourceProviderR4Test extends BaseResourceProviderR4Test { public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4Test.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4Test.class);
private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw; private SearchCoordinatorSvcImpl mySearchCoordinatorSvcRaw;
private CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor(); private final CapturingInterceptor myCapturingInterceptor = new CapturingInterceptor();
@Autowired @Autowired
private ISearchDao mySearchEntityDao; private ISearchDao mySearchEntityDao;
@ -413,15 +413,15 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient pt1 = new Patient(); Patient pt1 = new Patient();
pt1.addName().setFamily("Elizabeth"); pt1.addName().setFamily("Elizabeth");
String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue(); String pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue();
Patient pt2 = new Patient(); Patient pt2 = new Patient();
pt2.addName().setFamily("fghijk"); pt2.addName().setFamily("fghijk");
String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue(); String pt2id = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless().getValue();
Patient pt3 = new Patient(); Patient pt3 = new Patient();
pt3.addName().setFamily("zzzzz"); pt3.addName().setFamily("zzzzz");
myPatientDao.create(pt3).getId().toUnqualifiedVersionless().getValue(); myPatientDao.create(pt3, mySrd).getId().toUnqualifiedVersionless().getValue();
Bundle output = myClient Bundle output = myClient
@ -450,7 +450,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient pt1 = new Patient(); Patient pt1 = new Patient();
pt1.addName().setFamily("Smith%"); pt1.addName().setFamily("Smith%");
String pt1id = myPatientDao.create(pt1).getId().toUnqualifiedVersionless().getValue(); String pt1id = myPatientDao.create(pt1, mySrd).getId().toUnqualifiedVersionless().getValue();
Bundle output = myClient Bundle output = myClient
.search() .search()
@ -463,7 +463,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient pt2 = new Patient(); Patient pt2 = new Patient();
pt2.addName().setFamily("Sm%ith"); pt2.addName().setFamily("Sm%ith");
String pt2id = myPatientDao.create(pt2).getId().toUnqualifiedVersionless().getValue(); String pt2id = myPatientDao.create(pt2, mySrd).getId().toUnqualifiedVersionless().getValue();
output = myClient output = myClient
.search() .search()
@ -740,7 +740,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient p = new Patient(); Patient p = new Patient();
p.addName().setFamily("FAM").addGiven("GIV"); p.addName().setFamily("FAM").addGiven("GIV");
IIdType id = myPatientDao.create(p).getId(); IIdType id = myPatientDao.create(p, mySrd).getId();
myClient.read().resource("Patient").withId(id.toUnqualifiedVersionless()).execute(); myClient.read().resource("Patient").withId(id.toUnqualifiedVersionless()).execute();
@ -763,7 +763,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient p = new Patient(); Patient p = new Patient();
p.addName().setFamily("FAM").addGiven("GIV"); p.addName().setFamily("FAM").addGiven("GIV");
IIdType id = myPatientDao.create(p).getId().toUnqualifiedVersionless(); IIdType id = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
myClient myClient
.delete() .delete()
@ -1025,57 +1025,58 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
public void testCreateAndReadBackResourceWithContainedReferenceToContainer() { public void testCreateAndReadBackResourceWithContainedReferenceToContainer() {
myFhirContext.setParserErrorHandler(new StrictErrorHandler()); myFhirContext.setParserErrorHandler(new StrictErrorHandler());
String input = "{\n" + String input = """
" \"resourceType\": \"Organization\",\n" + {
" \"id\": \"1\",\n" + "resourceType": "Organization",
" \"meta\": {\n" + "id": "1",
" \"tag\": [\n" + "meta": {
" {\n" + "tag": [
" \"system\": \"https://blah.org/deployment\",\n" + {
" \"code\": \"e69414dd-b5c2-462d-bcfd-9d04d6b16596\",\n" + "system": "https://blah.org/deployment",
" \"display\": \"DEPLOYMENT\"\n" + "code": "e69414dd-b5c2-462d-bcfd-9d04d6b16596",
" },\n" + "display": "DEPLOYMENT"
" {\n" + },
" \"system\": \"https://blah.org/region\",\n" + {
" \"code\": \"b47d7a5b-b159-4bed-a8f8-3258e6603adb\",\n" + "system": "https://blah.org/region",
" \"display\": \"REGION\"\n" + "code": "b47d7a5b-b159-4bed-a8f8-3258e6603adb",
" },\n" + "display": "REGION"
" {\n" + },
" \"system\": \"https://blah.org/provider\",\n" + {
" \"code\": \"28c30004-0333-40cf-9e7f-3f9e080930bd\",\n" + "system": "https://blah.org/provider",
" \"display\": \"PROVIDER\"\n" + "code": "28c30004-0333-40cf-9e7f-3f9e080930bd",
" }\n" + "display": "PROVIDER"
" ]\n" + }
" },\n" + ]
" \"contained\": [\n" + },
" {\n" + "contained": [
" \"resourceType\": \"Location\",\n" + {
" \"id\": \"2\",\n" + "resourceType": "Location",
" \"position\": {\n" + "id": "2",
" \"longitude\": 51.443238301454289,\n" + "position": {
" \"latitude\": 7.34196905697293\n" + "longitude": 51.443238301454289,
" },\n" + "latitude": 7.34196905697293
" \"managingOrganization\": {\n" + },
" \"reference\": \"#\"\n" + "managingOrganization": {
" }\n" + "reference": "#"
" }\n" + }
" ],\n" + }
" \"type\": [\n" + ],
" {\n" + "type": [
" \"coding\": [\n" + {
" {\n" + "coding": [
" \"system\": \"https://blah.org/fmc/OrganizationType\",\n" + {
" \"code\": \"CLINIC\",\n" + "system": "https://blah.org/fmc/OrganizationType",
" \"display\": \"Clinic\"\n" + "code": "CLINIC",
" }\n" + "display": "Clinic"
" ]\n" + }
" }\n" + ]
" ],\n" + }
" \"name\": \"testOrg\"\n" + ],
"}"; "name": "testOrg"
}""";
Organization org = myFhirContext.newJsonParser().parseResource(Organization.class, input); Organization org = myFhirContext.newJsonParser().parseResource(Organization.class, input);
IIdType id = myOrganizationDao.create(org).getId(); IIdType id = myOrganizationDao.create(org, mySrd).getId();
org = myOrganizationDao.read(id); org = myOrganizationDao.read(id);
String output = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(org); String output = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(org);
@ -1095,9 +1096,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
} }
List<IBaseResource> outcome = myClient.transaction().withResources(resources).prettyPrint().encodedXml().execute(); List<IBaseResource> outcome = myClient.transaction().withResources(resources).prettyPrint().encodedXml().execute();
runInTransaction(() -> { runInTransaction(() -> assertEquals(100, myResourceTableDao.count()));
assertEquals(100, myResourceTableDao.count());
});
Bundle found = myClient Bundle found = myClient
.search() .search()
@ -1306,7 +1305,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
} }
@Test @Test
public void testCreateQuestionnaireResponseWithValidation() throws IOException { public void testCreateQuestionnaireResponseWithValidation() {
CodeSystem cs = new CodeSystem(); CodeSystem cs = new CodeSystem();
cs.setUrl("http://cs"); cs.setUrl("http://cs");
cs.setStatus(Enumerations.PublicationStatus.ACTIVE); cs.setStatus(Enumerations.PublicationStatus.ACTIVE);
@ -1906,8 +1905,8 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
* Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to make sure that works too.. * Try it with a raw socket call. The Apache client won't let us use the unescaped "|" in the URL but we want to make sure that works too..
*/ */
Socket sock = new Socket(); Socket sock = new Socket();
try (sock) {
sock.setSoTimeout(3000); sock.setSoTimeout(3000);
try {
sock.connect(new InetSocketAddress("localhost", myPort)); sock.connect(new InetSocketAddress("localhost", myPort));
sock.getOutputStream().write(("DELETE /fhir/context/Patient?identifier=http://ghh.org/patient|" + methodName + " HTTP/1.1\n").getBytes(StandardCharsets.UTF_8)); sock.getOutputStream().write(("DELETE /fhir/context/Patient?identifier=http://ghh.org/patient|" + methodName + " HTTP/1.1\n").getBytes(StandardCharsets.UTF_8));
sock.getOutputStream().write("Host: localhost\n".getBytes(StandardCharsets.UTF_8)); sock.getOutputStream().write("Host: localhost\n".getBytes(StandardCharsets.UTF_8));
@ -1915,7 +1914,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
BufferedReader socketInput = new BufferedReader(new InputStreamReader(sock.getInputStream())); BufferedReader socketInput = new BufferedReader(new InputStreamReader(sock.getInputStream()));
// String response = "";
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
char[] buf = new char[1000]; char[] buf = new char[1000];
while (socketInput.read(buf) != -1) { while (socketInput.read(buf) != -1) {
@ -1925,9 +1923,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
ourLog.debug("Resp: {}", resp); ourLog.debug("Resp: {}", resp);
} catch (SocketTimeoutException e) { } catch (SocketTimeoutException e) {
e.printStackTrace(); ourLog.debug(e.getMessage(), e);
} finally {
sock.close();
} }
Thread.sleep(1000); Thread.sleep(1000);
@ -2398,7 +2394,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertThat(idValues).as(idValues.toString()).hasSize(10); assertThat(idValues).as(idValues.toString()).hasSize(10);
idValues = searchAndReturnUnqualifiedIdValues(myServerBase + "/_history?_at=gt" + InstantDt.withCurrentTime().getYear()); idValues = searchAndReturnUnqualifiedIdValues(myServerBase + "/_history?_at=gt" + InstantDt.withCurrentTime().getYear());
assertThat(idValues).hasSize(0); assertThat(idValues).isEmpty();
} }
@ -2427,7 +2423,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myMemoryCacheService.invalidateCaches(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID); myMemoryCacheService.invalidateCaches(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID);
} }
Bundle history = myClient.history().onInstance(id.getValue()).andReturnBundle(Bundle.class).execute(); Bundle history = myClient.history().onInstance(id.getValue()).returnBundle(Bundle.class).execute();
assertEquals(1, history.getEntry().size()); assertEquals(1, history.getEntry().size());
BundleEntryComponent historyEntry0 = history.getEntry().get(0); BundleEntryComponent historyEntry0 = history.getEntry().get(0);
// validate entry.fullUrl // validate entry.fullUrl
@ -2476,7 +2472,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myMemoryCacheService.invalidateCaches(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID); myMemoryCacheService.invalidateCaches(MemoryCacheService.CacheEnum.PID_TO_FORCED_ID);
} }
Bundle history = myClient.history().onInstance(id.getValue()).andReturnBundle(Bundle.class).execute(); Bundle history = myClient.history().onInstance(id.getValue()).returnBundle(Bundle.class).execute();
assertEquals(1, history.getEntry().size()); assertEquals(1, history.getEntry().size());
BundleEntryComponent historyEntry0 = history.getEntry().get(0); BundleEntryComponent historyEntry0 = history.getEntry().get(0);
// validate entry.fullUrl // validate entry.fullUrl
@ -2508,7 +2504,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
ourLog.info("Res ID: {}", id); ourLog.info("Res ID: {}", id);
Bundle history = myClient.history().onInstance(id.getValue()).andReturnBundle(Bundle.class).prettyPrint().summaryMode(SummaryEnum.DATA).execute(); Bundle history = myClient.history().onInstance(id.getValue()).returnBundle(Bundle.class).prettyPrint().summaryMode(SummaryEnum.DATA).execute();
assertThat(history.getEntry()).hasSize(3); assertThat(history.getEntry()).hasSize(3);
assertEquals(id.withVersion("3").getValue(), history.getEntry().get(0).getResource().getId()); assertEquals(id.withVersion("3").getValue(), history.getEntry().get(0).getResource().getId());
assertThat(((Patient) history.getEntry().get(0).getResource()).getName()).hasSize(1); assertThat(((Patient) history.getEntry().get(0).getResource()).getName()).hasSize(1);
@ -2746,7 +2742,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
int total = 20; int total = 20;
Organization org = new Organization(); Organization org = new Organization();
org.setName("ORG"); org.setName("ORG");
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
Coding tagCode = new Coding(); Coding tagCode = new Coding();
tagCode.setCode("test"); tagCode.setCode("test");
@ -2757,7 +2753,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
.addTag(tagCode); .addTag(tagCode);
t.setStatus(Task.TaskStatus.REQUESTED); t.setStatus(Task.TaskStatus.REQUESTED);
t.getOwner().setReference(orgId.getValue()); t.getOwner().setReference(orgId.getValue());
myTaskDao.create(t); myTaskDao.create(t, mySrd);
} }
HashSet<String> ids = new HashSet<>(); HashSet<String> ids = new HashSet<>();
@ -2835,12 +2831,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
if (orgCount > 0) { if (orgCount > 0) {
Organization org = new Organization(); Organization org = new Organization();
org.setName("ORG"); org.setName("ORG");
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
orgCount--; orgCount--;
t.getOwner().setReference(orgId.getValue()); t.getOwner().setReference(orgId.getValue());
} }
myTaskDao.create(t); myTaskDao.create(t, mySrd);
} }
} }
@ -2909,12 +2905,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
if (orgCount > 0) { if (orgCount > 0) {
Organization org = new Organization(); Organization org = new Organization();
org.setName("ORG"); org.setName("ORG");
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
orgCount--; orgCount--;
t.getOwner().setReference(orgId.getValue()); t.getOwner().setReference(orgId.getValue());
} }
myTaskDao.create(t); myTaskDao.create(t, mySrd);
} }
} }
@ -2961,13 +2957,13 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
public void testIncludeCountDoesntIncludeIncludes() { public void testIncludeCountDoesntIncludeIncludes() {
Organization org = new Organization(); Organization org = new Organization();
org.setName("ORG"); org.setName("ORG");
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless(); IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless();
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
Patient pt = new Patient(); Patient pt = new Patient();
pt.getManagingOrganization().setReference(orgId.getValue()); pt.getManagingOrganization().setReference(orgId.getValue());
pt.addName().setFamily("FAM" + i); pt.addName().setFamily("FAM" + i);
myPatientDao.create(pt); myPatientDao.create(pt, mySrd);
} }
Bundle bundle = myClient Bundle bundle = myClient
@ -3168,7 +3164,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute(); Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
assertEquals("2", newPt.getIdElement().getVersionIdPart()); assertEquals("2", newPt.getIdElement().getVersionIdPart());
assertEquals(false, newPt.getActive()); assertFalse(newPt.getActive());
} }
@Test @Test
@ -3196,7 +3192,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute(); Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
assertEquals("1", newPt.getIdElement().getVersionIdPart()); assertEquals("1", newPt.getIdElement().getVersionIdPart());
assertEquals(true, newPt.getActive()); assertTrue(newPt.getActive());
} }
@Test @Test
@ -3226,7 +3222,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute(); Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
assertEquals("2", newPt.getIdElement().getVersionIdPart()); assertEquals("2", newPt.getIdElement().getVersionIdPart());
assertEquals(false, newPt.getActive()); assertFalse(newPt.getActive());
} }
@Test @Test
@ -3255,7 +3251,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute(); Patient newPt = myClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
assertEquals("2", newPt.getIdElement().getVersionIdPart()); assertEquals("2", newPt.getIdElement().getVersionIdPart());
assertEquals(false, newPt.getActive()); assertFalse(newPt.getActive());
} }
@Test @Test
@ -3323,12 +3319,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
{ {
Bundle returned = myClient.search().forResource(Patient.class).encodedXml().returnBundle(Bundle.class).execute(); Bundle returned = myClient.search().forResource(Patient.class).encodedXml().returnBundle(Bundle.class).execute();
assertThat(returned.getEntry().size()).isGreaterThan(1); assertThat(returned.getEntry()).hasSizeGreaterThan(1);
assertEquals(BundleType.SEARCHSET, returned.getType()); assertEquals(BundleType.SEARCHSET, returned.getType());
} }
{ {
Bundle returned = myClient.search().forResource(Patient.class).encodedJson().returnBundle(Bundle.class).execute(); Bundle returned = myClient.search().forResource(Patient.class).encodedJson().returnBundle(Bundle.class).execute();
assertThat(returned.getEntry().size()).isGreaterThan(1); assertThat(returned.getEntry()).hasSizeGreaterThan(1);
} }
} }
@ -3350,7 +3346,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
} }
}); });
Bundle bundle = myClient.history().onServer().andReturnBundle(Bundle.class).execute(); Bundle bundle = myClient.history().onServer().returnBundle(Bundle.class).execute();
assertEquals(1, bundle.getTotal()); assertEquals(1, bundle.getTotal());
assertThat(bundle.getEntry()).hasSize(1); assertThat(bundle.getEntry()).hasSize(1);
assertEquals(id2.getIdPart(), bundle.getEntry().get(0).getResource().getIdElement().getIdPart()); assertEquals(id2.getIdPart(), bundle.getEntry().get(0).getResource().getIdElement().getIdPart());
@ -3507,15 +3503,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
assertThat(text).doesNotContain("\"B\""); assertThat(text).doesNotContain("\"B\"");
assertThat(text).doesNotContain("\"B1\""); assertThat(text).doesNotContain("\"B1\"");
} }
// HttpGet read = new HttpGet(ourServerBase + "/Observation?patient=P5000000302&_sort:desc=code&code:in=http://fkcfhir.org/fhir/vs/ccdacapddialysisorder");
// try (CloseableHttpResponse response = ourHttpClient.execute(read)) {
// String text = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
// ourLog.info(text);
// assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatusLine().getStatusCode());
// assertThat(text).doesNotContain("\"text\",\"type\"");
// }
} }
@Test @Test
@ -3873,9 +3860,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
p.addName().setFamily(methodName + "1"); p.addName().setFamily(methodName + "1");
IIdType pid1 = myClient.create().resource(p).execute().getId().toUnqualifiedVersionless(); IIdType pid1 = myClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
Thread.sleep(10);
long time1 = System.currentTimeMillis(); long time1 = System.currentTimeMillis();
Thread.sleep(10);
Patient p2 = new Patient(); Patient p2 = new Patient();
p2.addName().setFamily(methodName + "2"); p2.addName().setFamily(methodName + "2");
@ -4064,9 +4049,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
public void testSearchLastUpdatedParamRp() throws InterruptedException { public void testSearchLastUpdatedParamRp() throws InterruptedException {
String methodName = "testSearchLastUpdatedParamRp"; String methodName = "testSearchLastUpdatedParamRp";
int sleep = 100;
Thread.sleep(sleep);
DateTimeType beforeAny = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI); DateTimeType beforeAny = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI);
IIdType id1a; IIdType id1a;
{ {
@ -4083,9 +4065,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
id1b = myClient.create().resource(patient).execute().getId().toUnqualifiedVersionless(); id1b = myClient.create().resource(patient).execute().getId().toUnqualifiedVersionless();
} }
Thread.sleep(1100);
DateTimeType beforeR2 = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI); DateTimeType beforeR2 = new DateTimeType(new Date(), TemporalPrecisionEnum.MILLI);
Thread.sleep(1100);
IIdType id2; IIdType id2;
{ {
@ -4249,13 +4229,12 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp); Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp);
matches = bundle.getEntry().size(); matches = bundle.getEntry().size();
assertThat(matches).isGreaterThan(0); assertThat(matches).isPositive();
} }
@Test @Test
public void testSearchReturnsSearchDate() throws Exception { public void testSearchReturnsSearchDate() throws Exception {
Date before = new Date(); Date before = new Date();
Thread.sleep(1);
//@formatter:off //@formatter:off
Bundle found = myClient Bundle found = myClient
@ -4266,7 +4245,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
.execute(); .execute();
//@formatter:on //@formatter:on
Thread.sleep(1);
Date after = new Date(); Date after = new Date();
InstantType updated = found.getMeta().getLastUpdatedElement(); InstantType updated = found.getMeta().getLastUpdatedElement();
@ -4300,7 +4278,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -4313,7 +4291,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -4326,7 +4304,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -4339,24 +4317,24 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
// > 1m // > 1m
String uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt1|http://unitsofmeasure.org|m"); String uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt1|http://unitsofmeasure.org|m");
ourLog.info("uri = " + uri); ourLog.info("uri = {}", uri);
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertThat(ids).hasSize(3); assertThat(ids).hasSize(3);
//>= 100cm //>= 100cm
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt100|http://unitsofmeasure.org|cm"); uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt100|http://unitsofmeasure.org|cm");
ourLog.info("uri = " + uri); ourLog.info("uri = {}", uri);
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertThat(ids).hasSize(3); assertThat(ids).hasSize(3);
//>= 10dm //>= 10dm
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt10|http://unitsofmeasure.org|dm"); uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt10|http://unitsofmeasure.org|dm");
ourLog.info("uri = " + uri); ourLog.info("uri = {}", uri);
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertThat(ids).hasSize(3); assertThat(ids).hasSize(3);
} }
@ -4381,7 +4359,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -4392,7 +4370,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -4403,7 +4381,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -4414,7 +4392,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
String uri; String uri;
@ -4451,7 +4429,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -4462,7 +4440,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -4474,7 +4452,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
@ -4490,8 +4468,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
//-- check use normalized quantity table to search //-- check use normalized quantity table to search
String searchSql = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueries().get(0).getSql(true, true);
assertThat(searchSql).doesNotContain("HFJ_SPIDX_QUANTITY t0"); assertThat(searchSql).doesNotContain("HFJ_SPIDX_QUANTITY t0").contains("HFJ_SPIDX_QUANTITY_NRML");
assertThat(searchSql).contains("HFJ_SPIDX_QUANTITY_NRML");
} }
@Test @Test
@ -5044,7 +5021,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -5056,7 +5033,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -5068,7 +5045,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -5080,7 +5057,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
String uri = myServerBase + "/Observation?_sort=code-value-quantity"; String uri = myServerBase + "/Observation?_sort=code-value-quantity";
@ -5092,7 +5069,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
found = myFhirContext.newXmlParser().parseResource(Bundle.class, output); found = myFhirContext.newXmlParser().parseResource(Bundle.class, output);
} }
ourLog.debug("Bundle: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found)); ourLog.debug("Bundle: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found));
List<IIdType> list = toUnqualifiedVersionlessIds(found); List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(found.getEntry()).hasSize(4); assertThat(found.getEntry()).hasSize(4);
@ -5129,7 +5106,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); oid1 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -5145,7 +5122,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); oid2 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -5161,7 +5138,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); oid3 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -5176,7 +5153,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
comp.setValue(new Quantity().setValue(250)); comp.setValue(new Quantity().setValue(250));
oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless(); oid4 = myObservationDao.create(obs, mySrd).getId().toUnqualifiedVersionless();
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
String uri = myServerBase + "/Observation?_sort=combo-code-value-quantity"; String uri = myServerBase + "/Observation?_sort=combo-code-value-quantity";
@ -5188,7 +5165,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
found = myFhirContext.newXmlParser().parseResource(Bundle.class, output); found = myFhirContext.newXmlParser().parseResource(Bundle.class, output);
} }
ourLog.debug("Bundle: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found)); ourLog.debug("Bundle: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(found));
List<IIdType> list = toUnqualifiedVersionlessIds(found); List<IIdType> list = toUnqualifiedVersionlessIds(found);
assertThat(found.getEntry()).hasSize(4); assertThat(found.getEntry()).hasSize(4);
@ -5264,9 +5241,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
List<IIdType> list = toUnqualifiedVersionlessIds(found); List<IIdType> list = toUnqualifiedVersionlessIds(found);
ourLog.info(methodName + " found: " + list.toString() + " - Wanted " + orgMissing + " but not " + orgNotMissing); ourLog.info(methodName + " found: " + list.toString() + " - Wanted " + orgMissing + " but not " + orgNotMissing);
assertThat(list).doesNotContain(orgNotMissing); assertThat(list).doesNotContain(orgNotMissing).doesNotContain(deletedIdMissingTrue).contains(orgMissing);
assertThat(list).doesNotContain(deletedIdMissingTrue);
assertThat(list).contains(orgMissing);
} }
@Test @Test
@ -5927,7 +5902,6 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
} }
Date before = new Date(); Date before = new Date();
Thread.sleep(100);
pt = new Patient(); pt = new Patient();
pt.setId(id.getIdPart()); pt.setId(id.getIdPart());
@ -6450,7 +6424,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org"); cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new Quantity().setValueElement(new DecimalType(125.12)).setUnit("CM").setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm")); obs.setValue(new Quantity().setValueElement(new DecimalType(125.12)).setUnit("CM").setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm"));
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
IIdType opid1 = myObservationDao.create(obs, mySrd).getId(); IIdType opid1 = myObservationDao.create(obs, mySrd).getId();
@ -6463,7 +6437,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
cc.addCoding().setCode("2345-7").setSystem("http://loinc.org"); cc.addCoding().setCode("2345-7").setSystem("http://loinc.org");
obs.setValue(new Quantity().setValueElement(new DecimalType(24.12)).setUnit("CM").setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm")); obs.setValue(new Quantity().setValueElement(new DecimalType(24.12)).setUnit("CM").setSystem(UcumServiceUtil.UCUM_CODESYSTEM_URL).setCode("cm"));
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
myObservationDao.update(obs, mySrd); myObservationDao.update(obs, mySrd);
} }
@ -6479,7 +6453,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -6492,7 +6466,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
{ {
@ -6505,25 +6479,25 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd); myObservationDao.create(obs, mySrd);
ourLog.debug("Observation: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs)); ourLog.debug("Observation: {}", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(obs));
} }
// > 1m // > 1m
String uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt1|http://unitsofmeasure.org|m"); String uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt1|http://unitsofmeasure.org|m");
ourLog.info("uri = " + uri); ourLog.info("uri = {}", uri);
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertThat(ids).hasSize(2); assertThat(ids).hasSize(2);
//>= 100cm //>= 100cm
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt100|http://unitsofmeasure.org|cm"); uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt100|http://unitsofmeasure.org|cm");
ourLog.info("uri = " + uri); ourLog.info("uri = {}", uri);
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertThat(ids).hasSize(2); assertThat(ids).hasSize(2);
//>= 10dm //>= 10dm
uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt10|http://unitsofmeasure.org|dm"); uri = myServerBase + "/Observation?code-value-quantity=http://" + UrlUtil.escapeUrlParam("loinc.org|2345-7$gt10|http://unitsofmeasure.org|dm");
ourLog.info("uri = " + uri); ourLog.info("uri = {}", uri);
ids = searchAndReturnUnqualifiedVersionlessIdValues(uri); ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertThat(ids).hasSize(2); assertThat(ids).hasSize(2);
} }
@ -6540,7 +6514,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
patient.setBirthDateElement(new DateType("2073")); patient.setBirthDateElement(new DateType("2073"));
pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); pid0 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
ourLog.debug("Patient: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient)); ourLog.debug("Patient: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
ourLog.info("pid0 " + pid0); ourLog.info("pid0 " + pid0);
} }
@ -6553,7 +6527,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp); ourLog.info(resp);
Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp); Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp);
ourLog.debug("Patient: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); ourLog.debug("Patient: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
} }
uri = myServerBase + "/Patient?_total=accurate&birthdate=gt2072-01-01"; uri = myServerBase + "/Patient?_total=accurate&birthdate=gt2072-01-01";
@ -6564,7 +6538,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp); ourLog.info(resp);
Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp); Bundle bundle = myFhirContext.newXmlParser().parseResource(Bundle.class, resp);
ourLog.debug("Patient: \n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle)); ourLog.debug("Patient: {}\n", myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
} }
} }
@ -6995,9 +6969,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
&& theInput.IsEnforceRefOnType && theInput.IsEnforceRefOnType
&& theInput.IsEnforceRefOnWrite).isFalse(); && theInput.IsEnforceRefOnWrite).isFalse();
} catch (InvalidRequestException ex) { } catch (InvalidRequestException ex) {
assertThat(ex.getMessage().contains( assertThat(ex.getMessage()).as(ex.getMessage()).contains("Invalid resource reference");
"Invalid resource reference"
)).as(ex.getMessage()).isTrue();
} finally { } finally {
myStorageSettings.setEnforceReferentialIntegrityOnWrite(isEnforceRefOnWrite); myStorageSettings.setEnforceReferentialIntegrityOnWrite(isEnforceRefOnWrite);
myStorageSettings.setEnforceReferenceTargetTypes(isEnforceRefTargetTypes); myStorageSettings.setEnforceReferenceTargetTypes(isEnforceRefTargetTypes);
@ -7331,9 +7303,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
patient.setBirthDate(cal.getTime()); patient.setBirthDate(cal.getTime());
} }
return patient; return patient;
}, (isMissing) -> { }, (isMissing) -> doSearch(Patient.class, Patient.BIRTHDATE.isMissing(isMissing)));
return doSearch(Patient.class, Patient.BIRTHDATE.isMissing(isMissing));
});
} }
@ParameterizedTest @ParameterizedTest
@ -7346,9 +7316,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
patient.setGender(AdministrativeGender.FEMALE); patient.setGender(AdministrativeGender.FEMALE);
} }
return patient; return patient;
}, isMissing -> { }, isMissing -> doSearch(Patient.class, Patient.GENDER.isMissing(isMissing)));
return doSearch(Patient.class, Patient.GENDER.isMissing(isMissing));
});
} }
@ParameterizedTest @ParameterizedTest
@ -7364,9 +7332,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
patient.setGeneralPractitioner(Collections.singletonList(new Reference(practitionerId))); patient.setGeneralPractitioner(Collections.singletonList(new Reference(practitionerId)));
} }
return patient; return patient;
}, isMissing -> { }, isMissing -> doSearch(Patient.class, Patient.GENERAL_PRACTITIONER.isMissing(isMissing)));
return doSearch(Patient.class, Patient.GENERAL_PRACTITIONER.isMissing(isMissing));
});
} }
@ParameterizedTest @ParameterizedTest
@ -7409,9 +7375,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
sp.setUrl("http://example.com"); sp.setUrl("http://example.com");
} }
return sp; return sp;
}, isMissing -> { }, isMissing -> doSearch(SearchParameter.class, SearchParameter.URL.isMissing(isMissing)));
return doSearch(SearchParameter.class, SearchParameter.URL.isMissing(isMissing));
});
} }
@ParameterizedTest @ParameterizedTest
@ -7424,9 +7388,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
obs.setValue(new Quantity(3)); obs.setValue(new Quantity(3));
} }
return obs; return obs;
}, isMissing -> { }, isMissing -> doSearch(Observation.class, Observation.VALUE_QUANTITY.isMissing(isMissing)));
return doSearch(Observation.class, Observation.VALUE_QUANTITY.isMissing(isMissing));
});
} }
@ParameterizedTest @ParameterizedTest
@ -7457,7 +7419,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
Y doTask(X theInput); Y doTask(X theInput);
} }
private static class MissingSearchTestParameters { public static class MissingSearchTestParameters {
/** /**
* The setting for IndexMissingFields * The setting for IndexMissingFields
*/ */

View File

@ -1,9 +1,8 @@
package ca.uhn.fhir.jpa.term; package ca.uhn.fhir.jpa.term;
import static org.junit.jupiter.api.Assertions.assertNull;
import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.LookupCodeRequest; import ca.uhn.fhir.context.support.LookupCodeRequest;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.config.JpaConfig; import ca.uhn.fhir.jpa.config.JpaConfig;
import ca.uhn.fhir.jpa.entity.TermCodeSystem; import ca.uhn.fhir.jpa.entity.TermCodeSystem;
@ -20,9 +19,10 @@ import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import jakarta.persistence.EntityManager;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
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.r4.model.CodeSystem; import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.CodeType; import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.IdType;
@ -40,8 +40,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.ClassPathResource;
import jakarta.persistence.EntityManager;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
@ -82,14 +80,12 @@ import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_UPLOAD_
import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_XML_FILE; import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.LOINC_XML_FILE;
import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.joining;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_ALL_VALUESET_ID; import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_ALL_VALUESET_ID;
import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_LOW; import static org.hl7.fhir.common.hapi.validation.support.ValidationConstants.LOINC_LOW;
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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
/** /**
@ -141,9 +137,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
@Autowired @Autowired
private Batch2JobHelper myBatchJobHelper; private Batch2JobHelper myBatchJobHelper;
private final ServletRequestDetails myRequestDetails = new ServletRequestDetails();
private ZipCollectionBuilder myFiles;
private ServletRequestDetails myRequestDetails = new ServletRequestDetails();
private Properties uploadProperties; private Properties uploadProperties;
private IFhirResourceDao<ValueSet> myValueSetIFhirResourceDao; private IFhirResourceDao<ValueSet> myValueSetIFhirResourceDao;
@ -182,17 +176,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
private void validateValidateCode(String theCurrentVersion, Collection<String> allVersions) { private void validateValidateCode(String theCurrentVersion, Collection<String> allVersions) {
IValidationSupport.CodeValidationResult resultNoVersioned = myCodeSystemDao.validateCode(null, validateValidateCodeForVersion(theCurrentVersion);
new UriType(BASE_LOINC_URL), null, new CodeType(VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE),
null, null, null, null);
assertNotNull(resultNoVersioned);
assertEquals(prefixWithVersion(theCurrentVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), resultNoVersioned.getDisplay());
IValidationSupport.CodeValidationResult resultVersioned = myCodeSystemDao.validateCode(null,
new UriType(BASE_LOINC_URL), null, new CodeType(VS_VERSIONED_ON_UPLOAD_FIRST_CODE),
null, null, null, null);
assertNotNull(resultVersioned);
assertEquals(prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), resultVersioned.getDisplay());
allVersions.forEach(this::validateValidateCodeForVersion); allVersions.forEach(this::validateValidateCodeForVersion);
} }
@ -215,13 +199,15 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
private void validateValueLookup(String theCurrentVersion, Collection<String> allVersions) { private void validateValueLookup(String theCurrentVersion, Collection<String> allVersions) {
IValidationSupport.LookupCodeResult resultNoVer = myValidationSupport.lookupCode( IValidationSupport.LookupCodeResult resultNoVer = myValidationSupport.lookupCode(
new ValidationSupportContext(myValidationSupport), new LookupCodeRequest(BASE_LOINC_URL, VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE)); new ValidationSupportContext(myValidationSupport),
new LookupCodeRequest(BASE_LOINC_URL, VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE));
assertNotNull(resultNoVer); assertNotNull(resultNoVer);
String expectedNoVer = prefixWithVersion(theCurrentVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY); String expectedNoVer = prefixWithVersion(theCurrentVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY);
assertEquals(expectedNoVer, resultNoVer.getCodeDisplay()); assertEquals(expectedNoVer, resultNoVer.getCodeDisplay());
IValidationSupport.LookupCodeResult resultWithVer = myValidationSupport.lookupCode( IValidationSupport.LookupCodeResult resultWithVer = myValidationSupport.lookupCode(
new ValidationSupportContext(myValidationSupport), new LookupCodeRequest(BASE_LOINC_URL, VS_VERSIONED_ON_UPLOAD_FIRST_CODE)); new ValidationSupportContext(myValidationSupport),
new LookupCodeRequest(BASE_LOINC_URL, VS_VERSIONED_ON_UPLOAD_FIRST_CODE));
assertNotNull(resultWithVer); assertNotNull(resultWithVer);
String expectedWithVer = prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY); String expectedWithVer = prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY);
assertEquals(expectedWithVer, resultWithVer.getCodeDisplay()); assertEquals(expectedWithVer, resultWithVer.getCodeDisplay());
@ -293,27 +279,13 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
assertEquals(prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), termConceptVerCsvNoVer.getDisplay()); assertEquals(prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), termConceptVerCsvNoVer.getDisplay());
if (theCurrentVersion != null) { if (theCurrentVersion != null) {
TermConcept termConceptNoVerCsvVer = (TermConcept) myEntityManager.createQuery( validateExpandedTermConceptsForVersion(theCurrentVersion);
"select tc from TermConcept tc join fetch tc.myCodeSystem tcsv where tc.myCode = '" +
VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE + "' and tcsv.myCodeSystemVersionId = '" + theCurrentVersion + "'").getSingleResult();
assertNotNull(termConceptNoVerCsvVer);
// data should have version because it was loaded with a version
assertEquals(prefixWithVersion(theCurrentVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), termConceptNoVerCsvVer.getDisplay());
TermConcept termConceptVerCsvVer = (TermConcept) myEntityManager.createQuery(
"select tc from TermConcept tc join fetch tc.myCodeSystem tcsv where tc.myCode = '" +
VS_VERSIONED_ON_UPLOAD_FIRST_CODE + "' and tcsv.myCodeSystemVersionId = '" + theCurrentVersion + "'").getSingleResult();
assertNotNull(termConceptVerCsvVer);
// data should have version because it was loaded with a version
assertEquals(prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), termConceptVerCsvVer.getDisplay());
} }
theAllVersions.forEach(this::validateExpandedTermConceptsForVersion); theAllVersions.forEach(this::validateExpandedTermConceptsForVersion);
}); });
} }
private void validateExpandedTermConceptsForVersion(String theVersion) { private void validateExpandedTermConceptsForVersion(String theVersion) {
TermConcept termConceptNoVer = (TermConcept) myEntityManager.createQuery( TermConcept termConceptNoVer = (TermConcept) myEntityManager.createQuery(
"select tc from TermConcept tc join fetch tc.myCodeSystem tcsv where tc.myCode = '" + "select tc from TermConcept tc join fetch tc.myCodeSystem tcsv where tc.myCode = '" +
@ -384,7 +356,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
* no the CodeSystem version. * no the CodeSystem version.
*/ */
private void validateValueSetSearchForVersion(String theVersion) { private void validateValueSetSearchForVersion(String theVersion) {
// for no versioned VS (VS version, different than CS version) // for no versioned VS (VS version, different from CS version)
SearchParameterMap paramsUploadNoVer = new SearchParameterMap("url", new UriParam(VS_NO_VERSIONED_ON_UPLOAD)); SearchParameterMap paramsUploadNoVer = new SearchParameterMap("url", new UriParam(VS_NO_VERSIONED_ON_UPLOAD));
paramsUploadNoVer.add("version", new TokenParam(theVersion)); paramsUploadNoVer.add("version", new TokenParam(theVersion));
@ -398,7 +370,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
assertEquals(expectedLoadNoVersionUnqualifiedId, loadNoVersionValueSet.getIdElement().getIdPart()); assertEquals(expectedLoadNoVersionUnqualifiedId, loadNoVersionValueSet.getIdElement().getIdPart());
// versioned VS (VS version, different than CS version) // versioned VS (VS version, different from CS version)
SearchParameterMap paramsUploadVer = new SearchParameterMap("url", new UriParam(VS_VERSIONED_ON_UPLOAD)); SearchParameterMap paramsUploadVer = new SearchParameterMap("url", new UriParam(VS_VERSIONED_ON_UPLOAD));
paramsUploadVer.add("version", new TokenParam(VS_ANSWER_LIST_VERSION + "-" + theVersion)); paramsUploadVer.add("version", new TokenParam(VS_ANSWER_LIST_VERSION + "-" + theVersion));
@ -437,8 +409,8 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
Set<String> theExpectedIdVersionsPlusNull = Sets.newHashSet(theExpectedIdVersions); Set<String> theExpectedIdVersionsPlusNull = Sets.newHashSet(theExpectedIdVersions);
theExpectedIdVersionsPlusNull.add(null); theExpectedIdVersionsPlusNull.add(null);
assertThat(theExpectedIdVersionsPlusNull).containsExactlyInAnyOrderElementsOf(theValueSets.stream().map(r -> ((ValueSet) r).getVersion()).toList()); List<String> valueSetVersions = theValueSets.stream().map(r -> ((ValueSet) r).getVersion()).toList();
assertThat(valueSetVersions).containsExactlyInAnyOrderElementsOf(theExpectedIdVersionsPlusNull);
} }
@ -455,14 +427,13 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
// for CodeSystem: // for CodeSystem:
// _ current CS is present and has no version // _ current CS is present and has no version
CodeSystem codeSystem = myCodeSystemDao.read(new IdType(LOINC_LOW)); CodeSystem codeSystem = myCodeSystemDao.read(new IdType(LOINC_LOW), myRequestDetails);
String csString = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem); String csString = myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem);
ourLog.info("CodeSystem:\n" + csString); ourLog.info("CodeSystem:\n {}", csString);
HashSet<String> shouldNotBePresentVersions = new HashSet<>(possibleVersions); HashSet<String> shouldNotBePresentVersions = new HashSet<>(possibleVersions);
theAllVersions.forEach(shouldNotBePresentVersions::remove); theAllVersions.forEach(shouldNotBePresentVersions::remove);
shouldNotBePresentVersions.forEach(vv -> assertFalse(csString.contains(vv), assertThat(shouldNotBePresentVersions).noneSatisfy(vv -> assertThat(vv).isEqualTo(codeSystem.getVersion()));
"Found version string: '" + vv + "' in CodeSystem: " + csString));
// same reading it from term service // same reading it from term service
CodeSystem cs = myITermReadSvc.fetchCanonicalCodeSystemFromCompleteContext(BASE_LOINC_URL); CodeSystem cs = myITermReadSvc.fetchCanonicalCodeSystemFromCompleteContext(BASE_LOINC_URL);
@ -501,7 +472,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
@Test() @Test()
public void uploadCurrentNoVersion() throws Exception { public void uploadCurrentNoVersion() throws Exception {
IIdType csId = uploadLoincCodeSystem(null, true); uploadLoincCodeSystem(null, true);
runCommonValidations(Collections.emptyList()); runCommonValidations(Collections.emptyList());
@ -516,7 +487,7 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
@Test() @Test()
public void uploadWithVersion() throws Exception { public void uploadWithVersion() throws Exception {
String ver = "2.67"; String ver = "2.67";
IIdType csId = uploadLoincCodeSystem(ver, true); uploadLoincCodeSystem(ver, true);
runCommonValidations(Collections.singletonList(ver)); runCommonValidations(Collections.singletonList(ver));
@ -637,25 +608,6 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
assertThat(vsContainsDisplay).contains(expectedDisplay); assertThat(vsContainsDisplay).contains(expectedDisplay);
} }
private void validateValidateCodeLoincAllVS(String theCurrentVersion, Collection<String> allVersions) {
IValidationSupport.CodeValidationResult resultNoVersioned = myCodeSystemDao.validateCode(null,
new UriType(BASE_LOINC_URL), null, new CodeType(VS_NO_VERSIONED_ON_UPLOAD_FIRST_CODE),
null, null, null, null);
assertNotNull(resultNoVersioned);
assertEquals(prefixWithVersion(theCurrentVersion, VS_NO_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), resultNoVersioned.getDisplay());
IValidationSupport.CodeValidationResult resultVersioned = myCodeSystemDao.validateCode(null,
new UriType(BASE_LOINC_URL), null, new CodeType(VS_VERSIONED_ON_UPLOAD_FIRST_CODE),
null, null, null, null);
assertNotNull(resultVersioned);
assertEquals(prefixWithVersion(theCurrentVersion, VS_VERSIONED_ON_UPLOAD_FIRST_DISPLAY), resultVersioned.getDisplay());
allVersions.forEach(this::validateValidateCodeForVersion);
}
private void validateValueExpandLoincAllVsForVersion(String theVersion) { private void validateValueExpandLoincAllVsForVersion(String theVersion) {
ValueSet vs = myValueSetDao.expandByIdentifier(LOINC_ALL_VS_URL + "|" + theVersion, null); ValueSet vs = myValueSetDao.expandByIdentifier(LOINC_ALL_VS_URL + "|" + theVersion, null);
assertThat(vs.getExpansion().getContains()).hasSize(ALL_VS_QTY); assertThat(vs.getExpansion().getContains()).hasSize(ALL_VS_QTY);
@ -664,7 +616,6 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
checkContainsElementVersion(vs, theVersion); checkContainsElementVersion(vs, theVersion);
} }
/** /**
* Validates TermConcepts were created in the sequence indicated by the parameters * Validates TermConcepts were created in the sequence indicated by the parameters
* and their displays match the expected versions * and their displays match the expected versions
@ -735,8 +686,8 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
} }
private IIdType uploadLoincCodeSystem(String theVersion, boolean theMakeItCurrent) throws Exception { private void uploadLoincCodeSystem(String theVersion, boolean theMakeItCurrent) throws Exception {
myFiles = new ZipCollectionBuilder(); ZipCollectionBuilder files = new ZipCollectionBuilder();
myRequestDetails.getUserData().put(LOINC_CODESYSTEM_MAKE_CURRENT, theMakeItCurrent); myRequestDetails.getUserData().put(LOINC_CODESYSTEM_MAKE_CURRENT, theMakeItCurrent);
uploadProperties.put(LOINC_CODESYSTEM_MAKE_CURRENT.getCode(), Boolean.toString(theMakeItCurrent)); uploadProperties.put(LOINC_CODESYSTEM_MAKE_CURRENT.getCode(), Boolean.toString(theMakeItCurrent));
@ -749,12 +700,10 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
uploadProperties.put(LOINC_CODESYSTEM_VERSION.getCode(), theVersion); uploadProperties.put(LOINC_CODESYSTEM_VERSION.getCode(), theVersion);
} }
addLoincMandatoryFilesToZip(myFiles, theVersion); addLoincMandatoryFilesToZip(files, theVersion);
UploadStatistics stats = myTermLoaderSvc.loadLoinc(myFiles.getFiles(), mySrd); myTermLoaderSvc.loadLoinc(files.getFiles(), mySrd);
myTerminologyDeferredStorageSvc.saveAllDeferred(); myTerminologyDeferredStorageSvc.saveAllDeferred();
return stats.getTarget();
} }
@ -787,10 +736,12 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
private TermCodeSystemVersion fetchCurrentCodeSystemVersion() { private TermCodeSystemVersion fetchCurrentCodeSystemVersion() {
runInTransaction(() -> { runInTransaction(() -> {
@SuppressWarnings("unchecked")
List<TermCodeSystem> tcsList = myEntityManager.createQuery("from TermCodeSystem").getResultList(); List<TermCodeSystem> tcsList = myEntityManager.createQuery("from TermCodeSystem").getResultList();
@SuppressWarnings("unchecked")
List<TermCodeSystemVersion> tcsvList = myEntityManager.createQuery("from TermCodeSystemVersion").getResultList(); List<TermCodeSystemVersion> tcsvList = myEntityManager.createQuery("from TermCodeSystemVersion").getResultList();
ourLog.error("tcslist: {}", tcsList.stream().map(tcs -> tcs.toString()).collect(joining("\n", "\n", ""))); ourLog.error("tcslist: {}", tcsList.stream().map(TermCodeSystem::toString).collect(joining("\n", "\n", "")));
ourLog.error("tcsvlist: {}", tcsvList.stream().map(v -> v.toString()).collect(joining("\n", "\n", ""))); ourLog.error("tcsvlist: {}", tcsvList.stream().map(TermCodeSystemVersion::toString).collect(joining("\n", "\n", "")));
if (tcsList.size() != 1) { if (tcsList.size() != 1) {
throw new IllegalStateException("More than one TCS: " + throw new IllegalStateException("More than one TCS: " +
@ -807,8 +758,9 @@ public class TerminologySvcImplCurrentVersionR4Test extends BaseJpaR4Test {
} }
private static void addBaseLoincMandatoryFilesToZip( private static void addBaseLoincMandatoryFilesToZip(ZipCollectionBuilder theFiles,
ZipCollectionBuilder theFiles, Boolean theIncludeTop2000, String theClassPathPrefix) throws IOException { @SuppressWarnings("SameParameterValue") Boolean theIncludeTop2000,
String theClassPathPrefix) throws IOException {
theFiles.addFileZip(theClassPathPrefix, LOINC_XML_FILE.getCode()); theFiles.addFileZip(theClassPathPrefix, LOINC_XML_FILE.getCode());
theFiles.addFileZip(theClassPathPrefix, LOINC_GROUP_FILE_DEFAULT.getCode()); theFiles.addFileZip(theClassPathPrefix, LOINC_GROUP_FILE_DEFAULT.getCode());
theFiles.addFileZip(theClassPathPrefix, LOINC_GROUP_TERMS_FILE_DEFAULT.getCode()); theFiles.addFileZip(theClassPathPrefix, LOINC_GROUP_TERMS_FILE_DEFAULT.getCode());

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -45,7 +45,7 @@ public interface ICdsServiceRegistry {
* @param theCdsServiceRequestJson the service request * @param theCdsServiceRequestJson the service request
* @return the service response * @return the service response
*/ */
CdsServiceResponseJson callService(String theServiceId, CdsServiceRequestJson theCdsServiceRequestJson); CdsServiceResponseJson callService(String theServiceId, Object theCdsServiceRequestJson);
/** /**
* This is the REST method available at https://example.com/cds-services/{theServiceId}/feedback * This is the REST method available at https://example.com/cds-services/{theServiceId}/feedback

View File

@ -20,9 +20,11 @@
package ca.uhn.hapi.fhir.cdshooks.api.json; package ca.uhn.hapi.fhir.cdshooks.api.json;
import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.model.api.IModelJson;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
/** /**
* Users can define CDS Hooks extensions by extending this class. * Users can define CDS Hooks extensions by extending this class.
* Implementors can extend this class for defining their custom extensions. * Implementors can extend this class for defining their custom extensions.
*/ */
@JsonIgnoreProperties(ignoreUnknown = true)
public class CdsHooksExtension implements IModelJson {} public class CdsHooksExtension implements IModelJson {}

View File

@ -21,6 +21,7 @@ package ca.uhn.hapi.fhir.cdshooks.api.json;
import ca.uhn.fhir.model.api.IModelJson; import ca.uhn.fhir.model.api.IModelJson;
import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.Collections; import java.util.Collections;
@ -29,6 +30,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@JsonIgnoreProperties(ignoreUnknown = true)
public class CdsServiceRequestContextJson extends BaseCdsServiceJson implements IModelJson { public class CdsServiceRequestContextJson extends BaseCdsServiceJson implements IModelJson {
@JsonAnyGetter @JsonAnyGetter

View File

@ -31,6 +31,7 @@ import ca.uhn.hapi.fhir.cdshooks.api.ICdsConfigService;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc; import ca.uhn.hapi.fhir.cdshooks.api.ICdsHooksDaoAuthorizationSvc;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry; import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry;
import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory; import ca.uhn.hapi.fhir.cdshooks.module.CdsHooksObjectMapperFactory;
import ca.uhn.hapi.fhir.cdshooks.serializer.CdsServiceRequestJsonDeserializer;
import ca.uhn.hapi.fhir.cdshooks.svc.CdsConfigServiceImpl; import ca.uhn.hapi.fhir.cdshooks.svc.CdsConfigServiceImpl;
import ca.uhn.hapi.fhir.cdshooks.svc.CdsHooksContextBooter; import ca.uhn.hapi.fhir.cdshooks.svc.CdsHooksContextBooter;
import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl; import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl;
@ -100,13 +101,15 @@ public class CdsHooksConfig {
ICdsCrServiceFactory theCdsCrServiceFactory, ICdsCrServiceFactory theCdsCrServiceFactory,
ICrDiscoveryServiceFactory theCrDiscoveryServiceFactory, ICrDiscoveryServiceFactory theCrDiscoveryServiceFactory,
FhirContext theFhirContext) { FhirContext theFhirContext) {
final CdsServiceRequestJsonDeserializer cdsServiceRequestJsonDeserializer =
new CdsServiceRequestJsonDeserializer(theFhirContext, theObjectMapper);
return new CdsServiceRegistryImpl( return new CdsServiceRegistryImpl(
theCdsHooksContextBooter, theCdsHooksContextBooter,
theCdsPrefetchSvc, theCdsPrefetchSvc,
theObjectMapper, theObjectMapper,
theCdsCrServiceFactory, theCdsCrServiceFactory,
theCrDiscoveryServiceFactory, theCrDiscoveryServiceFactory,
theFhirContext); cdsServiceRequestJsonDeserializer);
} }
@Bean @Bean

View File

@ -21,7 +21,6 @@ package ca.uhn.hapi.fhir.cdshooks.controller;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry; import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeedbackJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeedbackJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServicesJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServicesJson;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
@ -73,7 +72,7 @@ public class CdsHooksController {
method = {RequestMethod.POST}, method = {RequestMethod.POST},
consumes = {MediaType.APPLICATION_JSON_VALUE}) consumes = {MediaType.APPLICATION_JSON_VALUE})
public ResponseEntity<CdsServiceResponseJson> cdsServiceRequest( public ResponseEntity<CdsServiceResponseJson> cdsServiceRequest(
@PathVariable("cds_hook") String theCdsHook, @RequestBody CdsServiceRequestJson theCdsServiceRequestJson) { @PathVariable("cds_hook") String theCdsHook, @RequestBody Object theCdsServiceRequestJson) {
CdsServiceResponseJson response = myCdsServiceRegistry.callService(theCdsHook, theCdsServiceRequestJson); CdsServiceResponseJson response = myCdsServiceRegistry.callService(theCdsHook, theCdsServiceRequestJson);
return ResponseEntity.status(200) return ResponseEntity.status(200)
.contentType(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)

View File

@ -20,84 +20,60 @@
package ca.uhn.hapi.fhir.cdshooks.serializer; package ca.uhn.hapi.fhir.cdshooks.serializer;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.parser.IParser; import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.serializer.FhirResourceDeserializer; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsHooksExtension; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsHooksExtension;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestContextJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestContextJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import jakarta.annotation.Nonnull;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import java.io.IOException;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
public class CdsServiceRequestJsonDeserializer extends StdDeserializer<CdsServiceRequestJson> { public class CdsServiceRequestJsonDeserializer {
private final CdsServiceRegistryImpl myCdsServiceRegistry;
private final ObjectMapper myObjectMapper; private final ObjectMapper myObjectMapper;
private final FhirContext myFhirContext; private final FhirContext myFhirContext;
private final IParser myParser; private final IParser myParser;
public CdsServiceRequestJsonDeserializer(CdsServiceRegistryImpl theCdsServiceRegistry, FhirContext theFhirContext) { public CdsServiceRequestJsonDeserializer(
super(CdsServiceRequestJson.class); @Nonnull FhirContext theFhirContext, @Nonnull ObjectMapper theObjectMapper) {
myCdsServiceRegistry = theCdsServiceRegistry;
myFhirContext = theFhirContext; myFhirContext = theFhirContext;
myParser = myFhirContext.newJsonParser().setPrettyPrint(true); myParser = myFhirContext.newJsonParser().setPrettyPrint(true);
// We create a new ObjectMapper instead of using the one from the ApplicationContext to avoid an infinite loop myObjectMapper = theObjectMapper;
// during deserialization.
myObjectMapper = new ObjectMapper();
configureObjectMapper(myObjectMapper);
} }
@Override public CdsServiceRequestJson deserialize(
public CdsServiceRequestJson deserialize(JsonParser theJsonParser, DeserializationContext theDeserializationContext) @Nonnull CdsServiceJson theCdsServiceJson, @Nonnull Object theCdsServiceRequestJson) {
throws IOException { final JsonNode cdsServiceRequestJsonNode =
final JsonNode cdsServiceRequestJsonNode = theJsonParser.getCodec().readTree(theJsonParser); myObjectMapper.convertValue(theCdsServiceRequestJson, JsonNode.class);
final JsonNode hookNode = cdsServiceRequestJsonNode.get("hook"); final JsonNode contextNode = cdsServiceRequestJsonNode.get("context");
validateHookInstance(cdsServiceRequestJsonNode.get("hookInstance"));
validateHook(cdsServiceRequestJsonNode.get("hook"));
validateContext(contextNode);
try {
final JsonNode extensionNode = cdsServiceRequestJsonNode.get("extension"); final JsonNode extensionNode = cdsServiceRequestJsonNode.get("extension");
final JsonNode requestContext = cdsServiceRequestJsonNode.get("context");
final CdsServiceRequestJson cdsServiceRequestJson = final CdsServiceRequestJson cdsServiceRequestJson =
myObjectMapper.treeToValue(cdsServiceRequestJsonNode, CdsServiceRequestJson.class); myObjectMapper.convertValue(cdsServiceRequestJsonNode, CdsServiceRequestJson.class);
LinkedHashMap<String, Object> map = myObjectMapper.readValue(contextNode.toString(), LinkedHashMap.class);
cdsServiceRequestJson.setContext(deserializeContext(map));
if (extensionNode != null) { if (extensionNode != null) {
CdsHooksExtension myRequestExtension = deserializeExtension(hookNode.textValue(), extensionNode.toString()); CdsHooksExtension myRequestExtension =
deserializeExtension(theCdsServiceJson, extensionNode.toString());
cdsServiceRequestJson.setExtension(myRequestExtension); cdsServiceRequestJson.setExtension(myRequestExtension);
} }
if (requestContext != null) {
LinkedHashMap<String, Object> map =
myObjectMapper.readValue(requestContext.toString(), LinkedHashMap.class);
cdsServiceRequestJson.setContext(deserializeRequestContext(map));
}
return cdsServiceRequestJson; return cdsServiceRequestJson;
} catch (JsonProcessingException | IllegalArgumentException theEx) {
throw new InvalidRequestException(Msg.code(2551) + "Invalid CdsServiceRequest received. " + theEx);
}
} }
void configureObjectMapper(ObjectMapper theObjectMapper) { CdsServiceRequestContextJson deserializeContext(LinkedHashMap<String, Object> theMap)
SimpleModule module = new SimpleModule();
module.addDeserializer(IBaseResource.class, new FhirResourceDeserializer(myFhirContext));
theObjectMapper.registerModule(module);
// set this as we will need to ignore properties which are not defined by specific implementation.
theObjectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
CdsHooksExtension deserializeExtension(String theServiceId, String theExtension) throws JsonProcessingException {
final CdsServiceJson cdsServicesJson = myCdsServiceRegistry.getCdsServiceJson(theServiceId);
Class<? extends CdsHooksExtension> extensionClass = cdsServicesJson.getExtensionClass();
if (extensionClass == null) {
return null;
}
return myObjectMapper.readValue(theExtension, extensionClass);
}
CdsServiceRequestContextJson deserializeRequestContext(LinkedHashMap<String, Object> theMap)
throws JsonProcessingException { throws JsonProcessingException {
final CdsServiceRequestContextJson cdsServiceRequestContextJson = new CdsServiceRequestContextJson(); final CdsServiceRequestContextJson cdsServiceRequestContextJson = new CdsServiceRequestContextJson();
for (Map.Entry<String, Object> entry : theMap.entrySet()) { for (Map.Entry<String, Object> entry : theMap.entrySet()) {
@ -114,4 +90,31 @@ public class CdsServiceRequestJsonDeserializer extends StdDeserializer<CdsServic
} }
return cdsServiceRequestContextJson; return cdsServiceRequestContextJson;
} }
private CdsHooksExtension deserializeExtension(
@Nonnull CdsServiceJson theCdsServiceJson, @Nonnull String theExtension) throws JsonProcessingException {
Class<? extends CdsHooksExtension> extensionClass = theCdsServiceJson.getExtensionClass();
if (extensionClass == null) {
return null;
}
return myObjectMapper.readValue(theExtension, extensionClass);
}
private void validateHook(JsonNode hookIdNode) {
if (hookIdNode == null) {
throw new InvalidRequestException(Msg.code(2549) + "hook cannot be null for a CdsServiceRequest.");
}
}
private void validateHookInstance(JsonNode hookInstanceNode) {
if (hookInstanceNode == null) {
throw new InvalidRequestException(Msg.code(2548) + "hookInstance cannot be null for a CdsServiceRequest.");
}
}
private void validateContext(JsonNode requestContextNode) {
if (requestContextNode == null) {
throw new InvalidRequestException(Msg.code(2550) + "context cannot be null for a CdsServiceRequest.");
}
}
} }

View File

@ -20,7 +20,6 @@
package ca.uhn.hapi.fhir.cdshooks.svc; package ca.uhn.hapi.fhir.cdshooks.svc;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.hapi.fhir.cdshooks.api.ICdsMethod; import ca.uhn.hapi.fhir.cdshooks.api.ICdsMethod;
@ -38,7 +37,6 @@ import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory;
import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import jakarta.annotation.Nonnull; import jakarta.annotation.Nonnull;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
@ -50,7 +48,7 @@ import java.util.function.Function;
public class CdsServiceRegistryImpl implements ICdsServiceRegistry { public class CdsServiceRegistryImpl implements ICdsServiceRegistry {
private static final Logger ourLog = LoggerFactory.getLogger(CdsServiceRegistryImpl.class); private static final Logger ourLog = LoggerFactory.getLogger(CdsServiceRegistryImpl.class);
private final CdsServiceRequestJsonDeserializer myCdsServiceRequestJsonDeserializer;
private CdsServiceCache myServiceCache; private CdsServiceCache myServiceCache;
private final CdsHooksContextBooter myCdsHooksContextBooter; private final CdsHooksContextBooter myCdsHooksContextBooter;
@ -65,19 +63,13 @@ public class CdsServiceRegistryImpl implements ICdsServiceRegistry {
ObjectMapper theObjectMapper, ObjectMapper theObjectMapper,
ICdsCrServiceFactory theCdsCrServiceFactory, ICdsCrServiceFactory theCdsCrServiceFactory,
ICrDiscoveryServiceFactory theCrDiscoveryServiceFactory, ICrDiscoveryServiceFactory theCrDiscoveryServiceFactory,
FhirContext theFhirContext) { CdsServiceRequestJsonDeserializer theCdsServiceRequestJsonDeserializer) {
myCdsHooksContextBooter = theCdsHooksContextBooter; myCdsHooksContextBooter = theCdsHooksContextBooter;
myCdsPrefetchSvc = theCdsPrefetchSvc; myCdsPrefetchSvc = theCdsPrefetchSvc;
myObjectMapper = theObjectMapper; myObjectMapper = theObjectMapper;
// registering this deserializer here instead of
// CdsHooksObjectMapperFactory to avoid circular
// dependency
SimpleModule module = new SimpleModule();
module.addDeserializer(
CdsServiceRequestJson.class, new CdsServiceRequestJsonDeserializer(this, theFhirContext));
myObjectMapper.registerModule(module);
myCdsCrServiceFactory = theCdsCrServiceFactory; myCdsCrServiceFactory = theCdsCrServiceFactory;
myCrDiscoveryServiceFactory = theCrDiscoveryServiceFactory; myCrDiscoveryServiceFactory = theCrDiscoveryServiceFactory;
myCdsServiceRequestJsonDeserializer = theCdsServiceRequestJsonDeserializer;
} }
@PostConstruct @PostConstruct
@ -91,10 +83,13 @@ public class CdsServiceRegistryImpl implements ICdsServiceRegistry {
} }
@Override @Override
public CdsServiceResponseJson callService(String theServiceId, CdsServiceRequestJson theCdsServiceRequestJson) { public CdsServiceResponseJson callService(String theServiceId, Object theCdsServiceRequestJson) {
final CdsServiceJson cdsServiceJson = getCdsServiceJson(theServiceId);
final CdsServiceRequestJson deserializedRequest =
myCdsServiceRequestJsonDeserializer.deserialize(cdsServiceJson, theCdsServiceRequestJson);
ICdsServiceMethod serviceMethod = (ICdsServiceMethod) getCdsServiceMethodOrThrowException(theServiceId); ICdsServiceMethod serviceMethod = (ICdsServiceMethod) getCdsServiceMethodOrThrowException(theServiceId);
myCdsPrefetchSvc.augmentRequest(theCdsServiceRequestJson, serviceMethod); myCdsPrefetchSvc.augmentRequest(deserializedRequest, serviceMethod);
Object response = serviceMethod.invoke(myObjectMapper, theCdsServiceRequestJson, theServiceId); Object response = serviceMethod.invoke(myObjectMapper, deserializedRequest, theServiceId);
return encodeServiceResponse(theServiceId, response); return encodeServiceResponse(theServiceId, response);
} }

View File

@ -5,6 +5,7 @@ import ca.uhn.hapi.fhir.cdshooks.api.ICdsServiceRegistry;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeebackOutcomeEnum; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeebackOutcomeEnum;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeedbackJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeedbackJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestContextJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseCardJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson;
@ -118,6 +119,7 @@ public class CdsHooksControllerTest {
request.setHookInstance(TEST_HOOK_INSTANCE); request.setHookInstance(TEST_HOOK_INSTANCE);
request.setHook(HelloWorldService.TEST_HOOK); request.setHook(HelloWorldService.TEST_HOOK);
request.setFhirServer(TEST_FHIR_SERVER); request.setFhirServer(TEST_FHIR_SERVER);
request.setContext( withCdsServiceRequestContext());
String requestBody = myObjectMapper.writeValueAsString(request); String requestBody = myObjectMapper.writeValueAsString(request);
@ -142,8 +144,9 @@ public class CdsHooksControllerTest {
CdsServiceRequestJson request = new CdsServiceRequestJson(); CdsServiceRequestJson request = new CdsServiceRequestJson();
request.setExtension(requestExtension); request.setExtension(requestExtension);
request.setFhirServer(TEST_FHIR_SERVER); request.setFhirServer(TEST_FHIR_SERVER);
request.setHook(HelloWorldService.TEST_HOOK_UNIVERSE_ID); request.setHook(HelloWorldService.TEST_HOOK);
request.setContext(withCdsServiceRequestContext());
request.setHookInstance(UUID.randomUUID().toString());
String requestBody = myObjectMapper.writeValueAsString(request); String requestBody = myObjectMapper.writeValueAsString(request);
@ -163,6 +166,7 @@ public class CdsHooksControllerTest {
request.setHookInstance(TEST_HOOK_INSTANCE); request.setHookInstance(TEST_HOOK_INSTANCE);
request.setHook(HelloWorldService.TEST_HOOK); request.setHook(HelloWorldService.TEST_HOOK);
request.setFhirServer(TEST_FHIR_SERVER); request.setFhirServer(TEST_FHIR_SERVER);
request.setContext(withCdsServiceRequestContext());
String requestBody = myObjectMapper.writeValueAsString(request); String requestBody = myObjectMapper.writeValueAsString(request);
@ -268,4 +272,11 @@ public class CdsHooksControllerTest {
return JsonUtil.serialize(input, true); return JsonUtil.serialize(input, true);
} }
@Nonnull
private static CdsServiceRequestContextJson withCdsServiceRequestContext() {
CdsServiceRequestContextJson cdsServiceRequestContextJson = new CdsServiceRequestContextJson();
cdsServiceRequestContextJson.put("patientId", "Patient/123");
return cdsServiceRequestContextJson;
}
} }

View File

@ -1,106 +1,152 @@
package ca.uhn.hapi.fhir.cdshooks.serializer; package ca.uhn.hapi.fhir.cdshooks.serializer;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsHooksExtension; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestContextJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestContextJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceRequestJson;
import ca.uhn.hapi.fhir.cdshooks.custom.extensions.model.ExampleExtension; import ca.uhn.hapi.fhir.cdshooks.custom.extensions.model.ExampleExtension;
import ca.uhn.hapi.fhir.cdshooks.svc.CdsServiceRegistryImpl;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Nonnull;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn; import static org.assertj.core.api.Assertions.assertThatThrownBy;
@ExtendWith(MockitoExtension.class)
class CdsServiceRequestJsonDeserializerTest { class CdsServiceRequestJsonDeserializerTest {
@Mock private static final String SERVICE_ID = "service-id";
private CdsServiceRegistryImpl myCdsServiceRegistry; private static final String EXAMPLE_PROPERTY_VALUE = "example-value";
private static final String EXAMPLE_PROPERTY_KEY = "example-property";
private static final String HOOK_ID = "hook-id";
private final FhirContext myFhirContext = FhirContext.forR4(); private final FhirContext myFhirContext = FhirContext.forR4();
private final ObjectMapper myObjectMapper = new ObjectMapper();
private CdsServiceRequestJsonDeserializer myFixture; private CdsServiceRequestJsonDeserializer myFixture;
@BeforeEach() @BeforeEach()
void setup() { void setup() {
myFixture = new CdsServiceRequestJsonDeserializer(myCdsServiceRegistry, myFhirContext); myFixture = new CdsServiceRequestJsonDeserializer(myFhirContext, myObjectMapper);
} }
@Test @Test
void configureObjectMapper() { void deserialize_shouldDeserialize_whenValidCdsServiceRequestWithExtensionReceived() {
// setup // setup
ObjectMapper input = new ObjectMapper(); final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final LinkedHashMap<String, Object> extension = withExtension();
final LinkedHashMap<String, Object> request = withRequest(extension);
request.put("context", withContext());
// execute // execute
myFixture.configureObjectMapper(input); final CdsServiceRequestJson actual = myFixture.deserialize(cdsServiceJson, request);
// validate // validate
assertThat(input.getRegisteredModuleIds()).hasSize(1); assertThat(actual.getExtension()).isInstanceOf(ExampleExtension.class);
final ExampleExtension actualExtension = (ExampleExtension) actual.getExtension();
assertThat(actualExtension.getExampleProperty()).isEqualTo(EXAMPLE_PROPERTY_VALUE);
} }
@Test @Test
void deserializeExtensionWhenClassFoundShouldDeserializeExtension() throws JsonProcessingException { void deserialize_shouldIgnoreExtraFieldsInsideExtension_whenExtensionContainsMoreFieldsThanDefinedInClass() {
// setup // setup
final String serviceId = "service-id"; final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final String extension = """ final LinkedHashMap<String, Object> extension = withExtension();
{ extension.put("example-extra-property", "example-extra-value");
"example-property": "example-value" final LinkedHashMap<String, Object> request = withRequest(extension);
request.put("context", withContext());
// execute
final CdsServiceRequestJson actual = myFixture.deserialize(cdsServiceJson, request);
// validate
assertThat(actual.getExtension()).isInstanceOf(ExampleExtension.class);
final ExampleExtension actualExtension = (ExampleExtension) actual.getExtension();
assertThat(actualExtension.getExampleProperty()).isEqualTo(EXAMPLE_PROPERTY_VALUE);
assertThat(actual.getContext().get("encounterId")).isEqualTo("Encounter/123");
} }
""";
@Nonnull
private static LinkedHashMap<String, Object> withContext() {
final LinkedHashMap<String, Object> context = new LinkedHashMap<>();
context.put("encounterId", "Encounter/123");
return context;
}
@Test
void deserialize_shouldThrow_whenCdsServiceRequestIncludesInvalidProperty() {
// setup
final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final LinkedHashMap<String, Object> extension = withExtension();
final LinkedHashMap<String, Object> request = withRequest(extension);
request.put("invalid-key", "some-value");
request.put("context", withContext());
// execute & validate
assertThatThrownBy(
() -> myFixture.deserialize(cdsServiceJson, request))
.isInstanceOf(InvalidRequestException.class)
.hasMessageContaining("HAPI-2551:")
.hasMessageContaining("Invalid CdsServiceRequest received.");
}
@Test
void deserialize_shouldReturnNullExtension_whenNotClassFound() {
// setup
final CdsServiceJson cdsServiceJson = new CdsServiceJson(); final CdsServiceJson cdsServiceJson = new CdsServiceJson();
cdsServiceJson.setId(serviceId); cdsServiceJson.setId(SERVICE_ID);
cdsServiceJson.setExtensionClass(ExampleExtension.class); final LinkedHashMap<String, Object> extension = withExtension();
doReturn(cdsServiceJson).when(myCdsServiceRegistry).getCdsServiceJson(serviceId); extension.put("example-extra-property", "example-extra-value");
final LinkedHashMap<String, Object> request = withRequest(extension);
request.put("context", withContext());
// execute // execute
final ExampleExtension actual = (ExampleExtension) myFixture.deserializeExtension(serviceId, extension); final CdsServiceRequestJson actual = myFixture.deserialize(cdsServiceJson, request);
// validate // validate
assertThat(actual.getExampleProperty()).isEqualTo("example-value"); assertThat(actual.getExtension()).isNull();
} }
@Test @Test
void deserializeExtensionWhenClassFoundButExtensionHasExtraPropertiesShouldIgnoreExtraProperties() throws JsonProcessingException { void deserialize_shouldThrow_whenHookNotFoundInRequest() {
// setup // setup
final String serviceId = "service-id"; final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final String extension = """ final LinkedHashMap<String, Object> request = new LinkedHashMap<>();
{ request.put("context", withContext());
"example-property": "example-value", request.put("hookInstance", UUID.randomUUID().toString());
"example-extra-property": "example-extra-value" // execute and validate
} assertThatThrownBy(() -> myFixture.deserialize(cdsServiceJson, request))
"""; .isInstanceOf(InvalidRequestException.class)
final CdsServiceJson cdsServiceJson = new CdsServiceJson(); .hasMessageContaining("HAPI-2549:")
cdsServiceJson.setId(serviceId); .hasMessageContaining("hook cannot be null for a CdsServiceRequest.");
cdsServiceJson.setExtensionClass(ExampleExtension.class);
doReturn(cdsServiceJson).when(myCdsServiceRegistry).getCdsServiceJson(serviceId);
// execute
final ExampleExtension actual = (ExampleExtension) myFixture.deserializeExtension(serviceId, extension);
// validate
assertThat(actual.getExampleProperty()).isEqualTo("example-value");
} }
@Test @Test
void deserializeExtensionWhenNotClassFoundShouldReturnNull() throws JsonProcessingException { void deserialize_shouldThrow_whenContextNotFoundInRequest() {
// setup // setup
final String serviceId = "service-id"; final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final String extension = """ final LinkedHashMap<String, Object> request = new LinkedHashMap<>();
{ request.put("hook", HOOK_ID);
"example-property": "example-value" request.put("hookInstance", UUID.randomUUID().toString());
} // execute and validate
"""; assertThatThrownBy(() -> myFixture.deserialize(cdsServiceJson, request))
final CdsServiceJson cdsServiceJson = new CdsServiceJson(); .isInstanceOf(InvalidRequestException.class)
cdsServiceJson.setId(serviceId); .hasMessageContaining("HAPI-2550:")
doReturn(cdsServiceJson).when(myCdsServiceRegistry).getCdsServiceJson(serviceId); .hasMessageContaining("context cannot be null for a CdsServiceRequest.");
// execute
final CdsHooksExtension actual = myFixture.deserializeExtension(serviceId, extension);
// validate
assertThat(actual).isNull();
} }
@Test @Test
void deserializeRequestContextShouldDeserializeValidContext() throws JsonProcessingException { void deserialize_shouldThrow_whenHookInstanceNotFoundInRequest() {
// setup
final CdsServiceJson cdsServiceJson = withCdsServiceJsonIncludingExtensionClass();
final LinkedHashMap<String, Object> request = new LinkedHashMap<>();
request.put("context", withContext());
request.put("hook", HOOK_ID);
// execute and validate
assertThatThrownBy(() -> myFixture.deserialize(cdsServiceJson, request))
.isInstanceOf(InvalidRequestException.class)
.hasMessageContaining("HAPI-2548:")
.hasMessageContaining("hookInstance cannot be null for a CdsServiceRequest.");
}
@Test
void deserializeRequestContext_shouldDeserialize_whenContextIsValid() throws JsonProcessingException {
// setup // setup
final String encounterId = "123"; final String encounterId = "123";
final Patient patientContext = new Patient(); final Patient patientContext = new Patient();
@ -109,9 +155,34 @@ class CdsServiceRequestJsonDeserializerTest {
input.put("encounterId", encounterId); input.put("encounterId", encounterId);
input.put("patient", patientContext); input.put("patient", patientContext);
// execute // execute
final CdsServiceRequestContextJson actual = myFixture.deserializeRequestContext(input); final CdsServiceRequestContextJson actual = myFixture.deserializeContext(input);
// validate // validate
assertThat(actual.get("encounterId")).isEqualTo(encounterId); assertThat(actual.get("encounterId")).isEqualTo(encounterId);
assertThat(actual.get("patient")).usingRecursiveComparison().isEqualTo(patientContext); assertThat(actual.get("patient")).usingRecursiveComparison().isEqualTo(patientContext);
} }
@Nonnull
private static LinkedHashMap<String, Object> withExtension() {
final LinkedHashMap<String, Object> extension = new LinkedHashMap<>();
extension.put(EXAMPLE_PROPERTY_KEY, EXAMPLE_PROPERTY_VALUE);
return extension;
}
@Nonnull
private static CdsServiceJson withCdsServiceJsonIncludingExtensionClass() {
final CdsServiceJson cdsServiceJson = new CdsServiceJson();
cdsServiceJson.setId(SERVICE_ID);
cdsServiceJson.setExtensionClass(ExampleExtension.class);
cdsServiceJson.setHook(HOOK_ID);
return cdsServiceJson;
}
@Nonnull
private static LinkedHashMap<String, Object> withRequest(@Nonnull LinkedHashMap<String, Object> theExtension) {
final LinkedHashMap<String, Object> request = new LinkedHashMap<>();
request.put("extension", theExtension);
request.put("hookInstance", UUID.randomUUID().toString());
request.put("hook", HOOK_ID);
return request;
}
} }

View File

@ -1,10 +1,10 @@
package ca.uhn.hapi.fhir.cdshooks.svc; package ca.uhn.hapi.fhir.cdshooks.svc;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeedbackJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceFeedbackJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceJson;
import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson; import ca.uhn.hapi.fhir.cdshooks.api.json.CdsServiceResponseJson;
import ca.uhn.hapi.fhir.cdshooks.serializer.CdsServiceRequestJsonDeserializer;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory; import ca.uhn.hapi.fhir.cdshooks.svc.cr.ICdsCrServiceFactory;
import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory; import ca.uhn.hapi.fhir.cdshooks.svc.cr.discovery.ICrDiscoveryServiceFactory;
import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc; import ca.uhn.hapi.fhir.cdshooks.svc.prefetch.CdsPrefetchSvc;
@ -33,13 +33,14 @@ class CdsServiceRegistryImplTest {
private ICrDiscoveryServiceFactory myCrDiscoveryServiceFactory; private ICrDiscoveryServiceFactory myCrDiscoveryServiceFactory;
@Mock @Mock
private CdsServiceCache myCdsServiceCache; private CdsServiceCache myCdsServiceCache;
@Mock
private CdsServiceRequestJsonDeserializer myCdsServiceRequestJsonDeserializer;
private final ObjectMapper myObjectMapper = new ObjectMapper(); private final ObjectMapper myObjectMapper = new ObjectMapper();
private final FhirContext myFhirContext = FhirContext.forR4();
private CdsServiceRegistryImpl myFixture; private CdsServiceRegistryImpl myFixture;
@BeforeEach() @BeforeEach()
void setup() { void setup() {
myFixture = new CdsServiceRegistryImpl(myCdsHooksContextBooter, myCdsPrefetchSvc, myObjectMapper, myCdsCrServiceFactory, myCrDiscoveryServiceFactory, myFhirContext); myFixture = new CdsServiceRegistryImpl(myCdsHooksContextBooter, myCdsPrefetchSvc, myObjectMapper, myCdsCrServiceFactory, myCrDiscoveryServiceFactory, myCdsServiceRequestJsonDeserializer);
} }
@Test @Test

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -7,7 +7,7 @@
<parent> <parent>
<artifactId>hapi-fhir-serviceloaders</artifactId> <artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<version>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -7,7 +7,7 @@
<parent> <parent>
<artifactId>hapi-fhir-serviceloaders</artifactId> <artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<version>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>
@ -21,7 +21,7 @@
<dependency> <dependency>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-caching-api</artifactId> <artifactId>hapi-fhir-caching-api</artifactId>
<version>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -7,7 +7,7 @@
<parent> <parent>
<artifactId>hapi-fhir-serviceloaders</artifactId> <artifactId>hapi-fhir-serviceloaders</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<version>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -7,7 +7,7 @@
<parent> <parent>
<artifactId>hapi-fhir</artifactId> <artifactId>hapi-fhir</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<version>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath> <relativePath>../../pom.xml</relativePath>
</parent> </parent>

View File

@ -5,7 +5,7 @@
<parent> <parent>
<artifactId>hapi-deployable-pom</artifactId> <artifactId>hapi-deployable-pom</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId> <groupId>ca.uhn.hapi.fhir</groupId>
<version>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
</parent> </parent>
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId> <artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath> <relativePath>../pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -89,7 +89,7 @@ public class ForceIdMigrationFixTask extends BaseTask {
" set fhir_id = coalesce( " " set fhir_id = coalesce( "
+ +
// case 5. // case 5.
" trim(fhir_id), " trimFhirId()
+ +
// case 3 // case 3
" (select f.forced_id from hfj_forced_id f where f.resource_pid = res_id), " " (select f.forced_id from hfj_forced_id f where f.resource_pid = res_id), "
@ -109,6 +109,22 @@ public class ForceIdMigrationFixTask extends BaseTask {
} }
} }
private String trimFhirId() {
switch (getDriverType()) {
case MSSQL_2012:
return " LTRIM(RTRIM(fhir_id)), ";
case H2_EMBEDDED:
case DERBY_EMBEDDED:
case MARIADB_10_1:
case MYSQL_5_7:
case POSTGRES_9_4:
case ORACLE_12C:
case COCKROACHDB_21_1:
default:
return " trim(fhir_id), ";
}
}
private String getWhereClauseByDBType() { private String getWhereClauseByDBType() {
switch (getDriverType()) { switch (getDriverType()) {
case MSSQL_2012: case MSSQL_2012:

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>
@ -145,6 +145,20 @@
<artifactId>jakarta.xml.bind-api</artifactId> <artifactId>jakarta.xml.bind-api</artifactId>
</dependency> </dependency>
<!-- For test CR config that's exported to downstream modules -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>${project.version}</version>
<exclusions>
<!-- prevent conflict with other version of this dependency sourced from elsewhere -->
<exclusion>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.json</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- test --> <!-- test -->
<dependency> <dependency>
<groupId>org.testcontainers</groupId> <groupId>org.testcontainers</groupId>

View File

@ -21,8 +21,10 @@ package ca.uhn.fhir.cr.common;
import ca.uhn.fhir.cr.repo.HapiFhirRepository; import ca.uhn.fhir.cr.repo.HapiFhirRepository;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import com.google.common.annotations.Beta;
@FunctionalInterface @FunctionalInterface
@Beta
public interface IRepositoryFactory { public interface IRepositoryFactory {
HapiFhirRepository create(RequestDetails theRequestDetails); HapiFhirRepository create(RequestDetails theRequestDetails);
} }

View File

@ -0,0 +1,31 @@
/*-
* #%L
* HAPI FHIR - Clinical Reasoning
* %%
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package ca.uhn.fhir.cr.common;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.opencds.cqf.fhir.api.Repository;
/**
* Factory interface to return a {@link Repository} from a {@link RequestDetails}
*/
@FunctionalInterface
public interface RepositoryFactoryForRepositoryInterface {
Repository create(RequestDetails theRequestDetails);
}

View File

@ -20,6 +20,7 @@
package ca.uhn.fhir.cr.config; package ca.uhn.fhir.cr.config;
import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.common.IRepositoryFactory;
import ca.uhn.fhir.cr.common.RepositoryFactoryForRepositoryInterface;
import ca.uhn.fhir.cr.repo.HapiFhirRepository; import ca.uhn.fhir.cr.repo.HapiFhirRepository;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.RestfulServer;
@ -28,8 +29,15 @@ import org.springframework.context.annotation.Configuration;
@Configuration @Configuration
public class RepositoryConfig { public class RepositoryConfig {
@Bean @Bean
IRepositoryFactory repositoryFactory(DaoRegistry theDaoRegistry, RestfulServer theRestfulServer) { IRepositoryFactory repositoryFactory(DaoRegistry theDaoRegistry, RestfulServer theRestfulServer) {
return rd -> new HapiFhirRepository(theDaoRegistry, rd, theRestfulServer); return rd -> new HapiFhirRepository(theDaoRegistry, rd, theRestfulServer);
} }
@Bean
RepositoryFactoryForRepositoryInterface repositoryFactoryForInterface(
DaoRegistry theDaoRegistry, RestfulServer theRestfulServer) {
return rd -> new HapiFhirRepository(theDaoRegistry, rd, theRestfulServer);
}
} }

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.cr.config.r4;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.cr.common.IRepositoryFactory; import ca.uhn.fhir.cr.common.IRepositoryFactory;
import ca.uhn.fhir.cr.common.RepositoryFactoryForRepositoryInterface;
import ca.uhn.fhir.cr.config.ProviderLoader; import ca.uhn.fhir.cr.config.ProviderLoader;
import ca.uhn.fhir.cr.config.ProviderSelector; import ca.uhn.fhir.cr.config.ProviderSelector;
import ca.uhn.fhir.cr.config.RepositoryConfig; import ca.uhn.fhir.cr.config.RepositoryConfig;
@ -66,7 +67,8 @@ public class CrR4Config {
@Bean @Bean
IMeasureServiceFactory r4MeasureServiceFactory( IMeasureServiceFactory r4MeasureServiceFactory(
IRepositoryFactory theRepositoryFactory, MeasureEvaluationOptions theEvaluationOptions) { RepositoryFactoryForRepositoryInterface theRepositoryFactory,
MeasureEvaluationOptions theEvaluationOptions) {
return rd -> new R4MeasureService(theRepositoryFactory.create(rd), theEvaluationOptions); return rd -> new R4MeasureService(theRepositoryFactory.create(rd), theEvaluationOptions);
} }

View File

@ -1,4 +1,23 @@
package ca.uhn.fhir.cr; /*-
* #%L
* HAPI FHIR - Clinical Reasoning
* %%
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package ca.uhn.fhir.cr.config.test;
import org.cqframework.cql.cql2elm.CqlCompilerException; import org.cqframework.cql.cql2elm.CqlCompilerException;
import org.cqframework.cql.cql2elm.CqlCompilerOptions; import org.cqframework.cql.cql2elm.CqlCompilerOptions;
@ -7,7 +26,9 @@ import org.cqframework.cql.cql2elm.LibraryBuilder;
import org.opencds.cqf.fhir.cql.CqlEngineOptions; import org.opencds.cqf.fhir.cql.CqlEngineOptions;
import org.opencds.cqf.fhir.cql.CqlOptions; import org.opencds.cqf.fhir.cql.CqlOptions;
/**
* Common CQL properties shared with downstream modules.
*/
public class TestCqlProperties { public class TestCqlProperties {
//cql settings //cql settings

View File

@ -1,4 +1,23 @@
package ca.uhn.fhir.cr; /*-
* #%L
* HAPI FHIR - Clinical Reasoning
* %%
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package ca.uhn.fhir.cr.config.test;
import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider; import ca.uhn.fhir.batch2.jobs.reindex.ReindexProvider;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
@ -47,6 +66,9 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
/**
* Common hapi-fhir clinical reasoning config shared with downstream modules.
*/
@Configuration @Configuration
@Import({SubscriptionSubmitterConfig.class, SubscriptionChannelConfig.class}) @Import({SubscriptionSubmitterConfig.class, SubscriptionChannelConfig.class})
public class TestCrConfig { public class TestCrConfig {
@ -77,7 +99,9 @@ public class TestCrConfig {
} }
@Bean @Bean
public TestCqlProperties testCqlProperties(){ public TestCqlProperties testCqlProperties(){
return new TestCqlProperties();} return new TestCqlProperties();
}
@Bean @Bean
public JpaStorageSettings storageSettings() { public JpaStorageSettings storageSettings() {
JpaStorageSettings storageSettings = new JpaStorageSettings(); JpaStorageSettings storageSettings = new JpaStorageSettings();
@ -87,12 +111,6 @@ public class TestCrConfig {
storageSettings.setResourceClientIdStrategy(JpaStorageSettings.ClientIdStrategyEnum.ANY); storageSettings.setResourceClientIdStrategy(JpaStorageSettings.ClientIdStrategyEnum.ANY);
return storageSettings; return storageSettings;
} }
@Bean
public PartitionHelper partitionHelper() {
return new PartitionHelper();
}
@Bean @Bean
public ModelManager modelManager(Map<ModelIdentifier, Model> theGlobalModelCache) { public ModelManager modelManager(Map<ModelIdentifier, Model> theGlobalModelCache) {
return new ModelManager(theGlobalModelCache); return new ModelManager(theGlobalModelCache);

View File

@ -1,9 +1,28 @@
package ca.uhn.fhir.cr.r4; /*-
* #%L
* HAPI FHIR - Clinical Reasoning
* %%
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package ca.uhn.fhir.cr.config.test.r4;
import ca.uhn.fhir.cr.TestCqlProperties;
import ca.uhn.fhir.cr.TestCrConfig;
import ca.uhn.fhir.cr.common.CqlThreadFactory; import ca.uhn.fhir.cr.common.CqlThreadFactory;
import ca.uhn.fhir.cr.config.r4.CrR4Config; import ca.uhn.fhir.cr.config.r4.CrR4Config;
import ca.uhn.fhir.cr.config.test.TestCqlProperties;
import ca.uhn.fhir.cr.config.test.TestCrConfig;
import org.cqframework.cql.cql2elm.CqlCompilerOptions; import org.cqframework.cql.cql2elm.CqlCompilerOptions;
import org.cqframework.cql.cql2elm.model.CompiledLibrary; import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.Model; import org.cqframework.cql.cql2elm.model.Model;
@ -30,6 +49,9 @@ import java.util.Set;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
/**
* Common hapi-fhir clinical reasoning config specifically for R4 shared with downstream modules.
*/
@Configuration @Configuration
@Import({TestCrConfig.class, CrR4Config.class}) @Import({TestCrConfig.class, CrR4Config.class})
public class TestCrR4Config { public class TestCrR4Config {

View File

@ -0,0 +1,10 @@
package ca.uhn.fhir.cr;
import org.springframework.context.annotation.Bean;
public class TestHapiFhirCrPartitionConfig {
@Bean
public PartitionHelper partitionHelper() {
return new PartitionHelper();
}
}

View File

@ -1,8 +1,9 @@
package ca.uhn.fhir.cr.dstu3; package ca.uhn.fhir.cr.dstu3;
import ca.uhn.fhir.cr.TestCqlProperties; import ca.uhn.fhir.cr.TestHapiFhirCrPartitionConfig;
import ca.uhn.fhir.cr.TestCrConfig; import ca.uhn.fhir.cr.config.test.TestCqlProperties;
import ca.uhn.fhir.cr.config.dstu3.CrDstu3Config; import ca.uhn.fhir.cr.config.dstu3.CrDstu3Config;
import ca.uhn.fhir.cr.config.test.TestCrConfig;
import org.cqframework.cql.cql2elm.CqlCompilerOptions; import org.cqframework.cql.cql2elm.CqlCompilerOptions;
import org.cqframework.cql.cql2elm.model.CompiledLibrary; import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.Model; import org.cqframework.cql.cql2elm.model.Model;
@ -26,6 +27,7 @@ import java.util.Set;
@Configuration @Configuration
@Import({ @Import({
TestHapiFhirCrPartitionConfig.class,
TestCrConfig.class, TestCrConfig.class,
CrDstu3Config.class CrDstu3Config.class
}) })

View File

@ -2,10 +2,12 @@ package ca.uhn.fhir.cr.r4;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.cr.IResourceLoader; import ca.uhn.fhir.cr.IResourceLoader;
import ca.uhn.fhir.cr.TestHapiFhirCrPartitionConfig;
import ca.uhn.fhir.cr.config.r4.ApplyOperationConfig; import ca.uhn.fhir.cr.config.r4.ApplyOperationConfig;
import ca.uhn.fhir.cr.config.r4.ExtractOperationConfig; import ca.uhn.fhir.cr.config.r4.ExtractOperationConfig;
import ca.uhn.fhir.cr.config.r4.PackageOperationConfig; import ca.uhn.fhir.cr.config.r4.PackageOperationConfig;
import ca.uhn.fhir.cr.config.r4.PopulateOperationConfig; import ca.uhn.fhir.cr.config.r4.PopulateOperationConfig;
import ca.uhn.fhir.cr.config.test.r4.TestCrR4Config;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
@ -38,6 +40,7 @@ import java.util.concurrent.TimeUnit;
@ContextConfiguration(classes = { @ContextConfiguration(classes = {
TestHapiFhirCrPartitionConfig.class,
TestCrR4Config.class, TestCrR4Config.class,
ApplyOperationConfig.class, ApplyOperationConfig.class,
ExtractOperationConfig.class, ExtractOperationConfig.class,

View File

@ -1,6 +1,6 @@
package ca.uhn.fhir.cr.r4; package ca.uhn.fhir.cr.r4;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerCacheRefresherImpl; import ca.uhn.fhir.jpa.cache.IResourceChangeListenerCacheRefresher;
import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl; import ca.uhn.fhir.jpa.cache.ResourceChangeListenerRegistryImpl;
import org.hl7.fhir.r4.model.DateType; import org.hl7.fhir.r4.model.DateType;
import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.IdType;
@ -16,13 +16,13 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
@ExtendWith(SpringExtension.class) @ExtendWith(SpringExtension.class)
public class CrResourceListenerTests extends BaseCrR4TestServer { public class CrResourceListenerTest extends BaseCrR4TestServer {
@Autowired @Autowired
EvaluationSettings myEvaluationSettings; EvaluationSettings myEvaluationSettings;
@Autowired @Autowired
ResourceChangeListenerRegistryImpl myResourceChangeListenerRegistry; ResourceChangeListenerRegistryImpl myResourceChangeListenerRegistry;
@Autowired @Autowired
ResourceChangeListenerCacheRefresherImpl myResourceChangeListenerCacheRefresher; IResourceChangeListenerCacheRefresher myResourceChangeListenerCacheRefresher;
public MeasureReport runEvaluateMeasure(String periodStart, String periodEnd, String subject, String measureId, String reportType, String practitioner){ public MeasureReport runEvaluateMeasure(String periodStart, String periodEnd, String subject, String measureId, String reportType, String practitioner){
@ -118,7 +118,7 @@ public class CrResourceListenerTests extends BaseCrR4TestServer {
myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners(); myResourceChangeListenerCacheRefresher.refreshExpiredCachesAndNotifyListeners();
//cache should be invalidated for matching library name and version //cache should be invalidated for matching library name and version
assertThat(myEvaluationSettings.getLibraryCache()).hasSize(6); assertThat(myEvaluationSettings.getLibraryCache()).hasSize(7);
} }
@Test @Test

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -134,7 +134,7 @@ public class DaoResourceLinkResolver<T extends IResourcePersistentId> implements
type, targetReference, idPart, theRequest, theTransactionDetails); type, targetReference, idPart, theRequest, theTransactionDetails);
if (!createdTableOpt.isPresent()) { if (!createdTableOpt.isPresent()) {
if (myStorageSettings.isEnforceReferentialIntegrityOnWrite() == false) { if (!myStorageSettings.isEnforceReferentialIntegrityOnWrite()) {
return null; return null;
} }
@ -150,20 +150,8 @@ public class DaoResourceLinkResolver<T extends IResourcePersistentId> implements
"Resolved resource of type {} as PID: {}", "Resolved resource of type {} as PID: {}",
resolvedResource.getResourceType(), resolvedResource.getResourceType(),
resolvedResource.getPersistentId()); resolvedResource.getPersistentId());
if (!resourceType.equals(resolvedResource.getResourceType())) { if (!validateResolvedResourceOrThrow(resourceType, resolvedResource, targetResourceId, idPart, sourcePath)) {
ourLog.error( return null;
"Resource with PID {} was of type {} and wanted {}",
resolvedResource.getPersistentId(),
resourceType,
resolvedResource.getResourceType());
throw new UnprocessableEntityException(Msg.code(1095)
+ "Resource contains reference to unknown resource ID " + targetResourceId.getValue());
}
if (resolvedResource.getDeleted() != null) {
String resName = resolvedResource.getResourceType();
throw new InvalidRequestException(Msg.code(1096) + "Resource " + resName + "/" + idPart
+ " is deleted, specified in path: " + sourcePath);
} }
if (persistentId == null) { if (persistentId == null) {
@ -182,6 +170,45 @@ public class DaoResourceLinkResolver<T extends IResourcePersistentId> implements
return resolvedResource; return resolvedResource;
} }
/**
* Validates the resolved resource.
* If 'Enforce Referential Integrity on Write' is enabled:
* Throws <code>UnprocessableEntityException</code> when resource types do not match
* Throws <code>InvalidRequestException</code> when the resolved resource was deleted
* <p>
* Otherwise, return false when resource types do not match or resource was deleted
* and return true if the resolved resource is valid.
*/
private boolean validateResolvedResourceOrThrow(
String resourceType,
IResourceLookup resolvedResource,
IIdType targetResourceId,
String idPart,
String sourcePath) {
if (!resourceType.equals(resolvedResource.getResourceType())) {
ourLog.error(
"Resource with PID {} was of type {} and wanted {}",
resolvedResource.getPersistentId(),
resourceType,
resolvedResource.getResourceType());
if (!myStorageSettings.isEnforceReferentialIntegrityOnWrite()) {
return false;
}
throw new UnprocessableEntityException(Msg.code(1095)
+ "Resource contains reference to unknown resource ID " + targetResourceId.getValue());
}
if (resolvedResource.getDeleted() != null) {
if (!myStorageSettings.isEnforceReferentialIntegrityOnWrite()) {
return false;
}
String resName = resolvedResource.getResourceType();
throw new InvalidRequestException(Msg.code(1096) + "Resource " + resName + "/" + idPart
+ " is deleted, specified in path: " + sourcePath);
}
return true;
}
@Nullable @Nullable
@Override @Override
public IBaseResource loadTargetResource( public IBaseResource loadTargetResource(

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

View File

@ -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>7.5.0-SNAPSHOT</version> <version>7.5.1-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath> <relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent> </parent>

Some files were not shown because too many files have changed in this diff Show More