diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ISort.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ISort.java index acc3759746b..58807b32839 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ISort.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/gclient/ISort.java @@ -45,7 +45,7 @@ public interface ISort { /** * Sort descending * - * @param A query param - Could be a constant such as Patient.ADDRESS or a custom + * @param theParam A query param - Could be a constant such as Patient.ADDRESS or a custom * param such as new StringClientParam("foo") */ IQuery descending(IParam theParam); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java index c05a5b432ba..41bfee6d0e6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/interceptor/IServerInterceptor.java @@ -187,12 +187,6 @@ public interface IServerInterceptor { * A bean containing details about the request that is about to be processed, including * @param theResponseObject * The actual object which is being streamed to the client as a response - * @param theServletRequest - * The incoming request - * @param theServletResponse - * The response. Note that interceptors may choose to provide a response (i.e. by calling - * {@link HttpServletResponse#getWriter()}) but in that case it is important to return false - * to indicate that the server itself should not also provide a response. * @return Return true if processing should continue normally. This is generally the right thing to do. * If your interceptor is providing a response rather than letting HAPI handle the response normally, you * must return false. In this case, no further processing will occur and no further interceptors @@ -201,7 +195,7 @@ public interface IServerInterceptor { * This exception may be thrown to indicate that the interceptor has detected an unauthorized access * attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client. */ - boolean outgoingResponse(RequestDetails theRequest, Bundle bundle); + boolean outgoingResponse(RequestDetails theRequest, Bundle theResponseObject); /** * This method is called after the server implementation method has been called, but before any attempt to stream the diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java index 20f694e62b1..7b95cb667e2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/BaseDstu2Config.java @@ -20,6 +20,16 @@ package ca.uhn.fhir.jpa.config; * #L% */ +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.dao.*; +import ca.uhn.fhir.jpa.term.HapiTerminologySvcDstu2; +import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; +import ca.uhn.fhir.model.dstu2.composite.MetaDt; +import ca.uhn.fhir.validation.IValidatorModule; +import org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport; +import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator; +import org.hl7.fhir.instance.hapi.validation.ValidationSupportChain; +import org.hl7.fhir.instance.validation.IResourceValidator.BestPracticeWarningLevel; import org.springframework.beans.factory.annotation.Autowire; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -27,17 +37,6 @@ import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; import org.springframework.transaction.annotation.EnableTransactionManagement; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.dao.FulltextSearchSvcImpl; -import ca.uhn.fhir.jpa.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; -import ca.uhn.fhir.jpa.dao.ISearchParamRegistry; -import ca.uhn.fhir.jpa.dao.SearchParamExtractorDstu2; -import ca.uhn.fhir.jpa.dao.SearchParamRegistryDstu2; -import ca.uhn.fhir.jpa.term.HapiTerminologySvcDstu2; -import ca.uhn.fhir.jpa.term.IHapiTerminologySvc; -import ca.uhn.fhir.model.dstu2.composite.MetaDt; - @Configuration @EnableTransactionManagement public class BaseDstu2Config extends BaseConfig { @@ -75,6 +74,15 @@ public class BaseDstu2Config extends BaseConfig { return retVal; } + @Bean(name = "myInstanceValidatorDstu2") + @Lazy + public IValidatorModule instanceValidatorDstu2() { + FhirInstanceValidator retVal = new FhirInstanceValidator(); + retVal.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning); + retVal.setValidationSupport(new ValidationSupportChain(new DefaultProfileValidationSupport(), jpaValidationSupportDstu2())); + return retVal; + } + @Bean(autowire = Autowire.BY_TYPE) public IFulltextSearchSvc searchDao() { FulltextSearchSvcImpl searchDao = new FulltextSearchSvcImpl(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index d4b5053a328..80bff07e751 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -312,29 +312,14 @@ public abstract class BaseHapiFhirDao implements IDao { } } - Set treatReferencesAsLogical = myConfig.getTreatReferencesAsLogical(); - if (treatReferencesAsLogical != null) { - boolean isLogical = false; - for (String nextLogicalRef : treatReferencesAsLogical) { - nextLogicalRef = trim(nextLogicalRef); - if (nextLogicalRef.charAt(nextLogicalRef.length() - 1) == '*') { - if (nextId.getValue().startsWith(nextLogicalRef.substring(0, nextLogicalRef.length() -1))) { - isLogical = true; - break; - } - } else { - if (nextId.getValue().equals(nextLogicalRef)) { - isLogical = true; - break; - } - } - } - - if (isLogical) { - continue; + if (isLogicalReference(nextId)) { + ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime); + if (theLinks.add(resourceLink)) { + ourLog.info("Indexing remote resource reference URL: {}", nextId); } + continue; } - + String baseUrl = nextId.getBaseUrl(); String typeString = nextId.getResourceType(); if (isBlank(typeString)) { @@ -412,6 +397,26 @@ public abstract class BaseHapiFhirDao implements IDao { } + protected boolean isLogicalReference(IIdType theId) { + Set treatReferencesAsLogical = myConfig.getTreatReferencesAsLogical(); + if (treatReferencesAsLogical != null) { + for (String nextLogicalRef : treatReferencesAsLogical) { + nextLogicalRef = trim(nextLogicalRef); + if (nextLogicalRef.charAt(nextLogicalRef.length() - 1) == '*') { + if (theId.getValue().startsWith(nextLogicalRef.substring(0, nextLogicalRef.length() - 1))) { + return true; + } + } else { + if (theId.getValue().equals(nextLogicalRef)) { + return true; + } + } + } + + } + return false; + } + protected Set extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) { return mySearchParamExtractor.extractSearchParamCoords(theEntity, theResource); } @@ -959,12 +964,11 @@ public abstract class BaseHapiFhirDao 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 true if the tag should be removed */ - @SuppressWarnings("unused") protected void postPersist(ResourceTable theEntity, T theResource) { // nothing } @@ -973,9 +977,9 @@ public abstract class BaseHapiFhirDao 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 @@ -998,7 +1002,6 @@ public abstract class BaseHapiFhirDao implements IDao { return ids; } - @SuppressWarnings("unused") @CoverageIgnore public BaseHasResource readEntity(IIdType theValueId) { throw new NotImplementedException(""); @@ -1031,9 +1034,9 @@ public abstract class BaseHapiFhirDao implements IDao { *

* * @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 true if the tag should be removed */ protected boolean shouldDroppedTagBeRemovedOnUpdate(ResourceTable theEntity, ResourceTag theTag) { @@ -1269,7 +1272,7 @@ public abstract class BaseHapiFhirDao implements IDao { setUpdatedTime(uriParams, theUpdateTime); setUpdatedTime(coordsParams, theUpdateTime); setUpdatedTime(tokenParams, theUpdateTime); - + /* * 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 @@ -1531,11 +1534,14 @@ public abstract class BaseHapiFhirDao implements IDao { for (IBase nextChild : values) { IBaseReference nextRef = (IBaseReference) nextChild; - if (!isBlank(nextRef.getReferenceElement().getResourceType())) { - if (!nextRef.getReferenceElement().getValue().contains("?")) { - if (!validTypes.contains(nextRef.getReferenceElement().getResourceType())) { - throw new UnprocessableEntityException( - "Invalid reference found at path '" + newPath + "'. Resource type '" + nextRef.getReferenceElement().getResourceType() + "' is not valid for this path"); + IIdType referencedId = nextRef.getReferenceElement(); + if (!isBlank(referencedId.getResourceType())) { + if (!isLogicalReference(referencedId)) { + if (!referencedId.getValue().contains("?")) { + if (!validTypes.contains(referencedId.getResourceType())) { + throw new UnprocessableEntityException( + "Invalid reference found at path '" + newPath + "'. Resource type '" + referencedId.getResourceType() + "' is not valid for this path"); + } } } } @@ -1575,9 +1581,9 @@ public abstract class BaseHapiFhirDao 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; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java index 675cae7cb5c..930fdcae038 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoConfig.java @@ -56,7 +56,8 @@ public class DaoConfig { // *** // update setter javadoc if default changes // *** - private boolean myAllowInlineMatchUrlReferences = false; + private boolean myAllowInlineMatchUrlReferences = true; + private boolean myAllowMultipleDelete; private boolean myDefaultSearchParamsCanBeOverridden = false; // *** @@ -93,6 +94,20 @@ public class DaoConfig { private Set myTreatBaseUrlsAsLocal = new HashSet(); private Set myTreatReferencesAsLogical = new HashSet(DEFAULT_LOGICAL_BASE_URLS); + /** + * Add a value to the {@link #setTreatReferencesAsLogical(Set) logical references list}. + * + * @see #setTreatReferencesAsLogical(Set) + */ + public void addTreatReferencesAsLogical(String theTreatReferencesAsLogical) { + validateTreatBaseUrlsAsLocal(theTreatReferencesAsLogical); + + if (myTreatReferencesAsLogical == null) { + myTreatReferencesAsLogical = new HashSet(); + } + myTreatReferencesAsLogical.add(theTreatReferencesAsLogical); + } + /** * When a code system is added that contains more than this number of codes, * the code system will be indexed later in an incremental process in order to @@ -115,9 +130,9 @@ public class DaoConfig { * (next/prev links in search response bundles) will become invalid. Defaults to 1 hour. *

*

- * - * @see To disable this feature entirely, see {@link #setExpireSearchResults(boolean)} - *

+ *

+ * To disable this feature entirely, see {@link #setExpireSearchResults(boolean)} + *

* * @since 1.5 */ @@ -196,9 +211,9 @@ public class DaoConfig { * references instead of being treated as real references. *

* A logical reference is a reference which is treated as an identifier, and - * does not neccesarily resolve. See {@link http://hl7.org/fhir/references.html} for + * does not neccesarily resolve. See {@link "http://hl7.org/fhir/references.html"} for * a description of logical references. For example, the valueset - * {@link http://hl7.org/fhir/valueset-quantity-comparator.html} is a logical + * {@link "http://hl7.org/fhir/valueset-quantity-comparator.html"} is a logical * reference. *

*

@@ -209,7 +224,7 @@ public class DaoConfig { *

  • http://example.com/some-base* (will match anything beginning with the part before the *)
  • * * - * @see #DEFAULT_LOGICAL_BASE_URLS for a list of default values for this setting + * @see #DEFAULT_LOGICAL_BASE_URLS Default values for this property */ public Set getTreatReferencesAsLogical() { return myTreatReferencesAsLogical; @@ -337,7 +352,9 @@ public class DaoConfig { * to "Patient?identifier=12345", this is reference match URL will be resolved and replaced according * to the usual match URL rules. *

    - * Default is false for now, as this is an experimental feature. + * Default is {@literal true} beginning in HAPI FHIR 2.4, since this + * feature is now specified in the FHIR specification. (Previously it + * was an experimental/rpposed feature) *

    * * @since 1.5 @@ -401,8 +418,10 @@ public class DaoConfig { *

    *

    * - * @see To disable this feature entirely, see {@link #setExpireSearchResults(boolean)} - *

    + *

    + * To disable this feature entirely, see {@link #setExpireSearchResults(boolean)} + *

    + * * @since 1.5 */ public void setExpireSearchResultsAfterMillis(long theExpireSearchResultsAfterMillis) { @@ -418,7 +437,7 @@ public class DaoConfig { * paging provider instead. Deprecated in HAPI FHIR 2.3 (Jan 2017) */ @Deprecated - public void setHardSearchLimit(@SuppressWarnings("unused") int theHardSearchLimit) { + public void setHardSearchLimit(int theHardSearchLimit) { // this method does nothing } @@ -528,6 +547,12 @@ public class DaoConfig { * means no references will be treated as external */ public void setTreatBaseUrlsAsLocal(Set theTreatBaseUrlsAsLocal) { + if (theTreatBaseUrlsAsLocal != null) { + for (String next : theTreatBaseUrlsAsLocal) { + validateTreatBaseUrlsAsLocal(next); + } + } + HashSet treatBaseUrlsAsLocal = new HashSet(); for (String next : ObjectUtils.defaultIfNull(theTreatBaseUrlsAsLocal, new HashSet())) { while (next.endsWith("/")) { @@ -544,9 +569,9 @@ public class DaoConfig { * references instead of being treated as real references. *

    * A logical reference is a reference which is treated as an identifier, and - * does not neccesarily resolve. See {@link http://hl7.org/fhir/references.html} for + * does not neccesarily resolve. See {@link "http://hl7.org/fhir/references.html"} for * a description of logical references. For example, the valueset - * {@link http://hl7.org/fhir/valueset-quantity-comparator.html} is a logical + * {@link "http://hl7.org/fhir/valueset-quantity-comparator.html"} is a logical * reference. *

    *

    @@ -557,11 +582,23 @@ public class DaoConfig { *

  • http://example.com/some-base* (will match anything beginning with the part before the *)
  • * * - * @see #DEFAULT_LOGICAL_BASE_URLS for a list of default values for this setting + * @see #DEFAULT_LOGICAL_BASE_URLS Default values for this property */ public DaoConfig setTreatReferencesAsLogical(Set theTreatReferencesAsLogical) { myTreatReferencesAsLogical = theTreatReferencesAsLogical; return this; } + private static void validateTreatBaseUrlsAsLocal(String theUrl) { + Validate.notBlank(theUrl, "Base URL must not be null or empty"); + + int starIdx = theUrl.indexOf('*'); + if (starIdx != -1) { + if (starIdx != theUrl.length() - 1) { + throw new IllegalArgumentException("Base URL wildcard character (*) can only appear at the end of the string: " + theUrl); + } + } + + } + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java index 62ae1312e66..7df0c6be92a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirResourceDaoDstu2.java @@ -1,43 +1,5 @@ package ca.uhn.fhir.jpa.dao; -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import java.util.ArrayList; - -/* - * #%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.Collections; -import java.util.List; - -import org.hl7.fhir.instance.hapi.validation.DefaultProfileValidationSupport; -import org.hl7.fhir.instance.hapi.validation.FhirInstanceValidator; -import org.hl7.fhir.instance.hapi.validation.IValidationSupport; -import org.hl7.fhir.instance.hapi.validation.ValidationSupportChain; -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.validation.IResourceValidator.BestPracticeWarningLevel; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; - import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.jpa.entity.ResourceTable; @@ -64,6 +26,38 @@ import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.IValidationContext; import ca.uhn.fhir.validation.IValidatorModule; import ca.uhn.fhir.validation.ValidationResult; +import org.hl7.fhir.instance.hapi.validation.IValidationSupport; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static org.apache.commons.lang3.StringUtils.isNotBlank; + +/* + * #%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% + */ public class FhirResourceDaoDstu2 extends BaseHapiFhirResourceDao { @@ -71,6 +65,9 @@ public class FhirResourceDaoDstu2 extends BaseHapiFhirResou @Qualifier("myJpaValidationSupportDstu2") private IValidationSupport myJpaValidationSupport; + @Autowired() + @Qualifier("myInstanceValidatorDstu2") + private IValidatorModule myInstanceValidator; @Override protected List getIncludeValues(FhirTerser theTerser, Include theInclude, IBaseResource theResource, RuntimeResourceDefinition theResourceDef) { @@ -124,10 +121,7 @@ public class FhirResourceDaoDstu2 extends BaseHapiFhirResou FhirValidator validator = getContext().newValidator(); - FhirInstanceValidator val = new FhirInstanceValidator(); - val.setBestPracticeWarningLevel(BestPracticeWarningLevel.Warning); - val.setValidationSupport(new ValidationSupportChain(new DefaultProfileValidationSupport(), myJpaValidationSupport)); - validator.registerValidatorModule(val); + validator.registerValidatorModule(myInstanceValidator); validator.registerValidatorModule(new IdChecker(theMode)); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java index 51560436e1f..824133f0921 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestDstu2Config.java @@ -1,21 +1,20 @@ package ca.uhn.fhir.jpa.config; -import java.util.Properties; - -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; - +import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; +import ca.uhn.fhir.validation.ResultSeverityEnum; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.jpa.HibernatePersistenceProvider; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.Lazy; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.dao.DaoConfig; +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; +import java.util.Properties; @Configuration @EnableTransactionManagement() @@ -65,4 +64,19 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 { return extraProperties; } + /** + * Bean which validates incoming requests + */ + @Bean + @Lazy + public RequestValidatingInterceptor requestValidatingInterceptor() { + RequestValidatingInterceptor requestValidator = new RequestValidatingInterceptor(); + requestValidator.setFailOnSeverity(ResultSeverityEnum.ERROR); + requestValidator.setAddResponseHeaderOnSeverity(null); + requestValidator.setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum.INFORMATION); + requestValidator.addValidatorModule(instanceValidatorDstu2()); + + return requestValidator; + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/DaoConfigTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/DaoConfigTest.java new file mode 100644 index 00000000000..277f06668e6 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/DaoConfigTest.java @@ -0,0 +1,34 @@ +package ca.uhn.fhir.jpa.dao; + +import static org.junit.Assert.*; + +import java.util.Arrays; +import java.util.HashSet; + +import org.junit.Test; + +public class DaoConfigTest { + + @Test + public void testValidLogicalPattern() { + new DaoConfig().setTreatBaseUrlsAsLocal(new HashSet(Arrays.asList("http://foo"))); + new DaoConfig().setTreatBaseUrlsAsLocal(new HashSet(Arrays.asList("http://foo*"))); + } + + @Test + public void testInvalidLogicalPattern() { + try { + new DaoConfig().setTreatBaseUrlsAsLocal(new HashSet(Arrays.asList("http://*foo"))); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Base URL wildcard character (*) can only appear at the end of the string: http://*foo", e.getMessage()); + } + try { + new DaoConfig().setTreatBaseUrlsAsLocal(new HashSet(Arrays.asList("http://foo**"))); + fail(); + } catch (IllegalArgumentException e) { + assertEquals("Base URL wildcard character (*) can only appear at the end of the string: http://foo**", e.getMessage()); + } + } + +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java index d066eb2b729..c24b2f970bc 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirResourceDaoDstu2Test.java @@ -65,28 +65,12 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu2Test.class); - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); + @After + public final void after() { + myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences()); + myDaoConfig.setTreatReferencesAsLogical(new DaoConfig().getTreatReferencesAsLogical()); } - /** - * See #534 - */ - @Test - public void testBuiltInLogicalReferences() throws IOException { - myDaoConfig.getTreatReferencesAsLogical().add("http://phr.kanta.fi/fiphr-vs-*"); - - ValueSet vsBodySite = loadResourceFromClasspath(ValueSet.class, "/issue534/fiphr-vs-bodysite.xml"); - myValueSetDao.create(vsBodySite, mySrd); - ValueSet vsObsMethod = loadResourceFromClasspath(ValueSet.class, "/issue534/fiphr-vs-observationmethod.xml"); - myValueSetDao.create(vsObsMethod, mySrd); - - StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/issue534/bw_profile_snapshot.xml"); - myStructureDefinitionDao.create(sd, mySrd); - } - - private void assertGone(IIdType theId) { try { assertNotGone(theId); @@ -96,82 +80,13 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } } - @After - public final void after() { - myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences()); - myDaoConfig.setTreatReferencesAsLogical(new DaoConfig().getTreatReferencesAsLogical()); - } - - - @Test - public void testValidateAgainstDstu2Profile() throws Exception { - myDaoConfig.setAllowExternalReferences(true); - - String stream = IOUtils.toString(getClass().getResourceAsStream("/binu_testpatient_structuredefinition_dstu2.xml"), StandardCharsets.UTF_8); - - StructureDefinition sd = myFhirCtx.newXmlParser().parseResource(StructureDefinition.class, stream); - myStructureDefinitionDao.create(sd, mySrd); - - String rawResource = IOUtils.toString(getClass().getResourceAsStream("/binu_testpatient_resource.json"), StandardCharsets.UTF_8); - try { - myValueSetDao.validate(null, null, rawResource, EncodingEnum.JSON, ValidationModeEnum.UPDATE, null, mySrd); - fail(); - } catch (PreconditionFailedException e) { - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome())); - } - } - - - @Test - public void testCreateBundleAllowsDocumentAndCollection() { - String methodName = "testCreateBundleAllowsDocumentAndCollection"; - - Patient p = new Patient(); - p.addIdentifier().setSystem("urn:system").setValue(methodName); - IIdType pid = myPatientDao.create(p, mySrd).getId(); - p.setId(pid); - ourLog.info("Created patient, got it: {}", pid); - - Bundle bundle = new Bundle(); - bundle.setType((BundleTypeEnum)null); - bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); - try { - myBundleDao.create(bundle, mySrd); - fail(); - } catch (UnprocessableEntityException e) { - assertEquals("Unable to store a Bundle resource on this server with a Bundle.type of: (missing)", e.getMessage()); - } - - bundle = new Bundle(); - bundle.setType(BundleTypeEnum.BATCH_RESPONSE); - bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); - try { - myBundleDao.create(bundle, mySrd); - fail(); - } catch (UnprocessableEntityException e) { - assertEquals("Unable to store a Bundle resource on this server with a Bundle.type of: batch-response", e.getMessage()); - } - - bundle = new Bundle(); - bundle.setType(BundleTypeEnum.COLLECTION); - bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); - myBundleDao.create(bundle, mySrd); - - bundle = new Bundle(); - bundle.setType(BundleTypeEnum.DOCUMENT); - bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); - myBundleDao.create(bundle, mySrd); - - } - - /** * This gets called from assertGone too! Careful about exceptions... */ private void assertNotGone(IIdType theId) { if ("Patient".equals(theId.getResourceType())) { myPatientDao.read(theId, mySrd); - } else if ("Organization".equals(theId.getResourceType())){ + } else if ("Organization".equals(theId.getResourceType())) { myOrganizationDao.read(theId, mySrd); } else { fail("No type"); @@ -188,7 +103,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } private String log(IBundleProvider theHistory) { - StringBuilder b =new StringBuilder(theHistory.size() + " results: "); + StringBuilder b = new StringBuilder(theHistory.size() + " results: "); for (IBaseResource next : theHistory.getResources(0, theHistory.size())) { b.append("\n ").append(next.getIdElement().toUnqualified().getValue()); } @@ -233,6 +148,23 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { return retVal; } + /** + * See #534 + */ + @Test + public void testBuiltInLogicalReferences() throws IOException { + myDaoConfig.getTreatReferencesAsLogical().add("http://phr.kanta.fi/fiphr-vs-*"); + + ValueSet vsBodySite = loadResourceFromClasspath(ValueSet.class, "/issue534/fiphr-vs-bodysite.xml"); + myValueSetDao.create(vsBodySite, mySrd); + ValueSet vsObsMethod = loadResourceFromClasspath(ValueSet.class, "/issue534/fiphr-vs-observationmethod.xml"); + myValueSetDao.create(vsObsMethod, mySrd); + + // Just make sure this saves + StructureDefinition sd = loadResourceFromClasspath(StructureDefinition.class, "/issue534/bw_profile_snapshot.xml"); + myStructureDefinitionDao.create(sd, mySrd); + } + @Test public void testCantSearchForDeletedResourceByLanguageOrTag() { String methodName = "testCantSearchForDeletedResourceByLanguageOrTag"; @@ -384,6 +316,48 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } } + @Test + public void testCreateBundleAllowsDocumentAndCollection() { + String methodName = "testCreateBundleAllowsDocumentAndCollection"; + + Patient p = new Patient(); + p.addIdentifier().setSystem("urn:system").setValue(methodName); + IIdType pid = myPatientDao.create(p, mySrd).getId(); + p.setId(pid); + ourLog.info("Created patient, got it: {}", pid); + + Bundle bundle = new Bundle(); + bundle.setType((BundleTypeEnum) null); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + try { + myBundleDao.create(bundle, mySrd); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals("Unable to store a Bundle resource on this server with a Bundle.type of: (missing)", e.getMessage()); + } + + bundle = new Bundle(); + bundle.setType(BundleTypeEnum.BATCH_RESPONSE); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + try { + myBundleDao.create(bundle, mySrd); + fail(); + } catch (UnprocessableEntityException e) { + assertEquals("Unable to store a Bundle resource on this server with a Bundle.type of: batch-response", e.getMessage()); + } + + bundle = new Bundle(); + bundle.setType(BundleTypeEnum.COLLECTION); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + myBundleDao.create(bundle, mySrd); + + bundle = new Bundle(); + bundle.setType(BundleTypeEnum.DOCUMENT); + bundle.addEntry().setResource(p).setFullUrl(pid.toUnqualifiedVersionless().getValue()); + myBundleDao.create(bundle, mySrd); + + } + @Test public void testCreateOperationOutcome() { /* @@ -818,8 +792,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { assertThat(found, empty()); } - - + @Test public void testDeleteResource() { int initialHistory = myPatientDao.history(null, null, mySrd).size(); @@ -882,7 +855,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { assertEquals(0, patients.size()); } - @Test public void testDeleteThenUndelete() { @@ -917,7 +889,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { assertEquals(id2, gotId); } - @Test public void testDeleteWithMatchUrl() { String methodName = "testDeleteWithMatchUrl"; @@ -974,23 +945,24 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { myPatientDao.deleteByUrl("Patient?organization.identifier=http://example.com|" + methodName, mySrd); assertGone(id); assertNotGone(orgId); - + myOrganizationDao.deleteByUrl("Organization?identifier=http://example.com|" + methodName, mySrd); assertGone(id); assertGone(orgId); } + @Test public void testDeleteWithMatchUrlChainedProfile() { String methodName = "testDeleteWithMatchUrlChainedProfile"; List profileList = new ArrayList(); profileList.add(new IdDt("http://foo")); - + Organization org = new Organization(); ResourceMetadataKeyEnum.PROFILES.put(org, profileList); org.setName(methodName); - + IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); Patient p = new Patient(); @@ -1002,7 +974,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { myPatientDao.deleteByUrl("Patient?organization._profile=http://foo", mySrd); assertGone(id); - + myOrganizationDao.deleteByUrl("Organization?_profile=http://foo", mySrd); try { myOrganizationDao.read(orgId, mySrd); @@ -1027,8 +999,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } - - @Test public void testDeleteWithMatchUrlChainedString() { String methodName = "testDeleteWithMatchUrlChainedString"; @@ -1055,11 +1025,11 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { TagList tl = new TagList(); tl.addTag("http://foo", "term"); - + Organization org = new Organization(); ResourceMetadataKeyEnum.TAG_LIST.put(org, tl); org.setName(methodName); - + IIdType orgId = myOrganizationDao.create(org, mySrd).getId().toUnqualifiedVersionless(); Patient p = new Patient(); @@ -1071,7 +1041,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { myPatientDao.deleteByUrl("Patient?organization._tag=http://foo|term", mySrd); assertGone(id); - + myOrganizationDao.deleteByUrl("Organization?_tag=http://foo|term", mySrd); try { myOrganizationDao.read(orgId, mySrd); @@ -1116,9 +1086,9 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { /* * Org 2 has a name */ - + Organization org2 = new Organization(); - org2.setName(methodName); + org2.setName(methodName); org2.addIdentifier().setValue(methodName); IIdType org2Id = myOrganizationDao.create(org2, mySrd).getId().toUnqualifiedVersionless(); @@ -1131,13 +1101,13 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { ourLog.info("Org ID 1 : {}", org1Id); ourLog.info("Pat ID 2 : {}", patId2); ourLog.info("Org ID 2 : {}", org2Id); - + myPatientDao.deleteByUrl("Patient?organization.name:missing=true", mySrd); assertGone(patId1); assertNotGone(patId2); assertNotGone(org1Id); assertNotGone(org2Id); - + myOrganizationDao.deleteByUrl("Organization?name:missing=true", mySrd); assertGone(patId1); assertNotGone(patId2); @@ -1149,7 +1119,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { assertGone(patId2); assertGone(org1Id); assertNotGone(org2Id); - + myOrganizationDao.deleteByUrl("Organization?name:missing=false", mySrd); assertGone(patId1); assertGone(patId2); @@ -1186,13 +1156,13 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { String methodName = "testHistoryOverMultiplePages"; /* - for (int i = 0; i < 1000; i++) { - Patient patient = new Patient(); - patient.addName().addFamily(methodName + "__" + i); - myPatientDao.create(patient).getId().toUnqualifiedVersionless(); - } - */ - + * for (int i = 0; i < 1000; i++) { + * Patient patient = new Patient(); + * patient.addName().addFamily(methodName + "__" + i); + * myPatientDao.create(patient).getId().toUnqualifiedVersionless(); + * } + */ + Patient patient = new Patient(); patient.addName().addFamily(methodName); IIdType id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless(); @@ -2976,4 +2946,27 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test { } } + @Test + public void testValidateAgainstDstu2Profile() throws Exception { + myDaoConfig.setAllowExternalReferences(true); + + String stream = IOUtils.toString(getClass().getResourceAsStream("/binu_testpatient_structuredefinition_dstu2.xml"), StandardCharsets.UTF_8); + + StructureDefinition sd = myFhirCtx.newXmlParser().parseResource(StructureDefinition.class, stream); + myStructureDefinitionDao.create(sd, mySrd); + + String rawResource = IOUtils.toString(getClass().getResourceAsStream("/binu_testpatient_resource.json"), StandardCharsets.UTF_8); + try { + myValueSetDao.validate(null, null, rawResource, EncodingEnum.JSON, ValidationModeEnum.UPDATE, null, mySrd); + fail(); + } catch (PreconditionFailedException e) { + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome())); + } + } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java index 5756191b32a..527f2828d6f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3Test.java @@ -23,6 +23,7 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -82,6 +83,7 @@ import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.instance.model.api.IAnyResource; import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IIdType; +import org.junit.After; import org.junit.AfterClass; import org.junit.Ignore; import org.junit.Test; @@ -89,10 +91,7 @@ import org.mockito.ArgumentCaptor; import com.google.common.collect.Lists; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; -import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao; -import ca.uhn.fhir.jpa.dao.IFhirResourceDao; -import ca.uhn.fhir.jpa.dao.SearchParameterMap; +import ca.uhn.fhir.jpa.dao.*; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString; import ca.uhn.fhir.jpa.entity.TagTypeEnum; import ca.uhn.fhir.model.api.IQueryParameterType; @@ -129,6 +128,12 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3Test.class); + @After + public final void after() { + myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences()); + myDaoConfig.setTreatReferencesAsLogical(new DaoConfig().getTreatReferencesAsLogical()); + } + private void assertGone(IIdType theId) { try { assertNotGone(theId); @@ -152,7 +157,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { fail("Can't handle type: " + theId.getResourceType()); } } - + private List extractNames(IBundleProvider theSearch) { ArrayList retVal = new ArrayList(); for (IBaseResource next : theSearch.getResources(0, theSearch.size())) { @@ -167,7 +172,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { retVal.addCoding().setSystem(theSystem).setCode(theCode); return retVal; } - + private void sort(ArrayList thePublished) { ArrayList tags = new ArrayList(thePublished); Collections.sort(tags, new Comparator() { @@ -194,7 +199,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { } }); } - + private List sortIds(List theProfiles) { ArrayList retVal = new ArrayList(theProfiles); Collections.sort(retVal, new Comparator() { @@ -288,21 +293,21 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { Encounter enc = new Encounter(); enc.getPeriod().setStartElement(new DateTimeType("2016-05-10")).setEndElement(new DateTimeType("2016-05-20")); String id = myEncounterDao.create(enc, mySrd).getId().toUnqualifiedVersionless().getValue(); - + List ids; - + /* * This should not match, per the definition of eq */ - + ids = toUnqualifiedVersionlessIdValues(myEncounterDao.search(Encounter.SP_DATE, new DateParam("2016-05-15"))); assertThat(ids, empty()); ids = toUnqualifiedVersionlessIdValues(myEncounterDao.search(Encounter.SP_DATE, new DateParam("eq2016-05-15"))); assertThat(ids, empty()); - + // Should match - + ids = toUnqualifiedVersionlessIdValues(myEncounterDao.search(Encounter.SP_DATE, new DateParam("eq2016"))); assertThat(ids, contains(id)); @@ -468,9 +473,9 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { CodeSystem cs = new CodeSystem(); cs.setStatus(PublicationStatus.DRAFT); IIdType id = myCodeSystemDao.create(cs, mySrd).getId().toUnqualifiedVersionless(); - + myCodeSystemDao.delete(id, mySrd); - + assertGone(id.toUnqualifiedVersionless()); } @@ -544,7 +549,6 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { } - @Test public void testCreateDifferentTypesWithSameForcedId() { String idName = "forcedId"; @@ -1047,7 +1051,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { @Test public void testDeleteResource() { - int initialHistory = myPatientDao.history((Date)null, null, mySrd).size(); + int initialHistory = myPatientDao.history((Date) null, null, mySrd).size(); IIdType id1; IIdType id2; @@ -1089,7 +1093,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { // good } - IBundleProvider history = myPatientDao.history((Date)null, null, mySrd); + IBundleProvider history = myPatientDao.history((Date) null, null, mySrd); assertEquals(4 + initialHistory, history.size()); List resources = history.getResources(0, 4); assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) resources.get(0))); @@ -1146,7 +1150,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { Observation obs1 = new Observation(); obs1.setStatus(ObservationStatus.FINAL); IIdType obs1id = myObservationDao.create(obs1).getId().toUnqualifiedVersionless(); - + Observation obs2 = new Observation(); obs2.setStatus(ObservationStatus.FINAL); IIdType obs2id = myObservationDao.create(obs2).getId().toUnqualifiedVersionless(); @@ -1155,19 +1159,19 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { rpt.addIdentifier().setSystem("foo").setValue("IDENTIFIER"); rpt.addResult(new Reference(obs2id)); myDiagnosticReportDao.create(rpt).getId().toUnqualifiedVersionless(); - + myObservationDao.read(obs1id); myObservationDao.read(obs2id); - + try { myObservationDao.deleteByUrl("Observation?_has:DiagnosticReport:result:identifier=foo|IDENTIFIER", mySrd); fail(); } catch (ResourceVersionConflictException e) { assertConflictException(e); } - + myObservationDao.read(obs1id); - myObservationDao.read(obs2id); + myObservationDao.read(obs2id); } @Test @@ -1461,7 +1465,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { } // By type - history = myPatientDao.history((Date)null, null, mySrd); + history = myPatientDao.history((Date) null, null, mySrd); assertEquals(fullSize + 1, history.size()); for (int i = 0; i < fullSize; i++) { String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue(); @@ -1528,7 +1532,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { } // By type - history = myPatientDao.history((Date)null, null, mySrd); + history = myPatientDao.history((Date) null, null, mySrd); assertEquals(fullSize + 1, history.size()); for (int i = 0; i < fullSize; i++) { String expected = id.withVersion(Integer.toString(fullSize + 1 - i)).getValue(); @@ -1585,7 +1589,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { inPatient.getMeta().addProfile("http://example.com/1"); IIdType id = myPatientDao.create(inPatient, mySrd).getId().toUnqualifiedVersionless(); - IBundleProvider history = myPatientDao.history((Date)null, null, mySrd); + IBundleProvider history = myPatientDao.history((Date) null, null, mySrd); assertEquals(1, history.size()); Patient outPatient = (Patient) history.getResources(0, 1).get(0); assertEquals("version1", inPatient.getName().get(0).getFamily()); @@ -1599,7 +1603,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { inPatient.getMeta().addProfile("http://example.com/2"); myPatientDao.metaAddOperation(id, inPatient.getMeta(), mySrd); - history = myPatientDao.history((Date)null, null, mySrd); + history = myPatientDao.history((Date) null, null, mySrd); assertEquals(1, history.size()); outPatient = (Patient) history.getResources(0, 1).get(0); assertEquals("version1", inPatient.getName().get(0).getFamily()); @@ -1615,7 +1619,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { inPatient.getName().get(0).setFamily("version2"); myPatientDao.update(inPatient, mySrd); - history = myPatientDao.history((Date)null, null, mySrd); + history = myPatientDao.history((Date) null, null, mySrd); assertEquals(2, history.size()); outPatient = (Patient) history.getResources(0, 2).get(0); assertEquals("version2", outPatient.getName().get(0).getFamily()); @@ -1682,13 +1686,13 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { } List idValues; - + idValues = toUnqualifiedIdValues(myPatientDao.history(id, preDates.get(0), preDates.get(3), mySrd)); assertThat(idValues, contains(ids.get(2), ids.get(1), ids.get(0))); - + idValues = toUnqualifiedIdValues(myPatientDao.history(preDates.get(0), preDates.get(3), mySrd)); assertThat(idValues, contains(ids.get(2), ids.get(1), ids.get(0))); - + idValues = toUnqualifiedIdValues(mySystemDao.history(preDates.get(0), preDates.get(3), mySrd)); assertThat(idValues, contains(ids.get(2), ids.get(1), ids.get(0))); } @@ -1709,7 +1713,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { // No since - IBundleProvider history = myPatientDao.history((Date)null, null, mySrd); + IBundleProvider history = myPatientDao.history((Date) null, null, mySrd); assertEquals(1, history.size()); Patient outPatient = (Patient) history.getResources(0, 1).get(0); assertEquals("version1", inPatient.getName().get(0).getFamily()); @@ -1731,7 +1735,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { assertEquals(0, history.size()); } - + @Test public void testHistoryWithInvalidId() throws Exception { try { @@ -1815,11 +1819,11 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { @Test public void testIndexConditionWithAllOnsetTypes() { // DateTimeType.class, Age.class, Period.class, Range.class, StringType.class - + Condition c0 = new Condition(); c0.setOnset(new DateTimeType("2011-01-01")); myConditionDao.create(c0, mySrd).getId().toUnqualifiedVersionless(); - + Condition c1 = new Condition(); c1.setOnset(new Age().setValue(100L).setCode("AGECODE")); myConditionDao.create(c1, mySrd).getId().toUnqualifiedVersionless(); @@ -1835,7 +1839,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { Condition c4 = new Condition(); c4.setOnset(new StringType("FOO")); myConditionDao.create(c4, mySrd).getId().toUnqualifiedVersionless(); -} + } @Test public void testInstanceMetaOperations() { @@ -2011,6 +2015,27 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { } } + /** + * See #534 + */ + @Test + public void testLogicalReferencesAreSearchable() throws IOException { + myDaoConfig.setTreatReferencesAsLogical(null); + myDaoConfig.addTreatReferencesAsLogical("http://foo.com/identifier*"); + + Patient p1 = new Patient(); + p1.getManagingOrganization().setReference("http://foo.com/identifier/1"); + String p1id = myPatientDao.create(p1, mySrd).getId().toUnqualifiedVersionless().getValue(); + + Patient p2 = new Patient(); + p2.getManagingOrganization().setReference("http://foo.com/identifier/2"); + String p2id = myPatientDao.create(p2, mySrd).getId().toUnqualifiedVersionless().getValue(); + + IBundleProvider found = myPatientDao.search(Patient.SP_ORGANIZATION, new ReferenceParam("http://foo.com/identifier/1")); + assertThat(toUnqualifiedVersionlessIdValues(found), contains(p1id)); + assertThat(toUnqualifiedVersionlessIdValues(found), not(contains(p2id))); + } + @Test public void testOrganizationName() { @@ -3297,7 +3322,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { Date before = new DateTimeType("2011-01-01T10:00:00Z").getValue(); Date middle = new DateTimeType("2011-01-02T10:00:00Z").getValue(); Date after = new DateTimeType("2011-01-03T10:00:00Z").getValue(); - + CarePlan cp = new CarePlan(); cp.addActivity().getDetail().setScheduled(new Timing().addEvent(before).addEvent(middle).addEvent(after)); cp.addActivity().getDetail(); @@ -3307,9 +3332,9 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { cp2.addActivity().getDetail().setScheduled(new StringType("FOO")); cp2.addActivity().getDetail(); myCarePlanDao.create(cp2, mySrd).getId().toUnqualifiedVersionless(); - + SearchParameterMap params; - + params = new SearchParameterMap(); params.add(CarePlan.SP_ACTIVITY_DATE, new DateRangeParam("2010-01-01T10:00:00Z", null)); assertThat(toUnqualifiedVersionlessIdValues(myCarePlanDao.search(params)), contains(id.getValue())); @@ -3317,7 +3342,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { params = new SearchParameterMap(); params.add(CarePlan.SP_ACTIVITY_DATE, new DateRangeParam("2011-01-01T10:00:00Z", null)); assertThat(toUnqualifiedVersionlessIdValues(myCarePlanDao.search(params)), contains(id.getValue())); - + params = new SearchParameterMap(); params.add(CarePlan.SP_ACTIVITY_DATE, new DateRangeParam("2012-01-01T10:00:00Z", null)); assertThat(toUnqualifiedVersionlessIdValues(myCarePlanDao.search(params)), empty()); @@ -3364,7 +3389,8 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test { } public static void assertConflictException(ResourceVersionConflictException e) { - assertThat(e.getMessage(), matchesPattern("Unable to delete [a-zA-Z]+/[0-9]+ because at least one resource has a reference to this resource. First reference found was resource [a-zA-Z]+/[0-9]+ in path [a-zA-Z]+.[a-zA-Z]+")); + assertThat(e.getMessage(), matchesPattern( + "Unable to delete [a-zA-Z]+/[0-9]+ because at least one resource has a reference to this resource. First reference found was resource [a-zA-Z]+/[0-9]+ in path [a-zA-Z]+.[a-zA-Z]+")); } private static List toStringList(List theUriType) { diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirTesterConfigDstu3.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirTesterConfigDstu3.java new file mode 100644 index 00000000000..714b8f10cd6 --- /dev/null +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/FhirTesterConfigDstu3.java @@ -0,0 +1,56 @@ +package ca.uhn.fhir.jpa.demo; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.to.FhirTesterMvcConfig; +import ca.uhn.fhir.to.TesterConfig; + +//@formatter:off +/** + * This spring config file configures the web testing module. It serves two + * purposes: + * 1. It imports FhirTesterMvcConfig, which is the spring config for the + * tester itself + * 2. It tells the tester which server(s) to talk to, via the testerConfig() + * method below + */ +@Configuration +@Import(FhirTesterMvcConfig.class) +public class FhirTesterConfigDstu3 { + + /** + * This bean tells the testing webpage which servers it should configure itself + * to communicate with. In this example we configure it to talk to the local + * server, as well as one public server. If you are creating a project to + * deploy somewhere else, you might choose to only put your own server's + * address here. + * + * Note the use of the ${serverBase} variable below. This will be replaced with + * the base URL as reported by the server itself. Often for a simple Tomcat + * (or other container) installation, this will end up being something + * like "http://localhost:8080/hapi-fhir-jpaserver-example". If you are + * deploying your server to a place with a fully qualified domain name, + * you might want to use that instead of using the variable. + */ + @Bean + public TesterConfig testerConfig() { + TesterConfig retVal = new TesterConfig(); + retVal + .addServer() + .withId("home") + .withFhirVersion(FhirVersionEnum.DSTU3) + .withBaseUrl("${serverBase}/baseDstu3") + .withName("Local Tester") + .addServer() + .withId("hapi") + .withFhirVersion(FhirVersionEnum.DSTU3) + .withBaseUrl("http://fhirtest.uhn.ca/baseDstu3") + .withName("Public HAPI Test Server"); + return retVal; + } + +} +//@formatter:on diff --git a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index e2cf5f07fa1..cf1b8071e1a 100644 --- a/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/hapi-fhir-jpaserver-example/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -46,22 +46,20 @@ public class JpaServerDemo extends RestfulServer { protected void initialize() throws ServletException { super.initialize(); - /* + /* * We want to support FHIR DSTU2 format. This means that the server * will use the DSTU2 bundle format and other DSTU2 encoding changes. * * If you want to use DSTU1 instead, change the following line, and * change the 2 occurrences of dstu2 in web.xml to dstu1 */ - FhirVersionEnum fhirVersion = FhirVersionEnum.DSTU2; - FhirContext context = new FhirContext(fhirVersion); - - setFhirContext(context); + FhirVersionEnum fhirVersion = FhirVersionEnum.DSTU3; + setFhirContext(new FhirContext(fhirVersion)); // Get the spring context from the web container (it's declared in web.xml) myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext(); - /* + /* * The BaseJavaConfigDstu2.java class is a spring configuration * file which is automatically generated as a part of hapi-fhir-jpaserver-base and * contains bean definitions for a resource provider for each resource type @@ -78,8 +76,8 @@ public class JpaServerDemo extends RestfulServer { } List beans = myAppCtx.getBean(resourceProviderBeanName, List.class); setResourceProviders(beans); - - /* + + /* * The system provider implements non-resource-type methods, such as * transaction, and global history. */ @@ -174,15 +172,15 @@ public class JpaServerDemo extends RestfulServer { } /* - * If you are hosting this server at a specific DNS name, the server will try to + * If you are hosting this server at a specific DNS name, the server will try to * figure out the FHIR base URL based on what the web container tells it, but * this doesn't always work. If you are setting links in your search bundles that * just refer to "localhost", you might want to use a server address strategy: */ //setServerAddressStrategy(new HardcodedServerAddressStrategy("http://mydomain.com/fhir/baseDstu2")); - + /* - * If you are using DSTU3+, you may want to add a terminology uploader, which allows + * If you are using DSTU3+, you may want to add a terminology uploader, which allows * uploading of external terminologies such as Snomed CT. Note that this uploader * does not have any security attached (any anonymous user may use it by default) * so it is a potential security vulnerability. Consider using an AuthorizationInterceptor diff --git a/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/web.xml b/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/web.xml index 7fec6b6804a..fdfc4ce3545 100644 --- a/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/web.xml +++ b/hapi-fhir-jpaserver-example/src/main/webapp/WEB-INF/web.xml @@ -13,7 +13,7 @@ contextConfigLocation - ca.uhn.fhir.jpa.demo.FhirServerConfig + ca.uhn.fhir.jpa.demo.FhirServerConfigDstu3 @@ -28,7 +28,7 @@ contextConfigLocation - ca.uhn.fhir.jpa.demo.FhirTesterConfig + ca.uhn.fhir.jpa.demo.FhirTesterConfigDstu3 2 @@ -42,14 +42,14 @@ FhirVersion - DSTU2 + DSTU3 1 fhirServlet - /baseDstu2/* + /baseDstu3/* diff --git a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/ExampleServerIT.java b/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/ExampleServerIT.java index 89a6ac2f06e..9021194c009 100644 --- a/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/ExampleServerIT.java +++ b/hapi-fhir-jpaserver-example/src/test/java/ca/uhn/fhir/jpa/demo/ExampleServerIT.java @@ -7,13 +7,13 @@ import java.io.IOException; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.webapp.WebAppContext; +import org.hl7.fhir.dstu3.model.Patient; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.model.dstu2.resource.Patient; import ca.uhn.fhir.rest.client.IGenericClient; import ca.uhn.fhir.rest.client.ServerValidationModeEnum; import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; @@ -21,7 +21,7 @@ import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor; public class ExampleServerIT { private static IGenericClient ourClient; - private static FhirContext ourCtx = FhirContext.forDstu2(); + private static FhirContext ourCtx = FhirContext.forDstu3(); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ExampleServerIT.class); private static int ourPort; @@ -34,11 +34,11 @@ public class ExampleServerIT { String methodName = "testCreateResourceConditional"; Patient pt = new Patient(); - pt.addName().addFamily(methodName); + pt.addName().setFamily(methodName); IIdType id = ourClient.create().resource(pt).execute().getId(); Patient pt2 = ourClient.read().resource(Patient.class).withId(id).execute(); - assertEquals(methodName, pt2.getName().get(0).getFamily().get(0).getValue()); + assertEquals(methodName, pt2.getName().get(0).getFamily()); } @AfterClass @@ -72,7 +72,7 @@ public class ExampleServerIT { ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER); ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000); - ourServerBase = "http://localhost:" + ourPort + "/baseDstu2"; + ourServerBase = "http://localhost:" + ourPort + "/baseDstu3"; ourClient = ourCtx.newRestfulGenericClient(ourServerBase); ourClient.registerInterceptor(new LoggingInterceptor(true)); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java index 51ffedf1a03..43d286f5905 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TdlDstu2Config.java @@ -1,10 +1,14 @@ package ca.uhn.fhirtest.config; -import java.util.Properties; - -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; - +import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2; +import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu2; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; +import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor; +import ca.uhn.fhir.validation.ResultSeverityEnum; +import ca.uhn.fhirtest.interceptor.TdlSecurityInterceptor; import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.lang3.time.DateUtils; import org.hibernate.dialect.DerbyTenSevenDialect; @@ -13,18 +17,16 @@ import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Lazy; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; -import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2; -import ca.uhn.fhir.jpa.dao.DaoConfig; -import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu2; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; -import ca.uhn.fhirtest.interceptor.TdlSecurityInterceptor; +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; +import java.util.Properties; @Configuration @Import(CommonConfig.class) @@ -113,6 +115,49 @@ public class TdlDstu2Config extends BaseJavaConfigDstu2 { return extraProperties; } + /** + * Bean which validates incoming requests + */ + @Bean + @Lazy + public RequestValidatingInterceptor requestValidatingInterceptor() { + RequestValidatingInterceptor requestValidator = new RequestValidatingInterceptor(); + requestValidator.setFailOnSeverity(null); + requestValidator.setAddResponseHeaderOnSeverity(null); + requestValidator.setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum.INFORMATION); + requestValidator.addValidatorModule(instanceValidatorDstu2()); + requestValidator.setIgnoreValidatorExceptions(true); + + return requestValidator; + } + + /** + * Bean which validates outgoing responses + */ + @Bean + @Lazy + public ResponseValidatingInterceptor responseValidatingInterceptor() { + ResponseValidatingInterceptor responseValidator = new ResponseValidatingInterceptor(); + responseValidator.setResponseHeaderValueNoIssues("Validation did not detect any issues"); + responseValidator.setFailOnSeverity(null); + responseValidator.setAddResponseHeaderOnSeverity(null); + responseValidator.setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum.INFORMATION); + responseValidator.addExcludeOperationType(RestOperationTypeEnum.METADATA); + responseValidator.addExcludeOperationType(RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE); + responseValidator.addExcludeOperationType(RestOperationTypeEnum.EXTENDED_OPERATION_SERVER); + responseValidator.addExcludeOperationType(RestOperationTypeEnum.EXTENDED_OPERATION_TYPE); + responseValidator.addExcludeOperationType(RestOperationTypeEnum.GET_PAGE); + responseValidator.addExcludeOperationType(RestOperationTypeEnum.HISTORY_INSTANCE); + responseValidator.addExcludeOperationType(RestOperationTypeEnum.HISTORY_SYSTEM); + responseValidator.addExcludeOperationType(RestOperationTypeEnum.HISTORY_TYPE); + responseValidator.addExcludeOperationType(RestOperationTypeEnum.SEARCH_SYSTEM); + responseValidator.addExcludeOperationType(RestOperationTypeEnum.SEARCH_TYPE); + responseValidator.addValidatorModule(instanceValidatorDstu2()); + responseValidator.setIgnoreValidatorExceptions(true); + + return responseValidator; + } + @Bean(autowire=Autowire.BY_TYPE) public IServerInterceptor subscriptionSecurityInterceptor() { return new SubscriptionsRequireManualActivationInterceptorDstu2(); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java index cb8997c90aa..4fc19e053f1 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/config/TestDstu2Config.java @@ -1,30 +1,28 @@ package ca.uhn.fhirtest.config; -import java.util.Properties; - -import javax.persistence.EntityManagerFactory; -import javax.sql.DataSource; - +import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2; +import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; +import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; +import ca.uhn.fhir.validation.ResultSeverityEnum; +import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; import org.apache.commons.dbcp2.BasicDataSource; import org.apache.commons.lang3.time.DateUtils; import org.hibernate.dialect.PostgreSQL94Dialect; import org.hibernate.jpa.HibernatePersistenceProvider; -import org.springframework.beans.factory.annotation.Autowire; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Lazy; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; import org.springframework.transaction.annotation.EnableTransactionManagement; -import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2; -import ca.uhn.fhir.jpa.dao.DaoConfig; -import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu2; -import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; -import ca.uhn.fhirtest.interceptor.PublicSecurityInterceptor; +import javax.persistence.EntityManagerFactory; +import javax.sql.DataSource; +import java.util.Properties; @Configuration @Import(CommonConfig.class) @@ -114,6 +112,22 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 { return extraProperties; } + /** + * Bean which validates incoming requests + */ + @Bean + @Lazy + public RequestValidatingInterceptor requestValidatingInterceptor() { + RequestValidatingInterceptor requestValidator = new RequestValidatingInterceptor(); + requestValidator.setFailOnSeverity(null); + requestValidator.setAddResponseHeaderOnSeverity(null); + requestValidator.setAddResponseOutcomeHeaderOnSeverity(ResultSeverityEnum.INFORMATION); + requestValidator.addValidatorModule(instanceValidatorDstu2()); + requestValidator.setIgnoreValidatorExceptions(true); + + return requestValidator; + } + // @Bean(autowire = Autowire.BY_TYPE) // public IServerInterceptor subscriptionSecurityInterceptor() { // return new SubscriptionsRequireManualActivationInterceptorDstu2(); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 16ac5de552d..f66f67c06cb 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -26,6 +26,27 @@ Web testing UI displayed an error when a transaction was pasted into the UI for a DSTU2 server. Thanks to Suresh Kumar for reporting! + + DaoConfig#setAllowInlineMatchUrlReferences() now defaults to + true]]> since inline conditional references + are now a part of the FHIR specification. Thanks to Jan DÄ›dek for + pointing this out! + + + hapi-fhir-jpaserver-base now exposes a + FhirInstanceValidator bean named "myInstanceValidatorDstu2"]]> + for DSTU2. A similar bean for DSTU3 was previously implemented. + + + hapi-fhir-jpaserver-example project now defaults to STU3 mode instead of + the previous DSTU2. Thanks to Joel Schneider for the pull request! + + + JPA server now has a setting on the DaoConfig to force it to treat + certain reference URLs or reference URL patterns as logical URLs instead + of literal ones, meaning that the server will not try to resolve these + URLs. Thanks to Eeva Turkka for the suggestion! + diff --git a/src/site/xdoc/doc_jpa.xml b/src/site/xdoc/doc_jpa.xml index ffd098c50a8..ae2a9b8db84 100644 --- a/src/site/xdoc/doc_jpa.xml +++ b/src/site/xdoc/doc_jpa.xml @@ -174,6 +174,38 @@ public DaoConfig daoConfig() { return retVal; }]]> + + + +

    + In some cases, you may have references which are Logical References, + which means that they act as an identifier and not necessarily as a literal + web address. +

    +

    + A common use for logical references is in references to conformance + resources, such as ValueSets, StructureDefinitions, etc. For example, + you might refer to the ValueSet + http://hl7.org/fhir/ValueSet/quantity-comparator + from your own resources. In this case, you are not neccesarily telling + the server that this is a real address that it should resolve, but + rather that this is an identifier for a ValueSet where + ValueSet.url has the given URI/URL. +

    +

    + HAPI can be configured to treat certain URI/URL patterns as + logical by using the DaoConfig#setTreatReferencesAsLogical property + (see JavaDoc). + For example: +

    + + // Treat specific URL as logical + myDaoConfig.getTreatReferencesAsLogical().add("http://mysystem.com/ValueSet/cats-and-dogs"); + + // Treat all references with given prefix as logical + myDaoConfig.getTreatReferencesAsLogical().add("http://mysystem.com/mysystem-vs-*"); + +