diff --git a/hapi-fhir-base/TODO.txt b/hapi-fhir-base/TODO.txt index 2ab3fe8135f..8fc45a3b224 100644 --- a/hapi-fhir-base/TODO.txt +++ b/hapi-fhir-base/TODO.txt @@ -33,6 +33,8 @@ * Support "Binary" resource, which is a special resource type + * Submit "_pretty" as possible parameter to HL7 + --------- Issues: * Need to be able to bind Identifier.system to a set of allowable values in a profile. Graeme diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDatatypeDefinition.java index 7aeac1b3522..1061b2ebddf 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDatatypeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/BaseRuntimeChildDatatypeDefinition.java @@ -8,7 +8,6 @@ import java.util.Set; import ca.uhn.fhir.model.api.ICodeEnum; import ca.uhn.fhir.model.api.IDatatype; import ca.uhn.fhir.model.api.IElement; -import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.annotation.Child; import ca.uhn.fhir.model.api.annotation.Description; @@ -35,7 +34,8 @@ public abstract class BaseRuntimeChildDatatypeDefinition extends BaseRuntimeDecl @Override public BaseRuntimeElementDefinition getChildElementDefinitionByDatatype(Class theDatatype) { - if (myDatatype.equals(theDatatype)) { + Class datatype = theDatatype; + if (myDatatype.equals(datatype)) { return myElementDefinition; } return null; @@ -74,4 +74,11 @@ public abstract class BaseRuntimeChildDatatypeDefinition extends BaseRuntimeDecl } myCodeType = theType; } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + getElementName() + "]"; + } + + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildCompositeDatatypeDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildCompositeDatatypeDefinition.java index 55ff11e6b53..d023d346ede 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildCompositeDatatypeDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildCompositeDatatypeDefinition.java @@ -12,6 +12,5 @@ public class RuntimeChildCompositeDatatypeDefinition extends BaseRuntimeChildDat super(theField, theElementName, theChildAnnotation,theDescriptionAnnotation, theDatatype); } - } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceDefinition.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceDefinition.java index bec4393a17f..204b6051c09 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceDefinition.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/RuntimeChildResourceDefinition.java @@ -83,4 +83,9 @@ public class RuntimeChildResourceDefinition extends BaseRuntimeDeclaredChildDefi myValidChildNames = Collections.unmodifiableSet(myValidChildNames); } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + getElementName() + "]"; + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BoundCodeableConceptDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BoundCodeableConceptDt.java index 6cdbb66fd6a..c3c51aa7f01 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BoundCodeableConceptDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BoundCodeableConceptDt.java @@ -30,7 +30,7 @@ public class BoundCodeableConceptDt> extends CodeableConceptDt return; } for (T next : theValues) { - getCoding().add(new BoundCodingDt(myBinder, next)); + getCoding().add(new CodingDt(myBinder.toSystemString(next), myBinder.toCodeString(next))); } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BoundCodingDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BoundCodingDt__.java similarity index 72% rename from hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BoundCodingDt.java rename to hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BoundCodingDt__.java index 0ce3b5ae708..17d111f2844 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BoundCodingDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/BoundCodingDt__.java @@ -5,16 +5,16 @@ import ca.uhn.fhir.model.api.IValueSetEnumBinder; import ca.uhn.fhir.model.api.annotation.DatatypeDef; import ca.uhn.fhir.model.dstu.composite.CodingDt; -@DatatypeDef(name = "Coding") -public class BoundCodingDt> extends CodingDt { +@DatatypeDef(name = "Coding", isSpecialization=true) +public class BoundCodingDt__> extends CodingDt { private IValueSetEnumBinder myBinder; - public BoundCodingDt(IValueSetEnumBinder theBinder) { + public BoundCodingDt__(IValueSetEnumBinder theBinder) { myBinder = theBinder; } - public BoundCodingDt(IValueSetEnumBinder theBinder, T theValue) { + public BoundCodingDt__(IValueSetEnumBinder theBinder, T theValue) { myBinder = theBinder; setValueAsEnum(theValue); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java index 08d1fa3c318..efe085eb696 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/IdDt.java @@ -8,10 +8,8 @@ import ca.uhn.fhir.model.api.annotation.SimpleSetter; import ca.uhn.fhir.parser.DataFormatException; /** - * Represents the FHIR ID type. This is the actual resource ID, meaning the ID that - * will be used in RESTful URLs, Resource References, etc. to represent a specific - * instance of a resource. - * + * Represents the FHIR ID type. This is the actual resource ID, meaning the ID that will be used in RESTful URLs, Resource References, etc. to represent a specific instance of a resource. + * *

* Description: A whole number in the range 0 to 2^64-1 (optionally represented in hex), a uuid, an oid, or any other combination of lowercase letters, numerals, "-" and ".", with a length * limit of 36 characters. @@ -32,6 +30,17 @@ public class IdDt extends BasePrimitive { super(); } + /** + * Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string representation. + */ + public IdDt(BigDecimal thePid) { + if (thePid != null) { + setValue(thePid.toPlainString()); + } else { + setValue(null); + } + } + /** * Create a new ID using a long */ @@ -56,14 +65,24 @@ public class IdDt extends BasePrimitive { } /** - * Create a new ID, using a BigDecimal input. Uses {@link BigDecimal#toPlainString()} to generate the string representation. + * Returns the value of this ID as a big decimal, or null if the value is null + * + * @throws NumberFormatException + * If the value is not a valid BigDecimal */ - public IdDt(BigDecimal thePid) { - if (thePid != null) { - setValue(thePid.toPlainString()); - } else { - setValue(null); + public BigDecimal asBigDecimal() { + if (getValue() == null) { + return null; } + return new BigDecimal(getValueAsString()); + } + + /** + * Returns a reference to this IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API. + */ + @Override + public IdDt getId() { + return this; } @Override @@ -76,6 +95,14 @@ public class IdDt extends BasePrimitive { return myValue; } + /** + * Copies the value from the given IdDt to this IdDt. It is generally not neccesary to use this method but it is provided for consistency with the rest of the API. + */ + @Override + public void setId(IdDt theId) { + setValue(theId.getValue()); + } + /** * Set the value * @@ -114,16 +141,4 @@ public class IdDt extends BasePrimitive { return myValue; } - /** - * Returns the value of this ID as a big decimal, or null if the value is null - * - * @throws NumberFormatException If the value is not a valid BigDecimal - */ - public BigDecimal asBigDecimal() { - if (getValue() == null){ - return null; - } - return new BigDecimal(getValueAsString()); - } - } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/StringDt.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/StringDt.java index ca3c0072568..579280911b4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/StringDt.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/primitive/StringDt.java @@ -101,5 +101,13 @@ public class StringDt extends BasePrimitive implements IQueryParameterTy return getValue(); } + /** + * Returns true if this datatype has no extensions, and has either a null + * value or an empty ("") value. + */ + @Override + public boolean isEmpty() { + return super.isEmpty() && StringUtils.isBlank(getValue()); + } } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java index 8035860fa08..0a8269dfe21 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/JsonParser.java @@ -99,7 +99,7 @@ public class JsonParser extends BaseParser implements IParser { writeTagWithTextNode(eventWriter, "id", nextEntry.getEntryId()); eventWriter.writeStartArray("link"); - writeAtomLink(eventWriter, "self", theBundle.getLinkSelf()); + writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf()); eventWriter.writeEnd(); writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated()); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java index 32f9a9ef815..086dc4989c3 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/parser/XmlParser.java @@ -100,7 +100,7 @@ public class XmlParser extends BaseParser implements IParser { writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase()); if (theBundle.getTotalResults().getValue() != null) { - eventWriter.writeStartElement("os", "totalResults",OPENSEARCH_NS); + eventWriter.writeStartElement("os", "totalResults", OPENSEARCH_NS); eventWriter.writeNamespace("os", OPENSEARCH_NS); eventWriter.writeCharacters(theBundle.getTotalResults().getValue().toString()); eventWriter.writeEndElement(); @@ -119,6 +119,13 @@ public class XmlParser extends BaseParser implements IParser { for (BundleEntry nextEntry : theBundle.getEntries()) { eventWriter.writeStartElement("entry"); + writeTagWithTextNode(eventWriter, "title", nextEntry.getTitle()); + writeTagWithTextNode(eventWriter, "id", nextEntry.getEntryId()); + + if (!nextEntry.getLinkSelf().isEmpty()) { + writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf()); + } + eventWriter.writeStartElement("content"); eventWriter.writeAttribute("type", "text/xml"); @@ -138,10 +145,10 @@ public class XmlParser extends BaseParser implements IParser { @Override public String encodeResourceToString(IResource theResource) throws DataFormatException { - if (theResource==null) { + if (theResource == null) { throw new NullPointerException("Resource can not be null"); } - + Writer stringWriter = new StringWriter(); encodeResourceToWriter(theResource, stringWriter); return stringWriter.toString(); @@ -200,8 +207,6 @@ public class XmlParser extends BaseParser implements IParser { return parseResource(theResourceType, streamReader); } - - @Override public IParser setPrettyPrint(boolean thePrettyPrint) { myPrettyPrint = thePrettyPrint; @@ -290,7 +295,8 @@ public class XmlParser extends BaseParser implements IParser { } } - private void encodeChildElementToStreamWriter(XMLStreamWriter theEventWriter, IElement nextValue, String childName, BaseRuntimeElementDefinition childDef, String theExtensionUrl) throws XMLStreamException, DataFormatException { + private void encodeChildElementToStreamWriter(XMLStreamWriter theEventWriter, IElement nextValue, String childName, BaseRuntimeElementDefinition childDef, String theExtensionUrl) + throws XMLStreamException, DataFormatException { if (nextValue.isEmpty()) { return; } @@ -355,7 +361,8 @@ public class XmlParser extends BaseParser implements IParser { } - private void encodeCompositeElementChildrenToStreamWriter(IElement theElement, XMLStreamWriter theEventWriter, List children) throws XMLStreamException, DataFormatException { + private void encodeCompositeElementChildrenToStreamWriter(IElement theElement, XMLStreamWriter theEventWriter, List children) throws XMLStreamException, + DataFormatException { for (BaseRuntimeChildDefinition nextChild : children) { List values = nextChild.getAccessor().getValues(theElement); if (values == null || values.isEmpty()) { @@ -392,7 +399,8 @@ public class XmlParser extends BaseParser implements IParser { } } - private void encodeCompositeElementToStreamWriter(IElement theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition resDef) throws XMLStreamException, DataFormatException { + private void encodeCompositeElementToStreamWriter(IElement theElement, XMLStreamWriter theEventWriter, BaseRuntimeElementCompositeDefinition resDef) throws XMLStreamException, + DataFormatException { encodeExtensionsIfPresent(theEventWriter, theElement); encodeCompositeElementChildrenToStreamWriter(theElement, theEventWriter, resDef.getExtensions()); encodeCompositeElementChildrenToStreamWriter(theElement, theEventWriter, resDef.getChildren()); @@ -419,22 +427,21 @@ public class XmlParser extends BaseParser implements IParser { } } - private void encodeResourceToXmlStreamWriter(IResource theResource, XMLStreamWriter theEventWriter, boolean theIncludeResourceId) throws XMLStreamException, DataFormatException { super.containResourcesForEncoding(theResource); - + RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource); if (resDef == null) { throw new ConfigurationException("Unknown resource type: " + theResource.getClass()); } - + theEventWriter.writeStartElement(resDef.getName()); theEventWriter.writeDefaultNamespace(FHIR_NS); if (theIncludeResourceId && StringUtils.isNotBlank(theResource.getId().getValue())) { theEventWriter.writeAttribute("id", theResource.getId().getValue()); } - + encodeCompositeElementToStreamWriter(theResource, theEventWriter, resDef); theEventWriter.writeEndElement(); @@ -449,7 +456,7 @@ public class XmlParser extends BaseParser implements IParser { IElement nextValue = next.getValue(); RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition(); String childName = extDef.getChildNameByDatatype(nextValue.getClass()); - if (childName==null) { + if (childName == null) { throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + nextValue.getClass().getCanonicalName()); } BaseRuntimeElementDefinition childDef = extDef.getChildElementDefinitionByDatatype(nextValue.getClass()); @@ -579,7 +586,6 @@ public class XmlParser extends BaseParser implements IParser { } } - private void writeTagWithTextNode(XMLStreamWriter theEventWriter, String theElementName, StringDt theStringDt) throws XMLStreamException { theEventWriter.writeStartElement(theElementName); if (StringUtils.isNotBlank(theStringDt.getValue())) { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Include.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Include.java index b2975e379f4..5b70d5d4eed 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Include.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/annotation/Include.java @@ -10,11 +10,23 @@ import java.lang.annotation.Target; * be populated with the "_include" values for a search param. *

* Only one parameter may be annotated with this annotation, and that - * parameter should be of type Collection, List, or Set. + * parameter should be one of the following: *

+ *
    + *
  • Collection<PathSpecification>
  • + *
  • List<PathSpecification>
  • + *
  • Set<PathSpecification>
  • + *
*/ @Retention(RetentionPolicy.RUNTIME) @Target(value= {ElementType.PARAMETER}) public @interface Include { + /** + * Optional parameter, if provided the server will only allow the values + * within the given set. If an _include parameter is passed to the server + * which does not match any allowed values the server will return an error. + */ + String[] allow() default {}; + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java index 811bafd5668..25e89d2a309 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/method/SearchMethodBinding.java @@ -106,6 +106,9 @@ public class SearchMethodBinding extends BaseMethodBinding { IParameter param = myParameters.get(i); String[] value = parameterValues.get(param.getName()); if (value == null || value.length == 0) { + if (param.handlesMissing()) { + params[i] = param.parse(new ArrayList>(0)); + } continue; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/IParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/IParameter.java index 55bcee8cb63..86fab200d5a 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/IParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/IParameter.java @@ -8,14 +8,20 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public interface IParameter { - public abstract List> encode(Object theObject) throws InternalErrorException; + List> encode(Object theObject) throws InternalErrorException; - public abstract String getName(); + String getName(); - public abstract Object parse(List> theString) throws InternalErrorException, InvalidRequestException; + Object parse(List> theString) throws InternalErrorException, InvalidRequestException; - public abstract boolean isRequired(); + boolean isRequired(); + + /** + * Parameter should return true if {@link #parse(List)} should be called even + * if the query string contained no values for the given parameter + */ + boolean handlesMissing(); - public abstract SearchParamTypeEnum getParamType(); + SearchParamTypeEnum getParamType(); } \ No newline at end of file diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/IncludeParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/IncludeParameter.java index c45a0c7f9e1..f1279016ef6 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/IncludeParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/IncludeParameter.java @@ -3,19 +3,29 @@ package ca.uhn.fhir.rest.param; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.TreeSet; import ca.uhn.fhir.model.api.PathSpecification; import ca.uhn.fhir.model.dstu.valueset.SearchParamTypeEnum; +import ca.uhn.fhir.rest.annotation.Include; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; public class IncludeParameter implements IParameter { private Class> myInstantiableCollectionType; + private HashSet myAllow; - public IncludeParameter(Class> theInstantiableCollectionType) { + public IncludeParameter(Include theAnnotation, Class> theInstantiableCollectionType) { myInstantiableCollectionType = theInstantiableCollectionType; + if (theAnnotation.allow().length > 0) { + myAllow = new HashSet(); + for (String next : theAnnotation.allow()) { + myAllow.add(next); + } + } } @SuppressWarnings("unchecked") @@ -53,7 +63,13 @@ public class IncludeParameter implements IParameter { throw new InvalidRequestException("'OR' query parameters (values containing ',') are not supported in _include parameters"); } - retVal.add(new PathSpecification(nextParamList.get(0))); + String value = nextParamList.get(0); + if (myAllow != null) { + if (!myAllow.contains(value)) { + throw new InvalidRequestException("Invalid _include parameter value: '" + value + "'. Valid values are: " + new TreeSet(myAllow)); + } + } + retVal.add(new PathSpecification(value)); } return retVal; @@ -69,4 +85,9 @@ public class IncludeParameter implements IParameter { return null; } + @Override + public boolean handlesMissing() { + return true; + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/SearchParameter.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/SearchParameter.java index 7fe6ab3219b..f8d5f9987fa 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/SearchParameter.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/param/SearchParameter.java @@ -100,6 +100,8 @@ public class SearchParameter implements IParameter { throw new ConfigurationException("Unknown search parameter type: " + type); } + // NB: Once this is enabled, we should return true from handlesMissing if + // it's a collection type // if (theInnerCollectionType != null) { // this.parser = new CollectionBinder(this.parser, theInnerCollectionType); // } @@ -115,4 +117,9 @@ public class SearchParameter implements IParameter { return myParamType; } + @Override + public boolean handlesMissing() { + return false; + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java index 0d3e15c972a..b4667de3d62 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java @@ -16,6 +16,8 @@ public class Constants { public static final Set FORMAT_VAL_XML; public static final Set FORMAT_VAL_JSON; public static final Map FORMAT_VAL_TO_ENCODING; + public static final String CT_XML = "application/xml"; + public static final String CT_JSON = "application/json"; static { Map valToEncoding = new HashMap(); diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/EncodingUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/EncodingUtil.java index f9e7b3c4abc..65cbb3f6977 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/EncodingUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/EncodingUtil.java @@ -2,16 +2,20 @@ package ca.uhn.fhir.rest.server; public enum EncodingUtil { - XML(Constants.CT_FHIR_XML, Constants.CT_ATOM_XML), + XML(Constants.CT_FHIR_XML, Constants.CT_ATOM_XML, Constants.CT_XML), - JSON(Constants.CT_FHIR_JSON, Constants.CT_FHIR_JSON); + JSON(Constants.CT_FHIR_JSON, Constants.CT_FHIR_JSON, Constants.CT_JSON) + + ; private String myResourceContentType; private String myBundleContentType; - - EncodingUtil(String theResourceContentType, String theBundleContentType) { + private String myBrowserFriendlyContentType; + + EncodingUtil(String theResourceContentType, String theBundleContentType, String theBrowserFriendlyContentType) { myResourceContentType = theResourceContentType; myBundleContentType = theBundleContentType; + myBrowserFriendlyContentType = theBrowserFriendlyContentType; } public String getBundleContentType() { @@ -22,4 +26,8 @@ public enum EncodingUtil { return myResourceContentType; } + public String getBrowserFriendlyBundleContentType() { + return myBrowserFriendlyContentType; + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 7556775d070..5cbf9b8add5 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -40,11 +40,24 @@ import ca.uhn.fhir.rest.server.provider.ServerProfileProvider; public abstract class RestfulServer extends HttpServlet { + private static final String PARAM_HISTORY = "_history"; + private static final String PARAM_PRETTY = "_pretty"; + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServer.class); private static final long serialVersionUID = 1L; private FhirContext myFhirContext; + private boolean myUseBrowserFriendlyContentTypes; + + /** + * If set to true (default is false), the server will use browser friendly + * content-types (instead of standard FHIR ones) when it detects that the request + * is coming from a browser instead of a FHIR + */ + public void setUseBrowserFriendlyContentTypes(boolean theUseBrowserFriendlyContentTypes) { + myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes; + } private Map, IResourceProvider> myTypeToProvider = new HashMap, IResourceProvider>(); @@ -75,18 +88,17 @@ public abstract class RestfulServer extends HttpServlet { } return EncodingUtil.XML; } - + @Override protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { handleRequest(SearchMethodBinding.RequestType.DELETE, request, response); } - @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { handleRequest(SearchMethodBinding.RequestType.GET, request, response); } - + @Override protected void doOptions(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException { handleRequest(SearchMethodBinding.RequestType.OPTIONS, theReq, theResp); @@ -102,7 +114,6 @@ public abstract class RestfulServer extends HttpServlet { handleRequest(SearchMethodBinding.RequestType.PUT, request, response); } - private void findResourceMethods(IResourceProvider theProvider) throws Exception { Class resourceType = theProvider.getResourceType(); @@ -131,12 +142,11 @@ public abstract class RestfulServer extends HttpServlet { } } - public FhirContext getFhirContext() { return myFhirContext; } - private IParser getNewParser(EncodingUtil theResponseEncoding) { + private IParser getNewParser(EncodingUtil theResponseEncoding, boolean thePrettyPrint) { IParser parser; switch (theResponseEncoding) { case JSON: @@ -147,7 +157,7 @@ public abstract class RestfulServer extends HttpServlet { parser = myFhirContext.newXmlParser(); break; } - return parser; + return parser.setPrettyPrint(thePrettyPrint); } public Collection getResourceBindings() { @@ -160,8 +170,7 @@ public abstract class RestfulServer extends HttpServlet { public abstract Collection getResourceProviders(); /** - * This method should be overridden to provide a security manager instance. - * By default, returns null. + * This method should be overridden to provide a security manager instance. By default, returns null. */ public ISecurityManager getSecurityManager() { return null; @@ -181,40 +190,61 @@ public abstract class RestfulServer extends HttpServlet { if (null != securityManager) { securityManager.authenticate(request); } + + String uaHeader = request.getHeader("user-agent"); + boolean requestIsBrowser = false; + if (uaHeader != null && uaHeader.contains("Mozilla")) { + requestIsBrowser = true; + } + String resourceName = null; String requestFullPath = StringUtils.defaultString(request.getRequestURI()); -// String contextPath = StringUtils.defaultString(request.getContextPath()); + // String contextPath = StringUtils.defaultString(request.getContextPath()); String servletPath = StringUtils.defaultString(request.getServletPath()); StringBuffer requestUrl = request.getRequestURL(); String servletContextPath = ""; if (request.getServletContext() != null) { servletContextPath = StringUtils.defaultString(request.getServletContext().getContextPath()); } - + ourLog.info("Request FullPath: {}", requestFullPath); ourLog.info("Servlet Path: {}", servletPath); ourLog.info("Request Url: {}", requestUrl); ourLog.info("Context Path: {}", servletContextPath); servletPath = servletContextPath; - + IdDt id = null; IdDt versionId = null; - String operation = null; - + String operation = null; + String requestPath = requestFullPath.substring(servletPath.length()); if (requestPath.length() > 0 && requestPath.charAt(0) == '/') { requestPath = requestPath.substring(1); } - int contextIndex = requestUrl.indexOf(servletPath); + int contextIndex; + if (servletPath.length()==0) { + contextIndex = requestUrl.indexOf(requestPath); + }else { + contextIndex = requestUrl.indexOf(servletPath); + } + String fhirServerBase = requestUrl.substring(0, contextIndex + servletPath.length()); String completeUrl = StringUtils.isNotBlank(request.getQueryString()) ? requestUrl + "?" + request.getQueryString() : requestUrl.toString(); - + Map params = new HashMap(request.getParameterMap()); EncodingUtil responseEncoding = determineResponseEncoding(request, params); + String[] pretty = params.remove(PARAM_PRETTY); + boolean prettyPrint = false; + if (pretty != null && pretty.length > 0) { + if ("true".equals(pretty[0])) { + prettyPrint = true; + } + } + StringTokenizer tok = new StringTokenizer(requestPath, "/"); if (!tok.hasMoreTokens()) { throw new MethodNotFoundException("No resource name specified"); @@ -228,7 +258,7 @@ public abstract class RestfulServer extends HttpServlet { } else { resourceBinding = resources.get(resourceName); } - + if (resourceBinding == null) { throw new MethodNotFoundException("Unknown resource type: " + resourceName); } @@ -244,7 +274,7 @@ public abstract class RestfulServer extends HttpServlet { if (tok.hasMoreTokens()) { String nextString = tok.nextToken(); - if (nextString.startsWith("_history")) { + if (nextString.startsWith(PARAM_HISTORY)) { if (tok.hasMoreTokens()) { versionId = new IdDt(tok.nextToken()); } else { @@ -271,7 +301,7 @@ public abstract class RestfulServer extends HttpServlet { List result = resourceMethod.invokeServer(resourceBinding.getResourceProvider(), id, versionId, params); switch (resourceMethod.getReturnType()) { case BUNDLE: - streamResponseAsBundle(response, result, responseEncoding, fhirServerBase, completeUrl); + streamResponseAsBundle(response, result, responseEncoding, fhirServerBase, completeUrl, prettyPrint, requestIsBrowser); break; case RESOURCE: if (result.size() == 0) { @@ -279,7 +309,7 @@ public abstract class RestfulServer extends HttpServlet { } else if (result.size() > 1) { throw new InternalErrorException("Method returned multiple resources"); } - streamResponseAsResource(response, result.get(0), resourceBinding, responseEncoding); + streamResponseAsResource(response, result.get(0), responseEncoding, prettyPrint, requestIsBrowser); break; } // resourceMethod.get @@ -334,7 +364,7 @@ public abstract class RestfulServer extends HttpServlet { for (IResourceProvider provider : myTypeToProvider.values()) { findResourceMethods(provider); } - + findResourceMethods(getServerProfilesProvider()); findResourceMethods(getServerConformanceProvider()); @@ -344,16 +374,25 @@ public abstract class RestfulServer extends HttpServlet { } } - private void streamResponseAsBundle(HttpServletResponse theHttpResponse, List theResult, EncodingUtil theResponseEncoding, String theFhirServerBase, String theCompleteUrl) throws IOException { + private void streamResponseAsBundle(HttpServletResponse theHttpResponse, List theResult, EncodingUtil theResponseEncoding, String theServerBase, String theCompleteUrl, + boolean thePrettyPrint, boolean theRequestIsBrowser) throws IOException { + assert theServerBase.endsWith("/"); + theHttpResponse.setStatus(200); - theHttpResponse.setContentType(theResponseEncoding.getBundleContentType()); + + if (theRequestIsBrowser && myUseBrowserFriendlyContentTypes) { + theHttpResponse.setContentType(theResponseEncoding.getBrowserFriendlyBundleContentType()); + } else { + theHttpResponse.setContentType(theResponseEncoding.getBundleContentType()); + } + theHttpResponse.setCharacterEncoding("UTF-8"); Bundle bundle = new Bundle(); bundle.getAuthorName().setValue(getClass().getCanonicalName()); bundle.getBundleId().setValue(UUID.randomUUID().toString()); bundle.getPublished().setToCurrentTimeInLocalTimeZone(); - bundle.getLinkBase().setValue(theFhirServerBase); + bundle.getLinkBase().setValue(theServerBase); bundle.getLinkSelf().setValue(theCompleteUrl); for (IResource next : theResult) { @@ -361,23 +400,55 @@ public abstract class RestfulServer extends HttpServlet { bundle.getEntries().add(entry); entry.setResource(next); + + RuntimeResourceDefinition def = myFhirContext.getResourceDefinition(next); + + if (next.getId() != null && StringUtils.isNotBlank(next.getId().getValue())) { + entry.getEntryId().setValue(next.getId().getValue()); + entry.getTitle().setValue(def.getName() + " " + next.getId().getValue()); + + StringBuilder b = new StringBuilder(); + b.append(theServerBase); + b.append(def.getName()); + b.append('/'); + b.append(next.getId().getValue()); + boolean haveQ = false; + if (thePrettyPrint) { + b.append('?').append(PARAM_PRETTY).append("=true"); + haveQ = true; + } + if (theResponseEncoding == EncodingUtil.JSON) { + if (!haveQ) { + b.append('?'); + haveQ = true; + } else { + b.append('&'); + } + b.append(Constants.PARAM_FORMAT).append("=json"); + } + entry.getLinkSelf().setValue(b.toString()); + } } bundle.getTotalResults().setValue(theResult.size()); PrintWriter writer = theHttpResponse.getWriter(); - getNewParser(theResponseEncoding).encodeBundleToWriter(bundle, writer); + getNewParser(theResponseEncoding, thePrettyPrint).encodeBundleToWriter(bundle, writer); writer.close(); } - private void streamResponseAsResource(HttpServletResponse theHttpResponse, IResource theResource, ResourceBinding theResourceBinding, EncodingUtil theResponseEncoding) throws IOException { + private void streamResponseAsResource(HttpServletResponse theHttpResponse, IResource theResource, EncodingUtil theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser) throws IOException { theHttpResponse.setStatus(200); - theHttpResponse.setContentType(theResponseEncoding.getResourceContentType()); + if (theRequestIsBrowser && myUseBrowserFriendlyContentTypes) { + theHttpResponse.setContentType(theResponseEncoding.getBrowserFriendlyBundleContentType()); + } else { + theHttpResponse.setContentType(theResponseEncoding.getBundleContentType()); + } theHttpResponse.setCharacterEncoding("UTF-8"); PrintWriter writer = theHttpResponse.getWriter(); - getNewParser(theResponseEncoding).encodeResourceToWriter(theResource, writer); + getNewParser(theResponseEncoding, thePrettyPrint).encodeResourceToWriter(theResource, writer); writer.close(); } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Util.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Util.java index ac97b45f2ed..7cca3cd7044 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Util.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Util.java @@ -10,7 +10,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import ch.qos.logback.core.joran.action.ParamAction; import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.model.api.PathSpecification; import ca.uhn.fhir.rest.annotation.Include; @@ -89,7 +88,7 @@ public class Util { } Class> instantiableCollectionType = (Class>) CollectionBinder.getInstantiableCollectionType(innerCollectionType, "Method '" + method.getName() + "'"); - param = new IncludeParameter(instantiableCollectionType); + param = new IncludeParameter((Include) nextAnnotation, instantiableCollectionType); } else { continue; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java index 7776d5ce172..e4d3bef5262 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/provider/ServerConformanceProvider.java @@ -1,6 +1,5 @@ package ca.uhn.fhir.rest.server.provider; -import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -22,13 +21,11 @@ import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.StringDt; import ca.uhn.fhir.rest.annotation.Metadata; import ca.uhn.fhir.rest.method.BaseMethodBinding; -import ca.uhn.fhir.rest.method.ReadMethodBinding; import ca.uhn.fhir.rest.method.SearchMethodBinding; import ca.uhn.fhir.rest.param.IParameter; import ca.uhn.fhir.rest.server.IResourceProvider; import ca.uhn.fhir.rest.server.ResourceBinding; import ca.uhn.fhir.rest.server.RestfulServer; -import ca.uhn.fhir.util.DatatypeUtil; import ca.uhn.fhir.util.ExtensionConstants; import ca.uhn.fhir.util.VersionUtil; diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ElementUtil.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ElementUtil.java index 7fdcf3b636a..e0fae7e0edd 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ElementUtil.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/ElementUtil.java @@ -19,6 +19,8 @@ public class ElementUtil { if (!isEmpty((List) next)) { return false; } + } else if (next instanceof String && (!((String)next).isEmpty())) { + return false; } else if (next != null && !((IElement) next).isEmpty()) { return false; } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PrettyPrintWriterWrapper.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PrettyPrintWriterWrapper.java index 67ded980931..e755459c8ba 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PrettyPrintWriterWrapper.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/util/PrettyPrintWriterWrapper.java @@ -94,7 +94,7 @@ public class PrettyPrintWriterWrapper implements XMLStreamWriter { @Override public void writeStartElement(String thePrefix, String theLocalName, String theNamespaceURI) throws XMLStreamException { indentAndAdd(); - myTarget.writeStartElement(thePrefix, theNamespaceURI, theLocalName); + myTarget.writeStartElement(thePrefix, theLocalName, theNamespaceURI); } @Override diff --git a/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java b/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java index 9bcecb75209..459c7b77a32 100644 --- a/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java +++ b/hapi-fhir-base/src/site/example/java/example/RestfulPatientResourceProviderMore.java @@ -138,8 +138,11 @@ public Class getResourceType() { //START SNIPPET: pathSpec @Search() -public List getDiagnosticReport( @Required(name=DiagnosticReport.SP_IDENTIFIER) IdentifierDt theIdentifier, - @Include Set theIncludes ) { +public List getDiagnosticReport( + @Required(name=DiagnosticReport.SP_IDENTIFIER) + IdentifierDt theIdentifier, + @Include(allow= {"DiagnosticReport.subject"}) + Set theIncludes ) { List retVal = new ArrayList(); // Assume this method exists and loads the report from the DB diff --git a/hapi-fhir-base/src/site/xdoc/doc_rest_operations.xml b/hapi-fhir-base/src/site/xdoc/doc_rest_operations.xml index cee6662b814..57374052a08 100644 --- a/hapi-fhir-base/src/site/xdoc/doc_rest_operations.xml +++ b/hapi-fhir-base/src/site/xdoc/doc_rest_operations.xml @@ -300,7 +300,7 @@

- Invoking a client of thie type involves the following syntax: + Invoking a client of this type involves the following syntax:

@@ -328,6 +328,11 @@ +

+ Example URL to invoke this method:
+ http://fhir.example.com/DiagnosticReport?subject.identifier=7000135&_include=DiagnosticReport.subject +

+ diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java index 7ce45b551f6..982bdf4b152 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/parser/XmlParserTest.java @@ -27,6 +27,8 @@ import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Specimen; import ca.uhn.fhir.model.dstu.resource.ValueSet; +import ca.uhn.fhir.model.dstu.valueset.AddressUseEnum; +import ca.uhn.fhir.model.dstu.valueset.AdministrativeGenderCodesEnum; import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum; import ca.uhn.fhir.model.primitive.DecimalDt; @@ -43,6 +45,33 @@ public class XmlParserTest { assertEquals("Patient resource with id 3216379", bundle.getEntries().get(0).getTitle().getValue()); } + + @Test + public void testEncodeBoundCode() throws IOException { + + Patient patient = new Patient(); + patient.addAddress().setUse(AddressUseEnum.HOME); + + patient.getGender().setValueAsEnum(AdministrativeGenderCodesEnum.M); + + String val = new FhirContext().newXmlParser().encodeResourceToString(patient); + ourLog.info(val); + + } + + @Test + public void testEncodeBundleResultCount() throws IOException { + + Bundle b = new Bundle(); + b.getTotalResults().setValue(123); + + String val = new FhirContext().newXmlParser().setPrettyPrint(true).encodeBundleToString(b); + ourLog.info(val); + + assertThat(val, StringContains.containsString("123")); + + } + @Test public void testParseContainedResources() throws IOException { diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java index 56bfb189119..848becdf9c5 100644 --- a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/ResfulServerMethodTest.java @@ -1,9 +1,9 @@ package ca.uhn.fhir.rest.server; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -23,13 +23,15 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletHandler; import org.eclipse.jetty.servlet.ServletHolder; +import org.hamcrest.core.IsNot; +import org.hamcrest.core.StringContains; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import org.junit.experimental.theories.Theories; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.PathSpecification; import ca.uhn.fhir.model.dstu.composite.CodingDt; import ca.uhn.fhir.model.dstu.composite.HumanNameDt; @@ -104,12 +106,60 @@ public class ResfulServerMethodTest { assertEquals(200, status.getStatusLine().getStatusCode()); Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent); - Patient patient = (Patient) bundle.getEntries().get(0).getResource(); + BundleEntry entry0 = bundle.getEntries().get(0); + Patient patient = (Patient) entry0.getResource(); assertEquals("include1", patient.getCommunication().get(0).getText().getValue()); assertEquals("include2", patient.getAddress().get(0).getLine().get(0).getValue()); assertEquals("include3", patient.getAddress().get(1).getLine().get(0).getValue()); } + @Test + public void testSearchWithIncludesNone() throws Exception { + + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?withIncludes=include1"); + HttpResponse status = ourClient.execute(httpGet); + + String responseContent = IOUtils.toString(status.getEntity().getContent()); + ourLog.info("Response was:\n{}", responseContent); + + // Make sure there is no crash + assertEquals(200, status.getStatusLine().getStatusCode()); + } + + @Test + public void testSearchWithIncludesBad() throws Exception { + + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?withIncludes=include1&_include=include2&_include=include4"); + HttpResponse status = ourClient.execute(httpGet); + + String responseContent = IOUtils.toString(status.getEntity().getContent()); + ourLog.info("Response was:\n{}", responseContent); + + assertEquals(400, status.getStatusLine().getStatusCode()); + } + + @Test + public void testEntryLinkSelf() throws Exception { + + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?withIncludes=include1&_include=include2&_include=include3"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + assertEquals(200, status.getStatusLine().getStatusCode()); + Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent); + BundleEntry entry0 = bundle.getEntries().get(0); + assertEquals("http://localhost:" + ourPort + "/Patient/1", entry0.getLinkSelf().getValue()); + assertEquals("1", entry0.getEntryId().getValue()); + + httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?withIncludes=include1&_include=include2&_include=include3&_format=json"); + status = ourClient.execute(httpGet); + responseContent = IOUtils.toString(status.getEntity().getContent()); + assertEquals(200, status.getStatusLine().getStatusCode()); + bundle = ourCtx.newJsonParser().parseBundle(responseContent); + entry0 = bundle.getEntries().get(0); + assertEquals("http://localhost:" + ourPort + "/Patient/1?_format=json", entry0.getLinkSelf().getValue()); + + } + @Test public void testSearchAllProfiles() throws Exception { @@ -216,18 +266,18 @@ public class ResfulServerMethodTest { assertEquals("urn:bbb|bbb", patient.getIdentifier().get(2).getValueAsQueryToken()); } -// @Test -// public void testSearchByComplex() throws Exception { -// -// HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?Patient.identifier=urn:oid:2.16.840.1.113883.3.239.18.148%7C7000135&name=urn:oid:1.3.6.1.4.1.12201.102.5%7C522&date="); -// HttpResponse status = ourClient.execute(httpGet); -// -// String responseContent = IOUtils.toString(status.getEntity().getContent()); -// ourLog.info("Response was:\n{}", responseContent); -// -// assertEquals(200, status.getStatusLine().getStatusCode()); -// } - + // @Test + // public void testSearchByComplex() throws Exception { + // + // HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?Patient.identifier=urn:oid:2.16.840.1.113883.3.239.18.148%7C7000135&name=urn:oid:1.3.6.1.4.1.12201.102.5%7C522&date="); + // HttpResponse status = ourClient.execute(httpGet); + // + // String responseContent = IOUtils.toString(status.getEntity().getContent()); + // ourLog.info("Response was:\n{}", responseContent); + // + // assertEquals(200, status.getStatusLine().getStatusCode()); + // } + @Test public void testSearchByDob() throws Exception { @@ -404,6 +454,31 @@ public class ResfulServerMethodTest { } + @Test + public void testPrettyPrint() throws Exception { + + // HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + + // "/Patient/1"); + // httpPost.setEntity(new StringEntity("test", + // ContentType.create(Constants.CT_FHIR_XML, "UTF-8"))); + + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + assertThat(responseContent, StringContains.containsString(" theIncludes) { + public Patient getPatientWithIncludes(@Required(name = "withIncludes") StringDt theString, + @Include(allow= {"include1","include2", "include3"}) List theIncludes) { Patient next = getIdToPatient().get("1"); next.addCommunication().setText(theString.getValue()); @@ -553,14 +631,11 @@ public class ResfulServerMethodTest { } @SuppressWarnings("unused") - public List findDiagnosticReportsByPatient( - @Required(name="Patient.identifier") IdentifierDt thePatientId, - @Required(name=DiagnosticReport.SP_NAME) CodingListParam theNames, - @Optional(name=DiagnosticReport.SP_DATE) DateRangeParam theDateRange - ) throws Exception { + public List findDiagnosticReportsByPatient(@Required(name = "Patient.identifier") IdentifierDt thePatientId, @Required(name = DiagnosticReport.SP_NAME) CodingListParam theNames, + @Optional(name = DiagnosticReport.SP_DATE) DateRangeParam theDateRange) throws Exception { return Collections.emptyList(); } - + @Search() public Patient getPatientWithDOB(@Required(name = "dob") QualifiedDateParam theDob) { Patient next = getIdToPatient().get("1");