This commit is contained in:
jkv 2017-02-14 08:02:15 +01:00
commit fb80c79103
76 changed files with 5953 additions and 4650 deletions

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
[*.java]
charset = utf-8
indent_style = tab

View File

@ -47,7 +47,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>2.1-SNAPSHOT</version>
<version>2.3-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>

View File

@ -60,6 +60,11 @@ public class PagingPatientProvider implements IResourceProvider {
// Typically this method just returns null
return null;
}
@Override
public String getUuid() {
return null;
}
};
}

View File

@ -1889,7 +1889,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
}
for (Include next : myRevInclude) {
addParam(params, Constants.PARAM_REVINCLUDE, next.getValue());
if (next.isRecurse()) {
addParam(params, Constants.PARAM_REVINCLUDE_RECURSE, next.getValue());
} else {
addParam(params, Constants.PARAM_REVINCLUDE, next.getValue());
}
}
if (myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2)) {

View File

@ -171,7 +171,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
return new IBundleProvider() {
@Override
public InstantDt getPublished() {
public IPrimitiveType<Date> getPublished() {
return resources.getPublished();
}
@ -201,7 +201,12 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
@Override
public Integer preferredPageSize() {
return null;
return resources.preferredPageSize();
}
@Override
public String getUuid() {
return resources.getUuid();
}
};
}

View File

@ -14,7 +14,7 @@ import ca.uhn.fhir.context.FhirContext;
* 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
* 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,
@ -48,7 +48,7 @@ abstract class BaseParam implements IQueryParameterType {
public Boolean getMissing() {
return myMissing;
}
@Override
public final String getQueryParameterQualifier() {
if (myMissing != null && myMissing.booleanValue()) {
@ -73,8 +73,8 @@ abstract class BaseParam implements IQueryParameterType {
}
/**
* If set to non-null value, indicates that this parameter has been populated
* with a "[name]:missing=true" or "[name]:missing=false" vale instead of a
* If set to non-null value, indicates that this parameter has been populated
* with a "[name]:missing=true" or "[name]:missing=false" vale instead of a
* normal value
*
* @return Returns a reference to <code>this</code> for easier method chaining
@ -97,10 +97,10 @@ abstract class BaseParam implements IQueryParameterType {
throw new InvalidRequestException(msg);
}
}
myMissing = null;
doSetValueAsQueryToken(theContext, theParamName, theQualifier, theValue);
}
}
}

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.param;
import static org.apache.commons.lang3.StringUtils.defaultString;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@ -32,17 +33,26 @@ import ca.uhn.fhir.rest.server.Constants;
public class StringParam extends BaseParam implements IQueryParameterType {
private boolean myContains;
private boolean myExact;
private String myValue;
private boolean myContains;
/**
* Constructor
*/
public StringParam() {
}
/**
* Constructor
*/
public StringParam(String theValue) {
setValue(theValue);
}
/**
* Constructor
*/
public StringParam(String theValue, boolean theExact) {
setValue(theValue);
setExact(theExact);
@ -79,6 +89,29 @@ public class StringParam extends BaseParam implements IQueryParameterType {
myValue = ParameterUtil.unescape(theValue);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof StringParam)) {
return false;
}
StringParam other = (StringParam) obj;
EqualsBuilder eb = new EqualsBuilder();
eb.append(myExact, other.myExact);
eb.append(myContains, other.myContains);
eb.append(myValue, other.myValue);
eb.append(getMissing(), other.getMissing());
return eb.isEquals();
}
public String getValue() {
return myValue;
}
@ -91,6 +124,13 @@ public class StringParam extends BaseParam implements IQueryParameterType {
return defaultString(myValue);
}
/**
* String parameter modifier <code>:contains</code>
*/
public boolean isContains() {
return myContains;
}
public boolean isEmpty() {
return StringUtils.isEmpty(myValue);
}
@ -99,6 +139,18 @@ public class StringParam extends BaseParam implements IQueryParameterType {
return myExact;
}
/**
* String parameter modifier <code>:contains</code>
*/
public StringParam setContains(boolean theContains) {
myContains = theContains;
if (myContains) {
setExact(false);
setMissing(null);
}
return this;
}
public StringParam setExact(boolean theExact) {
myExact = theExact;
if (myExact) {
@ -108,13 +160,6 @@ public class StringParam extends BaseParam implements IQueryParameterType {
return this;
}
/**
* String parameter modifier <code>:contains</code>
*/
public boolean isContains() {
return myContains;
}
public StringParam setValue(String theValue) {
myValue = theValue;
return this;
@ -136,16 +181,4 @@ public class StringParam extends BaseParam implements IQueryParameterType {
return builder.toString();
}
/**
* String parameter modifier <code>:contains</code>
*/
public StringParam setContains(boolean theContains) {
myContains = theContains;
if (myContains) {
setExact(false);
setMissing(null);
}
return this;
}
}

View File

@ -0,0 +1,36 @@
package ca.uhn.fhir.rest.server;
import org.apache.commons.lang3.Validate;
public abstract class BasePagingProvider implements IPagingProvider {
private int myDefaultPageSize = 10;
private int myMaximumPageSize = 50;
public BasePagingProvider() {
super();
}
@Override
public int getDefaultPageSize() {
return myDefaultPageSize;
}
@Override
public int getMaximumPageSize() {
return myMaximumPageSize;
}
public BasePagingProvider setDefaultPageSize(int theDefaultPageSize) {
Validate.isTrue(theDefaultPageSize > 0, "size must be greater than 0");
myDefaultPageSize = theDefaultPageSize;
return this;
}
public BasePagingProvider setMaximumPageSize(int theMaximumPageSize) {
Validate.isTrue(theMaximumPageSize > 0, "size must be greater than 0");
myMaximumPageSize = theMaximumPageSize;
return this;
}
}

View File

@ -64,6 +64,11 @@ public class BundleProviders {
public Integer preferredPageSize() {
return null;
}
@Override
public String getUuid() {
return null;
}
};
}

View File

@ -40,11 +40,13 @@ public class Constants {
public static final String CT_HTML = "text/html";
public static final String CT_HTML_WITH_UTF8 = "text/html" + CHARSET_UTF8_CTSUFFIX;
public static final String CT_JSON = "application/json";
public static final String CT_JSON_PATCH = "application/json-patch+json";
public static final String CT_OCTET_STREAM = "application/octet-stream";
public static final String CT_TEXT = "text/plain";
public static final String CT_TEXT_WITH_UTF8 = CT_TEXT + CHARSET_UTF8_CTSUFFIX;
public static final String CT_X_FORM_URLENCODED = "application/x-www-form-urlencoded";
public static final String CT_XML = "application/xml";
public static final String CT_XML_PATCH = "application/xml-patch+xml";
public static final String ENCODING_GZIP = "gzip";
public static final String EXTOP_VALIDATE = "$validate";
public static final String EXTOP_VALIDATE_MODE = "mode";
@ -61,10 +63,10 @@ public class Constants {
public static final String FORMATS_HTML_XML = "html/xml";
public static final String HEADER_ACCEPT = "Accept";
public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
public static final String HEADER_ACCEPT_VALUE_JSON_NON_LEGACY = CT_FHIR_JSON_NEW + ";q=1.0, " + CT_FHIR_JSON + ";q=0.9";
public static final String HEADER_ACCEPT_VALUE_XML_NON_LEGACY = CT_FHIR_XML_NEW + ";q=1.0, " + CT_FHIR_XML + ";q=0.9";
public static final String HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY = CT_FHIR_XML + ";q=1.0, " + CT_FHIR_JSON + ";q=1.0";
public static final String HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY = CT_FHIR_XML_NEW + ";q=1.0, " + CT_FHIR_JSON_NEW + ";q=1.0, " + HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY.replace("1.0", "0.9");
public static final String HEADER_ACCEPT_VALUE_XML_NON_LEGACY = CT_FHIR_XML_NEW + ";q=1.0, " + CT_FHIR_XML + ";q=0.9";
public static final String HEADER_ACCEPT_VALUE_JSON_NON_LEGACY = CT_FHIR_JSON_NEW + ";q=1.0, " + CT_FHIR_JSON + ";q=0.9";
public static final String HEADER_ALLOW = "Allow";
public static final String HEADER_AUTHORIZATION = "Authorization";
public static final String HEADER_AUTHORIZATION_VALPREFIX_BASIC = "Basic ";
@ -127,10 +129,12 @@ public class Constants {
public static final String PARAM_PAGINGACTION = "_getpages";
public static final String PARAM_PAGINGOFFSET = "_getpagesoffset";
public static final String PARAM_PRETTY = "_pretty";
public static final String PARAM_PRETTY_VALUE_FALSE = "false";
public static final String PARAM_PRETTY_VALUE_TRUE = "true";
public static final String PARAM_PROFILE = "_profile";
public static final String PARAM_QUERY = "_query";
public static final String PARAM_REVINCLUDE = "_revinclude";
public static final String PARAM_REVINCLUDE_RECURSE = PARAM_REVINCLUDE+PARAM_INCLUDE_QUALIFIER_RECURSE;
public static final String PARAM_SEARCH = "_search";
public static final String PARAM_SECURITY = "_security";
public static final String PARAM_SINCE = "_since";
@ -156,11 +160,11 @@ public class Constants {
public static final int STATUS_HTTP_401_CLIENT_UNAUTHORIZED = 401;
public static final int STATUS_HTTP_403_FORBIDDEN = 403;
public static final int STATUS_HTTP_404_NOT_FOUND = 404;
public static final int STATUS_HTTP_405_METHOD_NOT_ALLOWED = 405;
public static final int STATUS_HTTP_409_CONFLICT = 409;
public static final int STATUS_HTTP_410_GONE = 410;
public static final int STATUS_HTTP_412_PRECONDITION_FAILED = 412;
public static final int STATUS_HTTP_422_UNPROCESSABLE_ENTITY = 422;
public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500;
public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501;
@ -168,9 +172,6 @@ public class Constants {
public static final String TAG_SUBSETTED_SYSTEM = "http://hl7.org/fhir/v3/ObservationValue";
public static final String URL_TOKEN_HISTORY = "_history";
public static final String URL_TOKEN_METADATA = "metadata";
public static final String CT_JSON_PATCH = "application/json-patch+json";
public static final String CT_XML_PATCH = "application/xml-patch+xml";
public static final String PARAM_PRETTY_VALUE_FALSE = "false";
static {
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.rest.server;
* 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
* 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,
@ -25,11 +25,9 @@ import java.util.UUID;
import org.apache.commons.lang3.Validate;
public class FifoMemoryPagingProvider implements IPagingProvider {
public class FifoMemoryPagingProvider extends BasePagingProvider implements IPagingProvider {
private LinkedHashMap<String, IBundleProvider> myBundleProviders;
private int myDefaultPageSize=10;
private int myMaximumPageSize=50;
private int mySize;
public FifoMemoryPagingProvider(int theSize) {
@ -39,33 +37,11 @@ public class FifoMemoryPagingProvider implements IPagingProvider {
myBundleProviders = new LinkedHashMap<String, IBundleProvider>(mySize);
}
@Override
public int getDefaultPageSize() {
return myDefaultPageSize;
}
@Override
public int getMaximumPageSize() {
return myMaximumPageSize;
}
@Override
public synchronized IBundleProvider retrieveResultList(String theId) {
return myBundleProviders.get(theId);
}
public FifoMemoryPagingProvider setDefaultPageSize(int theDefaultPageSize) {
Validate.isTrue(theDefaultPageSize > 0, "size must be greater than 0");
myDefaultPageSize = theDefaultPageSize;
return this;
}
public FifoMemoryPagingProvider setMaximumPageSize(int theMaximumPageSize) {
Validate.isTrue(theMaximumPageSize > 0, "size must be greater than 0");
myMaximumPageSize = theMaximumPageSize;
return this;
}
@Override
public synchronized String storeResultList(IBundleProvider theList) {
while (myBundleProviders.size() > mySize) {

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.rest.server;
import java.util.Date;
/*
* #%L
* HAPI FHIR - Core Library
@ -10,7 +12,7 @@ package ca.uhn.fhir.rest.server;
* 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
* 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,
@ -23,6 +25,7 @@ package ca.uhn.fhir.rest.server;
import java.util.List;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import ca.uhn.fhir.model.primitive.InstantDt;
@ -31,15 +34,17 @@ public interface IBundleProvider {
/**
* Load the given collection of resources by index, plus any additional resources per the
* server's processing rules (e.g. _include'd resources, OperationOutcome, etc.). For example,
* if the method is invoked with index 0,10 the method might return 10 search results, plus an
* if the method is invoked with index 0,10 the method might return 10 search results, plus an
* additional 20 resources which matched a client's _include specification.
*
* @param theFromIndex The low index (inclusive) to return
* @param theToIndex The high index (exclusive) to return
* @param theFromIndex
* The low index (inclusive) to return
* @param theToIndex
* The high index (exclusive) to return
* @return A list of resources. The size of this list must be at least <code>theToIndex - theFromIndex</code>.
*/
List<IBaseResource> getResources(int theFromIndex, int theToIndex);
/**
* Optionally may be used to signal a preferred page size to the server, e.g. because
* the implementing code recognizes that the resources which will be returned by this
@ -50,16 +55,31 @@ public interface IBundleProvider {
* @return Returns the preferred page size or <code>null</code>
*/
Integer preferredPageSize();
/**
* Returns the total number of results which match the given query (exclusive of any
* _include's or OperationOutcome)
*/
int size();
/**
* Returns the instant as of which this result was valid
*/
InstantDt getPublished();
IPrimitiveType<Date> getPublished();
/**
* Returns the UUID associated with this search. Note that this
* does not need to return a non-null value unless it a
* {@link IPagingProvider} is being used that requires UUIDs
* being returned.
* <p>
* In other words, if you are using the default {@link FifoMemoryPagingProvider} in
* your server, it is fine for this method to simply return {@code null} since {@link FifoMemoryPagingProvider}
* does not use the value anyhow. On the other hand, if you are creating a custom
* [@code IPagingProvider} implementation you might use this method to communicate
* the search ID back to the provider.
* </p>
*/
public String getUuid();
}

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.rest.server;
* 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
* 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,

View File

@ -65,5 +65,10 @@ public class SimpleBundleProvider implements IBundleProvider {
public Integer preferredPageSize() {
return null;
}
@Override
public String getUuid() {
return null;
}
}

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.util;
import java.util.Collection;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseDatatype;
import org.hl7.fhir.instance.model.api.IBaseParameters;
@ -84,6 +85,7 @@ public class ParametersUtil {
}
public static IBaseParameters newInstance(FhirContext theContext) {
Validate.notNull(theContext, "theContext must not be null");
return (IBaseParameters) theContext.getResourceDefinition("Parameters").newInstance();
}
}

View File

@ -67,6 +67,7 @@ ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionEntryHasInvalidVerb=Transac
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionMissingUrl=Unable to perform {0}, no URL provided.
ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao.transactionInvalidUrl=Unable to perform {0}, URL provided is invalid: {1}
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.cantValidateWithNoResource=No resource supplied for $validate operation (resource is required unless mode is \"delete\")
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.duplicateCreateForcedId=Can not create entity with ID[{0}], a resource with this ID already exists
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.failedToCreateWithInvalidId=Can not process entity with ID[{0}], this is not a valid FHIR ID
ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao.incorrectResourceType=Incorrect resource type detected for endpoint, found {0} but expected {1}

View File

@ -0,0 +1,19 @@
package ca.uhn.fhir.rest.param;
import static org.junit.Assert.*;
import org.junit.Test;
public class StringParamTest {
@Test
public void testEquals() {
StringParam input = new StringParam("foo", true);
assertTrue(input.equals(input));
assertFalse(input.equals(null));
assertFalse(input.equals(""));
assertFalse(input.equals(new StringParam("foo", false)));
}
}

View File

@ -101,6 +101,7 @@ public class BaseDstu2Config extends BaseConfig {
public ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2 systemProviderDstu2() {
ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2 retVal = new ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2();
retVal.setDao(systemDaoDstu2());
retVal.setContext(fhirContextDstu2());
return retVal;
}

View File

@ -11,7 +11,7 @@ import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
* 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
* 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,
@ -335,7 +335,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
continue;
}
}
Class<? extends IBaseResource> type = resourceDefinition.getImplementingClass();
String id = nextId.getIdPart();
if (StringUtils.isBlank(id)) {
@ -677,18 +677,19 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theRequestDetails.getUserData().put(PROCESSING_SUB_REQUEST, Boolean.TRUE);
}
}
protected void notifyInterceptors(RestOperationTypeEnum theOperationType, ActionRequestDetails theRequestDetails) {
if (theRequestDetails.getId() != null && theRequestDetails.getId().hasResourceType() && isNotBlank(theRequestDetails.getResourceType())) {
if (theRequestDetails.getId().getResourceType().equals(theRequestDetails.getResourceType()) == false) {
throw new InternalErrorException("Inconsistent server state - Resource types don't match: " + theRequestDetails.getId().getResourceType() + " / " + theRequestDetails.getResourceType());
throw new InternalErrorException(
"Inconsistent server state - Resource types don't match: " + theRequestDetails.getId().getResourceType() + " / " + theRequestDetails.getResourceType());
}
}
if (theRequestDetails.getUserData().get(PROCESSING_SUB_REQUEST) == Boolean.TRUE) {
theRequestDetails.notifyIncomingRequestPreHandled(theOperationType);
}
List<IServerInterceptor> interceptors = getConfig().getInterceptors();
if (interceptors == null) {
return;
@ -779,7 +780,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theEntity.setHasTags(true);
}
}
ArrayList<ResourceTag> existingTags = new ArrayList<ResourceTag>();
if (theEntity.isHasTags()) {
existingTags.addAll(theEntity.getTags());
@ -937,9 +938,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
* Subclasses may override to provide behaviour. Called when a resource has been inserted into the database for the first time.
*
* @param theEntity
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
* @param theTag
* The tag
* The tag
* @return Returns <code>true</code> if the tag should be removed
*/
@SuppressWarnings("unused")
@ -951,9 +952,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
* Subclasses may override to provide behaviour. Called when a pre-existing resource has been updated in the database
*
* @param theEntity
* The resource
* The resource
* @param theResource
* The resource being persisted
* The resource being persisted
*/
protected void postUpdate(ResourceTable theEntity, T theResource) {
// nothing
@ -1009,9 +1010,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
* </p>
*
* @param theEntity
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
* The entity being updated (Do not modify the entity! Undefined behaviour will occur!)
* @param theTag
* The tag
* The tag
* @return Retturns <code>true</code> if the tag should be removed
*/
protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) {
@ -1072,7 +1073,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
}
}
IParser parser = theEntity.getEncoding().newParser(getContext(theEntity.getFhirVersion()));
R retVal;
try {
@ -1241,7 +1242,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
/*
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the matching
* Handle references within the resource that are match URLs, for example references like "Patient?identifier=foo". These match URLs are resolved and replaced with the ID of the
* matching
* resource.
*/
if (myConfig.isAllowInlineMatchUrlReferences()) {
@ -1437,7 +1439,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
theEntity.setResourceLinks(links);
theEntity.toString();
} // if thePerformIndexing
theEntity = myEntityManager.merge(theEntity);
@ -1538,9 +1540,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
* "subsetted" tag and rejects resources which have it. Subclasses should call the superclass implementation to preserve this check.
*
* @param theResource
* The resource that is about to be persisted
* The resource that is about to be persisted
* @param theEntityToSave
* TODO
* TODO
*/
protected void validateResourceForStorage(T theResource, ResourceTable theEntityToSave) {
Object tag = null;
@ -1747,7 +1749,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
paramMap.add(nextParamName, param);
continue;
}
if (Constants.PARAM_COUNT.equals(nextParamName)) {
if (paramList.size() > 0 && paramList.get(0).size() > 0) {
String intString = paramList.get(0).get(0);
@ -1772,7 +1774,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
} else {
RuntimeSearchParam paramDef = theCallingDao.getSearchParamByName(resourceDef, nextParamName);
if (paramDef == null) {
throw new InvalidRequestException("Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
throw new InvalidRequestException(
"Failed to parse match URL[" + theMatchUrl + "] - Resource type " + resourceDef.getName() + " does not have a parameter with name: " + nextParamName);
}
IQueryParameterAnd<?> param = MethodUtil.parseQueryParams(theContext, paramDef, nextParamName, paramList);

View File

@ -22,29 +22,15 @@ package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import javax.annotation.PostConstruct;
import javax.persistence.NoResultException;
import javax.persistence.TypedQuery;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseCoding;
import org.hl7.fhir.instance.model.api.IBaseMetaType;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.instance.model.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.PlatformTransactionManager;
@ -59,42 +45,21 @@ import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
import ca.uhn.fhir.jpa.dao.data.IResourceTableDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.BaseTag;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.ResourceLink;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TagDefinition;
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.interceptor.IJpaServerInterceptor;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.util.DeleteConflict;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils;
import ca.uhn.fhir.model.api.IQueryParameterAnd;
import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.PatchTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.method.MethodUtil;
import ca.uhn.fhir.rest.method.QualifiedParamList;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
import ca.uhn.fhir.rest.method.SearchMethodBinding;
import ca.uhn.fhir.rest.method.*;
import ca.uhn.fhir.rest.method.SearchMethodBinding.QualifierDetails;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.exceptions.*;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.FhirTerser;
@ -110,14 +75,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
@Autowired
private DaoConfig myDaoConfig;
@Autowired
protected IResourceTableDao myResourceTableDao;
@Autowired
protected PlatformTransactionManager myPlatformTransactionManager;
@Autowired
private IResourceHistoryTableDao myResourceHistoryTableDao;
@Autowired()
protected IResourceIndexedSearchParamUriDao myResourceIndexedSearchParamUriDao;
private String myResourceName;
@Autowired
protected IResourceTableDao myResourceTableDao;
private Class<T> myResourceType;
@Autowired(required = false)
protected IFulltextSearchSvc mySearchDao;
@ -226,7 +191,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
T resourceToDelete = toResource(myResourceType, entity, false);
validateOkToDelete(deleteConflicts, entity);
preDelete(resourceToDelete);
// Notify interceptors
if (theRequestDetails != null) {
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getContext(), theId.getResourceType(), theId);
@ -259,10 +226,18 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
validateDeleteConflictsEmptyOrThrowException(deleteConflicts);
IBaseOperationOutcome oo = OperationOutcomeUtil.newInstance(getContext());
String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulDeletes", 1, w.getMillis());
String severity = "information";
String code = "informational";
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
ourLog.info("Processed delete on {} in {}ms", theId.getValue(), w.getMillisAndRestart());
return toMethodOutcome(savedEntity, null);
DaoMethodOutcome retVal = toMethodOutcome(savedEntity, null);
retVal.setOperationOutcome(oo);
return retVal;
}
@Override
public List<ResourceTable> deleteByUrl(String theUrl, List<DeleteConflict> deleteConflicts, RequestDetails theRequestDetails) {
Set<Long> resource = processMatchUrl(theUrl, myResourceType);
@ -301,7 +276,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return retVal;
}
@Override
public DaoMethodOutcome deleteByUrl(String theUrl, RequestDetails theRequestDetails) {
StopWatch w = new StopWatch();
@ -320,7 +295,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
} else {
oo = OperationOutcomeUtil.newInstance(getContext());
String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulDeletes", theUrl, deletedResources.size(), w.getMillis());
String message = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "successfulDeletes", deletedResources.size(), w.getMillis());
String severity = "information";
String code = "informational";
OperationOutcomeUtil.addIssue(getContext(), oo, severity, message, null, code);
@ -553,6 +528,38 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
return retVal;
}
@Override
public <MT extends IBaseMetaType> MT metaDeleteOperation(IIdType theResourceId, MT theMetaDel, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theResourceId);
notifyInterceptors(RestOperationTypeEnum.META_DELETE, requestDetails);
StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theResourceId);
if (entity == null) {
throw new ResourceNotFoundException(theResourceId);
}
ResourceTable latestVersion = readEntityLatestVersion(theResourceId);
if (latestVersion.getVersion() != entity.getVersion()) {
doMetaDelete(theMetaDel, entity);
} else {
doMetaDelete(theMetaDel, latestVersion);
// Also update history entry
ResourceHistoryTable history = myResourceHistoryTableDao.findForIdAndVersion(entity.getId(), entity.getVersion());
doMetaDelete(theMetaDel, history);
}
myEntityManager.flush();
ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[] { theResourceId.getValue(), w.getMillisAndRestart() });
@SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails);
return retVal;
}
// @Override
// public IBundleProvider everything(IIdType theId) {
// Search search = new Search();
@ -635,38 +642,6 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
// };
// }
@Override
public <MT extends IBaseMetaType> MT metaDeleteOperation(IIdType theResourceId, MT theMetaDel, RequestDetails theRequestDetails) {
// Notify interceptors
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, getResourceName(), theResourceId);
notifyInterceptors(RestOperationTypeEnum.META_DELETE, requestDetails);
StopWatch w = new StopWatch();
BaseHasResource entity = readEntity(theResourceId);
if (entity == null) {
throw new ResourceNotFoundException(theResourceId);
}
ResourceTable latestVersion = readEntityLatestVersion(theResourceId);
if (latestVersion.getVersion() != entity.getVersion()) {
doMetaDelete(theMetaDel, entity);
} else {
doMetaDelete(theMetaDel, latestVersion);
// Also update history entry
ResourceHistoryTable history = myResourceHistoryTableDao.findForIdAndVersion(entity.getId(), entity.getVersion());
doMetaDelete(theMetaDel, history);
}
myEntityManager.flush();
ourLog.info("Processed metaDeleteOperation on {} in {}ms", new Object[] { theResourceId.getValue(), w.getMillisAndRestart() });
@SuppressWarnings("unchecked")
MT retVal = (MT) metaGetOperation(theMetaDel.getClass(), theResourceId, theRequestDetails);
return retVal;
}
@Override
public <MT extends IBaseMetaType> MT metaGetOperation(Class<MT> theType, IIdType theId, RequestDetails theRequestDetails) {
// Notify interceptors
@ -743,6 +718,14 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
}
/**
* Subclasses may override to provide behaviour. Invoked within a delete
* transaction with the resource that is about to be deleted.
*/
protected void preDelete(T theResourceToDelete) {
// nothing by default
}
/**
* May be overridden by subclasses to validate resources prior to storage
*

View File

@ -27,6 +27,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantLock;
import javax.persistence.Query;
import javax.persistence.Tuple;
@ -64,15 +65,17 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirSystemDao.class);
@Autowired
private PlatformTransactionManager myTxManager;
@Autowired
private IForcedIdDao myForcedIdDao;
private ReentrantLock myReindexLock = new ReentrantLock(false);
@Autowired
private ITermConceptDao myTermConceptDao;
@Autowired
private PlatformTransactionManager myTxManager;
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void deleteAllTagsOnServer(RequestDetails theRequestDetails) {
@ -205,8 +208,14 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
@Transactional()
@Override
public int markAllResourcesForReindexing() {
ourLog.info("Marking all resources as needing reindexing");
int retVal = myEntityManager.createQuery("UPDATE " + ResourceTable.class.getSimpleName() + " t SET t.myIndexStatus = null").executeUpdate();
ourLog.info("Marking all concepts as needing reindexing");
retVal += myTermConceptDao.markAllForReindexing();
ourLog.info("Done marking reindexing");
return retVal;
}
@ -266,16 +275,21 @@ public abstract class BaseHapiFhirSystemDao<T, MT> extends BaseHapiFhirDao<IBase
}
});
}
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public int performReindexingPass(final Integer theCount) {
if (!myReindexLock.tryLock()) {
return 0;
}
try {
return doPerformReindexingPass(theCount);
} catch (ReindexFailureException e) {
ourLog.warn("Reindexing failed for resource {}", e.getResourceId());
markResourceAsIndexingFailed(e.getResourceId());
return -1;
} finally {
myReindexLock.unlock();
}
}

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.dao;
* 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
* 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,
@ -27,7 +27,6 @@ import java.util.Map;
import javax.annotation.PostConstruct;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.beans.factory.annotation.Autowired;
@ -37,8 +36,6 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
private static final Map<String, RuntimeSearchParam> EMPTY_SP_MAP = Collections.emptyMap();
private Map<String, Map<String, RuntimeSearchParam>> myBuiltInSearchParams;
@Autowired
@ -56,27 +53,32 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
// nothing by default
}
public Map<String, Map<String, RuntimeSearchParam>> getBuiltInSearchParams() {
@Override
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
return myBuiltInSearchParams;
}
@Override
public Map<String,RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
Validate.notBlank(theResourceName, "theResourceName must not be blank or null");
return myBuiltInSearchParams.get(theResourceName);
}
public Map<String, Map<String, RuntimeSearchParam>> getBuiltInSearchParams() {
return myBuiltInSearchParams;
}
@PostConstruct
public void postConstruct() {
Map<String, Map<String, RuntimeSearchParam>> resourceNameToSearchParams = new HashMap<String, Map<String,RuntimeSearchParam>>();
Map<String, Map<String, RuntimeSearchParam>> resourceNameToSearchParams = new HashMap<String, Map<String, RuntimeSearchParam>>();
for (IFhirResourceDao<?> nextDao : myDaos) {
RuntimeResourceDefinition nextResDef = myCtx.getResourceDefinition(nextDao.getResourceType());
String nextResourceName = nextResDef.getName();
HashMap<String, RuntimeSearchParam> nameToParam = new HashMap<String, RuntimeSearchParam>();
resourceNameToSearchParams.put(nextResourceName, nameToParam);
for (RuntimeSearchParam nextSp : nextResDef.getSearchParams()) {
nameToParam.put(nextSp.getName(), nextSp);
}
@ -85,13 +87,4 @@ public abstract class BaseSearchParamRegistry implements ISearchParamRegistry {
myBuiltInSearchParams = Collections.unmodifiableMap(resourceNameToSearchParams);
}
@Override
public Collection<RuntimeSearchParam> getAllSearchParams(String theResourceName) {
Validate.notBlank(theResourceName, "theResourceName must not be null or blank");
Map<String, RuntimeSearchParam> map = myBuiltInSearchParams.get(theResourceName);
map = ObjectUtils.defaultIfNull(map, EMPTY_SP_MAP);
return Collections.unmodifiableCollection(map.values());
}
}

View File

@ -56,6 +56,7 @@ import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.FhirTerser;
@ -133,8 +134,11 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
ValidationResult result;
if (isNotBlank(theRawResource)) {
result = validator.validateWithResult(theRawResource);
} else {
} else if (theResource != null) {
result = validator.validateWithResult(theResource);
} else {
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
throw new InvalidRequestException(msg);
}
if (result.isSuccessful()) {
@ -160,11 +164,11 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
if (myMode == ValidationModeEnum.CREATE) {
if (hasId) {
throw new InvalidRequestException("Resource has an ID - ID must not be populated for a FHIR create");
throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create");
}
} else if (myMode == ValidationModeEnum.UPDATE) {
if (hasId == false) {
throw new InvalidRequestException("Resource has no ID - ID must be populated for a FHIR update");
throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update");
}
}

View File

@ -492,7 +492,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao<Bundle, MetaDt> {
InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity());
updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, shouldUpdate, updateTime);
if (shouldUpdate) {
updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, true, updateTime);
}
}
myEntityManager.flush();

View File

@ -1,36 +1,15 @@
package ca.uhn.fhir.jpa.dao;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* 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.util.Collection;
import java.util.Map;
import ca.uhn.fhir.context.RuntimeSearchParam;
public interface ISearchParamRegistry {
Map<String,RuntimeSearchParam> getActiveSearchParams(String theResourceName);
Collection<RuntimeSearchParam> getAllSearchParams(String theResourceName);
void forceRefresh();
Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams();
Map<String,RuntimeSearchParam> getActiveSearchParams(String theResourceName);
}

View File

@ -2309,6 +2309,11 @@ public class SearchBuilder {
public int size() {
return myPids.size();
}
@Override
public String getUuid() {
return null;
}
}
}

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.jpa.dao.data;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* 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 org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamCoords;
public interface IResourceIndexedSearchParamCoordsDao extends JpaRepository<ResourceIndexedSearchParamCoords, Long> {
// nothing yet
}

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.jpa.dao.data;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* 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 org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
public interface IResourceIndexedSearchParamDateDao extends JpaRepository<ResourceIndexedSearchParamDate, Long> {
// nothing yet
}

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.jpa.dao.data;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* 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 org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
public interface IResourceIndexedSearchParamNumberDao extends JpaRepository<ResourceIndexedSearchParamNumber, Long> {
// nothing yet
}

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.jpa.dao.data;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* 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 org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamQuantity;
public interface IResourceIndexedSearchParamQuantityDao extends JpaRepository<ResourceIndexedSearchParamQuantity, Long> {
// nothing yet
}

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.jpa.dao.data;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* 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 org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
public interface IResourceIndexedSearchParamStringDao extends JpaRepository<ResourceIndexedSearchParamString, Long> {
// nothing yet
}

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.jpa.dao.data;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* 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 org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
public interface IResourceIndexedSearchParamTokenDao extends JpaRepository<ResourceIndexedSearchParamToken, Long> {
// nothing yet
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.dao.data;
import java.util.Collection;
/*
* #%L
* HAPI FHIR JPA Server
@ -32,5 +34,8 @@ public interface ITermConceptParentChildLinkDao extends JpaRepository<TermConcep
@Query("DELETE FROM TermConceptParentChildLink t WHERE t.myCodeSystem.myId = :cs_pid")
@Modifying
void deleteByCodeSystemVersion(@Param("cs_pid") Long thePid);
@Query("SELECT t FROM TermConceptParentChildLink t WHERE t.myChildPid = :child_pid")
Collection<TermConceptParentChildLink> findAllWithChild(@Param("child_pid") Long theConceptPid);
}

View File

@ -41,6 +41,7 @@ import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
@ -187,6 +188,9 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys
return retVal;
}
@Autowired
private ITermCodeSystemVersionDao myCsvDao;
@Override
protected ResourceTable updateEntity(IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime) {
@ -198,12 +202,22 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys
String codeSystemUrl = cs.getUrl();
if (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == null) {
ourLog.info("CodeSystem {} has a status of {}, going to store concepts in terminology tables", retVal.getIdDt().getValue(), cs.getContentElement().getValueAsString());
TermCodeSystemVersion persCs = new TermCodeSystemVersion();
persCs.setResource(retVal);
persCs.setResourceVersionId(retVal.getVersion());
persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs));
Long codeSystemResourcePid = retVal.getId();
TermCodeSystemVersion persCs = myCsvDao.findByCodeSystemResourceAndVersion(codeSystemResourcePid, retVal.getVersion());
if (persCs != null) {
ourLog.info("Code system version already exists in database");
} else {
persCs = new TermCodeSystemVersion();
persCs.setResource(retVal);
persCs.setResourceVersionId(retVal.getVersion());
persCs.getConcepts().addAll(toPersistedConcepts(cs.getConcept(), persCs));
ourLog.info("Code system has {} concepts", persCs.getConcepts().size());
myTerminologySvc.storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, persCs);
}
myTerminologySvc.storeNewCodeSystemVersion(retVal.getId(), codeSystemUrl, persCs);
}
}

View File

@ -54,6 +54,7 @@ import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
import ca.uhn.fhir.util.CoverageIgnore;
import ca.uhn.fhir.util.FhirTerser;
@ -143,7 +144,8 @@ public class FhirResourceDaoDstu3<T extends IAnyResource> extends BaseHapiFhirRe
if (resourceToValidateById != null) {
result = validator.validateWithResult(resourceToValidateById);
} else {
throw new InvalidRequestException("No resource supplied for $validate operation (resource is required unless mode is \"delete\")");
String msg = getContext().getLocalizer().getMessage(BaseHapiFhirResourceDao.class, "cantValidateWithNoResource");
throw new InvalidRequestException(msg);
}
} else if (isNotBlank(theRawResource)) {
result = validator.validateWithResult(theRawResource);
@ -180,11 +182,11 @@ public class FhirResourceDaoDstu3<T extends IAnyResource> extends BaseHapiFhirRe
boolean hasId = theCtx.getResource().getIdElement().hasIdPart();
if (myMode == ValidationModeEnum.CREATE) {
if (hasId) {
throw new InvalidRequestException("Resource has an ID - ID must not be populated for a FHIR create");
throw new UnprocessableEntityException("Resource has an ID - ID must not be populated for a FHIR create");
}
} else if (myMode == ValidationModeEnum.UPDATE) {
if (hasId == false) {
throw new InvalidRequestException("Resource has no ID - ID must be populated for a FHIR update");
throw new UnprocessableEntityException("Resource has no ID - ID must be populated for a FHIR update");
}
}

View File

@ -32,24 +32,32 @@ import org.springframework.scheduling.annotation.Scheduled;
import ca.uhn.fhir.jpa.dao.BaseSearchParamExtractor;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoSearchParameter;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.dao.ISearchParamRegistry;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.ElementUtil;
public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<SearchParameter> implements IFhirResourceDaoSearchParameter<SearchParameter> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoSearchParameterDstu3.class);
@Autowired
private ISearchParamRegistry mySearchParamRegistry;
@Autowired
private IFhirSystemDao<Bundle, Meta> mySystemDao;
private void markAffectedResources(SearchParameter theResource) {
String xpath = theResource.getXpath();
String resourceType = xpath.substring(0, xpath.indexOf('.'));
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", xpath);
int updatedCount = myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
ourLog.info("Marked {} resources for reindexing", updatedCount);
protected void markAffectedResources(SearchParameter theResource) {
if (theResource != null) {
String expression = theResource.getExpression();
String resourceType = expression.substring(0, expression.indexOf('.'));
ourLog.info("Marking all resources of type {} for reindexing due to updated search parameter with path: {}", expression);
int updatedCount = myResourceTableDao.markResourcesOfTypeAsRequiringReindexing(resourceType);
ourLog.info("Marked {} resources for reindexing", updatedCount);
}
mySearchParamRegistry.forceRefresh();
}
/**
@ -79,50 +87,67 @@ public class FhirResourceDaoSearchParameterDstu3 extends FhirResourceDaoDstu3<Se
@Override
protected void postPersist(ResourceTable theEntity, SearchParameter theResource) {
super.postPersist(theEntity, theResource);
markAffectedResources(theResource);
}
@Override
protected void postUpdate(ResourceTable theEntity, SearchParameter theResource) {
super.postUpdate(theEntity, theResource);
markAffectedResources(theResource);
}
@Override
protected void preDelete(SearchParameter theResourceToDelete) {
super.preDelete(theResourceToDelete);
markAffectedResources(theResourceToDelete);
}
@Override
protected void validateResourceForStorage(SearchParameter theResource, ResourceTable theEntityToSave) {
super.validateResourceForStorage(theResource, theEntityToSave);
if (theResource.getStatus() == null) {
throw new UnprocessableEntityException("Resource.status is missing or invalid: " + theResource.getStatusElement().getValueAsString());
throw new UnprocessableEntityException("SearchParameter.status is missing or invalid: " + theResource.getStatusElement().getValueAsString());
}
String xpath = theResource.getXpath();
if (isBlank(xpath)) {
throw new UnprocessableEntityException("Resource.xpath is missing");
String expression = theResource.getExpression();
if (isBlank(expression)) {
throw new UnprocessableEntityException("SearchParameter.expression is missing");
}
String[] xpathSplit = BaseSearchParamExtractor.SPLIT.split(xpath);
if (ElementUtil.isEmpty(theResource.getBase())) {
throw new UnprocessableEntityException("SearchParameter.base is missing");
}
expression = expression.trim();
theResource.setExpression(expression);
String[] expressionSplit = BaseSearchParamExtractor.SPLIT.split(expression);
String allResourceName = null;
for (String nextPath : xpathSplit) {
for (String nextPath : expressionSplit) {
nextPath = nextPath.trim();
int dotIdx = nextPath.indexOf('.');
if (dotIdx == -1) {
throw new UnprocessableEntityException("Invalid path value \"" + nextPath + "\". Must start with a resource name");
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". Must start with a resource name");
}
String resourceName = nextPath.substring(0, dotIdx);
try {
getContext().getResourceDefinition(resourceName);
} catch (DataFormatException e) {
throw new UnprocessableEntityException("Invalid path value \"" + nextPath + "\": " + e.getMessage());
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\": " + e.getMessage());
}
if (allResourceName == null) {
allResourceName = resourceName;
} else {
if (!allResourceName.equals(resourceName)) {
throw new UnprocessableEntityException("Invalid path value \"" + nextPath + "\". All paths in a single SearchParameter must match the same resource type");
throw new UnprocessableEntityException("Invalid SearchParameter.expression value \"" + nextPath + "\". All paths in a single SearchParameter must match the same resource type");
}
}
}
}

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
* 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
* 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,
@ -33,6 +33,7 @@ import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
import org.hl7.fhir.dstu3.model.IntegerType;
import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
@ -82,12 +83,12 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
return retVal;
// ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion();
// ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion();
//
// ValueSet retVal = new ValueSet();
// retVal.getMeta().setLastUpdated(new Date());
// retVal.setExpansion(expansion);
// return retVal;
// ValueSet retVal = new ValueSet();
// retVal.getMeta().setLastUpdated(new Date());
// retVal.setExpansion(expansion);
// return retVal;
}
private void validateIncludes(String name, List<ConceptSetComponent> listToValidate) {
@ -137,11 +138,11 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
public ValueSet expand(ValueSet source, String theFilter) {
ValueSet toExpand = new ValueSet();
// for (UriType next : source.getCompose().getInclude()) {
// ConceptSetComponent include = toExpand.getCompose().addInclude();
// include.setSystem(next.getValue());
// addFilterIfPresent(theFilter, include);
// }
// for (UriType next : source.getCompose().getInclude()) {
// ConceptSetComponent include = toExpand.getCompose().addInclude();
// include.setSystem(next.getValue());
// addFilterIfPresent(theFilter, include);
// }
for (ConceptSetComponent next : source.getCompose().getInclude()) {
toExpand.getCompose().addInclude(next);
@ -155,18 +156,41 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
toExpand.getCompose().getExclude().addAll(source.getCompose().getExclude());
ValueSet retVal = doExpand(toExpand);
if (isNotBlank(theFilter)) {
applyFilter(retVal.getExpansion().getTotalElement(), retVal.getExpansion().getContains(), theFilter);
}
return retVal;
}
private void applyFilter(IntegerType theTotalElement, List<ValueSetExpansionContainsComponent> theContains, String theFilter) {
for (int idx = 0; idx < theContains.size(); idx++) {
ValueSetExpansionContainsComponent next = theContains.get(idx);
if (isBlank(next.getDisplay()) || !org.apache.commons.lang3.StringUtils.containsIgnoreCase(next.getDisplay(), theFilter)) {
theContains.remove(idx);
idx--;
if (theTotalElement.getValue() != null) {
theTotalElement.setValue(theTotalElement.getValue() - 1);
}
}
applyFilter(theTotalElement, next.getContains(), theFilter);
}
}
private void addFilterIfPresent(String theFilter, ConceptSetComponent include) {
if (isNotBlank(theFilter)) {
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue(theFilter);
if (ElementUtil.isEmpty(include.getConcept())) {
if (isNotBlank(theFilter)) {
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue(theFilter);
}
}
}
@Override
public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding,
public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding,
CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
List<IIdType> valueSetIds = Collections.emptyList();
@ -226,7 +250,8 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
return thePrimitive != null ? thePrimitive.getValue() : null;
}
private ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCodeIsInContains(List<ValueSetExpansionContainsComponent> contains, String theSystem, String theCode, Coding theCoding, CodeableConcept theCodeableConcept) {
private ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCodeIsInContains(List<ValueSetExpansionContainsComponent> contains, String theSystem, String theCode,
Coding theCoding, CodeableConcept theCodeableConcept) {
for (ValueSetExpansionContainsComponent nextCode : contains) {
ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult result = validateCodeIsInContains(nextCode.getContains(), theSystem, theCode, theCoding, theCodeableConcept);
if (result != null) {

View File

@ -383,7 +383,8 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass());
res.setId((String) null);
DaoMethodOutcome outcome;
outcome = resourceDao.create(res, nextReqEntry.getRequest().getIfNoneExist(), false, theRequestDetails);
String matchUrl = nextReqEntry.getRequest().getIfNoneExist();
outcome = resourceDao.create(res, matchUrl, false, theRequestDetails);
handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res);
entriesToProcess.put(nextRespEntry, outcome.getEntity());
if (outcome.getCreated() == false) {
@ -489,7 +490,9 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
IPrimitiveType<Date> deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource);
Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null;
boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity());
updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, shouldUpdate, updateTime);
if (shouldUpdate) {
updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, true, updateTime);
}
}
myEntityManager.flush();

View File

@ -50,6 +50,7 @@ import org.hl7.fhir.dstu3.model.ContactPoint;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.Duration;
import org.hl7.fhir.dstu3.model.Enumeration;
import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.dstu3.model.HumanName;
import org.hl7.fhir.dstu3.model.Identifier;
import org.hl7.fhir.dstu3.model.IntegerType;
@ -520,7 +521,16 @@ public class SearchParamExtractorDstu3 extends BaseSearchParamExtractor implemen
// }
for (Object nextObject : extractValues(nextPath, theResource)) {
if (nextObject instanceof Extension) {
Extension nextExtension = (Extension)nextObject;
nextObject = nextExtension.getValue();
}
if (nextObject == null) {
continue;
}
// Patient:language
if (nextObject instanceof PatientCommunicationComponent) {
PatientCommunicationComponent nextValue = (PatientCommunicationComponent) nextObject;

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
* 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
* 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,
@ -35,6 +35,7 @@ import org.apache.commons.lang3.time.DateUtils;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.SearchParameter;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.context.RuntimeSearchParam;
@ -50,19 +51,43 @@ import ca.uhn.fhir.rest.server.IBundleProvider;
public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchParamRegistryDstu3.class);
@Autowired
private IFhirResourceDao<SearchParameter> mySpDao;
private long myLastRefresh;
private volatile Map<String, Map<String, RuntimeSearchParam>> myActiveSearchParams;
@Autowired
private DaoConfig myDaoConfig;
private long myLastRefresh;
@Autowired
private IFhirResourceDao<SearchParameter> mySpDao;
@Override
public void forceRefresh() {
myLastRefresh = 0;
}
@Override
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
refreshCacheIfNeccesary();
return myActiveSearchParams;
}
@Override
public Map<String, RuntimeSearchParam> getActiveSearchParams(String theResourceName) {
refreshCacheIfNeccesary();
return myActiveSearchParams.get(theResourceName);
}
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
if (retVal == null) {
retVal = new HashMap<String, RuntimeSearchParam>();
searchParams.put(theResourceName, retVal);
}
return retVal;
}
private void refreshCacheIfNeccesary() {
long refreshInterval = 60 * DateUtils.MILLIS_PER_MINUTE;
if (System.currentTimeMillis() - refreshInterval > myLastRefresh) {
StopWatch sw = new StopWatch();
@ -91,7 +116,7 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
if (runtimeSp == null) {
continue;
}
int dotIdx = runtimeSp.getPath().indexOf('.');
if (dotIdx == -1) {
ourLog.warn("Can not determine resource type of {}", runtimeSp.getPath());
@ -113,14 +138,14 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
if (nextSp.getStatus() != RuntimeSearchParamStatusEnum.ACTIVE) {
nextSp = null;
}
if (!activeSearchParams.containsKey(nextEntry.getKey())) {
activeSearchParams.put(nextEntry.getKey(), new HashMap<String, RuntimeSearchParam>());
}
if (activeSearchParams.containsKey(nextEntry.getKey())) {
ourLog.info("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName);
ourLog.debug("Replacing existing/built in search param {}:{} with new one", nextEntry.getKey(), nextName);
}
if (nextSp != null) {
activeSearchParams.get(nextEntry.getKey()).put(nextName, nextSp);
} else {
@ -134,28 +159,12 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
myLastRefresh = System.currentTimeMillis();
ourLog.info("Refreshed search parameter cache in {}ms", sw.getMillis());
}
return myActiveSearchParams.get(theResourceName);
}
@Override
public void forceRefresh() {
myLastRefresh = 0;
}
private Map<String, RuntimeSearchParam> getSearchParamMap(Map<String, Map<String, RuntimeSearchParam>> searchParams, String theResourceName) {
Map<String, RuntimeSearchParam> retVal = searchParams.get(theResourceName);
if (retVal == null) {
retVal = new HashMap<String, RuntimeSearchParam>();
searchParams.put(theResourceName, retVal);
}
return retVal;
}
private RuntimeSearchParam toRuntimeSp(SearchParameter theNextSp) {
String name = theNextSp.getCode();
String description = theNextSp.getDescription();
String path = theNextSp.getXpath();
String path = theNextSp.getExpression();
RestSearchParameterTypeEnum paramType = null;
RuntimeSearchParamStatusEnum status = null;
switch (theNextSp.getType()) {
@ -208,7 +217,9 @@ public class SearchParamRegistryDstu3 extends BaseSearchParamRegistry {
return null;
}
RuntimeSearchParam retVal = new RuntimeSearchParam(name, description, path, paramType, providesMembershipInCompartments, targets, status);
IIdType id = theNextSp.getIdElement();
String uri = "";
RuntimeSearchParam retVal = new RuntimeSearchParam(id, uri, name, description, path, paramType, null, providesMembershipInCompartments, targets, status);
return retVal;
}

View File

@ -70,6 +70,10 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable {
return myResourcePid;
}
public String getResourceType() {
return myResourceType;
}
public void setParamName(String theName) {
myParamName = theName;
}

View File

@ -233,12 +233,14 @@ public class TermConcept implements Serializable {
@PreUpdate
@PrePersist
public void prePersist() {
Set<Long> parentPids = new HashSet<Long>();
TermConcept entity = this;
parentPids(entity, parentPids);
entity.setParentPids(parentPids);
ourLog.trace("Code {}/{} has parents {}", entity.getId(), entity.getCode(), entity.getParentPidsAsString());
if (myParentPids == null) {
Set<Long> parentPids = new HashSet<Long>();
TermConcept entity = this;
parentPids(entity, parentPids);
entity.setParentPids(parentPids);
ourLog.trace("Code {}/{} has parents {}", entity.getId(), entity.getCode(), entity.getParentPidsAsString());
}
}
public void setCode(String theCode) {
@ -280,6 +282,10 @@ public class TermConcept implements Serializable {
myParentPids = b.toString();
}
public void setParentPids(String theParentPids) {
myParentPids = theParentPids;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).append("code", myCode).append("display", myDisplay).build();

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.entity;
* 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
* 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,
@ -36,77 +36,38 @@ import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
@Entity
@Table(name="TRM_CONCEPT_PC_LINK")
@Table(name = "TRM_CONCEPT_PC_LINK")
public class TermConceptParentChildLink implements Serializable {
private static final long serialVersionUID = 1L;
@ManyToOne()
@JoinColumn(name="CHILD_PID", nullable=false, referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_TERM_CONCEPTPC_CHILD"))
@JoinColumn(name = "CHILD_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CHILD"))
private TermConcept myChild;
@Column(name = "CHILD_PID", insertable = false, updatable = false)
private Long myChildPid;
@ManyToOne()
@JoinColumn(name="CODESYSTEM_PID", nullable=false, foreignKey=@ForeignKey(name="FK_TERM_CONCEPTPC_CS"))
@JoinColumn(name = "CODESYSTEM_PID", nullable = false, foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_CS"))
private TermCodeSystemVersion myCodeSystem;
@ManyToOne(cascade= {})
@JoinColumn(name="PARENT_PID", nullable=false, referencedColumnName="PID", foreignKey=@ForeignKey(name="FK_TERM_CONCEPTPC_PARENT"))
@ManyToOne(cascade = {})
@JoinColumn(name = "PARENT_PID", nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_TERM_CONCEPTPC_PARENT"))
private TermConcept myParent;
@Column(name = "PARENT_PID", insertable = false, updatable = false)
private Long myParentPid;
@Id()
@SequenceGenerator(name="SEQ_CONCEPT_PC_PID", sequenceName="SEQ_CONCEPT_PC_PID")
@GeneratedValue(strategy=GenerationType.AUTO, generator="SEQ_CONCEPT_PC_PID")
@Column(name="PID")
@SequenceGenerator(name = "SEQ_CONCEPT_PC_PID", sequenceName = "SEQ_CONCEPT_PC_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PC_PID")
@Column(name = "PID")
private Long myPid;
@Enumerated(EnumType.ORDINAL)
@Column(name="REL_TYPE", length=5, nullable=true)
@Column(name = "REL_TYPE", length = 5, nullable = true)
private RelationshipTypeEnum myRelationshipType;
public TermConcept getChild() {
return myChild;
}
public RelationshipTypeEnum getRelationshipType() {
return myRelationshipType;
}
public TermCodeSystemVersion getCodeSystem() {
return myCodeSystem;
}
public TermConcept getParent() {
return myParent;
}
public void setChild(TermConcept theChild) {
myChild = theChild;
}
public void setCodeSystem(TermCodeSystemVersion theCodeSystem) {
myCodeSystem = theCodeSystem;
}
public void setParent(TermConcept theParent) {
myParent = theParent;
}
public void setRelationshipType(RelationshipTypeEnum theRelationshipType) {
myRelationshipType = theRelationshipType;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((myChild == null) ? 0 : myChild.hashCode());
result = prime * result + ((myCodeSystem == null) ? 0 : myCodeSystem.hashCode());
result = prime * result + ((myParent == null) ? 0 : myParent.hashCode());
result = prime * result + ((myRelationshipType == null) ? 0 : myRelationshipType.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
@ -136,15 +97,64 @@ public class TermConceptParentChildLink implements Serializable {
return true;
}
public enum RelationshipTypeEnum{
// ********************************************
// IF YOU ADD HERE MAKE SURE ORDER IS PRESERVED
ISA
public TermConcept getChild() {
return myChild;
}
public Long getChildPid() {
return myChildPid;
}
public TermCodeSystemVersion getCodeSystem() {
return myCodeSystem;
}
public Long getId() {
return myPid;
}
public TermConcept getParent() {
return myParent;
}
public Long getParentPid() {
return myParentPid;
}
public RelationshipTypeEnum getRelationshipType() {
return myRelationshipType;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((myChild == null) ? 0 : myChild.hashCode());
result = prime * result + ((myCodeSystem == null) ? 0 : myCodeSystem.hashCode());
result = prime * result + ((myParent == null) ? 0 : myParent.hashCode());
result = prime * result + ((myRelationshipType == null) ? 0 : myRelationshipType.hashCode());
return result;
}
public void setChild(TermConcept theChild) {
myChild = theChild;
}
public void setCodeSystem(TermCodeSystemVersion theCodeSystem) {
myCodeSystem = theCodeSystem;
}
public void setParent(TermConcept theParent) {
myParent = theParent;
}
public void setRelationshipType(RelationshipTypeEnum theRelationshipType) {
myRelationshipType = theRelationshipType;
}
public enum RelationshipTypeEnum {
// ********************************************
// IF YOU ADD HERE MAKE SURE ORDER IS PRESERVED
ISA
}
}

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.search;
import javax.persistence.EntityManager;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
@ -30,10 +31,12 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IDao;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.rest.server.BasePagingProvider;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
public class DatabaseBackedPagingProvider extends FifoMemoryPagingProvider {
public class DatabaseBackedPagingProvider extends BasePagingProvider implements IPagingProvider {
@Autowired
private PlatformTransactionManager thePlatformTransactionManager;
@ -46,29 +49,36 @@ public class DatabaseBackedPagingProvider extends FifoMemoryPagingProvider {
@Autowired
private IFhirSystemDao<?, ?> theDao;
/**
* Constructor
* @deprecated Use {@link DatabaseBackedPagingProvider} as this constructor has no purpose
*/
@Deprecated
public DatabaseBackedPagingProvider(int theSize) {
super(theSize);
this();
}
/**
* Constructor
*/
public DatabaseBackedPagingProvider() {
super();
}
@Override
public synchronized IBundleProvider retrieveResultList(String theId) {
IBundleProvider retVal = super.retrieveResultList(theId);
if (retVal == null) {
PersistedJpaBundleProvider provider = new PersistedJpaBundleProvider(theId, theDao);
if (!provider.ensureSearchEntityLoaded()) {
return null;
}
retVal = provider;
PersistedJpaBundleProvider provider = new PersistedJpaBundleProvider(theId, theDao);
if (!provider.ensureSearchEntityLoaded()) {
return null;
}
return retVal;
return provider;
}
@Override
public synchronized String storeResultList(IBundleProvider theList) {
if (theList instanceof PersistedJpaBundleProvider) {
return ((PersistedJpaBundleProvider)theList).getSearchUuid();
}
return super.storeResultList(theList);
String uuid = theList.getUuid();
Validate.notNull(uuid);
return uuid;
}
}

View File

@ -19,13 +19,7 @@ package ca.uhn.fhir.jpa.search;
* limitations under the License.
* #L%
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
@ -49,11 +43,7 @@ import ca.uhn.fhir.jpa.dao.IDao;
import ca.uhn.fhir.jpa.dao.SearchBuilder;
import ca.uhn.fhir.jpa.dao.data.ISearchDao;
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.entity.SearchResult;
import ca.uhn.fhir.jpa.entity.SearchTypeEnum;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.server.IBundleProvider;
@ -220,7 +210,7 @@ public final class PersistedJpaBundleProvider implements IBundleProvider {
});
}
public String getSearchUuid() {
public String getUuid() {
return myUuid;
}

View File

@ -23,9 +23,11 @@ package ca.uhn.fhir.jpa.term;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@ -33,6 +35,7 @@ import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
@ -45,7 +48,10 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimaps;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
@ -66,6 +72,7 @@ import ca.uhn.fhir.util.ObjectUtil;
import ca.uhn.fhir.util.ValidateUtil;
public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
private static boolean ourForceSaveDeferredAlwaysForUnitTest;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiTerminologySvc.class);
private static final Object PLACEHOLDER_OBJECT = new Object();
@ -94,8 +101,11 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
protected EntityManager myEntityManager;
private boolean myProcessDeferred = true;
private long myNextReindexPass;
private boolean myProcessDeferred = true;
@Autowired
private PlatformTransactionManager myTransactionMgr;
private boolean addToSet(Set<TermConcept> theSetToPopulate, TermConcept theConcept) {
boolean retVal = theSetToPopulate.add(theConcept);
@ -108,6 +118,25 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal;
}
private int ensureParentsSaved(Collection<TermConceptParentChildLink> theParents) {
ourLog.trace("Checking {} parents", theParents.size());
int retVal = 0;
for (TermConceptParentChildLink nextLink : theParents) {
if (nextLink.getRelationshipType() == RelationshipTypeEnum.ISA) {
TermConcept nextParent = nextLink.getParent();
retVal += ensureParentsSaved(nextParent.getParents());
if (nextParent.getId() == null) {
myConceptDao.saveAndFlush(nextParent);
retVal++;
ourLog.debug("Saved parent code {} and got id {}", nextParent.getCode(), nextParent.getId());
}
}
}
return retVal;
}
private void fetchChildren(TermConcept theConcept, Set<TermConcept> theSetToPopulate) {
for (TermConceptParentChildLink nextChildLink : theConcept.getChildren()) {
TermConcept nextChild = nextChildLink.getChild();
@ -175,6 +204,15 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal;
}
/**
* Subclasses may override
* @param theSystem The code system
* @param theCode The code
*/
protected List<VersionIndependentConcept> findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) {
return Collections.emptyList();
}
@Transactional(propagation = Propagation.REQUIRED)
@Override
public Set<TermConcept> findCodesBelow(Long theCodeSystemResourcePid, Long theCodeSystemVersionPid, String theCode) {
@ -206,7 +244,6 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
ArrayList<VersionIndependentConcept> retVal = toVersionIndependentConcepts(theSystem, codes);
return retVal;
}
/**
* Subclasses may override
* @param theSystem The code system
@ -215,16 +252,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
protected List<VersionIndependentConcept> findCodesBelowUsingBuiltInSystems(String theSystem, String theCode) {
return Collections.emptyList();
}
/**
* Subclasses may override
* @param theSystem The code system
* @param theCode The code
*/
protected List<VersionIndependentConcept> findCodesAboveUsingBuiltInSystems(String theSystem, String theCode) {
return Collections.emptyList();
}
private TermCodeSystemVersion findCurrentCodeSystemVersionForSystem(String theCodeSystem) {
TermCodeSystem cs = getCodeSystem(theCodeSystem);
if (cs == null || cs.getCurrentVersion() == null) {
@ -233,11 +261,12 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
TermCodeSystemVersion csv = cs.getCurrentVersion();
return csv;
}
private TermCodeSystem getCodeSystem(String theSystem) {
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theSystem);
return cs;
}
private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack, int theTotalConcepts) {
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
return;
@ -271,15 +300,99 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
}
private void saveConceptLink(TermConceptParentChildLink next) {
if (next.getId() == null) {
myConceptParentChildLinkDao.save(next);
private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) {
if (theNext.getCodeSystem() != null) {
return;
}
theNext.setCodeSystem(theCodeSystemVersion);
for (TermConceptParentChildLink next : theNext.getChildren()) {
populateVersion(next.getChild(), theCodeSystemVersion);
}
}
private ArrayListMultimap<Long, Long> myChildToParentPidCache;
private void processReindexing() {
if (System.currentTimeMillis() < myNextReindexPass && !ourForceSaveDeferredAlwaysForUnitTest) {
return;
}
TransactionTemplate tt = new TransactionTemplate(myTransactionMgr);
tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
int maxResult = 1000;
Page<TermConcept> concepts = myConceptDao.findResourcesRequiringReindexing(new PageRequest(0, maxResult));
if (concepts.hasContent() == false) {
myNextReindexPass = System.currentTimeMillis() + DateUtils.MILLIS_PER_MINUTE;
myChildToParentPidCache = null;
return;
}
if (myChildToParentPidCache == null) {
myChildToParentPidCache = ArrayListMultimap.create();
}
ourLog.info("Indexing {} / {} concepts", concepts.getContent().size(), concepts.getTotalElements());
int count = 0;
StopWatch stopwatch = new StopWatch();
for (TermConcept nextConcept : concepts) {
StringBuilder parentsBuilder = new StringBuilder();
createParentsString(parentsBuilder, nextConcept.getId());
nextConcept.setParentPids(parentsBuilder.toString());
saveConcept(nextConcept);
count++;
}
ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", new Object[] { count, concepts.getContent().size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(count) });
}
private void createParentsString(StringBuilder theParentsBuilder, Long theConceptPid) {
Validate.notNull(theConceptPid, "theConceptPid must not be null");
List<Long> parents = myChildToParentPidCache.get(theConceptPid);
if (parents.contains(-1L)) {
return;
} else if (parents.isEmpty()) {
Collection<TermConceptParentChildLink> parentLinks = myConceptParentChildLinkDao.findAllWithChild(theConceptPid);
if (parentLinks.isEmpty()) {
myChildToParentPidCache.put(theConceptPid, -1L);
return;
} else {
for (TermConceptParentChildLink next : parentLinks) {
myChildToParentPidCache.put(theConceptPid, next.getParentPid());
}
}
}
for (Long nextParent : parents) {
if (theParentsBuilder.length() > 0) {
theParentsBuilder.append(' ');
}
theParentsBuilder.append(nextParent);
createParentsString(theParentsBuilder, nextParent);
}
}
});
}
private int saveConcept(TermConcept theConcept) {
int retVal = 0;
retVal += ensureParentsSaved(theConcept.getParents());
/*
* If the concept has an ID, we're reindexing, so there's no need to
* save parent concepts first (it's way too slow to do that)
*/
if (theConcept.getId() == null) {
retVal += ensureParentsSaved(theConcept.getParents());
}
if (theConcept.getId() == null || theConcept.getIndexStatus() == null) {
retVal++;
theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
@ -289,33 +402,10 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
ourLog.trace("Saved {} and got PID {}", theConcept.getCode(), theConcept.getId());
return retVal;
}
private int ensureParentsSaved(Collection<TermConceptParentChildLink> theParents) {
ourLog.trace("Checking {} parents", theParents.size());
int retVal = 0;
for (TermConceptParentChildLink nextLink : theParents) {
if (nextLink.getRelationshipType() == RelationshipTypeEnum.ISA) {
TermConcept nextParent = nextLink.getParent();
retVal += ensureParentsSaved(nextParent.getParents());
if (nextParent.getId() == null) {
myConceptDao.saveAndFlush(nextParent);
retVal++;
ourLog.debug("Saved parent code {} and got id {}", nextParent.getCode(), nextParent.getId());
}
}
}
return retVal;
}
private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) {
if (theNext.getCodeSystem() != null) {
return;
}
theNext.setCodeSystem(theCodeSystemVersion);
for (TermConceptParentChildLink next : theNext.getChildren()) {
populateVersion(next.getChild(), theCodeSystemVersion);
private void saveConceptLink(TermConceptParentChildLink next) {
if (next.getId() == null) {
myConceptParentChildLinkDao.save(next);
}
}
@ -368,43 +458,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
ourLog.info("All deferred concepts and relationships have now been synchronized to the database");
}
}
@Autowired
private PlatformTransactionManager myTransactionMgr;
private void processReindexing() {
if (System.currentTimeMillis() < myNextReindexPass) {
return;
}
TransactionTemplate tt = new TransactionTemplate(myTransactionMgr);
tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
tt.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
int maxResult = 1000;
Page<TermConcept> resources = myConceptDao.findResourcesRequiringReindexing(new PageRequest(0, maxResult));
if (resources.hasContent() == false) {
myNextReindexPass = System.currentTimeMillis() + DateUtils.MILLIS_PER_MINUTE;
return;
}
ourLog.info("Indexing {} / {} concepts", resources.getContent().size(), resources.getTotalElements());
int count = 0;
StopWatch stopwatch = new StopWatch();
for (TermConcept resourceTable : resources) {
saveConcept(resourceTable);
count++;
}
ourLog.info("Indexed {} / {} concepts in {}ms - Avg {}ms / resource", new Object[] { count, resources.getContent().size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(count) });
}
});
}
@Override
public void setProcessDeferred(boolean theProcessDeferred) {
myProcessDeferred = theProcessDeferred;
@ -470,7 +524,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
totalCodeCount += validateConceptForStorage(next, theCodeSystemVersion, conceptsStack, allConcepts);
}
ourLog.info("Saving version");
ourLog.info("Saving version containing {} concepts", totalCodeCount);
TermCodeSystemVersion codeSystemVersion = myCodeSystemVersionDao.saveAndFlush(theCodeSystemVersion);
@ -503,13 +557,13 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
ourLog.info("Note that some concept saving was deferred - still have {} concepts and {} relationships", myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size());
}
}
@Override
public boolean supportsSystem(String theSystem) {
TermCodeSystem cs = getCodeSystem(theSystem);
return cs != null;
}
private ArrayList<VersionIndependentConcept> toVersionIndependentConcepts(String theSystem, Set<TermConcept> codes) {
ArrayList<VersionIndependentConcept> retVal = new ArrayList<VersionIndependentConcept>(codes.size());
for (TermConcept next : codes) {
@ -547,4 +601,12 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
return retVal;
}
/**
* This method is present only for unit tests, do not call from client code
*/
@VisibleForTesting
public static void setForceSaveDeferredAlwaysForUnitTest(boolean theForceSaveDeferredAlwaysForUnitTest) {
ourForceSaveDeferredAlwaysForUnitTest = theForceSaveDeferredAlwaysForUnitTest;
}
}

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.term;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
@ -12,7 +13,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
* 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
* 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,
@ -64,6 +65,7 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.rest.method.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
@ -79,7 +81,7 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
@Autowired
private IValidationSupport myValidationSupport;
private void addCodeIfNotAlreadyAdded(String system, ValueSetExpansionComponent retVal, Set<String> addedCodes, TermConcept nextConcept) {
if (addedCodes.add(nextConcept.getCode())) {
ValueSetExpansionContainsComponent contains = retVal.addContains();
@ -126,12 +128,12 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
for (ConceptDefinitionComponent nextChild : theNext.getConcept()) {
foundCodeInChild |= addTreeIfItContainsCode(theSystemString, nextChild, theCode, theListToPopulate);
}
if (theCode.equals(theNext.getCode()) || foundCodeInChild) {
theListToPopulate.add(new VersionIndependentConcept(theSystemString, theNext.getCode()));
return true;
}
return false;
}
@ -159,17 +161,15 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
}
private void addDisplayFilterInexact(QueryBuilder qb, BooleanJunction<?> bool, ConceptSetFilterComponent nextFilter) {
//@formatter:off
Query textQuery = qb
.phrase()
.withSlop(2)
.onField("myDisplay").boostedTo(4.0f)
.andField("myDisplayEdgeNGram").boostedTo(2.0f)
//.andField("myDisplayNGram").boostedTo(1.0f)
//.andField("myDisplayPhonetic").boostedTo(0.5f)
// .andField("myDisplayNGram").boostedTo(1.0f)
// .andField("myDisplayPhonetic").boostedTo(0.5f)
.sentence(nextFilter.getValue().toLowerCase()).createQuery();
bool.must(textQuery);
//@formatter:on
}
@Override
@ -216,26 +216,33 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
for (ConceptSetFilterComponent nextFilter : theInclude.getFilter()) {
if (isNotBlank(nextFilter.getValue())) {
if (nextFilter.getProperty().equals("display:exact") && nextFilter.getOp() == FilterOperator.EQUAL) {
addDisplayFilterExact(qb, bool, nextFilter);
} else if (nextFilter.getProperty().equals("display") && nextFilter.getOp() == FilterOperator.EQUAL) {
if (nextFilter.getValue().trim().contains(" ")) {
addDisplayFilterExact(qb, bool, nextFilter);
} else {
addDisplayFilterInexact(qb, bool, nextFilter);
}
} else if ((nextFilter.getProperty().equals("concept") || nextFilter.getProperty().equals("code")) && nextFilter.getOp() == FilterOperator.ISA) {
TermConcept code = super.findCode(system, nextFilter.getValue());
if (code == null) {
throw new InvalidRequestException("Invalid filter criteria - code does not exist: {" + system + "}" + nextFilter.getValue());
}
if (isBlank(nextFilter.getValue()) && nextFilter.getOp() == null && isBlank(nextFilter.getProperty())) {
continue;
}
ourLog.info(" * Filtering on codes with a parent of {}/{}/{}", code.getId(), code.getCode(), code.getDisplay());
bool.must(qb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery());
if (isBlank(nextFilter.getValue()) || nextFilter.getOp() == null || isBlank(nextFilter.getProperty())) {
throw new InvalidRequestException("Invalid filter, must have fields populated: property op value");
}
if (nextFilter.getProperty().equals("display:exact") && nextFilter.getOp() == FilterOperator.EQUAL) {
addDisplayFilterExact(qb, bool, nextFilter);
} else if ("display".equals(nextFilter.getProperty()) && nextFilter.getOp() == FilterOperator.EQUAL) {
if (nextFilter.getValue().trim().contains(" ")) {
addDisplayFilterExact(qb, bool, nextFilter);
} else {
throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]");
addDisplayFilterInexact(qb, bool, nextFilter);
}
} else if ((nextFilter.getProperty().equals("concept") || nextFilter.getProperty().equals("code")) && nextFilter.getOp() == FilterOperator.ISA) {
TermConcept code = super.findCode(system, nextFilter.getValue());
if (code == null) {
throw new InvalidRequestException("Invalid filter criteria - code does not exist: {" + system + "}" + nextFilter.getValue());
}
ourLog.info(" * Filtering on codes with a parent of {}/{}/{}", code.getId(), code.getCode(), code.getDisplay());
bool.must(qb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery());
} else {
throw new InvalidRequestException("Unknown filter property[" + nextFilter + "] + op[" + nextFilter.getOpElement().getValueAsString() + "]");
}
}
@ -243,12 +250,17 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class);
jpaQuery.setMaxResults(1000);
StopWatch sw = new StopWatch();
@SuppressWarnings("unchecked")
List<TermConcept> result = jpaQuery.getResultList();
ourLog.info("Expansion completed in {}ms", sw.getMillis());
for (TermConcept nextConcept : result) {
addCodeIfNotAlreadyAdded(system, retVal, addedCodes, nextConcept);
}
retVal.setTotal(jpaQuery.getResultSize());
}
@ -259,7 +271,6 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
}
}
return retVal;
}

View File

@ -32,6 +32,7 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
@ -161,7 +162,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.CREATE, null, mySrd);
fail();
} catch (InvalidRequestException e) {
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must not be populated"));
}
@ -184,7 +185,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
fail();
} catch (InvalidRequestException e) {
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must be populated"));
}
@ -207,7 +208,7 @@ public class FhirResourceDaoDstu2ValidateTest extends BaseJpaDstu2Test {
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
fail();
} catch (InvalidRequestException e) {
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must be populated"));
}

View File

@ -19,10 +19,12 @@ import static org.mockito.Mockito.verify;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Test;
@ -73,6 +75,43 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2Test.class);
/**
* Per a message on the mailing list
*/
@Test
public void testTransactionWithPostDoesntUpdate() throws Exception {
// First bundle (name is Joshua)
String input = IOUtils.toString(getClass().getResource("/dstu3-post1.xml"), StandardCharsets.UTF_8);
Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
Bundle response = mySystemDao.transaction(mySrd, request);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
assertEquals(1, response.getEntry().size());
assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus());
assertEquals("1", response.getEntry().get(0).getResponse().getEtag());
String id = response.getEntry().get(0).getResponse().getLocation();
// Now the second (name is Adam, shouldn't get used)
input = IOUtils.toString(getClass().getResource("/dstu3-post2.xml"), StandardCharsets.UTF_8);
request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
response = mySystemDao.transaction(mySrd, request);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
assertEquals(1, response.getEntry().size());
assertEquals("200 OK", response.getEntry().get(0).getResponse().getStatus());
assertEquals("1", response.getEntry().get(0).getResponse().getEtag());
String id2 = response.getEntry().get(0).getResponse().getLocation();
assertEquals(id, id2);
Patient patient = myPatientDao.read(new IdType(id), mySrd);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient));
assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString());
}
@Test
public void testReindexing() {
Patient p = new Patient();

View File

@ -0,0 +1,44 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import static org.junit.Assert.assertNotEquals;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.CodeSystem;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDstu3CodeSystemTest extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3CodeSystemTest.class);
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(false);
}
@Test
public void testIndexContained() throws Exception {
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(true);
String input = IOUtils.toString(getClass().getResource("/dstu3_codesystem_complete.json"), StandardCharsets.UTF_8);
CodeSystem cs = myFhirCtx.newJsonParser().parseResource(CodeSystem.class, input);
myCodeSystemDao.create(cs, mySrd);
mySystemDao.markAllResourcesForReindexing();
int outcome = mySystemDao.performReindexingPass(100);
assertNotEquals(-1, outcome); // -1 means there was a failure
myTermSvc.saveDeferred();
}
}

View File

@ -1,8 +1,5 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.assertThat;
import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Reference;
@ -11,9 +8,6 @@ import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDstu3ContainedTest extends BaseJpaDstu3Test {

View File

@ -8,9 +8,11 @@ import static org.junit.Assert.fail;
import java.util.List;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.SearchParameter;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Test;
@ -27,40 +29,60 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
@Test
public void testCreateInvalidParamInvalidResourceName() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("PatientFoo.gender");
fooSp.setExpression("PatientFoo.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Invalid path value \"PatientFoo.gender\": Unknown resource name \"PatientFoo\" (this name is not known in FHIR version \"DSTU3\")", e.getMessage());
assertEquals("Invalid SearchParameter.expression value \"PatientFoo.gender\": Unknown resource name \"PatientFoo\" (this name is not known in FHIR version \"DSTU3\")", e.getMessage());
}
}
@Test
public void testCreateInvalidNoBase() {
SearchParameter fooSp = new SearchParameter();
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("SearchParameter.base is missing", e.getMessage());
}
}
@Test
public void testCreateInvalidParamMismatchedResourceName() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender or Observation.code");
fooSp.setExpression("Patient.gender or Observation.code");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Invalid path value \"Observation.code\". All paths in a single SearchParameter must match the same resource type", e.getMessage());
assertEquals("Invalid SearchParameter.expression value \"Observation.code\". All paths in a single SearchParameter must match the same resource type", e.getMessage());
}
}
@Test
public void testCreateInvalidParamNoPath() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
@ -70,24 +92,25 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Resource.xpath is missing", e.getMessage());
assertEquals("SearchParameter.expression is missing", e.getMessage());
}
}
@Test
public void testCreateInvalidParamNoResourceName() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("gender");
fooSp.setExpression("gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Invalid path value \"gender\". Must start with a resource name", e.getMessage());
assertEquals("Invalid SearchParameter.expression value \"gender\". Must start with a resource name", e.getMessage());
}
}
@ -95,32 +118,88 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
public void testCreateInvalidParamParamNullStatus() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(null);
try {
mySearchParameterDao.create(fooSp, mySrd);
fail();
} catch (UnprocessableEntityException e) {
assertEquals("Resource.status is missing or invalid: null", e.getMessage());
assertEquals("SearchParameter.status is missing or invalid: null", e.getMessage());
}
}
@Test
public void testExtensionWithNoValueIndexesWithoutFailure() {
SearchParameter eyeColourSp = new SearchParameter();
eyeColourSp.addBase("Patient");
eyeColourSp.setCode("eyecolour");
eyeColourSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
eyeColourSp.setTitle("Eye Colour");
eyeColourSp.setExpression("Patient.extension('http://acme.org/eyecolour')");
eyeColourSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
eyeColourSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(eyeColourSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.setActive(true);
p1.addExtension().setUrl("http://acme.org/eyecolour").addExtension().setUrl("http://foo").setValue(new StringType("VAL"));
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
}
@Test
public void testSearchForExtension() {
SearchParameter eyeColourSp = new SearchParameter();
eyeColourSp.addBase("Patient");
eyeColourSp.setCode("eyecolour");
eyeColourSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
eyeColourSp.setTitle("Eye Colour");
eyeColourSp.setExpression("Patient.extension('http://acme.org/eyecolour')");
eyeColourSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
eyeColourSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(eyeColourSp, mySrd);
mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.setActive(true);
p1.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("blue"));
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
Patient p2 = new Patient();
p2.setActive(true);
p2.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("green"));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
// Try with custom gender SP
SearchParameterMap map = new SearchParameterMap();
map.add("eyecolour", new TokenParam(null, "blue"));
IBundleProvider results = myPatientDao.search(map);
List<String> foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(p1id.getValue()));
}
@Test
public void testSearchWithCustomParam() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
IIdType spId = mySearchParameterDao.create(fooSp, mySrd).getId().toUnqualifiedVersionless();
mySearchParamRegsitry.forceRefresh();
@ -150,16 +229,28 @@ public class FhirResourceDaoDstu3SearchCustomSearchParamTest extends BaseJpaDstu
foundResources = toUnqualifiedVersionlessIdValues(results);
assertThat(foundResources, contains(patId.getValue()));
// Delete the param
mySearchParameterDao.delete(spId, mySrd);
mySearchParamRegsitry.forceRefresh();
mySystemDao.performReindexingPass(100);
// Try with custom gender SP
map = new SearchParameterMap();
map.add("foo", new TokenParam(null, "male"));
IBundleProvider res = myPatientDao.search(map);
assertEquals(0, res.size());
}
@Test
public void testSearchWithCustomParamDraft() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.DRAFT);
mySearchParameterDao.create(fooSp, mySrd);

View File

@ -16,6 +16,7 @@ import org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceCategory;
import org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceClinicalStatus;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent;
@ -26,7 +27,9 @@ import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirSystemDao;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
@ -34,6 +37,8 @@ import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier;
@ -49,9 +54,14 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system";
public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set";
@Autowired
private IHapiTerminologySvc myHapiTerminologySvc;
@After
public void after() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(false);
}
@Before
@ -110,6 +120,37 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
createLocalVs(codeSystem);
}
private CodeSystem createExternalCsDogs() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept hello = new TermConcept(cs, "hello").setDisplay("Hello");
cs.getConcepts().add(hello);
TermConcept goodbye = new TermConcept(cs, "goodbye").setDisplay("Goodbye");
cs.getConcepts().add(goodbye);
TermConcept dogs = new TermConcept(cs, "dogs").setDisplay("Dogs");
cs.getConcepts().add(dogs);
TermConcept labrador = new TermConcept(cs, "labrador").setDisplay("Labrador");
dogs.addChild(labrador, RelationshipTypeEnum.ISA);
TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle");
dogs.addChild(beagle, RelationshipTypeEnum.ISA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
return codeSystem;
}
private void createLocalCsAndVs() {
//@formatter:off
CodeSystem codeSystem = new CodeSystem();
@ -138,6 +179,22 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
myValueSetDao.create(valueSet, mySrd);
}
private void logAndValidateValueSet(ValueSet theResult) {
IParser parser = myFhirCtx.newXmlParser().setPrettyPrint(true);
String encoded = parser.encodeResourceToString(theResult);
ourLog.info(encoded);
FhirValidator validator = myFhirCtx.newValidator();
validator.setValidateAgainstStandardSchema(true);
validator.setValidateAgainstStandardSchematron(true);
ValidationResult result = validator.validateWithResult(theResult);
if (!result.isSuccessful()) {
ourLog.info(parser.encodeResourceToString(result.toOperationOutcome()));
fail(parser.encodeResourceToString(result.toOperationOutcome()));
}
}
@Test
public void testCodeSystemCreateDuplicateFails() {
CodeSystem codeSystem = new CodeSystem();
@ -156,28 +213,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
}
}
@Test
public void testLookupSnomed() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl("http://snomed.info/sct");
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", cs);
StringType code = new StringType("ParentA");
StringType system = new StringType("http://snomed.info/sct");
LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd);
assertEquals(true, outcome.isFound());
}
@Test
public void testCodeSystemWithDefinedCodes() {
//@formatter:off
@ -202,160 +237,128 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
}
@Test
public void testExpandWithExcludeInExternalValueSet() {
public void testExpandInvalid() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
ConceptSetComponent exclude = vs.getCompose().addExclude();
exclude.setSystem(URL_MY_CODE_SYSTEM);
exclude.addConcept().setCode("childAA");
exclude.addConcept().setCode("childAAA");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "childAB", "childAAB", "ParentC", "childBA", "childCA"));
}
private void logAndValidateValueSet(ValueSet theResult) {
IParser parser = myFhirCtx.newXmlParser().setPrettyPrint(true);
String encoded = parser.encodeResourceToString(theResult);
ourLog.info(encoded);
FhirValidator validator = myFhirCtx.newValidator();
validator.setValidateAgainstStandardSchema(true);
validator.setValidateAgainstStandardSchematron(true);
ValidationResult result = validator.validateWithResult(theResult);
if (!result.isSuccessful()) {
ourLog.info(parser.encodeResourceToString(result.toOperationOutcome()));
fail(parser.encodeResourceToString(result.toOperationOutcome()));
}
}
@Test
public void testExpandWithInvalidExclude() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
/*
* No system set on exclude
*/
ConceptSetComponent exclude = vs.getCompose().addExclude();
exclude.addConcept().setCode("childAA");
exclude.addConcept().setCode("childAAA");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("ValueSet contains exclude criteria with no system defined", e.getMessage());
}
}
@Test
public void testExpandWithNoResultsInLocalValueSet1() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ZZZZ");
include.addFilter();
include.addFilter().setOp(FilterOperator.ISA).setValue("childAA");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("Unable to find code 'ZZZZ' in code system http://example.com/my_code_system", e.getMessage());
}
}
@Test
public void testReindex() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ZZZZ");
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
// Again
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
}
@Test
public void testExpandWithNoResultsInLocalValueSet2() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM + "AA");
include.addConcept().setCode("A");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("unable to find code system http://example.com/my_code_systemAA", e.getMessage());
assertEquals("Invalid filter, must have fields populated: property op value", e.getMessage());
}
}
@Test
public void testExpandWithSystemAndCodesInExternalValueSet() {
createExternalCsAndLocalVs();
public void testExpandWithCodesAndDisplayFilterBlank() {
CodeSystem codeSystem = createExternalCsDogs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ParentA");
include.addConcept().setCode("childAA");
include.addConcept().setCode("childAAA");
ValueSet valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET);
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl())
.addConcept(new ConceptReferenceComponent().setCode("hello"))
.addConcept(new ConceptReferenceComponent().setCode("goodbye"));
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl())
.addFilter()
.setProperty("concept")
.setOp(FilterOperator.ISA)
.setValue("dogs");
myValueSetDao.create(valueSet, mySrd);
ValueSet result = myValueSetDao.expand(vs, null);
ValueSet result = myValueSetDao.expand(valueSet, "");
logAndValidateValueSet(result);
assertEquals(4, result.getExpansion().getTotal());
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA"));
assertThat(codes, containsInAnyOrder("hello", "goodbye", "labrador", "beagle"));
int idx = codes.indexOf("childAA");
assertEquals("childAA", result.getExpansion().getContains().get(idx).getCode());
assertEquals("Child AA", result.getExpansion().getContains().get(idx).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem());
}
@Test
public void testExpandWithSystemAndFilterInExternalValueSet() {
createExternalCsAndLocalVs();
public void testExpandWithCodesAndDisplayFilterPartialOnFilter() {
CodeSystem codeSystem = createExternalCsDogs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
ValueSet valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET);
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl())
.addConcept(new ConceptReferenceComponent().setCode("hello"))
.addConcept(new ConceptReferenceComponent().setCode("goodbye"));
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl())
.addFilter()
.setProperty("concept")
.setOp(FilterOperator.ISA)
.setValue("dogs");
myValueSetDao.create(valueSet, mySrd);
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Parent B");
ValueSet result = myValueSetDao.expand(vs, null);
ValueSet result = myValueSetDao.expand(valueSet, "lab");
logAndValidateValueSet(result);
assertEquals(1, result.getExpansion().getTotal());
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentB"));
assertThat(codes, containsInAnyOrder("labrador"));
}
@Test
public void testExpandWithCodesAndDisplayFilterPartialOnCodes() {
CodeSystem codeSystem = createExternalCsDogs();
ValueSet valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET);
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl())
.addConcept(new ConceptReferenceComponent().setCode("hello"))
.addConcept(new ConceptReferenceComponent().setCode("goodbye"));
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl())
.addFilter()
.setProperty("concept")
.setOp(FilterOperator.ISA)
.setValue("dogs");
myValueSetDao.create(valueSet, mySrd);
ValueSet result = myValueSetDao.expand(valueSet, "hel");
logAndValidateValueSet(result);
assertEquals(1, result.getExpansion().getTotal());
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("hello"));
}
@Test
public void testExpandWithCodesAndDisplayFilterPartialOnExpansion() {
CodeSystem codeSystem = createExternalCsDogs();
ValueSet valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET);
valueSet.getCompose().addInclude().setSystem(codeSystem.getUrl());
myValueSetDao.create(valueSet, mySrd);
ValueSet result = myValueSetDao.expand(valueSet, "lab");
logAndValidateValueSet(result);
assertEquals(1, result.getExpansion().getTotal());
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("labrador"));
}
@ -392,6 +395,127 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
}
@Test
public void testExpandWithExcludeInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
ConceptSetComponent exclude = vs.getCompose().addExclude();
exclude.setSystem(URL_MY_CODE_SYSTEM);
exclude.addConcept().setCode("childAA");
exclude.addConcept().setCode("childAAA");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "childAB", "childAAB", "ParentC", "childBA", "childCA"));
}
@Test
public void testExpandWithInvalidExclude() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
/*
* No system set on exclude
*/
ConceptSetComponent exclude = vs.getCompose().addExclude();
exclude.addConcept().setCode("childAA");
exclude.addConcept().setCode("childAAA");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("ValueSet contains exclude criteria with no system defined", e.getMessage());
}
}
@Test
public void testExpandWithIsAInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("childAAA", "childAAB"));
}
@Test
public void testExpandWithIsAInExternalValueSetReindex() {
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(true);
createExternalCsAndLocalVs();
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(100);
mySystemDao.performReindexingPass(100);
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("childAAA", "childAAB"));
}
@Test
public void testExpandWithNoResultsInLocalValueSet1() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ZZZZ");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("Unable to find code 'ZZZZ' in code system http://example.com/my_code_system", e.getMessage());
}
}
@Test
public void testExpandWithNoResultsInLocalValueSet2() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM + "AA");
include.addConcept().setCode("A");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("unable to find code system http://example.com/my_code_systemAA", e.getMessage());
}
}
@Test
public void testExpandWithSystemAndCodesAndFilterKeywordInLocalValueSet() {
createLocalCsAndVs();
@ -422,6 +546,29 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
//
}
@Test
public void testExpandWithSystemAndCodesInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ParentA");
include.addConcept().setCode("childAA");
include.addConcept().setCode("childAAA");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA"));
int idx = codes.indexOf("childAA");
assertEquals("childAA", result.getExpansion().getContains().get(idx).getCode());
assertEquals("Child AA", result.getExpansion().getContains().get(idx).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem());
}
@Test
public void testExpandWithSystemAndCodesInLocalValueSet() {
createLocalCsAndVs();
@ -449,6 +596,43 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
//
}
@Test
public void testExpandWithSystemAndDisplayFilterBlank() {
CodeSystem codeSystem = createExternalCsDogs();
ValueSet valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET);
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl());
ValueSet result = myValueSetDao.expand(valueSet, "");
logAndValidateValueSet(result);
assertEquals(5, result.getExpansion().getTotal());
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("hello", "goodbye", "dogs", "labrador", "beagle"));
}
@Test
public void testExpandWithSystemAndFilterInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Parent B");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentB"));
}
@Test
public void testIndexingIsDeferredForLargeCodeSystems() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(1);
@ -489,6 +673,28 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
assertThat(encoded, containsStringIgnoringCase("<code value=\"childAAB\"/>"));
}
@Test
public void testLookupSnomed() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl("http://snomed.info/sct");
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", cs);
StringType code = new StringType("ParentA");
StringType system = new StringType("http://snomed.info/sct");
LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd);
assertEquals(true, outcome.isFound());
}
/**
* Can't currently abort costly
*/
@ -523,6 +729,30 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
}
}
@Test
public void testReindex() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ZZZZ");
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
// Again
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
}
@Test
public void testSearchCodeAboveLocalCodesystem() {
createLocalCsAndVs();
@ -565,107 +795,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
}
@Test
public void testSearchCodeBelowLocalCodesystem() {
createLocalCsAndVs();
Observation obsAA = new Observation();
obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA");
IIdType idAA = myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless();
Observation obsBA = new Observation();
obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA");
IIdType idBA = myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless();
Observation obsCA = new Observation();
obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA");
IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "A").setModifier(TokenParamModifier.BELOW));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAA.getValue()));
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "AAA").setModifier(TokenParamModifier.BELOW));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
}
@Test
public void testSearchCodeInBuiltInValueSet() {
AllergyIntolerance ai1 = new AllergyIntolerance();
ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE);
String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai2 = new AllergyIntolerance();
ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED);
String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai3 = new AllergyIntolerance();
ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE);
String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue();
SearchParameterMap params;
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-clinical-status").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3));
// No codes in this one
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty());
// Invalid VS
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.IN));
try {
myAllergyIntoleranceDao.search(params);
} catch (InvalidRequestException e) {
assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage());
}
}
/**
* Todo: not yet implemented
*/
@Test
@Ignore
public void testSearchCodeNotInBuiltInValueSet() {
AllergyIntolerance ai1 = new AllergyIntolerance();
ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE);
String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai2 = new AllergyIntolerance();
ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED);
String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai3 = new AllergyIntolerance();
ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE);
String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue();
SearchParameterMap params;
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-status").setModifier(TokenParamModifier.NOT_IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty());
// No codes in this one
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.NOT_IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3));
// Invalid VS
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.NOT_IN));
try {
myAllergyIntoleranceDao.search(params);
} catch (InvalidRequestException e) {
assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage());
}
}
@Test
public void testSearchCodeBelowBuiltInCodesystem() {
AllergyIntolerance ai1 = new AllergyIntolerance();
@ -753,6 +882,68 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
}
@Test
public void testSearchCodeBelowLocalCodesystem() {
createLocalCsAndVs();
Observation obsAA = new Observation();
obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA");
IIdType idAA = myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless();
Observation obsBA = new Observation();
obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA");
IIdType idBA = myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless();
Observation obsCA = new Observation();
obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA");
IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "A").setModifier(TokenParamModifier.BELOW));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAA.getValue()));
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "AAA").setModifier(TokenParamModifier.BELOW));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
}
@Test
public void testSearchCodeInBuiltInValueSet() {
AllergyIntolerance ai1 = new AllergyIntolerance();
ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE);
String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai2 = new AllergyIntolerance();
ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED);
String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai3 = new AllergyIntolerance();
ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE);
String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue();
SearchParameterMap params;
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-clinical-status").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3));
// No codes in this one
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty());
// Invalid VS
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.IN));
try {
myAllergyIntoleranceDao.search(params);
} catch (InvalidRequestException e) {
assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage());
}
}
@Test
public void testSearchCodeInEmptyValueSet() {
ValueSet valueSet = new ValueSet();
@ -777,40 +968,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
ourLog.info("testSearchCodeInEmptyValueSet done");
}
@Test
public void testSearchCodeInValueSetThatImportsInvalidCodeSystem() {
ValueSet valueSet = new ValueSet();
valueSet.getCompose().addInclude().addValueSet("http://non_existant_VS");
valueSet.setUrl(URL_MY_VALUE_SET);
IIdType vsid = myValueSetDao.create(valueSet, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params;
ourLog.info("testSearchCodeInEmptyValueSet without status");
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
try {
myObservationDao.search(params);
} catch(InvalidRequestException e) {
assertEquals("Unable to expand imported value set: Unable to find imported value set http://non_existant_VS", e.getMessage());
}
// Now let's update
valueSet = new ValueSet();
valueSet.setId(vsid);
valueSet.getCompose().addInclude().setSystem("http://hl7.org/fhir/v3/MaritalStatus").addConcept().setCode("A");
valueSet.setUrl(URL_MY_VALUE_SET);
myValueSetDao.update(valueSet, mySrd).getId().toUnqualifiedVersionless();
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
params.add(Observation.SP_STATUS, new TokenParam(null, "final"));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
}
@Test
public void testSearchCodeInExternalCodesystem() {
createExternalCsAndLocalVs();
@ -870,6 +1027,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), empty());
}
@Test
public void testSearchCodeInLocalCodesystem() {
createLocalCsAndVs();
@ -892,6 +1050,78 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
}
@Test
public void testSearchCodeInValueSetThatImportsInvalidCodeSystem() {
ValueSet valueSet = new ValueSet();
valueSet.getCompose().addInclude().addValueSet("http://non_existant_VS");
valueSet.setUrl(URL_MY_VALUE_SET);
IIdType vsid = myValueSetDao.create(valueSet, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params;
ourLog.info("testSearchCodeInEmptyValueSet without status");
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
try {
myObservationDao.search(params);
} catch(InvalidRequestException e) {
assertEquals("Unable to expand imported value set: Unable to find imported value set http://non_existant_VS", e.getMessage());
}
// Now let's update
valueSet = new ValueSet();
valueSet.setId(vsid);
valueSet.getCompose().addInclude().setSystem("http://hl7.org/fhir/v3/MaritalStatus").addConcept().setCode("A");
valueSet.setUrl(URL_MY_VALUE_SET);
myValueSetDao.update(valueSet, mySrd).getId().toUnqualifiedVersionless();
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
params.add(Observation.SP_STATUS, new TokenParam(null, "final"));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
}
/**
* Todo: not yet implemented
*/
@Test
@Ignore
public void testSearchCodeNotInBuiltInValueSet() {
AllergyIntolerance ai1 = new AllergyIntolerance();
ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE);
String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai2 = new AllergyIntolerance();
ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED);
String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai3 = new AllergyIntolerance();
ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE);
String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue();
SearchParameterMap params;
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-status").setModifier(TokenParamModifier.NOT_IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty());
// No codes in this one
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.NOT_IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3));
// Invalid VS
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.NOT_IN));
try {
myAllergyIntoleranceDao.search(params);
} catch (InvalidRequestException e) {
assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage());
}
}
private ArrayList<String> toCodesContains(List<ValueSetExpansionContainsComponent> theContains) {
ArrayList<String> retVal = new ArrayList<String>();
for (ValueSetExpansionContainsComponent next : theContains) {

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
@ -186,7 +187,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.CREATE, null, mySrd);
fail();
} catch (InvalidRequestException e) {
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must not be populated"));
}
@ -209,7 +210,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
fail();
} catch (InvalidRequestException e) {
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must be populated"));
}
@ -232,7 +233,7 @@ public class FhirResourceDaoDstu3ValidateTest extends BaseJpaDstu3Test {
try {
myPatientDao.validate(pat, null, null, null, ValidationModeEnum.UPDATE, null, mySrd);
fail();
} catch (InvalidRequestException e) {
} catch (UnprocessableEntityException e) {
assertThat(e.getMessage(), containsString("ID must be populated"));
}

View File

@ -382,6 +382,42 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
}
}
/**
* Per a message on the mailing list
*/
@Test
public void testTransactionWithPostDoesntUpdate() throws Exception {
// First bundle (name is Joshua)
String input = IOUtils.toString(getClass().getResource("/dstu3-post1.xml"), StandardCharsets.UTF_8);
Bundle request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
Bundle response = mySystemDao.transaction(mySrd, request);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
assertEquals(1, response.getEntry().size());
assertEquals("201 Created", response.getEntry().get(0).getResponse().getStatus());
assertEquals("1", response.getEntry().get(0).getResponse().getEtag());
String id = response.getEntry().get(0).getResponse().getLocation();
// Now the second (name is Adam, shouldn't get used)
input = IOUtils.toString(getClass().getResource("/dstu3-post2.xml"), StandardCharsets.UTF_8);
request = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
response = mySystemDao.transaction(mySrd, request);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(response));
assertEquals(1, response.getEntry().size());
assertEquals("200 OK", response.getEntry().get(0).getResponse().getStatus());
assertEquals("1", response.getEntry().get(0).getResponse().getEtag());
String id2 = response.getEntry().get(0).getResponse().getLocation();
assertEquals(id, id2);
Patient patient = myPatientDao.read(new IdType(id), mySrd);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(patient));
assertEquals("Joshua", patient.getNameFirstRep().getGivenAsSingleString());
}
@Test
public void testTransactionCreateInlineMatchUrlWithOneMatch() {
String methodName = "testTransactionCreateInlineMatchUrlWithOneMatch";

View File

@ -60,7 +60,7 @@ public class SearchParamExtractorDstu3Test {
}
@Override
public Collection<RuntimeSearchParam> getAllSearchParams(String theResourceName) {
public Map<String, Map<String, RuntimeSearchParam>> getActiveSearchParams() {
throw new UnsupportedOperationException();
}
};

View File

@ -2556,4 +2556,61 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
}
@Test
public void testValidateOnNoId() throws Exception {
HttpGet get = new HttpGet(ourServerBase + "/QuestionnaireResponse/$validate");
CloseableHttpResponse response = ourHttpClient.execute(get);
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertThat(responseString, containsString("No resource supplied for $validate operation"));
assertEquals(400, response.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(response);
}
}
/**
* From a Skype message from Brian Postlethwaite
*/
@Test
public void testValidateQuestionnaireResponseWithNoIdForCreate() throws Exception {
String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"create\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"<div>!-- populated from the rendered HTML below --></div>\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\",\"group\":{\"question\":[{\"linkId\":\"d94b4f57-1ca0-4d65-acba-8bd9a3926c8c\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has a valid Medicare or DVA entitlement card\"},{\"linkId\":\"0cbe66db-ff12-473a-940e-4672fb82de44\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has received a MedsCheck, Diabetes MedsCheck, Home Medicines Review (HMR) otr Restidential Medication Management Review (RMMR) in the past 12 months\"},{\"linkId\":\"35790cfd-2d98-4721-963e-9663e1897a17\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient is living at home in a community setting\"},{\"linkId\":\"3ccc8304-76cd-41ff-9360-2c8755590bae\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has been recently diagnosed with type 3 diabetes (in the last 12 months) AND is unable to gain timely access to existing diabetes education or health services in the community OR \"},{\"linkId\":\"b05f6f09-49ec-40f9-a889-9a3fdff9e0da\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has type 2 diabetes , is less than ideally controlled AND is unable to gain timely access to existing diabetes education or health services in their community \"},{\"linkId\":\"4a777f56-800d-4e0b-a9c3-e929832adb5b\",\"answer\":[{\"valueBoolean\":false,\"group\":[{\"linkId\":\"95bbc904-149e-427f-88a4-7f6c8ab186fa\",\"question\":[{\"linkId\":\"f0acea9e-716c-4fce-b7a2-aad59de9d136\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Patient has had an Acute or Adverse Event\"},{\"linkId\":\"e1629159-6dea-4295-a93e-e7c2829ce180\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Exacerbation of a Chronic Disease or Condition\"},{\"linkId\":\"2ce526fa-edaa-44b3-8d5a-6e97f6379ce8\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"New Diagnosis\"},{\"linkId\":\"9d6ffa9f-0110-418c-9ed0-f04910fda2ed\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Recent hospital admission (<3 months)\"},{\"linkId\":\"d2803ff7-25f7-4c7b-ab92-356c49910478\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Major change to regular medication regime\"},{\"linkId\":\"b34af32d-c69d-4d44-889f-5b6d420a7d08\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Suspected non-adherence to the patient's medication regime \"},{\"linkId\":\"74bad553-c273-41e6-8647-22b860430bc2\",\"answer\":[],\"text\":\"Other\"}]}]}],\"text\":\"The patient has experienced one or more of the following recent significant medical events\"},{\"linkId\":\"ecbf4e5a-d4d1-43eb-9f43-0c0e35fc09c7\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The Pharmacist has obtained patient consent to take part in the MedsCheck Service or Diabetes MedsCheck Service&nbsp; and share information obtained during the services with other nominated members of the patients healthcare team (such as their GP, diabetes educator) if required\"},{\"linkId\":\"8ef66774-43b0-4190-873f-cfbb6e980aa9\",\"answer\":[],\"text\":\"Question\"}]}}}]}";
HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true");
post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertThat(responseString, containsString("No issues detected"));
assertEquals(200, response.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(response);
}
}
/**
* From a Skype message from Brian Postlethwaite
*/
@Test
public void testValidateQuestionnaireResponseWithNoIdForUpdate() throws Exception {
String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"update\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"<div>!-- populated from the rendered HTML below --></div>\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\",\"group\":{\"question\":[{\"linkId\":\"d94b4f57-1ca0-4d65-acba-8bd9a3926c8c\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has a valid Medicare or DVA entitlement card\"},{\"linkId\":\"0cbe66db-ff12-473a-940e-4672fb82de44\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has received a MedsCheck, Diabetes MedsCheck, Home Medicines Review (HMR) otr Restidential Medication Management Review (RMMR) in the past 12 months\"},{\"linkId\":\"35790cfd-2d98-4721-963e-9663e1897a17\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient is living at home in a community setting\"},{\"linkId\":\"3ccc8304-76cd-41ff-9360-2c8755590bae\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has been recently diagnosed with type 3 diabetes (in the last 12 months) AND is unable to gain timely access to existing diabetes education or health services in the community OR \"},{\"linkId\":\"b05f6f09-49ec-40f9-a889-9a3fdff9e0da\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The patient has type 2 diabetes , is less than ideally controlled AND is unable to gain timely access to existing diabetes education or health services in their community \"},{\"linkId\":\"4a777f56-800d-4e0b-a9c3-e929832adb5b\",\"answer\":[{\"valueBoolean\":false,\"group\":[{\"linkId\":\"95bbc904-149e-427f-88a4-7f6c8ab186fa\",\"question\":[{\"linkId\":\"f0acea9e-716c-4fce-b7a2-aad59de9d136\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Patient has had an Acute or Adverse Event\"},{\"linkId\":\"e1629159-6dea-4295-a93e-e7c2829ce180\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Exacerbation of a Chronic Disease or Condition\"},{\"linkId\":\"2ce526fa-edaa-44b3-8d5a-6e97f6379ce8\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"New Diagnosis\"},{\"linkId\":\"9d6ffa9f-0110-418c-9ed0-f04910fda2ed\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Recent hospital admission (<3 months)\"},{\"linkId\":\"d2803ff7-25f7-4c7b-ab92-356c49910478\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Major change to regular medication regime\"},{\"linkId\":\"b34af32d-c69d-4d44-889f-5b6d420a7d08\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"Suspected non-adherence to the patient's medication regime \"},{\"linkId\":\"74bad553-c273-41e6-8647-22b860430bc2\",\"answer\":[],\"text\":\"Other\"}]}]}],\"text\":\"The patient has experienced one or more of the following recent significant medical events\"},{\"linkId\":\"ecbf4e5a-d4d1-43eb-9f43-0c0e35fc09c7\",\"answer\":[{\"valueBoolean\":false}],\"text\":\"The Pharmacist has obtained patient consent to take part in the MedsCheck Service or Diabetes MedsCheck Service&nbsp; and share information obtained during the services with other nominated members of the patients healthcare team (such as their GP, diabetes educator) if required\"},{\"linkId\":\"8ef66774-43b0-4190-873f-cfbb6e980aa9\",\"answer\":[],\"text\":\"Question\"}]}}}]}";
HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true");
post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertThat(responseString, containsString("Resource has no ID"));
assertEquals(422, response.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(response);
}
}
}

View File

@ -10,6 +10,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import org.apache.commons.io.IOUtils;
@ -139,7 +140,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
get.addHeader("Accept", "application/xml, text/html");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String response = IOUtils.toString(http.getEntity().getContent());
String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
assertThat(response, (containsString("_format=json")));
assertEquals(200, http.getStatusLine().getStatusCode());
@ -167,7 +168,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
get.addHeader("Accept", "application/xml+fhir");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String response = IOUtils.toString(http.getEntity().getContent());
String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
assertThat(response, not(containsString("_format")));
assertEquals(200, http.getStatusLine().getStatusCode());
@ -216,7 +217,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(200, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
Parameters parameters = ourCtx.newXmlParser().parseResource(Parameters.class, output);
@ -246,7 +247,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'context' must be provided"));
} finally {
@ -257,7 +258,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'searchParam' must be provided"));
} finally {
@ -268,7 +269,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'text' must be provided"));
} finally {
@ -286,7 +287,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test
public void testTransactionFromBundle() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
}
@ -295,7 +296,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
public void testTransactionFromBundle2() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
@ -311,7 +312,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
*/
bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
bundle = IOUtils.toString(bundleRes);
bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
@ -335,7 +336,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
public void testTransactionFromBundle3() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/grahame-transaction.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
}
@ -343,7 +344,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test
public void testTransactionFromBundle4() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
Bundle bundleResp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
@ -358,7 +359,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test
public void testTransactionFromBundle5() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle2.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
try {
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
fail();
@ -372,7 +373,7 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
@Test
public void testTransactionFromBundle6() throws Exception {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/simone_bundle3.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
// try {
// fail();
@ -427,5 +428,18 @@ public class SystemProviderDstu2Test extends BaseJpaDstu2Test {
assertEquals(0, respSub.getEntry().size());
}
@Test
public void testMarkResourcesForReindexing() throws Exception {
HttpGet get = new HttpGet(ourServerBase + "/$mark-all-resources-for-reindexing");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(http);;
}
}
}

View File

@ -14,6 +14,7 @@ import java.util.Map;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.CapabilityStatement;
import org.hl7.fhir.dstu3.model.CodeType;
import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestComponent;
import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceComponent;
import org.hl7.fhir.dstu3.model.CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent;
@ -35,6 +36,7 @@ import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.rest.gclient.ReferenceClientParam;
import ca.uhn.fhir.rest.gclient.TokenClientParam;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.server.IBundleProvider;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
@ -58,7 +60,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
public void saveCreateSearchParamInvalidWithMissingStatus() throws IOException {
SearchParameter sp = new SearchParameter();
sp.setCode("foo");
sp.setXpath("Patient.gender");
sp.setExpression("Patient.gender");
sp.setXpathUsage(XPathUsageType.NORMAL);
sp.setTitle("Foo Param");
@ -66,10 +68,59 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
ourClient.create().resource(sp).execute();
fail();
} catch (UnprocessableEntityException e) {
assertEquals("HTTP 422 Unprocessable Entity: Resource.status is missing or invalid: null", e.getMessage());
assertEquals("HTTP 422 Unprocessable Entity: SearchParameter.status is missing or invalid: null", e.getMessage());
}
}
@Test
public void testSearchForExtension() {
SearchParameter eyeColourSp = new SearchParameter();
eyeColourSp.addBase("Patient");
eyeColourSp.setCode("eyecolour");
eyeColourSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
eyeColourSp.setTitle("Eye Colour");
eyeColourSp.setExpression("Patient.extension('http://acme.org/eyecolour')");
eyeColourSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
eyeColourSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(eyeColourSp));
ourClient
.create()
.resource(eyeColourSp)
.execute();
// mySearchParamRegsitry.forceRefresh();
Patient p1 = new Patient();
p1.setActive(true);
p1.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("blue"));
IIdType p1id = myPatientDao.create(p1).getId().toUnqualifiedVersionless();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(p1));
Patient p2 = new Patient();
p2.setActive(true);
p2.addExtension().setUrl("http://acme.org/eyecolour").setValue(new CodeType("green"));
IIdType p2id = myPatientDao.create(p2).getId().toUnqualifiedVersionless();
Bundle bundle = ourClient
.search()
.forResource(Patient.class)
.where(new TokenClientParam("eyecolour").exactly().code("blue"))
.returnBundle(Bundle.class)
.execute();
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(bundle));
List<String> foundResources = toUnqualifiedVersionlessIdValues(bundle);
assertThat(foundResources, contains(p1id.getValue()));
}
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderCustomSearchParamDstu3Test.class);
@Override
@Before
public void beforeResetConfig() {
@ -97,20 +148,22 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
// Add a custom search parameter
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
// Disable an existing parameter
fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("gender");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("Gender");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.RETIRED);
mySearchParameterDao.create(fooSp, mySrd);
@ -149,20 +202,22 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
// Add a custom search parameter
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
// Disable an existing parameter
fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("gender");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("Gender");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.RETIRED);
mySearchParameterDao.create(fooSp, mySrd);
@ -203,10 +258,11 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
public void testSearchWithCustomParam() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
@ -232,6 +288,7 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
.where(new TokenClientParam("foo").exactly().code("male"))
.returnBundle(Bundle.class)
.execute();
foundResources = toUnqualifiedVersionlessIdValues(result);
assertThat(foundResources, contains(patId.getValue()));
@ -253,10 +310,11 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
assertEquals(BaseHapiFhirDao.INDEX_STATUS_INDEXED, res.getIndexStatus().longValue());
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.TOKEN);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Patient.gender");
fooSp.setExpression("Patient.gender");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);
@ -273,10 +331,11 @@ public class ResourceProviderCustomSearchParamDstu3Test extends BaseResourceProv
public void testSearchQualifiedWithCustomReferenceParam() {
SearchParameter fooSp = new SearchParameter();
fooSp.addBase("Patient");
fooSp.setCode("foo");
fooSp.setType(org.hl7.fhir.dstu3.model.Enumerations.SearchParamType.REFERENCE);
fooSp.setTitle("FOO SP");
fooSp.setXpath("Observation.subject");
fooSp.setExpression("Observation.subject");
fooSp.setXpathUsage(org.hl7.fhir.dstu3.model.SearchParameter.XPathUsageType.NORMAL);
fooSp.setStatus(org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus.ACTIVE);
mySearchParameterDao.create(fooSp, mySrd);

View File

@ -149,7 +149,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
@After
public void after() throws Exception {
super.after();
myDaoConfig.setAllowMultipleDelete(new DaoConfig().isAllowMultipleDelete());
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
}
@ -160,15 +160,14 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
myDaoConfig.setAllowMultipleDelete(true);
}
private void checkParamMissing(String paramName) throws IOException, ClientProtocolException {
HttpGet get = new HttpGet(ourServerBase + "/Observation?" + paramName + ":missing=false");
CloseableHttpResponse resp = ourHttpClient.execute(get);
IOUtils.closeQuietly(resp.getEntity().getContent());
assertEquals(200, resp.getStatusLine().getStatusCode());
}
private ArrayList<IBaseResource> genResourcesOfType(Bundle theRes, Class<? extends IBaseResource> theClass) {
ArrayList<IBaseResource> retVal = new ArrayList<IBaseResource>();
for (BundleEntryComponent next : theRes.getEntry()) {
@ -197,7 +196,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
List<Extension> exts = basic.getExtensionsByUrl("http://localhost:1080/hapi-fhir-jpaserver-example/baseDstu2/StructureDefinition/DateID");
assertEquals(1, exts.size());
}
private List<String> searchAndReturnUnqualifiedIdValues(String uri) throws IOException, ClientProtocolException {
List<String> ids;
HttpGet get = new HttpGet(uri);
@ -339,9 +338,9 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
Binary binary = new Binary();
binary.setContent(arr);
binary.setContentType("dansk");
IIdType resource = ourClient.create().resource(binary).execute().getId();
Binary fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("1", fromDB.getIdElement().getVersionIdPart());
@ -355,7 +354,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
} finally {
IOUtils.closeQuietly(resp);
}
fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("2", fromDB.getIdElement().getVersionIdPart());
@ -370,12 +369,12 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
} finally {
IOUtils.closeQuietly(resp);
}
fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("3", fromDB.getIdElement().getVersionIdPart());
// Now an update with the wrong ID in the body
arr[0] = 4;
binary.setId("");
encoded = myFhirCtx.newJsonParser().encodeResourceToString(binary);
@ -387,7 +386,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
} finally {
IOUtils.closeQuietly(resp);
}
fromDB = ourClient.read().resource(Binary.class).withId(resource.toVersionless()).execute();
assertEquals("3", fromDB.getIdElement().getVersionIdPart());
@ -674,7 +673,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
String encoded = myFhirCtx.newXmlParser().encodeResourceToString(response);
ourLog.info(encoded);
assertThat(encoded, containsString(
"<issue><severity value=\"information\"/><code value=\"informational\"/><diagnostics value=\"Successfully deleted Patient?identifier=testDeleteConditionalMultiple resource(s) in 2ms\"/></issue>"));
"<issue><severity value=\"information\"/><code value=\"informational\"/><diagnostics value=\"Successfully deleted 2 resource(s) in "));
try {
ourClient.read().resource("Patient").withId(id1).execute();
fail();
@ -726,7 +725,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
* Test for #345
*/
@Test
public void testDeleteNormal() throws IOException {
public void testDeleteNormal() {
Patient p = new Patient();
p.addName().setFamily("FAM");
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
@ -743,6 +742,17 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
}
@Test
public void testDeleteReturnsOperationOutcome() {
Patient p = new Patient();
p.addName().setFamily("FAM");
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
IBaseOperationOutcome resp = ourClient.delete().resourceById(id).execute();
OperationOutcome oo = (OperationOutcome) resp;
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Successfully deleted 1 resource(s) in "));
}
@Test
public void testDeleteResourceConditional1() throws IOException {
String methodName = "testDeleteResourceConditional1";
@ -768,6 +778,10 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
response = ourHttpClient.execute(delete);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp);
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, resp);
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Successfully deleted 1 resource(s) in "));
} finally {
response.close();
}
@ -777,6 +791,24 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
try {
ourLog.info(response.toString());
assertEquals(Constants.STATUS_HTTP_410_GONE, response.getStatusLine().getStatusCode());
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp);
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, resp);
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Resource was deleted at"));
} finally {
response.close();
}
// Delete should now have no matches
delete = new HttpDelete(ourServerBase + "/Patient?name=" + methodName);
response = ourHttpClient.execute(delete);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp);
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, resp);
assertThat(oo.getIssueFirstRep().getDiagnostics(), startsWith("Unable to find resource matching URL \"Patient?name=testDeleteResourceConditional1\". Deletion failed."));
} finally {
response.close();
}
@ -1781,7 +1813,10 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
ourLog.info("Response: {}", respString);
assertEquals(400, response.getStatusLine().getStatusCode());
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, respString);
assertEquals("Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"AAA\" does not match URL ID of \"" + id.getIdPart() + "\"", oo.getIssue().get(0).getDiagnostics());
assertEquals(
"Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"AAA\" does not match URL ID of \""
+ id.getIdPart() + "\"",
oo.getIssue().get(0).getDiagnostics());
} finally {
response.close();
}
@ -1809,12 +1844,11 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
@Test
public void testIncludeWithExternalReferences() {
myDaoConfig.setAllowExternalReferences(true);
Patient p = new Patient();
p.getManagingOrganization().setReference("http://example.com/Organization/123");
ourClient.create().resource(p).execute();
Bundle b = ourClient.search().forResource("Patient").include(Patient.INCLUDE_ORGANIZATION).returnBundle(Bundle.class).execute();
assertEquals(1, b.getEntry().size());
}
@ -1834,7 +1868,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
}
@Test
public void testMetadataSuperParamsAreIncluded() throws IOException {
StructureDefinition p = new StructureDefinition();
@ -1843,12 +1876,12 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
IIdType id = ourClient.create().resource(p).execute().getId().toUnqualifiedVersionless();
Bundle resp = ourClient
.search()
.forResource(StructureDefinition.class)
.where(StructureDefinition.URL.matches().value("http://example.com/foo"))
.returnBundle(Bundle.class)
.execute();
.search()
.forResource(StructureDefinition.class)
.where(StructureDefinition.URL.matches().value("http://example.com/foo"))
.returnBundle(Bundle.class)
.execute();
assertEquals(1, resp.getTotal());
}
@ -2101,9 +2134,10 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
String input = IOUtils.toString(getClass().getResourceAsStream("/two_questionnaires.json"), StandardCharsets.UTF_8);
String respString = ourClient.transaction().withBundle(input).prettyPrint().execute();
ourLog.info(respString);
ourHttpClient.execute(new HttpGet("http://localhost:" + ourPort + "/QuestionnaireResponse?patient=QR3295&questionnaire=profile&_sort:desc=authored&_count=5&_include=QuestionnaireResponse:questionnaire&_include=QuestionnaireResponse:subject"));
// Bundle bundle =
ourHttpClient.execute(new HttpGet("http://localhost:" + ourPort
+ "/QuestionnaireResponse?patient=QR3295&questionnaire=profile&_sort:desc=authored&_count=5&_include=QuestionnaireResponse:questionnaire&_include=QuestionnaireResponse:subject"));
// Bundle bundle =
}
@Test
@ -2284,14 +2318,14 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
}
Bundle found = ourClient
.search()
.forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().systemAndValues(null, id1.getIdPart(), id2.getIdPart()))
.returnBundle(Bundle.class)
.execute();
.search()
.forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().systemAndValues(null, id1.getIdPart(), id2.getIdPart()))
.returnBundle(Bundle.class)
.execute();
assertThat(toUnqualifiedVersionlessIds(found), empty());
found = ourClient
.search()
.forResource(Patient.class)
@ -2319,14 +2353,14 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
.execute();
assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id1));
found = ourClient
.search()
.forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().codes(Arrays.asList(id1.getIdPart(), id2.getIdPart(), "FOOOOO")))
.and(BaseResource.RES_ID.exactly().code(id1.getIdPart()))
.returnBundle(Bundle.class)
.execute();
.search()
.forResource(Patient.class)
.where(BaseResource.RES_ID.exactly().codes(Arrays.asList(id1.getIdPart(), id2.getIdPart(), "FOOOOO")))
.and(BaseResource.RES_ID.exactly().code(id1.getIdPart()))
.returnBundle(Bundle.class)
.execute();
assertThat(toUnqualifiedVersionlessIds(found), containsInAnyOrder(id1));
@ -3445,7 +3479,9 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
ourLog.info(responseString);
assertEquals(400, response.getStatusLine().getStatusCode());
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, responseString);
assertEquals("Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"333\" does not match URL ID of \"A2\"", oo.getIssue().get(0).getDiagnostics());
assertEquals(
"Can not update resource, resource body must contain an ID element which matches the request URL for update (PUT) operation - Resource body ID of \"333\" does not match URL ID of \"A2\"",
oo.getIssue().get(0).getDiagnostics());
} finally {
response.close();
}

View File

@ -6,10 +6,12 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
@ -181,5 +183,63 @@ public class ResourceProviderQuestionnaireResponseDstu3Test extends BaseResource
}
@Test
public void testValidateOnNoId() throws Exception {
HttpGet get = new HttpGet(ourServerBase + "/QuestionnaireResponse/$validate");
CloseableHttpResponse response = ourHttpClient.execute(get);
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertThat(responseString, containsString("No resource supplied for $validate operation"));
assertEquals(400, response.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(response);
}
}
/**
* From a Skype message from Brian Postlethwaite
*/
@Test
public void testValidateQuestionnaireResponseWithNoIdForCreate() throws Exception {
String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"create\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">!-- populated from the rendered HTML below --></div>\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\"}}]}";
HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true");
post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertEquals(200, response.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(response);
}
}
/**
* From a Skype message from Brian Postlethwaite
*/
@Test
public void testValidateQuestionnaireResponseWithNoIdForUpdate() throws Exception {
String input = "{\"resourceType\":\"Parameters\",\"parameter\":[{\"name\":\"mode\",\"valueString\":\"update\"},{\"name\":\"resource\",\"resource\":{\"resourceType\":\"QuestionnaireResponse\",\"questionnaire\":{\"reference\":\"http://fhirtest.uhn.ca/baseDstu2/Questionnaire/MedsCheckEligibility\"},\"text\":{\"status\":\"generated\",\"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">!-- populated from the rendered HTML below --></div>\"},\"status\":\"completed\",\"authored\":\"2017-02-10T00:02:58.098Z\"}}]}";
HttpPost post = new HttpPost(ourServerBase + "/QuestionnaireResponse/$validate?_pretty=true");
post.setEntity(new StringEntity(input, ContentType.APPLICATION_JSON));
CloseableHttpResponse response = ourHttpClient.execute(post);
try {
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info("Response: {}", responseString);
assertThat(responseString, containsString("Resource has no ID"));
assertEquals(422, response.getStatusLine().getStatusCode());
} finally {
IOUtils.closeQuietly(response);
}
}
}

View File

@ -129,7 +129,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
CloseableHttpResponse resp = ourHttpClient.execute(req);
try {
String encoded = IOUtils.toString(resp.getEntity().getContent());
String encoded = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(encoded);
assertThat(encoded, containsString("transaction-response"));
@ -162,7 +162,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
CloseableHttpResponse resp = ourHttpClient.execute(req);
try {
String encoded = IOUtils.toString(resp.getEntity().getContent());
String encoded = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(encoded);
assertThat(encoded, containsString("transaction-response"));
@ -271,14 +271,14 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
try {
InputStream bundleRes = SystemProviderDstu2Test.class.getResourceAsStream("/questionnaire-sdc-profile-example-ussg-fht.xml");
String bundleStr = IOUtils.toString(bundleRes);
String bundleStr = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
HttpPost req = new HttpPost(ourServerBase);
req.setEntity(new StringEntity(bundleStr, ContentType.parse(Constants.CT_FHIR_XML + "; charset=utf-8")));
CloseableHttpResponse resp = ourHttpClient.execute(req);
try {
String encoded = IOUtils.toString(resp.getEntity().getContent());
String encoded = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(encoded);
//@formatter:off
@ -314,7 +314,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String response = IOUtils.toString(http.getEntity().getContent());
String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
assertThat(response, containsString("_format=json"));
assertEquals(200, http.getStatusLine().getStatusCode());
@ -342,7 +342,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
get.addHeader("Accept", "application/xml+fhir");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String response = IOUtils.toString(http.getEntity().getContent());
String response = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(response);
assertThat(response, not(containsString("_format")));
assertEquals(200, http.getStatusLine().getStatusCode());
@ -373,7 +373,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
HttpGet get = new HttpGet(ourServerBase + "/$mark-all-resources-for-reindexing");
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertEquals(200, http.getStatusLine().getStatusCode());
} finally {
@ -404,7 +404,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(200, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
Parameters parameters = ourCtx.newXmlParser().parseResource(Parameters.class, output);
@ -434,7 +434,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
CloseableHttpResponse http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'context' must be provided"));
} finally {
@ -445,7 +445,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'searchParam' must be provided"));
} finally {
@ -456,7 +456,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
http = ourHttpClient.execute(get);
try {
assertEquals(400, http.getStatusLine().getStatusCode());
String output = IOUtils.toString(http.getEntity().getContent());
String output = IOUtils.toString(http.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(output);
assertThat(output, containsString("Parameter 'text' must be provided"));
} finally {
@ -500,7 +500,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
public void testTransactionFromBundle2() throws Exception {
InputStream bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
@ -516,7 +516,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
*/
bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/transaction_link_patient_eve_temp.xml");
bundle = IOUtils.toString(bundleRes);
bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
@ -540,7 +540,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
public void testTransactionFromBundle3() throws Exception {
InputStream bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/grahame-transaction.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
}
@ -548,7 +548,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
@Test
public void testTransactionFromBundle4() throws Exception {
InputStream bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/simone_bundle.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
String response = ourClient.transaction().withBundle(bundle).prettyPrint().execute();
ourLog.info(response);
Bundle bundleResp = ourCtx.newXmlParser().parseResource(Bundle.class, response);
@ -563,7 +563,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
@Test
public void testTransactionFromBundle5() throws Exception {
InputStream bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/simone_bundle2.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
try {
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
fail();
@ -577,7 +577,7 @@ public class SystemProviderDstu3Test extends BaseJpaDstu3Test {
@Test
public void testTransactionFromBundle6() throws Exception {
InputStream bundleRes = SystemProviderDstu3Test.class.getResourceAsStream("/simone_bundle3.xml");
String bundle = IOUtils.toString(bundleRes);
String bundle = IOUtils.toString(bundleRes, StandardCharsets.UTF_8);
ourClient.transaction().withBundle(bundle).prettyPrint().execute();
// try {
// fail();

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bundle xmlns="http://hl7.org/fhir">
<type value="transaction"/>
<entry>
<resource>
<Patient>
<id value="ID613876"/>
<identifier>
<value value="ID613876"/>
</identifier>
<name>
<given value="Joshua"/>
</name>
</Patient>
</resource>
<request>
<method value="POST"/>
<url value="Patient/ID613876"/>
<ifNoneExist value="identifier=ID613876"/>
</request>
</entry>
</Bundle>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bundle xmlns="http://hl7.org/fhir">
<type value="transaction"/>
<entry>
<resource>
<Patient>
<id value="ID613876"/>
<identifier>
<value value="ID613876"/>
</identifier>
<name>
<given value="Adam"/>
</name>
</Patient>
</resource>
<request>
<method value="POST"/>
<url value="Patient/ID613876"/>
<ifNoneExist value="identifier=ID613876"/>
</request>
</entry>
</Bundle>

View File

@ -0,0 +1,27 @@
{
"resourceType": "CodeSystem",
"id": "22472",
"meta": {
"versionId": "3",
"lastUpdated": "2017-01-27T10:53:39.457-05:00"
},
"url": "xvalue://dedalus.eu/mci/CodeSystem/AddressUse",
"name": "AddressUse",
"status": "active",
"publisher": "x1v1-mci",
"date": "2016-04-07",
"description": "AddressUse",
"caseSensitive": true,
"compositional": false,
"versionNeeded": false,
"content": "complete",
"concept": {
"code": "work",
"display": "Work",
"definition": "An office address. First choice for business related contacts during business hours.",
"designation": {
"language": "IT",
"value": "Lavoro"
}
}
}

View File

@ -575,6 +575,11 @@ public class SearchDstu2Test {
public int size() {
return 0;
}
@Override
public String getUuid() {
return null;
}
};
}

View File

@ -0,0 +1,5 @@
[*.java]
charset = utf-8
indent_style = space
indent_size = 2

View File

@ -6,33 +6,33 @@ import java.io.FileNotFoundException;
import java.io.IOException;
/*
Copyright (c) 2011+, HL7, Inc
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
* Copyright (c) 2011+, HL7, Inc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
import java.util.ArrayList;
import java.util.HashMap;
@ -72,66 +72,67 @@ import org.hl7.fhir.utilities.Utilities;
public class ValueSetExpanderSimple implements ValueSetExpander {
private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
private IWorkerContext context;
private IWorkerContext context;
private boolean canBeHeirarchy = true;
private Set<String> excludeKeys = new HashSet<String>();
private Set<String> excludeKeys = new HashSet<String>();
private Set<String> excludeSystems = new HashSet<String>();
private ValueSetExpanderFactory factory;
private ValueSet focus;
private int maxExpansionSize = 500;
private ValueSetExpanderFactory factory;
private ValueSet focus;
private int maxExpansionSize = 500;
private int total;
private int total;
public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) {
super();
this.context = context;
this.factory = factory;
}
public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) {
super();
this.context = context;
this.factory = factory;
}
public void setMaxExpansionSize(int theMaxExpansionSize) {
maxExpansionSize = theMaxExpansionSize;
}
public void setMaxExpansionSize(int theMaxExpansionSize) {
maxExpansionSize = theMaxExpansionSize;
}
private ValueSetExpansionContainsComponent addCode(String system, String code, String display, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations, ExpansionProfile profile, boolean isAbstract, boolean inactive, List<ValueSet> filters) {
private ValueSetExpansionContainsComponent addCode(String system, String code, String display, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations,
ExpansionProfile profile, boolean isAbstract, boolean inactive, List<ValueSet> filters) {
if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code))
return null;
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(system);
n.setCode(code);
if (isAbstract)
n.setSystem(system);
n.setCode(code);
if (isAbstract)
n.setAbstract(true);
if (inactive)
n.setInactive(true);
if (profile.getIncludeDesignations() && designations != null) {
for (ConceptDefinitionDesignationComponent t : designations) {
ToolingExtensions.addLanguageTranslation(n, t.getLanguage(), t.getValue());
ToolingExtensions.addLanguageTranslation(n, t.getLanguage(), t.getValue());
}
}
ConceptDefinitionDesignationComponent t = profile.hasLanguage() ? getMatchingLang(designations, profile.getLanguage()) : null;
if (t == null)
n.setDisplay(display);
n.setDisplay(display);
else
n.setDisplay(t.getValue());
String s = key(n);
if (map.containsKey(s) || excludeKeys.contains(s)) {
canBeHeirarchy = false;
} else {
codes.add(n);
map.put(s, n);
}
if (canBeHeirarchy && parent != null) {
parent.getContains().add(n);
} else {
roots.add(n);
}
return n;
}
String s = key(n);
if (map.containsKey(s) || excludeKeys.contains(s)) {
canBeHeirarchy = false;
} else {
codes.add(n);
map.put(s, n);
total++;
}
if (canBeHeirarchy && parent != null) {
parent.getContains().add(n);
} else {
roots.add(n);
}
return n;
}
private boolean filterContainsCode(List<ValueSet> filters, String system, String code) {
for (ValueSet vse : filters)
@ -160,93 +161,92 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
return null;
}
private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filters) throws FHIRException {
if (!CodeSystemUtilities.isDeprecated(cs, def)) {
ValueSetExpansionContainsComponent np = null;
private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filters)
throws FHIRException {
if (!CodeSystemUtilities.isDeprecated(cs, def)) {
ValueSetExpansionContainsComponent np = null;
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
boolean inc = CodeSystemUtilities.isInactive(cs, def);
boolean inc = CodeSystemUtilities.isInactive(cs, def);
if (canBeHeirarchy || !abs)
np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), profile, abs, inc, filters);
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, system, c, np, profile, filters);
} else
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, system, c, null, profile, filters);
} else
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, system, c, null, profile, filters);
}
}
private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile, List<ValueSet> filters) throws ETooCostly {
if (expand.getContains().size() > maxExpansionSize)
throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")");
for (ValueSetExpansionParameterComponent p : expand.getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
if (expand.getContains().size() > maxExpansionSize)
throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")");
for (ValueSetExpansionParameterComponent p : expand.getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
copyImportContains(expand.getContains(), null, profile, filters);
total = expand.getTotal();
}
}
private void excludeCode(String theSystem, String theCode) {
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(theSystem);
n.setCode(theCode);
String s = key(n);
excludeKeys.add(s);
}
private void excludeCode(String theSystem, String theCode) {
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(theSystem);
n.setCode(theCode);
String s = key(n);
excludeKeys.add(s);
}
private void excludeCodes(ConceptSetComponent exc, List<ValueSetExpansionParameterComponent> params) throws TerminologyServiceException {
if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) {
excludeSystems.add(exc.getSystem());
}
private void excludeCodes(ConceptSetComponent exc, List<ValueSetExpansionParameterComponent> params) throws TerminologyServiceException {
if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) {
excludeSystems.add(exc.getSystem());
}
if (exc.hasValueSet())
throw new Error("Processing Value set references in exclude is not yet done");
// importValueSet(imp.getValue(), params, profile);
// importValueSet(imp.getValue(), params, profile);
CodeSystem cs = context.fetchCodeSystem(exc.getSystem());
if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem())) {
excludeCodes(context.expandVS(exc, false), params);
return;
}
CodeSystem cs = context.fetchCodeSystem(exc.getSystem());
if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem())) {
excludeCodes(context.expandVS(exc, false), params);
return;
}
for (ConceptReferenceComponent c : exc.getConcept()) {
excludeCode(exc.getSystem(), c.getCode());
}
if (exc.getFilter().size() > 0)
throw new NotImplementedException("not done yet");
}
for (ConceptReferenceComponent c : exc.getConcept()) {
excludeCode(exc.getSystem(), c.getCode());
}
private void excludeCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) {
for (ValueSetExpansionContainsComponent c : expand.getContains()) {
excludeCode(c.getSystem(), c.getCode());
}
}
if (exc.getFilter().size() > 0)
throw new NotImplementedException("not done yet");
}
private boolean existsInParams(List<ValueSetExpansionParameterComponent> params, String name, Type value) {
for (ValueSetExpansionParameterComponent p : params) {
if (p.getName().equals(name) && PrimitiveType.compareDeep(p.getValue(), value, false))
return true;
}
return false;
}
private void excludeCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) {
for (ValueSetExpansionContainsComponent c : expand.getContains()) {
excludeCode(c.getSystem(), c.getCode());
}
}
@Override
private boolean existsInParams(List<ValueSetExpansionParameterComponent> params, String name, Type value) {
for (ValueSetExpansionParameterComponent p : params) {
if (p.getName().equals(name) && PrimitiveType.compareDeep(p.getValue(), value, false))
return true;
}
return false;
}
@Override
public ValueSetExpansionOutcome expand(ValueSet source, ExpansionProfile profile) {
if (profile == null)
profile = makeDefaultExpansion();
try {
focus = source.copy();
focus.setExpansion(new ValueSet.ValueSetExpansionComponent());
focus.getExpansion().setTimestampElement(DateTimeType.now());
focus.getExpansion().setIdentifier(Factory.createUUID());
try {
focus = source.copy();
focus.setExpansion(new ValueSet.ValueSetExpansionComponent());
focus.getExpansion().setTimestampElement(DateTimeType.now());
focus.getExpansion().setIdentifier(Factory.createUUID());
if (!profile.getUrl().startsWith("urn:uuid:"))
focus.getExpansion().addParameter().setName("profile").setValue(new UriType(profile.getUrl()));
if (source.hasCompose())
if (source.hasCompose())
handleCompose(source.getCompose(), focus.getExpansion().getParameter(), profile);
if (canBeHeirarchy) {
@ -254,38 +254,38 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
focus.getExpansion().getContains().add(c);
}
} else {
for (ValueSetExpansionContainsComponent c : codes) {
for (ValueSetExpansionContainsComponent c : codes) {
if (map.containsKey(key(c)) && !c.getAbstract()) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them
focus.getExpansion().getContains().add(c);
focus.getExpansion().getContains().add(c);
c.getContains().clear(); // make sure any heirarchy is wiped
}
}
}
if (total > 0) {
focus.getExpansion().setTotal(total);
}
}
}
if (total > 0) {
focus.getExpansion().setTotal(total);
}
return new ValueSetExpansionOutcome(focus);
} catch (RuntimeException e) {
// TODO: we should put something more specific instead of just Exception below, since
// it swallows bugs.. what would be expected to be caught there?
throw e;
} catch (RuntimeException e) {
// TODO: we should put something more specific instead of just Exception below, since
// it swallows bugs.. what would be expected to be caught there?
throw e;
} catch (NoTerminologyServiceException e) {
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
// that might fail too, but it might not, later.
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.NOSERVICE);
} catch (Exception e) {
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
// that might fail too, but it might not, later.
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.UNKNOWN);
}
}
} catch (Exception e) {
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
// that might fail too, but it might not, later.
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.UNKNOWN);
}
}
private ExpansionProfile makeDefaultExpansion() {
ExpansionProfile res = new ExpansionProfile();
res.setUrl("urn:uuid:"+UUID.randomUUID().toString().toLowerCase());
res.setExcludeNested(true);
private ExpansionProfile makeDefaultExpansion() {
ExpansionProfile res = new ExpansionProfile();
res.setUrl("urn:uuid:" + UUID.randomUUID().toString().toLowerCase());
res.setExcludeNested(true);
res.setIncludeDesignations(false);
return res;
}
@ -296,68 +296,70 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
}
}
private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException {
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code);
if (def == null)
throw new TerminologyServiceException("Unable to find code '" + code + "' in code system " + cs.getUrl());
return def.getDisplay();
}
private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException {
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code);
if (def == null)
throw new TerminologyServiceException("Unable to find code '" + code + "' in code system " + cs.getUrl());
return def.getDisplay();
}
private ConceptDefinitionComponent getConceptForCode(List<ConceptDefinitionComponent> clist, String code) {
for (ConceptDefinitionComponent c : clist) {
if (code.equals(c.getCode()))
return c;
ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code);
if (v != null)
return v;
}
return null;
}
private ConceptDefinitionComponent getConceptForCode(List<ConceptDefinitionComponent> clist, String code) {
for (ConceptDefinitionComponent c : clist) {
if (code.equals(c.getCode()))
return c;
ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code);
if (v != null)
return v;
}
return null;
}
private void handleCompose(ValueSetComposeComponent compose, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile) throws ETooCostly, FileNotFoundException, IOException, FHIRException {
// Exclude comes first because we build up a map of things to exclude
for (ConceptSetComponent inc : compose.getExclude())
excludeCodes(inc, params);
private void handleCompose(ValueSetComposeComponent compose, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile)
throws ETooCostly, FileNotFoundException, IOException, FHIRException {
// Exclude comes first because we build up a map of things to exclude
for (ConceptSetComponent inc : compose.getExclude())
excludeCodes(inc, params);
canBeHeirarchy = !profile.getExcludeNested() && excludeKeys.isEmpty() && excludeSystems.isEmpty();
boolean first = true;
for (ConceptSetComponent inc : compose.getInclude()) {
if (first == true)
first = false;
else
canBeHeirarchy = false;
boolean first = true;
for (ConceptSetComponent inc : compose.getInclude()) {
if (first == true)
first = false;
else
canBeHeirarchy = false;
includeCodes(inc, params, profile);
}
}
}
}
private ValueSet importValueSet(String value, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile) throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException {
if (value == null)
throw new TerminologyServiceException("unable to find value set with no identity");
ValueSet vs = context.fetchResource(ValueSet.class, value);
if (vs == null)
throw new TerminologyServiceException("Unable to find imported value set " + value);
ValueSetExpansionOutcome vso = factory.getExpander().expand(vs, profile);
private ValueSet importValueSet(String value, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile)
throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException {
if (value == null)
throw new TerminologyServiceException("unable to find value set with no identity");
ValueSet vs = context.fetchResource(ValueSet.class, value);
if (vs == null)
throw new TerminologyServiceException("Unable to find imported value set " + value);
ValueSetExpansionOutcome vso = factory.getExpander().expand(vs, profile);
if (vso.getError() != null)
throw new TerminologyServiceException("Unable to expand imported value set: "+vso.getError());
if (vso.getService() != null)
throw new TerminologyServiceException("Unable to expand imported value set "+value);
if (vs.hasVersion())
if (!existsInParams(params, "version", new UriType(vs.getUrl()+"|"+vs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(vs.getUrl()+"|"+vs.getVersion())));
for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
throw new TerminologyServiceException("Unable to expand imported value set: " + vso.getError());
if (vso.getService() != null)
throw new TerminologyServiceException("Unable to expand imported value set " + value);
if (vs.hasVersion())
if (!existsInParams(params, "version", new UriType(vs.getUrl() + "|" + vs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(vs.getUrl() + "|" + vs.getVersion())));
for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
canBeHeirarchy = false; // if we're importing a value set, we have to be combining, so we won't try for a heirarchy
return vso.getValueset();
return vso.getValueset();
}
private void copyImportContains(List<ValueSetExpansionContainsComponent> list, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filter) {
for (ValueSetExpansionContainsComponent c : list) {
ValueSetExpansionContainsComponent np = addCode(c.getSystem(), c.getCode(), c.getDisplay(), parent, null, profile, c.getAbstract(), c.getInactive(), filter);
copyImportContains(c.getContains(), np, profile, filter);
}
}
}
}
private void includeCodes(ConceptSetComponent inc, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile) throws ETooCostly, FileNotFoundException, IOException, FHIRException {
List<ValueSet> imports = new ArrayList<ValueSet>();
@ -367,9 +369,9 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (!inc.hasSystem()) {
if (imports.isEmpty()) // though this is not supposed to be the case
return;
ValueSet base = imports.get(0);
ValueSet base = imports.get(0);
imports.remove(0);
copyImportContains(base.getExpansion().getContains(), null, profile, imports);
copyImportContains(base.getExpansion().getContains(), null, profile, imports);
} else {
CodeSystem cs = context.fetchCodeSystem(inc.getSystem());
if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(inc.getSystem())) {
@ -379,15 +381,15 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (cs == null) {
if (context.isNoTerminologyServer())
throw new NoTerminologyServiceException("unable to find code system "+inc.getSystem().toString());
throw new NoTerminologyServiceException("unable to find code system " + inc.getSystem().toString());
else
throw new TerminologyServiceException("unable to find code system "+inc.getSystem().toString());
throw new TerminologyServiceException("unable to find code system " + inc.getSystem().toString());
}
if (cs.getContent() != CodeSystemContentMode.COMPLETE)
throw new TerminologyServiceException("Code system "+inc.getSystem().toString()+" is incomplete");
throw new TerminologyServiceException("Code system " + inc.getSystem().toString() + " is incomplete");
if (cs.hasVersion())
if (!existsInParams(params, "version", new UriType(cs.getUrl()+"|"+cs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(cs.getUrl()+"|"+cs.getVersion())));
if (!existsInParams(params, "version", new UriType(cs.getUrl() + "|" + cs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(cs.getUrl() + "|" + cs.getVersion())));
if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
// special case - add all the code system
@ -399,7 +401,8 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (!inc.getConcept().isEmpty()) {
canBeHeirarchy = false;
for (ConceptReferenceComponent c : inc.getConcept()) {
addCode(inc.getSystem(), c.getCode(), Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay(), null, convertDesignations(c.getDesignation()), profile, false, CodeSystemUtilities.isInactive(cs, c.getCode()), imports);
addCode(inc.getSystem(), c.getCode(), Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay(), null, convertDesignations(c.getDesignation()), profile, false,
CodeSystemUtilities.isInactive(cs, c.getCode()), imports);
}
}
if (inc.getFilter().size() > 1) {
@ -412,13 +415,13 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
// special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw new TerminologyServiceException("Code '"+fc.getValue()+"' not found in system '"+inc.getSystem()+"'");
throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
addCodeAndDescendents(cs, inc.getSystem(), def, null, profile, imports);
} else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) {
// special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null)
throw new TerminologyServiceException("Code '"+fc.getValue()+"' not found in system '"+inc.getSystem()+"'");
throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, inc.getSystem(), c, null, profile, imports);
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
@ -428,7 +431,8 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
if (def.getDisplay().contains(fc.getValue())) {
addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), profile, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def), imports);
addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), profile, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
imports);
}
}
}
@ -449,12 +453,12 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
return res;
}
private String key(String uri, String code) {
return "{" + uri + "}" + code;
}
private String key(String uri, String code) {
return "{" + uri + "}" + code;
}
private String key(ValueSetExpansionContainsComponent c) {
return key(c.getSystem(), c.getCode());
}
private String key(ValueSetExpansionContainsComponent c) {
return key(c.getSystem(), c.getCode());
}
}

View File

@ -25,7 +25,9 @@ import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.hamcrest.collection.IsEmptyCollection;
@ -76,6 +78,24 @@ public class XmlParserDstu3Test {
ourCtx.setNarrativeGenerator(null);
}
/**
* See #551
*/
@Test
public void testXmlLargeAttribute() {
String largeString = StringUtils.leftPad("", (int) FileUtils.ONE_MB, 'A');
Patient p = new Patient();
p.addName().setFamily(largeString);
String encoded = ourCtx.newXmlParser().encodeResourceToString(p);
p = ourCtx.newXmlParser().parseResource(Patient.class, encoded);
assertEquals(largeString, p.getNameFirstRep().getFamily());
}
/**
* See #544
*/

View File

@ -98,7 +98,7 @@ public class GenericClientDstu3Test {
byte[] body = IOUtils.toByteArray(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent());
return body;
}
private String extractBodyAsString(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent(), "UTF-8");
return body;
@ -120,6 +120,26 @@ public class GenericClientDstu3Test {
return capt;
}
@Test
public void testRevIncludeRecursive() throws ClientProtocolException, IOException {
ArgumentCaptor<HttpUriRequest> capt = prepareClientForSearchResponse();
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
int idx = 0;
client.search()
.forResource(EpisodeOfCare.class)
.where(EpisodeOfCare.PATIENT.hasId("123"))
.revInclude(Encounter.INCLUDE_EPISODEOFCARE)
.revInclude(Observation.INCLUDE_ENCOUNTER.asRecursive())
.returnBundle(Bundle.class)
.execute();
assertEquals("http://example.com/fhir/EpisodeOfCare?patient=123&_revinclude=Encounter%3Aepisodeofcare&_revinclude%3Arecurse=Observation%3Aencounter", capt.getAllValues().get(idx).getURI().toString());
idx++;
}
@Test
public void testPatchJsonByIdString() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -143,20 +163,21 @@ public class GenericClientDstu3Test {
String patch = "[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.withId("Patient/123")
.execute();
.patch()
.withBody(patch)
.withId("Patient/123")
.execute();
assertEquals("http://example.com/fhir/Patient/123", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchJsonByIdType() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -180,20 +201,21 @@ public class GenericClientDstu3Test {
String patch = "[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.withId(new IdType("http://localhost/fhir/Patient/123/_history/234"))
.execute();
.patch()
.withBody(patch)
.withId(new IdType("http://localhost/fhir/Patient/123/_history/234"))
.execute();
assertEquals("http://example.com/fhir/Patient/123", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchJsonByConditionalString() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -217,20 +239,21 @@ public class GenericClientDstu3Test {
String patch = "[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.conditionalByUrl("Patient?foo=bar")
.execute();
.patch()
.withBody(patch)
.conditionalByUrl("Patient?foo=bar")
.execute();
assertEquals("http://example.com/fhir/Patient?foo=bar", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchJsonByConditionalParam() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -254,21 +277,22 @@ public class GenericClientDstu3Test {
String patch = "[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.conditional("Patient").where(Patient.NAME.matches().value("TEST"))
.and(Patient.FAMILY.matches().value("TEST2"))
.execute();
.patch()
.withBody(patch)
.conditional("Patient").where(Patient.NAME.matches().value("TEST"))
.and(Patient.FAMILY.matches().value("TEST2"))
.execute();
assertEquals("http://example.com/fhir/Patient?name=TEST&family=TEST2", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchJsonByConditionalParamResourceType() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -292,21 +316,22 @@ public class GenericClientDstu3Test {
String patch = "[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.conditional(Patient.class).where(Patient.NAME.matches().value("TEST"))
.and(Patient.FAMILY.matches().value("TEST2"))
.execute();
.patch()
.withBody(patch)
.conditional(Patient.class).where(Patient.NAME.matches().value("TEST"))
.and(Patient.FAMILY.matches().value("TEST2"))
.execute();
assertEquals("http://example.com/fhir/Patient?name=TEST&family=TEST2", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchXmlByIdString() throws Exception {
OperationOutcome conf = new OperationOutcome();
@ -330,31 +355,31 @@ public class GenericClientDstu3Test {
String patch = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><diff xmlns:fhir=\"http://hl7.org/fhir\"><replace sel=\"fhir:Patient/fhir:active/@value\">false</replace></diff>";
MethodOutcome outcome = client
.patch()
.withBody(patch)
.withId("Patient/123")
.execute();
.patch()
.withBody(patch)
.withId("Patient/123")
.execute();
assertEquals("http://example.com/fhir/Patient/123", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("PATCH", capt.getAllValues().get(0).getRequestLine().getMethod());
assertEquals(patch, extractBodyAsString(capt));
assertEquals(Constants.CT_XML_PATCH, capt.getAllValues().get(idx).getFirstHeader("Content-Type").getValue().replaceAll(";.*", ""));
idx++;
OperationOutcome oo = (OperationOutcome) outcome.getOperationOutcome();
assertThat(oo.getText().getDivAsString(), containsString("OK!"));
}
@Test
public void testPatchInvalid() throws Exception {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
try {
client
.patch()
.withBody("AA")
.withId("Patient/123")
.execute();
.patch()
.withBody("AA")
.withId("Patient/123")
.execute();
} catch (IllegalArgumentException e) {
assertEquals("Unable to determine encoding of patch", e.getMessage());
}
@ -388,7 +413,7 @@ public class GenericClientDstu3Test {
assertEquals("http://example.com/fhir/Device?_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("application/fhir+json;q=1.0, application/json+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
idx++;
//@formatter:off
client.setEncoding(EncodingEnum.XML);
client.search()
@ -399,7 +424,7 @@ public class GenericClientDstu3Test {
assertEquals("http://example.com/fhir/Device?_format=xml", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
assertEquals("application/fhir+xml;q=1.0, application/xml+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
idx++;
}
@Test
@ -480,9 +505,7 @@ public class GenericClientDstu3Test {
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
}
@SuppressWarnings("unchecked")
@Test
public void testClientFailures() throws Exception {
@ -592,7 +615,7 @@ public class GenericClientDstu3Test {
assertEquals(myAnswerCount, capt.getAllValues().size());
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
assertEquals(Constants.CT_FHIR_XML_NEW, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
assertEquals("http://foo.com/base/Patient/222/_history/3", capt.getAllValues().get(1).getURI().toASCIIString());
}
@ -1027,7 +1050,7 @@ public class GenericClientDstu3Test {
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
client
.search()
@ -1053,22 +1076,22 @@ public class GenericClientDstu3Test {
idx++;
}
@Test
public void testPutDoesntForceAllIdsJson() throws Exception {
IParser p = ourCtx.newJsonParser();
Patient patient = new Patient();
patient.setId("PATIENT1");
patient.addName().setFamily("PATIENT1");
Bundle bundle = new Bundle();
bundle.setId("BUNDLE1");
bundle.addEntry().setResource(patient);
final String encoded = p.encodeResourceToString(bundle);
assertEquals("{\"resourceType\":\"Bundle\",\"id\":\"BUNDLE1\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"PATIENT1\",\"name\":[{\"family\":\"PATIENT1\"}]}}]}", encoded);
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"));
@ -1251,7 +1274,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=gt"+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=gt" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1261,7 +1284,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=gt"+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=gt" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1271,7 +1294,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=ge"+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=ge" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1281,7 +1304,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=lt"+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=lt" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1291,7 +1314,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate=le"+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=le" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1301,7 +1324,7 @@ public class GenericClientDstu3Test {
.returnBundle(Bundle.class)
.execute();
//@formatter:on
assertEquals("http://example.com/fhir/Patient?birthdate="+dateString, capt.getAllValues().get(idx).getURI().toString());
assertEquals("http://example.com/fhir/Patient?birthdate=" + dateString, capt.getAllValues().get(idx).getURI().toString());
idx++;
//@formatter:off
@ -1639,7 +1662,7 @@ public class GenericClientDstu3Test {
int idx = 0;
Collection<String> values = Arrays.asList("VAL1", "VAL2", "VAL3A,B");
//@formatter:off
client.search()
.forResource("Patient")
@ -1743,7 +1766,6 @@ public class GenericClientDstu3Test {
assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage());
}
}
@Test
@ -2012,7 +2034,7 @@ public class GenericClientDstu3Test {
MyPatientWithExtensions patient = new MyPatientWithExtensions();
patient.setId("123");
patient.getText().setDivAsString("OK!");
final String respString = p.encodeResourceToString(patient);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
@ -2028,7 +2050,7 @@ public class GenericClientDstu3Test {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
MyPatientWithExtensions read = client.read().resource(MyPatientWithExtensions.class).withId(new IdType("1")).execute();
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div>", read.getText().getDivAsString());
// Ensure that we haven't overridden the default type for the name
assertFalse(MyPatientWithExtensions.class.isAssignableFrom(Patient.class));
assertFalse(Patient.class.isAssignableFrom(MyPatientWithExtensions.class));
@ -2037,7 +2059,7 @@ public class GenericClientDstu3Test {
IParser parser = ourCtx.newXmlParser();
String encoded = parser.encodeResourceToString(pt);
pt = (Patient) parser.parseResource(encoded);
}
@Test
@ -2045,13 +2067,12 @@ public class GenericClientDstu3Test {
IParser p = ourCtx.newXmlParser();
Bundle b = new Bundle();
MyPatientWithExtensions patient = new MyPatientWithExtensions();
patient.setId("123");
patient.getText().setDivAsString("OK!");
b.addEntry().setResource(patient);
final String respString = p.encodeResourceToString(b);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
@ -2066,7 +2087,7 @@ public class GenericClientDstu3Test {
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
Bundle bundle = client.search().forResource(MyPatientWithExtensions.class).returnBundle(Bundle.class).execute();
assertEquals(1, bundle.getEntry().size());
assertEquals(MyPatientWithExtensions.class, bundle.getEntry().get(0).getResource().getClass());
}
@ -2082,8 +2103,6 @@ public class GenericClientDstu3Test {
assertEquals(expectedUserAgent(), capt.getAllValues().get(0).getHeaders("User-Agent")[0].getValue());
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
@ -2093,5 +2112,5 @@ public class GenericClientDstu3Test {
public static void beforeClass() {
ourCtx = FhirContext.forDstu3();
}
}

View File

@ -1,8 +1,7 @@
package ca.uhn.fhir.rest.server;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.oneOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
@ -65,7 +64,7 @@ public class SearchDefaultMethodDstu3Test {
ourLog.info(responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
assertThat(ourLastMethod, either(equalTo("search01")).or(equalTo("search02")));
assertThat(ourLastMethod, oneOf("search01", "search02", "search03"));
assertEquals(null, ourLastParam1);
assertEquals(null, ourLastParam2);
assertEquals(null, ourLastAdditionalParams);

View File

@ -0,0 +1,5 @@
[*.java]
charset = utf-8
indent_style = space
indent_size = 2

View File

@ -152,6 +152,7 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IValid
throw new ConfigurationException(e);
}
v.setShouldCheckForIdPresence(false);
v.setBestPracticeWarningLevel(myBestPracticeWarningLevel);
v.setAnyExtensionsAllowed(true);
v.setRequireResourceId(false);

View File

@ -80,7 +80,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// used during the build process to keep the overall volume of messages down
private boolean suppressLoincSnomedMessages;
public InstanceValidator(IWorkerContext theContext) throws Exception {
private boolean shouldCheckForIdPresence = true;
public InstanceValidator(IWorkerContext theContext) throws Exception {
super();
this.context = theContext;
source = Source.InstanceValidator;
@ -117,6 +119,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
}
public void setShouldCheckForIdPresence(boolean theShouldCheckForIdPresence) {
shouldCheckForIdPresence = theShouldCheckForIdPresence;
}
private boolean check(String v1, String v2) {
return v1 == null ? Utilities.noString(v1) : v1.equals(v2);
}
@ -1825,7 +1831,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
if (type.equals("Extension"))
checkExtension(errors, ei.path, ei.element, ei.definition, profile, localStack);
else if (type.equals("Resource"))
validateContains(errors, ei.path, ei.definition, definition, ei.element, localStack, !isBundleEntry(ei.path) && !isParametersEntry(ei.path)); // if
validateContains(errors, ei.path, ei.definition, definition, ei.element, localStack, !isBundleEntry(ei.path) && !isParametersEntry(ei.path) && shouldCheckForIdPresence); // if
// (str.matches(".*([.,/])work\\1$"))
else {
StructureDefinition p = getProfileForType(type);

View File

@ -160,6 +160,11 @@ public class SearchHl7OrgDstu2Test {
public int size() {
return 0;
}
@Override
public String getUuid() {
return null;
}
};
}

File diff suppressed because it is too large Load Diff