Fix #534: Add setting for logical references to DAOConfig

Merge branch 'master' into issue534
This commit is contained in:
James Agnew 2017-03-30 03:21:50 +08:00
commit 2c9a6e65e7
18 changed files with 591 additions and 319 deletions

View File

@ -45,7 +45,7 @@ public interface ISort<T> {
/**
* Sort descending
*
* @param A query param - Could be a constant such as <code>Patient.ADDRESS</code> or a custom
* @param theParam A query param - Could be a constant such as <code>Patient.ADDRESS</code> or a custom
* param such as <code>new StringClientParam("foo")</code>
*/
IQuery<T> descending(IParam theParam);

View File

@ -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 <code>false</code>
* to indicate that the server itself should not also provide a response.
* @return Return <code>true</code> 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 <code>false</code>. 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

View File

@ -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();

View File

@ -312,28 +312,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
}
Set<String> 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;
if (isLogicalReference(nextId)) {
ResourceLink resourceLink = new ResourceLink(nextPathAndRef.getPath(), theEntity, nextId, theUpdateTime);
if (theLinks.add(resourceLink)) {
ourLog.info("Indexing remote resource reference URL: {}", nextId);
}
} else {
if (nextId.getValue().equals(nextLogicalRef)) {
isLogical = true;
break;
}
}
}
if (isLogical) {
continue;
}
}
String baseUrl = nextId.getBaseUrl();
String typeString = nextId.getResourceType();
@ -412,6 +397,26 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
}
protected boolean isLogicalReference(IIdType theId) {
Set<String> 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<ResourceIndexedSearchParamCoords> extractSearchParamCoords(ResourceTable theEntity, IBaseResource theResource) {
return mySearchParamExtractor.extractSearchParamCoords(theEntity, theResource);
}
@ -964,7 +969,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
* The tag
* @return Returns <code>true</code> if the tag should be removed
*/
@SuppressWarnings("unused")
protected void postPersist(ResourceTable theEntity, T theResource) {
// nothing
}
@ -998,7 +1002,6 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
return ids;
}
@SuppressWarnings("unused")
@CoverageIgnore
public BaseHasResource readEntity(IIdType theValueId) {
throw new NotImplementedException("");
@ -1531,11 +1534,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> 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())) {
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 '" + nextRef.getReferenceElement().getResourceType() + "' is not valid for this path");
"Invalid reference found at path '" + newPath + "'. Resource type '" + referencedId.getResourceType() + "' is not valid for this path");
}
}
}
}

View File

@ -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<String> myTreatBaseUrlsAsLocal = new HashSet<String>();
private Set<String> myTreatReferencesAsLogical = new HashSet<String>(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<String>();
}
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,8 +130,8 @@ public class DaoConfig {
* (next/prev links in search response bundles) will become invalid. Defaults to 1 hour.
* </p>
* <p>
*
* @see To disable this feature entirely, see {@link #setExpireSearchResults(boolean)}
* <p>
* To disable this feature entirely, see {@link #setExpireSearchResults(boolean)}
* </p>
*
* @since 1.5
@ -196,9 +211,9 @@ public class DaoConfig {
* references instead of being treated as real references.
* <p>
* 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.
* </p>
* <p>
@ -209,7 +224,7 @@ public class DaoConfig {
* <li><code>http://example.com/some-base*</code> <b>(will match anything beginning with the part before the *)</b></li>
* </ul>
*
* @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<String> 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.
* <p>
* 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)
* </p>
*
* @since 1.5
@ -401,8 +418,10 @@ public class DaoConfig {
* </p>
* <p>
*
* @see To disable this feature entirely, see {@link #setExpireSearchResults(boolean)}
* <p>
* To disable this feature entirely, see {@link #setExpireSearchResults(boolean)}
* </p>
*
* @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<String> theTreatBaseUrlsAsLocal) {
if (theTreatBaseUrlsAsLocal != null) {
for (String next : theTreatBaseUrlsAsLocal) {
validateTreatBaseUrlsAsLocal(next);
}
}
HashSet<String> treatBaseUrlsAsLocal = new HashSet<String>();
for (String next : ObjectUtils.defaultIfNull(theTreatBaseUrlsAsLocal, new HashSet<String>())) {
while (next.endsWith("/")) {
@ -544,9 +569,9 @@ public class DaoConfig {
* references instead of being treated as real references.
* <p>
* 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.
* </p>
* <p>
@ -557,11 +582,23 @@ public class DaoConfig {
* <li><code>http://example.com/some-base*</code> <b>(will match anything beginning with the part before the *)</b></li>
* </ul>
*
* @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<String> 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);
}
}
}
}

View File

@ -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<T extends IResource> extends BaseHapiFhirResourceDao<T> {
@ -71,6 +65,9 @@ public class FhirResourceDaoDstu2<T extends IResource> extends BaseHapiFhirResou
@Qualifier("myJpaValidationSupportDstu2")
private IValidationSupport myJpaValidationSupport;
@Autowired()
@Qualifier("myInstanceValidatorDstu2")
private IValidatorModule myInstanceValidator;
@Override
protected List<Object> getIncludeValues(FhirTerser theTerser, Include theInclude, IBaseResource theResource, RuntimeResourceDefinition theResourceDef) {
@ -124,10 +121,7 @@ public class FhirResourceDaoDstu2<T extends IResource> 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));

View File

@ -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;
}
}

View File

@ -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<String>(Arrays.asList("http://foo")));
new DaoConfig().setTreatBaseUrlsAsLocal(new HashSet<String>(Arrays.asList("http://foo*")));
}
@Test
public void testInvalidLogicalPattern() {
try {
new DaoConfig().setTreatBaseUrlsAsLocal(new HashSet<String>(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<String>(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());
}
}
}

View File

@ -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() {
/*
@ -819,7 +793,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
@Test
public void testDeleteResource() {
int initialHistory = myPatientDao.history(null, null, mySrd).size();
@ -883,7 +856,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
@Test
public void testDeleteThenUndelete() {
Patient patient = new Patient();
@ -917,7 +889,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
assertEquals(id2, gotId);
}
@Test
public void testDeleteWithMatchUrl() {
String methodName = "testDeleteWithMatchUrl";
@ -980,6 +951,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
assertGone(orgId);
}
@Test
public void testDeleteWithMatchUrlChainedProfile() {
String methodName = "testDeleteWithMatchUrlChainedProfile";
@ -1027,8 +999,6 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
@Test
public void testDeleteWithMatchUrlChainedString() {
String methodName = "testDeleteWithMatchUrlChainedString";
@ -1186,11 +1156,11 @@ 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();
@ -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();
}
}

View File

@ -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);
@ -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<IBaseResource> resources = history.getResources(0, 4);
assertNotNull(ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) resources.get(0)));
@ -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());
@ -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());
@ -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() {
@ -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<String> toStringList(List<UriType> theUriType) {

View File

@ -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

View File

@ -53,10 +53,8 @@ public class JpaServerDemo extends RestfulServer {
* 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();

View File

@ -13,7 +13,7 @@
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
ca.uhn.fhir.jpa.demo.FhirServerConfig
ca.uhn.fhir.jpa.demo.FhirServerConfigDstu3
</param-value>
</context-param>
@ -28,7 +28,7 @@
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>ca.uhn.fhir.jpa.demo.FhirTesterConfig</param-value>
<param-value>ca.uhn.fhir.jpa.demo.FhirTesterConfigDstu3</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
@ -42,14 +42,14 @@
</init-param>
<init-param>
<param-name>FhirVersion</param-name>
<param-value>DSTU2</param-value>
<param-value>DSTU3</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>fhirServlet</servlet-name>
<url-pattern>/baseDstu2/*</url-pattern>
<url-pattern>/baseDstu3/*</url-pattern>
</servlet-mapping>
<servlet-mapping>

View File

@ -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));

View File

@ -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();

View File

@ -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();

View File

@ -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!
</action>
<action type="add">
DaoConfig#setAllowInlineMatchUrlReferences() now defaults to
<![CDATA[<code>true</code>]]> since inline conditional references
are now a part of the FHIR specification. Thanks to Jan Dědek for
pointing this out!
</action>
<action type="add" issue="609">
hapi-fhir-jpaserver-base now exposes a
<![CDATA[<code>FhirInstanceValidator</code> bean named <code>"myInstanceValidatorDstu2"</code>]]>
for DSTU2. A similar bean for DSTU3 was previously implemented.
</action>
<action type="add" issue="453">
hapi-fhir-jpaserver-example project now defaults to STU3 mode instead of
the previous DSTU2. Thanks to Joel Schneider for the pull request!
</action>
<action type="add" issue="534">
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!
</action>
</release>
<release version="2.3" date="2017-03-18">
<action type="add">

View File

@ -175,6 +175,38 @@ public DaoConfig daoConfig() {
}]]></source>
</subsection>
<subsection name="Logical References">
<p>
In some cases, you may have references which are <i>Logical References</i>,
which means that they act as an identifier and not necessarily as a literal
web address.
</p>
<p>
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
<code>http://hl7.org/fhir/ValueSet/quantity-comparator</code>
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
<code>ValueSet.url</code> has the given URI/URL.
</p>
<p>
HAPI can be configured to treat certain URI/URL patterns as
logical by using the DaoConfig#setTreatReferencesAsLogical property
(see <a href="./apidocs-jpaserver/ca/uhn/fhir/jpa/dao/DaoConfig.html#setTreatReferencesAsLogical-java.util.Set-">JavaDoc</a>).
For example:
</p>
<code>
// 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-*");
</code>
</subsection>
</section>
<section name="Architecture">