Merge branch 'master' into reuse-subscription-channels

This commit is contained in:
Ken Stevens 2019-02-08 10:39:00 -05:00
commit 52c62884fb
71 changed files with 655 additions and 367 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-standalone-overlay-example</artifactId>

View File

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

View File

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

View File

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

View File

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

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.parser;
* 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.
@ -63,7 +63,6 @@ public abstract class BaseParser implements IParser {
private boolean mySummaryMode;
private boolean mySuppressNarratives;
private Set<String> myDontStripVersionsFromReferencesAtPaths;
/**
* Constructor
*/
@ -72,6 +71,55 @@ public abstract class BaseParser implements IParser {
myErrorHandler = theParserErrorHandler;
}
List<ElementsPath> getDontEncodeElements() {
return myDontEncodeElements;
}
@Override
public void setDontEncodeElements(Set<String> theDontEncodeElements) {
if (theDontEncodeElements == null || theDontEncodeElements.isEmpty()) {
myDontEncodeElements = null;
} else {
myDontEncodeElements = theDontEncodeElements
.stream()
.map(ElementsPath::new)
.collect(Collectors.toList());
}
}
List<ElementsPath> getEncodeElements() {
return myEncodeElements;
}
@Override
public void setEncodeElements(Set<String> theEncodeElements) {
if (theEncodeElements == null || theEncodeElements.isEmpty()) {
myEncodeElements = null;
myEncodeElementsAppliesToResourceTypes = null;
} else {
myEncodeElements = theEncodeElements
.stream()
.map(ElementsPath::new)
.collect(Collectors.toList());
myEncodeElementsAppliesToResourceTypes = new HashSet<>();
for (String next : myEncodeElements.stream().map(t -> t.getPath().get(0).getName()).collect(Collectors.toList())) {
if (next.startsWith("*")) {
myEncodeElementsAppliesToResourceTypes = null;
break;
}
int dotIdx = next.indexOf('.');
if (dotIdx == -1) {
myEncodeElementsAppliesToResourceTypes.add(next);
} else {
myEncodeElementsAppliesToResourceTypes.add(next.substring(0, dotIdx));
}
}
}
}
protected Iterable<CompositeChildElement> compositeChildIterator(IBase theCompositeElement, final boolean theContainedResource, final CompositeChildElement theParent, EncodeContext theEncodeContext) {
BaseRuntimeElementCompositeDefinition<?> elementDef = (BaseRuntimeElementCompositeDefinition<?>) myContext.getElementDefinition(theCompositeElement.getClass());
@ -402,35 +450,6 @@ public abstract class BaseParser implements IParser {
return myDontStripVersionsFromReferencesAtPaths;
}
@Override
public void setEncodeElements(Set<String> theEncodeElements) {
if (theEncodeElements == null || theEncodeElements.isEmpty()) {
myEncodeElements = null;
myEncodeElementsAppliesToResourceTypes = null;
} else {
myEncodeElements = theEncodeElements
.stream()
.map(ElementsPath::new)
.collect(Collectors.toList());
myEncodeElementsAppliesToResourceTypes = new HashSet<>();
for (String next : myEncodeElements.stream().map(t -> t.getPath().get(0).getName()).collect(Collectors.toList())) {
if (next.startsWith("*")) {
myEncodeElementsAppliesToResourceTypes = null;
break;
}
int dotIdx = next.indexOf('.');
if (dotIdx == -1) {
myEncodeElementsAppliesToResourceTypes.add(next);
} else {
myEncodeElementsAppliesToResourceTypes.add(next.substring(0, dotIdx));
}
}
}
}
@Override
public IIdType getEncodeForceResourceId() {
return myEncodeForceResourceId;
@ -803,18 +822,6 @@ public abstract class BaseParser implements IParser {
}
}
@Override
public void setDontEncodeElements(Set<String> theDontEncodeElements) {
if (theDontEncodeElements == null || theDontEncodeElements.isEmpty()) {
myDontEncodeElements = null;
} else {
myDontEncodeElements = theDontEncodeElements
.stream()
.map(ElementsPath::new)
.collect(Collectors.toList());
}
}
@Override
public IParser setDontStripVersionsFromReferencesAtPaths(String... thePaths) {
if (thePaths == null) {
@ -936,7 +943,7 @@ public abstract class BaseParser implements IParser {
String resourceName = myContext.getResourceDefinition(theResource).getName();
if (myDontEncodeElements.stream().anyMatch(t -> t.equalsPath(resourceName + "." + thePath))) {
return false;
} else if (myDontEncodeElements.stream().anyMatch(t -> t.equalsPath("*."+ thePath))) {
} else if (myDontEncodeElements.stream().anyMatch(t -> t.equalsPath("*." + thePath))) {
return false;
}
}
@ -963,6 +970,17 @@ public abstract class BaseParser implements IParser {
throw new DataFormatException(nextChild + " has no child of type " + theType);
}
protected boolean shouldEncodeResource(String theName) {
if (myDontEncodeElements != null) {
for (ElementsPath next : myDontEncodeElements) {
if (next.equalsPath(theName)) {
return false;
}
}
}
return true;
}
class ChildNameAndDef {
private final BaseRuntimeElementDefinition<?> myChildDef;
@ -1289,9 +1307,23 @@ public abstract class BaseParser implements IParser {
if (myResource != theOther.isResource()) {
return false;
}
if (myName.equals(theOther.getName())) {
String otherName = theOther.getName();
if (myName.equals(otherName)) {
return true;
}
/*
* This is here to handle situations where a path like
* Observation.valueQuantity has been specified as an include/exclude path,
* since we only know that path as
* Observation.value
* until we get to actually looking at the values there.
*/
if (myName.length() > otherName.length() && myName.startsWith(otherName)) {
char ch = myName.charAt(otherName.length());
if (Character.isUpperCase(ch)) {
return true;
}
}
if (myName.equals("*")) {
return true;
}

View File

@ -410,8 +410,17 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
continue;
}
String childName = childNameAndDef.getChildName();
theEncodeContext.pushPath(childName, false);
/*
* Often the two values below will be the same thing. There are cases though
* where they will not be. An example would be Observation.value, which is
* a choice type. If the value contains a Quantity, then:
* nextChildGenericName = "value"
* nextChildSpecificName = "valueQuantity"
*/
String nextChildSpecificName = childNameAndDef.getChildName();
String nextChildGenericName = nextChild.getElementName();
theEncodeContext.pushPath(nextChildGenericName, false);
BaseRuntimeElementDefinition<?> childDef = childNameAndDef.getChildDef();
boolean primitive = childDef.getChildType() == ChildTypeEnum.PRIMITIVE_DATATYPE;
@ -451,20 +460,20 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
}
if (currentChildName == null || !currentChildName.equals(childName)) {
if (currentChildName == null || !currentChildName.equals(nextChildSpecificName)) {
if (inArray) {
theEventWriter.endArray();
}
if (nextChild.getMax() > 1 || nextChild.getMax() == Child.MAX_UNLIMITED) {
beginArray(theEventWriter, childName);
beginArray(theEventWriter, nextChildSpecificName);
inArray = true;
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, nextChildElem, force, theEncodeContext);
} else if (nextChild instanceof RuntimeChildNarrativeDefinition && theContainedResource) {
// suppress narratives from contained resources
} else {
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, childName, theContainedResource, nextChildElem, false, theEncodeContext);
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, nextChildSpecificName, theContainedResource, nextChildElem, false, theEncodeContext);
}
currentChildName = childName;
currentChildName = nextChildSpecificName;
} else {
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, nextValue, childDef, null, theContainedResource, nextChildElem, force, theEncodeContext);
}
@ -594,6 +603,11 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonLikeWriter theEventWriter, String theObjectNameOrNull,
boolean theContainedResource, IIdType theResourceId, EncodeContext theEncodeContext) throws IOException {
if (!super.shouldEncodeResource(theResDef.getName())) {
return;
}
if (!theContainedResource) {
super.containResourcesForEncoding(theResource);
}

View File

@ -215,9 +215,19 @@ public class XmlParser extends BaseParser /* implements IParser */ {
}
}
private void encodeChildElementToStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, IBase theElement, String childName, BaseRuntimeElementDefinition<?> childDef,
private void encodeChildElementToStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, BaseRuntimeChildDefinition theChildDefinition, IBase theElement, String theChildName, BaseRuntimeElementDefinition<?> childDef,
String theExtensionUrl, boolean theIncludedResource, CompositeChildElement theParent, EncodeContext theEncodeContext) throws XMLStreamException, DataFormatException {
theEncodeContext.pushPath(childName, false);
/*
* Often the two values below will be the same thing. There are cases though
* where they will not be. An example would be Observation.value, which is
* a choice type. If the value contains a Quantity, then:
* childGenericName = "value"
* theChildName = "valueQuantity"
*/
String childGenericName = theChildDefinition.getElementName();
theEncodeContext.pushPath(childGenericName, false);
try {
if (theElement == null || theElement.isEmpty()) {
@ -233,9 +243,9 @@ public class XmlParser extends BaseParser /* implements IParser */ {
switch (childDef.getChildType()) {
case ID_DATATYPE: {
IIdType value = IIdType.class.cast(theElement);
String encodedValue = "id".equals(childName) ? value.getIdPart() : value.getValue();
String encodedValue = "id".equals(theChildName) ? value.getIdPart() : value.getValue();
if (StringUtils.isNotBlank(encodedValue) || !super.hasNoExtensions(value)) {
theEventWriter.writeStartElement(childName);
theEventWriter.writeStartElement(theChildName);
if (StringUtils.isNotBlank(encodedValue)) {
theEventWriter.writeAttribute("value", encodedValue);
}
@ -248,7 +258,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
IPrimitiveType<?> pd = IPrimitiveType.class.cast(theElement);
String value = pd.getValueAsString();
if (value != null || !super.hasNoExtensions(pd)) {
theEventWriter.writeStartElement(childName);
theEventWriter.writeStartElement(theChildName);
String elementId = getCompositeElementId(theElement);
if (isNotBlank(elementId)) {
theEventWriter.writeAttribute("id", elementId);
@ -263,7 +273,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
}
case RESOURCE_BLOCK:
case COMPOSITE_DATATYPE: {
theEventWriter.writeStartElement(childName);
theEventWriter.writeStartElement(theChildName);
String elementId = getCompositeElementId(theElement);
if (isNotBlank(elementId)) {
theEventWriter.writeAttribute("id", elementId);
@ -291,9 +301,12 @@ public class XmlParser extends BaseParser /* implements IParser */ {
break;
}
case RESOURCE: {
theEventWriter.writeStartElement(childName);
IBaseResource resource = (IBaseResource) theElement;
String resourceName = myContext.getResourceDefinition(resource).getName();
if (!super.shouldEncodeResource(resourceName)) {
break;
}
theEventWriter.writeStartElement(theChildName);
theEncodeContext.pushPath(resourceName, true);
encodeResourceToXmlStreamWriter(resource, theEventWriter, false, theEncodeContext);
theEncodeContext.popPath();
@ -364,13 +377,13 @@ public class XmlParser extends BaseParser /* implements IParser */ {
RuntimeChildNarrativeDefinition child = (RuntimeChildNarrativeDefinition) nextChild;
String childName = nextChild.getChildNameByDatatype(child.getDatatype());
BaseRuntimeElementDefinition<?> type = child.getChildByName(childName);
encodeChildElementToStreamWriter(theResource, theEventWriter, narr, childName, type, null, theContainedResource, nextChildElem, theEncodeContext);
encodeChildElementToStreamWriter(theResource, theEventWriter, nextChild, narr, childName, type, null, theContainedResource, nextChildElem, theEncodeContext);
continue;
}
}
if (nextChild instanceof RuntimeChildContainedResources) {
encodeChildElementToStreamWriter(theResource, theEventWriter, null, nextChild.getChildNameByDatatype(null), nextChild.getChildElementDefinitionByDatatype(null), null, theContainedResource, nextChildElem, theEncodeContext);
encodeChildElementToStreamWriter(theResource, theEventWriter, nextChild, null, nextChild.getChildNameByDatatype(null), nextChild.getChildElementDefinitionByDatatype(null), null, theContainedResource, nextChildElem, theEncodeContext);
} else {
List<? extends IBase> values = nextChild.getAccessor().getValues(theElement);
@ -402,11 +415,11 @@ public class XmlParser extends BaseParser /* implements IParser */ {
continue;
}
}
encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, getExtensionUrl(extension.getUrl()), theContainedResource, nextChildElem, theEncodeContext);
encodeChildElementToStreamWriter(theResource, theEventWriter, nextChild, nextValue, childName, childDef, getExtensionUrl(extension.getUrl()), theContainedResource, nextChildElem, theEncodeContext);
} else if (nextChild instanceof RuntimeChildNarrativeDefinition && theContainedResource) {
// suppress narratives from contained resources
} else {
encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, extensionUrl, theContainedResource, nextChildElem, theEncodeContext);
encodeChildElementToStreamWriter(theResource, theEventWriter, nextChild, nextValue, childName, childDef, extensionUrl, theContainedResource, nextChildElem, theEncodeContext);
}
}
@ -429,7 +442,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
}
theEventWriter.writeAttribute("url", extensionUrl);
encodeChildElementToStreamWriter(theResource, theEventWriter, nextValue, childName, childDef, null, theContainedResource, nextChildElem, theEncodeContext);
encodeChildElementToStreamWriter(theResource, theEventWriter, nextChild, nextValue, childName, childDef, null, theContainedResource, nextChildElem, theEncodeContext);
theEventWriter.writeEndElement();
}
@ -471,15 +484,15 @@ public class XmlParser extends BaseParser /* implements IParser */ {
}
private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theContainedResource, IIdType theResourceId, EncodeContext theEncodeContext) throws XMLStreamException {
if (!theContainedResource) {
super.containResourcesForEncoding(theResource);
}
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
if (resDef == null) {
throw new ConfigurationException("Unknown resource type: " + theResource.getClass());
}
if (!theContainedResource) {
super.containResourcesForEncoding(theResource);
}
theEventWriter.writeStartElement(resDef.getName());
theEventWriter.writeDefaultNamespace(FHIR_NS);
@ -609,7 +622,7 @@ public class XmlParser extends BaseParser /* implements IParser */ {
throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName());
}
}
encodeChildElementToStreamWriter(theResource, theEventWriter, value, childName, childDef, null, theIncludedResource, null, theEncodeContext);
encodeChildElementToStreamWriter(theResource, theEventWriter, extDef, value, childName, childDef, null, theIncludedResource, null, theEncodeContext);
}
// child extensions

View File

@ -26,6 +26,7 @@ public enum VersionEnum {
V3_4_0,
V3_5_0,
V3_6_0,
V3_7_0
V3_7_0,
V3_8_0
}

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

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

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

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

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -53,6 +53,47 @@
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>${project.version}</version>
<classifier>sources</classifier>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>${project.version}</version>
<classifier>javadoc</classifier>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
<classifier>sources</classifier>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
<classifier>javadoc</classifier>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-utilities</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>hapi-deployable-pom</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.jaxrs.server.util;
* 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.
@ -19,206 +19,54 @@ package ca.uhn.fhir.jaxrs.server.util;
* limitations under the License.
* #L%
*/
import java.io.*;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.List;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.api.server.IRestfulResponse;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.method.ResourceParameter;
import ca.uhn.fhir.util.UrlUtil;
import org.apache.commons.lang3.StringUtils;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.nio.charset.Charset;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* The JaxRsRequest is a jax-rs specific implementation of the RequestDetails.
*
* The JaxRsRequest is a jax-rs specific implementation of the RequestDetails.
*
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
*/
public class JaxRsRequest extends RequestDetails {
/**
* An implementation of the builder pattern for the JaxRsRequest
*/
public static class Builder {
private String myCompartment;
private String myId;
private RequestTypeEnum myRequestType;
private String myRequestUrl;
private String myResource;
private final String myResourceName;
private RestOperationTypeEnum myRestOperation;
private AbstractJaxRsProvider myServer;
private String myVersion;
/**
* Utility Constructor
* @param theServer the server
* @param theRequestType the request type
* @param theRestOperation the rest operation
* @param theRequestUrl
*/
public Builder(AbstractJaxRsProvider theServer, RequestTypeEnum theRequestType,
RestOperationTypeEnum theRestOperation, String theRequestUrl, String theResourceName) {
this.myServer = theServer;
this.myRequestType = theRequestType;
this.myRestOperation = theRestOperation;
this.myRequestUrl = theRequestUrl;
this.myResourceName = theResourceName;
}
/**
* Create the jax-rs request
* @return the jax-rs request
*/
public JaxRsRequest build() {
JaxRsRequest result = new JaxRsRequest(myServer, myResource, myRequestType, myRestOperation);
if ((StringUtils.isNotBlank(myVersion) || StringUtils.isNotBlank(myCompartment))
&& StringUtils.isBlank(myId)) {
throw new InvalidRequestException("Don't know how to handle request path: "
+ myServer.getUriInfo().getRequestUri().toASCIIString());
}
FhirVersionEnum fhirContextVersion = myServer.getFhirContext().getVersion().getVersion();
if (StringUtils.isNotBlank(myVersion)) {
switch (fhirContextVersion) {
case R4:
result.setId(new org.hl7.fhir.r4.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
break;
case DSTU3:
result.setId(new org.hl7.fhir.dstu3.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
break;
case DSTU2_1:
result.setId(new org.hl7.fhir.dstu2016may.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
break;
case DSTU2_HL7ORG:
result.setId(new org.hl7.fhir.instance.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
break;
case DSTU2:
result.setId(new ca.uhn.fhir.model.primitive.IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
break;
default:
throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion);
}
} else if (StringUtils.isNotBlank(myId)) {
switch (fhirContextVersion) {
case R4:
result.setId(new org.hl7.fhir.r4.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
break;
case DSTU3:
result.setId(new org.hl7.fhir.dstu3.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
break;
case DSTU2_1:
result.setId(new org.hl7.fhir.dstu2016may.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
break;
case DSTU2_HL7ORG:
result.setId(new org.hl7.fhir.instance.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
break;
case DSTU2:
result.setId(new ca.uhn.fhir.model.primitive.IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
break;
default:
throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion);
}
}
if (myRestOperation == RestOperationTypeEnum.UPDATE) {
String contentLocation = result.getHeader(Constants.HEADER_CONTENT_LOCATION);
if (contentLocation != null) {
switch (fhirContextVersion) {
case R4:
result.setId(new org.hl7.fhir.r4.model.IdType(contentLocation));
break;
case DSTU3:
result.setId(new org.hl7.fhir.dstu3.model.IdType(contentLocation));
break;
case DSTU2_1:
result.setId(new org.hl7.fhir.dstu2016may.model.IdType(contentLocation));
break;
case DSTU2_HL7ORG:
result.setId(new org.hl7.fhir.instance.model.IdType(contentLocation));
break;
case DSTU2:
result.setId(new ca.uhn.fhir.model.primitive.IdDt(contentLocation));
break;
default:
throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion);
}
}
}
result.setCompartmentName(myCompartment);
result.setCompleteUrl(myRequestUrl);
result.setResourceName(myResourceName);
return result;
}
/**
* Set the compartment
* @param compartment the compartment
* @return the builder
*/
public Builder compartment(String compartment) {
this.myCompartment = compartment;
return this;
}
/**
* Set the id
* @param id the resource id
* @return the builder
*/
public Builder id(String id) {
this.myId = id;
return this;
}
/**
* Set the resource
* @param resource the body contents of an http method
* @return the builder
*/
public Builder resource(String resource) {
this.myResource = resource;
return this;
}
/**
* Set the id version
* @param version the version of the resource
* @return the builder
*/
public Builder version(String version) {
this.myVersion = version;
return this;
}
}
private HttpHeaders myHeaders;
private String myResourceString;
private AbstractJaxRsProvider myServer;
private Map<String, Object> myAttributes = new HashMap<>();
/**
* Utility Constructor
* @param server the server
*
* @param server the server
* @param resourceString the resource body
* @param requestType the request type
* @param restOperation the operation type
* @param requestType the request type
* @param restOperation the operation type
*/
public JaxRsRequest(AbstractJaxRsProvider server, String resourceString, RequestTypeEnum requestType,
RestOperationTypeEnum restOperation) {
RestOperationTypeEnum restOperation) {
this.myHeaders = server.getHeaders();
this.myResourceString = resourceString;
this.setRestOperationType(restOperation);
@ -231,17 +79,17 @@ public class JaxRsRequest extends RequestDetails {
@Override
protected byte[] getByteStreamRequestContents() {
return StringUtils.defaultString(myResourceString, "")
.getBytes(ResourceParameter.determineRequestCharset(this));
.getBytes(ResourceParameter.determineRequestCharset(this));
}
@Override
public Charset getCharset() {
String charset = null;
if(myHeaders.getMediaType() != null && myHeaders.getMediaType().getParameters() != null) {
if (myHeaders.getMediaType() != null && myHeaders.getMediaType().getParameters() != null) {
charset = myHeaders.getMediaType().getParameters().get(MediaType.CHARSET_PARAMETER);
}
if(charset != null) {
if (charset != null) {
return Charset.forName(charset);
} else {
return null;
@ -262,7 +110,17 @@ public class JaxRsRequest extends RequestDetails {
@Override
public List<String> getHeaders(String name) {
List<String> requestHeader = myHeaders.getRequestHeader(name);
return requestHeader == null ? Collections.<String> emptyList() : requestHeader;
return requestHeader == null ? Collections.<String>emptyList() : requestHeader;
}
@Override
public Object getAttribute(String theAttributeName) {
return myAttributes.get(theAttributeName);
}
@Override
public void setAttribute(String theAttributeName, Object theAttributeValue) {
myAttributes.put(theAttributeName, theAttributeValue);
}
@Override
@ -290,16 +148,182 @@ public class JaxRsRequest extends RequestDetails {
return myServer;
}
/**
* Set the server
*
* @param theServer the server to set
*/
public void setServer(AbstractJaxRsProvider theServer) {
this.myServer = theServer;
}
@Override
public String getServerBaseForRequest() {
return getServer().getServerAddressStrategy().determineServerBase(null, null);
}
/**
* Set the server
* @param theServer the server to set
* An implementation of the builder pattern for the JaxRsRequest
*/
public void setServer(AbstractJaxRsProvider theServer) {
this.myServer = theServer;
public static class Builder {
private final String myResourceName;
private String myCompartment;
private String myId;
private RequestTypeEnum myRequestType;
private String myRequestUrl;
private String myResource;
private RestOperationTypeEnum myRestOperation;
private AbstractJaxRsProvider myServer;
private String myVersion;
/**
* Utility Constructor
*
* @param theServer the server
* @param theRequestType the request type
* @param theRestOperation the rest operation
* @param theRequestUrl
*/
public Builder(AbstractJaxRsProvider theServer, RequestTypeEnum theRequestType,
RestOperationTypeEnum theRestOperation, String theRequestUrl, String theResourceName) {
this.myServer = theServer;
this.myRequestType = theRequestType;
this.myRestOperation = theRestOperation;
this.myRequestUrl = theRequestUrl;
this.myResourceName = theResourceName;
}
/**
* Create the jax-rs request
*
* @return the jax-rs request
*/
public JaxRsRequest build() {
JaxRsRequest result = new JaxRsRequest(myServer, myResource, myRequestType, myRestOperation);
if ((StringUtils.isNotBlank(myVersion) || StringUtils.isNotBlank(myCompartment))
&& StringUtils.isBlank(myId)) {
throw new InvalidRequestException("Don't know how to handle request path: "
+ myServer.getUriInfo().getRequestUri().toASCIIString());
}
FhirVersionEnum fhirContextVersion = myServer.getFhirContext().getVersion().getVersion();
if (StringUtils.isNotBlank(myVersion)) {
switch (fhirContextVersion) {
case R4:
result.setId(new org.hl7.fhir.r4.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
break;
case DSTU3:
result.setId(new org.hl7.fhir.dstu3.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
break;
case DSTU2_1:
result.setId(new org.hl7.fhir.dstu2016may.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
break;
case DSTU2_HL7ORG:
result.setId(new org.hl7.fhir.instance.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
break;
case DSTU2:
result.setId(new ca.uhn.fhir.model.primitive.IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId), UrlUtil.unescape(myVersion)));
break;
default:
throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion);
}
} else if (StringUtils.isNotBlank(myId)) {
switch (fhirContextVersion) {
case R4:
result.setId(new org.hl7.fhir.r4.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
break;
case DSTU3:
result.setId(new org.hl7.fhir.dstu3.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
break;
case DSTU2_1:
result.setId(new org.hl7.fhir.dstu2016may.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
break;
case DSTU2_HL7ORG:
result.setId(new org.hl7.fhir.instance.model.IdType(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
break;
case DSTU2:
result.setId(new ca.uhn.fhir.model.primitive.IdDt(myServer.getBaseForRequest(), UrlUtil.unescape(myId)));
break;
default:
throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion);
}
}
if (myRestOperation == RestOperationTypeEnum.UPDATE) {
String contentLocation = result.getHeader(Constants.HEADER_CONTENT_LOCATION);
if (contentLocation != null) {
switch (fhirContextVersion) {
case R4:
result.setId(new org.hl7.fhir.r4.model.IdType(contentLocation));
break;
case DSTU3:
result.setId(new org.hl7.fhir.dstu3.model.IdType(contentLocation));
break;
case DSTU2_1:
result.setId(new org.hl7.fhir.dstu2016may.model.IdType(contentLocation));
break;
case DSTU2_HL7ORG:
result.setId(new org.hl7.fhir.instance.model.IdType(contentLocation));
break;
case DSTU2:
result.setId(new ca.uhn.fhir.model.primitive.IdDt(contentLocation));
break;
default:
throw new ConfigurationException("Unsupported Fhir version: " + fhirContextVersion);
}
}
}
result.setCompartmentName(myCompartment);
result.setCompleteUrl(myRequestUrl);
result.setResourceName(myResourceName);
return result;
}
/**
* Set the compartment
*
* @param compartment the compartment
* @return the builder
*/
public Builder compartment(String compartment) {
this.myCompartment = compartment;
return this;
}
/**
* Set the id
*
* @param id the resource id
* @return the builder
*/
public Builder id(String id) {
this.myId = id;
return this;
}
/**
* Set the resource
*
* @param resource the body contents of an http method
* @return the builder
*/
public Builder resource(String resource) {
this.myResource = resource;
return this;
}
/**
* Set the id version
*
* @param version the version of the resource
* @return the builder
*/
public Builder version(String version) {
this.myVersion = version;
return this;
}
}
}

View File

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

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -1165,6 +1165,22 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
}
@Test
public void testElements() throws IOException {
DiagnosticReport dr = new DiagnosticReport();
dr.setStatus(DiagnosticReport.DiagnosticReportStatus.FINAL);
dr.getCode().setText("CODE TEXT");
ourClient.create().resource(dr).execute();
HttpGet get = new HttpGet(ourServerBase + "/DiagnosticReport?_include=DiagnosticReport:result&_elements:exclude=DiagnosticReport&_elements=DiagnosticReport.status,Observation.value,Observation.code,Observation.subject&_pretty=true");
try (CloseableHttpResponse response = ourHttpClient.execute(get)) {
assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
assertThat(output, not(containsString("<Diagn")));
ourLog.info(output);
}
}
@Test
public void testEmptySearch() {
Bundle responseBundle;

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

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

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

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

View File

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

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -158,7 +158,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-converter</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</dependency>
</dependencies>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -165,6 +165,20 @@ public abstract class RequestDetails {
myId = theId;
}
/**
* Returns the attribute map for this request. Attributes are a place for user-supplied
* objects of any type to be attached to an individual request. They can be used to pass information
* between interceptor methods.
*/
public abstract Object getAttribute(String theAttributeName);
/**
* Returns the attribute map for this request. Attributes are a place for user-supplied
* objects of any type to be attached to an individual request. They can be used to pass information
* between interceptor methods.
*/
public abstract void setAttribute(String theAttributeName, Object theAttributeValue);
/**
* Retrieves the body of the request as binary data. Either this method or {@link #getReader} may be called to read
* the body, not both.

View File

@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server;
* 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.
@ -93,13 +93,13 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* context, in order to avoid a dependency on Servlet-API 3.0+
*/
public static final String SERVLET_CONTEXT_ATTRIBUTE = "ca.uhn.fhir.rest.server.RestfulServer.servlet_context";
private static final ExceptionHandlingInterceptor DEFAULT_EXCEPTION_HANDLER = new ExceptionHandlingInterceptor();
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServer.class);
private static final long serialVersionUID = 1L;
/**
* Default value for {@link #setDefaultPreferReturn(PreferReturnEnum)}
*/
public static final PreferReturnEnum DEFAULT_PREFER_RETURN = PreferReturnEnum.REPRESENTATION;
private static final ExceptionHandlingInterceptor DEFAULT_EXCEPTION_HANDLER = new ExceptionHandlingInterceptor();
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServer.class);
private static final long serialVersionUID = 1L;
private final List<IServerInterceptor> myInterceptors = new ArrayList<>();
private final List<Object> myPlainProviders = new ArrayList<>();
private final List<IResourceProvider> myResourceProviders = new ArrayList<>();
@ -499,6 +499,19 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return myETagSupport;
}
/**
* Sets (enables/disables) the server support for ETags. Must not be <code>null</code>. Default is
* {@link #DEFAULT_ETAG_SUPPORT}
*
* @param theETagSupport The ETag support mode
*/
public void setETagSupport(ETagSupportEnum theETagSupport) {
if (theETagSupport == null) {
throw new NullPointerException("theETagSupport can not be null");
}
myETagSupport = theETagSupport;
}
@Override
public ElementsSupportEnum getElementsSupport() {
return myElementsSupport;
@ -514,19 +527,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
myElementsSupport = theElementsSupport;
}
/**
* Sets (enables/disables) the server support for ETags. Must not be <code>null</code>. Default is
* {@link #DEFAULT_ETAG_SUPPORT}
*
* @param theETagSupport The ETag support mode
*/
public void setETagSupport(ETagSupportEnum theETagSupport) {
if (theETagSupport == null) {
throw new NullPointerException("theETagSupport can not be null");
}
myETagSupport = theETagSupport;
}
/**
* Gets the {@link FhirContext} associated with this server. For efficient processing, resource providers and plain
* providers should generally use this context if one is needed, as opposed to
@ -933,21 +933,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
requestDetails.setFhirServerBase(fhirServerBase);
requestDetails.setCompleteUrl(completeUrl);
// String pagingAction = theRequest.getParameter(Constants.PARAM_PAGINGACTION);
// if (getPagingProvider() != null && isNotBlank(pagingAction)) {
// requestDetails.setRestOperationType(RestOperationTypeEnum.GET_PAGE);
// if (theRequestType != RequestTypeEnum.GET) {
// /*
// * We reconstruct the link-self URL using the request parameters, and this would break if the parameters came
// in using a POST. We could probably work around that but why bother unless
// * someone comes up with a reason for needing it.
// */
// throw new InvalidRequestException(getFhirContext().getLocalizer().getMessage(RestfulServer.class,
// "getPagesNonHttpGet"));
// }
// handlePagingRequest(requestDetails, theResponse, pagingAction);
// return;
// }
validateRequest(requestDetails);
BaseMethodBinding<?> resourceMethod = determineResourceMethod(requestDetails, requestPath);
@ -1049,6 +1035,26 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
}
}
protected void validateRequest(ServletRequestDetails theRequestDetails) {
String[] elements = theRequestDetails.getParameters().get(Constants.PARAM_ELEMENTS);
if (elements != null) {
for (String next : elements) {
if (next.indexOf(':') != -1) {
throw new InvalidRequestException("Invalid _elements value: \"" + next + "\"");
}
}
}
elements = theRequestDetails.getParameters().get(Constants.PARAM_ELEMENTS + Constants.PARAM_ELEMENTS_EXCLUDE_MODIFIER);
if (elements != null) {
for (String next : elements) {
if (next.indexOf(':') != -1) {
throw new InvalidRequestException("Invalid _elements value: \"" + next + "\"");
}
}
}
}
/**
* Initializes the server. Note that this method is final to avoid accidentally introducing bugs in implementations,
* but subclasses may put initialization code in {@link #initialize()}, which is

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -103,6 +104,18 @@ public class ServletRequestDetails extends RequestDetails {
return headers == null ? Collections.<String> emptyList() : Collections.list(getServletRequest().getHeaders(name));
}
@Override
public Object getAttribute(String theAttributeName) {
Validate.notBlank(theAttributeName, "theAttributeName must not be null or blank");
return getServletRequest().getAttribute(theAttributeName);
}
@Override
public void setAttribute(String theAttributeName, Object theAttributeValue) {
Validate.notBlank(theAttributeName, "theAttributeName must not be null or blank");
getServletRequest().setAttribute(theAttributeName, theAttributeValue);
}
@Override
public InputStream getInputStream() throws IOException {
return getServletRequest().getInputStream();

View File

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

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-okhttp</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jpa</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

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

View File

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

View File

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

View File

@ -18,10 +18,7 @@ import org.eclipse.jetty.servlet.ServletHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.*;
import java.io.IOException;
import java.util.Collection;
@ -30,7 +27,8 @@ import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
public class ElementsParamR4Test {
@ -42,6 +40,7 @@ public class ElementsParamR4Test {
private static Server ourServer;
private static Procedure ourNextProcedure;
private static RestfulServer ourServlet;
private static Observation ourNextObservation;
@Before
public void before() {
@ -50,6 +49,92 @@ public class ElementsParamR4Test {
ourServlet.setElementsSupport(new RestfulServer().getElementsSupport());
}
@Test
public void testElementsOnChoiceWithGenericName() throws IOException {
createObservationWithQuantity();
verifyXmlAndJson(
"http://localhost:" + ourPort + "/Observation?_elements=value,status",
bundle -> {
Observation obs = (Observation) bundle.getEntry().get(0).getResource();
assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode());
assertEquals(Observation.ObservationStatus.FINAL, obs.getStatus());
assertEquals("222", obs.getValueQuantity().getValueElement().getValueAsString());
assertEquals("mg", obs.getValueQuantity().getCode());
});
}
@Test
public void testElementsOnChoiceWithSpecificName() throws IOException {
createObservationWithQuantity();
verifyXmlAndJson(
"http://localhost:" + ourPort + "/Observation?_elements=valueQuantity,status",
bundle -> {
Observation obs = (Observation) bundle.getEntry().get(0).getResource();
assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode());
assertEquals(Observation.ObservationStatus.FINAL, obs.getStatus());
assertEquals("222", obs.getValueQuantity().getValueElement().getValueAsString());
assertEquals("mg", obs.getValueQuantity().getCode());
});
}
@Test
@Ignore
public void testElementsOnChoiceWithSpecificNameNotMatching() throws IOException {
createObservationWithQuantity();
verifyXmlAndJson(
"http://localhost:" + ourPort + "/Observation?_elements=valueString,status",
bundle -> {
Observation obs = (Observation) bundle.getEntry().get(0).getResource();
assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode());
assertEquals(Observation.ObservationStatus.FINAL, obs.getStatus());
assertEquals(null, obs.getValueQuantity());
});
}
@Test
public void testExcludeResources() throws IOException {
createProcedureWithLongChain();
verifyXmlAndJson(
"http://localhost:" + ourPort + "/Procedure?_include=*&_elements:exclude=Procedure,DiagnosticReport,*.meta",
bundle -> {
assertEquals(null, bundle.getEntry().get(0).getResource());
assertEquals(null, bundle.getEntry().get(1).getResource());
Observation obs = (Observation) bundle.getEntry().get(2).getResource();
assertEquals(true, obs.getMeta().isEmpty());
assertEquals(Observation.ObservationStatus.FINAL, obs.getStatus());
assertEquals(1, obs.getCode().getCoding().size());
assertEquals("STRING VALUE", obs.getValueStringType().getValue());
});
}
@Test
public void testInvalidInclude() throws IOException {
createProcedureWithLongChain();
EncodingEnum encodingEnum;
HttpGet httpGet;
encodingEnum = EncodingEnum.JSON;
httpGet = new HttpGet(("http://localhost:" + ourPort + "/Procedure?_include=*&_elements=DiagnosticReport:foo") + "&_pretty=true&_format=" + encodingEnum.getFormatContentType());
try (CloseableHttpResponse status = ourClient.execute(httpGet)) {
String responseContent = IOUtils.toString(status.getEntity().getContent(), Charsets.UTF_8);
ourLog.info(responseContent);
assertEquals(400, status.getStatusLine().getStatusCode());
}
}
private void createObservationWithQuantity() {
ourNextObservation = new Observation();
ourNextObservation.setId("Observation/123/_history/456");
ourNextObservation.setStatus(Observation.ObservationStatus.FINAL);
ourNextObservation.setSubject(new Reference("Patient/AAA"));
ourNextObservation.setValue(new Quantity()
.setValue(222)
.setCode("mg")
.setSystem("http://unitsofmeasure.org"));
}
@Test
public void testReadSummaryData() throws Exception {
verifyXmlAndJson(
@ -108,7 +193,7 @@ public class ElementsParamR4Test {
assertThat(responseContent, containsString("THE DIV"));
assertThat(responseContent, not(containsString("family")));
assertThat(responseContent, not(containsString("maritalStatus")));
assertThat(ourLastElements, containsInAnyOrder( "text"));
assertThat(ourLastElements, containsInAnyOrder("text"));
}
);
}
@ -132,14 +217,13 @@ public class ElementsParamR4Test {
assertEquals(0, dr.getMeta().getTag().size());
assertEquals("Observation/OBSA", dr.getResult().get(0).getReference());
Observation obs = (Observation ) bundle.getEntry().get(2).getResource();
Observation obs = (Observation) bundle.getEntry().get(2).getResource();
assertEquals(0, obs.getMeta().getTag().size());
assertEquals(Observation.ObservationStatus.FINAL, obs.getStatus());
assertEquals("1234-5", obs.getCode().getCoding().get(0).getCode());
});
}
@Test
public void testMultiResourceElementsFilter() throws IOException {
createProcedureWithLongChain();
@ -154,7 +238,7 @@ public class ElementsParamR4Test {
DiagnosticReport dr = (DiagnosticReport) bundle.getEntry().get(1).getResource();
assertEquals(0, dr.getMeta().getTag().size());
Observation obs = (Observation ) bundle.getEntry().get(2).getResource();
Observation obs = (Observation) bundle.getEntry().get(2).getResource();
assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode());
assertEquals(Observation.ObservationStatus.FINAL, obs.getStatus());
assertEquals(0, obs.getCode().getCoding().size());
@ -176,7 +260,7 @@ public class ElementsParamR4Test {
DiagnosticReport dr = (DiagnosticReport) bundle.getEntry().get(1).getResource();
assertEquals(true, dr.getMeta().isEmpty());
Observation obs = (Observation ) bundle.getEntry().get(2).getResource();
Observation obs = (Observation) bundle.getEntry().get(2).getResource();
assertEquals(true, obs.getMeta().isEmpty());
assertEquals(Observation.ObservationStatus.FINAL, obs.getStatus());
assertEquals(0, obs.getCode().getCoding().size());
@ -203,7 +287,7 @@ public class ElementsParamR4Test {
assertEquals(true, dr.getMeta().isEmpty());
assertEquals(1, dr.getResult().size());
Observation obs = (Observation ) bundle.getEntry().get(2).getResource();
Observation obs = (Observation) bundle.getEntry().get(2).getResource();
assertEquals("SUBSETTED", obs.getMeta().getTag().get(0).getCode());
assertEquals(null, obs.getStatus());
assertEquals(0, obs.getCode().getCoding().size());
@ -228,7 +312,7 @@ public class ElementsParamR4Test {
assertEquals(true, dr.getMeta().isEmpty());
assertEquals(1, dr.getResult().size());
Observation obs = (Observation ) bundle.getEntry().get(2).getResource();
Observation obs = (Observation) bundle.getEntry().get(2).getResource();
assertEquals(true, obs.getMeta().isEmpty());
assertEquals(Observation.ObservationStatus.FINAL, obs.getStatus());
assertEquals(1, obs.getCode().getCoding().size());
@ -297,6 +381,21 @@ public class ElementsParamR4Test {
}
}
public static class DummyObservationResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Observation.class;
}
@Search
public Observation search(@IncludeParam(allow = {"*"}) Collection<Include> theIncludes) {
return ourNextObservation;
}
}
public static class DummyProcedureResourceProvider implements IResourceProvider {
@Override
@ -358,6 +457,8 @@ public class ElementsParamR4Test {
ourServlet.registerProvider(new DummyPatientResourceProvider());
ourServlet.registerProvider(new DummyProcedureResourceProvider());
ourServlet.registerProvider(new DummyObservationResourceProvider());
ServletHolder servletHolder = new ServletHolder(ourServlet);
proxyHandler.addServletWithMapping(servletHolder, "/*");
ourServer.setHandler(proxyHandler);

View File

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

View File

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

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

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

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
@ -73,7 +73,7 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>

View File

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

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -20,7 +20,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<packaging>pom</packaging>
<version>3.7.0</version>
<version>3.8.0-SNAPSHOT</version>
<name>HAPI-FHIR</name>
<description>An open-source implementation of the FHIR specification in Java.</description>
<url>https://hapifhir.io</url>
@ -1567,8 +1567,6 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-site-plugin</artifactId>
<!-- Be careful upgrading the version of this plugin- 3.5 breaks the relow-maven-skin that we use currently. Also once we move to 3.5 the site.xml <head> tag needs to have its contents put in a CDATA
block. What an unpleasant misadventure to figure that all out...... -->
<version>3.7.1</version>
<configuration>
<skip>false</skip>

View File

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

View File

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

View File

@ -6,6 +6,22 @@
<title>HAPI FHIR Changelog</title>
</properties>
<body>
<release version="3.8.0" date="TBD" description="Hippo">
<action type="add">
In Servers that are configured to support extended mode
<![CDATA[<code>_elements</code>]]> parameters, it is now possible to
use the :exclude modifier to exclude entire resource types.
</action>
<action type="add">
RequestDetails now has methods called getAttribute and setAttribute that can
be used by interceptors to pass arbitrary data between requests.
</action>
<action type="add">
The hapi-fhir-jpaserver-starter project has been updated to use a properties
file for configuration, making it much easier to get started with this
project. Thanks to Sean McIlvenna for the pull request!
</action>
</release>
<release version="3.7.0" date="2019-02-06" description="Gale">
<action type="add">
HAPI FHIR is now built using OpenJDK 11. Users are recommended to upgrade to this version
@ -544,7 +560,6 @@
date was corrected. Thanks Heinz-Dieter Conradi for the Pull Request!
</action>
</release>
<release version="3.5.0" date="2018-09-17">
<action type="add">
HAPI FHIR now supports JDK 9 and JDK 10, both for building HAPI FHIR
@ -6102,7 +6117,7 @@ Bundle bundle = client.search().forResource(Patient.class)
<![CDATA[<b>API CHANGE:</b>]]>: Most elements in the HAPI FHIR model contain
a getId() and setId() method. This method is confusing because it is only actually used
for IDREF elements (which are rare) but its name makes it easy to confuse with more
important identifiers. For this reason, these methods have been deprocated and replaced with
important identifiers. For this reason, these methods have been deprecated and replaced with
get/setElementSpecificId() methods. The old methods will be removed at some point. Resource
types are unchanged and retain their get/setId methods.
</action>

View File

@ -272,7 +272,7 @@
This could be used, for example, to allow the user to perform a search for<br/>
<code>http://baseurl/Observation?category=laboratory</code><br/>
and then receive results as though they had requested<br/>
<code>http://baseurl/Observation?subject=Patient/123&category=laboratory</code>.
<code>http://baseurl/Observation?subject=Patient/123&amp;category=laboratory</code>.
</p>
<p>
An example of this interceptor follows:

View File

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

View File

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