Lots of bug fixes and documentation tweaks - Several failing unit tests checked in, need to get these passing
This commit is contained in:
parent
9f302937d9
commit
4b7a4ac794
|
@ -225,6 +225,7 @@
|
||||||
<artifactId>maven-site-plugin</artifactId>
|
<artifactId>maven-site-plugin</artifactId>
|
||||||
<version>3.3</version>
|
<version>3.3</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<!--
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||||
|
@ -250,6 +251,7 @@
|
||||||
<artifactId>maven-jxr-plugin</artifactId>
|
<artifactId>maven-jxr-plugin</artifactId>
|
||||||
<version>2.4</version>
|
<version>2.4</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
-->
|
||||||
<!--
|
<!--
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
|
|
@ -20,8 +20,10 @@ package ca.uhn.fhir.context;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
|
@ -33,6 +35,7 @@ import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Properties;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
@ -56,16 +59,11 @@ import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
import ca.uhn.fhir.model.api.annotation.Extension;
|
import ca.uhn.fhir.model.api.annotation.Extension;
|
||||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||||
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
|
import ca.uhn.fhir.model.api.annotation.SearchParamDefinition;
|
||||||
import ca.uhn.fhir.model.dstu.composite.AttachmentDt;
|
|
||||||
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
|
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
|
|
||||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||||
import ca.uhn.fhir.model.primitive.BoundCodeableConceptDt;
|
import ca.uhn.fhir.model.primitive.BoundCodeableConceptDt;
|
||||||
import ca.uhn.fhir.model.primitive.CodeDt;
|
|
||||||
import ca.uhn.fhir.model.primitive.DateDt;
|
|
||||||
import ca.uhn.fhir.model.primitive.DecimalDt;
|
|
||||||
import ca.uhn.fhir.model.primitive.ICodedDatatype;
|
import ca.uhn.fhir.model.primitive.ICodedDatatype;
|
||||||
import ca.uhn.fhir.model.primitive.XhtmlDt;
|
import ca.uhn.fhir.model.primitive.XhtmlDt;
|
||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
|
@ -148,13 +146,43 @@ class ModelScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(Set<Class<? extends IElement>> toScan) {
|
private void init(Set<Class<? extends IElement>> toScan) {
|
||||||
toScan.add(DateDt.class);
|
long start = System.currentTimeMillis();
|
||||||
toScan.add(CodeDt.class);
|
|
||||||
toScan.add(DecimalDt.class);
|
InputStream str = ModelScanner.class.getResourceAsStream("/ca/uhn/fhir/model/dstu/model.properties");
|
||||||
toScan.add(AttachmentDt.class);
|
if (str == null) {
|
||||||
toScan.add(ResourceReferenceDt.class);
|
str = ModelScanner.class.getResourceAsStream("ca/uhn/fhir/model/dstu/model.properties");
|
||||||
toScan.add(QuantityDt.class); // TODO: why is this required
|
}
|
||||||
|
if (str == null) {
|
||||||
|
throw new ConfigurationException("Can not find model property file on classpath: " + "/ca/uhn/fhir/model/dstu/model.properties");
|
||||||
|
}
|
||||||
|
Properties prop = new Properties();
|
||||||
|
try {
|
||||||
|
prop.load(str);
|
||||||
|
for (Object nextValue : prop.values()) {
|
||||||
|
try {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Class<? extends IElement> nextClass = (Class<? extends IElement>) Class.forName((String) nextValue);
|
||||||
|
if (!IElement.class.isAssignableFrom(nextClass)) {
|
||||||
|
ourLog.warn("Class is not assignable from " + IElement.class.getSimpleName()+": " + nextValue);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
toScan.add(nextClass);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
ourLog.warn("Unknown class exception: " + nextValue, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ConfigurationException("Failed to load model property file from classpath: " + "/ca/uhn/fhir/model/dstu/model.properties");
|
||||||
|
}
|
||||||
|
|
||||||
|
// toScan.add(DateDt.class);
|
||||||
|
// toScan.add(CodeDt.class);
|
||||||
|
// toScan.add(DecimalDt.class);
|
||||||
|
// toScan.add(AttachmentDt.class);
|
||||||
|
// toScan.add(ResourceReferenceDt.class);
|
||||||
|
// toScan.add(QuantityDt.class);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
for (Class<? extends IElement> nextClass : toScan) {
|
for (Class<? extends IElement> nextClass : toScan) {
|
||||||
scan(nextClass);
|
scan(nextClass);
|
||||||
|
@ -176,7 +204,8 @@ class ModelScanner {
|
||||||
myRuntimeChildUndeclaredExtensionDefinition = new RuntimeChildUndeclaredExtensionDefinition();
|
myRuntimeChildUndeclaredExtensionDefinition = new RuntimeChildUndeclaredExtensionDefinition();
|
||||||
myRuntimeChildUndeclaredExtensionDefinition.sealAndInitialize(myClassToElementDefinitions);
|
myRuntimeChildUndeclaredExtensionDefinition.sealAndInitialize(myClassToElementDefinitions);
|
||||||
|
|
||||||
ourLog.info("Done scanning FHIR library, found {} model entries", myClassToElementDefinitions.size());
|
long time = System.currentTimeMillis() - start;
|
||||||
|
ourLog.info("Done scanning FHIR library, found {} model entries in {}ms", myClassToElementDefinitions.size(), time);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scan(Class<? extends IElement> theClass) throws ConfigurationException {
|
private void scan(Class<? extends IElement> theClass) throws ConfigurationException {
|
||||||
|
@ -315,8 +344,7 @@ class ModelScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private void scanCompositeElementForChildren(Class<? extends ICompositeElement> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
|
private void scanCompositeElementForChildren(Class<? extends ICompositeElement> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
|
||||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
|
|
||||||
int baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
|
int baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
|
||||||
|
|
||||||
for (Field next : theClass.getDeclaredFields()) {
|
for (Field next : theClass.getDeclaredFields()) {
|
||||||
|
@ -332,7 +360,7 @@ class ModelScanner {
|
||||||
String elementName = childAnnotation.name();
|
String elementName = childAnnotation.name();
|
||||||
int order = childAnnotation.order();
|
int order = childAnnotation.order();
|
||||||
if (order < 0 && order != Child.ORDER_UNKNOWN) {
|
if (order < 0 && order != Child.ORDER_UNKNOWN) {
|
||||||
throw new ConfigurationException("Invalid order '" + order +"' on @Child for field '" + next.getName()+ "' on target type: " + theClass);
|
throw new ConfigurationException("Invalid order '" + order + "' on @Child for field '" + next.getName() + "' on target type: " + theClass);
|
||||||
}
|
}
|
||||||
if (order != Child.ORDER_UNKNOWN) {
|
if (order != Child.ORDER_UNKNOWN) {
|
||||||
order = order + baseElementOrder;
|
order = order + baseElementOrder;
|
||||||
|
@ -376,7 +404,7 @@ class ModelScanner {
|
||||||
*/
|
*/
|
||||||
RuntimeChildContainedResources def = new RuntimeChildContainedResources(next, childAnnotation, descriptionAnnotation, elementName);
|
RuntimeChildContainedResources def = new RuntimeChildContainedResources(next, childAnnotation, descriptionAnnotation, elementName);
|
||||||
orderMap.put(order, def);
|
orderMap.put(order, def);
|
||||||
|
|
||||||
} else if (choiceTypes.size() > 1 && !ResourceReferenceDt.class.isAssignableFrom(nextElementType)) {
|
} else if (choiceTypes.size() > 1 && !ResourceReferenceDt.class.isAssignableFrom(nextElementType)) {
|
||||||
/*
|
/*
|
||||||
* Child is a choice element
|
* Child is a choice element
|
||||||
|
@ -424,7 +452,7 @@ class ModelScanner {
|
||||||
orderMap.put(order, def);
|
orderMap.put(order, def);
|
||||||
|
|
||||||
} else if (IDatatype.class.equals(nextElementType)) {
|
} else if (IDatatype.class.equals(nextElementType)) {
|
||||||
|
|
||||||
RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation);
|
RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation);
|
||||||
orderMap.put(order, def);
|
orderMap.put(order, def);
|
||||||
|
|
||||||
|
@ -534,15 +562,14 @@ class ModelScanner {
|
||||||
scanCompositeElementForChildren(theClass, resourceDef);
|
scanCompositeElementForChildren(theClass, resourceDef);
|
||||||
|
|
||||||
myIdToResourceDefinition.put(resourceId, resourceDef);
|
myIdToResourceDefinition.put(resourceId, resourceDef);
|
||||||
|
|
||||||
scanResourceForSearchParams(theClass, resourceDef);
|
scanResourceForSearchParams(theClass, resourceDef);
|
||||||
|
|
||||||
|
|
||||||
return resourceName;
|
return resourceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scanResourceForSearchParams(Class<? extends IResource> theClass, RuntimeResourceDefinition theResourceDef) {
|
private void scanResourceForSearchParams(Class<? extends IResource> theClass, RuntimeResourceDefinition theResourceDef) {
|
||||||
|
|
||||||
for (Field nextField : theClass.getFields()) {
|
for (Field nextField : theClass.getFields()) {
|
||||||
SearchParamDefinition searchParam = nextField.getAnnotation(SearchParamDefinition.class);
|
SearchParamDefinition searchParam = nextField.getAnnotation(SearchParamDefinition.class);
|
||||||
if (searchParam != null) {
|
if (searchParam != null) {
|
||||||
|
@ -550,7 +577,7 @@ class ModelScanner {
|
||||||
theResourceDef.addSearchParam(param);
|
theResourceDef.addSearchParam(param);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, RuntimeResourceDefinition> getIdToResourceDefinition() {
|
public Map<String, RuntimeResourceDefinition> getIdToResourceDefinition() {
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
package ca.uhn.fhir.model.api;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
public class BundleCategory extends BaseElement implements IElement {
|
|
||||||
|
|
||||||
private String myLabel;
|
|
||||||
private String myScheme;
|
|
||||||
private String myTerm;
|
|
||||||
|
|
||||||
public String getLabel() {
|
|
||||||
return myLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getScheme() {
|
|
||||||
return myScheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTerm() {
|
|
||||||
return myTerm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BundleCategory setLabel(String theLabel) {
|
|
||||||
myLabel = theLabel;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BundleCategory setScheme(String theScheme) {
|
|
||||||
myScheme = theScheme;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BundleCategory setTerm(String theTerm) {
|
|
||||||
myTerm = theTerm;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return StringUtils.isBlank(myLabel) && StringUtils.isBlank(myScheme) && StringUtils.isBlank(myTerm);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -20,9 +20,6 @@ package ca.uhn.fhir.model.api;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
|
@ -43,7 +40,7 @@ public class BundleEntry extends BaseBundle {
|
||||||
private StringDt myTitle;
|
private StringDt myTitle;
|
||||||
private InstantDt myUpdated;
|
private InstantDt myUpdated;
|
||||||
private XhtmlDt mySummary;
|
private XhtmlDt mySummary;
|
||||||
private List<BundleCategory> myCategories;
|
private TagList myCategories;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
|
@ -104,15 +101,15 @@ public class BundleEntry extends BaseBundle {
|
||||||
return mySummary;
|
return mySummary;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BundleCategory addCategory() {
|
public Tag addCategory() {
|
||||||
BundleCategory retVal = new BundleCategory();
|
Tag retVal = new Tag();
|
||||||
getCategories().add(retVal);
|
getCategories().add(retVal);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<BundleCategory> getCategories() {
|
public TagList getCategories() {
|
||||||
if (myCategories == null) {
|
if (myCategories == null) {
|
||||||
myCategories = new ArrayList<BundleCategory>();
|
myCategories = new TagList();
|
||||||
}
|
}
|
||||||
return myCategories;
|
return myCategories;
|
||||||
}
|
}
|
||||||
|
@ -127,4 +124,8 @@ public class BundleEntry extends BaseBundle {
|
||||||
myUpdated = theUpdated;
|
myUpdated = theUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addCategory(Tag theTag) {
|
||||||
|
getCategories().add(theTag);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import java.util.Map;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
|
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
|
||||||
public interface IResource extends ICompositeElement {
|
public interface IResource extends ICompositeElement {
|
||||||
|
|
||||||
|
@ -34,28 +35,42 @@ public interface IResource extends ICompositeElement {
|
||||||
* list automatically (placing inline resources in the contained list when
|
* list automatically (placing inline resources in the contained list when
|
||||||
* encoding, and copying contained resources from this list to their
|
* encoding, and copying contained resources from this list to their
|
||||||
* appropriate references when parsing) so it is generally not neccesary to
|
* appropriate references when parsing) so it is generally not neccesary to
|
||||||
* interact with this list directly.
|
* interact with this list directly. Instead, in a server you can place
|
||||||
|
* resource instances in reference fields (such as {@link Patient#setManagingOrganization(ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt)})
|
||||||
|
* and the resource will be automatically contained. In a client, contained resources will
|
||||||
|
* be automatically populated into their appropriate fields by the HAPI parser.
|
||||||
* </p>
|
* </p>
|
||||||
* TODO: document contained resources and link there
|
* TODO: document contained resources and link there
|
||||||
*/
|
*/
|
||||||
ContainedDt getContained();
|
ContainedDt getContained();
|
||||||
|
|
||||||
NarrativeDt getText();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the metadata map for this object, creating it if neccesary. Metadata
|
* Returns the narrative block for this resource
|
||||||
* entries are used to get/set feed bundle entries, such as the
|
*/
|
||||||
|
NarrativeDt getText();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the metadata map for this object, creating it if neccesary.
|
||||||
|
* Metadata entries are used to get/set feed bundle entries, such as the
|
||||||
* resource version, or the last updated timestamp.
|
* resource version, or the last updated timestamp.
|
||||||
|
* <p>
|
||||||
|
* Keys in this map are enumerated in the {@link ResourceMetadataKeyEnum},
|
||||||
|
* and each key has a specific value type that it must use.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see ResourceMetadataKeyEnum for a list of allowable keys and the object
|
||||||
|
* types that values of a given key must use.
|
||||||
*/
|
*/
|
||||||
Map<ResourceMetadataKeyEnum, Object> getResourceMetadata();
|
Map<ResourceMetadataKeyEnum, Object> getResourceMetadata();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the metadata map for this object. Metadata
|
* Sets the metadata map for this object. Metadata entries are used to
|
||||||
* entries are used to get/set feed bundle entries, such as the
|
* get/set feed bundle entries, such as the resource version, or the last
|
||||||
* resource version, or the last updated timestamp.
|
* updated timestamp.
|
||||||
*
|
*
|
||||||
* @throws NullPointerException The map must not be null
|
* @throws NullPointerException
|
||||||
|
* The map must not be null
|
||||||
*/
|
*/
|
||||||
void setResourceMetadata(Map<ResourceMetadataKeyEnum, Object> theMap);
|
void setResourceMetadata(Map<ResourceMetadataKeyEnum, Object> theMap);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,17 +24,21 @@ import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||||
|
|
||||||
public class Tag {
|
public class Tag extends BaseElement implements IElement {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience constant containing the "http://hl7.org/fhir/tag" scheme
|
* Convenience constant containing the "http://hl7.org/fhir/tag" scheme
|
||||||
* value
|
* value
|
||||||
*/
|
*/
|
||||||
public static final String HL7_ORG_FHIR_TAG = "http://hl7.org/fhir/tag";
|
public static final String HL7_ORG_FHIR_TAG = "http://hl7.org/fhir/tag";
|
||||||
|
|
||||||
|
public static final String ATTR_TERM = "term";
|
||||||
|
public static final String ATTR_LABEL = "label";
|
||||||
|
public static final String ATTR_SCHEME = "scheme";
|
||||||
|
|
||||||
private String myLabel;
|
private String myLabel;
|
||||||
private String myScheme;
|
private String myScheme;
|
||||||
private String myTerm;
|
private String myTerm;
|
||||||
|
@ -87,15 +91,6 @@ public class Tag {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SIMPLE_STYLE);
|
|
||||||
b.append("Term", myTerm);
|
|
||||||
b.append("Label", myLabel);
|
|
||||||
b.append("Scheme", myScheme);
|
|
||||||
return b.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLabel() {
|
public String getLabel() {
|
||||||
return myLabel;
|
return myLabel;
|
||||||
}
|
}
|
||||||
|
@ -118,16 +113,24 @@ public class Tag {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLabel(String theLabel) {
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return StringUtils.isBlank(myLabel) && StringUtils.isBlank(myScheme) && StringUtils.isBlank(myTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tag setLabel(String theLabel) {
|
||||||
myLabel = theLabel;
|
myLabel = theLabel;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setScheme(String theScheme) {
|
public Tag setScheme(String theScheme) {
|
||||||
myScheme = theScheme;
|
myScheme = theScheme;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTerm(String theTerm) {
|
public Tag setTerm(String theTerm) {
|
||||||
myTerm = theTerm;
|
myTerm = theTerm;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toHeaderValue() {
|
public String toHeaderValue() {
|
||||||
|
@ -139,7 +142,16 @@ public class Tag {
|
||||||
if (isNotBlank(this.getScheme())) {
|
if (isNotBlank(this.getScheme())) {
|
||||||
b.append("; scheme=\"").append(this.getScheme()).append('"');
|
b.append("; scheme=\"").append(this.getScheme()).append('"');
|
||||||
}
|
}
|
||||||
return b.toString();
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SIMPLE_STYLE);
|
||||||
|
b.append("Term", myTerm);
|
||||||
|
b.append("Label", myLabel);
|
||||||
|
b.append("Scheme", myScheme);
|
||||||
|
return b.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,18 @@ import java.util.ArrayList;
|
||||||
public class TagList extends ArrayList<Tag> {
|
public class TagList extends ArrayList<Tag> {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
public static final String ATTR_CATEGORY = "category";
|
||||||
|
public static final String ELEMENT_NAME = "TagList";
|
||||||
|
public static final String ELEMENT_NAME_LC = ELEMENT_NAME.toLowerCase();
|
||||||
|
|
||||||
public void addTag(String theTerm, String theLabel, String theScheme) {
|
public Tag addTag(String theTerm, String theLabel, String theScheme) {
|
||||||
add(new Tag(theTerm, theLabel, theScheme));
|
Tag retVal = new Tag(theTerm, theLabel, theScheme);
|
||||||
|
add(retVal);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Tag addTag() {
|
||||||
|
return addTag(null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,99 +0,0 @@
|
||||||
package ca.uhn.fhir.model.api;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
public abstract class ValueSetRegistry {
|
|
||||||
|
|
||||||
// private Map<String, Class<? extends Enum>>
|
|
||||||
|
|
||||||
// private static final Map<Class<? extends ValueSetRegistry>, CodeMap> myClassToCodeMap = new HashMap<Class<? extends ValueSetRegistry>, ValueSetRegistry.CodeMap>();
|
|
||||||
//
|
|
||||||
// public static final ValueSetRegistry OTHER = new OtherCode();
|
|
||||||
//
|
|
||||||
// private final String myCode;
|
|
||||||
// private final int myOrdinal;
|
|
||||||
//
|
|
||||||
// public ValueSetRegistry(String theCode, String theValueSetIdentifier) {
|
|
||||||
// myCode = theCode;
|
|
||||||
// myOrdinal = ourNextOrdinal++;
|
|
||||||
//
|
|
||||||
// CodeMap codeMap = myClassToCodeMap.get(getClass());
|
|
||||||
// if (codeMap == null) {
|
|
||||||
// codeMap = new CodeMap(theValueSetIdentifier);
|
|
||||||
// myClassToCodeMap.put(getClass(), codeMap);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// codeMap.addCode(this);
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public ValueSetRegistry() {
|
|
||||||
// // TODO Auto-generated constructor stub
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public ValueSetRegistry getCode(Class<? extends ValueSetRegistry> theType, String theCode) {
|
|
||||||
// CodeMap codeMap = myClassToCodeMap.get(theType);
|
|
||||||
// if (codeMap == null) {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public String getCode() {
|
|
||||||
// return myCode;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public int getOrdinal() {
|
|
||||||
// return myOrdinal;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static final class OtherCode extends ValueSetRegistry {
|
|
||||||
// private OtherCode() {
|
|
||||||
// super();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static final class OtherInstance extends ValueSetRegistry {
|
|
||||||
// private OtherInstance(String theCode) {
|
|
||||||
// super();
|
|
||||||
// myCode = theCode;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static class CodeMap {
|
|
||||||
// private Map<String, ValueSetRegistry> myCodeMap = new HashMap<String, ValueSetRegistry>();
|
|
||||||
// private String myValueSetIdentifier;
|
|
||||||
// private int myNextOrdinal = 0;
|
|
||||||
//
|
|
||||||
// public CodeMap(String theValueSetIdentifier) {
|
|
||||||
// myValueSetIdentifier = theValueSetIdentifier;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public void addCode(ValueSetRegistry theValueSetEnumeration) {
|
|
||||||
// myCodeMap.put(theValueSetEnumeration.getCode(), theValueSetEnumeration);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public int nextOrdinal() {
|
|
||||||
// return myNextOrdinal++;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package ca.uhn.fhir.model.api.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.rest.annotation.AddTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.GetTags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameter annotation for the {@link TagList} parameter in a {@link GetTags},
|
||||||
|
* {@link AddTags}, or {@link DeleteTags} method.
|
||||||
|
*
|
||||||
|
* @see GetTags
|
||||||
|
* @see AddTags
|
||||||
|
* @see DeleteTags
|
||||||
|
*/
|
||||||
|
@Target(value = ElementType.PARAMETER)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface TagListParam {
|
||||||
|
// nothing
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
package ca.uhn.fhir.model.dstu.composite;
|
package ca.uhn.fhir.model.dstu.composite;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR Library
|
* HAPI FHIR Library
|
||||||
|
@ -20,6 +22,7 @@ package ca.uhn.fhir.model.dstu.composite;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@DatatypeDef(name="AgeDt")
|
||||||
public class AgeDt extends QuantityDt {
|
public class AgeDt extends QuantityDt {
|
||||||
|
|
||||||
// TODO: implement restricions
|
// TODO: implement restricions
|
||||||
|
|
|
@ -41,19 +41,16 @@ public class Binary extends BaseResource implements IResource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<IElement> getAllPopulatedChildElements() {
|
public List<IElement> getAllPopulatedChildElements() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
|
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContainedDt getContained() {
|
public ContainedDt getContained() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,35 +31,61 @@ import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||||
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
|
import ca.uhn.fhir.model.dstu.composite.CodeableConceptDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.CodingDt;
|
import ca.uhn.fhir.model.dstu.composite.CodingDt;
|
||||||
|
|
||||||
@DatatypeDef(name = "CodeableConcept", isSpecialization=true)
|
@DatatypeDef(name = "CodeableConcept", isSpecialization = true)
|
||||||
public class BoundCodeableConceptDt<T extends Enum<?>> extends CodeableConceptDt {
|
public class BoundCodeableConceptDt<T extends Enum<?>> extends CodeableConceptDt {
|
||||||
|
|
||||||
private IValueSetEnumBinder<T> myBinder;
|
private IValueSetEnumBinder<T> myBinder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public BoundCodeableConceptDt(IValueSetEnumBinder<T> theBinder) {
|
public BoundCodeableConceptDt(IValueSetEnumBinder<T> theBinder) {
|
||||||
myBinder = theBinder;
|
myBinder = theBinder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public BoundCodeableConceptDt(IValueSetEnumBinder<T> theBinder, T theValue) {
|
public BoundCodeableConceptDt(IValueSetEnumBinder<T> theBinder, T theValue) {
|
||||||
myBinder = theBinder;
|
myBinder = theBinder;
|
||||||
setValueAsEnum(theValue);
|
setValueAsEnum(theValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public BoundCodeableConceptDt(IValueSetEnumBinder<T> theBinder, Collection<T> theValues) {
|
public BoundCodeableConceptDt(IValueSetEnumBinder<T> theBinder, Collection<T> theValues) {
|
||||||
myBinder = theBinder;
|
myBinder = theBinder;
|
||||||
setValueAsEnum(theValues);
|
setValueAsEnum(theValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link #getCoding()} to contain a coding with the code and
|
||||||
|
* system defined by the given enumerated types, AND clearing any existing
|
||||||
|
* codings first. If theValue is null, existing codings are cleared and no
|
||||||
|
* codings are added.
|
||||||
|
*
|
||||||
|
* @param theValue
|
||||||
|
* The value to add, or <code>null</code>
|
||||||
|
*/
|
||||||
public void setValueAsEnum(Collection<T> theValues) {
|
public void setValueAsEnum(Collection<T> theValues) {
|
||||||
getCoding().clear();
|
getCoding().clear();
|
||||||
if (theValues == null) {
|
if (theValues != null) {
|
||||||
return;
|
for (T next : theValues) {
|
||||||
}
|
getCoding().add(new CodingDt(myBinder.toSystemString(next), myBinder.toCodeString(next)));
|
||||||
for (T next : theValues) {
|
}
|
||||||
getCoding().add(new CodingDt(myBinder.toSystemString(next), myBinder.toCodeString(next)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the {@link #getCoding()} to contain a coding with the code and
|
||||||
|
* system defined by the given enumerated type, AND clearing any existing
|
||||||
|
* codings first. If theValue is null, existing codings are cleared and no
|
||||||
|
* codings are added.
|
||||||
|
*
|
||||||
|
* @param theValue
|
||||||
|
* The value to add, or <code>null</code>
|
||||||
|
*/
|
||||||
public void setValueAsEnum(T theValue) {
|
public void setValueAsEnum(T theValue) {
|
||||||
getCoding().clear();
|
getCoding().clear();
|
||||||
if (theValue == null) {
|
if (theValue == null) {
|
||||||
|
@ -68,6 +94,17 @@ public class BoundCodeableConceptDt<T extends Enum<?>> extends CodeableConceptDt
|
||||||
getCoding().add(new CodingDt(myBinder.toSystemString(theValue), myBinder.toCodeString(theValue)));
|
getCoding().add(new CodingDt(myBinder.toSystemString(theValue), myBinder.toCodeString(theValue)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loops through the {@link #getCoding() codings} in this codeable concept
|
||||||
|
* and returns the first bound enumerated type that matches. <b>Use
|
||||||
|
* caution</b> using this method, see the return description for more
|
||||||
|
* information.
|
||||||
|
*
|
||||||
|
* @return Returns the bound enumerated type, or <code>null</code> if none
|
||||||
|
* are found. Note that a null return value doesn't neccesarily
|
||||||
|
* imply that this Codeable Concept has no codes, only that it has
|
||||||
|
* no codes that match the enum.
|
||||||
|
*/
|
||||||
public Set<T> getValueAsEnum() {
|
public Set<T> getValueAsEnum() {
|
||||||
Set<T> retVal = new HashSet<T>();
|
Set<T> retVal = new HashSet<T>();
|
||||||
for (CodingDt next : getCoding()) {
|
for (CodingDt next : getCoding()) {
|
||||||
|
|
|
@ -37,6 +37,7 @@ import ca.uhn.fhir.context.RuntimeChildChoiceDefinition;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.IElement;
|
import ca.uhn.fhir.model.api.IElement;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
|
||||||
|
@ -44,6 +45,11 @@ public abstract class BaseParser implements IParser {
|
||||||
|
|
||||||
private boolean mySuppressNarratives;
|
private boolean mySuppressNarratives;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagList parseTagList(String theString) {
|
||||||
|
return parseTagList(new StringReader(theString));
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("cast")
|
@SuppressWarnings("cast")
|
||||||
@Override
|
@Override
|
||||||
public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
|
public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
|
||||||
|
@ -68,6 +74,17 @@ public abstract class BaseParser implements IParser {
|
||||||
return stringWriter.toString();
|
return stringWriter.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String encodeTagListToString(TagList theTagList) {
|
||||||
|
Writer stringWriter = new StringWriter();
|
||||||
|
try {
|
||||||
|
encodeTagListToWriter(theTagList, stringWriter);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new Error("Encountered IOException during write to string - This should not happen!");
|
||||||
|
}
|
||||||
|
return stringWriter.toString();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String encodeBundleToString(Bundle theBundle) throws DataFormatException {
|
public String encodeBundleToString(Bundle theBundle) throws DataFormatException {
|
||||||
if (theBundle == null) {
|
if (theBundle == null) {
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.io.Writer;
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
|
||||||
public interface IParser {
|
public interface IParser {
|
||||||
|
|
||||||
|
@ -36,8 +37,29 @@ public interface IParser {
|
||||||
|
|
||||||
String encodeResourceToString(IResource theResource) throws DataFormatException;
|
String encodeResourceToString(IResource theResource) throws DataFormatException;
|
||||||
|
|
||||||
void encodeResourceToWriter(IResource theResource, Writer stringWriter) throws IOException, DataFormatException;
|
void encodeResourceToWriter(IResource theResource, Writer theWriter) throws IOException, DataFormatException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a tag list, as defined in the <a
|
||||||
|
* href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR
|
||||||
|
* Specification</a>.
|
||||||
|
*
|
||||||
|
* @param theTagList The tag list to encode. Must not be null.
|
||||||
|
* @return An encoded tag list
|
||||||
|
*/
|
||||||
|
String encodeTagListToString(TagList theTagList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a tag list, as defined in the <a
|
||||||
|
* href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR
|
||||||
|
* Specification</a>.
|
||||||
|
*
|
||||||
|
* @param theTagList The tag list to encode. Must not be null.
|
||||||
|
* @param theWriter The writer to encode to
|
||||||
|
*/
|
||||||
|
void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException;
|
||||||
|
|
||||||
|
|
||||||
<T extends IResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader);
|
<T extends IResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader);
|
||||||
|
|
||||||
Bundle parseBundle(Reader theReader);
|
Bundle parseBundle(Reader theReader);
|
||||||
|
@ -80,6 +102,28 @@ public interface IParser {
|
||||||
|
|
||||||
IResource parseResource(String theMessageString) throws ConfigurationException, DataFormatException;
|
IResource parseResource(String theMessageString) throws ConfigurationException, DataFormatException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a tag list, as defined in the <a
|
||||||
|
* href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR
|
||||||
|
* Specification</a>.
|
||||||
|
*
|
||||||
|
* @param theReader
|
||||||
|
* A reader which will supply a tag list
|
||||||
|
* @return A parsed tag list
|
||||||
|
*/
|
||||||
|
TagList parseTagList(Reader theReader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses a tag list, as defined in the <a
|
||||||
|
* href="http://hl7.org/implement/standards/fhir/http.html#tags">FHIR
|
||||||
|
* Specification</a>.
|
||||||
|
*
|
||||||
|
* @param theString
|
||||||
|
* A string containing a tag list
|
||||||
|
* @return A parsed tag list
|
||||||
|
*/
|
||||||
|
TagList parseTagList(String theString);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the "pretty print" flag, meaning that the parser will encode
|
* Sets the "pretty print" flag, meaning that the parser will encode
|
||||||
* resources with human-readable spacing and newlines between elements
|
* resources with human-readable spacing and newlines between elements
|
||||||
|
|
|
@ -45,10 +45,13 @@ import javax.json.JsonValue;
|
||||||
import javax.json.JsonValue.ValueType;
|
import javax.json.JsonValue.ValueType;
|
||||||
import javax.json.stream.JsonGenerator;
|
import javax.json.stream.JsonGenerator;
|
||||||
import javax.json.stream.JsonGeneratorFactory;
|
import javax.json.stream.JsonGeneratorFactory;
|
||||||
|
import javax.xml.stream.XMLStreamException;
|
||||||
|
import javax.xml.stream.XMLStreamWriter;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
|
||||||
|
import ch.qos.logback.core.boolex.EventEvaluator;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||||
|
@ -61,7 +64,7 @@ import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.model.api.BaseBundle;
|
import ca.uhn.fhir.model.api.BaseBundle;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.BundleCategory;
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
import ca.uhn.fhir.model.api.BundleEntry;
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||||
import ca.uhn.fhir.model.api.IElement;
|
import ca.uhn.fhir.model.api.IElement;
|
||||||
|
@ -69,6 +72,7 @@ import ca.uhn.fhir.model.api.IIdentifiableElement;
|
||||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.api.annotation.Child;
|
import ca.uhn.fhir.model.api.annotation.Child;
|
||||||
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
|
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
||||||
|
@ -101,6 +105,195 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
myContext = theContext;
|
myContext = theContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException {
|
||||||
|
JsonGenerator eventWriter = createJsonGenerator(theWriter);
|
||||||
|
eventWriter.writeStartObject();
|
||||||
|
|
||||||
|
eventWriter.write("resourceType", "Bundle");
|
||||||
|
|
||||||
|
writeTagWithTextNode(eventWriter, "title", theBundle.getTitle());
|
||||||
|
writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId());
|
||||||
|
writeOptionalTagWithTextNode(eventWriter, "updated", theBundle.getUpdated());
|
||||||
|
writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished());
|
||||||
|
|
||||||
|
eventWriter.writeStartArray("link");
|
||||||
|
writeAtomLink(eventWriter, "self", theBundle.getLinkSelf());
|
||||||
|
writeAtomLink(eventWriter, "first", theBundle.getLinkFirst());
|
||||||
|
writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious());
|
||||||
|
writeAtomLink(eventWriter, "next", theBundle.getLinkNext());
|
||||||
|
writeAtomLink(eventWriter, "last", theBundle.getLinkLast());
|
||||||
|
writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase());
|
||||||
|
eventWriter.writeEnd();
|
||||||
|
|
||||||
|
writeOptionalTagWithTextNode(eventWriter, "totalResults", theBundle.getTotalResults());
|
||||||
|
|
||||||
|
writeAuthor(theBundle, eventWriter);
|
||||||
|
|
||||||
|
eventWriter.writeStartArray("entry");
|
||||||
|
for (BundleEntry nextEntry : theBundle.getEntries()) {
|
||||||
|
eventWriter.writeStartObject();
|
||||||
|
|
||||||
|
writeTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
|
||||||
|
writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
|
||||||
|
|
||||||
|
eventWriter.writeStartArray("link");
|
||||||
|
writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf());
|
||||||
|
eventWriter.writeEnd();
|
||||||
|
|
||||||
|
writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated());
|
||||||
|
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
|
||||||
|
|
||||||
|
if (nextEntry.getCategories() != null) {
|
||||||
|
eventWriter.writeStartArray("category");
|
||||||
|
for (Tag next : nextEntry.getCategories()) {
|
||||||
|
eventWriter.writeStartObject();
|
||||||
|
eventWriter.write("term", defaultString(next.getTerm()));
|
||||||
|
eventWriter.write("label", defaultString(next.getLabel()));
|
||||||
|
eventWriter.write("scheme", defaultString(next.getScheme()));
|
||||||
|
eventWriter.writeEnd();
|
||||||
|
}
|
||||||
|
eventWriter.writeEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
writeAuthor(nextEntry, eventWriter);
|
||||||
|
|
||||||
|
IResource resource = nextEntry.getResource();
|
||||||
|
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
|
||||||
|
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content");
|
||||||
|
|
||||||
|
eventWriter.writeEnd(); // entry object
|
||||||
|
}
|
||||||
|
eventWriter.writeEnd(); // entry array
|
||||||
|
|
||||||
|
eventWriter.writeEnd();
|
||||||
|
eventWriter.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encodeResourceToWriter(IResource theResource, Writer theWriter) throws IOException {
|
||||||
|
Validate.notNull(theResource, "Resource can not be null");
|
||||||
|
|
||||||
|
JsonGenerator eventWriter = createJsonGenerator(theWriter);
|
||||||
|
|
||||||
|
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
|
||||||
|
encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null);
|
||||||
|
eventWriter.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException {
|
||||||
|
JsonGenerator eventWriter = createJsonGenerator(theWriter);
|
||||||
|
|
||||||
|
eventWriter.writeStartObject();
|
||||||
|
|
||||||
|
eventWriter.write("resourceType", TagList.ELEMENT_NAME);
|
||||||
|
|
||||||
|
eventWriter.writeStartArray(TagList.ATTR_CATEGORY);
|
||||||
|
for (Tag next : theTagList) {
|
||||||
|
eventWriter.writeStartObject();
|
||||||
|
|
||||||
|
if (isNotBlank(next.getTerm())) {
|
||||||
|
eventWriter.write(Tag.ATTR_TERM, next.getTerm());
|
||||||
|
}
|
||||||
|
if (isNotBlank(next.getLabel())) {
|
||||||
|
eventWriter.write(Tag.ATTR_LABEL, next.getLabel());
|
||||||
|
}
|
||||||
|
if (isNotBlank(next.getScheme())) {
|
||||||
|
eventWriter.write(Tag.ATTR_SCHEME, next.getScheme());
|
||||||
|
}
|
||||||
|
|
||||||
|
eventWriter.writeEnd();
|
||||||
|
}
|
||||||
|
eventWriter.writeEnd();
|
||||||
|
|
||||||
|
eventWriter.writeEnd();
|
||||||
|
eventWriter.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends IResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) {
|
||||||
|
JsonReader reader = Json.createReader(theReader);
|
||||||
|
JsonObject object = reader.readObject();
|
||||||
|
|
||||||
|
JsonValue resourceTypeObj = object.get("resourceType");
|
||||||
|
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
|
||||||
|
String resourceType = ((JsonString) resourceTypeObj).getString();
|
||||||
|
if (!"Bundle".equals(resourceType)) {
|
||||||
|
throw new DataFormatException("Trying to parse bundle but found resourceType other than 'Bundle'. Found: '" + resourceType + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserState<Bundle> state = ParserState.getPreAtomInstance(myContext, theResourceType, true);
|
||||||
|
state.enteringNewElement(null, "feed");
|
||||||
|
|
||||||
|
parseBundleChildren(object, state);
|
||||||
|
|
||||||
|
state.endingElement();
|
||||||
|
|
||||||
|
Bundle retVal = state.getObject();
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader) {
|
||||||
|
JsonReader reader = Json.createReader(theReader);
|
||||||
|
JsonObject object = reader.readObject();
|
||||||
|
|
||||||
|
JsonValue resourceTypeObj = object.get("resourceType");
|
||||||
|
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
|
||||||
|
String resourceType = ((JsonString) resourceTypeObj).getString();
|
||||||
|
|
||||||
|
RuntimeResourceDefinition def;
|
||||||
|
if (theResourceType != null) {
|
||||||
|
def = myContext.getResourceDefinition(theResourceType);
|
||||||
|
} else {
|
||||||
|
def = myContext.getResourceDefinition(resourceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParserState<? extends IResource> state = ParserState.getPreResourceInstance(def.getImplementingClass(), myContext, true);
|
||||||
|
state.enteringNewElement(null, def.getName());
|
||||||
|
|
||||||
|
parseChildren(object, state);
|
||||||
|
|
||||||
|
state.endingElement();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T retVal = (T) state.getObject();
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
|
||||||
|
return parseResource(theResourceType, new StringReader(theMessageString));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagList parseTagList(Reader theReader) {
|
||||||
|
JsonReader reader = Json.createReader(theReader);
|
||||||
|
JsonObject object = reader.readObject();
|
||||||
|
|
||||||
|
JsonValue resourceTypeObj = object.get("resourceType");
|
||||||
|
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
|
||||||
|
String resourceType = ((JsonString) resourceTypeObj).getString();
|
||||||
|
|
||||||
|
ParserState<TagList> state = ParserState.getPreTagListInstance(myContext, true);
|
||||||
|
state.enteringNewElement(null, resourceType);
|
||||||
|
|
||||||
|
parseChildren(object, state);
|
||||||
|
|
||||||
|
state.endingElement();
|
||||||
|
|
||||||
|
return state.getObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IParser setPrettyPrint(boolean thePrettyPrint) {
|
||||||
|
myPrettyPrint = thePrettyPrint;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private void addToHeldExtensions(int valueIdx, ArrayList<ArrayList<HeldExtension>> list, RuntimeChildDeclaredExtensionDefinition theDef, IElement theValue) {
|
private void addToHeldExtensions(int valueIdx, ArrayList<ArrayList<HeldExtension>> list, RuntimeChildDeclaredExtensionDefinition theDef, IElement theValue) {
|
||||||
list.ensureCapacity(valueIdx);
|
list.ensureCapacity(valueIdx);
|
||||||
while (list.size() <= valueIdx) {
|
while (list.size() <= valueIdx) {
|
||||||
|
@ -143,73 +336,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
return eventWriter;
|
return eventWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName) throws IOException {
|
||||||
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws IOException {
|
|
||||||
JsonGenerator eventWriter = createJsonGenerator(theWriter);
|
|
||||||
eventWriter.writeStartObject();
|
|
||||||
|
|
||||||
eventWriter.write("resourceType", "Bundle");
|
|
||||||
|
|
||||||
writeTagWithTextNode(eventWriter, "title", theBundle.getTitle());
|
|
||||||
writeTagWithTextNode(eventWriter, "id", theBundle.getBundleId());
|
|
||||||
writeOptionalTagWithTextNode(eventWriter, "updated", theBundle.getUpdated());
|
|
||||||
writeOptionalTagWithTextNode(eventWriter, "published", theBundle.getPublished());
|
|
||||||
|
|
||||||
eventWriter.writeStartArray("link");
|
|
||||||
writeAtomLink(eventWriter, "self", theBundle.getLinkSelf());
|
|
||||||
writeAtomLink(eventWriter, "first", theBundle.getLinkFirst());
|
|
||||||
writeAtomLink(eventWriter, "previous", theBundle.getLinkPrevious());
|
|
||||||
writeAtomLink(eventWriter, "next", theBundle.getLinkNext());
|
|
||||||
writeAtomLink(eventWriter, "last", theBundle.getLinkLast());
|
|
||||||
writeAtomLink(eventWriter, "fhir-base", theBundle.getLinkBase());
|
|
||||||
eventWriter.writeEnd();
|
|
||||||
|
|
||||||
writeOptionalTagWithTextNode(eventWriter, "totalResults", theBundle.getTotalResults());
|
|
||||||
|
|
||||||
writeAuthor(theBundle, eventWriter);
|
|
||||||
|
|
||||||
eventWriter.writeStartArray("entry");
|
|
||||||
for (BundleEntry nextEntry : theBundle.getEntries()) {
|
|
||||||
eventWriter.writeStartObject();
|
|
||||||
|
|
||||||
writeTagWithTextNode(eventWriter, "title", nextEntry.getTitle());
|
|
||||||
writeTagWithTextNode(eventWriter, "id", nextEntry.getId());
|
|
||||||
|
|
||||||
eventWriter.writeStartArray("link");
|
|
||||||
writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf());
|
|
||||||
eventWriter.writeEnd();
|
|
||||||
|
|
||||||
writeOptionalTagWithTextNode(eventWriter, "updated", nextEntry.getUpdated());
|
|
||||||
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
|
|
||||||
|
|
||||||
if (nextEntry.getCategories() != null) {
|
|
||||||
eventWriter.writeStartArray("category");
|
|
||||||
for (BundleCategory next : nextEntry.getCategories()) {
|
|
||||||
eventWriter.writeStartObject();
|
|
||||||
eventWriter.write("term", defaultString(next.getTerm()));
|
|
||||||
eventWriter.write("label", defaultString(next.getLabel()));
|
|
||||||
eventWriter.write("scheme", defaultString(next.getScheme()));
|
|
||||||
eventWriter.writeEnd();
|
|
||||||
}
|
|
||||||
eventWriter.writeEnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
writeAuthor(nextEntry, eventWriter);
|
|
||||||
|
|
||||||
IResource resource = nextEntry.getResource();
|
|
||||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
|
|
||||||
encodeResourceToJsonStreamWriter(resDef, resource, eventWriter, "content");
|
|
||||||
|
|
||||||
eventWriter.writeEnd(); // entry object
|
|
||||||
}
|
|
||||||
eventWriter.writeEnd(); // entry array
|
|
||||||
|
|
||||||
eventWriter.writeEnd();
|
|
||||||
eventWriter.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef,
|
|
||||||
String theChildName) throws IOException {
|
|
||||||
|
|
||||||
switch (theChildDef.getChildType()) {
|
switch (theChildDef.getChildType()) {
|
||||||
case PRIMITIVE_DATATYPE: {
|
case PRIMITIVE_DATATYPE: {
|
||||||
|
@ -311,8 +438,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
|
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren) throws IOException {
|
||||||
List<? extends BaseRuntimeChildDefinition> theChildren) throws IOException {
|
|
||||||
for (BaseRuntimeChildDefinition nextChild : theChildren) {
|
for (BaseRuntimeChildDefinition nextChild : theChildren) {
|
||||||
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
||||||
INarrativeGenerator gen = myContext.getNarrativeGenerator();
|
INarrativeGenerator gen = myContext.getNarrativeGenerator();
|
||||||
|
@ -395,7 +521,8 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extensions.size() > 0 || modifierExtensions.size() > 0) {
|
if (extensions.size() > 0 || modifierExtensions.size() > 0) {
|
||||||
// Ignore extensions if we're encoding a resource, since they are handled one level up
|
// Ignore extensions if we're encoding a resource, since they
|
||||||
|
// are handled one level up
|
||||||
if (currentChildName != null) {
|
if (currentChildName != null) {
|
||||||
theEventWriter.writeStartArray('_' + currentChildName);
|
theEventWriter.writeStartArray('_' + currentChildName);
|
||||||
|
|
||||||
|
@ -443,8 +570,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter,
|
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef) throws IOException, DataFormatException {
|
||||||
BaseRuntimeElementCompositeDefinition<?> resDef) throws IOException, DataFormatException {
|
|
||||||
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions());
|
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions());
|
||||||
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren());
|
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren());
|
||||||
}
|
}
|
||||||
|
@ -472,23 +598,12 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
theEventWriter.writeEnd();
|
theEventWriter.writeEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void encodeResourceToWriter(IResource theResource, Writer theWriter) throws IOException {
|
|
||||||
Validate.notNull(theResource, "Resource can not be null");
|
|
||||||
|
|
||||||
JsonGenerator eventWriter = createJsonGenerator(theWriter);
|
|
||||||
|
|
||||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
|
|
||||||
encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null);
|
|
||||||
eventWriter.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is useful only for the two cases where extensions are encoded as direct children (e.g. not in some object called _name): resource extensions, and extension extensions
|
* This is useful only for the two cases where extensions are encoded as
|
||||||
|
* direct children (e.g. not in some object called _name): resource
|
||||||
|
* extensions, and extension extensions
|
||||||
*/
|
*/
|
||||||
private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef,
|
private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IResource theResource) throws IOException {
|
||||||
IResource theResource) throws IOException {
|
|
||||||
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
|
List<HeldExtension> extensions = new ArrayList<HeldExtension>(0);
|
||||||
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
|
List<HeldExtension> modifierExtensions = new ArrayList<HeldExtension>(0);
|
||||||
|
|
||||||
|
@ -563,31 +678,6 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends IResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) {
|
|
||||||
JsonReader reader = Json.createReader(theReader);
|
|
||||||
JsonObject object = reader.readObject();
|
|
||||||
|
|
||||||
JsonValue resourceTypeObj = object.get("resourceType");
|
|
||||||
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
|
|
||||||
String resourceType = ((JsonString) resourceTypeObj).getString();
|
|
||||||
if (!"Bundle".equals(resourceType)) {
|
|
||||||
throw new DataFormatException("Trying to parse bundle but found resourceType other than 'Bundle'. Found: '" + resourceType + "'");
|
|
||||||
}
|
|
||||||
|
|
||||||
ParserState<Bundle> state = ParserState.getPreAtomInstance(myContext, theResourceType, true);
|
|
||||||
state.enteringNewElement(null, "feed");
|
|
||||||
|
|
||||||
parseBundleChildren(object, state);
|
|
||||||
|
|
||||||
state.endingElement();
|
|
||||||
|
|
||||||
Bundle retVal = state.getObject();
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void parseBundleChildren(JsonObject theObject, ParserState<?> theState) {
|
private void parseBundleChildren(JsonObject theObject, ParserState<?> theState) {
|
||||||
for (String nextName : theObject.keySet()) {
|
for (String nextName : theObject.keySet()) {
|
||||||
if ("resourceType".equals(nextName)) {
|
if ("resourceType".equals(nextName)) {
|
||||||
|
@ -653,7 +743,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elementId != null) {
|
if (elementId != null) {
|
||||||
IElement object = theState.getObject();
|
IElement object = (IElement) theState.getObject();
|
||||||
if (object instanceof IIdentifiableElement) {
|
if (object instanceof IIdentifiableElement) {
|
||||||
((IIdentifiableElement) object).setId(new IdDt(elementId));
|
((IIdentifiableElement) object).setId(new IdDt(elementId));
|
||||||
}
|
}
|
||||||
|
@ -717,7 +807,6 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseExtension(ParserState<?> theState, JsonArray array, boolean theIsModifier) {
|
private void parseExtension(ParserState<?> theState, JsonArray array, boolean theIsModifier) {
|
||||||
// TODO: use theIsModifier
|
|
||||||
for (int i = 0; i < array.size(); i++) {
|
for (int i = 0; i < array.size(); i++) {
|
||||||
JsonObject nextExtObj = array.getJsonObject(i);
|
JsonObject nextExtObj = array.getJsonObject(i);
|
||||||
String url = nextExtObj.getString("url");
|
String url = nextExtObj.getString("url");
|
||||||
|
@ -741,46 +830,6 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader) {
|
|
||||||
JsonReader reader = Json.createReader(theReader);
|
|
||||||
JsonObject object = reader.readObject();
|
|
||||||
|
|
||||||
JsonValue resourceTypeObj = object.get("resourceType");
|
|
||||||
assertObjectOfType(resourceTypeObj, JsonValue.ValueType.STRING, "resourceType");
|
|
||||||
String resourceType = ((JsonString) resourceTypeObj).getString();
|
|
||||||
|
|
||||||
RuntimeResourceDefinition def;
|
|
||||||
if (theResourceType != null) {
|
|
||||||
def = myContext.getResourceDefinition(theResourceType);
|
|
||||||
} else {
|
|
||||||
def = myContext.getResourceDefinition(resourceType);
|
|
||||||
}
|
|
||||||
|
|
||||||
ParserState<? extends IResource> state = ParserState.getPreResourceInstance(def.getImplementingClass(), myContext, true);
|
|
||||||
state.enteringNewElement(null, def.getName());
|
|
||||||
|
|
||||||
parseChildren(object, state);
|
|
||||||
|
|
||||||
state.endingElement();
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
T retVal = (T) state.getObject();
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
|
|
||||||
return parseResource(theResourceType, new StringReader(theMessageString));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IParser setPrettyPrint(boolean thePrettyPrint) {
|
|
||||||
myPrettyPrint = thePrettyPrint;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink) {
|
private void writeAtomLink(JsonGenerator theEventWriter, String theRel, StringDt theLink) {
|
||||||
if (isNotBlank(theLink.getValue())) {
|
if (isNotBlank(theLink.getValue())) {
|
||||||
theEventWriter.writeStartObject();
|
theEventWriter.writeStartObject();
|
||||||
|
@ -801,8 +850,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions,
|
private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) throws IOException {
|
||||||
List<HeldExtension> modifierExtensions) throws IOException {
|
|
||||||
if (extensions.isEmpty() == false) {
|
if (extensions.isEmpty() == false) {
|
||||||
theEventWriter.writeStartArray("extension");
|
theEventWriter.writeStartArray("extension");
|
||||||
for (HeldExtension next : extensions) {
|
for (HeldExtension next : extensions) {
|
||||||
|
@ -871,7 +919,9 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) {
|
if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) {
|
||||||
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource);
|
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource);
|
||||||
} else {
|
} else {
|
||||||
// encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, "value" + WordUtils.capitalize(def.getName()));
|
// encodeChildElementToStreamWriter(theResDef, theResource,
|
||||||
|
// theEventWriter, myValue, def, "value" +
|
||||||
|
// WordUtils.capitalize(def.getName()));
|
||||||
String childName = myDef.getChildNameByDatatype(myValue.getClass());
|
String childName = myDef.getChildNameByDatatype(myValue.getClass());
|
||||||
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName);
|
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, myValue, def, childName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceReferenceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceReferenceDefinition;
|
||||||
import ca.uhn.fhir.model.api.BaseBundle;
|
import ca.uhn.fhir.model.api.BaseBundle;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.BundleCategory;
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
import ca.uhn.fhir.model.api.BundleEntry;
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||||
import ca.uhn.fhir.model.api.ICompositeDatatype;
|
import ca.uhn.fhir.model.api.ICompositeDatatype;
|
||||||
|
@ -57,13 +57,14 @@ import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.IResourceBlock;
|
import ca.uhn.fhir.model.api.IResourceBlock;
|
||||||
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
||||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
|
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.XhtmlDt;
|
import ca.uhn.fhir.model.primitive.XhtmlDt;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
|
||||||
class ParserState<T extends IElement> {
|
class ParserState<T> {
|
||||||
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ParserState.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ParserState.class);
|
||||||
private FhirContext myContext;
|
private FhirContext myContext;
|
||||||
|
@ -151,6 +152,12 @@ class ParserState<T extends IElement> {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ParserState<TagList> getPreTagListInstance(FhirContext theContext, boolean theJsonMode) {
|
||||||
|
ParserState<TagList> retVal = new ParserState<TagList>(theContext, theJsonMode);
|
||||||
|
retVal.push(retVal.new PreTagListState());
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
public class AtomAuthorState extends BaseState {
|
public class AtomAuthorState extends BaseState {
|
||||||
|
|
||||||
private BaseBundle myInstance;
|
private BaseBundle myInstance;
|
||||||
|
@ -189,9 +196,9 @@ class ParserState<T extends IElement> {
|
||||||
|
|
||||||
private int myCatState = STATE_NONE;
|
private int myCatState = STATE_NONE;
|
||||||
|
|
||||||
private BundleCategory myInstance;
|
private Tag myInstance;
|
||||||
|
|
||||||
public AtomCategoryState(BundleCategory theEntry) {
|
public AtomCategoryState(Tag theEntry) {
|
||||||
super(null);
|
super(null);
|
||||||
myInstance = theEntry;
|
myInstance = theEntry;
|
||||||
}
|
}
|
||||||
|
@ -205,7 +212,10 @@ class ParserState<T extends IElement> {
|
||||||
} else if ("scheme".equals(theName)) {
|
} else if ("scheme".equals(theName)) {
|
||||||
myInstance.setScheme(theValue);
|
myInstance.setScheme(theValue);
|
||||||
} else if ("value".equals(theName)) {
|
} else if ("value".equals(theName)) {
|
||||||
// This is for the JSON parsing, which is weird for Categories..
|
/*
|
||||||
|
* This handles XML parsing, which is odd for this quasi-resource type,
|
||||||
|
* since the tag has three values instead of one like everything else.
|
||||||
|
*/
|
||||||
switch (myCatState) {
|
switch (myCatState) {
|
||||||
case STATE_LABEL:
|
case STATE_LABEL:
|
||||||
myInstance.setLabel(theValue);
|
myInstance.setLabel(theValue);
|
||||||
|
@ -306,12 +316,39 @@ class ParserState<T extends IElement> {
|
||||||
if (myEntry.getUpdated().isEmpty() == false) {
|
if (myEntry.getUpdated().isEmpty() == false) {
|
||||||
metadata.put(ResourceMetadataKeyEnum.UPDATED, myEntry.getUpdated());
|
metadata.put(ResourceMetadataKeyEnum.UPDATED, myEntry.getUpdated());
|
||||||
}
|
}
|
||||||
|
if (myEntry.getCategories().isEmpty() == false) {
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
for (Tag next : myEntry.getCategories()) {
|
||||||
|
tagList.add(next);
|
||||||
|
}
|
||||||
|
metadata.put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
|
||||||
|
}
|
||||||
if (!myEntry.getLinkSelf().isEmpty()) {
|
if (!myEntry.getLinkSelf().isEmpty()) {
|
||||||
String subStr = "/" + Constants.PARAM_HISTORY + "/";
|
|
||||||
String linkSelfValue = myEntry.getLinkSelf().getValue();
|
String linkSelfValue = myEntry.getLinkSelf().getValue();
|
||||||
int startIndex = linkSelfValue.indexOf(subStr);
|
/*
|
||||||
|
* Find resource ID if it is there
|
||||||
|
*/
|
||||||
|
String resNameLc = myContext.getResourceDefinition(myEntry.getResource()).getName().toLowerCase();
|
||||||
|
String subStrId = "/" + resNameLc + "/";
|
||||||
|
int idIdx = linkSelfValue.toLowerCase().lastIndexOf(subStrId);
|
||||||
|
if (idIdx != -1) {
|
||||||
|
int endIndex = linkSelfValue.indexOf('/', idIdx + subStrId.length());
|
||||||
|
String id;
|
||||||
|
if (endIndex == -1) {
|
||||||
|
id = linkSelfValue.substring(idIdx + subStrId.length());
|
||||||
|
} else {
|
||||||
|
id = linkSelfValue.substring(idIdx + subStrId.length(), endIndex);
|
||||||
|
}
|
||||||
|
myEntry.getResource().setId(new IdDt(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find resource version ID if it is there
|
||||||
|
*/
|
||||||
|
String subStrVid = "/" + Constants.PARAM_HISTORY + "/";
|
||||||
|
int startIndex = linkSelfValue.indexOf(subStrVid);
|
||||||
if (startIndex > 0) {
|
if (startIndex > 0) {
|
||||||
startIndex = startIndex + subStr.length();
|
startIndex = startIndex + subStrVid.length();
|
||||||
int endIndex = linkSelfValue.indexOf('?', startIndex);
|
int endIndex = linkSelfValue.indexOf('?', startIndex);
|
||||||
if (endIndex == -1) {
|
if (endIndex == -1) {
|
||||||
endIndex = linkSelfValue.length();
|
endIndex = linkSelfValue.length();
|
||||||
|
@ -324,7 +361,7 @@ class ParserState<T extends IElement> {
|
||||||
ourLog.warn("Bundle entry link-self contains path information beyond version (this will be ignored): {}", versionId);
|
ourLog.warn("Bundle entry link-self contains path information beyond version (this will be ignored): {}", versionId);
|
||||||
versionId = versionId.substring(0, idx);
|
versionId = versionId.substring(0, idx);
|
||||||
}
|
}
|
||||||
metadata.put(ResourceMetadataKeyEnum.VERSION_ID, versionId);
|
metadata.put(ResourceMetadataKeyEnum.VERSION_ID, new IdDt(versionId));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -548,7 +585,7 @@ class ParserState<T extends IElement> {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
protected IElement getCurrentElement() {
|
protected Object getCurrentElement() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -993,6 +1030,47 @@ class ParserState<T extends IElement> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class PreTagListState extends BaseState {
|
||||||
|
|
||||||
|
private TagList myTagList;
|
||||||
|
|
||||||
|
public PreTagListState() {
|
||||||
|
super(null);
|
||||||
|
myTagList = new TagList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endingElement() throws DataFormatException {
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
|
||||||
|
if (!TagList.ELEMENT_NAME_LC.equals(theLocalPart.toLowerCase())) {
|
||||||
|
throw new DataFormatException("resourceType does not appear to be 'TagList', found: " + theLocalPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
push(new TagListState(myTagList));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPreResource() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public void wereBack() {
|
||||||
|
myObject = (T) myTagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TagList getCurrentElement() {
|
||||||
|
return myTagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private class PrimitiveState extends BaseState {
|
private class PrimitiveState extends BaseState {
|
||||||
private IPrimitiveDatatype<?> myInstance;
|
private IPrimitiveDatatype<?> myInstance;
|
||||||
|
|
||||||
|
@ -1137,6 +1215,93 @@ class ParserState<T extends IElement> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TagListState extends BaseState {
|
||||||
|
|
||||||
|
private TagList myTagList;
|
||||||
|
|
||||||
|
public TagListState(TagList theTagList) {
|
||||||
|
super(null);
|
||||||
|
myTagList = theTagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endingElement() throws DataFormatException {
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
|
||||||
|
if (TagList.ATTR_CATEGORY.equals(theLocalPart)) {
|
||||||
|
push(new TagState(myTagList.addTag()));
|
||||||
|
} else {
|
||||||
|
throw new DataFormatException("Unexpected element: " + theLocalPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TagState extends BaseState {
|
||||||
|
|
||||||
|
private static final int LABEL = 2;
|
||||||
|
private static final int NONE = 0;
|
||||||
|
|
||||||
|
private static final int SCHEME = 3;
|
||||||
|
private static final int TERM = 1;
|
||||||
|
private int mySubState = 0;
|
||||||
|
private Tag myTag;
|
||||||
|
|
||||||
|
public TagState(Tag theTag) {
|
||||||
|
super(null);
|
||||||
|
myTag = theTag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attributeValue(String theName, String theValue) throws DataFormatException {
|
||||||
|
String value = defaultIfBlank(theValue, null);
|
||||||
|
|
||||||
|
switch (mySubState) {
|
||||||
|
case TERM:
|
||||||
|
myTag.setTerm(value);
|
||||||
|
break;
|
||||||
|
case LABEL:
|
||||||
|
myTag.setLabel(value);
|
||||||
|
break;
|
||||||
|
case SCHEME:
|
||||||
|
myTag.setScheme(value);
|
||||||
|
break;
|
||||||
|
case NONE:
|
||||||
|
// This handles JSON encoding, which is a bit weird
|
||||||
|
enteringNewElement(null, theName);
|
||||||
|
attributeValue(null, value);
|
||||||
|
endingElement();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endingElement() throws DataFormatException {
|
||||||
|
if (mySubState != NONE) {
|
||||||
|
mySubState = NONE;
|
||||||
|
} else {
|
||||||
|
pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enteringNewElement(String theNamespaceURI, String theLocalPart) throws DataFormatException {
|
||||||
|
if (Tag.ATTR_TERM.equals(theLocalPart)) {
|
||||||
|
mySubState = TERM;
|
||||||
|
} else if (Tag.ATTR_SCHEME.equals(theLocalPart)) {
|
||||||
|
mySubState = SCHEME;
|
||||||
|
} else if (Tag.ATTR_LABEL.equals(theLocalPart)) {
|
||||||
|
mySubState = LABEL;
|
||||||
|
} else {
|
||||||
|
throw new DataFormatException("Unexpected element: " + theLocalPart);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private class XhtmlState extends BaseState {
|
private class XhtmlState extends BaseState {
|
||||||
private int myDepth;
|
private int myDepth;
|
||||||
private XhtmlDt myDt;
|
private XhtmlDt myDt;
|
||||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
|
@ -56,13 +57,14 @@ import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
|
import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.BundleCategory;
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
import ca.uhn.fhir.model.api.BundleEntry;
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||||
import ca.uhn.fhir.model.api.IElement;
|
import ca.uhn.fhir.model.api.IElement;
|
||||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
|
import ca.uhn.fhir.model.dstu.composite.ContainedDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||||
|
@ -104,9 +106,7 @@ public class XmlParser extends BaseParser implements IParser {
|
||||||
@Override
|
@Override
|
||||||
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException {
|
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException {
|
||||||
try {
|
try {
|
||||||
XMLStreamWriter eventWriter;
|
XMLStreamWriter eventWriter = createXmlWriter(theWriter);
|
||||||
eventWriter = myXmlOutputFactory.createXMLStreamWriter(theWriter);
|
|
||||||
eventWriter = decorateStreamWriter(eventWriter);
|
|
||||||
|
|
||||||
eventWriter.writeStartElement("feed");
|
eventWriter.writeStartElement("feed");
|
||||||
eventWriter.writeDefaultNamespace(ATOM_NS);
|
eventWriter.writeDefaultNamespace(ATOM_NS);
|
||||||
|
@ -147,7 +147,7 @@ public class XmlParser extends BaseParser implements IParser {
|
||||||
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
|
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
|
||||||
|
|
||||||
if (nextEntry.getCategories() != null) {
|
if (nextEntry.getCategories() != null) {
|
||||||
for (BundleCategory next : nextEntry.getCategories()) {
|
for (Tag next : nextEntry.getCategories()) {
|
||||||
eventWriter.writeStartElement("category");
|
eventWriter.writeStartElement("category");
|
||||||
eventWriter.writeAttribute("term", defaultString(next.getTerm()));
|
eventWriter.writeAttribute("term", defaultString(next.getTerm()));
|
||||||
eventWriter.writeAttribute("label", defaultString(next.getLabel()));
|
eventWriter.writeAttribute("label", defaultString(next.getLabel()));
|
||||||
|
@ -155,7 +155,7 @@ public class XmlParser extends BaseParser implements IParser {
|
||||||
eventWriter.writeEndElement();
|
eventWriter.writeEndElement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nextEntry.getLinkSelf().isEmpty()) {
|
if (!nextEntry.getLinkSelf().isEmpty()) {
|
||||||
writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf());
|
writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf());
|
||||||
}
|
}
|
||||||
|
@ -193,11 +193,10 @@ public class XmlParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encodeResourceToWriter(IResource theResource, Writer stringWriter) throws DataFormatException {
|
public void encodeResourceToWriter(IResource theResource, Writer theWriter) throws DataFormatException {
|
||||||
XMLStreamWriter eventWriter;
|
XMLStreamWriter eventWriter;
|
||||||
try {
|
try {
|
||||||
eventWriter = myXmlOutputFactory.createXMLStreamWriter(stringWriter);
|
eventWriter = createXmlWriter(theWriter);
|
||||||
eventWriter = decorateStreamWriter(eventWriter);
|
|
||||||
|
|
||||||
encodeResourceToXmlStreamWriter(theResource, eventWriter, false);
|
encodeResourceToXmlStreamWriter(theResource, eventWriter, false);
|
||||||
eventWriter.flush();
|
eventWriter.flush();
|
||||||
|
@ -206,22 +205,73 @@ public class XmlParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private XMLStreamWriter createXmlWriter(Writer theWriter) throws XMLStreamException {
|
||||||
|
XMLStreamWriter eventWriter;
|
||||||
|
eventWriter = myXmlOutputFactory.createXMLStreamWriter(theWriter);
|
||||||
|
eventWriter = decorateStreamWriter(eventWriter);
|
||||||
|
return eventWriter;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) {
|
public void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException {
|
||||||
XMLEventReader streamReader;
|
|
||||||
try {
|
try {
|
||||||
streamReader = myXmlInputFactory.createXMLEventReader(theReader);
|
XMLStreamWriter eventWriter = createXmlWriter(theWriter);
|
||||||
|
|
||||||
|
eventWriter.writeStartElement(TagList.ELEMENT_NAME_LC);
|
||||||
|
eventWriter.writeDefaultNamespace(FHIR_NS);
|
||||||
|
|
||||||
|
for (Tag next : theTagList) {
|
||||||
|
eventWriter.writeStartElement(TagList.ATTR_CATEGORY);
|
||||||
|
|
||||||
|
if (isNotBlank(next.getTerm())) {
|
||||||
|
eventWriter.writeAttribute(Tag.ATTR_TERM, next.getTerm());
|
||||||
|
}
|
||||||
|
if (isNotBlank(next.getLabel())) {
|
||||||
|
eventWriter.writeAttribute(Tag.ATTR_LABEL, next.getLabel());
|
||||||
|
}
|
||||||
|
if (isNotBlank(next.getScheme())) {
|
||||||
|
eventWriter.writeAttribute(Tag.ATTR_SCHEME, next.getScheme());
|
||||||
|
}
|
||||||
|
|
||||||
|
eventWriter.writeEndElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
eventWriter.writeEndElement();
|
||||||
|
eventWriter.close();
|
||||||
} catch (XMLStreamException e) {
|
} catch (XMLStreamException e) {
|
||||||
throw new DataFormatException(e);
|
|
||||||
} catch (FactoryConfigurationError e) {
|
|
||||||
throw new ConfigurationException("Failed to initialize STaX event factory", e);
|
throw new ConfigurationException("Failed to initialize STaX event factory", e);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends IResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) {
|
||||||
|
XMLEventReader streamReader = createStreamReader(theReader);
|
||||||
|
|
||||||
return parseBundle(streamReader, theResourceType);
|
return parseBundle(streamReader, theResourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader) {
|
public <T extends IResource> T parseResource(Class<T> theResourceType, Reader theReader) {
|
||||||
|
XMLEventReader streamReader = createStreamReader(theReader);
|
||||||
|
|
||||||
|
return parseResource(theResourceType, streamReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TagList parseTagList(Reader theReader) {
|
||||||
|
XMLEventReader streamReader = createStreamReader(theReader);
|
||||||
|
|
||||||
|
ParserState<TagList> parserState = ParserState.getPreTagListInstance(myContext, false);
|
||||||
|
return doXmlLoop(streamReader, parserState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IParser setPrettyPrint(boolean thePrettyPrint) {
|
||||||
|
myPrettyPrint = thePrettyPrint;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private XMLEventReader createStreamReader(Reader theReader) {
|
||||||
XMLEventReader streamReader;
|
XMLEventReader streamReader;
|
||||||
try {
|
try {
|
||||||
streamReader = myXmlInputFactory.createXMLEventReader(theReader);
|
streamReader = myXmlInputFactory.createXMLEventReader(theReader);
|
||||||
|
@ -230,14 +280,7 @@ public class XmlParser extends BaseParser implements IParser {
|
||||||
} catch (FactoryConfigurationError e) {
|
} catch (FactoryConfigurationError e) {
|
||||||
throw new ConfigurationException("Failed to initialize STaX event factory", e);
|
throw new ConfigurationException("Failed to initialize STaX event factory", e);
|
||||||
}
|
}
|
||||||
|
return streamReader;
|
||||||
return parseResource(theResourceType, streamReader);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IParser setPrettyPrint(boolean thePrettyPrint) {
|
|
||||||
myPrettyPrint = thePrettyPrint;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private XMLStreamWriter decorateStreamWriter(XMLStreamWriter eventWriter) {
|
private XMLStreamWriter decorateStreamWriter(XMLStreamWriter eventWriter) {
|
||||||
|
@ -249,7 +292,9 @@ public class XmlParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T extends IElement> T doXmlLoop(XMLEventReader streamReader, ParserState<T> parserState) {
|
private <T> T doXmlLoop(XMLEventReader streamReader, ParserState<T> parserState) {
|
||||||
|
ourLog.trace("Entering XML parsing loop with state: {}", parserState);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
while (streamReader.hasNext()) {
|
while (streamReader.hasNext()) {
|
||||||
XMLEvent nextEvent = streamReader.nextEvent();
|
XMLEvent nextEvent = streamReader.nextEvent();
|
||||||
|
@ -639,14 +684,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())) {
|
|
||||||
theEventWriter.writeCharacters(theStringDt.getValue());
|
|
||||||
}
|
|
||||||
theEventWriter.writeEndElement();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeTagWithTextNode(XMLStreamWriter theEventWriter, String theElementName, IdDt theIdDt) throws XMLStreamException {
|
private void writeTagWithTextNode(XMLStreamWriter theEventWriter, String theElementName, IdDt theIdDt) throws XMLStreamException {
|
||||||
theEventWriter.writeStartElement(theElementName);
|
theEventWriter.writeStartElement(theElementName);
|
||||||
if (StringUtils.isNotBlank(theIdDt.getValue())) {
|
if (StringUtils.isNotBlank(theIdDt.getValue())) {
|
||||||
|
@ -654,4 +691,12 @@ public class XmlParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
theEventWriter.writeEndElement();
|
theEventWriter.writeEndElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void writeTagWithTextNode(XMLStreamWriter theEventWriter, String theElementName, StringDt theStringDt) throws XMLStreamException {
|
||||||
|
theEventWriter.writeStartElement(theElementName);
|
||||||
|
if (StringUtils.isNotBlank(theStringDt.getValue())) {
|
||||||
|
theEventWriter.writeCharacters(theStringDt.getValue());
|
||||||
|
}
|
||||||
|
theEventWriter.writeEndElement();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
package ca.uhn.fhir.rest.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RESTful method annotation to be used for the FHIR <a
|
||||||
|
* href="http://hl7.org/implement/standards/fhir/http.html#tags">Tag
|
||||||
|
* Operations</a> which have to do with adding tags.
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* To add tag(s) <b>to the given resource
|
||||||
|
* instance</b>, this annotation should contain a {@link #type()} attribute
|
||||||
|
* specifying the resource type, and the method should have a parameter of type
|
||||||
|
* {@link IdDt} annotated with the {@link IdParam} annotation, as well as
|
||||||
|
* a parameter of type {@link TagList}. Note that this {@link TagList} parameter
|
||||||
|
* does not need to contain a complete list of tags for the resource, only a list
|
||||||
|
* of tags to be added. Server implementations must not remove tags based on this
|
||||||
|
* operation.
|
||||||
|
* Note that for a
|
||||||
|
* server implementation, the {@link #type()} annotation is optional if the
|
||||||
|
* method is defined in a <a href=
|
||||||
|
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
|
||||||
|
* >resource provider</a>, since the type is implied.</li>
|
||||||
|
* <li>
|
||||||
|
* To add tag(s) on the server <b>to the given version of the
|
||||||
|
* resource instance</b>, this annotation should contain a {@link #type()}
|
||||||
|
* attribute specifying the resource type, and the method should have a
|
||||||
|
* parameter of type {@link IdDt} annotated with the {@link VersionIdParam}
|
||||||
|
* annotation, <b>and</b> a parameter of type {@link IdDt} annotated with the
|
||||||
|
* {@link IdParam} annotation, as well as
|
||||||
|
* a parameter of type {@link TagList}. Note that this {@link TagList} parameter
|
||||||
|
* does not need to contain a complete list of tags for the resource, only a list
|
||||||
|
* of tags to be added. Server implementations must not remove tags based on this
|
||||||
|
* operation.
|
||||||
|
* Note that for a server implementation, the
|
||||||
|
* {@link #type()} annotation is optional if the method is defined in a <a href=
|
||||||
|
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
|
||||||
|
* >resource provider</a>, since the type is implied.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@Target(value= ElementType.METHOD)
|
||||||
|
@Retention(value=RetentionPolicy.RUNTIME)
|
||||||
|
public @interface AddTags {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to a type other than the default (which is {@link IResource.class}
|
||||||
|
* , this method is expected to return a TagList containing only tags which
|
||||||
|
* are specific to the given resource type.
|
||||||
|
*/
|
||||||
|
Class<? extends IResource> type() default IResource.class;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
package ca.uhn.fhir.rest.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RESTful method annotation to be used for the FHIR <a
|
||||||
|
* href="http://hl7.org/implement/standards/fhir/http.html#tags">Tag
|
||||||
|
* Operations</a> which have to do with deleting tags.
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* To delete tag(s) <b>to the given resource
|
||||||
|
* instance</b>, this annotation should contain a {@link #type()} attribute
|
||||||
|
* specifying the resource type, and the method should have a parameter of type
|
||||||
|
* {@link IdDt} annotated with the {@link IdParam} annotation, as well as
|
||||||
|
* a parameter of type {@link TagList} which will contain the list of tags
|
||||||
|
* to be deleted.
|
||||||
|
* Note that for a
|
||||||
|
* server implementation, the {@link #type()} annotation is optional if the
|
||||||
|
* method is defined in a <a href=
|
||||||
|
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
|
||||||
|
* >resource provider</a>, since the type is implied.</li>
|
||||||
|
* <li>
|
||||||
|
* To delete tag(s) on the server <b>to the given version of the
|
||||||
|
* resource instance</b>, this annotation should contain a {@link #type()}
|
||||||
|
* attribute specifying the resource type, and the method should have a
|
||||||
|
* parameter of type {@link IdDt} annotated with the {@link VersionIdParam}
|
||||||
|
* annotation, <b>and</b> a parameter of type {@link IdDt} annotated with the
|
||||||
|
* {@link IdParam} annotation, as well as
|
||||||
|
* a parameter of type {@link TagList} which will contain the list of tags
|
||||||
|
* to be deleted.
|
||||||
|
* Note that for a server implementation, the
|
||||||
|
* {@link #type()} annotation is optional if the method is defined in a <a href=
|
||||||
|
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
|
||||||
|
* >resource provider</a>, since the type is implied.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@Target(value= ElementType.METHOD)
|
||||||
|
@Retention(value=RetentionPolicy.RUNTIME)
|
||||||
|
public @interface DeleteTags {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to a type other than the default (which is {@link IResource.class}
|
||||||
|
* , this method is expected to return a TagList containing only tags which
|
||||||
|
* are specific to the given resource type.
|
||||||
|
*/
|
||||||
|
Class<? extends IResource> type() default IResource.class;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package ca.uhn.fhir.rest.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RESTful method annotation to be used for the FHIR <a
|
||||||
|
* href="http://hl7.org/implement/standards/fhir/http.html#tags">Tag
|
||||||
|
* Operations</a> which have to do with getting tags.
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* To return a global list of all tags on the server, this annotation should not
|
||||||
|
* contain a {@link #type()} attribute, and the method should not have an ID or
|
||||||
|
* Version ID parameter. On server implementations, the method must be defined
|
||||||
|
* in a <a href=
|
||||||
|
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#plain_providers"
|
||||||
|
* >plain provider</a>.</li>
|
||||||
|
* <li>
|
||||||
|
* To return a list of all tags on the server <b>for the given resource
|
||||||
|
* type</b>, this annotation should contain a {@link #type()} attribute
|
||||||
|
* specifying the resource type, and the method should not have an ID or Version
|
||||||
|
* ID parameter. Note that for a server implementation, the {@link #type()}
|
||||||
|
* annotation is optional if the method is defined in a <a href=
|
||||||
|
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
|
||||||
|
* >resource provider</a>, since the type is implied.</li>
|
||||||
|
* <li>
|
||||||
|
* To return a list of all tags on the server <b>for the given resource
|
||||||
|
* instance</b>, this annotation should contain a {@link #type()} attribute
|
||||||
|
* specifying the resource type, and the method should have a parameter of type
|
||||||
|
* {@link IdDt} annotated with the {@link IdParam} annotation. Note that for a
|
||||||
|
* server implementation, the {@link #type()} annotation is optional if the
|
||||||
|
* method is defined in a <a href=
|
||||||
|
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
|
||||||
|
* >resource provider</a>, since the type is implied.</li>
|
||||||
|
* <li>
|
||||||
|
* To return a list of all tags on the server <b>for the given version of the
|
||||||
|
* resource instance</b>, this annotation should contain a {@link #type()}
|
||||||
|
* attribute specifying the resource type, and the method should have a
|
||||||
|
* parameter of type {@link IdDt} annotated with the {@link VersionIdParam}
|
||||||
|
* annotation, <b>and</b> a parameter of type {@link IdDt} annotated with the
|
||||||
|
* {@link IdParam} annotation. Note that for a server implementation, the
|
||||||
|
* {@link #type()} annotation is optional if the method is defined in a <a href=
|
||||||
|
* "http://hl7api.sourceforge.net/hapi-fhir/doc_rest_server.html#resource_providers"
|
||||||
|
* >resource provider</a>, since the type is implied.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
@Target(value= ElementType.METHOD)
|
||||||
|
@Retention(value=RetentionPolicy.RUNTIME)
|
||||||
|
public @interface GetTags {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If set to a type other than the default (which is {@link IResource.class}
|
||||||
|
* , this method is expected to return a TagList containing only tags which
|
||||||
|
* are specific to the given resource type.
|
||||||
|
*/
|
||||||
|
Class<? extends IResource> type() default IResource.class;
|
||||||
|
|
||||||
|
}
|
|
@ -112,7 +112,7 @@ public abstract class BaseClient {
|
||||||
HttpRequestBase httpRequest;
|
HttpRequestBase httpRequest;
|
||||||
HttpResponse response;
|
HttpResponse response;
|
||||||
try {
|
try {
|
||||||
httpRequest = clientInvocation.asHttpRequest(myUrlBase, createExtraParams());
|
httpRequest = clientInvocation.asHttpRequest(myUrlBase, createExtraParams(), getEncoding());
|
||||||
response = myClient.execute(httpRequest);
|
response = myClient.execute(httpRequest);
|
||||||
} catch (DataFormatException e) {
|
} catch (DataFormatException e) {
|
||||||
throw new FhirClientConnectionException(e);
|
throw new FhirClientConnectionException(e);
|
||||||
|
@ -179,9 +179,9 @@ public abstract class BaseClient {
|
||||||
HashMap<String, List<String>> retVal = new LinkedHashMap<String, List<String>>();
|
HashMap<String, List<String>> retVal = new LinkedHashMap<String, List<String>>();
|
||||||
|
|
||||||
if (getEncoding() == EncodingEnum.XML) {
|
if (getEncoding() == EncodingEnum.XML) {
|
||||||
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList(Constants.CT_XML));
|
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList("xml"));
|
||||||
} else if (getEncoding() == EncodingEnum.JSON) {
|
} else if (getEncoding() == EncodingEnum.JSON) {
|
||||||
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList(Constants.CT_JSON));
|
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList("json"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPrettyPrint()) {
|
if (isPrettyPrint()) {
|
||||||
|
@ -229,7 +229,7 @@ public abstract class BaseClient {
|
||||||
return new StringReader("");
|
return new StringReader("");
|
||||||
}
|
}
|
||||||
Charset charset = null;
|
Charset charset = null;
|
||||||
if (entity.getContentType().getElements() != null) {
|
if (entity.getContentType() != null && entity.getContentType().getElements() != null && entity.getContentType().getElements().length>0) {
|
||||||
ContentType ct = ContentType.get(entity);
|
ContentType ct = ContentType.get(entity);
|
||||||
charset = ct.getCharset();
|
charset = ct.getCharset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ import org.apache.http.Header;
|
||||||
import org.apache.http.client.methods.HttpRequestBase;
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
import org.apache.http.message.BasicHeader;
|
import org.apache.http.message.BasicHeader;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
|
||||||
public abstract class BaseClientInvocation {
|
public abstract class BaseClientInvocation {
|
||||||
|
|
||||||
private List<Header> myHeaders;
|
private List<Header> myHeaders;
|
||||||
|
@ -49,8 +51,11 @@ public abstract class BaseClientInvocation {
|
||||||
* The FHIR server base url (with a trailing "/")
|
* The FHIR server base url (with a trailing "/")
|
||||||
* @param theExtraParams
|
* @param theExtraParams
|
||||||
* Any extra request parameters the server wishes to add
|
* Any extra request parameters the server wishes to add
|
||||||
|
* @param theEncoding
|
||||||
|
* The encoding to use for any serialized content sent to the
|
||||||
|
* server
|
||||||
*/
|
*/
|
||||||
public abstract HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams);
|
public abstract HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding);
|
||||||
|
|
||||||
protected static void appendExtraParamsWithQuestionMark(Map<String, List<String>> theExtraParams, StringBuilder theUrlBuilder, boolean theWithQuestionMark) {
|
protected static void appendExtraParamsWithQuestionMark(Map<String, List<String>> theExtraParams, StringBuilder theUrlBuilder, boolean theWithQuestionMark) {
|
||||||
boolean first = theWithQuestionMark;
|
boolean first = theWithQuestionMark;
|
||||||
|
|
|
@ -20,48 +20,52 @@ package ca.uhn.fhir.rest.client;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.Header;
|
|
||||||
import org.apache.http.client.methods.HttpRequestBase;
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
import org.apache.http.message.BasicHeader;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
|
||||||
public abstract class BaseClientInvocationWithContents extends BaseClientInvocation {
|
public abstract class BaseClientInvocationWithContents extends BaseClientInvocation {
|
||||||
|
|
||||||
private final Bundle myBundle;
|
|
||||||
private final FhirContext myContext;
|
private final FhirContext myContext;
|
||||||
private final IResource myResource;
|
private final IResource myResource;
|
||||||
private String myUrlExtension;
|
private final String myUrlExtension;
|
||||||
|
private final TagList myTagList;
|
||||||
public BaseClientInvocationWithContents(FhirContext theContext, Bundle theBundle) {
|
|
||||||
super();
|
|
||||||
myContext = theContext;
|
|
||||||
myResource = null;
|
|
||||||
myBundle = theBundle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BaseClientInvocationWithContents(FhirContext theContext, IResource theResource, String theUrlExtension) {
|
public BaseClientInvocationWithContents(FhirContext theContext, IResource theResource, String theUrlExtension) {
|
||||||
super();
|
super();
|
||||||
myContext = theContext;
|
myContext = theContext;
|
||||||
myResource = theResource;
|
myResource = theResource;
|
||||||
myBundle = null;
|
|
||||||
myUrlExtension = theUrlExtension;
|
myUrlExtension = theUrlExtension;
|
||||||
|
myTagList = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseClientInvocationWithContents(FhirContext theContext, TagList theTagList, String... theUrlExtension) {
|
||||||
|
super();
|
||||||
|
if (theTagList == null) {
|
||||||
|
throw new NullPointerException("Tag list must not be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
myResource = null;
|
||||||
|
myContext = theContext;
|
||||||
|
myTagList = theTagList;
|
||||||
|
|
||||||
|
myUrlExtension = StringUtils.join(theUrlExtension, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams) throws DataFormatException {
|
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) throws DataFormatException {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
b.append(theUrlBase);
|
b.append(theUrlBase);
|
||||||
if (!theUrlBase.endsWith("/")) {
|
if (!theUrlBase.endsWith("/")) {
|
||||||
|
@ -71,11 +75,26 @@ public abstract class BaseClientInvocationWithContents extends BaseClientInvocat
|
||||||
|
|
||||||
appendExtraParamsWithQuestionMark(theExtraParams, b, true);
|
appendExtraParamsWithQuestionMark(theExtraParams, b, true);
|
||||||
|
|
||||||
String url = b.toString();
|
IParser parser;
|
||||||
String contents = myContext.newXmlParser().encodeResourceToString(myResource);
|
String contentType;
|
||||||
StringEntity entity = new StringEntity(contents, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"));
|
if (theEncoding == EncodingEnum.JSON) {
|
||||||
|
parser = myContext.newJsonParser();
|
||||||
|
contentType = Constants.CT_FHIR_JSON;
|
||||||
|
} else {
|
||||||
|
parser = myContext.newXmlParser();
|
||||||
|
contentType = Constants.CT_FHIR_XML;
|
||||||
|
}
|
||||||
|
|
||||||
HttpRequestBase retVal = createRequest(url, entity);
|
String contents;
|
||||||
|
if (myTagList != null) {
|
||||||
|
contents = parser.encodeTagListToString(myTagList);
|
||||||
|
} else {
|
||||||
|
contents = parser.encodeResourceToString(myResource);
|
||||||
|
}
|
||||||
|
|
||||||
|
StringEntity entity = new StringEntity(contents, ContentType.create(contentType, "UTF-8"));
|
||||||
|
|
||||||
|
HttpRequestBase retVal = createRequest(b.toString(), entity);
|
||||||
super.addHeadersToRequest(retVal);
|
super.addHeadersToRequest(retVal);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,124 +0,0 @@
|
||||||
package ca.uhn.fhir.rest.client;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.model.api.IDatatype;
|
|
||||||
import ca.uhn.fhir.model.api.annotation.Child;
|
|
||||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
|
||||||
import ca.uhn.fhir.model.primitive.Base64BinaryDt;
|
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
|
||||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
|
||||||
|
|
||||||
public class ClientCache {
|
|
||||||
|
|
||||||
private FhirContext myPrivateContext;
|
|
||||||
private FhirContext myPublicContext;
|
|
||||||
|
|
||||||
private ClientCache(FhirContext theContext) {
|
|
||||||
myPublicContext = theContext;
|
|
||||||
myPrivateContext = new FhirContext(CacheEntry.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Accepts a restful client instance and decorates it
|
|
||||||
*/
|
|
||||||
public <T extends IRestfulClient> T decorateClient(Class<T> theInterface, T theClientToDecorate) {
|
|
||||||
for (Method nextMethod : theInterface.getMethods()){
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return theClientToDecorate;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ResourceDef(name="HapiClientCacheEntry", id="hapiclientcacheentry")
|
|
||||||
private static class CacheEntry {
|
|
||||||
|
|
||||||
@Child(order=2, min=0,max=Child.MAX_UNLIMITED,name="arguments")
|
|
||||||
private List<IDatatype> myArguments;
|
|
||||||
|
|
||||||
@Child(order=1, min=0,max=Child.MAX_UNLIMITED,name="argumentTypes")
|
|
||||||
private List<StringDt> myArgumentTypes;
|
|
||||||
|
|
||||||
@Child(order=3, min=1,max=1,name="loaded")
|
|
||||||
private InstantDt myLoaded;
|
|
||||||
|
|
||||||
@Child(order=0, min=1, max=1, name="methodName")
|
|
||||||
private StringDt myMethodName;
|
|
||||||
|
|
||||||
@Child(order=4, min=1,max=1,name="resourceText")
|
|
||||||
private Base64BinaryDt myResourceText;
|
|
||||||
|
|
||||||
public List<IDatatype> getArguments() {
|
|
||||||
if (myArguments==null) {
|
|
||||||
myArguments=new ArrayList<IDatatype>();
|
|
||||||
}
|
|
||||||
return myArguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<StringDt> getArgumentTypes() {
|
|
||||||
return myArgumentTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InstantDt getLoaded() {
|
|
||||||
return myLoaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StringDt getMethodName() {
|
|
||||||
return myMethodName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Base64BinaryDt getResourceText() {
|
|
||||||
return myResourceText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setArguments(List<IDatatype> theArguments) {
|
|
||||||
myArguments = theArguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setArgumentTypes(List<StringDt> theArgumentTypes) {
|
|
||||||
myArgumentTypes = theArgumentTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLoaded(InstantDt theLoaded) {
|
|
||||||
myLoaded = theLoaded;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMethodName(StringDt theMethodName) {
|
|
||||||
myMethodName = theMethodName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setResourceText(Base64BinaryDt theResourceText) {
|
|
||||||
myResourceText = theResourceText;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -31,19 +31,27 @@ import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
|
||||||
public class ClientInvocationHandler extends BaseClient implements InvocationHandler {
|
public class ClientInvocationHandler extends BaseClient implements InvocationHandler {
|
||||||
|
|
||||||
private final Map<Method, BaseMethodBinding> myBindings = new HashMap<Method, BaseMethodBinding>();
|
private final Map<Method, BaseMethodBinding> myBindings = new HashMap<Method, BaseMethodBinding>();
|
||||||
|
|
||||||
|
private final Map<Method, ILambda> myMethodToLambda = new HashMap<Method, ILambda>();
|
||||||
|
|
||||||
private final Map<Method, Object> myMethodToReturnValue = new HashMap<Method, Object>();
|
private final Map<Method, Object> myMethodToReturnValue = new HashMap<Method, Object>();
|
||||||
|
|
||||||
public ClientInvocationHandler(HttpClient theClient, FhirContext theContext, String theUrlBase, Class<? extends IRestfulClient> theClientType) {
|
public ClientInvocationHandler(HttpClient theClient, FhirContext theContext, String theUrlBase, Class<? extends IRestfulClient> theClientType) {
|
||||||
super(theClient, theUrlBase);
|
super(theClient, theUrlBase);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
myMethodToReturnValue.put(theClientType.getMethod("getFhirContext"), theContext);
|
myMethodToReturnValue.put(theClientType.getMethod("getFhirContext"), theContext);
|
||||||
myMethodToReturnValue.put(theClientType.getMethod("getHttpClient"), theClient);
|
myMethodToReturnValue.put(theClientType.getMethod("getHttpClient"), theClient);
|
||||||
myMethodToReturnValue.put(theClientType.getMethod("getServerBase"), theUrlBase);
|
myMethodToReturnValue.put(theClientType.getMethod("getServerBase"), theUrlBase);
|
||||||
|
|
||||||
|
myMethodToLambda.put(theClientType.getMethod("setEncoding", EncodingEnum.class), new SetEncodingLambda());
|
||||||
|
myMethodToLambda.put(theClientType.getMethod("setPrettyPrint", boolean.class), new SetPrettyPrintLambda());
|
||||||
|
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
|
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
|
||||||
} catch (SecurityException e) {
|
} catch (SecurityException e) {
|
||||||
|
@ -63,11 +71,39 @@ public class ClientInvocationHandler extends BaseClient implements InvocationHan
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseMethodBinding binding = myBindings.get(theMethod);
|
BaseMethodBinding binding = myBindings.get(theMethod);
|
||||||
BaseClientInvocation clientInvocation = binding.invokeClient(theArgs);
|
if (binding != null) {
|
||||||
|
BaseClientInvocation clientInvocation = binding.invokeClient(theArgs);
|
||||||
return invokeClient(binding, clientInvocation);
|
return invokeClient(binding, clientInvocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
ILambda lambda = myMethodToLambda.get(theMethod);
|
||||||
|
if (lambda != null) {
|
||||||
|
return lambda.handle(theArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new UnsupportedOperationException("The method '" + theMethod.getName() + "' in type " + theMethod.getDeclaringClass().getSimpleName() + " has no handler. Did you forget to annotate it with a RESTful method annotation?");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface ILambda {
|
||||||
|
Object handle(Object[] theArgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SetEncodingLambda implements ILambda {
|
||||||
|
@Override
|
||||||
|
public Object handle(Object[] theArgs) {
|
||||||
|
EncodingEnum encoding = (EncodingEnum) theArgs[0];
|
||||||
|
setEncoding(encoding);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SetPrettyPrintLambda implements ILambda {
|
||||||
|
@Override
|
||||||
|
public Object handle(Object[] theArgs) {
|
||||||
|
Boolean prettyPrint = (Boolean) theArgs[0];
|
||||||
|
setPrettyPrint(prettyPrint);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.client.methods.HttpDelete;
|
import org.apache.http.client.methods.HttpDelete;
|
||||||
import org.apache.http.client.methods.HttpRequestBase;
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
|
||||||
public class DeleteClientInvocation extends BaseClientInvocation {
|
public class DeleteClientInvocation extends BaseClientInvocation {
|
||||||
|
|
||||||
private String myUrlPath;
|
private String myUrlPath;
|
||||||
|
@ -37,7 +39,7 @@ public class DeleteClientInvocation extends BaseClientInvocation {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams) {
|
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
b.append(theUrlBase);
|
b.append(theUrlBase);
|
||||||
if (!theUrlBase.endsWith("/")) {
|
if (!theUrlBase.endsWith("/")) {
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
public <T extends IResource> T read(final Class<T> theType, IdDt theId) {
|
public <T extends IResource> T read(final Class<T> theType, IdDt theId) {
|
||||||
GetClientInvocation invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType));
|
GetClientInvocation invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType));
|
||||||
if (isKeepResponses()) {
|
if (isKeepResponses()) {
|
||||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||||
}
|
}
|
||||||
|
|
||||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||||
|
@ -92,12 +92,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodOutcome delete(final Class<? extends IResource> theType, IdDt theId) {
|
public MethodOutcome delete(final Class<? extends IResource> theType, IdDt theId) {
|
||||||
DeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(toResourceName(theType), theId);
|
DeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(toResourceName(theType), theId);
|
||||||
if (isKeepResponses()) {
|
if (isKeepResponses()) {
|
||||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||||
}
|
}
|
||||||
|
|
||||||
final String resourceName = myContext.getResourceDefinition(theType).getName();
|
final String resourceName = myContext.getResourceDefinition(theType).getName();
|
||||||
|
@ -109,7 +108,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MethodOutcome resp = (MethodOutcome) invokeClient(binding, invocation);
|
MethodOutcome resp = (MethodOutcome) invokeClient(binding, invocation);
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +128,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
public <T extends IResource> T vread(final Class<T> theType, IdDt theId, IdDt theVersionId) {
|
public <T extends IResource> T vread(final Class<T> theType, IdDt theId, IdDt theVersionId) {
|
||||||
GetClientInvocation invocation = ReadMethodBinding.createVReadInvocation(theId, theVersionId, toResourceName(theType));
|
GetClientInvocation invocation = ReadMethodBinding.createVReadInvocation(theId, theVersionId, toResourceName(theType));
|
||||||
if (isKeepResponses()) {
|
if (isKeepResponses()) {
|
||||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||||
}
|
}
|
||||||
|
|
||||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||||
|
@ -159,7 +158,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
|
|
||||||
GetClientInvocation invocation = SearchMethodBinding.createSearchInvocation(toResourceName(theType), params);
|
GetClientInvocation invocation = SearchMethodBinding.createSearchInvocation(toResourceName(theType), params);
|
||||||
if (isKeepResponses()) {
|
if (isKeepResponses()) {
|
||||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||||
}
|
}
|
||||||
|
|
||||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||||
|
@ -179,12 +178,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
public MethodOutcome create(IResource theResource) {
|
public MethodOutcome create(IResource theResource) {
|
||||||
BaseClientInvocation invocation = CreateMethodBinding.createCreateInvocation(theResource, myContext);
|
BaseClientInvocation invocation = CreateMethodBinding.createCreateInvocation(theResource, myContext);
|
||||||
if (isKeepResponses()) {
|
if (isKeepResponses()) {
|
||||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||||
final String resourceName = def.getName();
|
final String resourceName = def.getName();
|
||||||
|
|
||||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||||
@Override
|
@Override
|
||||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||||
|
@ -202,12 +201,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
public MethodOutcome update(IdDt theIdDt, IResource theResource) {
|
public MethodOutcome update(IdDt theIdDt, IResource theResource) {
|
||||||
BaseClientInvocation invocation = UpdateMethodBinding.createUpdateInvocation(theResource, theIdDt, null, myContext);
|
BaseClientInvocation invocation = UpdateMethodBinding.createUpdateInvocation(theResource, theIdDt, null, myContext);
|
||||||
if (isKeepResponses()) {
|
if (isKeepResponses()) {
|
||||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||||
final String resourceName = def.getName();
|
final String resourceName = def.getName();
|
||||||
|
|
||||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||||
@Override
|
@Override
|
||||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||||
|
@ -224,12 +223,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
public MethodOutcome validate(IResource theResource) {
|
public MethodOutcome validate(IResource theResource) {
|
||||||
BaseClientInvocation invocation = ValidateMethodBinding.createValidateInvocation(theResource, null, myContext);
|
BaseClientInvocation invocation = ValidateMethodBinding.createValidateInvocation(theResource, null, myContext);
|
||||||
if (isKeepResponses()) {
|
if (isKeepResponses()) {
|
||||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||||
}
|
}
|
||||||
|
|
||||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||||
final String resourceName = def.getName();
|
final String resourceName = def.getName();
|
||||||
|
|
||||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||||
@Override
|
@Override
|
||||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||||
|
@ -246,7 +245,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
public <T extends IResource> Bundle history(final Class<T> theType, IdDt theIdDt) {
|
public <T extends IResource> Bundle history(final Class<T> theType, IdDt theIdDt) {
|
||||||
GetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(toResourceName(theType), theIdDt);
|
GetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(toResourceName(theType), theIdDt);
|
||||||
if (isKeepResponses()) {
|
if (isKeepResponses()) {
|
||||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||||
}
|
}
|
||||||
|
|
||||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||||
|
@ -267,7 +266,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
||||||
public Conformance conformance() {
|
public Conformance conformance() {
|
||||||
GetClientInvocation invocation = ConformanceMethodBinding.createConformanceInvocation();
|
GetClientInvocation invocation = ConformanceMethodBinding.createConformanceInvocation();
|
||||||
if (isKeepResponses()) {
|
if (isKeepResponses()) {
|
||||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||||
}
|
}
|
||||||
|
|
||||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||||
|
|
|
@ -32,6 +32,8 @@ import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.client.methods.HttpRequestBase;
|
import org.apache.http.client.methods.HttpRequestBase;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
|
||||||
public class GetClientInvocation extends BaseClientInvocation {
|
public class GetClientInvocation extends BaseClientInvocation {
|
||||||
|
|
||||||
|
@ -42,13 +44,12 @@ public class GetClientInvocation extends BaseClientInvocation {
|
||||||
myParameters = theParameters;
|
myParameters = theParameters;
|
||||||
myUrlPath = StringUtils.join(theUrlFragments, '/');
|
myUrlPath = StringUtils.join(theUrlFragments, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
public GetClientInvocation(String theUrlPath) {
|
public GetClientInvocation(String theUrlPath) {
|
||||||
myParameters = new HashMap<String, List<String>>();
|
myParameters = new HashMap<String, List<String>>();
|
||||||
myUrlPath = theUrlPath;
|
myUrlPath = theUrlPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public GetClientInvocation(String... theUrlFragments) {
|
public GetClientInvocation(String... theUrlFragments) {
|
||||||
myParameters = new HashMap<String, List<String>>();
|
myParameters = new HashMap<String, List<String>>();
|
||||||
myUrlPath = StringUtils.join(theUrlFragments, '/');
|
myUrlPath = StringUtils.join(theUrlFragments, '/');
|
||||||
|
@ -63,7 +64,7 @@ public class GetClientInvocation extends BaseClientInvocation {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams) {
|
public HttpRequestBase asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding) {
|
||||||
StringBuilder b = new StringBuilder();
|
StringBuilder b = new StringBuilder();
|
||||||
b.append(theUrlBase);
|
b.append(theUrlBase);
|
||||||
if (!theUrlBase.endsWith("/")) {
|
if (!theUrlBase.endsWith("/")) {
|
||||||
|
@ -76,29 +77,35 @@ public class GetClientInvocation extends BaseClientInvocation {
|
||||||
if (next.getValue() == null || next.getValue().isEmpty()) {
|
if (next.getValue() == null || next.getValue().isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
String nextKey = next.getKey();
|
||||||
for (String nextValue : next.getValue()) {
|
for (String nextValue : next.getValue()) {
|
||||||
if (first) {
|
first = addQueryParameter(b, first, nextKey, nextValue);
|
||||||
b.append('?');
|
|
||||||
first = false;
|
|
||||||
} else {
|
|
||||||
b.append('&');
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
b.append(URLEncoder.encode(next.getKey(), "UTF-8"));
|
|
||||||
b.append('=');
|
|
||||||
b.append(URLEncoder.encode(nextValue, "UTF-8"));
|
|
||||||
} catch (UnsupportedEncodingException e) {
|
|
||||||
throw new ConfigurationException("Could not find UTF-8 encoding. This shouldn't happen.", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appendExtraParamsWithQuestionMark(theExtraParams, b, first);
|
appendExtraParamsWithQuestionMark(theExtraParams, b, first);
|
||||||
|
|
||||||
HttpGet retVal = new HttpGet(b.toString());
|
HttpGet retVal = new HttpGet(b.toString());
|
||||||
super.addHeadersToRequest(retVal);
|
super.addHeadersToRequest(retVal);
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean addQueryParameter(StringBuilder b, boolean first, String nextKey, String nextValue) {
|
||||||
|
if (first) {
|
||||||
|
b.append('?');
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
b.append('&');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
b.append(URLEncoder.encode(nextKey, "UTF-8"));
|
||||||
|
b.append('=');
|
||||||
|
b.append(URLEncoder.encode(nextValue, "UTF-8"));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new ConfigurationException("Could not find UTF-8 encoding. This shouldn't happen.", e);
|
||||||
|
}
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,19 +24,21 @@ import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
|
||||||
public class PostClientInvocation extends BaseClientInvocationWithContents {
|
public class PostClientInvocation extends BaseClientInvocationWithContents {
|
||||||
|
|
||||||
public PostClientInvocation(FhirContext theContext, Bundle theBundle) {
|
|
||||||
super(theContext, theBundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PostClientInvocation(FhirContext theContext, IResource theResource, String theUrlExtension) {
|
public PostClientInvocation(FhirContext theContext, IResource theResource, String theUrlExtension) {
|
||||||
super(theContext, theResource, theUrlExtension);
|
super(theContext, theResource, theUrlExtension);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public PostClientInvocation(FhirContext theContext, TagList theTagList, String... theUrlExtension) {
|
||||||
|
super(theContext, theTagList, theUrlExtension);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpPost createRequest(String url, StringEntity theEntity) {
|
protected HttpPost createRequest(String url, StringEntity theEntity) {
|
||||||
HttpPost retVal = new HttpPost(url);
|
HttpPost retVal = new HttpPost(url);
|
||||||
|
|
|
@ -25,15 +25,10 @@ import org.apache.http.client.methods.HttpRequestBase;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
|
||||||
public class PutClientInvocation extends BaseClientInvocationWithContents {
|
public class PutClientInvocation extends BaseClientInvocationWithContents {
|
||||||
|
|
||||||
public PutClientInvocation(FhirContext theContext, Bundle theBundle) {
|
|
||||||
super(theContext, theBundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PutClientInvocation(FhirContext theContext, IResource theResource, String theUrlExtension) {
|
public PutClientInvocation(FhirContext theContext, IResource theResource, String theUrlExtension) {
|
||||||
super(theContext, theResource, theUrlExtension);
|
super(theContext, theResource, theUrlExtension);
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,15 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
|
||||||
public interface IRestfulClient {
|
public interface IRestfulClient {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the FHIR context associated with this client
|
||||||
|
*/
|
||||||
FhirContext getFhirContext();
|
FhirContext getFhirContext();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not call this method in client code. It is a part of the internal HAPI API and
|
||||||
|
* is subject to change!
|
||||||
|
*/
|
||||||
HttpClient getHttpClient();
|
HttpClient getHttpClient();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -37,6 +44,9 @@ public interface IRestfulClient {
|
||||||
* queries. This means that the client will append the "_format" param
|
* queries. This means that the client will append the "_format" param
|
||||||
* to GET methods (read/search/etc), and will add an appropriate header for
|
* to GET methods (read/search/etc), and will add an appropriate header for
|
||||||
* write methods.
|
* write methods.
|
||||||
|
*
|
||||||
|
* @param theEncoding The encoding to use in the request, or <code>null</code> not specify
|
||||||
|
* an encoding (which generally implies the use of XML). The default is <code>null</code>.
|
||||||
*/
|
*/
|
||||||
void setEncoding(EncodingEnum theEncoding);
|
void setEncoding(EncodingEnum theEncoding);
|
||||||
|
|
||||||
|
@ -44,6 +54,8 @@ public interface IRestfulClient {
|
||||||
* Specifies that the client should request that the server respond with "pretty printing"
|
* Specifies that the client should request that the server respond with "pretty printing"
|
||||||
* enabled. Note that this is a non-standard parameter, so it may only
|
* enabled. Note that this is a non-standard parameter, so it may only
|
||||||
* work against HAPI based servers.
|
* work against HAPI based servers.
|
||||||
|
*
|
||||||
|
* @param thePrettyPrint The pretty print flag to use in the request (default is <code>false</code>)
|
||||||
*/
|
*/
|
||||||
void setPrettyPrint(boolean thePrettyPrint);
|
void setPrettyPrint(boolean thePrettyPrint);
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,6 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
public class NonFhirResponseException extends BaseServerResponseException {
|
public class NonFhirResponseException extends BaseServerResponseException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
private final String myContentType;
|
|
||||||
private final String myResponseText;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -37,18 +35,8 @@ public class NonFhirResponseException extends BaseServerResponseException {
|
||||||
* @param theStatusCode
|
* @param theStatusCode
|
||||||
* @param theContentType
|
* @param theContentType
|
||||||
*/
|
*/
|
||||||
public NonFhirResponseException(String theMessage, String theContentType, int theStatusCode, String theResponseText) {
|
public NonFhirResponseException(int theStatusCode, String theMessage) {
|
||||||
super(theStatusCode, theMessage);
|
super(theStatusCode, theMessage);
|
||||||
myContentType = theContentType;
|
|
||||||
myResponseText = theResponseText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getContentType() {
|
|
||||||
return myContentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getResponseText() {
|
|
||||||
return myResponseText;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.annotation.AddTags;
|
||||||
|
|
||||||
|
public class AddTagsMethodBinding extends BaseAddOrDeleteTagsMethodBinding {
|
||||||
|
|
||||||
|
public AddTagsMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider, AddTags theAnnotation) {
|
||||||
|
super(theMethod, theConetxt, theProvider, theAnnotation.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isDelete() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,207 @@
|
||||||
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.TagListParam;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||||
|
import ca.uhn.fhir.rest.client.PostClientInvocation;
|
||||||
|
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||||
|
import ca.uhn.fhir.rest.param.IParameter;
|
||||||
|
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
|
public abstract class BaseAddOrDeleteTagsMethodBinding extends BaseMethodBinding {
|
||||||
|
|
||||||
|
private Class<? extends IResource> myType;
|
||||||
|
private Integer myIdParamIndex;
|
||||||
|
private Integer myVersionIdParamIndex;
|
||||||
|
private String myResourceName;
|
||||||
|
private Integer myTagListParamIndex;
|
||||||
|
|
||||||
|
public BaseAddOrDeleteTagsMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider, Class<? extends IResource> theTypeFromMethodAnnotation) {
|
||||||
|
super(theMethod, theConetxt, theProvider);
|
||||||
|
|
||||||
|
if (theProvider instanceof IResourceProvider) {
|
||||||
|
myType = ((IResourceProvider) theProvider).getResourceType();
|
||||||
|
} else {
|
||||||
|
myType = theTypeFromMethodAnnotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myType.equals(IResource.class)) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' does not specify a resource type, but has an @" + IdParam.class.getSimpleName() + " parameter. Please specity a resource type in the method annotation on this method");
|
||||||
|
}
|
||||||
|
myResourceName = theConetxt.getResourceDefinition(myType).getName();
|
||||||
|
|
||||||
|
myIdParamIndex = ParameterUtil.findIdParameterIndex(theMethod);
|
||||||
|
myVersionIdParamIndex = ParameterUtil.findVersionIdParameterIndex(theMethod);
|
||||||
|
myTagListParamIndex = ParameterUtil.findTagListParameterIndex(theMethod);
|
||||||
|
|
||||||
|
if (myIdParamIndex == null) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' does not have an @" + IdParam.class.getSimpleName() + " parameter.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myTagListParamIndex == null) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' does not have a parameter of type " + TagList.class.getSimpleName() + ", or paramater is not annotated with the @" + TagListParam.class.getSimpleName() + " annotation");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||||
|
switch (theResponseStatusCode) {
|
||||||
|
case Constants.STATUS_HTTP_200_OK:
|
||||||
|
case Constants.STATUS_HTTP_201_CREATED:
|
||||||
|
case Constants.STATUS_HTTP_204_NO_CONTENT:
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
throw processNon2xxResponseAndReturnExceptionToThrow(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResourceName() {
|
||||||
|
return myResourceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestfulOperationTypeEnum getResourceOperationType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestfulOperationSystemEnum getSystemOperationType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract boolean isDelete();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||||
|
PostClientInvocation retVal;
|
||||||
|
|
||||||
|
IdDt id = (IdDt) theArgs[myIdParamIndex];
|
||||||
|
if (id == null || id.isEmpty()) {
|
||||||
|
throw new InvalidRequestException("ID must not be null or empty for this operation");
|
||||||
|
}
|
||||||
|
|
||||||
|
IdDt versionId = null;
|
||||||
|
if (myVersionIdParamIndex != null) {
|
||||||
|
versionId = (IdDt) theArgs[myVersionIdParamIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
TagList tagList = (TagList) theArgs[myTagListParamIndex];
|
||||||
|
|
||||||
|
Class<? extends IResource> type = myType;
|
||||||
|
assert type != null;
|
||||||
|
|
||||||
|
if (isDelete()) {
|
||||||
|
if (versionId != null) {
|
||||||
|
retVal = new PostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS, Constants.PARAM_DELETE);
|
||||||
|
} else {
|
||||||
|
retVal = new PostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_TAGS, Constants.PARAM_DELETE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (versionId != null) {
|
||||||
|
retVal = new PostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS);
|
||||||
|
} else {
|
||||||
|
retVal = new PostClientInvocation(getContext(), tagList, getResourceName(), id.getValue(), Constants.PARAM_TAGS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int idx = 0; idx < theArgs.length; idx++) {
|
||||||
|
IParameter nextParam = getParameters().get(idx);
|
||||||
|
nextParam.translateClientArgumentIntoQueryArgument(theArgs[idx], null, retVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
|
||||||
|
Object[] params = createParametersForServerRequest(theRequest, null);
|
||||||
|
|
||||||
|
params[myIdParamIndex] = theRequest.getId();
|
||||||
|
|
||||||
|
if (myVersionIdParamIndex != null) {
|
||||||
|
params[myVersionIdParamIndex] = theRequest.getVersionId();
|
||||||
|
}
|
||||||
|
|
||||||
|
IParser parser = createAppropriateParserForParsingServerRequest(theRequest);
|
||||||
|
Reader reader = theRequest.getInputReader();
|
||||||
|
try {
|
||||||
|
TagList tagList = parser.parseTagList(reader);
|
||||||
|
params[myTagListParamIndex] = tagList;
|
||||||
|
} finally {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
invokeServerMethod(params);
|
||||||
|
|
||||||
|
EncodingEnum responseEncoding = determineResponseEncoding(theRequest);
|
||||||
|
|
||||||
|
theResponse.setContentType(responseEncoding.getResourceContentType());
|
||||||
|
theResponse.setStatus(Constants.STATUS_HTTP_200_OK);
|
||||||
|
theResponse.setCharacterEncoding(Constants.CHARSET_UTF_8);
|
||||||
|
|
||||||
|
theServer.addHeadersToResponse(theResponse);
|
||||||
|
|
||||||
|
PrintWriter writer = theResponse.getWriter();
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||||
|
if (theRequest.getRequestType() != RequestType.POST) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Constants.PARAM_TAGS.equals(theRequest.getOperation())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!myResourceName.equals(theRequest.getResourceName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theRequest.getId() == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((myVersionIdParamIndex != null) != (theRequest.getVersionId() != null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isDelete()) {
|
||||||
|
if (Constants.PARAM_DELETE.equals(theRequest.getSecondaryOperation()) == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (theRequest.getSecondaryOperation() != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -20,6 +20,8 @@ package ca.uhn.fhir.rest.method;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
@ -32,7 +34,6 @@ import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
|
@ -41,11 +42,16 @@ import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
import ca.uhn.fhir.rest.annotation.AddTags;
|
||||||
import ca.uhn.fhir.rest.annotation.Create;
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
import ca.uhn.fhir.rest.annotation.Delete;
|
import ca.uhn.fhir.rest.annotation.Delete;
|
||||||
|
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.GetTags;
|
||||||
import ca.uhn.fhir.rest.annotation.History;
|
import ca.uhn.fhir.rest.annotation.History;
|
||||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||||
import ca.uhn.fhir.rest.annotation.Read;
|
import ca.uhn.fhir.rest.annotation.Read;
|
||||||
|
@ -63,6 +69,13 @@ import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionNotSpecifiedException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
|
|
||||||
public abstract class BaseMethodBinding implements IClientResponseHandler {
|
public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
|
@ -72,12 +85,12 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
private List<IParameter> myParameters;
|
private List<IParameter> myParameters;
|
||||||
private Object myProvider;
|
private Object myProvider;
|
||||||
|
|
||||||
public BaseMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
|
public BaseMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||||
assert theMethod != null;
|
assert theMethod != null;
|
||||||
assert theConetxt != null;
|
assert theContext != null;
|
||||||
|
|
||||||
myMethod = theMethod;
|
myMethod = theMethod;
|
||||||
myContext = theConetxt;
|
myContext = theContext;
|
||||||
myProvider = theProvider;
|
myProvider = theProvider;
|
||||||
myParameters = ParameterUtil.getResourceParameters(theMethod);
|
myParameters = ParameterUtil.getResourceParameters(theMethod);
|
||||||
}
|
}
|
||||||
|
@ -108,25 +121,17 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
|
|
||||||
public abstract void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException;
|
public abstract void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException;
|
||||||
|
|
||||||
public abstract boolean matches(Request theRequest);
|
public abstract boolean incomingServerRequestMatchesMethod(Request theRequest);
|
||||||
|
|
||||||
protected IParser createAppropriateParser(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode) throws IOException {
|
protected IParser createAppropriateParserForParsingResponse(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode) {
|
||||||
IParser parser;
|
EncodingEnum encoding = EncodingEnum.forContentType(theResponseMimeType);
|
||||||
if (Constants.CT_ATOM_XML.equals(theResponseMimeType)) {
|
if (encoding==null) {
|
||||||
parser = getContext().newXmlParser();
|
NonFhirResponseException ex = new NonFhirResponseException(theResponseStatusCode, "Response contains non-FHIR content-type: " + theResponseMimeType);
|
||||||
} else if (Constants.CT_FHIR_XML.equals(theResponseMimeType)) {
|
populateException(ex, theResponseReader);
|
||||||
parser = getContext().newXmlParser();
|
throw ex;
|
||||||
} else if (Constants.CT_FHIR_JSON.equals(theResponseMimeType)) {
|
|
||||||
parser = getContext().newJsonParser(); // TODO: move all this so it only happens in one place in the lib, and maybe use a hashmap?
|
|
||||||
} else if ("application/json".equals(theResponseMimeType)) {
|
|
||||||
parser = getContext().newJsonParser();
|
|
||||||
} else if ("application/xml".equals(theResponseMimeType)) {
|
|
||||||
parser = getContext().newXmlParser();
|
|
||||||
} else if ("text/xml".equals(theResponseMimeType)) {
|
|
||||||
parser = getContext().newXmlParser();
|
|
||||||
} else {
|
|
||||||
throw new NonFhirResponseException("Response contains non-FHIR content-type: " + theResponseMimeType, theResponseMimeType, theResponseStatusCode, IOUtils.toString(theResponseReader));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IParser parser=encoding.newParser(getContext());
|
||||||
return parser;
|
return parser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,10 +139,10 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
return myParameters;
|
return myParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Object invokeServerMethod(Object theResourceProvider, Object[] theMethodParams) {
|
protected Object invokeServerMethod(Object[] theMethodParams) {
|
||||||
try {
|
try {
|
||||||
Method method = getMethod();
|
Method method = getMethod();
|
||||||
return method.invoke(theResourceProvider, theMethodParams);
|
return method.invoke(getProvider(), theMethodParams);
|
||||||
} catch (InvocationTargetException e) {
|
} catch (InvocationTargetException e) {
|
||||||
if (e.getCause() instanceof BaseServerResponseException) {
|
if (e.getCause() instanceof BaseServerResponseException) {
|
||||||
throw (BaseServerResponseException) e.getCause();
|
throw (BaseServerResponseException) e.getCause();
|
||||||
|
@ -164,8 +169,11 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
Delete delete = theMethod.getAnnotation(Delete.class);
|
Delete delete = theMethod.getAnnotation(Delete.class);
|
||||||
History history = theMethod.getAnnotation(History.class);
|
History history = theMethod.getAnnotation(History.class);
|
||||||
Validate validate = theMethod.getAnnotation(Validate.class);
|
Validate validate = theMethod.getAnnotation(Validate.class);
|
||||||
|
GetTags getTags = theMethod.getAnnotation(GetTags.class);
|
||||||
|
AddTags addTags = theMethod.getAnnotation(AddTags.class);
|
||||||
|
DeleteTags deleteTags = theMethod.getAnnotation(DeleteTags.class);
|
||||||
// ** if you add another annotation above, also add it to the next line:
|
// ** if you add another annotation above, also add it to the next line:
|
||||||
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate)) {
|
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +188,11 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> returnTypeFromMethod = theMethod.getReturnType();
|
Class<?> returnTypeFromMethod = theMethod.getReturnType();
|
||||||
if (MethodOutcome.class.equals(returnTypeFromMethod)) {
|
if (getTags != null) {
|
||||||
|
if (!TagList.class.equals(returnTypeFromMethod)) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' from type " + theMethod.getDeclaringClass().getCanonicalName() + " is annotated with @" + GetTags.class.getSimpleName() + " but does not return type " + TagList.class.getName());
|
||||||
|
}
|
||||||
|
} else if (MethodOutcome.class.equals(returnTypeFromMethod)) {
|
||||||
// returns a method outcome
|
// returns a method outcome
|
||||||
} else if (Bundle.class.equals(returnTypeFromMethod)) {
|
} else if (Bundle.class.equals(returnTypeFromMethod)) {
|
||||||
// returns a bundle
|
// returns a bundle
|
||||||
|
@ -190,7 +202,7 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
returnTypeFromMethod = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(theMethod);
|
returnTypeFromMethod = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(theMethod);
|
||||||
if (!verifyIsValidResourceReturnType(returnTypeFromMethod) && !IResource.class.equals(returnTypeFromMethod)) {
|
if (!verifyIsValidResourceReturnType(returnTypeFromMethod) && !IResource.class.equals(returnTypeFromMethod)) {
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returns a collection with generic type " + toLogString(returnTypeFromMethod)
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' from " + IResourceProvider.class.getSimpleName() + " type " + theMethod.getDeclaringClass().getCanonicalName() + " returns a collection with generic type " + toLogString(returnTypeFromMethod)
|
||||||
+ " - Must return a resource type or a collection (List, Set) of a resource type");
|
+ " - Must return a resource type or a collection (List, Set) with a resource type parameter (e.g. List<Patient> )");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!IResource.class.equals(returnTypeFromMethod) && !verifyIsValidResourceReturnType(returnTypeFromMethod)) {
|
if (!IResource.class.equals(returnTypeFromMethod) && !verifyIsValidResourceReturnType(returnTypeFromMethod)) {
|
||||||
|
@ -214,6 +226,12 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
returnTypeFromAnnotation = update.type();
|
returnTypeFromAnnotation = update.type();
|
||||||
} else if (validate != null) {
|
} else if (validate != null) {
|
||||||
returnTypeFromAnnotation = validate.type();
|
returnTypeFromAnnotation = validate.type();
|
||||||
|
} else if (getTags != null) {
|
||||||
|
returnTypeFromAnnotation = getTags.type();
|
||||||
|
} else if (addTags != null) {
|
||||||
|
returnTypeFromAnnotation = addTags.type();
|
||||||
|
} else if (deleteTags != null) {
|
||||||
|
returnTypeFromAnnotation = deleteTags.type();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnTypeFromRp != null) {
|
if (returnTypeFromRp != null) {
|
||||||
|
@ -259,6 +277,12 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
return new HistoryMethodBinding(theMethod, theContext, theProvider);
|
return new HistoryMethodBinding(theMethod, theContext, theProvider);
|
||||||
} else if (validate != null) {
|
} else if (validate != null) {
|
||||||
return new ValidateMethodBinding(theMethod, theContext, theProvider);
|
return new ValidateMethodBinding(theMethod, theContext, theProvider);
|
||||||
|
} else if (getTags != null) {
|
||||||
|
return new GetTagsMethodBinding(theMethod, theContext, theProvider, getTags);
|
||||||
|
} else if (addTags != null) {
|
||||||
|
return new AddTagsMethodBinding(theMethod, theContext, theProvider, addTags);
|
||||||
|
} else if (deleteTags != null) {
|
||||||
|
return new DeleteTagsMethodBinding(theMethod, theContext, theProvider, deleteTags);
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
throw new ConfigurationException("Did not detect any FHIR annotations on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
||||||
}
|
}
|
||||||
|
@ -285,8 +309,8 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
// return sm;
|
// return sm;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EncodingEnum determineResponseEncoding(HttpServletRequest theRequest, Map<String, String[]> theParams) {
|
public static EncodingEnum determineResponseEncoding(Request theReq) {
|
||||||
String[] format = theParams.remove(Constants.PARAM_FORMAT);
|
String[] format = theReq.getParameters().remove(Constants.PARAM_FORMAT);
|
||||||
if (format != null) {
|
if (format != null) {
|
||||||
for (String nextFormat : format) {
|
for (String nextFormat : format) {
|
||||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
|
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(nextFormat);
|
||||||
|
@ -296,7 +320,7 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Enumeration<String> acceptValues = theRequest.getHeaders("Accept");
|
Enumeration<String> acceptValues = theReq.getServletRequest().getHeaders("Accept");
|
||||||
if (acceptValues != null) {
|
if (acceptValues != null) {
|
||||||
while (acceptValues.hasMoreElements()) {
|
while (acceptValues.hasMoreElements()) {
|
||||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(acceptValues.nextElement());
|
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(acceptValues.nextElement());
|
||||||
|
@ -308,6 +332,27 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
return EncodingEnum.XML;
|
return EncodingEnum.XML;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected IParser createAppropriateParserForParsingServerRequest(Request theRequest) {
|
||||||
|
String contentTypeHeader = theRequest.getServletRequest().getHeader("content-type");
|
||||||
|
EncodingEnum encoding;
|
||||||
|
if (isBlank(contentTypeHeader)) {
|
||||||
|
encoding = EncodingEnum.XML;
|
||||||
|
} else {
|
||||||
|
int semicolon = contentTypeHeader.indexOf(';');
|
||||||
|
if (semicolon!=-1) {
|
||||||
|
contentTypeHeader=contentTypeHeader.substring(0,semicolon);
|
||||||
|
}
|
||||||
|
encoding = EncodingEnum.forContentType(contentTypeHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encoding==null) {
|
||||||
|
throw new InvalidRequestException("Request contins non-FHIR conent-type header value: " + contentTypeHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
IParser parser=encoding.newParser(getContext());
|
||||||
|
return parser;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean verifyMethodHasZeroOrOneOperationAnnotation(Method theNextMethod, Object... theAnnotations) {
|
public static boolean verifyMethodHasZeroOrOneOperationAnnotation(Method theNextMethod, Object... theAnnotations) {
|
||||||
Object obj1 = null;
|
Object obj1 = null;
|
||||||
for (Object object : theAnnotations) {
|
for (Object object : theAnnotations) {
|
||||||
|
@ -349,6 +394,18 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Object[] createParametersForServerRequest(Request theRequest, IResource theResource) {
|
||||||
|
Object[] params = new Object[getParameters().size()];
|
||||||
|
for (int i = 0; i < getParameters().size(); i++) {
|
||||||
|
IParameter param = getParameters().get(i);
|
||||||
|
if (param == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, theResource);
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
protected static List<IResource> toResourceList(Object response) throws InternalErrorException {
|
protected static List<IResource> toResourceList(Object response) throws InternalErrorException {
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
@ -377,4 +434,47 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||||
return prettyPrint;
|
return prettyPrint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
|
||||||
|
|
||||||
|
protected BaseServerResponseException processNon2xxResponseAndReturnExceptionToThrow(int theStatusCode, String theResponseMimeType, Reader theResponseReader) {
|
||||||
|
BaseServerResponseException ex;
|
||||||
|
switch (theStatusCode) {
|
||||||
|
case Constants.STATUS_HTTP_400_BAD_REQUEST:
|
||||||
|
ex = new InvalidRequestException("Server responded with HTTP 400");
|
||||||
|
break;
|
||||||
|
case Constants.STATUS_HTTP_404_NOT_FOUND:
|
||||||
|
ex = new ResourceNotFoundException("Server responded with HTTP 404");
|
||||||
|
break;
|
||||||
|
case Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED:
|
||||||
|
ex = new MethodNotAllowedException("Server responded with HTTP 405");
|
||||||
|
break;
|
||||||
|
case Constants.STATUS_HTTP_409_CONFLICT:
|
||||||
|
ex = new ResourceVersionConflictException("Server responded with HTTP 409");
|
||||||
|
break;
|
||||||
|
case Constants.STATUS_HTTP_412_PRECONDITION_FAILED:
|
||||||
|
ex = new ResourceVersionNotSpecifiedException("Server responded with HTTP 412");
|
||||||
|
break;
|
||||||
|
case Constants.STATUS_HTTP_422_UNPROCESSABLE_ENTITY:
|
||||||
|
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theStatusCode);
|
||||||
|
OperationOutcome operationOutcome = parser.parseResource(OperationOutcome.class, theResponseReader);
|
||||||
|
ex = new UnprocessableEntityException(operationOutcome);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ex = new UnclassifiedServerFailureException(theStatusCode, "Server responded with HTTP " + theStatusCode);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
populateException(ex, theResponseReader);
|
||||||
|
return ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void populateException(BaseServerResponseException theEx, Reader theResponseReader) {
|
||||||
|
try {
|
||||||
|
String responseText = IOUtils.toString(theResponseReader);
|
||||||
|
theEx.setResponseBody(responseText);
|
||||||
|
} catch (IOException e) {
|
||||||
|
ourLog.debug("Failed to read response", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,23 +20,28 @@ package ca.uhn.fhir.rest.method;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PushbackReader;
|
import java.io.PushbackReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.Writer;
|
import java.io.Writer;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Enumeration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
@ -44,22 +49,17 @@ import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||||
import ca.uhn.fhir.rest.param.IParameter;
|
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionNotSpecifiedException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
|
||||||
|
|
||||||
public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
|
public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
|
||||||
|
private static final String LABEL = "label=\"";
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class);
|
||||||
|
private static final String SCHEME = "scheme=\"";
|
||||||
|
|
||||||
private boolean myReturnVoid;
|
private boolean myReturnVoid;
|
||||||
|
|
||||||
public BaseOutcomeReturningMethodBinding(Method theMethod, FhirContext theContext, Class<?> theMethodAnnotation, Object theProvider) {
|
public BaseOutcomeReturningMethodBinding(Method theMethod, FhirContext theContext, Class<?> theMethodAnnotation, Object theProvider) {
|
||||||
|
@ -74,6 +74,25 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||||
|
Set<RequestType> allowableRequestTypes = provideAllowableRequestTypes();
|
||||||
|
RequestType requestType = theRequest.getRequestType();
|
||||||
|
if (!allowableRequestTypes.contains(requestType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!getResourceName().equals(theRequest.getResourceName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getMatchingOperation() == null && StringUtils.isNotBlank(theRequest.getOperation())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getMatchingOperation() != null && !getMatchingOperation().equals(theRequest.getOperation())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||||
switch (theResponseStatusCode) {
|
switch (theResponseStatusCode) {
|
||||||
|
@ -85,51 +104,38 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
||||||
}
|
}
|
||||||
MethodOutcome retVal = process2xxResponse(getContext(), getResourceName(), theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
MethodOutcome retVal = process2xxResponse(getContext(), getResourceName(), theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
||||||
return retVal;
|
return retVal;
|
||||||
case Constants.STATUS_HTTP_400_BAD_REQUEST:
|
|
||||||
throw new InvalidRequestException("Server responded with: " + IOUtils.toString(theResponseReader));
|
|
||||||
case Constants.STATUS_HTTP_404_NOT_FOUND:
|
|
||||||
throw new ResourceNotFoundException("Server responded with: " + IOUtils.toString(theResponseReader));
|
|
||||||
case Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED:
|
|
||||||
throw new MethodNotAllowedException("Server responded with: " + IOUtils.toString(theResponseReader));
|
|
||||||
case Constants.STATUS_HTTP_409_CONFLICT:
|
|
||||||
throw new ResourceVersionConflictException("Server responded with: " + IOUtils.toString(theResponseReader));
|
|
||||||
case Constants.STATUS_HTTP_412_PRECONDITION_FAILED:
|
|
||||||
throw new ResourceVersionNotSpecifiedException("Server responded with: " + IOUtils.toString(theResponseReader));
|
|
||||||
case Constants.STATUS_HTTP_422_UNPROCESSABLE_ENTITY:
|
|
||||||
IParser parser = createAppropriateParser(theResponseMimeType, theResponseReader, theResponseStatusCode);
|
|
||||||
OperationOutcome operationOutcome = parser.parseResource(OperationOutcome.class, theResponseReader);
|
|
||||||
throw new UnprocessableEntityException(operationOutcome);
|
|
||||||
default:
|
default:
|
||||||
throw new UnclassifiedServerFailureException(theResponseStatusCode, IOUtils.toString(theResponseReader));
|
throw processNon2xxResponseAndReturnExceptionToThrow(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
|
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
|
||||||
EncodingEnum encoding = BaseMethodBinding.determineResponseEncoding(theRequest.getServletRequest(), theRequest.getParameters());
|
EncodingEnum encoding = determineResponseEncoding(theRequest);
|
||||||
IParser parser = encoding.newParser(getContext());
|
IParser parser = encoding.newParser(getContext());
|
||||||
IResource resource;
|
IResource resource;
|
||||||
if (requestContainsResource()) {
|
if (requestContainsResource()) {
|
||||||
resource = parser.parseResource(theRequest.getInputReader());
|
resource = parser.parseResource(theRequest.getInputReader());
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
for (Enumeration<String> enumeration = theRequest.getServletRequest().getHeaders(Constants.HEADER_CATEGORY); enumeration.hasMoreElements();) {
|
||||||
|
String nextTagComplete = enumeration.nextElement();
|
||||||
|
StringBuilder next = new StringBuilder(nextTagComplete);
|
||||||
|
parseTagValue(tagList, nextTagComplete, next);
|
||||||
|
}
|
||||||
|
if (tagList.isEmpty() == false) {
|
||||||
|
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
resource = null;
|
resource = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object[] params = new Object[getParameters().size()];
|
Object[] params = createParametersForServerRequest(theRequest, resource);
|
||||||
for (int i = 0; i < getParameters().size(); i++) {
|
|
||||||
IParameter param = getParameters().get(i);
|
|
||||||
if (param == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
params[i] = param.translateQueryParametersIntoServerArgument(theRequest, resource);
|
|
||||||
}
|
|
||||||
|
|
||||||
addParametersForServerRequest(theRequest, params);
|
addParametersForServerRequest(theRequest, params);
|
||||||
|
|
||||||
MethodOutcome response;
|
MethodOutcome response;
|
||||||
try {
|
try {
|
||||||
response = (MethodOutcome) invokeServerMethod(getProvider(), params);
|
response = (MethodOutcome) invokeServerMethod(params);
|
||||||
} catch (InternalErrorException e) {
|
} catch (InternalErrorException e) {
|
||||||
ourLog.error("Internal error during method invocation", e);
|
ourLog.error("Internal error during method invocation", e);
|
||||||
streamOperationOutcome(e, theServer, encoding, theResponse, theRequest);
|
streamOperationOutcome(e, theServer, encoding, theResponse, theRequest);
|
||||||
|
@ -183,10 +189,6 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
||||||
// getMethod().in
|
// getMethod().in
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isReturnVoid() {
|
|
||||||
return myReturnVoid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @Override public void invokeServer(RestfulServer theServer, Request
|
* @Override public void invokeServer(RestfulServer theServer, Request
|
||||||
* theRequest, HttpServletResponse theResponse) throws
|
* theRequest, HttpServletResponse theResponse) throws
|
||||||
|
@ -232,23 +234,8 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
||||||
* writer.close(); } // getMethod().in }
|
* writer.close(); } // getMethod().in }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Override
|
public boolean isReturnVoid() {
|
||||||
public boolean matches(Request theRequest) {
|
return myReturnVoid;
|
||||||
Set<RequestType> allowableRequestTypes = provideAllowableRequestTypes();
|
|
||||||
RequestType requestType = theRequest.getRequestType();
|
|
||||||
if (!allowableRequestTypes.contains(requestType)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!getResourceName().equals(theRequest.getResourceName())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (getMatchingOperation() == null && StringUtils.isNotBlank(theRequest.getOperation())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (getMatchingOperation() != null && !getMatchingOperation().equals(theRequest.getOperation())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addLocationHeader(Request theRequest, HttpServletResponse theResponse, MethodOutcome response) {
|
private void addLocationHeader(Request theRequest, HttpServletResponse theResponse, MethodOutcome response) {
|
||||||
|
@ -265,6 +252,78 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
||||||
theResponse.addHeader("Location", b.toString());
|
theResponse.addHeader("Location", b.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void parseTagValue(TagList theTagList, String theCompleteHeaderValue, StringBuilder theBuffer) {
|
||||||
|
int firstSemicolon = theBuffer.indexOf(";");
|
||||||
|
int deleteTo;
|
||||||
|
if (firstSemicolon == -1) {
|
||||||
|
firstSemicolon = theBuffer.indexOf(",");
|
||||||
|
if (firstSemicolon == -1) {
|
||||||
|
firstSemicolon = theBuffer.length();
|
||||||
|
deleteTo = theBuffer.length();
|
||||||
|
} else {
|
||||||
|
deleteTo = firstSemicolon;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
deleteTo = firstSemicolon + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String term = theBuffer.substring(0, firstSemicolon);
|
||||||
|
String scheme = null;
|
||||||
|
String label = null;
|
||||||
|
if (isBlank(term)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
theBuffer.delete(0, deleteTo);
|
||||||
|
while (theBuffer.length() > 0 && theBuffer.charAt(0) == ' ') {
|
||||||
|
theBuffer.deleteCharAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (theBuffer.length() > 0) {
|
||||||
|
boolean foundSomething = false;
|
||||||
|
if (theBuffer.length() > SCHEME.length() && theBuffer.substring(0, SCHEME.length()).equals(SCHEME)) {
|
||||||
|
int closeIdx = theBuffer.indexOf("\"", SCHEME.length());
|
||||||
|
scheme = theBuffer.substring(SCHEME.length(), closeIdx);
|
||||||
|
theBuffer.delete(0, closeIdx + 1);
|
||||||
|
foundSomething = true;
|
||||||
|
}
|
||||||
|
if (theBuffer.length() > LABEL.length() && theBuffer.substring(0, LABEL.length()).equals(LABEL)) {
|
||||||
|
int closeIdx = theBuffer.indexOf("\"", LABEL.length());
|
||||||
|
label = theBuffer.substring(LABEL.length(), closeIdx);
|
||||||
|
theBuffer.delete(0, closeIdx + 1);
|
||||||
|
foundSomething = true;
|
||||||
|
}
|
||||||
|
// TODO: support enc2231-string as described in
|
||||||
|
// http://tools.ietf.org/html/draft-johnston-http-category-header-02
|
||||||
|
// TODO: support multiple tags in one header as described in
|
||||||
|
// http://hl7.org/implement/standards/fhir/http.html#tags
|
||||||
|
|
||||||
|
while (theBuffer.length() > 0 && (theBuffer.charAt(0) == ' ' || theBuffer.charAt(0) == ';')) {
|
||||||
|
theBuffer.deleteCharAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundSomething) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theBuffer.length() > 0 && theBuffer.charAt(0) == ',') {
|
||||||
|
theBuffer.deleteCharAt(0);
|
||||||
|
while (theBuffer.length() > 0 && theBuffer.charAt(0) == ' ') {
|
||||||
|
theBuffer.deleteCharAt(0);
|
||||||
|
}
|
||||||
|
theTagList.add(new Tag(term, label, scheme));
|
||||||
|
parseTagValue(theTagList, theCompleteHeaderValue, theBuffer);
|
||||||
|
} else {
|
||||||
|
theTagList.add(new Tag(term, label, scheme));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theBuffer.length() > 0) {
|
||||||
|
ourLog.warn("Ignoring extra text at the end of " + Constants.HEADER_CATEGORY + " tag '" + theBuffer.toString() + "' - Complete tag value was: " + theCompleteHeaderValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract void addParametersForServerRequest(Request theRequest, Object[] theParams);
|
protected abstract void addParametersForServerRequest(Request theRequest, Object[] theParams);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -22,13 +22,19 @@ package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.ConfigurationException;
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||||
import ca.uhn.fhir.rest.param.IParameter;
|
import ca.uhn.fhir.rest.param.IParameter;
|
||||||
import ca.uhn.fhir.rest.param.ResourceParameter;
|
import ca.uhn.fhir.rest.param.ResourceParameter;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
|
||||||
|
@ -88,7 +94,18 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
|
||||||
throw new NullPointerException("Resource can not be null");
|
throw new NullPointerException("Resource can not be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
return createClientInvocation(theArgs, resource);
|
BaseClientInvocation retVal = createClientInvocation(theArgs, resource);
|
||||||
|
|
||||||
|
TagList list = (TagList) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||||
|
if (list != null) {
|
||||||
|
for (Tag tag : list) {
|
||||||
|
if (StringUtils.isNotBlank(tag.getTerm())) {
|
||||||
|
retVal.addHeader(Constants.HEADER_CATEGORY, tag.toHeaderValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ package ca.uhn.fhir.rest.method;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
@ -56,7 +56,6 @@ import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.parser.IParser;
|
import ca.uhn.fhir.parser.IParser;
|
||||||
import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
|
import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
|
||||||
import ca.uhn.fhir.rest.param.IParameter;
|
import ca.uhn.fhir.rest.param.IParameter;
|
||||||
import ca.uhn.fhir.rest.param.ParameterUtil;
|
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
@ -92,8 +91,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
} else if (Bundle.class.isAssignableFrom(methodReturnType)) {
|
} else if (Bundle.class.isAssignableFrom(methodReturnType)) {
|
||||||
myMethodReturnType = MethodReturnTypeEnum.BUNDLE;
|
myMethodReturnType = MethodReturnTypeEnum.BUNDLE;
|
||||||
} else {
|
} else {
|
||||||
throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: "
|
throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
||||||
+ theMethod.getDeclaringClass().getCanonicalName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
myResourceType = theReturnResourceType;
|
myResourceType = theReturnResourceType;
|
||||||
|
@ -119,14 +117,14 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException {
|
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException {
|
||||||
IParser parser = createAppropriateParser(theResponseMimeType, theResponseReader, theResponseStatusCode);
|
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theResponseStatusCode);
|
||||||
|
|
||||||
switch (getReturnType()) {
|
switch (getReturnType()) {
|
||||||
case BUNDLE: {
|
case BUNDLE: {
|
||||||
Bundle bundle;
|
Bundle bundle;
|
||||||
if (myResourceType != null) {
|
if (myResourceType != null) {
|
||||||
bundle = parser.parseBundle(myResourceType, theResponseReader);
|
bundle = parser.parseBundle(myResourceType, theResponseReader);
|
||||||
}else {
|
} else {
|
||||||
bundle = parser.parseBundle(theResponseReader);
|
bundle = parser.parseBundle(theResponseReader);
|
||||||
}
|
}
|
||||||
switch (getMethodReturnType()) {
|
switch (getMethodReturnType()) {
|
||||||
|
@ -157,10 +155,11 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
List<String> lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE);
|
List<String> lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE);
|
||||||
if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) {
|
if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) {
|
||||||
try {
|
try {
|
||||||
InstantDt lmValue = new InstantDt(lmHeaders.get(0));
|
InstantDt lmValue = new InstantDt(lmHeaders.get(0));
|
||||||
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, lmValue);
|
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, lmValue);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// TODO: This shouldn't be thrown - Time format is not in InstandDt format for this header, find examples online
|
// TODO: This shouldn't be thrown - Time format is not in
|
||||||
|
// InstandDt format for this header, find examples online
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,14 +178,14 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
throw new IllegalStateException("Should not get here!");
|
throw new IllegalStateException("Should not get here!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract List<IResource> invokeServer(Object theResourceProvider, Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException;
|
public abstract List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
|
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
|
||||||
|
|
||||||
// Pretty print
|
// Pretty print
|
||||||
boolean prettyPrint = prettyPrintResponse(theRequest);
|
boolean prettyPrint = prettyPrintResponse(theRequest);
|
||||||
|
|
||||||
// Narrative mode
|
// Narrative mode
|
||||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||||
String[] narrative = requestParams.remove(Constants.PARAM_NARRATIVE);
|
String[] narrative = requestParams.remove(Constants.PARAM_NARRATIVE);
|
||||||
|
@ -199,7 +198,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine response encoding
|
// Determine response encoding
|
||||||
EncodingEnum responseEncoding = determineResponseEncoding(theRequest.getServletRequest(), requestParams);
|
EncodingEnum responseEncoding = determineResponseEncoding(theRequest);
|
||||||
|
|
||||||
// Is this request coming from a browser
|
// Is this request coming from a browser
|
||||||
String uaHeader = theRequest.getServletRequest().getHeader("user-agent");
|
String uaHeader = theRequest.getServletRequest().getHeader("user-agent");
|
||||||
|
@ -217,7 +216,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<IResource> result = invokeServer(getProvider(), theRequest, params);
|
List<IResource> result = invokeServer(theRequest, params);
|
||||||
switch (getReturnType()) {
|
switch (getReturnType()) {
|
||||||
case BUNDLE:
|
case BUNDLE:
|
||||||
streamResponseAsBundle(theServer, theResponse, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode);
|
streamResponseAsBundle(theServer, theResponse, result, responseEncoding, theRequest.getFhirServerBase(), theRequest.getCompleteUrl(), prettyPrint, requestIsBrowser, narrativeMode);
|
||||||
|
@ -233,7 +232,6 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
|
protected static IdDt getIdFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
|
||||||
Object retValObj = theResourceMetadata.get(theKey);
|
Object retValObj = theResourceMetadata.get(theKey);
|
||||||
if (retValObj == null) {
|
if (retValObj == null) {
|
||||||
|
@ -251,8 +249,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
return (IdDt) retValObj;
|
return (IdDt) retValObj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
|
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName());
|
||||||
+ IdDt.class.getCanonicalName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
|
private InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
|
||||||
|
@ -268,8 +265,21 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
return (InstantDt) retValObj;
|
return (InstantDt) retValObj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
|
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName());
|
||||||
+ InstantDt.class.getCanonicalName());
|
}
|
||||||
|
|
||||||
|
private TagList getTagListFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
|
||||||
|
Object retValObj = theResourceMetadata.get(theKey);
|
||||||
|
if (retValObj == null) {
|
||||||
|
return null;
|
||||||
|
} else if (retValObj instanceof TagList) {
|
||||||
|
if (((TagList) retValObj).isEmpty()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return (TagList) retValObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + TagList.class.getCanonicalName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private IParser getNewParser(EncodingEnum theResponseEncoding, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
|
private IParser getNewParser(EncodingEnum theResponseEncoding, boolean thePrettyPrint, NarrativeModeEnum theNarrativeMode) {
|
||||||
|
@ -286,8 +296,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == NarrativeModeEnum.SUPPRESS);
|
return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == NarrativeModeEnum.SUPPRESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, List<IResource> theResult, EncodingEnum theResponseEncoding, String theServerBase,
|
private void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, List<IResource> theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser,
|
||||||
String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
|
NarrativeModeEnum theNarrativeMode) throws IOException {
|
||||||
assert !theServerBase.endsWith("/");
|
assert !theServerBase.endsWith("/");
|
||||||
|
|
||||||
theHttpResponse.setStatus(200);
|
theHttpResponse.setStatus(200);
|
||||||
|
@ -300,7 +310,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
theHttpResponse.setContentType(theResponseEncoding.getBundleContentType());
|
theHttpResponse.setContentType(theResponseEncoding.getBundleContentType());
|
||||||
}
|
}
|
||||||
|
|
||||||
theHttpResponse.setCharacterEncoding("UTF-8");
|
theHttpResponse.setCharacterEncoding(Constants.CHARSET_UTF_8);
|
||||||
|
|
||||||
theServer.addHeadersToResponse(theHttpResponse);
|
theServer.addHeadersToResponse(theHttpResponse);
|
||||||
|
|
||||||
|
@ -325,7 +335,6 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(next);
|
RuntimeResourceDefinition def = getContext().getResourceDefinition(next);
|
||||||
|
|
||||||
if (next.getId() != null && StringUtils.isNotBlank(next.getId().getValue())) {
|
if (next.getId() != null && StringUtils.isNotBlank(next.getId().getValue())) {
|
||||||
|
@ -341,10 +350,10 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
b.append(resId);
|
b.append(resId);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If this is a history operation, we add the version of the resource to the self link to indicate the version
|
* If this is a history operation, we add the version of the
|
||||||
|
* resource to the self link to indicate the version
|
||||||
*/
|
*/
|
||||||
if (getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_INSTANCE || getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_TYPE
|
if (getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_INSTANCE || getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_TYPE || getSystemOperationType() == RestfulOperationSystemEnum.HISTORY_SYSTEM) {
|
||||||
|| getSystemOperationType() == RestfulOperationSystemEnum.HISTORY_SYSTEM) {
|
|
||||||
IdDt versionId = getIdFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.VERSION_ID);
|
IdDt versionId = getIdFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.VERSION_ID);
|
||||||
if (versionId != null) {
|
if (versionId != null) {
|
||||||
b.append('/');
|
b.append('/');
|
||||||
|
@ -368,6 +377,13 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
entry.setUpdated(updated);
|
entry.setUpdated(updated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TagList tagList = getTagListFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.TAG_LIST);
|
||||||
|
if (tagList!=null) {
|
||||||
|
for (Tag nextTag : tagList) {
|
||||||
|
entry.addCategory(nextTag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boolean haveQ = false;
|
boolean haveQ = false;
|
||||||
if (thePrettyPrint) {
|
if (thePrettyPrint) {
|
||||||
b.append('?').append(Constants.PARAM_PRETTY).append("=true");
|
b.append('?').append(Constants.PARAM_PRETTY).append("=true");
|
||||||
|
@ -406,8 +422,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
private void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
|
||||||
boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
|
|
||||||
|
|
||||||
theHttpResponse.setStatus(200);
|
theHttpResponse.setStatus(200);
|
||||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||||
|
@ -417,7 +432,9 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
} else {
|
} else {
|
||||||
theHttpResponse.setContentType(theResponseEncoding.getResourceContentType());
|
theHttpResponse.setContentType(theResponseEncoding.getResourceContentType());
|
||||||
}
|
}
|
||||||
theHttpResponse.setCharacterEncoding("UTF-8");
|
theHttpResponse.setCharacterEncoding(Constants.CHARSET_UTF_8);
|
||||||
|
|
||||||
|
theServer.addHeadersToResponse(theHttpResponse);
|
||||||
|
|
||||||
InstantDt lastUpdated = getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ResourceMetadataKeyEnum.UPDATED);
|
InstantDt lastUpdated = getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ResourceMetadataKeyEnum.UPDATED);
|
||||||
if (lastUpdated != null) {
|
if (lastUpdated != null) {
|
||||||
|
@ -432,7 +449,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintWriter writer = theHttpResponse.getWriter();
|
PrintWriter writer = theHttpResponse.getWriter();
|
||||||
try {
|
try {
|
||||||
if (theNarrativeMode == NarrativeModeEnum.ONLY) {
|
if (theNarrativeMode == NarrativeModeEnum.ONLY) {
|
||||||
|
|
|
@ -33,8 +33,8 @@ import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||||
import ca.uhn.fhir.rest.client.GetClientInvocation;
|
import ca.uhn.fhir.rest.client.GetClientInvocation;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||||
import ca.uhn.fhir.rest.param.IParameter;
|
import ca.uhn.fhir.rest.param.IParameter;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|
||||||
|
|
||||||
public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding {
|
public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
|
|
||||||
|
@ -71,19 +71,13 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<IResource> invokeServer(Object theResourceProvider, Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
public List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||||
IResource conf;
|
IResource conf = (IResource) invokeServerMethod(theMethodParams);
|
||||||
try {
|
|
||||||
conf = (Conformance) invokeServerMethod(theResourceProvider, theMethodParams);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new InternalErrorException("Failed to call access method", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Collections.singletonList(conf);
|
return Collections.singletonList(conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Request theRequest) {
|
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||||
if (theRequest.getRequestType() == RequestType.OPTIONS) {
|
if (theRequest.getRequestType() == RequestType.OPTIONS) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,9 +39,9 @@ import ca.uhn.fhir.rest.client.DeleteClientInvocation;
|
||||||
import ca.uhn.fhir.rest.client.PostClientInvocation;
|
import ca.uhn.fhir.rest.client.PostClientInvocation;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||||
import ca.uhn.fhir.rest.param.IParameter;
|
import ca.uhn.fhir.rest.param.IParameter;
|
||||||
|
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|
||||||
|
|
||||||
public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
||||||
|
|
||||||
|
@ -67,12 +67,12 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
myIdParameterIndex = Util.findIdParameterIndex(theMethod);
|
myIdParameterIndex = ParameterUtil.findIdParameterIndex(theMethod);
|
||||||
if (myIdParameterIndex == null) {
|
if (myIdParameterIndex == null) {
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has no parameter annotated with the @" + IdParam.class.getSimpleName() + " annotation");
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has no parameter annotated with the @" + IdParam.class.getSimpleName() + " annotation");
|
||||||
}
|
}
|
||||||
|
|
||||||
Integer versionIdParameterIndex = Util.findVersionIdParameterIndex(theMethod);
|
Integer versionIdParameterIndex = ParameterUtil.findVersionIdParameterIndex(theMethod);
|
||||||
if (versionIdParameterIndex != null) {
|
if (versionIdParameterIndex != null) {
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has a parameter annotated with the @" + VersionIdParam.class.getSimpleName()
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has a parameter annotated with the @" + VersionIdParam.class.getSimpleName()
|
||||||
+ " annotation but delete methods may not have this annotation");
|
+ " annotation but delete methods may not have this annotation");
|
||||||
|
@ -118,12 +118,6 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
||||||
return myResourceName;
|
return myResourceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @Override
|
|
||||||
// public boolean matches(Request theRequest) {
|
|
||||||
// // TODO Auto-generated method stub
|
|
||||||
// return super.matches(theRequest);
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||||
IdDt idDt = (IdDt) theArgs[myIdParameterIndex];
|
IdDt idDt = (IdDt) theArgs[myIdParameterIndex];
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
||||||
|
|
||||||
|
public class DeleteTagsMethodBinding extends BaseAddOrDeleteTagsMethodBinding {
|
||||||
|
|
||||||
|
public DeleteTagsMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider, DeleteTags theDeleteTags) {
|
||||||
|
super(theMethod, theConetxt, theProvider, theDeleteTags.type());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isDelete() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,182 @@
|
||||||
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.ConfigurationException;
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationSystemEnum;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.RestfulOperationTypeEnum;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.parser.IParser;
|
||||||
|
import ca.uhn.fhir.rest.annotation.GetTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||||
|
import ca.uhn.fhir.rest.client.GetClientInvocation;
|
||||||
|
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||||
|
import ca.uhn.fhir.rest.param.IParameter;
|
||||||
|
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
|
||||||
|
public class GetTagsMethodBinding extends BaseMethodBinding {
|
||||||
|
|
||||||
|
private Class<? extends IResource> myType;
|
||||||
|
private Integer myIdParamIndex;
|
||||||
|
private Integer myVersionIdParamIndex;
|
||||||
|
private String myResourceName;
|
||||||
|
|
||||||
|
public GetTagsMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider, GetTags theAnnotation) {
|
||||||
|
super(theMethod, theConetxt, theProvider);
|
||||||
|
|
||||||
|
if (theProvider instanceof IResourceProvider) {
|
||||||
|
myType = ((IResourceProvider) theProvider).getResourceType();
|
||||||
|
} else {
|
||||||
|
myType = theAnnotation.type();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IResource.class.equals(myType)) {
|
||||||
|
myResourceName = theConetxt.getResourceDefinition(myType).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
myIdParamIndex = ParameterUtil.findIdParameterIndex(theMethod);
|
||||||
|
myVersionIdParamIndex = ParameterUtil.findVersionIdParameterIndex(theMethod);
|
||||||
|
|
||||||
|
if (myIdParamIndex != null && myType.equals(IResource.class)) {
|
||||||
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' does not specify a resource type, but has an @" + IdParam.class.getSimpleName() + " parameter. Please specity a resource type in the @" + GetTags.class.getSimpleName() + " annotation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||||
|
if (theResponseStatusCode == Constants.STATUS_HTTP_200_OK) {
|
||||||
|
IParser parser = createAppropriateParserForParsingResponse(theResponseMimeType, theResponseReader, theResponseStatusCode);
|
||||||
|
TagList retVal = parser.parseTagList(theResponseReader);
|
||||||
|
return retVal;
|
||||||
|
} else {
|
||||||
|
throw processNon2xxResponseAndReturnExceptionToThrow(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getResourceName() {
|
||||||
|
return myResourceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestfulOperationTypeEnum getResourceOperationType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RestfulOperationSystemEnum getSystemOperationType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||||
|
GetClientInvocation retVal;
|
||||||
|
|
||||||
|
IdDt id = null;
|
||||||
|
IdDt versionId = null;
|
||||||
|
if (myIdParamIndex != null) {
|
||||||
|
id = (IdDt) theArgs[myIdParamIndex];
|
||||||
|
if (myVersionIdParamIndex != null) {
|
||||||
|
versionId = (IdDt) theArgs[myVersionIdParamIndex];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (myType != IResource.class) {
|
||||||
|
if (id != null) {
|
||||||
|
if (versionId != null) {
|
||||||
|
retVal = new GetClientInvocation(getResourceName(), id.getValue(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS);
|
||||||
|
} else {
|
||||||
|
retVal = new GetClientInvocation(getResourceName(), id.getValue(), Constants.PARAM_TAGS);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
retVal = new GetClientInvocation(getResourceName(), Constants.PARAM_TAGS);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
retVal = new GetClientInvocation(Constants.PARAM_TAGS);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theArgs != null) {
|
||||||
|
for (int idx = 0; idx < theArgs.length; idx++) {
|
||||||
|
IParameter nextParam = getParameters().get(idx);
|
||||||
|
nextParam.translateClientArgumentIntoQueryArgument(theArgs[idx], null, retVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
|
||||||
|
Object[] params = createParametersForServerRequest(theRequest, null);
|
||||||
|
|
||||||
|
if (myIdParamIndex != null) {
|
||||||
|
params[myIdParamIndex] = theRequest.getId();
|
||||||
|
}
|
||||||
|
if (myVersionIdParamIndex != null) {
|
||||||
|
params[myVersionIdParamIndex] = theRequest.getVersionId();
|
||||||
|
}
|
||||||
|
|
||||||
|
TagList resp = (TagList) invokeServerMethod(params);
|
||||||
|
|
||||||
|
EncodingEnum responseEncoding = determineResponseEncoding(theRequest);
|
||||||
|
|
||||||
|
theResponse.setContentType(responseEncoding.getResourceContentType());
|
||||||
|
theResponse.setStatus(Constants.STATUS_HTTP_200_OK);
|
||||||
|
theResponse.setCharacterEncoding(Constants.CHARSET_UTF_8);
|
||||||
|
|
||||||
|
theServer.addHeadersToResponse(theResponse);
|
||||||
|
|
||||||
|
IParser parser = responseEncoding.newParser(getContext());
|
||||||
|
parser.setPrettyPrint(prettyPrintResponse(theRequest));
|
||||||
|
PrintWriter writer = theResponse.getWriter();
|
||||||
|
try {
|
||||||
|
parser.encodeTagListToWriter(resp, writer);
|
||||||
|
} finally {
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||||
|
if (theRequest.getRequestType()!=RequestType.GET) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!Constants.PARAM_TAGS.equals(theRequest.getOperation())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (myResourceName == null) {
|
||||||
|
if (getResourceName() != null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (!myResourceName.equals(theRequest.getResourceName())) {
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
if ((myIdParamIndex != null) != (theRequest.getId() != null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((myVersionIdParamIndex != null) != (theRequest.getVersionId() != null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -37,6 +37,7 @@ import ca.uhn.fhir.rest.annotation.History;
|
||||||
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||||
import ca.uhn.fhir.rest.client.GetClientInvocation;
|
import ca.uhn.fhir.rest.client.GetClientInvocation;
|
||||||
import ca.uhn.fhir.rest.param.IParameter;
|
import ca.uhn.fhir.rest.param.IParameter;
|
||||||
|
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
@ -52,7 +53,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
public HistoryMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
|
public HistoryMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
|
||||||
super(toReturnType(theMethod, theProvider), theMethod, theConetxt, theProvider);
|
super(toReturnType(theMethod, theProvider), theMethod, theConetxt, theProvider);
|
||||||
|
|
||||||
myIdParamIndex = Util.findIdParameterIndex(theMethod);
|
myIdParamIndex = ParameterUtil.findIdParameterIndex(theMethod);
|
||||||
|
|
||||||
History historyAnnotation = theMethod.getAnnotation(History.class);
|
History historyAnnotation = theMethod.getAnnotation(History.class);
|
||||||
Class<? extends IResource> type = historyAnnotation.type();
|
Class<? extends IResource> type = historyAnnotation.type();
|
||||||
|
@ -142,12 +143,12 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<IResource> invokeServer(Object theResourceProvider, Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
public List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
if (myIdParamIndex != null) {
|
if (myIdParamIndex != null) {
|
||||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
Object response = invokeServerMethod(theResourceProvider, theMethodParams);
|
Object response = invokeServerMethod(theMethodParams);
|
||||||
|
|
||||||
List<IResource> resources = toResourceList(response);
|
List<IResource> resources = toResourceList(response);
|
||||||
int index=0;
|
int index=0;
|
||||||
|
@ -168,7 +169,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
// ObjectUtils.equals is replaced by a JDK7 method..
|
// ObjectUtils.equals is replaced by a JDK7 method..
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Request theRequest) {
|
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||||
if (!Constants.PARAM_HISTORY.equals(theRequest.getOperation())) {
|
if (!Constants.PARAM_HISTORY.equals(theRequest.getOperation())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -185,7 +186,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theRequest.getVersion() != null && !theRequest.getVersion().isEmpty()) {
|
if (theRequest.getVersionId() != null && !theRequest.getVersionId().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.rest.client.GetClientInvocation;
|
import ca.uhn.fhir.rest.client.GetClientInvocation;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||||
import ca.uhn.fhir.rest.param.IParameter;
|
import ca.uhn.fhir.rest.param.IParameter;
|
||||||
|
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
@ -50,8 +51,8 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
|
|
||||||
Validate.notNull(theMethod, "Method must not be null");
|
Validate.notNull(theMethod, "Method must not be null");
|
||||||
|
|
||||||
Integer idIndex = Util.findIdParameterIndex(theMethod);
|
Integer idIndex = ParameterUtil.findIdParameterIndex(theMethod);
|
||||||
Integer versionIdIndex = Util.findVersionIdParameterIndex(theMethod);
|
Integer versionIdIndex = ParameterUtil.findVersionIdParameterIndex(theMethod);
|
||||||
|
|
||||||
myIdIndex = idIndex;
|
myIdIndex = idIndex;
|
||||||
myVersionIdIndex = versionIdIndex;
|
myVersionIdIndex = versionIdIndex;
|
||||||
|
@ -71,7 +72,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Request theRequest) {
|
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||||
if (!theRequest.getResourceName().equals(getResourceName())) {
|
if (!theRequest.getResourceName().equals(getResourceName())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -80,7 +81,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((theRequest.getVersion() == null) != (myVersionIdIndex == null)) {
|
if ((theRequest.getVersionId() == null) != (myVersionIdIndex == null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (theRequest.getId() == null) {
|
if (theRequest.getId() == null) {
|
||||||
|
@ -106,13 +107,13 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<IResource> invokeServer(Object theResourceProvider, Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
public List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
theMethodParams[myIdIndex] = theRequest.getId();
|
theMethodParams[myIdIndex] = theRequest.getId();
|
||||||
if (myVersionIdIndex != null) {
|
if (myVersionIdIndex != null) {
|
||||||
theMethodParams[myVersionIdIndex] = theRequest.getVersion();
|
theMethodParams[myVersionIdIndex] = theRequest.getVersionId();
|
||||||
}
|
}
|
||||||
|
|
||||||
Object response = invokeServerMethod(theResourceProvider, theMethodParams);
|
Object response = invokeServerMethod(theMethodParams);
|
||||||
|
|
||||||
return toResourceList(response);
|
return toResourceList(response);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ public class Request {
|
||||||
private Map<String, String[]> myParameters;
|
private Map<String, String[]> myParameters;
|
||||||
private RequestType myRequestType;
|
private RequestType myRequestType;
|
||||||
private String myResourceName;
|
private String myResourceName;
|
||||||
|
private String mySecondaryOperation;
|
||||||
private HttpServletRequest myServletRequest;
|
private HttpServletRequest myServletRequest;
|
||||||
private HttpServletResponse myServletResponse;
|
private HttpServletResponse myServletResponse;
|
||||||
private IdDt myVersion;
|
private IdDt myVersion;
|
||||||
|
@ -77,6 +78,10 @@ public class Request {
|
||||||
return myResourceName;
|
return myResourceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getSecondaryOperation() {
|
||||||
|
return mySecondaryOperation;
|
||||||
|
}
|
||||||
|
|
||||||
public HttpServletRequest getServletRequest() {
|
public HttpServletRequest getServletRequest() {
|
||||||
return myServletRequest;
|
return myServletRequest;
|
||||||
}
|
}
|
||||||
|
@ -85,7 +90,7 @@ public class Request {
|
||||||
return myServletResponse;
|
return myServletResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IdDt getVersion() {
|
public IdDt getVersionId() {
|
||||||
return myVersion;
|
return myVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,6 +126,10 @@ public class Request {
|
||||||
myResourceName = theResourceName;
|
myResourceName = theResourceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSecondaryOperation(String theSecondaryOperation) {
|
||||||
|
mySecondaryOperation = theSecondaryOperation;
|
||||||
|
}
|
||||||
|
|
||||||
public void setServletRequest(HttpServletRequest theRequest) {
|
public void setServletRequest(HttpServletRequest theRequest) {
|
||||||
myServletRequest = theRequest;
|
myServletRequest = theRequest;
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,24 +103,24 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<IResource> invokeServer(Object theResourceProvider, Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
public List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||||
assert theRequest.getId() == null;
|
assert theRequest.getId() == null;
|
||||||
assert theRequest.getVersion() == null;
|
assert theRequest.getVersionId() == null;
|
||||||
|
|
||||||
Object response = invokeServerMethod(theResourceProvider, theMethodParams);
|
Object response = invokeServerMethod(theMethodParams);
|
||||||
|
|
||||||
return toResourceList(response);
|
return toResourceList(response);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Request theRequest) {
|
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||||
if (!theRequest.getResourceName().equals(getResourceName())) {
|
if (!theRequest.getResourceName().equals(getResourceName())) {
|
||||||
ourLog.trace("Method {} doesn't match because resource name {} != {}", getMethod().getName(), theRequest.getResourceName(), getResourceName());
|
ourLog.trace("Method {} doesn't match because resource name {} != {}", getMethod().getName(), theRequest.getResourceName(), getResourceName());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (theRequest.getId() != null || theRequest.getVersion() != null) {
|
if (theRequest.getId() != null || theRequest.getVersionId() != null) {
|
||||||
ourLog.trace("Method {} doesn't match because ID or Version are not null: {} - {}", theRequest.getId(), theRequest.getVersion());
|
ourLog.trace("Method {} doesn't match because ID or Version are not null: {} - {}", theRequest.getId(), theRequest.getVersionId());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (theRequest.getRequestType() == RequestType.GET && theRequest.getOperation() != null && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
|
if (theRequest.getRequestType() == RequestType.GET && theRequest.getOperation() != null && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||||
import ca.uhn.fhir.rest.client.PutClientInvocation;
|
import ca.uhn.fhir.rest.client.PutClientInvocation;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||||
import ca.uhn.fhir.rest.param.IParameter;
|
import ca.uhn.fhir.rest.param.IParameter;
|
||||||
|
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
|
@ -53,11 +54,11 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
||||||
public UpdateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
public UpdateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||||
super(theMethod, theContext, Update.class, theProvider);
|
super(theMethod, theContext, Update.class, theProvider);
|
||||||
|
|
||||||
myIdParameterIndex = Util.findIdParameterIndex(theMethod);
|
myIdParameterIndex = ParameterUtil.findIdParameterIndex(theMethod);
|
||||||
if (myIdParameterIndex == null) {
|
if (myIdParameterIndex == null) {
|
||||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has no parameter annotated with the @" + IdParam.class.getSimpleName() + " annotation");
|
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has no parameter annotated with the @" + IdParam.class.getSimpleName() + " annotation");
|
||||||
}
|
}
|
||||||
myVersionIdParameterIndex = Util.findVersionIdParameterIndex(theMethod);
|
myVersionIdParameterIndex = ParameterUtil.findVersionIdParameterIndex(theMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -89,13 +90,9 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we should send an HTTP 412 automatically if the server
|
|
||||||
// only has a method which requires a version ID, and no
|
|
||||||
// Content-Location header is present
|
|
||||||
|
|
||||||
theParams[myIdParameterIndex] = theRequest.getId();
|
theParams[myIdParameterIndex] = theRequest.getId();
|
||||||
if (myVersionIdParameterIndex != null) {
|
if (myVersionIdParameterIndex != null) {
|
||||||
theParams[myVersionIdParameterIndex] = theRequest.getVersion();
|
theParams[myVersionIdParameterIndex] = theRequest.getVersionId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,10 +143,10 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(Request theRequest) {
|
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||||
if (super.matches(theRequest)) {
|
if (super.incomingServerRequestMatchesMethod(theRequest)) {
|
||||||
if (myVersionIdParameterIndex != null) {
|
if (myVersionIdParameterIndex != null) {
|
||||||
if (theRequest.getVersion() == null) {
|
if (theRequest.getVersionId() == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,15 +21,10 @@ package ca.uhn.fhir.rest.method;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
|
||||||
import ca.uhn.fhir.rest.annotation.VersionIdParam;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by dsotnikov on 2/25/2014.
|
* Created by dsotnikov on 2/25/2014.
|
||||||
*/
|
*/
|
||||||
|
@ -38,32 +33,6 @@ class Util {
|
||||||
// return findParamIndex(theMethod, Count.class);
|
// return findParamIndex(theMethod, Count.class);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
public static Integer findIdParameterIndex(Method theMethod) {
|
|
||||||
return findParamIndex(theMethod, IdParam.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Integer findParamIndex(Method theMethod, Class<?> toFind) {
|
|
||||||
int paramIndex = 0;
|
|
||||||
for (Annotation[] annotations : theMethod.getParameterAnnotations()) {
|
|
||||||
for (int annotationIndex = 0; annotationIndex < annotations.length; annotationIndex++) {
|
|
||||||
Annotation nextAnnotation = annotations[annotationIndex];
|
|
||||||
Class<? extends Annotation> class1 = nextAnnotation.getClass();
|
|
||||||
if (toFind.isAssignableFrom(class1)) {
|
|
||||||
return paramIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
paramIndex++;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// public static Integer findSinceParameterIndex(Method theMethod) {
|
|
||||||
// return findParamIndex(theMethod, Since.class);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public static Integer findVersionIdParameterIndex(Method theMethod) {
|
|
||||||
return findParamIndex(theMethod, VersionIdParam.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map<String, String> getQueryParams(String query) {
|
public static Map<String, String> getQueryParams(String query) {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -34,6 +34,7 @@ import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||||
import ca.uhn.fhir.rest.client.PostClientInvocation;
|
import ca.uhn.fhir.rest.client.PostClientInvocation;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding.RequestType;
|
||||||
import ca.uhn.fhir.rest.param.IParameter;
|
import ca.uhn.fhir.rest.param.IParameter;
|
||||||
|
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
|
||||||
public class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
public class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||||
|
@ -43,7 +44,7 @@ public class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWith
|
||||||
public ValidateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
public ValidateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||||
super(theMethod, theContext, Validate.class, theProvider);
|
super(theMethod, theContext, Validate.class, theProvider);
|
||||||
|
|
||||||
myIdParameterIndex = Util.findIdParameterIndex(theMethod);
|
myIdParameterIndex = ParameterUtil.findIdParameterIndex(theMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -27,7 +27,6 @@ import java.util.Map;
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
||||||
import ca.uhn.fhir.rest.client.PutClientInvocation;
|
|
||||||
import ca.uhn.fhir.rest.method.Request;
|
import ca.uhn.fhir.rest.method.Request;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
|
|
@ -27,6 +27,7 @@ import java.lang.reflect.Method;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -44,6 +45,7 @@ import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.PathSpecification;
|
import ca.uhn.fhir.model.api.PathSpecification;
|
||||||
import ca.uhn.fhir.model.api.TagList;
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.api.annotation.Description;
|
import ca.uhn.fhir.model.api.annotation.Description;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.TagListParam;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.model.primitive.IntegerDt;
|
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||||
import ca.uhn.fhir.rest.annotation.Count;
|
import ca.uhn.fhir.rest.annotation.Count;
|
||||||
|
@ -59,6 +61,84 @@ import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
|
|
||||||
public class ParameterUtil {
|
public class ParameterUtil {
|
||||||
|
|
||||||
|
private static final Set<Class<?>> BINDABLE_INTEGER_TYPES;
|
||||||
|
private static final Set<Class<?>> BINDABLE_TIME_TYPES;
|
||||||
|
|
||||||
|
static {
|
||||||
|
HashSet<Class<?>> intTypes = new HashSet<Class<?>>();
|
||||||
|
intTypes.add(IntegerDt.class);
|
||||||
|
intTypes.add(Integer.class);
|
||||||
|
BINDABLE_INTEGER_TYPES = Collections.unmodifiableSet(intTypes);
|
||||||
|
|
||||||
|
HashSet<Class<?>> timeTypes = new HashSet<Class<?>>();
|
||||||
|
timeTypes.add(InstantDt.class);
|
||||||
|
timeTypes.add(Date.class);
|
||||||
|
timeTypes.add(Calendar.class);
|
||||||
|
BINDABLE_TIME_TYPES = Collections.unmodifiableSet(timeTypes);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer findIdParameterIndex(Method theMethod) {
|
||||||
|
return findParamAnnotationIndex(theMethod, IdParam.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
// public static Integer findSinceParameterIndex(Method theMethod) {
|
||||||
|
// return findParamIndex(theMethod, Since.class);
|
||||||
|
// }
|
||||||
|
|
||||||
|
public static Integer findTagListParameterIndex(Method theMethod) {
|
||||||
|
return findParamAnnotationIndex(theMethod, TagListParam.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Integer findVersionIdParameterIndex(Method theMethod) {
|
||||||
|
return findParamAnnotationIndex(theMethod, VersionIdParam.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object fromInstant(Class<?> theType, InstantDt theArgument) {
|
||||||
|
if (theType.equals(InstantDt.class)) {
|
||||||
|
if (theArgument == null) {
|
||||||
|
return new InstantDt();
|
||||||
|
}
|
||||||
|
return theArgument;
|
||||||
|
}
|
||||||
|
if (theType.equals(Date.class)) {
|
||||||
|
if (theArgument == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return theArgument.getValue();
|
||||||
|
}
|
||||||
|
if (theType.equals(Calendar.class)) {
|
||||||
|
if (theArgument == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return DateUtils.toCalendar(theArgument.getValue());
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Invalid instant type:" + theType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Object fromInteger(Class<?> theType, IntegerDt theArgument) {
|
||||||
|
if (theType.equals(IntegerDt.class)) {
|
||||||
|
if (theArgument == null) {
|
||||||
|
return new IntegerDt();
|
||||||
|
}
|
||||||
|
return theArgument;
|
||||||
|
}
|
||||||
|
if (theType.equals(Integer.class)) {
|
||||||
|
if (theArgument == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return theArgument.getValue();
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Invalid Integer type:" + theType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<Class<?>> getBindableInstantTypes() {
|
||||||
|
return BINDABLE_TIME_TYPES;
|
||||||
|
}
|
||||||
|
public static Set<Class<?>> getBindableIntegerTypes() {
|
||||||
|
return BINDABLE_INTEGER_TYPES;
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public static List<IParameter> getResourceParameters(Method theMethod) {
|
public static List<IParameter> getResourceParameters(Method theMethod) {
|
||||||
List<IParameter> parameters = new ArrayList<IParameter>();
|
List<IParameter> parameters = new ArrayList<IParameter>();
|
||||||
|
@ -72,7 +152,8 @@ public class ParameterUtil {
|
||||||
Class<? extends java.util.Collection<?>> outerCollectionType = null;
|
Class<? extends java.util.Collection<?>> outerCollectionType = null;
|
||||||
Class<? extends java.util.Collection<?>> innerCollectionType = null;
|
Class<? extends java.util.Collection<?>> innerCollectionType = null;
|
||||||
if (TagList.class.isAssignableFrom(parameterType)) {
|
if (TagList.class.isAssignableFrom(parameterType)) {
|
||||||
param = new TagListParameter();
|
// TagList is handled directly within the method bindings
|
||||||
|
param=new NullParameter();
|
||||||
} else {
|
} else {
|
||||||
if (Collection.class.isAssignableFrom(parameterType)) {
|
if (Collection.class.isAssignableFrom(parameterType)) {
|
||||||
innerCollectionType = (Class<? extends java.util.Collection<?>>) parameterType;
|
innerCollectionType = (Class<? extends java.util.Collection<?>>) parameterType;
|
||||||
|
@ -158,19 +239,6 @@ public class ParameterUtil {
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void extractDescription(SearchParameter theParameter, Annotation[] theAnnotations) {
|
|
||||||
for (Annotation annotation : theAnnotations) {
|
|
||||||
if (annotation instanceof Description) {
|
|
||||||
Description desc = (Description) annotation;
|
|
||||||
if (isNotBlank(desc.formalDefinition())) {
|
|
||||||
theParameter.setDescription(desc.formalDefinition());
|
|
||||||
} else {
|
|
||||||
theParameter.setDescription(desc.shortDefinition());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InstantDt toInstant(Object theArgument) {
|
public static InstantDt toInstant(Object theArgument) {
|
||||||
if (theArgument instanceof InstantDt) {
|
if (theArgument instanceof InstantDt) {
|
||||||
return (InstantDt) theArgument;
|
return (InstantDt) theArgument;
|
||||||
|
@ -184,37 +252,6 @@ public class ParameterUtil {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Set<Class<?>> getBindableInstantTypes() {
|
|
||||||
// TODO: make this constant
|
|
||||||
HashSet<Class<?>> retVal = new HashSet<Class<?>>();
|
|
||||||
retVal.add(InstantDt.class);
|
|
||||||
retVal.add(Date.class);
|
|
||||||
retVal.add(Calendar.class);
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Object fromInstant(Class<?> theType, InstantDt theArgument) {
|
|
||||||
if (theType.equals(InstantDt.class)) {
|
|
||||||
if (theArgument == null) {
|
|
||||||
return new InstantDt();
|
|
||||||
}
|
|
||||||
return theArgument;
|
|
||||||
}
|
|
||||||
if (theType.equals(Date.class)) {
|
|
||||||
if (theArgument == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return theArgument.getValue();
|
|
||||||
}
|
|
||||||
if (theType.equals(Calendar.class)) {
|
|
||||||
if (theArgument == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return DateUtils.toCalendar(theArgument.getValue());
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Invalid instant type:" + theType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IntegerDt toInteger(Object theArgument) {
|
public static IntegerDt toInteger(Object theArgument) {
|
||||||
if (theArgument instanceof IntegerDt) {
|
if (theArgument instanceof IntegerDt) {
|
||||||
return (IntegerDt) theArgument;
|
return (IntegerDt) theArgument;
|
||||||
|
@ -225,28 +262,32 @@ public class ParameterUtil {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Set<Class<?>> getBindableIntegerTypes() {
|
private static void extractDescription(SearchParameter theParameter, Annotation[] theAnnotations) {
|
||||||
// TODO: make this constant
|
for (Annotation annotation : theAnnotations) {
|
||||||
HashSet<Class<?>> retVal = new HashSet<Class<?>>();
|
if (annotation instanceof Description) {
|
||||||
retVal.add(IntegerDt.class);
|
Description desc = (Description) annotation;
|
||||||
retVal.add(Integer.class);
|
if (isNotBlank(desc.formalDefinition())) {
|
||||||
return retVal;
|
theParameter.setDescription(desc.formalDefinition());
|
||||||
|
} else {
|
||||||
|
theParameter.setDescription(desc.shortDefinition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Object fromInteger(Class<?> theType, IntegerDt theArgument) {
|
private static Integer findParamAnnotationIndex(Method theMethod, Class<?> toFind) {
|
||||||
if (theType.equals(IntegerDt.class)) {
|
int paramIndex = 0;
|
||||||
if (theArgument == null) {
|
for (Annotation[] annotations : theMethod.getParameterAnnotations()) {
|
||||||
return new IntegerDt();
|
for (int annotationIndex = 0; annotationIndex < annotations.length; annotationIndex++) {
|
||||||
|
Annotation nextAnnotation = annotations[annotationIndex];
|
||||||
|
Class<? extends Annotation> class1 = nextAnnotation.getClass();
|
||||||
|
if (toFind.isAssignableFrom(class1)) {
|
||||||
|
return paramIndex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return theArgument;
|
paramIndex++;
|
||||||
}
|
}
|
||||||
if (theType.equals(Integer.class)) {
|
return null;
|
||||||
if (theArgument == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return theArgument.getValue();
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Invalid Integer type:" + theType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,150 +0,0 @@
|
||||||
package ca.uhn.fhir.rest.param;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR Library
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 University Health Network
|
|
||||||
* %%
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.model.api.Tag;
|
|
||||||
import ca.uhn.fhir.model.api.TagList;
|
|
||||||
import ca.uhn.fhir.rest.client.BaseClientInvocation;
|
|
||||||
import ca.uhn.fhir.rest.method.Request;
|
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|
||||||
|
|
||||||
public class TagListParameter implements IParameter {
|
|
||||||
private static final String SCHEME = "scheme=\"";
|
|
||||||
private static final String LABEL = "label=\"";
|
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TagListParameter.class);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void translateClientArgumentIntoQueryArgument(Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, BaseClientInvocation theClientInvocation) throws InternalErrorException {
|
|
||||||
TagList list = (TagList) theSourceClientArgument;
|
|
||||||
if (list != null) {
|
|
||||||
for (Tag tag : list) {
|
|
||||||
if (StringUtils.isNotBlank(tag.getTerm())) {
|
|
||||||
theClientInvocation.addHeader(Constants.HEADER_CATEGORY, tag.toHeaderValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
|
|
||||||
TagList retVal = new TagList();
|
|
||||||
|
|
||||||
for (Enumeration<String> enumeration = theRequest.getServletRequest().getHeaders(Constants.HEADER_CATEGORY); enumeration.hasMoreElements();) {
|
|
||||||
String nextTagComplete = enumeration.nextElement();
|
|
||||||
StringBuilder next = new StringBuilder(nextTagComplete);
|
|
||||||
|
|
||||||
parseTagValue(retVal, nextTagComplete, next);
|
|
||||||
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void parseTagValue(TagList theTagList, String theCompleteHeaderValue, StringBuilder theBuffer) {
|
|
||||||
int firstSemicolon = theBuffer.indexOf(";");
|
|
||||||
int deleteTo;
|
|
||||||
if (firstSemicolon == -1) {
|
|
||||||
firstSemicolon = theBuffer.indexOf(",");
|
|
||||||
if (firstSemicolon == -1) {
|
|
||||||
firstSemicolon = theBuffer.length();
|
|
||||||
deleteTo = theBuffer.length();
|
|
||||||
} else {
|
|
||||||
deleteTo = firstSemicolon;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
deleteTo = firstSemicolon + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
String term = theBuffer.substring(0, firstSemicolon);
|
|
||||||
String scheme = null;
|
|
||||||
String label = null;
|
|
||||||
if (isBlank(term)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
theBuffer.delete(0, deleteTo);
|
|
||||||
while (theBuffer.length() > 0 && theBuffer.charAt(0) == ' ') {
|
|
||||||
theBuffer.deleteCharAt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (theBuffer.length() > 0) {
|
|
||||||
boolean foundSomething = false;
|
|
||||||
if (theBuffer.length() > SCHEME.length() && theBuffer.substring(0, SCHEME.length()).equals(SCHEME)) {
|
|
||||||
int closeIdx = theBuffer.indexOf("\"", SCHEME.length());
|
|
||||||
scheme = theBuffer.substring(SCHEME.length(), closeIdx);
|
|
||||||
theBuffer.delete(0, closeIdx + 1);
|
|
||||||
foundSomething = true;
|
|
||||||
}
|
|
||||||
if (theBuffer.length() > LABEL.length() && theBuffer.substring(0, LABEL.length()).equals(LABEL)) {
|
|
||||||
int closeIdx = theBuffer.indexOf("\"", LABEL.length());
|
|
||||||
label = theBuffer.substring(LABEL.length(), closeIdx);
|
|
||||||
theBuffer.delete(0, closeIdx + 1);
|
|
||||||
foundSomething = true;
|
|
||||||
}
|
|
||||||
// TODO: support enc2231-string as described in
|
|
||||||
// http://tools.ietf.org/html/draft-johnston-http-category-header-02
|
|
||||||
// TODO: support multiple tags in one header as described in
|
|
||||||
// http://hl7.org/implement/standards/fhir/http.html#tags
|
|
||||||
|
|
||||||
while (theBuffer.length() > 0 && (theBuffer.charAt(0) == ' ' || theBuffer.charAt(0) == ';')) {
|
|
||||||
theBuffer.deleteCharAt(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!foundSomething) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (theBuffer.length() > 0 && theBuffer.charAt(0) == ',') {
|
|
||||||
theBuffer.deleteCharAt(0);
|
|
||||||
while (theBuffer.length() > 0 && theBuffer.charAt(0) == ' ') {
|
|
||||||
theBuffer.deleteCharAt(0);
|
|
||||||
}
|
|
||||||
theTagList.add(new Tag(term, label, scheme));
|
|
||||||
parseTagValue(theTagList, theCompleteHeaderValue, theBuffer);
|
|
||||||
} else {
|
|
||||||
theTagList.add(new Tag(term, label, scheme));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (theBuffer.length() > 0) {
|
|
||||||
ourLog.warn("Ignoring extra text at the end of " + Constants.HEADER_CATEGORY + " tag '" + theBuffer.toString() + "' - Complete tag value was: " + theCompleteHeaderValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -68,6 +68,9 @@ public class Constants {
|
||||||
public static final String PARAM_SORT_DESC = "_sort:desc";
|
public static final String PARAM_SORT_DESC = "_sort:desc";
|
||||||
public static final String HEADER_CATEGORY = "Category";
|
public static final String HEADER_CATEGORY = "Category";
|
||||||
public static final String OPENSEARCH_NS_OLDER = "http://purl.org/atompub/tombstones/1.0";
|
public static final String OPENSEARCH_NS_OLDER = "http://purl.org/atompub/tombstones/1.0";
|
||||||
|
public static final String PARAM_TAGS = "_tags";
|
||||||
|
public static final String CHARSET_UTF_8 = "UTF-8";
|
||||||
|
public static final String PARAM_DELETE = "_delete";
|
||||||
|
|
||||||
static {
|
static {
|
||||||
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();
|
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();
|
||||||
|
|
|
@ -52,6 +52,16 @@ public enum EncodingEnum {
|
||||||
ourContentTypeToEncoding.put(next.getResourceContentType(), next);
|
ourContentTypeToEncoding.put(next.getResourceContentType(), next);
|
||||||
ourContentTypeToEncoding.put(next.getBrowserFriendlyBundleContentType(), next);
|
ourContentTypeToEncoding.put(next.getBrowserFriendlyBundleContentType(), next);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These are wrong, but we add them just to be tolerant
|
||||||
|
* of other people's mistakes
|
||||||
|
*/
|
||||||
|
ourContentTypeToEncoding.put("application/json", JSON);
|
||||||
|
ourContentTypeToEncoding.put("application/xml", XML);
|
||||||
|
ourContentTypeToEncoding.put("text/json", JSON);
|
||||||
|
ourContentTypeToEncoding.put("text/xml", XML);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String myResourceContentType;
|
private String myResourceContentType;
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class ResourceBinding {
|
||||||
|
|
||||||
ourLog.debug("Looking for a handler for {}", theRequest);
|
ourLog.debug("Looking for a handler for {}", theRequest);
|
||||||
for (BaseMethodBinding rm : methods) {
|
for (BaseMethodBinding rm : methods) {
|
||||||
if (rm.matches(theRequest)) {
|
if (rm.incomingServerRequestMatchesMethod(theRequest)) {
|
||||||
ourLog.debug("Handler {} matches", rm);
|
ourLog.debug("Handler {} matches", rm);
|
||||||
return rm;
|
return rm;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -63,6 +63,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private FhirContext myFhirContext;
|
private FhirContext myFhirContext;
|
||||||
|
private ResourceBinding myNullResourceBinding = new ResourceBinding();
|
||||||
private Collection<Object> myPlainProviders;
|
private Collection<Object> myPlainProviders;
|
||||||
private Map<String, ResourceBinding> myResourceNameToProvider = new HashMap<String, ResourceBinding>();
|
private Map<String, ResourceBinding> myResourceNameToProvider = new HashMap<String, ResourceBinding>();
|
||||||
private Collection<IResourceProvider> myResourceProviders;
|
private Collection<IResourceProvider> myResourceProviders;
|
||||||
|
@ -70,12 +71,10 @@ public class RestfulServer extends HttpServlet {
|
||||||
private BaseMethodBinding myServerConformanceMethod;
|
private BaseMethodBinding myServerConformanceMethod;
|
||||||
private Object myServerConformanceProvider;
|
private Object myServerConformanceProvider;
|
||||||
private String myServerName = "HAPI FHIR Server";
|
private String myServerName = "HAPI FHIR Server";
|
||||||
private String myServerVersion = VersionUtil.getVersion(); // defaults to
|
/** This is configurable but by default we jsut use HAPI version */
|
||||||
// HAPI version
|
private String myServerVersion = VersionUtil.getVersion();
|
||||||
private boolean myUseBrowserFriendlyContentTypes;
|
|
||||||
private ResourceBinding myNullResourceBinding = new ResourceBinding();
|
|
||||||
|
|
||||||
private boolean myStarted;
|
private boolean myStarted;
|
||||||
|
private boolean myUseBrowserFriendlyContentTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
|
@ -90,22 +89,22 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called prior to sending a response to incoming requests. It is
|
* This method is called prior to sending a response to incoming requests.
|
||||||
* used to add custom headers.
|
* It is used to add custom headers.
|
||||||
* <p>
|
* <p>
|
||||||
* Use caution if overriding this method: it is recommended to call
|
* Use caution if overriding this method: it is recommended to call
|
||||||
* <code>super.addHeadersToResponse</code> to avoid inadvertantly
|
* <code>super.addHeadersToResponse</code> to avoid inadvertantly disabling
|
||||||
* disabling functionality.
|
* functionality.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
||||||
theHttpResponse.addHeader("X-PoweredBy", "HAPI FHIR " + VersionUtil.getVersion() + " RESTful Server");
|
theHttpResponse.addHeader("X-PoweredBy", "HAPI FHIR " + VersionUtil.getVersion() + " RESTful Server");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link FhirContext} associated with this server. For efficient processing,
|
* Gets the {@link FhirContext} associated with this server. For efficient
|
||||||
* resource providers and plain providers should generally use this context
|
* processing, resource providers and plain providers should generally use
|
||||||
* if one is needed, as opposed to creating their own.
|
* this context if one is needed, as opposed to creating their own.
|
||||||
*/
|
*/
|
||||||
public FhirContext getFhirContext() {
|
public FhirContext getFhirContext() {
|
||||||
return myFhirContext;
|
return myFhirContext;
|
||||||
|
@ -177,10 +176,10 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the server. Note that this method is final to avoid accidentally
|
* Initializes the server. Note that this method is final to avoid
|
||||||
* introducing bugs in implementations, but subclasses may put initialization code in
|
* accidentally introducing bugs in implementations, but subclasses may put
|
||||||
* {@link #initialize()}, which is called immediately before beginning initialization of
|
* initialization code in {@link #initialize()}, which is called immediately
|
||||||
* the restful server's internal init.
|
* before beginning initialization of the restful server's internal init.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final void init() throws ServletException {
|
public final void init() throws ServletException {
|
||||||
|
@ -190,7 +189,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
mySecurityManager = getSecurityManager();
|
mySecurityManager = getSecurityManager();
|
||||||
if (null == mySecurityManager) {
|
if (null == mySecurityManager) {
|
||||||
ourLog.warn("No security manager has been provided, requests will not be authenticated!");
|
ourLog.trace("No security manager has been provided");
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<IResourceProvider> resourceProvider = getResourceProviders();
|
Collection<IResourceProvider> resourceProvider = getResourceProviders();
|
||||||
|
@ -233,19 +232,13 @@ public class RestfulServer extends HttpServlet {
|
||||||
ourLog.info("A FHIR has been lit on this server");
|
ourLog.info("A FHIR has been lit on this server");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertProviderIsValid(Object theNext) throws ConfigurationException {
|
|
||||||
if (Modifier.isPublic(theNext.getClass().getModifiers()) == false) {
|
|
||||||
throw new ConfigurationException("Can not use provider '" + theNext.getClass() + "' - Must be public");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUseBrowserFriendlyContentTypes() {
|
public boolean isUseBrowserFriendlyContentTypes() {
|
||||||
return myUseBrowserFriendlyContentTypes;
|
return myUseBrowserFriendlyContentTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the non-resource specific providers which implement method calls on
|
* Sets the non-resource specific providers which implement method calls on
|
||||||
* this server.
|
* this server.
|
||||||
*
|
*
|
||||||
* @see #setResourceProviders(Collection)
|
* @see #setResourceProviders(Collection)
|
||||||
*/
|
*/
|
||||||
|
@ -253,6 +246,16 @@ public class RestfulServer extends HttpServlet {
|
||||||
myPlainProviders = theProviders;
|
myPlainProviders = theProviders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the non-resource specific providers which implement method calls on
|
||||||
|
* this server.
|
||||||
|
*
|
||||||
|
* @see #setResourceProviders(Collection)
|
||||||
|
*/
|
||||||
|
public void setPlainProviders(Object... theProv) {
|
||||||
|
setPlainProviders(Arrays.asList(theProv));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the non-resource specific providers which implement method calls on
|
* Sets the non-resource specific providers which implement method calls on
|
||||||
* this server
|
* this server
|
||||||
|
@ -336,10 +339,16 @@ public class RestfulServer extends HttpServlet {
|
||||||
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
myUseBrowserFriendlyContentTypes = theUseBrowserFriendlyContentTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertProviderIsValid(Object theNext) throws ConfigurationException {
|
||||||
|
if (Modifier.isPublic(theNext.getClass().getModifiers()) == false) {
|
||||||
|
throw new ConfigurationException("Can not use provider '" + theNext.getClass() + "' - Must be public");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void findResourceMethods(Object theProvider) throws Exception {
|
private void findResourceMethods(Object theProvider) throws Exception {
|
||||||
|
|
||||||
ourLog.info("Scanning type for RESTful methods: {}", theProvider.getClass());
|
ourLog.info("Scanning type for RESTful methods: {}", theProvider.getClass());
|
||||||
|
|
||||||
Class<?> clazz = theProvider.getClass();
|
Class<?> clazz = theProvider.getClass();
|
||||||
Class<?> supertype = clazz.getSuperclass();
|
Class<?> supertype = clazz.getSuperclass();
|
||||||
if (!Object.class.equals(supertype)) {
|
if (!Object.class.equals(supertype)) {
|
||||||
|
@ -373,7 +382,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
resourceBinding.addMethod(foundMethodBinding);
|
resourceBinding.addMethod(foundMethodBinding);
|
||||||
ourLog.info(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
|
ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
|
||||||
} else {
|
} else {
|
||||||
ourLog.debug(" * Method: {}#{} is not a handler", theProvider.getClass(), m.getName());
|
ourLog.debug(" * Method: {}#{} is not a handler", theProvider.getClass(), m.getName());
|
||||||
}
|
}
|
||||||
|
@ -383,7 +392,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
private void findSystemMethods(Object theSystemProvider) {
|
private void findSystemMethods(Object theSystemProvider) {
|
||||||
Class<?> clazz = theSystemProvider.getClass();
|
Class<?> clazz = theSystemProvider.getClass();
|
||||||
|
|
||||||
findSystemMethods(theSystemProvider, clazz);
|
findSystemMethods(theSystemProvider, clazz);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -411,11 +420,6 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
|
||||||
handleRequest(SearchMethodBinding.RequestType.DELETE, request, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
// /**
|
// /**
|
||||||
// * Sets the {@link INarrativeGenerator Narrative Generator} to use when
|
// * Sets the {@link INarrativeGenerator Narrative Generator} to use when
|
||||||
// serializing responses from this server, or <code>null</code> (which is
|
// serializing responses from this server, or <code>null</code> (which is
|
||||||
|
@ -433,6 +437,11 @@ public class RestfulServer extends HttpServlet {
|
||||||
// myNarrativeGenerator = theNarrativeGenerator;
|
// myNarrativeGenerator = theNarrativeGenerator;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
|
handleRequest(SearchMethodBinding.RequestType.DELETE, request, response);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||||
handleRequest(SearchMethodBinding.RequestType.GET, request, response);
|
handleRequest(SearchMethodBinding.RequestType.GET, request, response);
|
||||||
|
@ -473,10 +482,12 @@ public class RestfulServer extends HttpServlet {
|
||||||
servletContextPath = servletPath;
|
servletContextPath = servletPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
ourLog.info("Request FullPath: {}", requestFullPath);
|
if (ourLog.isTraceEnabled()) {
|
||||||
ourLog.info("Servlet Path: {}", servletPath);
|
ourLog.trace("Request FullPath: {}", requestFullPath);
|
||||||
ourLog.info("Request Url: {}", requestUrl);
|
ourLog.trace("Servlet Path: {}", servletPath);
|
||||||
ourLog.info("Context Path: {}", servletContextPath);
|
ourLog.trace("Request Url: {}", requestUrl);
|
||||||
|
ourLog.trace("Context Path: {}", servletContextPath);
|
||||||
|
}
|
||||||
|
|
||||||
servletPath = servletContextPath;
|
servletPath = servletContextPath;
|
||||||
|
|
||||||
|
@ -539,7 +550,12 @@ public class RestfulServer extends HttpServlet {
|
||||||
|
|
||||||
if (tok.hasMoreTokens()) {
|
if (tok.hasMoreTokens()) {
|
||||||
String nextString = tok.nextToken();
|
String nextString = tok.nextToken();
|
||||||
if (nextString.startsWith("_")) {
|
if (nextString.equals(Constants.PARAM_HISTORY)) {
|
||||||
|
if (tok.hasMoreTokens()) {
|
||||||
|
String versionString = tok.nextToken();
|
||||||
|
versionId = new IdDt(versionString);
|
||||||
|
}
|
||||||
|
} else if (nextString.startsWith("_")) {
|
||||||
if (operation != null) {
|
if (operation != null) {
|
||||||
throw new InvalidRequestException("URL Path contains two operations (part beginning with _): " + requestPath);
|
throw new InvalidRequestException("URL Path contains two operations (part beginning with _): " + requestPath);
|
||||||
}
|
}
|
||||||
|
@ -547,14 +563,23 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tok.hasMoreTokens()) {
|
// Secondary is for things like ..../_tags/_delete
|
||||||
String nextString = tok.nextToken();
|
String secondaryOperation=null;
|
||||||
versionId = new IdDt(nextString);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (theRequestType==RequestType.PUT && versionId==null) {
|
while (tok.hasMoreTokens()) {
|
||||||
|
String nextString = tok.nextToken();
|
||||||
|
if (operation == null) {
|
||||||
|
operation = nextString;
|
||||||
|
}else if (secondaryOperation==null) {
|
||||||
|
secondaryOperation=nextString;
|
||||||
|
}else {
|
||||||
|
throw new InvalidRequestException("URL path has unexpected token '"+nextString + "' at the end: " + requestPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theRequestType == RequestType.PUT && versionId == null) {
|
||||||
String contentLocation = theRequest.getHeader("Content-Location");
|
String contentLocation = theRequest.getHeader("Content-Location");
|
||||||
if (contentLocation!=null) {
|
if (contentLocation != null) {
|
||||||
int idx = contentLocation.indexOf("/_history/");
|
int idx = contentLocation.indexOf("/_history/");
|
||||||
if (idx != -1) {
|
if (idx != -1) {
|
||||||
String versionIdString = contentLocation.substring(idx + "/_history/".length());
|
String versionIdString = contentLocation.substring(idx + "/_history/".length());
|
||||||
|
@ -562,7 +587,6 @@ public class RestfulServer extends HttpServlet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: look for more tokens for version, compartments, etc...
|
// TODO: look for more tokens for version, compartments, etc...
|
||||||
|
|
||||||
|
@ -571,6 +595,7 @@ public class RestfulServer extends HttpServlet {
|
||||||
r.setId(id);
|
r.setId(id);
|
||||||
r.setVersion(versionId);
|
r.setVersion(versionId);
|
||||||
r.setOperation(operation);
|
r.setOperation(operation);
|
||||||
|
r.setSecondaryOperation(secondaryOperation);
|
||||||
r.setParameters(params);
|
r.setParameters(params);
|
||||||
r.setRequestType(theRequestType);
|
r.setRequestType(theRequestType);
|
||||||
if ("application/x-www-form-urlencoded".equals(theRequest.getContentType())) {
|
if ("application/x-www-form-urlencoded".equals(theRequest.getContentType())) {
|
||||||
|
|
|
@ -26,6 +26,12 @@ import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for RESTful client and server exceptions. RESTful client methods
|
||||||
|
* will only throw exceptions which are subclasses of this exception type, and
|
||||||
|
* RESTful server methods should also only call subclasses of this exception
|
||||||
|
* type.
|
||||||
|
*/
|
||||||
public abstract class BaseServerResponseException extends RuntimeException {
|
public abstract class BaseServerResponseException extends RuntimeException {
|
||||||
|
|
||||||
private static final Map<Integer, Class<? extends BaseServerResponseException>> ourStatusCodeToExceptionType = new HashMap<Integer, Class<? extends BaseServerResponseException>>();
|
private static final Map<Integer, Class<? extends BaseServerResponseException>> ourStatusCodeToExceptionType = new HashMap<Integer, Class<? extends BaseServerResponseException>>();
|
||||||
|
@ -44,6 +50,9 @@ public abstract class BaseServerResponseException extends RuntimeException {
|
||||||
|
|
||||||
private final OperationOutcome myOperationOutcome;
|
private final OperationOutcome myOperationOutcome;
|
||||||
|
|
||||||
|
private String myResponseBody;
|
||||||
|
private String myResponseMimeType;
|
||||||
|
|
||||||
private int myStatusCode;
|
private int myStatusCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,12 +114,36 @@ public abstract class BaseServerResponseException extends RuntimeException {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link OperationOutcome} resource if any which was supplied in the response, or <code>null</code>
|
* Returns the {@link OperationOutcome} resource if any which was supplied
|
||||||
|
* in the response, or <code>null</code>
|
||||||
*/
|
*/
|
||||||
public OperationOutcome getOperationOutcome() {
|
public OperationOutcome getOperationOutcome() {
|
||||||
return myOperationOutcome;
|
return myOperationOutcome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In a RESTful client, this method will be populated with the body of the
|
||||||
|
* HTTP respone if one was provided by the server, or <code>null</code>
|
||||||
|
* otherwise.
|
||||||
|
* <p>
|
||||||
|
* In a restful server, this method is currently ignored.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public String getResponseBody() {
|
||||||
|
return myResponseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In a RESTful client, this method will be populated with the HTTP status
|
||||||
|
* code that was returned with the HTTP response.
|
||||||
|
* <p>
|
||||||
|
* In a restful server, this method is currently ignored.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public String getResponseMimeType() {
|
||||||
|
return myResponseMimeType;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the HTTP status code corresponding to this problem
|
* Returns the HTTP status code corresponding to this problem
|
||||||
*/
|
*/
|
||||||
|
@ -118,6 +151,22 @@ public abstract class BaseServerResponseException extends RuntimeException {
|
||||||
return myStatusCode;
|
return myStatusCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is currently only called internally by HAPI, it should not be
|
||||||
|
* called by user code.
|
||||||
|
*/
|
||||||
|
public void setResponseBody(String theResponseBody) {
|
||||||
|
myResponseBody = theResponseBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is currently only called internally by HAPI, it should not be
|
||||||
|
* called by user code.
|
||||||
|
*/
|
||||||
|
public void setResponseMimeType(String theResponseMimeType) {
|
||||||
|
myResponseMimeType = theResponseMimeType;
|
||||||
|
}
|
||||||
|
|
||||||
public static BaseServerResponseException newInstance(int theStatusCode, String theMessage) {
|
public static BaseServerResponseException newInstance(int theStatusCode, String theMessage) {
|
||||||
if (ourStatusCodeToExceptionType.containsKey(theStatusCode)) {
|
if (ourStatusCodeToExceptionType.containsKey(theStatusCode)) {
|
||||||
try {
|
try {
|
||||||
|
@ -146,4 +195,5 @@ public abstract class BaseServerResponseException extends RuntimeException {
|
||||||
}
|
}
|
||||||
ourStatusCodeToExceptionType.put(theStatusCode, theType);
|
ourStatusCodeToExceptionType.put(theStatusCode, theType);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,20 @@ import ca.uhn.fhir.rest.server.Constants;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: javadoc this
|
* Represents an <b>HTTP 500 Internal Error</b> response.
|
||||||
|
* This status indicates that the server failed to successfully process the
|
||||||
|
* request. This generally means that the server is misbehaving or is
|
||||||
|
* misconfigured in some way, although a misbehaving server might also
|
||||||
|
* send this status code in the case of a bad request message (although it
|
||||||
|
* should not do this; an HTTP 4xx response is more appropriate in that
|
||||||
|
* situation).
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that a complete list of RESTful exceptions is available in the
|
||||||
|
* <a href="./package-summary.html">Package Summary</a>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see UnprocessableEntityException Which should be used for business level validation failures
|
||||||
*/
|
*/
|
||||||
public class InternalErrorException extends BaseServerResponseException {
|
public class InternalErrorException extends BaseServerResponseException {
|
||||||
|
|
||||||
|
|
|
@ -23,11 +23,16 @@ import ca.uhn.fhir.rest.server.Constants;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RESTful method exception which corresponds to the <b>HTTP 400 Bad Request</b> status.
|
* Represents an <b>HTTP 400 Bad Request</b> response.
|
||||||
* This status indicates that the client's message was invalid (e.g. not a valid FHIR Resource
|
* This status indicates that the client's message was invalid (e.g. not a valid FHIR Resource
|
||||||
* per the specifications), as opposed to the {@link InvalidRequestException} which indicates
|
* per the specifications), as opposed to the {@link InvalidRequestException} which indicates
|
||||||
* that data does not pass business rule validation on the server.
|
* that data does not pass business rule validation on the server.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that a complete list of RESTful exceptions is available in the
|
||||||
|
* <a href="./package-summary.html">Package Summary</a>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
* @see UnprocessableEntityException Which should be used for business level validation failures
|
* @see UnprocessableEntityException Which should be used for business level validation failures
|
||||||
*/
|
*/
|
||||||
public class InvalidRequestException extends BaseServerResponseException {
|
public class InvalidRequestException extends BaseServerResponseException {
|
||||||
|
|
|
@ -23,7 +23,14 @@ import ca.uhn.fhir.rest.server.Constants;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: javadoc this
|
* Represents an <b>HTTP 405 Method Not Allowed</b> response.
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* Note that a complete list of RESTful exceptions is available in the
|
||||||
|
* <a href="./package-summary.html">Package Summary</a>.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @see UnprocessableEntityException Which should be used for business level validation failures
|
||||||
*/
|
*/
|
||||||
public class MethodNotAllowedException extends BaseServerResponseException {
|
public class MethodNotAllowedException extends BaseServerResponseException {
|
||||||
public static final int STATUS_CODE = Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED;
|
public static final int STATUS_CODE = Constants.STATUS_HTTP_405_METHOD_NOT_ALLOWED;
|
||||||
|
|
|
@ -46,7 +46,11 @@ public class ReflectionUtil {
|
||||||
|
|
||||||
public static Class<?> getGenericCollectionTypeOfMethodParameter(Method theMethod, int theParamIndex) {
|
public static Class<?> getGenericCollectionTypeOfMethodParameter(Method theMethod, int theParamIndex) {
|
||||||
Class<?> type;
|
Class<?> type;
|
||||||
ParameterizedType collectionType = (ParameterizedType) theMethod.getGenericParameterTypes()[theParamIndex];
|
Type genericParameterType = theMethod.getGenericParameterTypes()[theParamIndex];
|
||||||
|
if (Class.class.equals(genericParameterType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ParameterizedType collectionType = (ParameterizedType) genericParameterType;
|
||||||
Type firstArg = collectionType.getActualTypeArguments()[0];
|
Type firstArg = collectionType.getActualTypeArguments()[0];
|
||||||
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
||||||
ParameterizedType pt = ((ParameterizedType) firstArg);
|
ParameterizedType pt = ((ParameterizedType) firstArg);
|
||||||
|
@ -60,7 +64,11 @@ public class ReflectionUtil {
|
||||||
@SuppressWarnings({ "unused", "rawtypes" })
|
@SuppressWarnings({ "unused", "rawtypes" })
|
||||||
public static Class<?> getGenericCollectionTypeOfMethodReturnType(Method theMethod) {
|
public static Class<?> getGenericCollectionTypeOfMethodReturnType(Method theMethod) {
|
||||||
Class<?> type;
|
Class<?> type;
|
||||||
ParameterizedType collectionType = (ParameterizedType) theMethod.getGenericReturnType();
|
Type genericReturnType = theMethod.getGenericReturnType();
|
||||||
|
if (!(genericReturnType instanceof ParameterizedType)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
ParameterizedType collectionType = (ParameterizedType) genericReturnType;
|
||||||
Type firstArg = collectionType.getActualTypeArguments()[0];
|
Type firstArg = collectionType.getActualTypeArguments()[0];
|
||||||
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
||||||
ParameterizedType pt = ((ParameterizedType) firstArg);
|
ParameterizedType pt = ((ParameterizedType) firstArg);
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
dt.1=ca.uhn.fhir.model.dstu.composite.AddressDt
|
||||||
|
dt.2=ca.uhn.fhir.model.dstu.composite.AgeDt
|
||||||
|
dt.3=ca.uhn.fhir.model.dstu.composite.AttachmentDt
|
||||||
|
dt.4=ca.uhn.fhir.model.dstu.composite.CodeableConceptDt
|
||||||
|
dt.5=ca.uhn.fhir.model.dstu.composite.CodingDt
|
||||||
|
dt.6=ca.uhn.fhir.model.dstu.composite.ContactDt
|
||||||
|
dt.8=ca.uhn.fhir.model.dstu.composite.DurationDt
|
||||||
|
dt.9=ca.uhn.fhir.model.dstu.composite.HumanNameDt
|
||||||
|
dt.10=ca.uhn.fhir.model.dstu.composite.IdentifierDt
|
||||||
|
dt.11=ca.uhn.fhir.model.dstu.composite.NarrativeDt
|
||||||
|
dt.12=ca.uhn.fhir.model.dstu.composite.PeriodDt
|
||||||
|
dt.13=ca.uhn.fhir.model.dstu.composite.QuantityDt
|
||||||
|
dt.14=ca.uhn.fhir.model.dstu.composite.RangeDt
|
||||||
|
dt.15=ca.uhn.fhir.model.dstu.composite.RatioDt
|
||||||
|
dt.16=ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt
|
||||||
|
dt.17=ca.uhn.fhir.model.dstu.composite.SampledDataDt
|
||||||
|
dt.18=ca.uhn.fhir.model.dstu.composite.ScheduleDt
|
||||||
|
|
||||||
|
dt.31=ca.uhn.fhir.model.primitive.Base64BinaryDt
|
||||||
|
dt.33=ca.uhn.fhir.model.primitive.BooleanDt
|
||||||
|
dt.34=ca.uhn.fhir.model.primitive.BoundCodeableConceptDt
|
||||||
|
dt.35=ca.uhn.fhir.model.primitive.BoundCodeDt
|
||||||
|
dt.36=ca.uhn.fhir.model.primitive.CodeDt
|
||||||
|
dt.37=ca.uhn.fhir.model.primitive.DateDt
|
||||||
|
dt.38=ca.uhn.fhir.model.primitive.DateTimeDt
|
||||||
|
dt.39=ca.uhn.fhir.model.primitive.DecimalDt
|
||||||
|
dt.40=ca.uhn.fhir.model.primitive.IdDt
|
||||||
|
dt.42=ca.uhn.fhir.model.primitive.InstantDt
|
||||||
|
dt.43=ca.uhn.fhir.model.primitive.IntegerDt
|
||||||
|
dt.44=ca.uhn.fhir.model.primitive.OidDt
|
||||||
|
dt.45=ca.uhn.fhir.model.primitive.StringDt
|
||||||
|
dt.46=ca.uhn.fhir.model.primitive.UriDt
|
|
@ -6,15 +6,17 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.rest.client.HttpBasicAuthInterceptor;
|
import ca.uhn.fhir.rest.client.HttpBasicAuthInterceptor;
|
||||||
import ca.uhn.fhir.rest.client.IRestfulClientFactory;
|
import ca.uhn.fhir.rest.client.IRestfulClientFactory;
|
||||||
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||||
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
|
||||||
public class ClientExamples {
|
public class ClientExamples {
|
||||||
|
|
||||||
public interface PatientClient extends IBasicClient {
|
public interface IPatientClient extends IBasicClient {
|
||||||
// nothing yet
|
// nothing yet
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public void createSecurity() {
|
public void createSecurity() {
|
||||||
|
{
|
||||||
//START SNIPPET: security
|
//START SNIPPET: security
|
||||||
// Create a context and get the client factory so it can be configured
|
// Create a context and get the client factory so it can be configured
|
||||||
FhirContext ctx = new FhirContext();
|
FhirContext ctx = new FhirContext();
|
||||||
|
@ -32,9 +34,25 @@ builder.addInterceptorFirst(new HttpBasicAuthInterceptor(username, password));
|
||||||
clientFactory.setHttpClient(builder.build());
|
clientFactory.setHttpClient(builder.build());
|
||||||
|
|
||||||
// Actually create a client instance
|
// Actually create a client instance
|
||||||
PatientClient client = ctx.newRestfulClient(PatientClient.class, "http://localhost:9999/");
|
IPatientClient client = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/");
|
||||||
//END SNIPPET: security
|
//END SNIPPET: security
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************************/
|
||||||
|
{
|
||||||
|
//START SNIPPET: clientConfig
|
||||||
|
//Create a client
|
||||||
|
FhirContext ctx = new FhirContext();
|
||||||
|
IPatientClient client = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/");
|
||||||
|
|
||||||
|
// Request JSON encoding from the server (_format=json)
|
||||||
|
client.setEncoding(EncodingEnum.JSON);
|
||||||
|
|
||||||
|
// Request pretty printing from the server (_pretty=true)
|
||||||
|
client.setPrettyPrint(true);
|
||||||
|
//END SNIPPET: clientConfig
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
|
||||||
public class Extensions {
|
public class Extensions {
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public static void main(String[] args) throws DataFormatException, IOException {
|
public static void main(String[] args) throws DataFormatException, IOException {
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,10 @@ import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.PathSpecification;
|
import ca.uhn.fhir.model.api.PathSpecification;
|
||||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.TagListParam;
|
||||||
import ca.uhn.fhir.model.dstu.composite.CodingDt;
|
import ca.uhn.fhir.model.dstu.composite.CodingDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||||
|
@ -23,13 +26,17 @@ import ca.uhn.fhir.model.dstu.resource.Observation;
|
||||||
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Organization;
|
import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum;
|
import ca.uhn.fhir.model.dstu.valueset.IssueSeverityEnum;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
|
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
import ca.uhn.fhir.parser.DataFormatException;
|
||||||
|
import ca.uhn.fhir.rest.annotation.AddTags;
|
||||||
import ca.uhn.fhir.rest.annotation.Count;
|
import ca.uhn.fhir.rest.annotation.Count;
|
||||||
import ca.uhn.fhir.rest.annotation.Create;
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.GetTags;
|
||||||
import ca.uhn.fhir.rest.annotation.History;
|
import ca.uhn.fhir.rest.annotation.History;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
||||||
|
@ -508,8 +515,146 @@ System.out.println(ctx.newXmlParser().encodeResourceToString(metadata));
|
||||||
//END SNIPPET: metadataClientUsage
|
//END SNIPPET: metadataClientUsage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//START SNIPPET: readTags
|
||||||
|
@Read()
|
||||||
|
public Patient readPatient(@IdParam IdDt theId) {
|
||||||
|
Patient retVal = new Patient();
|
||||||
|
|
||||||
|
// ..populate demographics, contact, or anything else you usually would..
|
||||||
|
|
||||||
|
// Create a TagList and place a complete list of the patient's tags inside
|
||||||
|
TagList tags = new TagList();
|
||||||
|
tags.addTag("Dog", "Canine Patient", "http://animals"); // TODO: more realistic example
|
||||||
|
tags.addTag("Friendly", "Friendly", "http://personality"); // TODO: more realistic example
|
||||||
|
|
||||||
|
// The tags are then stored in the Patient resource instance
|
||||||
|
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tags);
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
//END SNIPPET: readTags
|
||||||
|
|
||||||
|
//START SNIPPET: clientReadInterface
|
||||||
|
private interface IPatientClient extends IBasicClient
|
||||||
|
{
|
||||||
|
/** Read a patient from a server by ID */
|
||||||
|
@Read
|
||||||
|
Patient readPatient(@IdParam IdDt theId);
|
||||||
|
|
||||||
|
// Only one method is shown here, but many methods may be
|
||||||
|
// added to the same client interface!
|
||||||
|
}
|
||||||
|
//START SNIPPET: clientReadInterface
|
||||||
|
|
||||||
|
public void clientRead() {
|
||||||
|
//START SNIPPET: clientReadTags
|
||||||
|
IPatientClient client = new FhirContext().newRestfulClient(IPatientClient.class, "http://foo/fhir");
|
||||||
|
Patient patient = client.readPatient(new IdDt("1234"));
|
||||||
|
|
||||||
|
// Access the tag list
|
||||||
|
TagList tagList = (TagList) patient.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||||
|
for (Tag next : tagList) {
|
||||||
|
// ..process the tags somehow..
|
||||||
|
}
|
||||||
|
//END SNIPPET: clientReadTags
|
||||||
|
|
||||||
|
//START SNIPPET: clientCreateTags
|
||||||
|
Patient newPatient = new Patient();
|
||||||
|
|
||||||
|
// Populate the resource object
|
||||||
|
newPatient.addIdentifier().setUse(IdentifierUseEnum.OFFICIAL).setValue("123");
|
||||||
|
newPatient.addName().addFamily("Jones").addGiven("Frank");
|
||||||
|
|
||||||
|
// Populate tags
|
||||||
|
TagList tags = new TagList();
|
||||||
|
tags.addTag("Dog", "Canine Patient", "http://animals"); // TODO: more realistic example
|
||||||
|
tags.addTag("Friendly", "Friendly", "http://personality"); // TODO: more realistic example
|
||||||
|
newPatient.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tags);
|
||||||
|
|
||||||
|
// ...invoke the create method on the client...
|
||||||
|
//END SNIPPET: clientCreateTags
|
||||||
|
}
|
||||||
|
|
||||||
|
//START SNIPPET: createTags
|
||||||
|
@Create
|
||||||
|
public MethodOutcome createPatientResource(@ResourceParam Patient thePatient) {
|
||||||
|
|
||||||
|
// ..save the resouce..
|
||||||
|
IdDt id = new IdDt("123"); // the new databse primary key for this resource
|
||||||
|
|
||||||
|
// Get the tag list
|
||||||
|
TagList tags = (TagList) thePatient.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||||
|
for (Tag tag : tags) {
|
||||||
|
// process/save each tag somehow
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MethodOutcome(id);
|
||||||
|
}
|
||||||
|
//END SNIPPET: createTags
|
||||||
|
|
||||||
|
//START SNIPPET: tagMethodProvider
|
||||||
|
public class TagMethodProvider
|
||||||
|
{
|
||||||
|
/** Return a list of all tags that exist on the server */
|
||||||
|
@GetTags
|
||||||
|
public TagList getAllTagsOnServer() {
|
||||||
|
return new TagList(); // populate this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return a list of all tags that exist on at least one instance
|
||||||
|
* of the given resource type */
|
||||||
|
@GetTags(type=Patient.class)
|
||||||
|
public TagList getTagsForAllResourcesOfResourceType() {
|
||||||
|
return new TagList(); // populate this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return a list of all tags that exist on a specific instance
|
||||||
|
* of the given resource type */
|
||||||
|
@GetTags(type=Patient.class)
|
||||||
|
public TagList getTagsForResources(@IdParam IdDt theId) {
|
||||||
|
return new TagList(); // populate this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return a list of all tags that exist on a specific version
|
||||||
|
* of the given resource type */
|
||||||
|
@GetTags(type=Patient.class)
|
||||||
|
public TagList getTagsForResourceVersion(@IdParam IdDt theId,
|
||||||
|
@VersionIdParam IdDt theVersion) {
|
||||||
|
return new TagList(); // populate this
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add tags to a resource */
|
||||||
|
@AddTags(type=Patient.class)
|
||||||
|
public void getTagsForResourceVersion(@IdParam IdDt theId,
|
||||||
|
@TagListParam TagList theTagList) {
|
||||||
|
// add tags
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add tags to a resource version */
|
||||||
|
@AddTags(type=Patient.class)
|
||||||
|
public void addTagsToResourceVersion(@IdParam IdDt theId,
|
||||||
|
@VersionIdParam IdDt theVersion,
|
||||||
|
@TagListParam TagList theTagList) {
|
||||||
|
// add tags
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Remove tags from a resource */
|
||||||
|
@DeleteTags(type=Patient.class)
|
||||||
|
public void deleteTagsFromResourceVersion(@IdParam IdDt theId,
|
||||||
|
@TagListParam TagList theTagList) {
|
||||||
|
// add tags
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Remove tags from a resource version */
|
||||||
|
@DeleteTags(type=Patient.class)
|
||||||
|
public void deleteTagsFromResourceVersion(@IdParam IdDt theId,
|
||||||
|
@VersionIdParam IdDt theVersion,
|
||||||
|
@TagListParam TagList theTagList) {
|
||||||
|
// add tags
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//END SNIPPET: tagMethodProvider
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section name="Configuring the HTTP Client">
|
<section name="Configuring the Client">
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
The client uses <a href="http://hc.apache.org/httpcomponents-client-ga/">Apache HTTP Client</a>
|
The client uses <a href="http://hc.apache.org/httpcomponents-client-ga/">Apache HTTP Client</a>
|
||||||
|
@ -155,6 +155,28 @@
|
||||||
|
|
||||||
</subsection>
|
</subsection>
|
||||||
|
|
||||||
|
<subsection name="Configuring Encoding (JSON/XML)">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Restful client interfaces that you create will also extend
|
||||||
|
the interface
|
||||||
|
<a href="./apidocs/ca/uhn/fhir/rest/client/api/IRestfulClient.html">IRestfulClient</a>,
|
||||||
|
which comes with some helpful methods for configuring the way that
|
||||||
|
the client will interact with the server.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The following snippet shows how to configure the cliet to explicitly
|
||||||
|
request JSON or XML responses, and how to request "pretty printed" responses
|
||||||
|
on servers that support this (HAPI based servers currently).
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="clientConfig" />
|
||||||
|
<param name="file" value="src/site/example/java/example/ClientExamples.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
|
</subsection>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -14,15 +14,23 @@
|
||||||
Jump To...
|
Jump To...
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="#operations">Operations</a></li>
|
<li>
|
||||||
<li><a href="#exceptions">Exceptions</a></li>
|
<a href="#operations">Operations</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#exceptions">Exceptions</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#tags">Tags</a>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
RESTful Clients and Servers both share the same
|
RESTful Clients and Servers both share the same
|
||||||
method pattern, with one key difference: A client
|
method pattern, with one key difference: A client
|
||||||
is defined using annotated methods on an interface
|
is defined using annotated methods on an interface
|
||||||
which are used to retrieve resources,
|
which are used to retrieve
|
||||||
|
resources,
|
||||||
whereas a server requires concrete method
|
whereas a server requires concrete method
|
||||||
implementations
|
implementations
|
||||||
to actually provide those resources.
|
to actually provide those resources.
|
||||||
|
@ -34,9 +42,9 @@
|
||||||
implementations, but client methods will follow the same patterns.
|
implementations, but client methods will follow the same patterns.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a name="operations"/>
|
<a name="operations" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section name="Operations">
|
<section name="Operations">
|
||||||
<p>
|
<p>
|
||||||
The following table lists the operations supported by
|
The following table lists the operations supported by
|
||||||
|
@ -93,7 +101,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="#type_search">Type - Create</a>
|
<a href="#type_create">Type - Create</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
Create a new resource with a server assigned id
|
Create a new resource with a server assigned id
|
||||||
|
@ -101,7 +109,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<a href="#instance_update">Type - Search</a>
|
<a href="#type_search">Type - Search</a>
|
||||||
<macro name="toc">
|
<macro name="toc">
|
||||||
<param name="section" value="8" />
|
<param name="section" value="8" />
|
||||||
<param name="fromDepth" value="2" />
|
<param name="fromDepth" value="2" />
|
||||||
|
@ -159,6 +167,14 @@
|
||||||
Search across all resource types based on some filter criteria
|
Search across all resource types based on some filter criteria
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<a href="#tags">Tag Operations</a>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
Search across all resource types based on some filter criteria
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
@ -195,6 +211,16 @@
|
||||||
<code>http://fhir.example.com/Patient/111</code>
|
<code>http://fhir.example.com/Patient/111</code>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The following snippet shows how to define a client interface
|
||||||
|
to handle a read method.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="clientReadInterface" />
|
||||||
|
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
<a name="instance_vread" />
|
<a name="instance_vread" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -210,7 +236,7 @@
|
||||||
<b>vread</b>
|
<b>vread</b>
|
||||||
</a>
|
</a>
|
||||||
operation retrieves a specific version of a resource with a given ID. It looks exactly
|
operation retrieves a specific version of a resource with a given ID. It looks exactly
|
||||||
like a "read" operation, but with a second
|
like a "read" operation, but with a second <code>IdDt</code>
|
||||||
parameter annotated with the
|
parameter annotated with the
|
||||||
<a href="./apidocs/ca/uhn/fhir/rest/annotation/VersionIdParam.html">@VersionIdParam</a>
|
<a href="./apidocs/ca/uhn/fhir/rest/annotation/VersionIdParam.html">@VersionIdParam</a>
|
||||||
annotation.
|
annotation.
|
||||||
|
@ -226,6 +252,13 @@
|
||||||
<br />
|
<br />
|
||||||
<code>http://fhir.example.com/Patient/111/_history/2</code>
|
<code>http://fhir.example.com/Patient/111/_history/2</code>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
To define a client method to handle a VRead method, the
|
||||||
|
syntax is the same as the <a href="#instance_vread">read</a> method
|
||||||
|
above, except that you will add a second <code>IdDt</code> parameter
|
||||||
|
annotated with the <code>@VersionIdParam</code> annotation.
|
||||||
|
</p>
|
||||||
|
|
||||||
<a name="instance_update" />
|
<a name="instance_update" />
|
||||||
</section>
|
</section>
|
||||||
|
@ -465,7 +498,8 @@
|
||||||
<p>
|
<p>
|
||||||
This annotation takes a "name" parameter which specifies the parameter's
|
This annotation takes a "name" parameter which specifies the parameter's
|
||||||
name (as it will appear in the search URL). FHIR defines standardized parameter
|
name (as it will appear in the search URL). FHIR defines standardized parameter
|
||||||
names for each resource, and these are available as constants on the
|
names for each
|
||||||
|
resource, and these are available as constants on the
|
||||||
individual HAPI resource
|
individual HAPI resource
|
||||||
classes.
|
classes.
|
||||||
</p>
|
</p>
|
||||||
|
@ -490,10 +524,10 @@
|
||||||
interface.
|
interface.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
The "token" type is used for parameters which have two parts, such as
|
The "token" type is used for parameters which have two parts, such as
|
||||||
an idnetifier (which has a system URI, as well as the actual identifier)
|
an idnetifier (which has a system URI, as well as the actual identifier)
|
||||||
or a code (which has a code system, as well as the actual code).
|
or a code (which has a code system, as well as the actual code).
|
||||||
For example, the search below can be used to search by
|
For example, the search below can be used to search by
|
||||||
identifier (e.g. search for an MRN).
|
identifier (e.g. search for an MRN).
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -509,7 +543,7 @@
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</subsection>
|
</subsection>
|
||||||
|
|
||||||
<subsection name="Search Parameters: Date (Simple)">
|
<subsection name="Search Parameters: Date (Simple)">
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
@ -597,32 +631,41 @@
|
||||||
<param name="id" value="dateClient" />
|
<param name="id" value="dateClient" />
|
||||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
</macro>
|
</macro>
|
||||||
|
|
||||||
<h4>Unbounded Ranges</h4>
|
<h4>Unbounded Ranges</h4>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Note that when using a date range parameter, it is also possible for
|
Note that when using a date range parameter, it is also possible for
|
||||||
the client to request an "unbounded" range. In other words, a range that
|
the client to request an "unbounded" range. In other words, a range that
|
||||||
only a start date and no end date, or vice versa.
|
only a start date and no end
|
||||||
|
date, or vice versa.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
An example of this might be the following URL, which refers to any Observation
|
An example of this might be the following URL, which refers to any Observation
|
||||||
resources for the given MRN and having a date after 2011-01-01.
|
resources for the given MRN and having a date after 2011-01-01.
|
||||||
<br/>
|
<br />
|
||||||
<code>http://fhir.example.com/Observation?subject.identifier=7000135&date=>=2011-01-01</code><br/>
|
<code>http://fhir.example.com/Observation?subject.identifier=7000135&date=>=2011-01-01</code>
|
||||||
|
<br />
|
||||||
When such a request is made of a server (or to make such a request from a client),
|
When such a request is made of a server (or to make such a request from a client),
|
||||||
the <code>getLowerBound()</code> or <code>getUpperBound()</code> property of the
|
the
|
||||||
<code>DateRangeParam</code> object will be set to <code>null</code>.
|
<code>getLowerBound()</code>
|
||||||
|
or
|
||||||
|
<code>getUpperBound()</code>
|
||||||
|
property of the
|
||||||
|
<code>DateRangeParam</code>
|
||||||
|
object will be set to
|
||||||
|
<code>null</code>
|
||||||
|
.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
</subsection>
|
</subsection>
|
||||||
|
|
||||||
<subsection name="Combining Multiple Parameters">
|
<subsection name="Combining Multiple Parameters">
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Search methods may take multiple parameters, and these parameters
|
Search methods may take multiple parameters, and these parameters
|
||||||
may (or may not) be optional.
|
may (or may not) be optional.
|
||||||
aaaa
|
aaaa
|
||||||
To add a second required parameter, annotate the
|
To add a second required parameter, annotate the
|
||||||
parameter with
|
parameter with
|
||||||
|
@ -761,7 +804,7 @@
|
||||||
which is more convenient if only a single include (or null for none)
|
which is more convenient if only a single include (or null for none)
|
||||||
is all that is required.
|
is all that is required.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<macro name="snippet">
|
<macro name="snippet">
|
||||||
<param name="id" value="pathSpecSimple" />
|
<param name="id" value="pathSpecSimple" />
|
||||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
|
@ -847,7 +890,8 @@
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
An object of type
|
An object of type
|
||||||
<a href="./apidocs/ca/uhn/fhir/rest/api/MethodOutcome.html">MethodOutcome</a>. The
|
<a href="./apidocs/ca/uhn/fhir/rest/api/MethodOutcome.html">MethodOutcome</a>
|
||||||
|
. The
|
||||||
MethodOutcome may optionally be populated with an OperationOutcome resource, which
|
MethodOutcome may optionally be populated with an OperationOutcome resource, which
|
||||||
will be returned to the client if it exists.
|
will be returned to the client if it exists.
|
||||||
</li>
|
</li>
|
||||||
|
@ -867,7 +911,7 @@
|
||||||
<br />
|
<br />
|
||||||
<code>http://fhir.example.com/Patient/_validate</code>
|
<code>http://fhir.example.com/Patient/_validate</code>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a name="system_conformance" />
|
<a name="system_conformance" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -987,38 +1031,54 @@
|
||||||
annotated with the
|
annotated with the
|
||||||
<a href="./apidocs/ca/uhn/fhir/rest/annotation/IdParam.html">@IdParam</a>
|
<a href="./apidocs/ca/uhn/fhir/rest/annotation/IdParam.html">@IdParam</a>
|
||||||
annotation, indicating the ID of the resource for which to return history.
|
annotation, indicating the ID of the resource for which to return history.
|
||||||
<ul><li>
|
<ul>
|
||||||
For a server
|
<li>
|
||||||
implementation, the method must either be defined in a
|
For a server
|
||||||
<a href="./doc_rest_server.html#resource_providers">resource provider</a>
|
implementation, the method must either be defined in a
|
||||||
or have a <code>type()</code> value in the @History annotation if it is
|
<a href="./doc_rest_server.html#resource_providers">resource provider</a>
|
||||||
defined in a
|
or have a
|
||||||
<a href="./doc_rest_server.html#plain_providers">plain provider</a>.
|
<code>type()</code>
|
||||||
</li></ul>
|
value in the @History annotation if it is
|
||||||
|
defined in a
|
||||||
|
<a href="./doc_rest_server.html#plain_providers">plain provider</a>
|
||||||
|
.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
For a
|
For a
|
||||||
<b>Type History</b>
|
<b>Type History</b>
|
||||||
method, the method must not have any @IdParam parameter.
|
method, the method must not have any @IdParam parameter.
|
||||||
<ul><li>
|
<ul>
|
||||||
For a server
|
<li>
|
||||||
implementation, the method must either be defined in a
|
For a server
|
||||||
<a href="./doc_rest_server.html#resource_providers">resource provider</a>
|
implementation, the method must either be defined in a
|
||||||
or have a <code>type()</code> value in the @History annotation if it is
|
<a href="./doc_rest_server.html#resource_providers">resource provider</a>
|
||||||
defined in a
|
or have a
|
||||||
<a href="./doc_rest_server.html#plain_providers">plain provider</a>.
|
<code>type()</code>
|
||||||
</li></ul>
|
value in the @History annotation if it is
|
||||||
|
defined in a
|
||||||
|
<a href="./doc_rest_server.html#plain_providers">plain provider</a>
|
||||||
|
.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
For a
|
For a
|
||||||
<b>Server History</b>
|
<b>Server History</b>
|
||||||
method, the method must not have any @IdParam parameter, and
|
method, the method must not have any @IdParam parameter, and
|
||||||
must not have a <code>type()</code> value specified in
|
must not have a
|
||||||
the @History annotation.
|
<code>type()</code>
|
||||||
<ul><li>
|
value specified in
|
||||||
In a server implementation, the method must
|
the @History annotation.
|
||||||
be defined in a <a href="./doc_rest_server.html#plain_providers">plain provider</a>.
|
<ul>
|
||||||
</li></ul>
|
<li>
|
||||||
|
In a server implementation, the method must
|
||||||
|
be defined in a
|
||||||
|
<a href="./doc_rest_server.html#plain_providers">plain provider</a>
|
||||||
|
.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
|
@ -1039,34 +1099,155 @@
|
||||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
</macro>
|
</macro>
|
||||||
|
|
||||||
<a name="exceptions"/>
|
<a name="exceptions" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- ****************************************************************** -->
|
||||||
|
<!-- ****************************************************************** -->
|
||||||
|
<!-- ****************************************************************** -->
|
||||||
|
|
||||||
<section name="Exceptions">
|
<section name="Exceptions">
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
When implementing a server operation, there are a number of failure conditions
|
When implementing a server operation, there are a number of failure conditions
|
||||||
specified. For example, an
|
specified. For example, an
|
||||||
<a href="#instance_read">Instance Read</a> request might specify an unknown
|
<a href="#instance_read">Instance Read</a>
|
||||||
resource ID, or a <a href="#type_create">Type Create</a> request might contain an
|
request might specify an unknown
|
||||||
|
resource ID, or a
|
||||||
|
<a href="#type_create">Type Create</a>
|
||||||
|
request might contain an
|
||||||
invalid resource which can not be created.
|
invalid resource which can not be created.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
In these cases, an appropriate exception should be thrown. The HAPI RESTful
|
In these cases, an appropriate exception should be thrown. The HAPI RESTful
|
||||||
API includes a set of exceptions extending
|
API includes a set of exceptions extending
|
||||||
<a href="./apidocs/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.html">BaseServerResponseException</a>
|
<a href="./apidocs/ca/uhn/fhir/rest/server/exceptions/BaseServerResponseException.html">BaseServerResponseException</a>
|
||||||
which represent specific HTTP failure codes.
|
which represent specific HTTP failure codes.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
See the
|
See the
|
||||||
<a href="./apidocs/ca/uhn/fhir/rest/server/exceptions/package-summary.html">Exceptions List</a>
|
<a href="./apidocs/ca/uhn/fhir/rest/server/exceptions/package-summary.html">Exceptions List</a>
|
||||||
for a complete list of these exceptions. Note that these exceptions are all <b>unchecked</b>
|
for a complete list of these exceptions. Note that these exceptions are all
|
||||||
|
<b>unchecked</b>
|
||||||
exceptions, so they do not need to ne explicitly declared in the method
|
exceptions, so they do not need to ne explicitly declared in the method
|
||||||
signature.
|
signature.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<a name="tags" />
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- ****************************************************************** -->
|
||||||
|
<!-- ****************************************************************** -->
|
||||||
|
<!-- ****************************************************************** -->
|
||||||
|
|
||||||
|
<section name="Tags">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
FHIR RESTful servers may support a feature known as tagging. Tags are a set of
|
||||||
|
named flags called "terms" (with an optional accompanying human friendly name
|
||||||
|
called a "label",
|
||||||
|
and an optional namespace called a "scheme").
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Tags have very specific semantics, which may not be
|
||||||
|
obvious simply by using the HAPI API. It is important to review the specification
|
||||||
|
pages
|
||||||
|
<a href="http://hl7.org/implement/standards/fhir/http.html#tags">here</a>
|
||||||
|
and
|
||||||
|
<a href="http://hl7.org/implement/standards/fhir/extras.html#tags">here</a>
|
||||||
|
before attempting to implement tagging in your own applications.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<subsection name="Accessing Tags in a Read / VRead / Search Method">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Tags are stored within a resource object, in the
|
||||||
|
<a href="./apidocs/ca/uhn/fhir/model/api/IResource.html#getResourceMetadata()">IResource.html#getResourceMetadata()</a>
|
||||||
|
map, under the key
|
||||||
|
<a href="./apidocs/ca/uhn/fhir/model/api/ResourceMetadataKeyEnum.html#TAG_LIST">TAG_LIST</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
In a server implementation, you may populate your tags into the
|
||||||
|
returned resource(s) and HAPI will automatically place these tags into
|
||||||
|
the response headers (for read/vread) or the bundle category tags (for search).
|
||||||
|
The following example illustrates how to return tags from a server method. This
|
||||||
|
example shows how to supply tags in a read method, but the same approach applies
|
||||||
|
to vread and search operations as well.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="readTags" />
|
||||||
|
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
In a client operation, you simply call the read/vread/search method as you
|
||||||
|
normally would (as described above), and if any tags have been returned
|
||||||
|
by the server, these may be accessed from the resource metadata.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="clientReadTags" />
|
||||||
|
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
|
</subsection>
|
||||||
|
|
||||||
|
<subsection name="Setting Tags in a Create/Update Method">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Within a <a href="#type_create">Type Create</a>
|
||||||
|
or <a href="#instance_update">Instance Update</a> method, it is
|
||||||
|
possible for the client to specify a set of tags to be stored
|
||||||
|
along with the saved resource instance.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Note that FHIR specifies that in an update method, any tags supplied
|
||||||
|
by the client are copied to the newly saved version, as well as any
|
||||||
|
tags the existing version had.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
To work with tags in a create/update method, the pattern used in the
|
||||||
|
read examples above is simply revered. In a server, the resource which
|
||||||
|
is passed in will be populated with any tags that the client supplied:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="createTags" />
|
||||||
|
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
|
</subsection>
|
||||||
|
|
||||||
|
<subsection name="More tag methods">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
FHIR also provides a number of operations to interact directly
|
||||||
|
with tags. These methods may be used to retrieve lists of tags
|
||||||
|
that are available on the server, or to add or remove tags from
|
||||||
|
resources without interacting directly with those resources.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
On a server these methods may be placed in a plain provider, or in a resource
|
||||||
|
provider in the case of resource type specific methods.
|
||||||
|
</p>
|
||||||
|
<macro name="snippet">
|
||||||
|
<param name="id" value="tagMethodProvider" />
|
||||||
|
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||||
|
</macro>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
On a client, the methods are defined in the exact same way, except that
|
||||||
|
there is no method body in the client interface.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</subsection>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</document>
|
</document>
|
||||||
|
|
|
@ -100,7 +100,7 @@ public class ResourceWithExtensionsA extends BaseResource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return false; // TODO: implement
|
return false; // not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -110,7 +110,7 @@ public class ResourceWithExtensionsA extends BaseResource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
|
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
|
||||||
return ca.uhn.fhir.util.ElementUtil.allPopulatedChildElements(theType ); // TODO: implement
|
return ca.uhn.fhir.util.ElementUtil.allPopulatedChildElements(theType ); // not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -159,7 +159,7 @@ public class ResourceWithExtensionsA extends BaseResource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return false; // TODO: implement
|
return false; // not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -169,7 +169,7 @@ public class ResourceWithExtensionsA extends BaseResource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
|
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
|
||||||
return ca.uhn.fhir.util.ElementUtil.allPopulatedChildElements(theType ); // TODO: implement
|
return ca.uhn.fhir.util.ElementUtil.allPopulatedChildElements(theType ); // not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ public class ResourceWithExtensionsA extends BaseResource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return false; // TODO: implement
|
return false; // not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -214,7 +214,7 @@ public class ResourceWithExtensionsA extends BaseResource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
|
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
|
||||||
return ca.uhn.fhir.util.ElementUtil.allPopulatedChildElements(theType ); // TODO: implement
|
return ca.uhn.fhir.util.ElementUtil.allPopulatedChildElements(theType ); // not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@ package ca.uhn.fhir.narrative;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
@ -77,7 +76,7 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGenerateDiagnosticReportWithObservations() throws DataFormatException, IOException {
|
public void testGenerateDiagnosticReportWithObservations() throws DataFormatException {
|
||||||
DiagnosticReport value = new DiagnosticReport();
|
DiagnosticReport value = new DiagnosticReport();
|
||||||
|
|
||||||
value.getIssued().setValueAsString("2011-02-22T11:13:00");
|
value.getIssued().setValueAsString("2011-02-22T11:13:00");
|
||||||
|
|
|
@ -23,6 +23,7 @@ import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.BundleEntry;
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.api.annotation.Child;
|
import ca.uhn.fhir.model.api.annotation.Child;
|
||||||
import ca.uhn.fhir.model.api.annotation.Extension;
|
import ca.uhn.fhir.model.api.annotation.Extension;
|
||||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||||
|
@ -50,19 +51,73 @@ import ca.uhn.fhir.narrative.INarrativeGenerator;
|
||||||
public class JsonParserTest {
|
public class JsonParserTest {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserTest.class);
|
||||||
|
|
||||||
/**
|
|
||||||
* This sample has extra elements in <searchParam> that are not actually a part of the spec any more..
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
public void testParseFuroreMetadataWithExtraElements() throws IOException {
|
public void testTagList() {
|
||||||
String msg = IOUtils.toString(JsonParserTest.class.getResourceAsStream("/furore-conformance.json"));
|
|
||||||
|
//@formatter:off
|
||||||
|
String tagListStr = "{\n" +
|
||||||
|
" \"resourceType\" : \"TagList\", " +
|
||||||
|
" \"category\" : [" +
|
||||||
|
" { " +
|
||||||
|
" \"term\" : \"term0\", " +
|
||||||
|
" \"label\" : \"label0\", " +
|
||||||
|
" \"scheme\" : \"scheme0\" " +
|
||||||
|
" }," +
|
||||||
|
" { " +
|
||||||
|
" \"term\" : \"term1\", " +
|
||||||
|
" \"label\" : \"label1\", " +
|
||||||
|
" \"scheme\" : null " +
|
||||||
|
" }," +
|
||||||
|
" { " +
|
||||||
|
" \"term\" : \"term2\", " +
|
||||||
|
" \"label\" : \"label2\" " +
|
||||||
|
" }" +
|
||||||
|
" ] " +
|
||||||
|
"}";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
TagList tagList = new FhirContext().newJsonParser().parseTagList(tagListStr);
|
||||||
|
assertEquals(3, tagList.size());
|
||||||
|
assertEquals("term0", tagList.get(0).getTerm());
|
||||||
|
assertEquals("label0", tagList.get(0).getLabel());
|
||||||
|
assertEquals("scheme0", tagList.get(0).getScheme());
|
||||||
|
assertEquals("term1", tagList.get(1).getTerm());
|
||||||
|
assertEquals("label1", tagList.get(1).getLabel());
|
||||||
|
assertEquals(null, tagList.get(1).getScheme());
|
||||||
|
assertEquals("term2", tagList.get(2).getTerm());
|
||||||
|
assertEquals("label2", tagList.get(2).getLabel());
|
||||||
|
assertEquals(null, tagList.get(2).getScheme());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encode
|
||||||
|
*/
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
String expected = "{" +
|
||||||
|
"\"resourceType\":\"TagList\"," +
|
||||||
|
"\"category\":[" +
|
||||||
|
"{" +
|
||||||
|
"\"term\":\"term0\"," +
|
||||||
|
"\"label\":\"label0\"," +
|
||||||
|
"\"scheme\":\"scheme0\"" +
|
||||||
|
"}," +
|
||||||
|
"{" +
|
||||||
|
"\"term\":\"term1\"," +
|
||||||
|
"\"label\":\"label1\"" +
|
||||||
|
"}," +
|
||||||
|
"{" +
|
||||||
|
"\"term\":\"term2\"," +
|
||||||
|
"\"label\":\"label2\"" +
|
||||||
|
"}" +
|
||||||
|
"]" +
|
||||||
|
"}";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
String encoded = new FhirContext().newJsonParser().encodeTagListToString(tagList);
|
||||||
|
assertEquals(expected,encoded);
|
||||||
|
|
||||||
IParser p = new FhirContext(ValueSet.class).newJsonParser();
|
|
||||||
Conformance conf = p.parseResource(Conformance.class, msg);
|
|
||||||
RestResource res = conf.getRestFirstRep().getResourceFirstRep();
|
|
||||||
assertEquals("_id", res.getSearchParam().get(1).getName().getValue());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeBundleCategory() {
|
public void testEncodeBundleCategory() {
|
||||||
|
|
||||||
|
@ -88,368 +143,6 @@ public class JsonParserTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBrokenPatient() {
|
|
||||||
|
|
||||||
String resource = "{\n" +
|
|
||||||
" \"resourceType\": \"Patient\",\n" +
|
|
||||||
" \"extension\": [\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"url\": \"http://mayoweb.mayo.edu/dms-it/reference/data_standards/reg-ctable-race.html#curr_race\",\n" +
|
|
||||||
" \"valueCodeableConcept\": {\n" +
|
|
||||||
" \"coding\": [\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"system\": \"http://mayoweb.mayo.edu/dms-it/reference/data_standards/reg-ctable-race.html#curr_race\",\n" +
|
|
||||||
" \"code\": \"O\",\n" +
|
|
||||||
" \"display\": \"Other\"\n" +
|
|
||||||
" }\n" +
|
|
||||||
" ]\n" +
|
|
||||||
" }\n" +
|
|
||||||
" },\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"url\": \"http://mayoweb.mayo.edu/dms-it/reference/data_standards/reg-ctable-race.html#curr_ethn\",\n" +
|
|
||||||
" \"valueCodeableConcept\": {\n" +
|
|
||||||
" \"coding\": [\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"system\": \"http://mayoweb.mayo.edu/dms-it/reference/data_standards/reg-ctable-race.html#curr_ethn\",\n" +
|
|
||||||
" \"code\": \"HA\",\n" +
|
|
||||||
" \"display\": \"Central American\"\n" +
|
|
||||||
" }\n" +
|
|
||||||
" ]\n" +
|
|
||||||
" }\n" +
|
|
||||||
" }\n" +
|
|
||||||
" ],\n" +
|
|
||||||
" \"identifier\": [\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"label\": \"MAYO-CLINIC-NUMBER\",\n" +
|
|
||||||
" \"value\": \"3982540\"\n" +
|
|
||||||
" }\n" +
|
|
||||||
" ],\n" +
|
|
||||||
" \"name\": [\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"use\": \"usual\",\n" +
|
|
||||||
" \"text\": \"Hurt, Mr. John Stevens\",\n" +
|
|
||||||
" \"family\": [\n" +
|
|
||||||
" \"Hurt\"\n" +
|
|
||||||
" ],\n" +
|
|
||||||
" \"given\": [\n" +
|
|
||||||
" \"John Stevens\"\n" +
|
|
||||||
" ],\n" +
|
|
||||||
" \"prefix\": [\n" +
|
|
||||||
" \"Mr.\"\n" +
|
|
||||||
" ],\n" +
|
|
||||||
" \"suffix\": [\n" +
|
|
||||||
" null\n" +
|
|
||||||
" ]\n" +
|
|
||||||
" }\n" +
|
|
||||||
" ],\n" +
|
|
||||||
" \"telecom\": [\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"system\": \"phone\",\n" +
|
|
||||||
" \"value\": \"(507)284-0122\"\n" +
|
|
||||||
" }\n" +
|
|
||||||
" ],\n" +
|
|
||||||
" \"gender\": {\n" +
|
|
||||||
" \"coding\": [\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"system\": \"http://hl7.org/fhir/v3/AdministrativeGender\",\n" +
|
|
||||||
" \"code\": \"M\",\n" +
|
|
||||||
" \"display\": \"MALE\"\n" +
|
|
||||||
" }\n" +
|
|
||||||
" ]\n" +
|
|
||||||
" },\n" +
|
|
||||||
" \"birthDate\": \"1986-05-01T00:00:00-05:00\",\n" +
|
|
||||||
" \"deceasedDateTime\": \"0001-01-01T00:00:00-06:00\",\n" +
|
|
||||||
" \"address\": [\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"use\": \"home\",\n" +
|
|
||||||
" \"line\": [\n" +
|
|
||||||
" \"100 Quality Control Avenue\"\n" +
|
|
||||||
" ],\n" +
|
|
||||||
" \"city\": \"Test Patient\",\n" +
|
|
||||||
" \"state\": \"MINNESOTA\",\n" +
|
|
||||||
" \"zip\": \"55905\",\n" +
|
|
||||||
" \"country\": \"USA\"\n" +
|
|
||||||
" }\n" +
|
|
||||||
" ],\n" +
|
|
||||||
" \"maritalStatus\": {\n" +
|
|
||||||
" \"coding\": [\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"system\": \"http://hl7.org/fhir/v3/MaritalStatus\",\n" +
|
|
||||||
" \"code\": \"M\",\n" +
|
|
||||||
" \"display\": \"Married\"\n" +
|
|
||||||
" }\n" +
|
|
||||||
" ]\n" +
|
|
||||||
" },\n" +
|
|
||||||
" \"communication\": [\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"coding\": [\n" +
|
|
||||||
" {\n" +
|
|
||||||
" \"code\": \"ENG\",\n" +
|
|
||||||
" \"display\": \"ENGLISH\"\n" +
|
|
||||||
" }\n" +
|
|
||||||
" ]\n" +
|
|
||||||
" }\n" +
|
|
||||||
" ]\n" +
|
|
||||||
"}";
|
|
||||||
|
|
||||||
FhirContext ctx = new FhirContext();
|
|
||||||
Patient patient = ctx.newJsonParser().parseResource(Patient.class, resource);
|
|
||||||
System.out.println(patient.getNameFirstRep().getFamilyAsSingleString());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeExtensionWithResourceContent() throws IOException {
|
|
||||||
IParser parser = new FhirContext().newJsonParser();
|
|
||||||
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.addAddress().setUse(AddressUseEnum.HOME);
|
|
||||||
patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt(Organization.class, "123"));
|
|
||||||
|
|
||||||
String val = parser.encodeResourceToString(patient);
|
|
||||||
ourLog.info(val);
|
|
||||||
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"resource\":\"Organization/123\"}}]"));
|
|
||||||
|
|
||||||
Patient actual = parser.parseResource(Patient.class, val);
|
|
||||||
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
|
||||||
List<ExtensionDt> ext = actual.getUndeclaredExtensionsByUrl("urn:foo");
|
|
||||||
assertEquals(1, ext.size());
|
|
||||||
ResourceReferenceDt ref = (ResourceReferenceDt) ext.get(0).getValue();
|
|
||||||
assertEquals("Organization/123", ref.getResourceUrl());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeDeclaredExtensionWithResourceContent() throws IOException {
|
|
||||||
IParser parser = new FhirContext().newJsonParser();
|
|
||||||
|
|
||||||
MyPatientWithOneDeclaredExtension patient = new MyPatientWithOneDeclaredExtension();
|
|
||||||
patient.addAddress().setUse(AddressUseEnum.HOME);
|
|
||||||
patient.setFoo(new ResourceReferenceDt(Organization.class, "123"));
|
|
||||||
|
|
||||||
String val = parser.encodeResourceToString(patient);
|
|
||||||
ourLog.info(val);
|
|
||||||
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"resource\":\"Organization/123\"}}]"));
|
|
||||||
|
|
||||||
MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
|
|
||||||
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
|
||||||
ResourceReferenceDt ref = actual.getFoo();
|
|
||||||
assertEquals("Organization/123", ref.getResourceUrl());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeDeclaredExtensionWithAddressContent() throws IOException {
|
|
||||||
IParser parser = new FhirContext().newJsonParser();
|
|
||||||
|
|
||||||
MyPatientWithOneDeclaredAddressExtension patient = new MyPatientWithOneDeclaredAddressExtension();
|
|
||||||
patient.addAddress().setUse(AddressUseEnum.HOME);
|
|
||||||
patient.setFoo(new AddressDt().addLine("line1"));
|
|
||||||
|
|
||||||
String val = parser.encodeResourceToString(patient);
|
|
||||||
ourLog.info(val);
|
|
||||||
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueAddress\":{\"line\":[\"line1\"]}}]"));
|
|
||||||
|
|
||||||
MyPatientWithOneDeclaredAddressExtension actual = parser.parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
|
|
||||||
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
|
||||||
AddressDt ref = actual.getFoo();
|
|
||||||
assertEquals("line1", ref.getLineFirstRep().getValue());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeUndeclaredExtensionWithAddressContent() throws IOException {
|
|
||||||
IParser parser = new FhirContext().newJsonParser();
|
|
||||||
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.addAddress().setUse(AddressUseEnum.HOME);
|
|
||||||
patient.addUndeclaredExtension(false, "urn:foo", new AddressDt().addLine("line1"));
|
|
||||||
|
|
||||||
String val = parser.encodeResourceToString(patient);
|
|
||||||
ourLog.info(val);
|
|
||||||
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueAddress\":{\"line\":[\"line1\"]}}]"));
|
|
||||||
|
|
||||||
MyPatientWithOneDeclaredAddressExtension actual = parser.parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
|
|
||||||
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
|
||||||
AddressDt ref = actual.getFoo();
|
|
||||||
assertEquals("line1", ref.getLineFirstRep().getValue());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ResourceDef(name = "Patient")
|
|
||||||
public static class MyPatientWithOneDeclaredExtension extends Patient {
|
|
||||||
|
|
||||||
@Child(order = 0, name = "foo")
|
|
||||||
@Extension(url = "urn:foo", definedLocally = true, isModifier = false)
|
|
||||||
private ResourceReferenceDt myFoo;
|
|
||||||
|
|
||||||
public ResourceReferenceDt getFoo() {
|
|
||||||
return myFoo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFoo(ResourceReferenceDt theFoo) {
|
|
||||||
myFoo = theFoo;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@ResourceDef(name = "Patient")
|
|
||||||
public static class MyPatientWithOneDeclaredAddressExtension extends Patient {
|
|
||||||
|
|
||||||
@Child(order = 0, name = "foo")
|
|
||||||
@Extension(url = "urn:foo", definedLocally = true, isModifier = false)
|
|
||||||
private AddressDt myFoo;
|
|
||||||
|
|
||||||
public AddressDt getFoo() {
|
|
||||||
return myFoo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFoo(AddressDt theFoo) {
|
|
||||||
myFoo = theFoo;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeExt() throws Exception {
|
|
||||||
|
|
||||||
ValueSet valueSet = new ValueSet();
|
|
||||||
Define define = valueSet.getDefine();
|
|
||||||
DefineConcept code = define.addConcept();
|
|
||||||
code.setCode("someCode");
|
|
||||||
code.setDisplay("someDisplay");
|
|
||||||
code.addUndeclaredExtension(false, "urn:alt", new StringDt("alt name"));
|
|
||||||
|
|
||||||
String encoded = new FhirContext().newJsonParser().encodeResourceToString(valueSet);
|
|
||||||
ourLog.info(encoded);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeResourceRef() throws DataFormatException, IOException {
|
|
||||||
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.setManagingOrganization(new ResourceReferenceDt());
|
|
||||||
|
|
||||||
IParser p = new FhirContext().newJsonParser();
|
|
||||||
String str = p.encodeResourceToString(patient);
|
|
||||||
assertThat(str, IsNot.not(StringContains.containsString("managingOrganization")));
|
|
||||||
|
|
||||||
patient.setManagingOrganization(new ResourceReferenceDt(Organization.class, "123"));
|
|
||||||
str = p.encodeResourceToString(patient);
|
|
||||||
assertThat(str, StringContains.containsString("\"managingOrganization\":{\"resource\":\"Organization/123\"}"));
|
|
||||||
|
|
||||||
Organization org = new Organization();
|
|
||||||
org.addIdentifier().setSystem("foo").setValue("bar");
|
|
||||||
patient.setManagingOrganization(new ResourceReferenceDt(org));
|
|
||||||
str = p.encodeResourceToString(patient);
|
|
||||||
assertThat(str, StringContains.containsString("\"contained\":[{\"resourceType\":\"Organization\""));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNarrativeGeneration() throws DataFormatException, IOException {
|
|
||||||
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.addName().addFamily("Smith");
|
|
||||||
Organization org = new Organization();
|
|
||||||
patient.getManagingOrganization().setResource(org);
|
|
||||||
|
|
||||||
INarrativeGenerator gen = mock(INarrativeGenerator.class);
|
|
||||||
XhtmlDt xhtmlDt = new XhtmlDt("<div>help</div>");
|
|
||||||
NarrativeDt nar = new NarrativeDt(xhtmlDt, NarrativeStatusEnum.GENERATED);
|
|
||||||
when(gen.generateNarrative(eq("http://hl7.org/fhir/profiles/Patient"), eq(patient))).thenReturn(nar);
|
|
||||||
|
|
||||||
FhirContext context = new FhirContext();
|
|
||||||
context.setNarrativeGenerator(gen);
|
|
||||||
IParser p = context.newJsonParser();
|
|
||||||
p.encodeResourceToWriter(patient, new OutputStreamWriter(System.out));
|
|
||||||
String str = p.encodeResourceToString(patient);
|
|
||||||
|
|
||||||
ourLog.info(str);
|
|
||||||
|
|
||||||
assertThat(str, StringContains.containsString(",\"text\":{\"status\":\"generated\",\"div\":\"<div>help</div>\"},"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSimpleResourceEncode() throws IOException {
|
|
||||||
|
|
||||||
FhirContext ctx = new FhirContext(Observation.class);
|
|
||||||
String xmlString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.xml"), Charset.forName("UTF-8"));
|
|
||||||
Patient obs = ctx.newXmlParser().parseResource(Patient.class, xmlString);
|
|
||||||
|
|
||||||
List<ExtensionDt> undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getUndeclaredExtensions();
|
|
||||||
ExtensionDt undeclaredExtension = undeclaredExtensions.get(0);
|
|
||||||
assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue());
|
|
||||||
|
|
||||||
ctx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
|
|
||||||
|
|
||||||
IParser jsonParser = ctx.newJsonParser();
|
|
||||||
String encoded = jsonParser.encodeResourceToString(obs);
|
|
||||||
ourLog.info(encoded);
|
|
||||||
|
|
||||||
String jsonString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.json"));
|
|
||||||
|
|
||||||
JSON expected = JSONSerializer.toJSON(jsonString);
|
|
||||||
JSON actual = JSONSerializer.toJSON(encoded.trim());
|
|
||||||
|
|
||||||
ourLog.info("Expected: {}", expected);
|
|
||||||
ourLog.info("Actual : {}", actual);
|
|
||||||
assertEquals(expected.toString(), actual.toString());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSimpleResourceEncodeWithCustomType() throws IOException {
|
|
||||||
|
|
||||||
FhirContext ctx = new FhirContext(MyObservationWithExtensions.class);
|
|
||||||
String xmlString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.xml"), Charset.forName("UTF-8"));
|
|
||||||
MyObservationWithExtensions obs = ctx.newXmlParser().parseResource(MyObservationWithExtensions.class, xmlString);
|
|
||||||
|
|
||||||
assertEquals(0, obs.getAllUndeclaredExtensions().size());
|
|
||||||
assertEquals("aaaa", obs.getExtAtt().getContentType().getValue());
|
|
||||||
assertEquals("str1", obs.getMoreExt().getStr1().getValue());
|
|
||||||
assertEquals("2011-01-02", obs.getModExt().getValueAsString());
|
|
||||||
|
|
||||||
List<ExtensionDt> undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getUndeclaredExtensions();
|
|
||||||
ExtensionDt undeclaredExtension = undeclaredExtensions.get(0);
|
|
||||||
assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue());
|
|
||||||
|
|
||||||
ctx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
|
|
||||||
|
|
||||||
IParser jsonParser = ctx.newJsonParser();
|
|
||||||
String encoded = jsonParser.encodeResourceToString(obs);
|
|
||||||
ourLog.info(encoded);
|
|
||||||
|
|
||||||
String jsonString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.json"));
|
|
||||||
|
|
||||||
JSON expected = JSONSerializer.toJSON(jsonString);
|
|
||||||
JSON actual = JSONSerializer.toJSON(encoded.trim());
|
|
||||||
|
|
||||||
ourLog.info("Expected: {}", expected);
|
|
||||||
ourLog.info("Actual : {}", actual);
|
|
||||||
assertEquals(expected.toString(), actual.toString());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSimpleBundleEncode() throws IOException {
|
|
||||||
|
|
||||||
FhirContext ctx = new FhirContext(Observation.class, Patient.class);
|
|
||||||
String xmlString = IOUtils.toString(JsonParser.class.getResourceAsStream("/atom-document-large.xml"), Charset.forName("UTF-8"));
|
|
||||||
Bundle obs = ctx.newXmlParser().parseBundle(xmlString);
|
|
||||||
|
|
||||||
String encoded = ctx.newJsonParser().encodeBundleToString(obs);
|
|
||||||
ourLog.info(encoded);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeContainedResources() throws IOException {
|
public void testEncodeContainedResources() throws IOException {
|
||||||
|
|
||||||
|
@ -462,69 +155,10 @@ public class JsonParserTest {
|
||||||
ourLog.info(encoded);
|
ourLog.info(encoded);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleParse() throws DataFormatException, IOException {
|
public void testEncodeContainedResourcesMore() {
|
||||||
|
|
||||||
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/example-patient-general.json"));
|
|
||||||
FhirContext ctx = new FhirContext(Patient.class);
|
|
||||||
IParser p = ctx.newJsonParser();
|
|
||||||
// ourLog.info("Reading in message: {}", msg);
|
|
||||||
Patient res = p.parseResource(Patient.class, msg);
|
|
||||||
|
|
||||||
assertEquals(2, res.getUndeclaredExtensions().size());
|
|
||||||
assertEquals(1, res.getUndeclaredModifierExtensions().size());
|
|
||||||
|
|
||||||
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
|
|
||||||
ourLog.info(encoded);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testParseBundle() throws DataFormatException, IOException {
|
|
||||||
|
|
||||||
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/atom-document-large.json"));
|
|
||||||
FhirContext ctx = new FhirContext(Patient.class);
|
|
||||||
IParser p = ctx.newJsonParser();
|
|
||||||
Bundle bundle = p.parseBundle(msg);
|
|
||||||
|
|
||||||
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeBundleToString(bundle);
|
|
||||||
ourLog.info(encoded);
|
|
||||||
|
|
||||||
assertEquals("http://fhir.healthintersections.com.au/open/DiagnosticReport/_search?_format=application/json+fhir&search-id=46d5f0e7-9240-4d4f-9f51-f8ac975c65&search-sort=_id", bundle
|
|
||||||
.getLinkSelf().getValue());
|
|
||||||
assertEquals("urn:uuid:0b754ff9-03cf-4322-a119-15019af8a3", bundle.getBundleId().getValue());
|
|
||||||
|
|
||||||
BundleEntry entry = bundle.getEntries().get(0);
|
|
||||||
assertEquals("http://fhir.healthintersections.com.au/open/DiagnosticReport/101", entry.getId().getValue());
|
|
||||||
assertEquals("http://fhir.healthintersections.com.au/open/DiagnosticReport/101/_history/1", entry.getLinkSelf().getValue());
|
|
||||||
assertEquals("2014-03-10T11:55:59Z", entry.getUpdated().getValueAsString());
|
|
||||||
|
|
||||||
DiagnosticReport res = (DiagnosticReport) entry.getResource();
|
|
||||||
assertEquals("Complete Blood Count", res.getName().getText().getValue());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testParseWithContained() throws DataFormatException, IOException {
|
|
||||||
|
|
||||||
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/diagnostic-report.json"));
|
|
||||||
FhirContext ctx = new FhirContext(Patient.class);
|
|
||||||
IParser p = ctx.newJsonParser();
|
|
||||||
// ourLog.info("Reading in message: {}", msg);
|
|
||||||
DiagnosticReport res = p.parseResource(DiagnosticReport.class, msg);
|
|
||||||
|
|
||||||
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
|
|
||||||
ourLog.info(encoded);
|
|
||||||
|
|
||||||
ResourceReferenceDt reference = res.getResult().get(1);
|
|
||||||
Observation obs = (Observation) reference.getResource();
|
|
||||||
|
|
||||||
assertEquals("789-8", obs.getName().getCoding().get(0).getCode().getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEncodeContainedResourcesMore() throws IOException {
|
|
||||||
|
|
||||||
DiagnosticReport rpt = new DiagnosticReport();
|
DiagnosticReport rpt = new DiagnosticReport();
|
||||||
Specimen spm = new Specimen();
|
Specimen spm = new Specimen();
|
||||||
|
@ -548,7 +182,85 @@ public class JsonParserTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeInvalidChildGoodException() throws IOException {
|
public void testEncodeDeclaredExtensionWithAddressContent() {
|
||||||
|
IParser parser = new FhirContext().newJsonParser();
|
||||||
|
|
||||||
|
MyPatientWithOneDeclaredAddressExtension patient = new MyPatientWithOneDeclaredAddressExtension();
|
||||||
|
patient.addAddress().setUse(AddressUseEnum.HOME);
|
||||||
|
patient.setFoo(new AddressDt().addLine("line1"));
|
||||||
|
|
||||||
|
String val = parser.encodeResourceToString(patient);
|
||||||
|
ourLog.info(val);
|
||||||
|
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueAddress\":{\"line\":[\"line1\"]}}]"));
|
||||||
|
|
||||||
|
MyPatientWithOneDeclaredAddressExtension actual = parser.parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
|
||||||
|
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
||||||
|
AddressDt ref = actual.getFoo();
|
||||||
|
assertEquals("line1", ref.getLineFirstRep().getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeDeclaredExtensionWithResourceContent() {
|
||||||
|
IParser parser = new FhirContext().newJsonParser();
|
||||||
|
|
||||||
|
MyPatientWithOneDeclaredExtension patient = new MyPatientWithOneDeclaredExtension();
|
||||||
|
patient.addAddress().setUse(AddressUseEnum.HOME);
|
||||||
|
patient.setFoo(new ResourceReferenceDt(Organization.class, "123"));
|
||||||
|
|
||||||
|
String val = parser.encodeResourceToString(patient);
|
||||||
|
ourLog.info(val);
|
||||||
|
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"resource\":\"Organization/123\"}}]"));
|
||||||
|
|
||||||
|
MyPatientWithOneDeclaredExtension actual = parser.parseResource(MyPatientWithOneDeclaredExtension.class, val);
|
||||||
|
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
||||||
|
ResourceReferenceDt ref = actual.getFoo();
|
||||||
|
assertEquals("Organization/123", ref.getResourceUrl());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeExt() throws Exception {
|
||||||
|
|
||||||
|
ValueSet valueSet = new ValueSet();
|
||||||
|
Define define = valueSet.getDefine();
|
||||||
|
DefineConcept code = define.addConcept();
|
||||||
|
code.setCode("someCode");
|
||||||
|
code.setDisplay("someDisplay");
|
||||||
|
code.addUndeclaredExtension(false, "urn:alt", new StringDt("alt name"));
|
||||||
|
|
||||||
|
String encoded = new FhirContext().newJsonParser().encodeResourceToString(valueSet);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeExtensionWithResourceContent() {
|
||||||
|
IParser parser = new FhirContext().newJsonParser();
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addAddress().setUse(AddressUseEnum.HOME);
|
||||||
|
patient.addUndeclaredExtension(false, "urn:foo", new ResourceReferenceDt(Organization.class, "123"));
|
||||||
|
|
||||||
|
String val = parser.encodeResourceToString(patient);
|
||||||
|
ourLog.info(val);
|
||||||
|
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueResource\":{\"resource\":\"Organization/123\"}}]"));
|
||||||
|
|
||||||
|
Patient actual = parser.parseResource(Patient.class, val);
|
||||||
|
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
||||||
|
List<ExtensionDt> ext = actual.getUndeclaredExtensionsByUrl("urn:foo");
|
||||||
|
assertEquals(1, ext.size());
|
||||||
|
ResourceReferenceDt ref = (ResourceReferenceDt) ext.get(0).getValue();
|
||||||
|
assertEquals("Organization/123", ref.getResourceUrl());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeInvalidChildGoodException() {
|
||||||
Observation obs = new Observation();
|
Observation obs = new Observation();
|
||||||
obs.setValue(new DecimalDt(112.22));
|
obs.setValue(new DecimalDt(112.22));
|
||||||
|
|
||||||
|
@ -560,6 +272,47 @@ public class JsonParserTest {
|
||||||
assertThat(e.getMessage(), StringContains.containsString("PeriodDt"));
|
assertThat(e.getMessage(), StringContains.containsString("PeriodDt"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeResourceRef() throws DataFormatException {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setManagingOrganization(new ResourceReferenceDt());
|
||||||
|
|
||||||
|
IParser p = new FhirContext().newJsonParser();
|
||||||
|
String str = p.encodeResourceToString(patient);
|
||||||
|
assertThat(str, IsNot.not(StringContains.containsString("managingOrganization")));
|
||||||
|
|
||||||
|
patient.setManagingOrganization(new ResourceReferenceDt(Organization.class, "123"));
|
||||||
|
str = p.encodeResourceToString(patient);
|
||||||
|
assertThat(str, StringContains.containsString("\"managingOrganization\":{\"resource\":\"Organization/123\"}"));
|
||||||
|
|
||||||
|
Organization org = new Organization();
|
||||||
|
org.addIdentifier().setSystem("foo").setValue("bar");
|
||||||
|
patient.setManagingOrganization(new ResourceReferenceDt(org));
|
||||||
|
str = p.encodeResourceToString(patient);
|
||||||
|
assertThat(str, StringContains.containsString("\"contained\":[{\"resourceType\":\"Organization\""));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeUndeclaredExtensionWithAddressContent() {
|
||||||
|
IParser parser = new FhirContext().newJsonParser();
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addAddress().setUse(AddressUseEnum.HOME);
|
||||||
|
patient.addUndeclaredExtension(false, "urn:foo", new AddressDt().addLine("line1"));
|
||||||
|
|
||||||
|
String val = parser.encodeResourceToString(patient);
|
||||||
|
ourLog.info(val);
|
||||||
|
assertThat(val, StringContains.containsString("\"extension\":[{\"url\":\"urn:foo\",\"valueAddress\":{\"line\":[\"line1\"]}}]"));
|
||||||
|
|
||||||
|
MyPatientWithOneDeclaredAddressExtension actual = parser.parseResource(MyPatientWithOneDeclaredAddressExtension.class, val);
|
||||||
|
assertEquals(AddressUseEnum.HOME, patient.getAddressFirstRep().getUse().getValueAsEnum());
|
||||||
|
AddressDt ref = actual.getFoo();
|
||||||
|
assertEquals("line1", ref.getLineFirstRep().getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExtensionOnComposite() throws Exception {
|
public void testExtensionOnComposite() throws Exception {
|
||||||
|
@ -654,4 +407,208 @@ public class JsonParserTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNarrativeGeneration() throws DataFormatException, IOException {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addName().addFamily("Smith");
|
||||||
|
Organization org = new Organization();
|
||||||
|
patient.getManagingOrganization().setResource(org);
|
||||||
|
|
||||||
|
INarrativeGenerator gen = mock(INarrativeGenerator.class);
|
||||||
|
XhtmlDt xhtmlDt = new XhtmlDt("<div>help</div>");
|
||||||
|
NarrativeDt nar = new NarrativeDt(xhtmlDt, NarrativeStatusEnum.GENERATED);
|
||||||
|
when(gen.generateNarrative(eq("http://hl7.org/fhir/profiles/Patient"), eq(patient))).thenReturn(nar);
|
||||||
|
|
||||||
|
FhirContext context = new FhirContext();
|
||||||
|
context.setNarrativeGenerator(gen);
|
||||||
|
IParser p = context.newJsonParser();
|
||||||
|
p.encodeResourceToWriter(patient, new OutputStreamWriter(System.out));
|
||||||
|
String str = p.encodeResourceToString(patient);
|
||||||
|
|
||||||
|
ourLog.info(str);
|
||||||
|
|
||||||
|
assertThat(str, StringContains.containsString(",\"text\":{\"status\":\"generated\",\"div\":\"<div>help</div>\"},"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseBundle() throws DataFormatException, IOException {
|
||||||
|
|
||||||
|
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/atom-document-large.json"));
|
||||||
|
FhirContext ctx = new FhirContext(Patient.class);
|
||||||
|
IParser p = ctx.newJsonParser();
|
||||||
|
Bundle bundle = p.parseBundle(msg);
|
||||||
|
|
||||||
|
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeBundleToString(bundle);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
assertEquals("http://fhir.healthintersections.com.au/open/DiagnosticReport/_search?_format=application/json+fhir&search-id=46d5f0e7-9240-4d4f-9f51-f8ac975c65&search-sort=_id", bundle
|
||||||
|
.getLinkSelf().getValue());
|
||||||
|
assertEquals("urn:uuid:0b754ff9-03cf-4322-a119-15019af8a3", bundle.getBundleId().getValue());
|
||||||
|
|
||||||
|
BundleEntry entry = bundle.getEntries().get(0);
|
||||||
|
assertEquals("http://fhir.healthintersections.com.au/open/DiagnosticReport/101", entry.getId().getValue());
|
||||||
|
assertEquals("http://fhir.healthintersections.com.au/open/DiagnosticReport/101/_history/1", entry.getLinkSelf().getValue());
|
||||||
|
assertEquals("2014-03-10T11:55:59Z", entry.getUpdated().getValueAsString());
|
||||||
|
|
||||||
|
DiagnosticReport res = (DiagnosticReport) entry.getResource();
|
||||||
|
assertEquals("Complete Blood Count", res.getName().getText().getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This sample has extra elements in <searchParam> that are not actually a part of the spec any more..
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testParseFuroreMetadataWithExtraElements() throws IOException {
|
||||||
|
String msg = IOUtils.toString(JsonParserTest.class.getResourceAsStream("/furore-conformance.json"));
|
||||||
|
|
||||||
|
IParser p = new FhirContext(ValueSet.class).newJsonParser();
|
||||||
|
Conformance conf = p.parseResource(Conformance.class, msg);
|
||||||
|
RestResource res = conf.getRestFirstRep().getResourceFirstRep();
|
||||||
|
assertEquals("_id", res.getSearchParam().get(1).getName().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseWithContained() throws DataFormatException, IOException {
|
||||||
|
|
||||||
|
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/diagnostic-report.json"));
|
||||||
|
FhirContext ctx = new FhirContext(Patient.class);
|
||||||
|
IParser p = ctx.newJsonParser();
|
||||||
|
// ourLog.info("Reading in message: {}", msg);
|
||||||
|
DiagnosticReport res = p.parseResource(DiagnosticReport.class, msg);
|
||||||
|
|
||||||
|
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
ResourceReferenceDt reference = res.getResult().get(1);
|
||||||
|
Observation obs = (Observation) reference.getResource();
|
||||||
|
|
||||||
|
assertEquals("789-8", obs.getName().getCoding().get(0).getCode().getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimpleBundleEncode() throws IOException {
|
||||||
|
|
||||||
|
FhirContext ctx = new FhirContext(Observation.class, Patient.class);
|
||||||
|
String xmlString = IOUtils.toString(JsonParser.class.getResourceAsStream("/atom-document-large.xml"), Charset.forName("UTF-8"));
|
||||||
|
Bundle obs = ctx.newXmlParser().parseBundle(xmlString);
|
||||||
|
|
||||||
|
String encoded = ctx.newJsonParser().encodeBundleToString(obs);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimpleParse() throws DataFormatException, IOException {
|
||||||
|
|
||||||
|
String msg = IOUtils.toString(XmlParser.class.getResourceAsStream("/example-patient-general.json"));
|
||||||
|
FhirContext ctx = new FhirContext(Patient.class);
|
||||||
|
IParser p = ctx.newJsonParser();
|
||||||
|
// ourLog.info("Reading in message: {}", msg);
|
||||||
|
Patient res = p.parseResource(Patient.class, msg);
|
||||||
|
|
||||||
|
assertEquals(2, res.getUndeclaredExtensions().size());
|
||||||
|
assertEquals(1, res.getUndeclaredModifierExtensions().size());
|
||||||
|
|
||||||
|
String encoded = ctx.newXmlParser().setPrettyPrint(true).encodeResourceToString(res);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimpleResourceEncode() throws IOException {
|
||||||
|
|
||||||
|
FhirContext ctx = new FhirContext(Observation.class);
|
||||||
|
String xmlString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.xml"), Charset.forName("UTF-8"));
|
||||||
|
Patient obs = ctx.newXmlParser().parseResource(Patient.class, xmlString);
|
||||||
|
|
||||||
|
List<ExtensionDt> undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getUndeclaredExtensions();
|
||||||
|
ExtensionDt undeclaredExtension = undeclaredExtensions.get(0);
|
||||||
|
assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue());
|
||||||
|
|
||||||
|
ctx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
|
||||||
|
|
||||||
|
IParser jsonParser = ctx.newJsonParser();
|
||||||
|
String encoded = jsonParser.encodeResourceToString(obs);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
String jsonString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.json"));
|
||||||
|
|
||||||
|
JSON expected = JSONSerializer.toJSON(jsonString);
|
||||||
|
JSON actual = JSONSerializer.toJSON(encoded.trim());
|
||||||
|
|
||||||
|
ourLog.info("Expected: {}", expected);
|
||||||
|
ourLog.info("Actual : {}", actual);
|
||||||
|
assertEquals(expected.toString(), actual.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSimpleResourceEncodeWithCustomType() throws IOException {
|
||||||
|
|
||||||
|
FhirContext ctx = new FhirContext(MyObservationWithExtensions.class);
|
||||||
|
String xmlString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.xml"), Charset.forName("UTF-8"));
|
||||||
|
MyObservationWithExtensions obs = ctx.newXmlParser().parseResource(MyObservationWithExtensions.class, xmlString);
|
||||||
|
|
||||||
|
assertEquals(0, obs.getAllUndeclaredExtensions().size());
|
||||||
|
assertEquals("aaaa", obs.getExtAtt().getContentType().getValue());
|
||||||
|
assertEquals("str1", obs.getMoreExt().getStr1().getValue());
|
||||||
|
assertEquals("2011-01-02", obs.getModExt().getValueAsString());
|
||||||
|
|
||||||
|
List<ExtensionDt> undeclaredExtensions = obs.getContact().get(0).getName().getFamily().get(0).getUndeclaredExtensions();
|
||||||
|
ExtensionDt undeclaredExtension = undeclaredExtensions.get(0);
|
||||||
|
assertEquals("http://hl7.org/fhir/Profile/iso-21090#qualifier", undeclaredExtension.getUrl().getValue());
|
||||||
|
|
||||||
|
ctx.newJsonParser().setPrettyPrint(true).encodeResourceToWriter(obs, new OutputStreamWriter(System.out));
|
||||||
|
|
||||||
|
IParser jsonParser = ctx.newJsonParser();
|
||||||
|
String encoded = jsonParser.encodeResourceToString(obs);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
|
||||||
|
String jsonString = IOUtils.toString(JsonParser.class.getResourceAsStream("/example-patient-general.json"));
|
||||||
|
|
||||||
|
JSON expected = JSONSerializer.toJSON(jsonString);
|
||||||
|
JSON actual = JSONSerializer.toJSON(encoded.trim());
|
||||||
|
|
||||||
|
ourLog.info("Expected: {}", expected);
|
||||||
|
ourLog.info("Actual : {}", actual);
|
||||||
|
assertEquals(expected.toString(), actual.toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResourceDef(name = "Patient")
|
||||||
|
public static class MyPatientWithOneDeclaredAddressExtension extends Patient {
|
||||||
|
|
||||||
|
@Child(order = 0, name = "foo")
|
||||||
|
@Extension(url = "urn:foo", definedLocally = true, isModifier = false)
|
||||||
|
private AddressDt myFoo;
|
||||||
|
|
||||||
|
public AddressDt getFoo() {
|
||||||
|
return myFoo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFoo(AddressDt theFoo) {
|
||||||
|
myFoo = theFoo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ResourceDef(name = "Patient")
|
||||||
|
public static class MyPatientWithOneDeclaredExtension extends Patient {
|
||||||
|
|
||||||
|
@Child(order = 0, name = "foo")
|
||||||
|
@Extension(url = "urn:foo", definedLocally = true, isModifier = false)
|
||||||
|
private ResourceReferenceDt myFoo;
|
||||||
|
|
||||||
|
public ResourceReferenceDt getFoo() {
|
||||||
|
return myFoo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFoo(ResourceReferenceDt theFoo) {
|
||||||
|
myFoo = theFoo;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,8 @@ import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.BundleEntry;
|
import ca.uhn.fhir.model.api.BundleEntry;
|
||||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
import ca.uhn.fhir.model.dstu.composite.AddressDt;
|
import ca.uhn.fhir.model.dstu.composite.AddressDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
import ca.uhn.fhir.model.dstu.composite.HumanNameDt;
|
||||||
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
||||||
|
@ -49,6 +51,7 @@ import ca.uhn.fhir.model.dstu.valueset.IdentifierUseEnum;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum;
|
import ca.uhn.fhir.model.dstu.valueset.NarrativeStatusEnum;
|
||||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||||
import ca.uhn.fhir.model.primitive.DecimalDt;
|
import ca.uhn.fhir.model.primitive.DecimalDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
import ca.uhn.fhir.model.primitive.XhtmlDt;
|
import ca.uhn.fhir.model.primitive.XhtmlDt;
|
||||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
||||||
|
@ -72,6 +75,47 @@ public class XmlParserTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTagList() {
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
String tagListStr = "<taglist xmlns=\"http://hl7.org/fhir\"> \n" +
|
||||||
|
" <category term=\"term0\" label=\"label0\" scheme=\"scheme0\" /> \n" +
|
||||||
|
" <category term=\"term1\" label=\"label1\" scheme=\"\" /> \n" +
|
||||||
|
" <category term=\"term2\" label=\"label2\" /> \n" +
|
||||||
|
"</taglist>";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
TagList tagList = new FhirContext().newXmlParser().parseTagList(tagListStr);
|
||||||
|
assertEquals(3, tagList.size());
|
||||||
|
assertEquals("term0", tagList.get(0).getTerm());
|
||||||
|
assertEquals("label0", tagList.get(0).getLabel());
|
||||||
|
assertEquals("scheme0", tagList.get(0).getScheme());
|
||||||
|
assertEquals("term1", tagList.get(1).getTerm());
|
||||||
|
assertEquals("label1", tagList.get(1).getLabel());
|
||||||
|
assertEquals(null, tagList.get(1).getScheme());
|
||||||
|
assertEquals("term2", tagList.get(2).getTerm());
|
||||||
|
assertEquals("label2", tagList.get(2).getLabel());
|
||||||
|
assertEquals(null, tagList.get(2).getScheme());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encode
|
||||||
|
*/
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
String expected = "<taglist xmlns=\"http://hl7.org/fhir\">" +
|
||||||
|
"<category term=\"term0\" label=\"label0\" scheme=\"scheme0\"/>" +
|
||||||
|
"<category term=\"term1\" label=\"label1\"/>" +
|
||||||
|
"<category term=\"term2\" label=\"label2\"/>" +
|
||||||
|
"</taglist>";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
String encoded = new FhirContext().newXmlParser().encodeTagListToString(tagList);
|
||||||
|
assertEquals(expected,encoded);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTotalResultsUsingOldNamespace() {
|
public void testTotalResultsUsingOldNamespace() {
|
||||||
|
|
||||||
|
@ -541,7 +585,8 @@ public class XmlParserTest {
|
||||||
" <name>HL7, Inc (FHIR Project)</name>\n" +
|
" <name>HL7, Inc (FHIR Project)</name>\n" +
|
||||||
" <uri>http://hl7.org/fhir</uri>\n" +
|
" <uri>http://hl7.org/fhir</uri>\n" +
|
||||||
" </author>\n" +
|
" </author>\n" +
|
||||||
" <published>2014-02-10T04:10:46.987-00:00</published>\n" +
|
" <published>2014-02-10T04:10:46.987-00:00</published>\n" +
|
||||||
|
" <category term=\"term\" label=\"label\" scheme=\"http://foo\"/>\n "+
|
||||||
" <content type=\"text/xml\">\n" +
|
" <content type=\"text/xml\">\n" +
|
||||||
" <ValueSet xmlns=\"http://hl7.org/fhir\">\n" +
|
" <ValueSet xmlns=\"http://hl7.org/fhir\">\n" +
|
||||||
" <text>\n" +
|
" <text>\n" +
|
||||||
|
@ -589,10 +634,29 @@ public class XmlParserTest {
|
||||||
BundleEntry entry = bundle.getEntries().get(0);
|
BundleEntry entry = bundle.getEntries().get(0);
|
||||||
assertEquals("HL7, Inc (FHIR Project)", entry.getAuthorName().getValue());
|
assertEquals("HL7, Inc (FHIR Project)", entry.getAuthorName().getValue());
|
||||||
assertEquals("http://hl7.org/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d", entry.getId().getValue());
|
assertEquals("http://hl7.org/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d", entry.getId().getValue());
|
||||||
|
assertEquals(1, entry.getCategories().size());
|
||||||
|
assertEquals("term", entry.getCategories().get(0).getTerm());
|
||||||
|
assertEquals("label", entry.getCategories().get(0).getLabel());
|
||||||
|
assertEquals("http://foo", entry.getCategories().get(0).getScheme());
|
||||||
|
|
||||||
ValueSet resource = (ValueSet) entry.getResource();
|
ValueSet resource = (ValueSet) entry.getResource();
|
||||||
assertEquals("LOINC Codes for Cholesterol", resource.getName().getValue());
|
assertEquals("LOINC Codes for Cholesterol", resource.getName().getValue());
|
||||||
assertEquals(summaryText.trim(), entry.getSummary().getValueAsString().trim());
|
assertEquals(summaryText.trim(), entry.getSummary().getValueAsString().trim());
|
||||||
|
|
||||||
|
TagList tl = (TagList) resource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||||
|
assertEquals(1, tl.size());
|
||||||
|
assertEquals("term", tl.get(0).getTerm());
|
||||||
|
assertEquals("label", tl.get(0).getLabel());
|
||||||
|
assertEquals("http://foo", tl.get(0).getScheme());
|
||||||
|
|
||||||
|
assertEquals(new IdDt("256a5231-a2bb-49bd-9fea-f349d428b70d"), resource.getId());
|
||||||
|
|
||||||
|
msg = msg.replace("<link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d\" rel=\"self\"/>", "<link href=\"http://hl7.org/implement/standards/fhir/valueset/256a5231-a2bb-49bd-9fea-f349d428b70d/_history/12345\" rel=\"self\"/>");
|
||||||
|
entry = p.parseBundle(msg).getEntries().get(0);
|
||||||
|
resource = (ValueSet) entry.getResource();
|
||||||
|
assertEquals(new IdDt("256a5231-a2bb-49bd-9fea-f349d428b70d"), resource.getId());
|
||||||
|
assertEquals(new IdDt("12345"), resource.getResourceMetadata().get(ResourceMetadataKeyEnum.VERSION_ID));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhir.rest.client;
|
package ca.uhn.fhir.rest.client;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ -49,12 +50,9 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
import ca.uhn.fhir.model.primitive.IntegerDt;
|
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
import ca.uhn.fhir.rest.annotation.Create;
|
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Read;
|
|
||||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||||
|
@ -64,6 +62,7 @@ import ca.uhn.fhir.rest.param.QualifiedDateParam;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||||
|
|
||||||
public class ClientTest {
|
public class ClientTest {
|
||||||
|
@ -107,11 +106,29 @@ public class ClientTest {
|
||||||
assertEquals("200", response.getVersionId().getValue());
|
assertEquals("200", response.getVersionId().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateBad() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier("urn:foo", "123");
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 400, "foobar"));
|
||||||
|
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT + "; charset=UTF-8"));
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("foobar"), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
try {
|
||||||
|
ctx.newRestfulClient(ITestClient.class, "http://foo").createPatient(patient);
|
||||||
|
fail();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertThat(e.getMessage(), StringContains.containsString("foobar"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some servers (older ones?) return the resourcde you created instead
|
* Some servers (older ones?) return the resourcde you created instead of an
|
||||||
* of an OperationOutcome. We just need to ignore it.
|
* OperationOutcome. We just need to ignore it.
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testCreateWithResourceResponse() throws Exception {
|
public void testCreateWithResourceResponse() throws Exception {
|
||||||
|
@ -135,14 +152,7 @@ public class ClientTest {
|
||||||
assertEquals("100", response.getId().getValue());
|
assertEquals("100", response.getId().getValue());
|
||||||
assertEquals("200", response.getVersionId().getValue());
|
assertEquals("200", response.getVersionId().getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public interface CreateWithTagList extends IBasicClient
|
|
||||||
{
|
|
||||||
@Create
|
|
||||||
public MethodOutcome createPatient(@ResourceParam Patient thePatient, TagList theTagList);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateWithTagList() throws Exception {
|
public void testCreateWithTagList() throws Exception {
|
||||||
|
|
||||||
|
@ -156,44 +166,25 @@ public class ClientTest {
|
||||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||||
when(httpResponse.getAllHeaders()).thenReturn(toHeaderArray("Location", "http://example.com/fhir/Patient/100/_history/200"));
|
when(httpResponse.getAllHeaders()).thenReturn(toHeaderArray("Location", "http://example.com/fhir/Patient/100/_history/200"));
|
||||||
|
|
||||||
CreateWithTagList client = ctx.newRestfulClient(CreateWithTagList.class, "http://foo");
|
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||||
TagList tagList = new TagList();
|
TagList tagList = new TagList();
|
||||||
tagList.add(new Tag("Dog", "DogLabel", (String)null));
|
tagList.add(new Tag("Dog", "DogLabel", (String) null));
|
||||||
tagList.add(new Tag("Cat", "CatLabel", "http://cats"));
|
tagList.add(new Tag("Cat", "CatLabel", "http://cats"));
|
||||||
MethodOutcome response = client.createPatient(patient, tagList);
|
patient.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
|
||||||
|
|
||||||
|
MethodOutcome response = client.createPatient(patient);
|
||||||
|
|
||||||
assertEquals(HttpPost.class, capt.getValue().getClass());
|
assertEquals(HttpPost.class, capt.getValue().getClass());
|
||||||
HttpPost post = (HttpPost) capt.getValue();
|
HttpPost post = (HttpPost) capt.getValue();
|
||||||
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
||||||
assertEquals("100", response.getId().getValue());
|
assertEquals("100", response.getId().getValue());
|
||||||
assertEquals("200", response.getVersionId().getValue());
|
assertEquals("200", response.getVersionId().getValue());
|
||||||
|
|
||||||
Header[] headers = post.getHeaders("Category");
|
Header[] headers = post.getHeaders("Category");
|
||||||
assertEquals(2, headers.length);
|
assertEquals(2, headers.length);
|
||||||
assertEquals("Dog; label=\"DogLabel\"", headers[0].getValue());
|
assertEquals("Dog; label=\"DogLabel\"", headers[0].getValue());
|
||||||
assertEquals("Cat; label=\"CatLabel\"; scheme=\"http://cats\"", headers[1].getValue());
|
assertEquals("Cat; label=\"CatLabel\"; scheme=\"http://cats\"", headers[1].getValue());
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateBad() throws Exception {
|
|
||||||
|
|
||||||
Patient patient = new Patient();
|
|
||||||
patient.addIdentifier("urn:foo", "123");
|
|
||||||
|
|
||||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
|
||||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
|
||||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 400, "foobar"));
|
|
||||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_TEXT + "; charset=UTF-8"));
|
|
||||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("foobar"), Charset.forName("UTF-8")));
|
|
||||||
|
|
||||||
try {
|
|
||||||
ctx.newRestfulClient(ITestClient.class, "http://foo").createPatient(patient);
|
|
||||||
fail();
|
|
||||||
} catch (InvalidRequestException e) {
|
|
||||||
assertThat(e.getMessage(), StringContains.containsString("foobar"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -398,7 +389,6 @@ public class ClientTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHistoryServer() throws Exception {
|
public void testHistoryServer() throws Exception {
|
||||||
InstantDt date1 = new InstantDt(new Date(20000L));
|
InstantDt date1 = new InstantDt(new Date(20000L));
|
||||||
|
@ -470,7 +460,7 @@ public class ClientTest {
|
||||||
assertEquals(updExpected.getValueAsString(), updActualBundle.getValueAsString());
|
assertEquals(updExpected.getValueAsString(), updActualBundle.getValueAsString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHistoryWithParams() throws Exception {
|
public void testHistoryWithParams() throws Exception {
|
||||||
|
|
||||||
|
@ -488,17 +478,19 @@ public class ClientTest {
|
||||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||||
|
|
||||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02"), new IntegerDt(12));
|
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02"), new IntegerDt(12));
|
||||||
assertEquals("http://foo/Patient/111/_history?_since=2012-01-02T00%3A01%3A02&_count=12", capt.getAllValues().get(0).getURI().toString());
|
assertEquals("http://foo/Patient/111/_history?_since=2012-01-02T00%3A01%3A02&_count=12", capt.getAllValues().get(0).getURI().toString());
|
||||||
|
|
||||||
String expectedDateString= new InstantDt(new InstantDt("2012-01-02T00:01:02").getValue()).getValueAsString(); // ensures the local timezone
|
String expectedDateString = new InstantDt(new InstantDt("2012-01-02T00:01:02").getValue()).getValueAsString(); // ensures
|
||||||
expectedDateString=expectedDateString.replace(":", "%3A");
|
// the
|
||||||
|
// local
|
||||||
|
// timezone
|
||||||
|
expectedDateString = expectedDateString.replace(":", "%3A");
|
||||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02").getValue(), new IntegerDt(12).getValue());
|
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T00:01:02").getValue(), new IntegerDt(12).getValue());
|
||||||
assertEquals("http://foo/Patient/111/_history?_since="+expectedDateString+"&_count=12", capt.getAllValues().get(1).getURI().toString());
|
assertEquals("http://foo/Patient/111/_history?_since=" + expectedDateString + "&_count=12", capt.getAllValues().get(1).getURI().toString());
|
||||||
|
|
||||||
client.getHistoryPatientInstance(new IdDt("111"), null, new IntegerDt(12));
|
client.getHistoryPatientInstance(new IdDt("111"), null, new IntegerDt(12));
|
||||||
assertEquals("http://foo/Patient/111/_history?_count=12", capt.getAllValues().get(2).getURI().toString());
|
assertEquals("http://foo/Patient/111/_history?_count=12", capt.getAllValues().get(2).getURI().toString());
|
||||||
|
@ -514,6 +506,22 @@ public class ClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNonAnnotatedMethodFailsGracefully() {
|
||||||
|
|
||||||
|
// TODO: remove the read annotation and make sure we get a sensible
|
||||||
|
// error message to tell the user why the method isn't working
|
||||||
|
ClientWithoutAnnotation client = new FhirContext().newRestfulClient(ClientWithoutAnnotation.class, "http://wildfhir.aegis.net/fhir");
|
||||||
|
|
||||||
|
try {
|
||||||
|
client.read(new IdDt("8"));
|
||||||
|
fail();
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
assertThat(e.getMessage(), containsString("annotation"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRead() throws Exception {
|
public void testRead() throws Exception {
|
||||||
|
|
||||||
|
@ -539,7 +547,8 @@ public class ClientTest {
|
||||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||||
// Patient response = client.findPatientByMrn(new IdentifierDt("urn:foo", "123"));
|
// Patient response = client.findPatientByMrn(new
|
||||||
|
// IdentifierDt("urn:foo", "123"));
|
||||||
Patient response = client.getPatientById(new IdDt("111"));
|
Patient response = client.getPatientById(new IdDt("111"));
|
||||||
|
|
||||||
assertEquals("http://foo/Patient/111", capt.getValue().getURI().toString());
|
assertEquals("http://foo/Patient/111", capt.getValue().getURI().toString());
|
||||||
|
@ -547,10 +556,35 @@ public class ClientTest {
|
||||||
|
|
||||||
InstantDt lm = (InstantDt) response.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
|
InstantDt lm = (InstantDt) response.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
|
||||||
assertEquals("2011-01-02T22:01:02", lm.getValueAsString());
|
assertEquals("2011-01-02T22:01:02", lm.getValueAsString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadFailureNoCharset() throws Exception {
|
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
String msg = "<OperationOutcome xmlns=\"http://hl7.org/fhir\"></OperationOutcome>";
|
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 404, "NOT FOUND"));
|
||||||
|
Header[] headers = new Header[1];
|
||||||
|
headers[0] = new BasicHeader(Constants.HEADER_LAST_MODIFIED, "2011-01-02T22:01:02");
|
||||||
|
when(httpResponse.getAllHeaders()).thenReturn(headers);
|
||||||
|
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML));
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||||
|
try {
|
||||||
|
client.getPatientById(new IdDt("111"));
|
||||||
|
fail();
|
||||||
|
} catch (ResourceNotFoundException e) {
|
||||||
|
// good
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadNoCharset() throws Exception {
|
public void testReadNoCharset() throws Exception {
|
||||||
|
|
||||||
|
@ -576,7 +610,8 @@ public class ClientTest {
|
||||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||||
// Patient response = client.findPatientByMrn(new IdentifierDt("urn:foo", "123"));
|
// Patient response = client.findPatientByMrn(new
|
||||||
|
// IdentifierDt("urn:foo", "123"));
|
||||||
Patient response = client.getPatientById(new IdDt("111"));
|
Patient response = client.getPatientById(new IdDt("111"));
|
||||||
|
|
||||||
assertEquals("http://foo/Patient/111", capt.getValue().getURI().toString());
|
assertEquals("http://foo/Patient/111", capt.getValue().getURI().toString());
|
||||||
|
@ -584,52 +619,9 @@ public class ClientTest {
|
||||||
|
|
||||||
InstantDt lm = (InstantDt) response.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
|
InstantDt lm = (InstantDt) response.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
|
||||||
assertEquals("2011-01-02T22:01:02", lm.getValueAsString());
|
assertEquals("2011-01-02T22:01:02", lm.getValueAsString());
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface ClientWithoutAnnotation extends IBasicClient {
|
|
||||||
@Read
|
|
||||||
Patient read(@IdParam IdDt theId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testIt() {
|
|
||||||
|
|
||||||
// TODO: remove the read annotation and make sure we get a sensible
|
|
||||||
// error message to tell the user why the method isn't working
|
|
||||||
ClientWithoutAnnotation client = new FhirContext().newRestfulClient(ClientWithoutAnnotation.class, "http://wildfhir.aegis.net/fhir");
|
|
||||||
client.read(new IdDt("8"));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testReadFailureNoCharset() throws Exception {
|
|
||||||
|
|
||||||
//@formatter:off
|
|
||||||
String msg = "<OperationOutcome xmlns=\"http://hl7.org/fhir\"></OperationOutcome>";
|
|
||||||
//@formatter:on
|
|
||||||
|
|
||||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
|
||||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
|
||||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 404, "NOT FOUND"));
|
|
||||||
Header[] headers = new Header[1];
|
|
||||||
headers[0] = new BasicHeader(Constants.HEADER_LAST_MODIFIED, "2011-01-02T22:01:02");
|
|
||||||
when(httpResponse.getAllHeaders()).thenReturn(headers);
|
|
||||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML));
|
|
||||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
|
||||||
|
|
||||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
|
||||||
Patient response = client.getPatientById(new IdDt("111"));
|
|
||||||
|
|
||||||
assertEquals("http://foo/Patient/111", capt.getValue().getURI().toString());
|
|
||||||
assertEquals("PRP1660", response.getIdentifier().get(0).getValue().getValue());
|
|
||||||
|
|
||||||
InstantDt lm = (InstantDt) response.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
|
|
||||||
assertEquals("2011-01-02T22:01:02", lm.getValueAsString());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchByDateRange() throws Exception {
|
public void testSearchByDateRange() throws Exception {
|
||||||
|
|
||||||
|
@ -670,35 +662,6 @@ public class ClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSearchWithFormatAndPrettyPrint() throws Exception {
|
|
||||||
|
|
||||||
String msg = getPatientFeedWithOneResult();
|
|
||||||
|
|
||||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
|
||||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
|
||||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
|
||||||
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
|
||||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
|
||||||
|
|
||||||
// TODO: document this
|
|
||||||
|
|
||||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
|
||||||
client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
|
||||||
assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02", capt.getAllValues().get(0).getURI().toString());
|
|
||||||
|
|
||||||
client.setEncoding(EncodingEnum.JSON);
|
|
||||||
client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
|
||||||
assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02&_format=json", capt.getAllValues().get(1).getURI().toString());
|
|
||||||
|
|
||||||
client.setPrettyPrint(true);
|
|
||||||
client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
|
||||||
assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02&_format=json&_pretty=true", capt.getAllValues().get(2).getURI().toString());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchByToken() throws Exception {
|
public void testSearchByToken() throws Exception {
|
||||||
|
|
||||||
|
@ -718,7 +681,6 @@ public class ClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchComposite() throws Exception {
|
public void testSearchComposite() throws Exception {
|
||||||
|
|
||||||
|
@ -739,7 +701,7 @@ public class ClientTest {
|
||||||
assertEquals("http://foo/Patient?ids=foo%7Cbar%2Cbaz%7Cboz", capt.getValue().getURI().toString());
|
assertEquals("http://foo/Patient?ids=foo%7Cbar%2Cbaz%7Cboz", capt.getValue().getURI().toString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchNamedQueryNoParams() throws Exception {
|
public void testSearchNamedQueryNoParams() throws Exception {
|
||||||
|
|
||||||
|
@ -775,7 +737,7 @@ public class ClientTest {
|
||||||
assertEquals("http://foo/Patient?_query=someQueryOneParam¶m1=BB", capt.getValue().getURI().toString());
|
assertEquals("http://foo/Patient?_query=someQueryOneParam¶m1=BB", capt.getValue().getURI().toString());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchWithCustomType() throws Exception {
|
public void testSearchWithCustomType() throws Exception {
|
||||||
|
|
||||||
|
@ -813,8 +775,37 @@ public class ClientTest {
|
||||||
assertEquals("PRP1660", response.get(0).getIdentifier().get(0).getValue().getValue());
|
assertEquals("PRP1660", response.get(0).getIdentifier().get(0).getValue().getValue());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSearchWithFormatAndPrettyPrint() throws Exception {
|
||||||
|
|
||||||
|
String msg = getPatientFeedWithOneResult();
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
// TODO: document this
|
||||||
|
|
||||||
|
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||||
|
client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
||||||
|
assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02", capt.getAllValues().get(0).getURI().toString());
|
||||||
|
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||||
|
client.setEncoding(EncodingEnum.JSON); // this needs to be actually
|
||||||
|
// implemented
|
||||||
|
client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
||||||
|
assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02&_format=json", capt.getAllValues().get(1).getURI().toString());
|
||||||
|
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||||
|
client.setPrettyPrint(true);
|
||||||
|
client.getPatientByDob(new QualifiedDateParam(QuantityCompararatorEnum.GREATERTHAN_OR_EQUALS, "2011-01-02"));
|
||||||
|
assertEquals("http://foo/Patient?birthdate=%3E%3D2011-01-02&_format=json&_pretty=true", capt.getAllValues().get(2).getURI().toString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchWithIncludes() throws Exception {
|
public void testSearchWithIncludes() throws Exception {
|
||||||
|
|
||||||
|
@ -908,7 +899,8 @@ public class ClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a FHIR content type, but no content and make sure we handle this without crashing
|
* Return a FHIR content type, but no content and make sure we handle this
|
||||||
|
* without crashing
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateWithEmptyResponse() throws Exception {
|
public void testUpdateWithEmptyResponse() throws Exception {
|
||||||
|
@ -1015,7 +1007,8 @@ public class ClientTest {
|
||||||
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||||
// Patient response = client.findPatientByMrn(new IdentifierDt("urn:foo", "123"));
|
// Patient response = client.findPatientByMrn(new
|
||||||
|
// IdentifierDt("urn:foo", "123"));
|
||||||
Patient response = client.getPatientByVersionId(new IdDt("111"), new IdDt("999"));
|
Patient response = client.getPatientByVersionId(new IdDt("111"), new IdDt("999"));
|
||||||
|
|
||||||
assertEquals("http://foo/Patient/111/_history/999", capt.getValue().getURI().toString());
|
assertEquals("http://foo/Patient/111/_history/999", capt.getValue().getURI().toString());
|
||||||
|
@ -1055,24 +1048,27 @@ public class ClientTest {
|
||||||
return new Header[] { new BasicHeader(theName, theValue) };
|
return new Header[] { new BasicHeader(theName, theValue) };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResourceDef(name="Patient")
|
@ResourceDef(name = "Patient")
|
||||||
public static class CustomPatient extends Patient
|
public static class CustomPatient extends Patient {
|
||||||
{
|
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ITestClientWithCustomType extends IBasicClient {
|
public interface ITestClientWithCustomType extends IBasicClient {
|
||||||
@Search()
|
@Search()
|
||||||
public CustomPatient getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
|
public CustomPatient getPatientByDob(@RequiredParam(name = Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ITestClientWithCustomTypeList extends IBasicClient {
|
public interface ITestClientWithCustomTypeList extends IBasicClient {
|
||||||
@Search()
|
@Search()
|
||||||
public List<CustomPatient> getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
|
public List<CustomPatient> getPatientByDob(@RequiredParam(name = Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ITestClientWithStringIncludes extends IBasicClient {
|
public interface ITestClientWithStringIncludes extends IBasicClient {
|
||||||
@Search()
|
@Search()
|
||||||
public Patient getPatientWithIncludes(@RequiredParam(name = "withIncludes") StringDt theString, @IncludeParam String theInclude);
|
public Patient getPatientWithIncludes(@RequiredParam(name = "withIncludes") StringDt theString, @IncludeParam String theInclude);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface ClientWithoutAnnotation extends IBasicClient {
|
||||||
|
Patient read(@IdParam IdDt theId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
package ca.uhn.fhir.rest.client;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
|
||||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
|
||||||
import ca.uhn.fhir.rest.client.ClientTest.CustomPatient;
|
|
||||||
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
|
||||||
import ca.uhn.fhir.rest.param.QualifiedDateParam;
|
|
||||||
|
|
||||||
public class InvalidClientDefinitionTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAnnotationTypeIsNotAssignableToMethodReturnType() {
|
|
||||||
// TODO: this should fail
|
|
||||||
new FhirContext().newRestfulClient(ITestClientWithCustomType.class, "http://example.com");
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ITestClientWithCustomType extends IBasicClient {
|
|
||||||
@Search(type=Patient.class)
|
|
||||||
public CustomPatient getPatientByDob(@RequiredParam(name=Patient.SP_BIRTHDATE) QualifiedDateParam theBirthDate);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,270 @@
|
||||||
|
package ca.uhn.fhir.rest.client;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import java.io.StringReader;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.io.input.ReaderInputStream;
|
||||||
|
import org.apache.http.HeaderElement;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.ProtocolVersion;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.client.methods.HttpUriRequest;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.apache.http.message.BasicStatusLine;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.TagListParam;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Conformance;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.AddTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.GetTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.VersionIdParam;
|
||||||
|
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
|
||||||
|
public class TagsClientTest {
|
||||||
|
|
||||||
|
private FhirContext ctx;
|
||||||
|
private HttpClient httpClient;
|
||||||
|
private HttpResponse httpResponse;
|
||||||
|
|
||||||
|
// atom-document-large.xml
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
ctx = new FhirContext(Patient.class, Conformance.class);
|
||||||
|
|
||||||
|
httpClient = mock(HttpClient.class, new ReturnsDeepStubs());
|
||||||
|
ctx.getRestfulClientFactory().setHttpClient(httpClient);
|
||||||
|
|
||||||
|
httpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAllTags() throws Exception {
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("AAA", "BBB", "CCC"));
|
||||||
|
String ser = ctx.newXmlParser().encodeTagListToString(tagList);
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ser), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
|
||||||
|
TagList response = client.getAllTags();
|
||||||
|
assertEquals(tagList, response);
|
||||||
|
|
||||||
|
assertEquals(HttpGet.class, capt.getValue().getClass());
|
||||||
|
HttpGet get = (HttpGet) capt.getValue();
|
||||||
|
assertEquals("http://foo/_tags", get.getURI().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAllTagsPatient() throws Exception {
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("AAA", "BBB", "CCC"));
|
||||||
|
String ser = ctx.newXmlParser().encodeTagListToString(tagList);
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ser), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
|
||||||
|
TagList response = client.getAllTagsPatient();
|
||||||
|
assertEquals(tagList, response);
|
||||||
|
|
||||||
|
assertEquals(HttpGet.class, capt.getValue().getClass());
|
||||||
|
HttpGet get = (HttpGet) capt.getValue();
|
||||||
|
assertEquals("http://foo/Patient/_tags", get.getURI().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteTagsPatient() throws Exception {
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(httpResponse.getEntity().getContentType().getElements()).thenReturn(new HeaderElement[0]);
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("AAA", "BBB", "CCC"));
|
||||||
|
|
||||||
|
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
|
||||||
|
client.deleteTags(new IdDt("111"), tagList);
|
||||||
|
|
||||||
|
assertEquals(HttpPost.class, capt.getValue().getClass());
|
||||||
|
HttpPost post = (HttpPost) capt.getValue();
|
||||||
|
assertEquals("http://foo/Patient/111/_tags/_delete", post.getURI().toString());
|
||||||
|
|
||||||
|
String ser = IOUtils.toString(post.getEntity().getContent());
|
||||||
|
TagList actualTagList = ctx.newXmlParser().parseTagList(ser);
|
||||||
|
assertEquals(tagList, actualTagList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDeleteTagsPatientVersion() throws Exception {
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(httpResponse.getEntity().getContentType().getElements()).thenReturn(new HeaderElement[0]);
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("AAA", "BBB", "CCC"));
|
||||||
|
|
||||||
|
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
|
||||||
|
client.deleteTags(new IdDt("111"), new IdDt("222"),tagList);
|
||||||
|
|
||||||
|
assertEquals(HttpPost.class, capt.getValue().getClass());
|
||||||
|
HttpPost post = (HttpPost) capt.getValue();
|
||||||
|
assertEquals("http://foo/Patient/111/_history/222/_tags/_delete", post.getURI().toString());
|
||||||
|
|
||||||
|
String ser = IOUtils.toString(post.getEntity().getContent());
|
||||||
|
TagList actualTagList = ctx.newXmlParser().parseTagList(ser);
|
||||||
|
assertEquals(tagList, actualTagList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddTagsPatient() throws Exception {
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(httpResponse.getEntity().getContentType().getElements()).thenReturn(new HeaderElement[0]);
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("AAA", "BBB", "CCC"));
|
||||||
|
|
||||||
|
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
|
||||||
|
client.addTags(new IdDt("111"), tagList);
|
||||||
|
|
||||||
|
assertEquals(HttpPost.class, capt.getValue().getClass());
|
||||||
|
HttpPost post = (HttpPost) capt.getValue();
|
||||||
|
assertEquals("http://foo/Patient/111/_tags", post.getURI().toString());
|
||||||
|
|
||||||
|
String ser = IOUtils.toString(post.getEntity().getContent());
|
||||||
|
TagList actualTagList = ctx.newXmlParser().parseTagList(ser);
|
||||||
|
assertEquals(tagList, actualTagList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddTagsPatientVersion() throws Exception {
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(httpResponse.getEntity().getContentType().getElements()).thenReturn(new HeaderElement[0]);
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("AAA", "BBB", "CCC"));
|
||||||
|
|
||||||
|
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
|
||||||
|
client.addTags(new IdDt("111"), new IdDt("222"),tagList);
|
||||||
|
|
||||||
|
assertEquals(HttpPost.class, capt.getValue().getClass());
|
||||||
|
HttpPost post = (HttpPost) capt.getValue();
|
||||||
|
assertEquals("http://foo/Patient/111/_history/222/_tags", post.getURI().toString());
|
||||||
|
|
||||||
|
String ser = IOUtils.toString(post.getEntity().getContent());
|
||||||
|
TagList actualTagList = ctx.newXmlParser().parseTagList(ser);
|
||||||
|
assertEquals(tagList, actualTagList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAllTagsPatientId() throws Exception {
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("AAA", "BBB", "CCC"));
|
||||||
|
String ser = ctx.newXmlParser().encodeTagListToString(tagList);
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ser), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
|
||||||
|
TagList response = client.getAllTagsPatientId(new IdDt("111"));
|
||||||
|
assertEquals(tagList, response);
|
||||||
|
|
||||||
|
assertEquals(HttpGet.class, capt.getValue().getClass());
|
||||||
|
HttpGet get = (HttpGet) capt.getValue();
|
||||||
|
assertEquals("http://foo/Patient/111/_tags", get.getURI().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAllTagsPatientIdVersion() throws Exception {
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("AAA", "BBB", "CCC"));
|
||||||
|
String ser = ctx.newXmlParser().encodeTagListToString(tagList);
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ser), Charset.forName("UTF-8")));
|
||||||
|
|
||||||
|
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
|
||||||
|
TagList response = client.getAllTagsPatientIdVersion(new IdDt("111"), new IdDt("222"));
|
||||||
|
assertEquals(tagList, response);
|
||||||
|
|
||||||
|
assertEquals(HttpGet.class, capt.getValue().getClass());
|
||||||
|
HttpGet get = (HttpGet) capt.getValue();
|
||||||
|
assertEquals("http://foo/Patient/111/_history/222/_tags", get.getURI().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface IClient extends IBasicClient {
|
||||||
|
|
||||||
|
@GetTags
|
||||||
|
public TagList getAllTags();
|
||||||
|
|
||||||
|
@GetTags(type = Patient.class)
|
||||||
|
public TagList getAllTagsPatient();
|
||||||
|
|
||||||
|
@GetTags(type = Patient.class)
|
||||||
|
public TagList getAllTagsPatientId(@IdParam IdDt theId);
|
||||||
|
|
||||||
|
@GetTags(type = Patient.class)
|
||||||
|
public TagList getAllTagsPatientIdVersion(@IdParam IdDt theId, @VersionIdParam IdDt theVersion);
|
||||||
|
|
||||||
|
@AddTags(type = Patient.class)
|
||||||
|
public void addTags(@IdParam IdDt theId, @TagListParam TagList theTagList);
|
||||||
|
|
||||||
|
@AddTags(type = Patient.class)
|
||||||
|
public void addTags(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId, @TagListParam TagList theTagList);
|
||||||
|
|
||||||
|
@DeleteTags(type = Patient.class)
|
||||||
|
public void deleteTags(@IdParam IdDt theId, @TagListParam TagList theTagList);
|
||||||
|
|
||||||
|
@DeleteTags(type = Patient.class)
|
||||||
|
public void deleteTags(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId, @TagListParam TagList theTagList);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -24,7 +24,7 @@ public class Tester {
|
||||||
|
|
||||||
} catch (NonFhirResponseException e) {
|
} catch (NonFhirResponseException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
System.out.println(e.getResponseText());
|
System.out.println(e.getResponseBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import static org.junit.Assert.*;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hamcrest.core.StringContains;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
@ -59,6 +58,7 @@ public class DocumentationTest {
|
||||||
/**
|
/**
|
||||||
* Created by dsotnikov on 2/25/2014.
|
* Created by dsotnikov on 2/25/2014.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public static class SearchProvider {
|
public static class SearchProvider {
|
||||||
|
|
||||||
@Search(type = Patient.class)
|
@Search(type = Patient.class)
|
||||||
|
|
|
@ -921,11 +921,8 @@ public class ResfulServerMethodTest {
|
||||||
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
|
HttpPut httpPost = new HttpPut("http://localhost:" + ourPort + "/DiagnosticReport/001");
|
||||||
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
HttpResponse status = ourClient.execute(httpPost);
|
ourClient.execute(httpPost);
|
||||||
|
fail();
|
||||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
|
||||||
assertEquals("http://localhost:" + ourPort + "/DiagnosticReport/001/_history/002", status.getFirstHeader("Location").getValue());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1199,12 +1196,11 @@ public class ResfulServerMethodTest {
|
||||||
return DiagnosticReport.class;
|
return DiagnosticReport.class;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Update()
|
@Update()
|
||||||
public MethodOutcome updateDiagnosticReportWithNoResponse(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId, @ResourceParam DiagnosticReport thePatient, TagList theTagList) {
|
public MethodOutcome updateDiagnosticReportWithNoResponse(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId, @ResourceParam DiagnosticReport theDr) {
|
||||||
IdDt id = theId;
|
IdDt id = theId;
|
||||||
IdDt version = theVersionId;
|
IdDt version = theVersionId;
|
||||||
myLastTags = theTagList;
|
myLastTags = (TagList) theDr.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||||
return new MethodOutcome(id, version);
|
return new MethodOutcome(id, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1212,12 +1208,11 @@ public class ResfulServerMethodTest {
|
||||||
return myLastTags;
|
return myLastTags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Update()
|
@Update()
|
||||||
public MethodOutcome updateDiagnosticReportWithVersionAndNoResponse(@IdParam IdDt theId, @ResourceParam DiagnosticReport thePatient, TagList theTagList) {
|
public MethodOutcome updateDiagnosticReportWithVersionAndNoResponse(@IdParam IdDt theId, @ResourceParam DiagnosticReport theDr) {
|
||||||
IdDt id = theId;
|
IdDt id = theId;
|
||||||
IdDt version = new IdDt("002");
|
IdDt version = new IdDt("002");
|
||||||
myLastTags=theTagList;
|
myLastTags = (TagList) theDr.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||||
return new MethodOutcome(id, version);
|
return new MethodOutcome(id, version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class ResourceMethodTest {
|
||||||
inputParams.add("firstName");
|
inputParams.add("firstName");
|
||||||
inputParams.add("lastName");
|
inputParams.add("lastName");
|
||||||
|
|
||||||
assertEquals(false, rm.matches(Request.withResourceAndParams("Patient", RequestType.GET, inputParams))); // False
|
assertEquals(false, rm.incomingServerRequestMatchesMethod(Request.withResourceAndParams("Patient", RequestType.GET, inputParams))); // False
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -61,7 +61,7 @@ public class ResourceMethodTest {
|
||||||
|
|
||||||
Set<String> inputParams = new HashSet<String>();
|
Set<String> inputParams = new HashSet<String>();
|
||||||
inputParams.add("mrn");
|
inputParams.add("mrn");
|
||||||
assertEquals(true, rm.matches(Request.withResourceAndParams("Patient", RequestType.GET, inputParams))); // True
|
assertEquals(true, rm.incomingServerRequestMatchesMethod(Request.withResourceAndParams("Patient", RequestType.GET, inputParams))); // True
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -78,7 +78,7 @@ public class ResourceMethodTest {
|
||||||
inputParams.add("firstName");
|
inputParams.add("firstName");
|
||||||
inputParams.add("mrn");
|
inputParams.add("mrn");
|
||||||
|
|
||||||
assertEquals(true, rm.matches(Request.withResourceAndParams("Patient", RequestType.GET, inputParams))); // True
|
assertEquals(true, rm.incomingServerRequestMatchesMethod(Request.withResourceAndParams("Patient", RequestType.GET, inputParams))); // True
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -96,7 +96,7 @@ public class ResourceMethodTest {
|
||||||
inputParams.add("lastName");
|
inputParams.add("lastName");
|
||||||
inputParams.add("mrn");
|
inputParams.add("mrn");
|
||||||
|
|
||||||
assertEquals(true, rm.matches(Request.withResourceAndParams("Patient", RequestType.GET, inputParams))); // True
|
assertEquals(true, rm.incomingServerRequestMatchesMethod(Request.withResourceAndParams("Patient", RequestType.GET, inputParams))); // True
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -115,6 +115,6 @@ public class ResourceMethodTest {
|
||||||
inputParams.add("mrn");
|
inputParams.add("mrn");
|
||||||
inputParams.add("foo");
|
inputParams.add("foo");
|
||||||
|
|
||||||
assertEquals(false, rm.matches(Request.withResourceAndParams("Patient", RequestType.GET, inputParams))); // False
|
assertEquals(false, rm.incomingServerRequestMatchesMethod(Request.withResourceAndParams("Patient", RequestType.GET, inputParams))); // False
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,294 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpGet;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
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.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.Tag;
|
||||||
|
import ca.uhn.fhir.model.api.TagList;
|
||||||
|
import ca.uhn.fhir.model.api.annotation.TagListParam;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.AddTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.GetTags;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.VersionIdParam;
|
||||||
|
import ca.uhn.fhir.testutil.RandomServerPortProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public class TagsServerTest {
|
||||||
|
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
private static FhirContext ourCtx;
|
||||||
|
private static String ourLastOutcome;
|
||||||
|
private static TagList ourLastTagList;
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TagsServerTest.class);
|
||||||
|
private static int ourPort;
|
||||||
|
private static DummyProvider ourProvider;
|
||||||
|
private static Server ourServer;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
ourLastOutcome = null;
|
||||||
|
ourLastTagList = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddTagsById() throws Exception {
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.addTag("term", "label", "scheme");
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_tags");
|
||||||
|
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals("add111", ourLastOutcome);
|
||||||
|
assertEquals(tagList, ourLastTagList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddTagsByIdAndVersion() throws Exception {
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.addTag("term", "label", "scheme");
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_history/222/_tags");
|
||||||
|
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals("add111222", ourLastOutcome);
|
||||||
|
assertEquals(tagList, ourLastTagList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEquals() {
|
||||||
|
TagList list1 = ourProvider.getAllTagsPatient();
|
||||||
|
TagList list2 = ourProvider.getAllTagsPatient();
|
||||||
|
assertEquals(list1, list2);
|
||||||
|
|
||||||
|
list1 = ourProvider.getAllTagsPatient();
|
||||||
|
list2 = ourProvider.getAllTagsPatient();
|
||||||
|
list2.get(0).setTerm("!!!!!");
|
||||||
|
assertNotEquals(list1, list2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAllTags() throws Exception {
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/_tags");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
TagList tagList = ourCtx.newXmlParser().parseTagList(responseContent);
|
||||||
|
assertEquals(ourProvider.getAllTags(), tagList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAllTagsPatient() throws Exception {
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/_tags");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
TagList actual = ourCtx.newXmlParser().parseTagList(responseContent);
|
||||||
|
TagList expected = ourProvider.getAllTags();
|
||||||
|
expected.get(0).setTerm("Patient");
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAllTagsPatientId() throws Exception {
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/111/_tags");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
TagList actual = ourCtx.newXmlParser().parseTagList(responseContent);
|
||||||
|
TagList expected = ourProvider.getAllTags();
|
||||||
|
expected.get(0).setTerm("Patient111");
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAllTagsPatientIdVersion() throws Exception {
|
||||||
|
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/111/_history/222/_tags");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
|
||||||
|
TagList actual = ourCtx.newXmlParser().parseTagList(responseContent);
|
||||||
|
TagList expected = ourProvider.getAllTags();
|
||||||
|
expected.get(0).setTerm("Patient111222");
|
||||||
|
assertEquals(expected, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveTagsById() throws Exception {
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.addTag("term", "label", "scheme");
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_tags/_delete");
|
||||||
|
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals("Remove111", ourLastOutcome);
|
||||||
|
assertEquals(tagList, ourLastTagList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveTagsByIdAndVersion() throws Exception {
|
||||||
|
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.addTag("term", "label", "scheme");
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_history/222/_tags/_delete");
|
||||||
|
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8")));
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals("Remove111222", ourLastOutcome);
|
||||||
|
assertEquals(tagList, ourLastTagList);
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = RandomServerPortProvider.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
ourCtx = new FhirContext(Patient.class);
|
||||||
|
|
||||||
|
ourProvider = new DummyProvider();
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
servlet.setPlainProviders(ourProvider);
|
||||||
|
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||||
|
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||||
|
ourServer.setHandler(proxyHandler);
|
||||||
|
ourServer.start();
|
||||||
|
|
||||||
|
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||||
|
builder.setConnectionManager(connectionManager);
|
||||||
|
ourClient = builder.build();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class DummyProvider {
|
||||||
|
|
||||||
|
@AddTags(type = Patient.class)
|
||||||
|
public void addTagsPatient(@IdParam IdDt theId, @VersionIdParam IdDt theVersion, @TagListParam TagList theTagList) {
|
||||||
|
ourLastOutcome = "add" + theId.getValue() + theVersion.getValue();
|
||||||
|
ourLastTagList=theTagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AddTags(type = Patient.class)
|
||||||
|
public void addTagsPatient(@IdParam IdDt theId, @TagListParam TagList theTagList) {
|
||||||
|
ourLastOutcome = "add" + theId.getValue();
|
||||||
|
ourLastTagList=theTagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetTags
|
||||||
|
public TagList getAllTags() {
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("AllDog", "DogLabel", (String) null));
|
||||||
|
tagList.add(new Tag("AllCat", "CatLabel", "http://cats"));
|
||||||
|
return tagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetTags(type = Patient.class)
|
||||||
|
public TagList getAllTagsPatient() {
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("Patient", "DogLabel", (String) null));
|
||||||
|
tagList.add(new Tag("AllCat", "CatLabel", "http://cats"));
|
||||||
|
return tagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetTags(type = Patient.class)
|
||||||
|
public TagList getAllTagsPatientId(@IdParam IdDt theId) {
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("Patient" + theId.getValue(), "DogLabel", (String) null));
|
||||||
|
tagList.add(new Tag("AllCat", "CatLabel", "http://cats"));
|
||||||
|
return tagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetTags(type = Patient.class)
|
||||||
|
public TagList getAllTagsPatientIdVersion(@IdParam IdDt theId, @VersionIdParam IdDt theVersion) {
|
||||||
|
TagList tagList = new TagList();
|
||||||
|
tagList.add(new Tag("Patient" + theId.getValue() + theVersion.getValue(), "DogLabel", (String) null));
|
||||||
|
tagList.add(new Tag("AllCat", "CatLabel", "http://cats"));
|
||||||
|
return tagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteTags(type = Patient.class)
|
||||||
|
public void RemoveTagsPatient(@IdParam IdDt theId, @VersionIdParam IdDt theVersion, @TagListParam TagList theTagList) {
|
||||||
|
ourLastOutcome = "Remove" + theId.getValue() + theVersion.getValue();
|
||||||
|
ourLastTagList=theTagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DeleteTags(type = Patient.class)
|
||||||
|
public void RemoveTagsPatient(@IdParam IdDt theId, @TagListParam TagList theTagList) {
|
||||||
|
ourLastOutcome = "Remove" + theId.getValue();
|
||||||
|
ourLastTagList=theTagList;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -239,7 +239,9 @@ public class FhirResourceDao<T extends IResource, X extends BaseResourceTable<T>
|
||||||
String path = param.getPath();
|
String path = param.getPath();
|
||||||
List<Object> values = terser.getValues(resource, path);
|
List<Object> values = terser.getValues(resource, path);
|
||||||
for (Object nextValue : values) {
|
for (Object nextValue : values) {
|
||||||
if (nextValue.equals(nextParamEntry.getValue())) {
|
IQueryParameterType expectedQt = nextParamEntry.getValue();
|
||||||
|
IQueryParameterType actualQt = (IQueryParameterType)nextValue;
|
||||||
|
if (actualQt.getValueAsQueryToken().equals(expectedQt.getValueAsQueryToken())) {
|
||||||
shouldAdd = true;
|
shouldAdd = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
<persistence-unit name="FHIR_UT" transaction-type="RESOURCE_LOCAL">
|
<persistence-unit name="FHIR_UT" transaction-type="RESOURCE_LOCAL">
|
||||||
<provider>org.hibernate.ejb.HibernatePersistence</provider>
|
<provider>org.hibernate.ejb.HibernatePersistence</provider>
|
||||||
<class>ca.uhn.fhir.jpa.entity.PatientResourceTable</class>
|
<!-- <class>ca.uhn.fhir.jpa.entity.PatientResourceTable</class> -->
|
||||||
<exclude-unlisted-classes>false</exclude-unlisted-classes>
|
<exclude-unlisted-classes>false</exclude-unlisted-classes>
|
||||||
<properties>
|
<properties>
|
||||||
<property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa" />
|
<property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa" />
|
||||||
|
|
|
@ -60,13 +60,13 @@ public class FhirResourceDaoTest {
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateWithTags() {
|
public void testTagsWithCreateAndReadAndSearch() {
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
patient.addIdentifier("urn:system", "001");
|
patient.addIdentifier("urn:system", "testTagsWithCreateAndReadAndSearch");
|
||||||
patient.addName().addFamily("Tester").addGiven("Joe");
|
patient.addName().addFamily("Tester").addGiven("Joe");
|
||||||
TagList tagList= new TagList();
|
TagList tagList= new TagList();
|
||||||
tagList.addTag("Dog", "Puppies", null);
|
tagList.addTag("Dog", "Puppies", null);
|
||||||
tagList.addTag("Cat", "Kittens", null);
|
tagList.addTag("Cat", "Kittens", "http://foo");
|
||||||
patient.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
|
patient.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
|
||||||
|
|
||||||
MethodOutcome outcome = ourPatientDao.create(patient);
|
MethodOutcome outcome = ourPatientDao.create(patient);
|
||||||
|
@ -77,10 +77,27 @@ public class FhirResourceDaoTest {
|
||||||
TagList published = (TagList) retrieved.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
TagList published = (TagList) retrieved.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||||
assertEquals(2, published.size());
|
assertEquals(2, published.size());
|
||||||
assertEquals("Dog", published.get(0).getTerm());
|
assertEquals("Dog", published.get(0).getTerm());
|
||||||
|
assertEquals("Puppies", published.get(0).getLabel());
|
||||||
|
assertEquals(null, published.get(0).getScheme());
|
||||||
assertEquals("Cat", published.get(1).getTerm());
|
assertEquals("Cat", published.get(1).getTerm());
|
||||||
|
assertEquals("Kittens", published.get(1).getLabel());
|
||||||
|
assertEquals("http://foo", published.get(1).getScheme());
|
||||||
|
|
||||||
|
List<Patient> search = ourPatientDao.search(Patient.SP_IDENTIFIER, patient.getIdentifierFirstRep());
|
||||||
|
assertEquals(1,search.size());
|
||||||
|
retrieved = search.get(0);
|
||||||
|
published = (TagList) retrieved.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||||
|
assertEquals("Dog", published.get(0).getTerm());
|
||||||
|
assertEquals("Puppies", published.get(0).getLabel());
|
||||||
|
assertEquals(null, published.get(0).getScheme());
|
||||||
|
assertEquals("Cat", published.get(1).getTerm());
|
||||||
|
assertEquals("Kittens", published.get(1).getLabel());
|
||||||
|
assertEquals("http://foo", published.get(1).getScheme());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSearchAll() {
|
public void testSearchAll() {
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue