Merge branch 'master' of https://github.com/jamesagnew/hapi-fhir into validate-subscription-criteria
This commit is contained in:
commit
0b578e3b0b
|
@ -1,5 +1,6 @@
|
|||
# Use docker-based build environment (instead of openvz)
|
||||
sudo: false
|
||||
#sudo: false
|
||||
sudo: required
|
||||
|
||||
language: java
|
||||
jdk:
|
||||
|
@ -21,4 +22,4 @@ before_script:
|
|||
script:
|
||||
# - mvn -e -B clean install && cd hapi-fhir-ra && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID clean test jacoco:report coveralls:report
|
||||
# - mvn -Dci=true -e -B -P ALLMODULES,NOPARALLEL,ERRORPRONE clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report
|
||||
- mvn -Dci=true -e -B -P ALLMODULES,NOPARALLEL clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report
|
||||
- mvn -Dci=true -e -B -P ALLMODULES,MINPARALLEL clean install && cd hapi-fhir-jacoco && mvn -e -B -DTRAVIS_JOB_ID=$TRAVIS_JOB_ID jacoco:report coveralls:report
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
package ca.uhn.fhir.rest.client.interceptor;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2017 University Health Network
|
||||
* %%
|
||||
* 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%
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import ca.uhn.fhir.rest.client.IClientInterceptor;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpResponse;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
/**
|
||||
* This interceptor adds an arbitrary header to requests made by this client. Both the
|
||||
* header name and the header value are specified by the calling code.
|
||||
*/
|
||||
public class SimpleRequestHeaderInterceptor implements IClientInterceptor {
|
||||
|
||||
private String myHeaderName;
|
||||
private String myHeaderValue;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SimpleRequestHeaderInterceptor() {
|
||||
this(null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public SimpleRequestHeaderInterceptor(String theHeaderName, String theHeaderValue) {
|
||||
super();
|
||||
myHeaderName = theHeaderName;
|
||||
myHeaderValue = theHeaderValue;
|
||||
}
|
||||
|
||||
public String getHeaderName() {
|
||||
return myHeaderName;
|
||||
}
|
||||
|
||||
public String getHeaderValue() {
|
||||
return myHeaderValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptRequest(IHttpRequest theRequest) {
|
||||
if (isNotBlank(getHeaderName())) {
|
||||
theRequest.addHeader(getHeaderName(), getHeaderValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void interceptResponse(IHttpResponse theResponse) throws IOException {
|
||||
// nothing
|
||||
}
|
||||
|
||||
public void setHeaderName(String theHeaderName) {
|
||||
myHeaderName = theHeaderName;
|
||||
}
|
||||
|
||||
public void setHeaderValue(String theHeaderValue) {
|
||||
myHeaderValue = theHeaderValue;
|
||||
}
|
||||
|
||||
}
|
|
@ -100,6 +100,11 @@
|
|||
<artifactId>thymeleaf</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.ttddyy</groupId>
|
||||
<artifactId>datasource-proxy</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
|
@ -627,13 +632,21 @@
|
|||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<!-- <profile> <id>DIST</id> <build> <plugins> <plugin> <groupId>de.juplo</groupId> <artifactId>hibernate4-maven-plugin</artifactId> <configuration> <force>true</force> <target>SCRIPT</target> <skip>${skip-hib4}</skip>
|
||||
</configuration> <dependencies> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>${hibernate_version}</version> </dependency> </dependencies> <executions>
|
||||
<execution> <id>o10g</id> <goals> <goal>export</goal> </goals> <phase>test</phase> <configuration> <hibernateDialect>org.hibernate.dialect.Oracle10gDialect</hibernateDialect> <outputFile>${project.build.directory}/schema_oracle_10g.sql</outputFile>
|
||||
</configuration> </execution> <execution> <id>derby</id> <goals> <goal>export</goal> </goals> <phase>test</phase> <configuration> <hibernateDialect>org.hibernate.dialect.DerbyTenSevenDialect</hibernateDialect>
|
||||
<outputFile>${project.build.directory}/schema_derby.sql</outputFile> </configuration> </execution> <execution> <id>hsql</id> <goals> <goal>export</goal> </goals> <phase>test</phase> <configuration> <hibernateDialect>org.hibernate.dialect.HSQLDialect</hibernateDialect>
|
||||
<outputFile>${project.build.directory}/schema_hsql.sql</outputFile> </configuration> </execution> <execution> <id>mysql5</id> <goals> <goal>export</goal> </goals> <phase>test</phase> <configuration> <hibernateDialect>org.hibernate.dialect.MySQL5Dialect</hibernateDialect>
|
||||
<outputFile>${project.build.directory}/schema_mysql_5.sql</outputFile> </configuration> </execution> </executions> </plugin> </plugins> </build> </profile> -->
|
||||
</profiles>
|
||||
<profile>
|
||||
<id>MINPARALLEL</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<forkCount>2</forkCount>
|
||||
<reuseForks>true</reuseForks>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -33,6 +33,7 @@ import javax.persistence.criteria.Root;
|
|||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
|
@ -40,8 +41,7 @@ import org.springframework.transaction.annotation.Transactional;
|
|||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ITermConceptDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.*;
|
||||
import ca.uhn.fhir.jpa.entity.ForcedId;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.util.ReindexFailureException;
|
||||
|
@ -87,30 +87,33 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private IResourceTableDao myResourceTableDao;
|
||||
|
||||
private int doPerformReindexingPassForResources(final Integer theCount, TransactionTemplate txTemplate) {
|
||||
return txTemplate.execute(new TransactionCallback<Integer>() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Integer doInTransaction(TransactionStatus theStatus) {
|
||||
TypedQuery<ResourceTable> q = myEntityManager.createQuery("SELECT t FROM " + ResourceTable.class.getSimpleName() + " t WHERE t.myIndexStatus IS null", ResourceTable.class);
|
||||
|
||||
int maxResult = 500;
|
||||
if (theCount != null) {
|
||||
maxResult = Math.min(theCount, 2000);
|
||||
}
|
||||
maxResult = Math.max(maxResult, 10);
|
||||
|
||||
TypedQuery<Long> q = myEntityManager.createQuery("SELECT t.myId FROM ResourceTable t WHERE t.myIndexStatus IS NULL", Long.class);
|
||||
|
||||
ourLog.debug("Beginning indexing query with maximum {}", maxResult);
|
||||
q.setMaxResults(maxResult);
|
||||
List<ResourceTable> resources = q.getResultList();
|
||||
if (resources.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ourLog.info("Indexing {} resources", resources.size());
|
||||
Collection<Long> resources = q.getResultList();
|
||||
|
||||
int count = 0;
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
for (ResourceTable resourceTable : resources) {
|
||||
for (Long nextId : resources) {
|
||||
ResourceTable resourceTable = myResourceTableDao.findOne(nextId);
|
||||
|
||||
try {
|
||||
/*
|
||||
* This part is because from HAPI 1.5 - 1.6 we changed the format of forced ID to be "type/id" instead of just "id"
|
||||
|
@ -135,13 +138,22 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
throw new ReindexFailureException(resourceTable.getId());
|
||||
}
|
||||
count++;
|
||||
|
||||
if (count >= maxResult) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
long delay = System.currentTimeMillis() - start;
|
||||
long avg = (delay / resources.size());
|
||||
ourLog.info("Indexed {} / {} resources in {}ms - Avg {}ms / resource", new Object[] { count, resources.size(), delay, avg });
|
||||
|
||||
return resources.size();
|
||||
long avg;
|
||||
if (count > 0) {
|
||||
avg = (delay / count);
|
||||
ourLog.info("Indexed {} resources in {}ms - Avg {}ms / resource", new Object[] { count, delay, avg });
|
||||
} else {
|
||||
ourLog.debug("Indexed 0 resources in {}ms", delay);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -158,6 +170,7 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED, readOnly=true)
|
||||
@Override
|
||||
public Map<String, Long> getResourceCounts() {
|
||||
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
|
|
|
@ -64,21 +64,21 @@ public class DaoConfig {
|
|||
*/
|
||||
public static final Long DEFAULT_REUSE_CACHED_SEARCH_RESULTS_FOR_MILLIS = DateUtils.MILLIS_PER_MINUTE;
|
||||
|
||||
// ***
|
||||
// update setter javadoc if default changes
|
||||
// ***
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
*/
|
||||
private boolean myAllowExternalReferences = false;
|
||||
|
||||
// ***
|
||||
// update setter javadoc if default changes
|
||||
// ***
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
*/
|
||||
private boolean myAllowInlineMatchUrlReferences = true;
|
||||
private boolean myAllowMultipleDelete;
|
||||
private boolean myDefaultSearchParamsCanBeOverridden = false;
|
||||
|
||||
// ***
|
||||
// update setter javadoc if default changes
|
||||
// ***
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
*/
|
||||
private int myDeferIndexingForCodesystemsOfSize = 2000;
|
||||
|
||||
private boolean myDeleteStaleSearches = true;
|
||||
|
@ -87,27 +87,35 @@ public class DaoConfig {
|
|||
|
||||
private boolean myEnforceReferentialIntegrityOnWrite = true;
|
||||
|
||||
// ***
|
||||
// update setter javadoc if default changes
|
||||
// ***
|
||||
private int myEverythingIncludesFetchPageSize = 50;
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
*/
|
||||
private long myExpireSearchResultsAfterMillis = DateUtils.MILLIS_PER_HOUR;
|
||||
private int myHardTagListLimit = 1000;
|
||||
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
*/
|
||||
private Integer myFetchSizeDefaultMaximum = null;
|
||||
private int myHardTagListLimit = 1000;
|
||||
private int myIncludeLimit = 2000;
|
||||
// ***
|
||||
// update setter javadoc if default changes
|
||||
// ***
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
*/
|
||||
private boolean myIndexContainedResources = true;
|
||||
private List<IServerInterceptor> myInterceptors;
|
||||
// ***
|
||||
// update setter javadoc if default changes
|
||||
// ***
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
*/
|
||||
private int myMaximumExpansionSize = 5000;
|
||||
private int myMaximumSearchResultCountInTransaction = DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION;
|
||||
private ResourceEncodingEnum myResourceEncoding = ResourceEncodingEnum.JSONC;
|
||||
private Long myReuseCachedSearchResultsForMillis = DEFAULT_REUSE_CACHED_SEARCH_RESULTS_FOR_MILLIS;
|
||||
private boolean mySchedulingDisabled;
|
||||
private boolean mySubscriptionEnabled;
|
||||
/**
|
||||
* update setter javadoc if default changes
|
||||
*/
|
||||
private long mySubscriptionPollDelay = 1000;
|
||||
private Long mySubscriptionPurgeInactiveAfterMillis;
|
||||
private boolean mySuppressUpdatesWithNoChange = true;
|
||||
|
@ -140,6 +148,22 @@ public class DaoConfig {
|
|||
return myDeferIndexingForCodesystemsOfSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlike with normal search queries, $everything queries have their _includes loaded by the main search thread and these included results
|
||||
* are added to the normal search results instead of being added on as extras in a page. This means that they will not appear multiple times
|
||||
* as the search results are paged over.
|
||||
* <p>
|
||||
* In order to recursively load _includes, we process the original results in batches of this size. Adjust with caution, increasing this
|
||||
* value may improve performance but may also cause memory issues.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value is 50
|
||||
* </p>
|
||||
*/
|
||||
public int getEverythingIncludesFetchPageSize() {
|
||||
return myEverythingIncludesFetchPageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of milliseconds that search results for a given client search
|
||||
* should be preserved before being purged from the database.
|
||||
|
@ -160,6 +184,20 @@ public class DaoConfig {
|
|||
return myExpireSearchResultsAfterMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default maximum number of results to load in a query.
|
||||
* <p>
|
||||
* For example, if the database has a million Patient resources in it, and
|
||||
* the client requests <code>GET /Patient</code>, if this value is set
|
||||
* to a non-null value (default is <code>null</code>) only this number
|
||||
* of results will be fetched. Setting this value appropriately
|
||||
* can be useful to improve performance in some situations.
|
||||
* </p>
|
||||
*/
|
||||
public Integer getFetchSizeDefaultMaximum() {
|
||||
return myFetchSizeDefaultMaximum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the maximum number of results to return in a GetTags query (DSTU1 only)
|
||||
*/
|
||||
|
@ -516,6 +554,23 @@ public class DaoConfig {
|
|||
myEnforceReferentialIntegrityOnWrite = theEnforceReferentialIntegrityOnWrite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlike with normal search queries, $everything queries have their _includes loaded by the main search thread and these included results
|
||||
* are added to the normal search results instead of being added on as extras in a page. This means that they will not appear multiple times
|
||||
* as the search results are paged over.
|
||||
* <p>
|
||||
* In order to recursively load _includes, we process the original results in batches of this size. Adjust with caution, increasing this
|
||||
* value may improve performance but may also cause memory issues.
|
||||
* </p>
|
||||
* <p>
|
||||
* The default value is 50
|
||||
* </p>
|
||||
*/
|
||||
public void setEverythingIncludesFetchPageSize(int theEverythingIncludesFetchPageSize) {
|
||||
Validate.inclusiveBetween(1, Integer.MAX_VALUE, theEverythingIncludesFetchPageSize);
|
||||
myEverythingIncludesFetchPageSize = theEverythingIncludesFetchPageSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is set to <code>false</code> (default is <code>true</code>) the stale search deletion
|
||||
* task will be disabled (meaning that search results will be retained in the database indefinitely). USE WITH CAUTION.
|
||||
|
@ -549,6 +604,20 @@ public class DaoConfig {
|
|||
myExpireSearchResultsAfterMillis = theExpireSearchResultsAfterMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default maximum number of results to load in a query.
|
||||
* <p>
|
||||
* For example, if the database has a million Patient resources in it, and
|
||||
* the client requests <code>GET /Patient</code>, if this value is set
|
||||
* to a non-null value (default is <code>null</code>) only this number
|
||||
* of results will be fetched. Setting this value appropriately
|
||||
* can be useful to improve performance in some situations.
|
||||
* </p>
|
||||
*/
|
||||
public void setFetchSizeDefaultMaximum(Integer theFetchSizeDefaultMaximum) {
|
||||
myFetchSizeDefaultMaximum = theFetchSizeDefaultMaximum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do not call this method, it exists only for legacy reasons. It
|
||||
* will be removed in a future version. Configure the page size on your
|
||||
|
|
|
@ -58,12 +58,15 @@ import javax.persistence.criteria.Subquery;
|
|||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
|
@ -136,14 +139,18 @@ import ca.uhn.fhir.util.UrlUtil;
|
|||
* searchs for resources
|
||||
*/
|
||||
public class SearchBuilder implements ISearchBuilder {
|
||||
private static final List<Long> EMPTY_LONG_LIST = Collections.unmodifiableList(new ArrayList<Long>());
|
||||
|
||||
private static Long NO_MORE = Long.valueOf(-1);
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchBuilder.class);
|
||||
private List<Long> myAlsoIncludePids;
|
||||
private CriteriaBuilder myBuilder;
|
||||
private BaseHapiFhirDao<?> myCallingDao;
|
||||
private FhirContext myContext;
|
||||
private EntityManager myEntityManager;
|
||||
private IForcedIdDao myForcedIdDao;
|
||||
private IFulltextSearchSvc myFulltextSearchSvc;
|
||||
private Map<JoinKey, Join<?, ?>> myIndexJoins = Maps.newHashMap();
|
||||
private SearchParameterMap myParams;
|
||||
private ArrayList<Predicate> myPredicates;
|
||||
private IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
|
||||
|
@ -152,8 +159,8 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
private Root<ResourceTable> myResourceTableRoot;
|
||||
private Class<? extends IBaseResource> myResourceType;
|
||||
private ISearchParamRegistry mySearchParamRegistry;
|
||||
private IHapiTerminologySvc myTerminologySvc;
|
||||
private String mySearchUuid;
|
||||
private IHapiTerminologySvc myTerminologySvc;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -192,7 +199,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
private void addPredicateDate(String theResourceName, String theParamName, List<? extends IQueryParameterType> theList) {
|
||||
|
||||
Join<ResourceTable, ResourceIndexedSearchParamDate> join = myResourceTableRoot.join("myParamsDate", JoinType.LEFT);
|
||||
Join<ResourceTable, ResourceIndexedSearchParamDate> join = createOrReuseJoin(JoinEnum.DATE, theParamName);
|
||||
|
||||
if (theList.get(0).getMissing() != null) {
|
||||
Boolean missing = theList.get(0).getMissing();
|
||||
|
@ -268,28 +275,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
// private void addPredicateId(Set<Long> thePids) {
|
||||
// if (thePids == null || thePids.isEmpty()) {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
|
||||
// CriteriaQuery<Long> cq = builder.createQuery(Long.class);
|
||||
// Root<ResourceTable> from = cq.from(ResourceTable.class);
|
||||
// cq.select(from.get("myId").as(Long.class));
|
||||
//
|
||||
// List<Predicate> predicates = new ArrayList<Predicate>();
|
||||
// predicates.add(builder.equal(from.get("myResourceType"), myResourceName));
|
||||
// predicates.add(from.get("myId").in(thePids));
|
||||
// createPredicateResourceId(builder, cq, predicates, from.get("myId").as(Long.class));
|
||||
// createPredicateLastUpdatedForResourceTable(builder, from, predicates);
|
||||
//
|
||||
// cq.where(toArray(predicates));
|
||||
//
|
||||
// TypedQuery<Long> q = myEntityManager.createQuery(cq);
|
||||
// doSetPids(q.getResultList());
|
||||
// }
|
||||
|
||||
private void addPredicateLanguage(List<List<? extends IQueryParameterType>> theList) {
|
||||
for (List<? extends IQueryParameterType> nextList : theList) {
|
||||
|
||||
|
@ -319,7 +304,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
private void addPredicateNumber(String theResourceName, String theParamName, List<? extends IQueryParameterType> theList) {
|
||||
|
||||
Join<ResourceTable, ResourceIndexedSearchParamNumber> join = myResourceTableRoot.join("myParamsNumber", JoinType.LEFT);
|
||||
Join<ResourceTable, ResourceIndexedSearchParamNumber> join = createOrReuseJoin(JoinEnum.NUMBER, theParamName);
|
||||
|
||||
if (theList.get(0).getMissing() != null) {
|
||||
addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join);
|
||||
|
@ -371,7 +356,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
private void addPredicateQuantity(String theResourceName, String theParamName, List<? extends IQueryParameterType> theList) {
|
||||
Join<ResourceTable, ResourceIndexedSearchParamQuantity> join = myResourceTableRoot.join("myParamsQuantity", JoinType.LEFT);
|
||||
Join<ResourceTable, ResourceIndexedSearchParamQuantity> join = createOrReuseJoin(JoinEnum.QUANTITY, theParamName);
|
||||
|
||||
if (theList.get(0).getMissing() != null) {
|
||||
addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join);
|
||||
|
@ -399,7 +384,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
return;
|
||||
}
|
||||
|
||||
Join<ResourceTable, ResourceLink> join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT);
|
||||
Join<ResourceTable, ResourceLink> join = createOrReuseJoin(JoinEnum.REFERENCE, theParamName);
|
||||
|
||||
List<Predicate> codePredicates = new ArrayList<Predicate>();
|
||||
|
||||
|
@ -459,7 +444,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
RuntimeResourceDefinition resourceDef = myContext.getResourceDefinition(theResourceName);
|
||||
RuntimeSearchParam searchParamByName = myCallingDao.getSearchParamByName(resourceDef, theParamName);
|
||||
if (searchParamByName == null) {
|
||||
throw new InternalErrorException("Could not find parameter " + theParamName );
|
||||
throw new InternalErrorException("Could not find parameter " + theParamName);
|
||||
}
|
||||
String paramPath = searchParamByName.getPath();
|
||||
if (paramPath.endsWith(".as(Reference)")) {
|
||||
|
@ -489,7 +474,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
if (resourceTypes.isEmpty()) {
|
||||
for (BaseRuntimeElementDefinition<?> next : myContext.getElementDefinitions()) {
|
||||
if (next instanceof RuntimeResourceDefinition) {
|
||||
RuntimeResourceDefinition nextResDef = (RuntimeResourceDefinition)next;
|
||||
RuntimeResourceDefinition nextResDef = (RuntimeResourceDefinition) next;
|
||||
resourceTypes.add(nextResDef.getImplementingClass());
|
||||
}
|
||||
}
|
||||
|
@ -575,8 +560,10 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
*/
|
||||
Root<ResourceTable> stackRoot = myResourceTableRoot;
|
||||
ArrayList<Predicate> stackPredicates = myPredicates;
|
||||
Map<JoinKey, Join<?, ?>> stackIndexJoins = myIndexJoins;
|
||||
myResourceTableRoot = subQfrom;
|
||||
myPredicates = new ArrayList<Predicate>();
|
||||
myPredicates = Lists.newArrayList();
|
||||
myIndexJoins = Maps.newHashMap();
|
||||
|
||||
// Create the subquery predicates
|
||||
myPredicates.add(myBuilder.equal(myResourceTableRoot.get("myResourceType"), subResourceName));
|
||||
|
@ -590,6 +577,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
*/
|
||||
myResourceTableRoot = stackRoot;
|
||||
myPredicates = stackPredicates;
|
||||
myIndexJoins = stackIndexJoins;
|
||||
|
||||
Predicate pathPredicate = createResourceLinkPathPredicate(theResourceName, theParamName, join);
|
||||
Predicate pidPredicate = join.get("myTargetResourcePid").in(subQ);
|
||||
|
@ -654,7 +642,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
private void addPredicateString(String theResourceName, String theParamName, List<? extends IQueryParameterType> theList) {
|
||||
|
||||
Join<ResourceTable, ResourceIndexedSearchParamString> join = myResourceTableRoot.join("myParamsString", JoinType.LEFT);
|
||||
Join<ResourceTable, ResourceIndexedSearchParamString> join = createOrReuseJoin(JoinEnum.STRING, theParamName);
|
||||
|
||||
if (theList.get(0).getMissing() != null) {
|
||||
addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join);
|
||||
|
@ -802,7 +790,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
private void addPredicateToken(String theResourceName, String theParamName, List<? extends IQueryParameterType> theList) {
|
||||
|
||||
Join<ResourceTable, ResourceIndexedSearchParamToken> join = myResourceTableRoot.join("myParamsToken", JoinType.LEFT);
|
||||
Join<ResourceTable, ResourceIndexedSearchParamToken> join = createOrReuseJoin(JoinEnum.TOKEN, theParamName);
|
||||
|
||||
if (theList.get(0).getMissing() != null) {
|
||||
addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join);
|
||||
|
@ -834,7 +822,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
private void addPredicateUri(String theResourceName, String theParamName, List<? extends IQueryParameterType> theList) {
|
||||
|
||||
Join<ResourceTable, ResourceIndexedSearchParamUri> join = myResourceTableRoot.join("myParamsUri", JoinType.LEFT);
|
||||
Join<ResourceTable, ResourceIndexedSearchParamUri> join = createOrReuseJoin(JoinEnum.URI, theParamName);
|
||||
|
||||
if (theList.get(0).getMissing() != null) {
|
||||
addPredicateParamMissing(theResourceName, theParamName, theList.get(0).getMissing(), join);
|
||||
|
@ -959,6 +947,42 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> Join<ResourceTable, T> createOrReuseJoin(JoinEnum theType, String theSearchParameterName) {
|
||||
Join<ResourceTable, ResourceIndexedSearchParamDate> join = null;
|
||||
|
||||
switch (theType) {
|
||||
case DATE:
|
||||
join = myResourceTableRoot.join("myParamsDate", JoinType.LEFT);
|
||||
break;
|
||||
case NUMBER:
|
||||
join = myResourceTableRoot.join("myParamsNumber", JoinType.LEFT);
|
||||
break;
|
||||
case QUANTITY:
|
||||
join = myResourceTableRoot.join("myParamsQuantity", JoinType.LEFT);
|
||||
break;
|
||||
case REFERENCE:
|
||||
join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT);
|
||||
break;
|
||||
case STRING:
|
||||
join = myResourceTableRoot.join("myParamsString", JoinType.LEFT);
|
||||
break;
|
||||
case URI:
|
||||
join = myResourceTableRoot.join("myParamsUri", JoinType.LEFT);
|
||||
break;
|
||||
case TOKEN:
|
||||
join = myResourceTableRoot.join("myParamsToken", JoinType.LEFT);
|
||||
break;
|
||||
}
|
||||
|
||||
JoinKey key = new JoinKey(theSearchParameterName, theType);
|
||||
if (!myIndexJoins.containsKey(key)) {
|
||||
myIndexJoins.put(key, join);
|
||||
}
|
||||
|
||||
return (Join<ResourceTable, T>) join;
|
||||
}
|
||||
|
||||
private Predicate createPredicateDate(IQueryParameterType theParam, String theResourceName, String theParamName, CriteriaBuilder theBuilder, From<?, ResourceIndexedSearchParamDate> theFrom) {
|
||||
Predicate p;
|
||||
if (theParam instanceof DateParam) {
|
||||
|
@ -1095,7 +1119,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
if (!isBlank(unitsValue)) {
|
||||
code = theBuilder.equal(theFrom.get("myUnits"), unitsValue);
|
||||
}
|
||||
|
||||
|
||||
cmpValue = ObjectUtils.defaultIfNull(cmpValue, ParamPrefixEnum.EQUAL);
|
||||
final Expression<BigDecimal> path = theFrom.get("myValue");
|
||||
String invalidMessageName = "invalidQuantityPrefix";
|
||||
|
@ -1238,10 +1262,10 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
// Use "in" in case of large numbers of codes due to param modifiers
|
||||
final Path<String> systemExpression = theFrom.get("mySystem");
|
||||
final Path<String> valueExpression = theFrom.get("myValue");
|
||||
for (Map.Entry<String, List<VersionIndependentConcept>> entry: map.entrySet()) {
|
||||
for (Map.Entry<String, List<VersionIndependentConcept>> entry : map.entrySet()) {
|
||||
Predicate systemPredicate = theBuilder.equal(systemExpression, entry.getKey());
|
||||
In<String> codePredicate = theBuilder.in(valueExpression);
|
||||
for (VersionIndependentConcept nextCode: entry.getValue()) {
|
||||
for (VersionIndependentConcept nextCode : entry.getValue()) {
|
||||
codePredicate.value(nextCode.getCode());
|
||||
}
|
||||
orPredicates.add(theBuilder.and(systemPredicate, codePredicate));
|
||||
|
@ -1291,9 +1315,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
return new QueryIterator();
|
||||
}
|
||||
|
||||
private List<Long> myAlsoIncludePids;
|
||||
|
||||
private TypedQuery<Long> createQuery(SortSpec sort) {
|
||||
private TypedQuery<Long> createQuery(SortSpec sort, Integer theMaximumResults) {
|
||||
CriteriaQuery<Long> outerQuery;
|
||||
/*
|
||||
* Sort
|
||||
|
@ -1337,16 +1359,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
}
|
||||
|
||||
myResourceTableQuery.distinct(true);
|
||||
myPredicates = new ArrayList<Predicate>();
|
||||
if (myParams.getEverythingMode() == null) {
|
||||
myPredicates.add(myBuilder.equal(myResourceTableRoot.get("myResourceType"), myResourceName));
|
||||
}
|
||||
myPredicates.add(myBuilder.isNull(myResourceTableRoot.get("myDeleted")));
|
||||
|
||||
DateRangeParam lu = myParams.getLastUpdated();
|
||||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(lu, myBuilder, myResourceTableRoot);
|
||||
myPredicates.addAll(lastUpdatedPredicates);
|
||||
|
||||
if (myParams.getEverythingMode() != null) {
|
||||
Join<ResourceTable, ResourceLink> join = myResourceTableRoot.join("myResourceLinks", JoinType.LEFT);
|
||||
|
@ -1390,12 +1403,36 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
myPredicates.add(myResourceTableRoot.get("myId").as(Long.class).in(pids));
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a predicate to make sure we only include non-deleted resources, and only include
|
||||
* resources of the right type.
|
||||
*
|
||||
* If we have any joins to index tables, we get this behaviour already guaranteed so we don't
|
||||
* need an explicit predicate for it.
|
||||
*/
|
||||
if (myIndexJoins.isEmpty()) {
|
||||
if (myParams.getEverythingMode() == null) {
|
||||
myPredicates.add(myBuilder.equal(myResourceTableRoot.get("myResourceType"), myResourceName));
|
||||
}
|
||||
myPredicates.add(myBuilder.isNull(myResourceTableRoot.get("myDeleted")));
|
||||
}
|
||||
|
||||
// Last updated
|
||||
DateRangeParam lu = myParams.getLastUpdated();
|
||||
List<Predicate> lastUpdatedPredicates = createLastUpdatedPredicates(lu, myBuilder, myResourceTableRoot);
|
||||
myPredicates.addAll(lastUpdatedPredicates);
|
||||
|
||||
myResourceTableQuery.where(myBuilder.and(SearchBuilder.toArray(myPredicates)));
|
||||
|
||||
/*
|
||||
* Now perform the search
|
||||
*/
|
||||
final TypedQuery<Long> query = myEntityManager.createQuery(outerQuery);
|
||||
|
||||
if (theMaximumResults != null) {
|
||||
query.setMaxResults(theMaximumResults);
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
|
@ -1443,47 +1480,65 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
String joinAttrName;
|
||||
String[] sortAttrName;
|
||||
JoinEnum joinType;
|
||||
|
||||
switch (param.getParamType()) {
|
||||
case STRING:
|
||||
joinAttrName = "myParamsString";
|
||||
sortAttrName = new String[] { "myValueExact" };
|
||||
joinType = JoinEnum.STRING;
|
||||
break;
|
||||
case DATE:
|
||||
joinAttrName = "myParamsDate";
|
||||
sortAttrName = new String[] { "myValueLow" };
|
||||
joinType = JoinEnum.DATE;
|
||||
break;
|
||||
case REFERENCE:
|
||||
joinAttrName = "myResourceLinks";
|
||||
sortAttrName = new String[] { "myTargetResourcePid" };
|
||||
joinType = JoinEnum.REFERENCE;
|
||||
break;
|
||||
case TOKEN:
|
||||
joinAttrName = "myParamsToken";
|
||||
sortAttrName = new String[] { "mySystem", "myValue" };
|
||||
joinType = JoinEnum.TOKEN;
|
||||
break;
|
||||
case NUMBER:
|
||||
joinAttrName = "myParamsNumber";
|
||||
sortAttrName = new String[] { "myValue" };
|
||||
joinType = JoinEnum.NUMBER;
|
||||
break;
|
||||
case URI:
|
||||
joinAttrName = "myParamsUri";
|
||||
sortAttrName = new String[] { "myUri" };
|
||||
joinType = JoinEnum.URI;
|
||||
break;
|
||||
case QUANTITY:
|
||||
joinAttrName = "myParamsQuantity";
|
||||
sortAttrName = new String[] { "myValue" };
|
||||
joinType = JoinEnum.QUANTITY;
|
||||
break;
|
||||
default:
|
||||
throw new InvalidRequestException("This server does not support _sort specifications of type " + param.getParamType() + " - Can't serve _sort=" + theSort.getParamName());
|
||||
}
|
||||
|
||||
From<?, ?> join = theFrom.join(joinAttrName, JoinType.LEFT);
|
||||
/*
|
||||
* If we've already got a join for the specific parameter we're
|
||||
* sorting on, we'll also sort with it. Otherwise we need a new join.
|
||||
*/
|
||||
JoinKey key = new JoinKey(theSort.getParamName(), joinType);
|
||||
Join<?, ?> join = myIndexJoins.get(key);
|
||||
if (join == null) {
|
||||
join = theFrom.join(joinAttrName, JoinType.LEFT);
|
||||
|
||||
if (param.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
|
||||
thePredicates.add(join.get("mySourcePath").as(String.class).in(param.getPathsSplit()));
|
||||
if (param.getParamType() == RestSearchParameterTypeEnum.REFERENCE) {
|
||||
thePredicates.add(join.get("mySourcePath").as(String.class).in(param.getPathsSplit()));
|
||||
} else {
|
||||
Predicate joinParam1 = theBuilder.equal(join.get("myParamName"), theSort.getParamName());
|
||||
thePredicates.add(joinParam1);
|
||||
}
|
||||
} else {
|
||||
Predicate joinParam1 = theBuilder.equal(join.get("myParamName"), theSort.getParamName());
|
||||
thePredicates.add(joinParam1);
|
||||
ourLog.info("Reusing join for {}", theSort.getParamName());
|
||||
}
|
||||
|
||||
for (String next : sortAttrName) {
|
||||
|
@ -1529,41 +1584,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation,
|
||||
EntityManager entityManager, FhirContext context, IDao theDao) {
|
||||
if (theIncludePids.isEmpty()) {
|
||||
ourLog.info("The include pids are empty");
|
||||
//return;
|
||||
}
|
||||
|
||||
// Dupes will cause a crash later anyhow, but this is expensive so only do it
|
||||
// when running asserts
|
||||
assert new HashSet<Long>(theIncludePids).size() == theIncludePids.size() : "PID list contains duplicates: " + theIncludePids;
|
||||
|
||||
Map<Long, Integer> position = new HashMap<Long, Integer>();
|
||||
for (Long next : theIncludePids) {
|
||||
position.put(next, theResourceListToPopulate.size());
|
||||
theResourceListToPopulate.add(null);
|
||||
}
|
||||
|
||||
/*
|
||||
* As always, Oracle can't handle things that other databases don't mind.. In this
|
||||
* case it doesn't like more than ~1000 IDs in a single load, so we break this up
|
||||
* if it's lots of IDs. I suppose maybe we should be doing this as a join anyhow
|
||||
* but this should work too. Sigh.
|
||||
*/
|
||||
int maxLoad = 800;
|
||||
List<Long> pids = new ArrayList<Long>(theIncludePids);
|
||||
for (int i = 0; i < pids.size(); i += maxLoad) {
|
||||
int to = i + maxLoad;
|
||||
to = Math.min(to, pids.size());
|
||||
List<Long> pidsSubList = pids.subList(i, to);
|
||||
doLoadPids(theResourceListToPopulate, theRevIncludedPids, theForHistoryOperation, entityManager, context, theDao, position, pidsSubList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void doLoadPids(List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation, EntityManager entityManager, FhirContext context, IDao theDao,
|
||||
Map<Long, Integer> position, Collection<Long> pids) {
|
||||
CriteriaBuilder builder = entityManager.getCriteriaBuilder();
|
||||
|
@ -1599,6 +1619,41 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadResourcesByPid(Collection<Long> theIncludePids, List<IBaseResource> theResourceListToPopulate, Set<Long> theRevIncludedPids, boolean theForHistoryOperation,
|
||||
EntityManager entityManager, FhirContext context, IDao theDao) {
|
||||
if (theIncludePids.isEmpty()) {
|
||||
ourLog.info("The include pids are empty");
|
||||
// return;
|
||||
}
|
||||
|
||||
// Dupes will cause a crash later anyhow, but this is expensive so only do it
|
||||
// when running asserts
|
||||
assert new HashSet<Long>(theIncludePids).size() == theIncludePids.size() : "PID list contains duplicates: " + theIncludePids;
|
||||
|
||||
Map<Long, Integer> position = new HashMap<Long, Integer>();
|
||||
for (Long next : theIncludePids) {
|
||||
position.put(next, theResourceListToPopulate.size());
|
||||
theResourceListToPopulate.add(null);
|
||||
}
|
||||
|
||||
/*
|
||||
* As always, Oracle can't handle things that other databases don't mind.. In this
|
||||
* case it doesn't like more than ~1000 IDs in a single load, so we break this up
|
||||
* if it's lots of IDs. I suppose maybe we should be doing this as a join anyhow
|
||||
* but this should work too. Sigh.
|
||||
*/
|
||||
int maxLoad = 800;
|
||||
List<Long> pids = new ArrayList<Long>(theIncludePids);
|
||||
for (int i = 0; i < pids.size(); i += maxLoad) {
|
||||
int to = i + maxLoad;
|
||||
to = Math.min(to, pids.size());
|
||||
List<Long> pidsSubList = pids.subList(i, to);
|
||||
doLoadPids(theResourceListToPopulate, theRevIncludedPids, theForHistoryOperation, entityManager, context, theDao, position, pidsSubList);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* THIS SHOULD RETURN HASHSET and not jsut Set because we add to it later (so it can't be Collections.emptySet())
|
||||
*
|
||||
|
@ -1645,12 +1700,6 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
List<ResourceLink> results = q.getResultList();
|
||||
for (ResourceLink resourceLink : results) {
|
||||
if (theReverseMode) {
|
||||
// if (theEverythingModeEnum.isEncounter()) {
|
||||
// if (resourceLink.getSourcePath().equals("Encounter.subject") ||
|
||||
// resourceLink.getSourcePath().equals("Encounter.patient")) {
|
||||
// nextRoundOmit.add(resourceLink.getSourceResourcePid());
|
||||
// }
|
||||
// }
|
||||
pidsToInclude.add(resourceLink.getSourceResourcePid());
|
||||
} else {
|
||||
pidsToInclude.add(resourceLink.getTargetResourcePid());
|
||||
|
@ -1761,10 +1810,10 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
private void searchForIdsWithAndOr(String theResourceName, String theParamName, List<List<? extends IQueryParameterType>> theAndOrParams) {
|
||||
|
||||
|
||||
for (int andListIdx = 0; andListIdx < theAndOrParams.size(); andListIdx++) {
|
||||
List<? extends IQueryParameterType> nextOrList = theAndOrParams.get(andListIdx);
|
||||
|
||||
|
||||
for (int orListIdx = 0; orListIdx < nextOrList.size(); orListIdx++) {
|
||||
IQueryParameterType nextOr = nextOrList.get(orListIdx);
|
||||
boolean hasNoValue = false;
|
||||
|
@ -1776,14 +1825,14 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
hasNoValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (hasNoValue) {
|
||||
ourLog.debug("Ignoring empty parameter: {}", theParamName);
|
||||
nextOrList.remove(orListIdx);
|
||||
orListIdx--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (nextOrList.isEmpty()) {
|
||||
theAndOrParams.remove(andListIdx);
|
||||
andListIdx--;
|
||||
|
@ -1793,7 +1842,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
if (theAndOrParams.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (theParamName.equals(BaseResource.SP_RES_ID)) {
|
||||
|
||||
addPredicateResourceId(theAndOrParams);
|
||||
|
@ -1870,7 +1919,7 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setType(Class<? extends IBaseResource> theResourceType, String theResourceName) {
|
||||
public void setType(Class<? extends IBaseResource> theResourceType, String theResourceName) {
|
||||
myResourceType = theResourceType;
|
||||
myResourceName = theResourceName;
|
||||
}
|
||||
|
@ -1988,18 +2037,116 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
static Predicate[] toArray(List<Predicate> thePredicates) {
|
||||
return thePredicates.toArray(new Predicate[thePredicates.size()]);
|
||||
}
|
||||
public class IncludesIterator implements Iterator<Long>{
|
||||
|
||||
private Iterator<Long> myCurrentIterator;
|
||||
private int myCurrentOffset;
|
||||
private ArrayList<Long> myCurrentPids;
|
||||
private Long myNext;
|
||||
private int myPageSize = myCallingDao.getConfig().getEverythingIncludesFetchPageSize();
|
||||
|
||||
public IncludesIterator(Set<Long> thePidSet) {
|
||||
myCurrentPids = new ArrayList<Long>(thePidSet);
|
||||
myCurrentIterator = EMPTY_LONG_LIST.iterator();
|
||||
myCurrentOffset = 0;
|
||||
}
|
||||
|
||||
private void fetchNext() {
|
||||
while (myNext == null) {
|
||||
|
||||
if (myCurrentIterator.hasNext()) {
|
||||
myNext = myCurrentIterator.next();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!myCurrentIterator.hasNext()) {
|
||||
int start = myCurrentOffset;
|
||||
int end = myCurrentOffset + myPageSize;
|
||||
if (end > myCurrentPids.size()) {
|
||||
end = myCurrentPids.size();
|
||||
}
|
||||
if (end - start <= 0) {
|
||||
myNext = NO_MORE;
|
||||
break;
|
||||
}
|
||||
myCurrentOffset = end;
|
||||
Collection<Long> pidsToScan = myCurrentPids.subList(start, end);
|
||||
Set<Include> includes = Collections.singleton(new Include("*", true));
|
||||
Set<Long> newPids = loadReverseIncludes(myCallingDao, myContext, myEntityManager, pidsToScan, includes, false, myParams.getLastUpdated());
|
||||
myCurrentIterator = newPids.iterator();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
fetchNext();
|
||||
return myNext != NO_MORE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long next() {
|
||||
fetchNext();
|
||||
Long retVal = myNext;
|
||||
myNext = null;
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private enum JoinEnum {
|
||||
DATE, NUMBER, QUANTITY, REFERENCE, STRING, TOKEN, URI
|
||||
|
||||
}
|
||||
|
||||
private static class JoinKey {
|
||||
private final JoinEnum myJoinType;
|
||||
private final String myParamName;
|
||||
|
||||
public JoinKey(String theParamName, JoinEnum theJoinType) {
|
||||
super();
|
||||
myParamName = theParamName;
|
||||
myJoinType = theJoinType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object theObj) {
|
||||
JoinKey obj = (JoinKey) theObj;
|
||||
return new EqualsBuilder()
|
||||
.append(myParamName, obj.myParamName)
|
||||
.append(myJoinType, obj.myJoinType)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return new HashCodeBuilder()
|
||||
.append(myParamName)
|
||||
.append(myJoinType)
|
||||
.toHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
private final class QueryIterator implements Iterator<Long> {
|
||||
|
||||
private boolean myFirst = true;
|
||||
private IncludesIterator myIncludesIterator;
|
||||
private Long myNext;
|
||||
private final Set<Long> myPidSet = new HashSet<Long>();
|
||||
private Iterator<Long> myPreResultsIterator;
|
||||
private Iterator<Long> myResultsIterator;
|
||||
private SortSpec mySort;
|
||||
private Iterator<Long> myPreResultsIterator;
|
||||
private boolean myFirst = true;
|
||||
private boolean myStillNeedToFetchIncludes;
|
||||
private StopWatch myStopwatch = null;
|
||||
|
||||
private QueryIterator() {
|
||||
mySort = myParams.getSort();
|
||||
|
||||
// Includes are processed inline for $everything query
|
||||
if (myParams.getEverythingMode() != null) {
|
||||
myStillNeedToFetchIncludes = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void fetchNext() {
|
||||
|
@ -2010,7 +2157,9 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
|
||||
// If we don't have a query yet, create one
|
||||
if (myResultsIterator == null) {
|
||||
final TypedQuery<Long> query = createQuery(mySort);
|
||||
Integer maximumResults = myCallingDao.getConfig().getFetchSizeDefaultMaximum();
|
||||
|
||||
final TypedQuery<Long> query = createQuery(mySort, maximumResults);
|
||||
myResultsIterator = query.getResultList().iterator();
|
||||
|
||||
// If the query resulted in extra results being requested
|
||||
|
@ -2042,7 +2191,24 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
}
|
||||
|
||||
if (myNext == null) {
|
||||
myNext = NO_MORE;
|
||||
if (myStillNeedToFetchIncludes) {
|
||||
myIncludesIterator = new IncludesIterator(myPidSet);
|
||||
myStillNeedToFetchIncludes = false;
|
||||
}
|
||||
if (myIncludesIterator != null) {
|
||||
while (myIncludesIterator.hasNext()) {
|
||||
Long next = myIncludesIterator.next();
|
||||
if (next != null && myPidSet.add(next)) {
|
||||
myNext = next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (myNext == null) {
|
||||
myNext = NO_MORE;
|
||||
}
|
||||
} else {
|
||||
myNext = NO_MORE;
|
||||
}
|
||||
}
|
||||
|
||||
} // if we need to fetch the next result
|
||||
|
@ -2051,9 +2217,11 @@ public class SearchBuilder implements ISearchBuilder {
|
|||
ourLog.info("Initial query result returned in {}ms for query {}", myStopwatch.getMillis(), mySearchUuid);
|
||||
myFirst = false;
|
||||
}
|
||||
|
||||
if (myNext == NO_MORE) {
|
||||
ourLog.info("Query found {} matches in {}ms for query {}", myPidSet.size(), myStopwatch.getMillis(), mySearchUuid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.dao.data;
|
||||
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR JPA Server
|
||||
|
@ -19,10 +21,7 @@ package ca.uhn.fhir.jpa.dao.data;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.jpa.repository.*;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
|
@ -33,4 +32,7 @@ public interface IResourceTableDao extends JpaRepository<ResourceTable, Long> {
|
|||
@Query("UPDATE ResourceTable r SET r.myIndexStatus = null WHERE r.myResourceType = :restype")
|
||||
int markResourcesOfTypeAsRequiringReindexing(@Param("restype") String theResourceType);
|
||||
|
||||
@Query("SELECT t.myId FROM ResourceTable t WHERE t.myIndexStatus IS NULL")
|
||||
Slice<Long> findUnindexed(Pageable thePageRequest);
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import java.util.Map.Entry;
|
|||
import java.util.Set;
|
||||
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
|
@ -79,6 +80,7 @@ import ca.uhn.fhir.jpa.util.DeleteConflict;
|
|||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
|
@ -282,7 +284,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
super.clearRequestAsProcessingSubRequest(theRequestDetails);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Bundle doTransaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) {
|
||||
BundleType transactionType = theRequest.getTypeElement().getValue();
|
||||
|
@ -408,7 +410,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
String matchUrl = nextReqEntry.getRequest().getIfNoneExist();
|
||||
outcome = resourceDao.create(res, matchUrl, false, theRequestDetails);
|
||||
if (nextResourceId != null) {
|
||||
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res);
|
||||
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails);
|
||||
}
|
||||
entriesToProcess.put(nextRespEntry, outcome.getEntity());
|
||||
if (outcome.getCreated() == false) {
|
||||
|
@ -445,12 +447,12 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
if (allDeleted.isEmpty()) {
|
||||
status = Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||
}
|
||||
|
||||
|
||||
nextRespEntry.getResponse().setOutcome((Resource) deleteOutcome.getOperationOutcome());
|
||||
}
|
||||
|
||||
nextRespEntry.getResponse().setStatus(toStatusString(status));
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
case PUT: {
|
||||
|
@ -474,7 +476,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
}
|
||||
}
|
||||
|
||||
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res);
|
||||
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails);
|
||||
entriesToProcess.put(nextRespEntry, outcome.getEntity());
|
||||
break;
|
||||
}
|
||||
|
@ -646,10 +648,8 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
return response;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static void handleTransactionCreateOrUpdateOutcome(Map<IdType, IdType> idSubstitutions, Map<IdType, DaoMethodOutcome> idToPersistedOutcome, IdType nextResourceId, DaoMethodOutcome outcome,
|
||||
BundleEntryComponent newEntry, String theResourceType, IBaseResource theRes) {
|
||||
BundleEntryComponent newEntry, String theResourceType, IBaseResource theRes, ServletRequestDetails theRequestDetails) {
|
||||
IdType newId = (IdType) outcome.getId().toUnqualifiedVersionless();
|
||||
IdType resourceId = isPlaceholder(nextResourceId) ? nextResourceId : nextResourceId.toUnqualifiedVersionless();
|
||||
if (newId.equals(resourceId) == false) {
|
||||
|
@ -668,6 +668,19 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
|
|||
newEntry.getResponse().setStatus(toStatusString(Constants.STATUS_HTTP_200_OK));
|
||||
}
|
||||
newEntry.getResponse().setLastModified(((Resource) theRes).getMeta().getLastUpdated());
|
||||
|
||||
if (theRequestDetails != null) {
|
||||
if (outcome.getResource() != null) {
|
||||
String prefer = theRequestDetails.getHeader(Constants.HEADER_PREFER);
|
||||
PreferReturnEnum preferReturn = RestfulServerUtils.parsePreferHeader(prefer);
|
||||
if (preferReturn != null) {
|
||||
if (preferReturn == PreferReturnEnum.REPRESENTATION) {
|
||||
newEntry.setResource((Resource) outcome.getResource());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static boolean isPlaceholder(IdType theId) {
|
||||
|
|
|
@ -241,8 +241,8 @@ public class PersistedJpaBundleProvider implements IBundleProvider {
|
|||
Set<Long> includedPids = new HashSet<Long>();
|
||||
if (mySearchEntity.getSearchType() == SearchTypeEnum.SEARCH) {
|
||||
includedPids.addAll(sb.loadReverseIncludes(myDao, myContext, myEntityManager, pidsSubList, mySearchEntity.toRevIncludesList(), true, mySearchEntity.getLastUpdated()));
|
||||
includedPids.addAll(sb.loadReverseIncludes(myDao, myContext, myEntityManager, pidsSubList, mySearchEntity.toIncludesList(), false, mySearchEntity.getLastUpdated()));
|
||||
}
|
||||
includedPids.addAll(sb.loadReverseIncludes(myDao, myContext, myEntityManager, pidsSubList, mySearchEntity.toIncludesList(), false, mySearchEntity.getLastUpdated()));
|
||||
|
||||
// Execute the query and make sure we return distinct results
|
||||
List<IBaseResource> resources = new ArrayList<IBaseResource>();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.jpa.config;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
@ -17,6 +18,8 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
|||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||
import ca.uhn.fhir.validation.ResultSeverityEnum;
|
||||
import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel;
|
||||
import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder;
|
||||
|
||||
@Configuration
|
||||
@EnableTransactionManagement()
|
||||
|
@ -34,7 +37,15 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
|
|||
retVal.setUrl("jdbc:derby:memory:myUnitTestDB;create=true");
|
||||
retVal.setUsername("");
|
||||
retVal.setPassword("");
|
||||
return retVal;
|
||||
|
||||
DataSource dataSource = ProxyDataSourceBuilder
|
||||
.create(retVal)
|
||||
.logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL")
|
||||
.logSlowQueryBySlf4j(10, TimeUnit.SECONDS)
|
||||
.countQuery()
|
||||
.build();
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@Bean()
|
||||
|
|
|
@ -81,8 +81,7 @@ import org.springframework.transaction.TransactionStatus;
|
|||
import org.springframework.transaction.support.TransactionCallback;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.dao.*;
|
||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap.EverythingModeEnum;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
|
||||
|
@ -130,6 +129,7 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
@Before
|
||||
public void beforeDisableResultReuse() {
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
||||
myDaoConfig.setFetchSizeDefaultMaximum(new DaoConfig().getFetchSizeDefaultMaximum());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1181,6 +1181,23 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
assertThat(patients, (hasItems(id1a, id1b)));
|
||||
assertThat(patients, not(hasItems(id2)));
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.GREATERTHAN_OR_EQUALS, beforeR2)));
|
||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
|
||||
assertThat(patients, not(hasItems(id1a, id1b)));
|
||||
assertThat(patients, (hasItems(id2)));
|
||||
}
|
||||
{
|
||||
SearchParameterMap params = new SearchParameterMap();
|
||||
params.setLastUpdated(new DateRangeParam(new DateParam(ParamPrefixEnum.LESSTHAN_OR_EQUALS, beforeR2)));
|
||||
List<IIdType> patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
|
||||
assertThat(patients, (hasItems(id1a, id1b)));
|
||||
assertThat(patients, not(hasItems(id2)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
@ -1827,6 +1844,48 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchStringParamDoesntMatchWrongType() throws Exception {
|
||||
IIdType pid1;
|
||||
IIdType pid2;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.addName().setFamily("HELLO");
|
||||
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
{
|
||||
Practitioner patient = new Practitioner();
|
||||
patient.addName().setFamily("HELLO");
|
||||
pid2 = myPractitionerDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
SearchParameterMap params;
|
||||
List<IIdType> patients;
|
||||
|
||||
params = new SearchParameterMap();
|
||||
params.add(Patient.SP_FAMILY, new StringParam("HELLO"));
|
||||
patients = toUnqualifiedVersionlessIds(myPatientDao.search(params));
|
||||
assertThat(patients, containsInAnyOrder(pid1));
|
||||
assertThat(patients, not(containsInAnyOrder(pid2)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithFetchSizeDefaultMaximum() {
|
||||
myDaoConfig.setFetchSizeDefaultMaximum(5);
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily("PT" + i);
|
||||
myPatientDao.create(p);
|
||||
}
|
||||
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider values = myPatientDao.search(map);
|
||||
assertEquals(5, values.size().intValue());
|
||||
assertEquals(5, values.getResources(0, 1000).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchStringParam() throws Exception {
|
||||
IIdType pid1;
|
||||
|
@ -3251,11 +3310,26 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
|
|||
SearchParameterMap map;
|
||||
List<String> ids;
|
||||
|
||||
// No search param
|
||||
map = new SearchParameterMap();
|
||||
map.setSort(new SortSpec("family", SortOrderEnum.ASC).setChain(new SortSpec("given", SortOrderEnum.ASC)));
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(ids, contains("Patient/AA", "Patient/AB", "Patient/BA", "Patient/BB"));
|
||||
|
||||
// Same SP as sort
|
||||
map = new SearchParameterMap();
|
||||
map.add(Patient.SP_ACTIVE, new TokenParam(null, "true"));
|
||||
map.setSort(new SortSpec("family", SortOrderEnum.ASC).setChain(new SortSpec("given", SortOrderEnum.ASC)));
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(ids, contains("Patient/AA", "Patient/AB", "Patient/BA", "Patient/BB"));
|
||||
|
||||
// Different SP from sort
|
||||
map = new SearchParameterMap();
|
||||
map.add(Patient.SP_GENDER, new TokenParam(null, "male"));
|
||||
map.setSort(new SortSpec("family", SortOrderEnum.ASC).setChain(new SortSpec("given", SortOrderEnum.ASC)));
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
assertThat(ids, contains("Patient/AA", "Patient/AB", "Patient/BA", "Patient/BB"));
|
||||
|
||||
map = new SearchParameterMap();
|
||||
map.setSort(new SortSpec("gender").setChain(new SortSpec("family", SortOrderEnum.ASC).setChain(new SortSpec("given", SortOrderEnum.ASC))));
|
||||
ids = toUnqualifiedVersionlessIdValues(myPatientDao.search(map));
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
package ca.uhn.fhir.jpa.provider.dstu3;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsInRelativeOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.empty;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.entity.*;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.*;
|
||||
import org.hl7.fhir.dstu3.model.Encounter.EncounterLocationComponent;
|
||||
import org.hl7.fhir.dstu3.model.Encounter.EncounterStatus;
|
||||
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.dstu3.model.Narrative.NarrativeStatus;
|
||||
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
|
||||
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType;
|
||||
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType;
|
||||
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus;
|
||||
import org.hl7.fhir.instance.model.Encounter.EncounterState;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
import org.junit.*;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionCallback;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.entity.Search;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
||||
import ca.uhn.fhir.rest.param.*;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.*;
|
||||
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
public class PatientEverythingDstu3Test extends BaseResourceProviderDstu3Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatientEverythingDstu3Test.class);
|
||||
private String orgId;
|
||||
private String patId;
|
||||
private String encId1;
|
||||
private String encId2;
|
||||
private ArrayList<String> myObsIds;
|
||||
private String myWrongPatId;
|
||||
private String myWrongEnc1;
|
||||
|
||||
@Before
|
||||
public void beforeDisableResultReuse() {
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void after() throws Exception {
|
||||
super.after();
|
||||
|
||||
myDaoConfig.setReuseCachedSearchResultsForMillis(new DaoConfig().getReuseCachedSearchResultsForMillis());
|
||||
myDaoConfig.setEverythingIncludesFetchPageSize(new DaoConfig().getEverythingIncludesFetchPageSize());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void before() throws Exception {
|
||||
super.before();
|
||||
myFhirCtx.setParserErrorHandler(new StrictErrorHandler());
|
||||
|
||||
myDaoConfig.setAllowMultipleDelete(true);
|
||||
|
||||
Organization org = new Organization();
|
||||
org.setName("an org");
|
||||
orgId = ourClient.create().resource(org).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
ourLog.info("OrgId: {}", orgId);
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.getManagingOrganization().setReference(orgId);
|
||||
patId = ourClient.create().resource(patient).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
Patient patient2 = new Patient();
|
||||
patient2.getManagingOrganization().setReference(orgId);
|
||||
myWrongPatId = ourClient.create().resource(patient2).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
Encounter enc1 = new Encounter();
|
||||
enc1.setStatus(EncounterStatus.CANCELLED);
|
||||
enc1.getSubject().setReference(patId);
|
||||
enc1.getServiceProvider().setReference(orgId);
|
||||
encId1 = ourClient.create().resource(enc1).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
Encounter enc2 = new Encounter();
|
||||
enc2.setStatus(EncounterStatus.ARRIVED);
|
||||
enc2.getSubject().setReference(patId);
|
||||
enc2.getServiceProvider().setReference(orgId);
|
||||
encId2 = ourClient.create().resource(enc2).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
Encounter wrongEnc1 = new Encounter();
|
||||
wrongEnc1.setStatus(EncounterStatus.ARRIVED);
|
||||
wrongEnc1.getSubject().setReference(myWrongPatId);
|
||||
wrongEnc1.getServiceProvider().setReference(orgId);
|
||||
myWrongEnc1 = ourClient.create().resource(wrongEnc1).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
|
||||
myObsIds = new ArrayList<String>();
|
||||
for (int i = 0; i < 20; i++) {
|
||||
Observation obs = new Observation();
|
||||
obs.getSubject().setReference(patId);
|
||||
obs.setStatus(ObservationStatus.FINAL);
|
||||
String obsId = ourClient.create().resource(obs).execute().getId().toUnqualifiedVersionless().getValue();
|
||||
myObsIds.add(obsId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See #674
|
||||
*/
|
||||
@Test
|
||||
public void testEverythingReturnsCorrectResources() throws Exception {
|
||||
|
||||
Bundle bundle = fetchBundle(ourServerBase + "/" + patId + "/$everything?_format=json&_count=100", EncodingEnum.JSON);
|
||||
|
||||
assertNull(bundle.getLink("next"));
|
||||
|
||||
Set<String> actual = new TreeSet<String>();
|
||||
for (BundleEntryComponent nextEntry : bundle.getEntry()) {
|
||||
actual.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
|
||||
ourLog.info("Found IDs: {}", actual);
|
||||
|
||||
assertThat(actual, hasItem(patId));
|
||||
assertThat(actual, hasItem(encId1));
|
||||
assertThat(actual, hasItem(encId2));
|
||||
assertThat(actual, hasItem(orgId));
|
||||
assertThat(actual, hasItems(myObsIds.toArray(new String[0])));
|
||||
assertThat(actual, not(hasItem(myWrongPatId)));
|
||||
assertThat(actual, not(hasItem(myWrongEnc1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* See #674
|
||||
*/
|
||||
@Test
|
||||
public void testEverythingReturnsCorrectResourcesSmallPage() throws Exception {
|
||||
myDaoConfig.setEverythingIncludesFetchPageSize(1);
|
||||
|
||||
Bundle bundle = fetchBundle(ourServerBase + "/" + patId + "/$everything?_format=json&_count=100", EncodingEnum.JSON);
|
||||
|
||||
assertNull(bundle.getLink("next"));
|
||||
|
||||
Set<String> actual = new TreeSet<String>();
|
||||
for (BundleEntryComponent nextEntry : bundle.getEntry()) {
|
||||
actual.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
|
||||
ourLog.info("Found IDs: {}", actual);
|
||||
|
||||
assertThat(actual, hasItem(patId));
|
||||
assertThat(actual, hasItem(encId1));
|
||||
assertThat(actual, hasItem(encId2));
|
||||
assertThat(actual, hasItem(orgId));
|
||||
assertThat(actual, hasItems(myObsIds.toArray(new String[0])));
|
||||
assertThat(actual, not(hasItem(myWrongPatId)));
|
||||
assertThat(actual, not(hasItem(myWrongEnc1)));
|
||||
}
|
||||
|
||||
/**
|
||||
* See #674
|
||||
*/
|
||||
@Test
|
||||
public void testEverythingPagesWithCorrectEncodingJson() throws Exception {
|
||||
|
||||
Bundle bundle = fetchBundle(ourServerBase + "/" + patId + "/$everything?_format=json&_count=1", EncodingEnum.JSON);
|
||||
|
||||
assertNotNull(bundle.getLink("next").getUrl());
|
||||
assertThat(bundle.getLink("next").getUrl(), containsString("_format=json"));
|
||||
bundle = fetchBundle(bundle.getLink("next").getUrl(), EncodingEnum.JSON);
|
||||
|
||||
assertNotNull(bundle.getLink("next").getUrl());
|
||||
assertThat(bundle.getLink("next").getUrl(), containsString("_format=json"));
|
||||
bundle = fetchBundle(bundle.getLink("next").getUrl(), EncodingEnum.JSON);
|
||||
}
|
||||
|
||||
/**
|
||||
* See #674
|
||||
*/
|
||||
@Test
|
||||
public void testEverythingPagesWithCorrectEncodingXml() throws Exception {
|
||||
|
||||
Bundle bundle = fetchBundle(ourServerBase + "/" + patId + "/$everything?_format=xml&_count=1", EncodingEnum.XML);
|
||||
|
||||
assertNotNull(bundle.getLink("next").getUrl());
|
||||
ourLog.info("Next link: {}", bundle.getLink("next").getUrl());
|
||||
assertThat(bundle.getLink("next").getUrl(), containsString("_format=xml"));
|
||||
bundle = fetchBundle(bundle.getLink("next").getUrl(), EncodingEnum.XML);
|
||||
|
||||
assertNotNull(bundle.getLink("next").getUrl());
|
||||
ourLog.info("Next link: {}", bundle.getLink("next").getUrl());
|
||||
assertThat(bundle.getLink("next").getUrl(), containsString("_format=xml"));
|
||||
bundle = fetchBundle(bundle.getLink("next").getUrl(), EncodingEnum.XML);
|
||||
}
|
||||
|
||||
private Bundle fetchBundle(String theUrl, EncodingEnum theEncoding) throws IOException, ClientProtocolException {
|
||||
Bundle bundle;
|
||||
HttpGet get = new HttpGet(theUrl);
|
||||
CloseableHttpResponse resp = ourHttpClient.execute(get);
|
||||
try {
|
||||
assertEquals(theEncoding.getResourceContentTypeNonLegacy(), resp.getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue().replaceAll(";.*", ""));
|
||||
bundle = theEncoding.newParser(myFhirCtx).parseResource(Bundle.class, IOUtils.toString(resp.getEntity().getContent(), Charsets.UTF_8));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(resp);
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
}
|
|
@ -10,6 +10,8 @@ import static org.hamcrest.Matchers.greaterThan;
|
|||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.hasItems;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.lessThan;
|
||||
import static org.hamcrest.Matchers.lessThanOrEqualTo;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||
|
@ -45,6 +47,7 @@ import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
|
|||
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType;
|
||||
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType;
|
||||
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus;
|
||||
import org.hl7.fhir.instance.model.Encounter.EncounterState;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
import org.junit.*;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
|
@ -1367,6 +1370,53 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
ourLog.info(ids.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByLastUpdated() throws Exception {
|
||||
String methodName = "testSearchByLastUpdated";
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addName().setFamily(methodName+"1");
|
||||
IIdType pid1 = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
Thread.sleep(10);
|
||||
long time1 = System.currentTimeMillis();
|
||||
Thread.sleep(10);
|
||||
|
||||
Patient p2 = new Patient();
|
||||
p2.addName().setFamily(methodName+"2");
|
||||
IIdType pid2 = ourClient.create().resource(p2).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Patient?_lastUpdated=lt" + new InstantType(new Date(time1)).getValueAsString());
|
||||
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
ourLog.info(output);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
|
||||
ourLog.info(ids.toString());
|
||||
assertThat(ids, containsInAnyOrder(pid1));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
get = new HttpGet(ourServerBase + "/Patient?_lastUpdated=gt" + new InstantType(new Date(time1)).getValueAsString());
|
||||
response = ourHttpClient.execute(get);
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(response.getEntity().getContent());
|
||||
ourLog.info(output);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
|
||||
ourLog.info(ids.toString());
|
||||
assertThat(ids, containsInAnyOrder(pid2));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEverythingPatientType() throws Exception {
|
||||
String methodName = "testEverythingPatientType";
|
||||
|
@ -1536,12 +1586,19 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(responseBundle));
|
||||
|
||||
assertEquals(22, responseBundle.getEntry().size());
|
||||
List<String> ids = new ArrayList<String>();
|
||||
for (BundleEntryComponent nextEntry : responseBundle.getEntry()) {
|
||||
ids.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
Collections.sort(ids);
|
||||
ourLog.info("{} ids: {}", ids.size(), ids);
|
||||
|
||||
assertThat(responseBundle.getEntry().size(), lessThanOrEqualTo(25));
|
||||
|
||||
TreeSet<String> ids = new TreeSet<String>();
|
||||
TreeSet<String> idsSet = new TreeSet<String>();
|
||||
for (int i = 0; i < responseBundle.getEntry().size(); i++) {
|
||||
for (BundleEntryComponent nextEntry : responseBundle.getEntry()) {
|
||||
ids.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
idsSet.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1549,7 +1606,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
responseBundle = ourClient.fetchResourceFromUrl(Bundle.class, nextUrl);
|
||||
for (int i = 0; i < responseBundle.getEntry().size(); i++) {
|
||||
for (BundleEntryComponent nextEntry : responseBundle.getEntry()) {
|
||||
ids.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
idsSet.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1557,19 +1614,19 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
responseBundle = ourClient.fetchResourceFromUrl(Bundle.class, nextUrl);
|
||||
for (int i = 0; i < responseBundle.getEntry().size(); i++) {
|
||||
for (BundleEntryComponent nextEntry : responseBundle.getEntry()) {
|
||||
ids.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
idsSet.add(nextEntry.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
assertEquals(null, responseBundle.getLink("next"));
|
||||
|
||||
assertThat(ids, hasItem("List/A161444"));
|
||||
assertThat(ids, hasItem("List/A161468"));
|
||||
assertThat(ids, hasItem("List/A161500"));
|
||||
assertThat(idsSet, hasItem("List/A161444"));
|
||||
assertThat(idsSet, hasItem("List/A161468"));
|
||||
assertThat(idsSet, hasItem("List/A161500"));
|
||||
|
||||
ourLog.info("Expected {} - {}", allIds.size(), allIds);
|
||||
ourLog.info("Actual {} - {}", ids.size(), ids);
|
||||
assertEquals(allIds, ids);
|
||||
ourLog.info("Actual {} - {}", idsSet.size(), idsSet);
|
||||
assertEquals(allIds, idsSet);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -60,6 +60,7 @@ import ca.uhn.fhir.jpa.rp.dstu3.OrganizationResourceProvider;
|
|||
import ca.uhn.fhir.jpa.rp.dstu3.PatientResourceProvider;
|
||||
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||
|
@ -80,6 +81,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SystemProviderDstu3Test.class);
|
||||
private static Server ourServer;
|
||||
private static String ourServerBase;
|
||||
private SimpleRequestHeaderInterceptor mySimpleHeaderInterceptor;
|
||||
|
||||
@Test
|
||||
public void testTransactionWithInlineConditionalUrl() throws Exception {
|
||||
|
@ -237,10 +239,17 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
myRestServer.setDefaultResponseEncoding(EncodingEnum.XML);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
mySimpleHeaderInterceptor = new SimpleRequestHeaderInterceptor();
|
||||
ourClient.registerInterceptor(mySimpleHeaderInterceptor);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@After
|
||||
public void after() {
|
||||
myRestServer.setUseBrowserFriendlyContentTypes(true);
|
||||
ourClient.unregisterInterceptor(mySimpleHeaderInterceptor);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
@ -632,6 +641,44 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
|
|||
assertEquals(0, respSub.getEntry().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransactionCreateWithPreferHeader() throws Exception {
|
||||
|
||||
Patient p = new Patient();
|
||||
p.setActive(true);
|
||||
|
||||
Bundle req;
|
||||
Bundle resp;
|
||||
|
||||
// No prefer header
|
||||
req = new Bundle();
|
||||
req.setType(BundleType.TRANSACTION);
|
||||
req.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient");
|
||||
resp = ourClient.transaction().withBundle(req).execute();
|
||||
assertEquals(null, resp.getEntry().get(0).getResource());
|
||||
assertEquals("201 Created", resp.getEntry().get(0).getResponse().getStatus());
|
||||
|
||||
// Prefer return=minimal
|
||||
mySimpleHeaderInterceptor.setHeaderName(Constants.HEADER_PREFER);
|
||||
mySimpleHeaderInterceptor.setHeaderValue(Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_MINIMAL);
|
||||
req = new Bundle();
|
||||
req.setType(BundleType.TRANSACTION);
|
||||
req.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient");
|
||||
resp = ourClient.transaction().withBundle(req).execute();
|
||||
assertEquals(null, resp.getEntry().get(0).getResource());
|
||||
assertEquals("201 Created", resp.getEntry().get(0).getResponse().getStatus());
|
||||
|
||||
// Prefer return=representation
|
||||
mySimpleHeaderInterceptor.setHeaderName(Constants.HEADER_PREFER);
|
||||
mySimpleHeaderInterceptor.setHeaderValue(Constants.HEADER_PREFER_RETURN + "=" + Constants.HEADER_PREFER_RETURN_REPRESENTATION);
|
||||
req = new Bundle();
|
||||
req.setType(BundleType.TRANSACTION);
|
||||
req.addEntry().setResource(p).getRequest().setMethod(HTTPVerb.POST).setUrl("Patient");
|
||||
resp = ourClient.transaction().withBundle(req).execute();
|
||||
assertEquals(Patient.class, resp.getEntry().get(0).getResource().getClass());
|
||||
assertEquals("201 Created", resp.getEntry().get(0).getResponse().getStatus());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.hl7.fhir.dstu3.model.CapabilityStatement.UnknownContentCode;
|
|||
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.dstu3.model.Identifier.IdentifierUse;
|
||||
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.utilities.xhtml.XhtmlNode;
|
||||
import org.junit.After;
|
||||
|
@ -56,8 +57,10 @@ import org.junit.Test;
|
|||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.parser.IParserErrorHandler.IParseLocation;
|
||||
|
@ -81,14 +84,14 @@ public class JsonParserDstu3Test {
|
|||
public void after() {
|
||||
ourCtx.setNarrativeGenerator(null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See #563
|
||||
*/
|
||||
@Test
|
||||
public void testBadMessageForUnknownElement() throws IOException {
|
||||
String input = IOUtils.toString(JsonParserDstu3Test.class.getResourceAsStream("/bad_parse_bundle_1.json"), StandardCharsets.UTF_8);
|
||||
|
||||
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
p.setParserErrorHandler(new StrictErrorHandler());
|
||||
try {
|
||||
|
@ -99,14 +102,13 @@ public class JsonParserDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See #563
|
||||
*/
|
||||
@Test
|
||||
public void testBadMessageForUnknownElement2() throws IOException {
|
||||
String input = IOUtils.toString(JsonParserDstu3Test.class.getResourceAsStream("/bad_parse_bundle_2.json"), StandardCharsets.UTF_8);
|
||||
|
||||
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
p.setParserErrorHandler(new StrictErrorHandler());
|
||||
try {
|
||||
|
@ -116,35 +118,35 @@ public class JsonParserDstu3Test {
|
|||
assertEquals("Found incorrect type for element context - Expected OBJECT and found SCALAR (STRING)", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See #544
|
||||
*/
|
||||
@Test
|
||||
public void testBundleStitchReferencesByUuid() throws Exception {
|
||||
Bundle bundle = new Bundle();
|
||||
|
||||
|
||||
DocumentManifest dm = new DocumentManifest();
|
||||
dm.getSubject().setReference("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3");
|
||||
bundle.addEntry().setResource(dm);
|
||||
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addName().setFamily("FAMILY");
|
||||
bundle.addEntry().setResource(patient).setFullUrl("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3");
|
||||
|
||||
|
||||
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle);
|
||||
ourLog.info(encoded);
|
||||
|
||||
|
||||
bundle = ourCtx.newJsonParser().parseResource(Bundle.class, encoded);
|
||||
dm = (DocumentManifest) bundle.getEntry().get(0).getResource();
|
||||
|
||||
|
||||
assertEquals("urn:uuid:96e85cca-9797-45d6-834a-c4eb27f331d3", dm.getSubject().getReference());
|
||||
|
||||
|
||||
Patient subject = (Patient) dm.getSubject().getResource();
|
||||
assertNotNull(subject);
|
||||
assertEquals("FAMILY", subject.getNameFirstRep().getFamily());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test for the url generated based on the server config
|
||||
*/
|
||||
|
@ -170,12 +172,12 @@ public class JsonParserDstu3Test {
|
|||
newPatient = jsonParser.parseResource(MyPatientWithCustomUrlExtension.class, new StringReader(parsedPatient));
|
||||
assertEquals("myName", newPatient.getPetName().getValue());
|
||||
|
||||
//Check no NPE if base server not configure
|
||||
// Check no NPE if base server not configure
|
||||
newPatient = ourCtx.newJsonParser().parseResource(MyPatientWithCustomUrlExtension.class, new StringReader(parsedPatient));
|
||||
assertNull("myName", newPatient.getPetName().getValue());
|
||||
assertEquals("myName", ((StringType) newPatient.getExtensionsByUrl("http://www.example.com/petname").get(0).getValue()).getValue());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCustomUrlExtensioninBundle() {
|
||||
final String expected = "{\"resourceType\":\"Bundle\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"extension\":[{\"url\":\"http://www.example.com/petname\",\"valueString\":\"myName\"}]}}]}";
|
||||
|
@ -237,7 +239,6 @@ public class JsonParserDstu3Test {
|
|||
assertEquals(3, countMatches(encoded, "resourceType"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncodeAndParseExtensions() throws Exception {
|
||||
|
||||
|
@ -323,7 +324,7 @@ public class JsonParserDstu3Test {
|
|||
assertEquals("CHILD", ((StringType) given2ext2.getValue()).getValue());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncodeAndParseMetaProfileAndTags() {
|
||||
Patient p = new Patient();
|
||||
|
@ -402,7 +403,6 @@ public class JsonParserDstu3Test {
|
|||
assertEquals("sec_label2", tagList.get(1).getDisplay());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* See #336
|
||||
*/
|
||||
|
@ -701,7 +701,7 @@ public class JsonParserDstu3Test {
|
|||
.addExtension()
|
||||
.setUrl("http://foo")
|
||||
.setValue(new Reference("Practitioner/A"));
|
||||
|
||||
|
||||
IParser parser = ourCtx.newJsonParser().setPrettyPrint(true);
|
||||
parser.setDontEncodeElements(new HashSet<String>(Arrays.asList("*.id", "*.meta")));
|
||||
|
||||
|
@ -1305,9 +1305,25 @@ public class JsonParserDstu3Test {
|
|||
assertEquals("{\"resourceType\":\"Observation\",\"valueQuantity\":{\"value\":0.0000000000000001}}", str);
|
||||
}
|
||||
|
||||
/**
|
||||
* See #658
|
||||
*/
|
||||
@Test
|
||||
public void testExtraElement() throws Exception {
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
p.setParserErrorHandler(new StrictErrorHandler());
|
||||
try {
|
||||
p.parseResource(IOUtils.toString(JsonParserDstu3Test.class.getResourceAsStream("/Patient.json.txt"), Charsets.UTF_8));
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Found incorrect type for element assigner - Expected OBJECT and found SCALAR (STRING)", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncorrectJsonTypesIdAndArray() {
|
||||
|
||||
|
||||
// ID should be a String and communication should be an Array
|
||||
String input = "{\"resourceType\": \"Patient\",\n" +
|
||||
" \"id\": 123,\n" +
|
||||
|
@ -1320,35 +1336,35 @@ public class JsonParserDstu3Test {
|
|||
"}";
|
||||
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
|
||||
|
||||
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
|
||||
p.setParserErrorHandler(errorHandler);
|
||||
Patient patient = (Patient) p.parseResource(input);
|
||||
|
||||
|
||||
ArgumentCaptor<String> elementName = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<ValueType> found = ArgumentCaptor.forClass(ValueType.class);
|
||||
ArgumentCaptor<ValueType> expected = ArgumentCaptor.forClass(ValueType.class);
|
||||
ArgumentCaptor<ScalarType> expectedScalarType = ArgumentCaptor.forClass(ScalarType.class);
|
||||
ArgumentCaptor<ScalarType> foundScalarType = ArgumentCaptor.forClass(ScalarType.class);
|
||||
verify(errorHandler, times(2)).incorrectJsonType(any(IParseLocation.class), elementName.capture(), expected.capture(), expectedScalarType.capture(), found.capture(), foundScalarType.capture());
|
||||
|
||||
|
||||
assertEquals(ValueType.SCALAR, found.getAllValues().get(0));
|
||||
assertEquals(ValueType.SCALAR, expected.getAllValues().get(0));
|
||||
assertEquals(ScalarType.NUMBER, foundScalarType.getAllValues().get(0));
|
||||
assertEquals(ScalarType.STRING, expectedScalarType.getAllValues().get(0));
|
||||
|
||||
|
||||
assertEquals(ValueType.OBJECT, found.getAllValues().get(1));
|
||||
assertEquals(ValueType.ARRAY, expected.getAllValues().get(1));
|
||||
assertEquals(null, foundScalarType.getAllValues().get(1));
|
||||
assertEquals(null, expectedScalarType.getAllValues().get(1));
|
||||
|
||||
|
||||
assertEquals("123", patient.getIdElement().getIdPart());
|
||||
assertEquals("Hindi", patient.getCommunicationFirstRep().getLanguage().getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncorrectJsonTypesNone() {
|
||||
|
||||
|
||||
// ID should be a String and communication should be an Array
|
||||
String input = "{\"resourceType\": \"Patient\",\n" +
|
||||
" \"id\": \"123\",\n" +
|
||||
|
@ -1361,18 +1377,18 @@ public class JsonParserDstu3Test {
|
|||
"}";
|
||||
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
|
||||
|
||||
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
|
||||
p.setParserErrorHandler(errorHandler);
|
||||
Patient patient = (Patient) p.parseResource(input);
|
||||
|
||||
|
||||
ArgumentCaptor<String> elementName = ArgumentCaptor.forClass(String.class);
|
||||
ArgumentCaptor<ValueType> found = ArgumentCaptor.forClass(ValueType.class);
|
||||
ArgumentCaptor<ValueType> expected = ArgumentCaptor.forClass(ValueType.class);
|
||||
ArgumentCaptor<ScalarType> expectedScalarType = ArgumentCaptor.forClass(ScalarType.class);
|
||||
ArgumentCaptor<ScalarType> foundScalarType = ArgumentCaptor.forClass(ScalarType.class);
|
||||
verify(errorHandler, times(0)).incorrectJsonType(any(IParseLocation.class), elementName.capture(), expected.capture(), expectedScalarType.capture(), found.capture(), foundScalarType.capture());
|
||||
|
||||
|
||||
assertEquals("123", patient.getIdElement().getIdPart());
|
||||
assertEquals("Hindi", patient.getCommunicationFirstRep().getLanguage().getText());
|
||||
}
|
||||
|
@ -1380,21 +1396,21 @@ public class JsonParserDstu3Test {
|
|||
@Test
|
||||
public void testInvalidDateTimeValueInvalid() throws Exception {
|
||||
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
|
||||
|
||||
|
||||
String res = "{ \"resourceType\": \"Observation\", \"valueDateTime\": \"foo\" }";
|
||||
IParser parser = ourCtx.newJsonParser();
|
||||
parser.setParserErrorHandler(errorHandler);
|
||||
Observation parsed = parser.parseResource(Observation.class, res);
|
||||
|
||||
|
||||
assertEquals(null, parsed.getValueDateTimeType().getValue());
|
||||
assertEquals("foo", parsed.getValueDateTimeType().getValueAsString());
|
||||
|
||||
|
||||
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(errorHandler, times(1)).invalidValue(isNull(IParseLocation.class), eq("foo"), msgCaptor.capture());
|
||||
assertEquals("Invalid date/time format: \"foo\"", msgCaptor.getValue());
|
||||
|
||||
|
||||
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
|
||||
assertEquals("{\"resourceType\":\"Observation\",\"valueDateTime\":\"foo\"}", encoded);
|
||||
assertEquals("{\"resourceType\":\"Observation\",\"valueDateTime\":\"foo\"}", encoded);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1412,19 +1428,19 @@ public class JsonParserDstu3Test {
|
|||
@Test
|
||||
public void testInvalidEnumValueBlank() {
|
||||
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
|
||||
|
||||
|
||||
String res = "{ \"resourceType\": \"Patient\", \"gender\": \"\" }";
|
||||
IParser parser = ourCtx.newJsonParser();
|
||||
parser.setParserErrorHandler(errorHandler);
|
||||
Patient parsed = parser.parseResource(Patient.class, res);
|
||||
|
||||
|
||||
assertEquals(null, parsed.getGenderElement().getValue());
|
||||
assertEquals(null, parsed.getGenderElement().getValueAsString());
|
||||
|
||||
|
||||
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(errorHandler, times(1)).invalidValue(isNull(IParseLocation.class), eq(""), msgCaptor.capture());
|
||||
assertEquals("Attribute values must not be empty (\"\")", msgCaptor.getValue());
|
||||
|
||||
|
||||
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
|
||||
assertEquals("{\"resourceType\":\"Patient\"}", encoded);
|
||||
}
|
||||
|
@ -1432,21 +1448,21 @@ public class JsonParserDstu3Test {
|
|||
@Test
|
||||
public void testInvalidEnumValueInvalid() {
|
||||
IParserErrorHandler errorHandler = mock(IParserErrorHandler.class);
|
||||
|
||||
|
||||
String res = "{ \"resourceType\": \"Patient\", \"gender\": \"foo\" }";
|
||||
IParser parser = ourCtx.newJsonParser();
|
||||
parser.setParserErrorHandler(errorHandler);
|
||||
Patient parsed = parser.parseResource(Patient.class, res);
|
||||
|
||||
|
||||
assertEquals(null, parsed.getGenderElement().getValue());
|
||||
assertEquals("foo", parsed.getGenderElement().getValueAsString());
|
||||
|
||||
|
||||
ArgumentCaptor<String> msgCaptor = ArgumentCaptor.forClass(String.class);
|
||||
verify(errorHandler, times(1)).invalidValue(isNull(IParseLocation.class), eq("foo"), msgCaptor.capture());
|
||||
assertEquals("Unknown AdministrativeGender code 'foo'", msgCaptor.getValue());
|
||||
|
||||
|
||||
String encoded = ourCtx.newJsonParser().encodeResourceToString(parsed);
|
||||
assertEquals("{\"resourceType\":\"Patient\",\"gender\":\"foo\"}", encoded);
|
||||
assertEquals("{\"resourceType\":\"Patient\",\"gender\":\"foo\"}", encoded);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1910,10 +1926,10 @@ public class JsonParserDstu3Test {
|
|||
public void testParseEmptyValue() {
|
||||
String input = "{\"resourceType\":\"QuestionnaireResponse\",\"id\":\"123\",\"authored\":\"\",\"group\":{\"linkId\":\"\"}}";
|
||||
IParser parser = ourCtx.newJsonParser();
|
||||
|
||||
|
||||
parser.setParserErrorHandler(new LenientErrorHandler().setErrorOnInvalidValue(false));
|
||||
QuestionnaireResponse qr = parser.parseResource(QuestionnaireResponse.class, input);
|
||||
|
||||
|
||||
assertEquals("QuestionnaireResponse/123", qr.getIdElement().getValue());
|
||||
assertEquals(null, qr.getAuthored());
|
||||
assertEquals(null, qr.getAuthoredElement().getValue());
|
||||
|
@ -2298,10 +2314,14 @@ public class JsonParserDstu3Test {
|
|||
ArgumentCaptor<ValueType> actual = ArgumentCaptor.forClass(ValueType.class);
|
||||
ArgumentCaptor<ScalarType> expectedScalar = ArgumentCaptor.forClass(ScalarType.class);
|
||||
ArgumentCaptor<ScalarType> actualScalar = ArgumentCaptor.forClass(ScalarType.class);
|
||||
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), elementName.capture(), expected.capture(), expectedScalar.capture(), actual.capture(), actualScalar.capture());
|
||||
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("_id"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(), Mockito.eq(ValueType.SCALAR), actualScalar.capture());
|
||||
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("__v"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(), Mockito.eq(ValueType.SCALAR), actualScalar.capture());
|
||||
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("_status"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(), Mockito.eq(ValueType.SCALAR), actualScalar.capture());
|
||||
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), elementName.capture(), expected.capture(), expectedScalar.capture(), actual.capture(),
|
||||
actualScalar.capture());
|
||||
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("_id"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(), Mockito.eq(ValueType.SCALAR),
|
||||
actualScalar.capture());
|
||||
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("__v"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(), Mockito.eq(ValueType.SCALAR),
|
||||
actualScalar.capture());
|
||||
verify(errorHandler, atLeastOnce()).incorrectJsonType(Mockito.any(IParseLocation.class), Mockito.eq("_status"), Mockito.eq(ValueType.OBJECT), expectedScalar.capture(),
|
||||
Mockito.eq(ValueType.SCALAR), actualScalar.capture());
|
||||
|
||||
assertEquals("_id", elementName.getAllValues().get(0));
|
||||
assertEquals(ValueType.OBJECT, expected.getAllValues().get(0));
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.http.ProtocolVersion;
|
|||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.entity.ContentType;
|
||||
|
@ -60,6 +61,7 @@ import ca.uhn.fhir.parser.CustomTypeDstu3Test.MyCustomPatient;
|
|||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
import ca.uhn.fhir.rest.client.SearchClientDstu3Test.ILocationClient;
|
||||
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
|
||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||
import ca.uhn.fhir.rest.client.interceptor.CookieInterceptor;
|
||||
|
@ -177,6 +179,8 @@ public class GenericClientDstu3Test {
|
|||
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
|
||||
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testPatchJsonByIdType() throws Exception {
|
||||
|
@ -1191,6 +1195,36 @@ public class GenericClientDstu3Test {
|
|||
// assertEquals("FAM", resp.getNameFirstRep().getFamilyAsSingleString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithNullParameters() throws Exception {
|
||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
int idx = 0;
|
||||
|
||||
DateTimeDt now = DateTimeDt.withCurrentTime();
|
||||
String dateString = now.getValueAsString().substring(0, 10);
|
||||
|
||||
client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.NAME.matches().value((String)null))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByDate() throws Exception {
|
||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
|
|
@ -43,7 +43,6 @@ import ca.uhn.fhir.rest.annotation.Sort;
|
|||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
{
|
||||
"resourceType": "Patient",
|
||||
"id": "patient1",
|
||||
"contained": [{
|
||||
"resourceType": "Practitioner",
|
||||
"id": "p1",
|
||||
"identifier": [{
|
||||
"system": "urn:oid:2.16.840.1.113883.3.566",
|
||||
"value": "145148",
|
||||
"assigner": "UHC"
|
||||
}],
|
||||
"name": [{
|
||||
"text": " Richard J Y Ha MD",
|
||||
"given": ["Richard",
|
||||
"J Y"],
|
||||
"family": "Ha",
|
||||
"suffix": ["MD"]
|
||||
}]
|
||||
}],
|
||||
"identifier": [{
|
||||
"use": "official",
|
||||
"type": {
|
||||
"coding": [{
|
||||
"system": "http://hl7.org/fhir/identifier-type",
|
||||
"code": "MR",
|
||||
"display": "Medical Record Number",
|
||||
"userSelected": true
|
||||
}]
|
||||
},
|
||||
"system": "urn:oid:2.16.840.1.113883.3.566",
|
||||
"value": "220457511",
|
||||
"assigner": "MU"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"coding": [{
|
||||
"system": "http://hl7.org/fhir/identifier-type",
|
||||
"code": "MR",
|
||||
"display": "UHC ID",
|
||||
"userSelected": true
|
||||
}]
|
||||
},
|
||||
"system": "urn:oid:2.16.840.1.113883.3.566",
|
||||
"value": "15246931",
|
||||
"assigner": "UHC"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"coding": [{
|
||||
"system": "http://hl7.org/fhir/identifier-type",
|
||||
"code": "MR",
|
||||
"display": "ACCT NUM",
|
||||
"userSelected": true
|
||||
}]
|
||||
},
|
||||
"system": "urn:oid:2.16.840.1.113883.3.566",
|
||||
"value": "226274321",
|
||||
"assigner": "MU"
|
||||
},
|
||||
{
|
||||
"type": {
|
||||
"coding": [{
|
||||
"system": "http://hl7.org/fhir/identifier-type",
|
||||
"code": "MR",
|
||||
"display": "HNAMPERSON_ID",
|
||||
"userSelected": true
|
||||
}]
|
||||
},
|
||||
"system": "urn:oid:2.16.840.1.113883.3.566",
|
||||
"value": "25296343"
|
||||
}],
|
||||
"active": true,
|
||||
"name": [{
|
||||
"use": "official",
|
||||
"text": " MOM TWO CERNER",
|
||||
"given": ["MOM",
|
||||
"TWO"],
|
||||
"family": "CERNER"
|
||||
}],
|
||||
"gender": "female",
|
||||
"birthDate": "1990-10-18",
|
||||
"address": [{
|
||||
"use": "home",
|
||||
"text": "2401 LEMONE INDUSTRIAL BLVD, COLUMBIA, MO 65201 ",
|
||||
"line": "2401 LEMONE INDUSTRIAL BLVD",
|
||||
"city": "COLUMBIA",
|
||||
"state": "MO",
|
||||
"postalCode": "65201"
|
||||
}],
|
||||
"maritalStatus": {
|
||||
"coding": [{
|
||||
"system": "urn:oid:2.16.840.1.113883.3.566",
|
||||
"code": "M",
|
||||
"display": "Married",
|
||||
"userSelected": true
|
||||
}]
|
||||
},
|
||||
"contact": [{
|
||||
"name": {
|
||||
"text": "BABY TWO CERNER",
|
||||
"given": ["BABY",
|
||||
"TWO"],
|
||||
"family": "CERNER"
|
||||
},
|
||||
"address": {
|
||||
"text": "2401 LEMONE INDUSTRIAL BLVD, COLUMBIA MO 65201 ",
|
||||
"line": "2401 LEMONE INDUSTRIAL BLVD",
|
||||
"city": "COLUMBIA",
|
||||
"state": "MO",
|
||||
"postalCode": "65201"
|
||||
}
|
||||
}],
|
||||
"communication": [{
|
||||
"language": {
|
||||
"coding": [{
|
||||
"system": "urn:oid:2.16.840.1.113883.3.566",
|
||||
"code": "ENG",
|
||||
"userSelected": true
|
||||
}]
|
||||
}
|
||||
}],
|
||||
"generalPractitioner": [{
|
||||
"reference": "#p1"
|
||||
}],
|
||||
"managingOrganization": {
|
||||
"display": "Organization/UHC"
|
||||
}
|
||||
}
|
19
pom.xml
19
pom.xml
|
@ -538,6 +538,11 @@
|
|||
<artifactId>Saxon-HE</artifactId>
|
||||
<version>9.5.1-5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.ttddyy</groupId>
|
||||
<artifactId>datasource-proxy</artifactId>
|
||||
<version>1.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
|
@ -1769,6 +1774,20 @@
|
|||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>MINPARALLEL</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<forkCount>2</forkCount>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>ERRORPRONE</id>
|
||||
<build>
|
||||
|
|
|
@ -32,6 +32,26 @@
|
|||
Fix HTTP 500 error in JPA server if a numeric search parameter was supplied with no value, e.g.
|
||||
<![CDATA[<code>GET /Observation?value-quantity=</code>]]>
|
||||
</action>
|
||||
<action type="add">
|
||||
JPA server transaction processing now honours the Prefer header and includes
|
||||
created and updated resource bodies in the response bundle if it is set
|
||||
appropriately.
|
||||
</action>
|
||||
<action type="add">
|
||||
Optimize queries in JPA server remove a few redundant select columns when performing
|
||||
searches. This provides a slight speed increase in some cases.
|
||||
</action>
|
||||
<action type="add">
|
||||
Add configuration to JPA server DaoConfig that allows a maximum
|
||||
number of search results to be specified. Queries will never return
|
||||
more than this number, which can be good for avoiding accidental
|
||||
performance problems in situations where lare queries should not be
|
||||
needed
|
||||
</action>
|
||||
<action type="fix" issue="674">
|
||||
Prevent duplicates in $everything query response in JPA server. Thanks to @vlad-ignatov
|
||||
for reporting!
|
||||
</action>
|
||||
</release>
|
||||
<release version="2.5" date="2017-06-08">
|
||||
<action type="fix">
|
||||
|
|
|
@ -145,4 +145,26 @@
|
|||
</answer>
|
||||
</faq>
|
||||
</part>
|
||||
<part id="Contributing">
|
||||
<title>Contributing</title>
|
||||
<faq id="vm_quit_during_build">
|
||||
<question>
|
||||
My build is failing with the following error:
|
||||
<code>[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.19.1:test (default-test) on project hapi-fhir-jpaserver-base: Execution default-test of goal org.apache.maven.plugins:maven-surefire-plugin:2.19.1:test failed: The forked VM terminated without properly saying goodbye. VM crash or System.exit called?</code>
|
||||
</question>
|
||||
<answer>
|
||||
<p>
|
||||
This typically means that your build is running out of memory. HAPI's unit tests execute by
|
||||
default in multiple threads (the thread count is determined by the number of CPU cores available)
|
||||
so in an environment with lots of cores but not enough RAM, you may run out. If you are getting
|
||||
this error, try executing the build with the following arguments:
|
||||
</p>
|
||||
<pre>mvn -P ALLMODULES,NOPARALLEL install</pre>
|
||||
<p>
|
||||
See <a href="/hacking_hapi_fhir.html">Hacking HAPI FHIR</a> for more information on
|
||||
the build process.
|
||||
</p>
|
||||
</answer>
|
||||
</faq>
|
||||
</part>
|
||||
</faqs>
|
||||
|
|
Loading…
Reference in New Issue