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>
|
||||
<version>3.3</version>
|
||||
</plugin>
|
||||
<!--
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-report-plugin</artifactId>
|
||||
|
@ -250,6 +251,7 @@
|
|||
<artifactId>maven-jxr-plugin</artifactId>
|
||||
<version>2.4</version>
|
||||
</plugin>
|
||||
-->
|
||||
<!--
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
|
|
|
@ -20,8 +20,10 @@ package ca.uhn.fhir.context;
|
|||
* #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.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
@ -33,6 +35,7 @@ import java.util.Iterator;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
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.ResourceDef;
|
||||
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.NarrativeDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||
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.XhtmlDt;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
|
@ -148,13 +146,43 @@ class ModelScanner {
|
|||
}
|
||||
|
||||
private void init(Set<Class<? extends IElement>> toScan) {
|
||||
toScan.add(DateDt.class);
|
||||
toScan.add(CodeDt.class);
|
||||
toScan.add(DecimalDt.class);
|
||||
toScan.add(AttachmentDt.class);
|
||||
toScan.add(ResourceReferenceDt.class);
|
||||
toScan.add(QuantityDt.class); // TODO: why is this required
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
InputStream str = ModelScanner.class.getResourceAsStream("/ca/uhn/fhir/model/dstu/model.properties");
|
||||
if (str == null) {
|
||||
str = ModelScanner.class.getResourceAsStream("ca/uhn/fhir/model/dstu/model.properties");
|
||||
}
|
||||
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 {
|
||||
for (Class<? extends IElement> nextClass : toScan) {
|
||||
scan(nextClass);
|
||||
|
@ -176,7 +204,8 @@ class ModelScanner {
|
|||
myRuntimeChildUndeclaredExtensionDefinition = new RuntimeChildUndeclaredExtensionDefinition();
|
||||
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 {
|
||||
|
@ -315,8 +344,7 @@ class ModelScanner {
|
|||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void scanCompositeElementForChildren(Class<? extends ICompositeElement> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef,
|
||||
TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
|
||||
private void scanCompositeElementForChildren(Class<? extends ICompositeElement> theClass, Set<String> elementNames, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToElementDef, TreeMap<Integer, BaseRuntimeDeclaredChildDefinition> theOrderToExtensionDef) {
|
||||
int baseElementOrder = theOrderToElementDef.isEmpty() ? 0 : theOrderToElementDef.lastEntry().getKey() + 1;
|
||||
|
||||
for (Field next : theClass.getDeclaredFields()) {
|
||||
|
@ -332,7 +360,7 @@ class ModelScanner {
|
|||
String elementName = childAnnotation.name();
|
||||
int order = childAnnotation.order();
|
||||
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) {
|
||||
order = order + baseElementOrder;
|
||||
|
@ -376,7 +404,7 @@ class ModelScanner {
|
|||
*/
|
||||
RuntimeChildContainedResources def = new RuntimeChildContainedResources(next, childAnnotation, descriptionAnnotation, elementName);
|
||||
orderMap.put(order, def);
|
||||
|
||||
|
||||
} else if (choiceTypes.size() > 1 && !ResourceReferenceDt.class.isAssignableFrom(nextElementType)) {
|
||||
/*
|
||||
* Child is a choice element
|
||||
|
@ -424,7 +452,7 @@ class ModelScanner {
|
|||
orderMap.put(order, def);
|
||||
|
||||
} else if (IDatatype.class.equals(nextElementType)) {
|
||||
|
||||
|
||||
RuntimeChildAny def = new RuntimeChildAny(next, elementName, childAnnotation, descriptionAnnotation);
|
||||
orderMap.put(order, def);
|
||||
|
||||
|
@ -534,15 +562,14 @@ class ModelScanner {
|
|||
scanCompositeElementForChildren(theClass, resourceDef);
|
||||
|
||||
myIdToResourceDefinition.put(resourceId, resourceDef);
|
||||
|
||||
|
||||
scanResourceForSearchParams(theClass, resourceDef);
|
||||
|
||||
|
||||
|
||||
return resourceName;
|
||||
}
|
||||
|
||||
private void scanResourceForSearchParams(Class<? extends IResource> theClass, RuntimeResourceDefinition theResourceDef) {
|
||||
|
||||
|
||||
for (Field nextField : theClass.getFields()) {
|
||||
SearchParamDefinition searchParam = nextField.getAnnotation(SearchParamDefinition.class);
|
||||
if (searchParam != null) {
|
||||
|
@ -550,7 +577,7 @@ class ModelScanner {
|
|||
theResourceDef.addSearchParam(param);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
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%
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
|
@ -43,7 +40,7 @@ public class BundleEntry extends BaseBundle {
|
|||
private StringDt myTitle;
|
||||
private InstantDt myUpdated;
|
||||
private XhtmlDt mySummary;
|
||||
private List<BundleCategory> myCategories;
|
||||
private TagList myCategories;
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
|
@ -104,15 +101,15 @@ public class BundleEntry extends BaseBundle {
|
|||
return mySummary;
|
||||
}
|
||||
|
||||
public BundleCategory addCategory() {
|
||||
BundleCategory retVal = new BundleCategory();
|
||||
public Tag addCategory() {
|
||||
Tag retVal = new Tag();
|
||||
getCategories().add(retVal);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public List<BundleCategory> getCategories() {
|
||||
public TagList getCategories() {
|
||||
if (myCategories == null) {
|
||||
myCategories = new ArrayList<BundleCategory>();
|
||||
myCategories = new TagList();
|
||||
}
|
||||
return myCategories;
|
||||
}
|
||||
|
@ -127,4 +124,8 @@ public class BundleEntry extends BaseBundle {
|
|||
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.NarrativeDt;
|
||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||
|
||||
public interface IResource extends ICompositeElement {
|
||||
|
||||
|
@ -34,28 +35,42 @@ public interface IResource extends ICompositeElement {
|
|||
* list automatically (placing inline resources in the contained list when
|
||||
* encoding, and copying contained resources from this list to their
|
||||
* 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>
|
||||
* TODO: document contained resources and link there
|
||||
*/
|
||||
ContainedDt getContained();
|
||||
|
||||
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
|
||||
* Returns the narrative block for this resource
|
||||
*/
|
||||
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.
|
||||
* <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();
|
||||
|
||||
|
||||
/**
|
||||
* Sets the metadata map for this object. Metadata
|
||||
* entries are used to get/set feed bundle entries, such as the
|
||||
* resource version, or the last updated timestamp.
|
||||
* Sets the metadata map for this object. Metadata entries are used to
|
||||
* get/set feed bundle entries, such as the resource version, or the last
|
||||
* updated timestamp.
|
||||
*
|
||||
* @throws NullPointerException The map must not be null
|
||||
* @throws NullPointerException
|
||||
* The map must not be null
|
||||
*/
|
||||
void setResourceMetadata(Map<ResourceMetadataKeyEnum, Object> theMap);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -24,17 +24,21 @@ import static org.apache.commons.lang3.StringUtils.*;
|
|||
|
||||
import java.net.URI;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
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
|
||||
* value
|
||||
*/
|
||||
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 myScheme;
|
||||
private String myTerm;
|
||||
|
@ -87,15 +91,6 @@ public class Tag {
|
|||
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() {
|
||||
return myLabel;
|
||||
}
|
||||
|
@ -118,16 +113,24 @@ public class Tag {
|
|||
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;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setScheme(String theScheme) {
|
||||
public Tag setScheme(String theScheme) {
|
||||
myScheme = theScheme;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setTerm(String theTerm) {
|
||||
public Tag setTerm(String theTerm) {
|
||||
myTerm = theTerm;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String toHeaderValue() {
|
||||
|
@ -139,7 +142,16 @@ public class Tag {
|
|||
if (isNotBlank(this.getScheme())) {
|
||||
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> {
|
||||
|
||||
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) {
|
||||
add(new Tag(theTerm, theLabel, theScheme));
|
||||
public Tag addTag(String theTerm, String theLabel, String 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;
|
||||
|
||||
import ca.uhn.fhir.model.api.annotation.DatatypeDef;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR Library
|
||||
|
@ -20,6 +22,7 @@ package ca.uhn.fhir.model.dstu.composite;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
@DatatypeDef(name="AgeDt")
|
||||
public class AgeDt extends QuantityDt {
|
||||
|
||||
// TODO: implement restricions
|
||||
|
|
|
@ -41,19 +41,16 @@ public class Binary extends BaseResource implements IResource {
|
|||
|
||||
@Override
|
||||
public List<IElement> getAllPopulatedChildElements() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends IElement> List<T> getAllPopulatedChildElementsOfType(Class<T> theType) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContainedDt getContained() {
|
||||
// TODO Auto-generated method stub
|
||||
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.CodingDt;
|
||||
|
||||
@DatatypeDef(name = "CodeableConcept", isSpecialization=true)
|
||||
@DatatypeDef(name = "CodeableConcept", isSpecialization = true)
|
||||
public class BoundCodeableConceptDt<T extends Enum<?>> extends CodeableConceptDt {
|
||||
|
||||
private IValueSetEnumBinder<T> myBinder;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public BoundCodeableConceptDt(IValueSetEnumBinder<T> theBinder) {
|
||||
myBinder = theBinder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public BoundCodeableConceptDt(IValueSetEnumBinder<T> theBinder, T theValue) {
|
||||
myBinder = theBinder;
|
||||
setValueAsEnum(theValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public BoundCodeableConceptDt(IValueSetEnumBinder<T> theBinder, Collection<T> theValues) {
|
||||
myBinder = theBinder;
|
||||
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) {
|
||||
getCoding().clear();
|
||||
if (theValues == null) {
|
||||
return;
|
||||
}
|
||||
for (T next : theValues) {
|
||||
getCoding().add(new CodingDt(myBinder.toSystemString(next), myBinder.toCodeString(next)));
|
||||
if (theValues != null) {
|
||||
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) {
|
||||
getCoding().clear();
|
||||
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)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
Set<T> retVal = new HashSet<T>();
|
||||
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.IElement;
|
||||
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.primitive.IdDt;
|
||||
|
||||
|
@ -44,6 +45,11 @@ public abstract class BaseParser implements IParser {
|
|||
|
||||
private boolean mySuppressNarratives;
|
||||
|
||||
@Override
|
||||
public TagList parseTagList(String theString) {
|
||||
return parseTagList(new StringReader(theString));
|
||||
}
|
||||
|
||||
@SuppressWarnings("cast")
|
||||
@Override
|
||||
public <T extends IResource> T parseResource(Class<T> theResourceType, String theMessageString) {
|
||||
|
@ -68,6 +74,17 @@ public abstract class BaseParser implements IParser {
|
|||
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
|
||||
public String encodeBundleToString(Bundle theBundle) throws DataFormatException {
|
||||
if (theBundle == null) {
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.io.Writer;
|
|||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
|
||||
public interface IParser {
|
||||
|
||||
|
@ -36,8 +37,29 @@ public interface IParser {
|
|||
|
||||
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);
|
||||
|
||||
Bundle parseBundle(Reader theReader);
|
||||
|
@ -80,6 +102,28 @@ public interface IParser {
|
|||
|
||||
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
|
||||
* 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.stream.JsonGenerator;
|
||||
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.Validate;
|
||||
|
||||
import ch.qos.logback.core.boolex.EventEvaluator;
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
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.model.api.BaseBundle;
|
||||
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.ExtensionDt;
|
||||
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.IResource;
|
||||
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.dstu.composite.ContainedDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.NarrativeDt;
|
||||
|
@ -101,6 +105,195 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
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) {
|
||||
list.ensureCapacity(valueIdx);
|
||||
while (list.size() <= valueIdx) {
|
||||
|
@ -143,73 +336,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
return eventWriter;
|
||||
}
|
||||
|
||||
@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 (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 {
|
||||
private void encodeChildElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, JsonGenerator theWriter, IElement theValue, BaseRuntimeElementDefinition<?> theChildDef, String theChildName) throws IOException {
|
||||
|
||||
switch (theChildDef.getChildType()) {
|
||||
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,
|
||||
List<? extends BaseRuntimeChildDefinition> theChildren) throws IOException {
|
||||
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, List<? extends BaseRuntimeChildDefinition> theChildren) throws IOException {
|
||||
for (BaseRuntimeChildDefinition nextChild : theChildren) {
|
||||
if (nextChild instanceof RuntimeChildNarrativeDefinition) {
|
||||
INarrativeGenerator gen = myContext.getNarrativeGenerator();
|
||||
|
@ -395,7 +521,8 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
}
|
||||
|
||||
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) {
|
||||
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,
|
||||
BaseRuntimeElementCompositeDefinition<?> resDef) throws IOException, DataFormatException {
|
||||
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IResource theResource, IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementCompositeDefinition<?> resDef) throws IOException, DataFormatException {
|
||||
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getExtensions());
|
||||
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theElement, theEventWriter, resDef.getChildren());
|
||||
}
|
||||
|
@ -472,23 +598,12 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
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,
|
||||
IResource theResource) throws IOException {
|
||||
private void extractAndWriteExtensionsAsDirectChild(IElement theElement, JsonGenerator theEventWriter, BaseRuntimeElementDefinition<?> theElementDef, RuntimeResourceDefinition theResDef, IResource theResource) throws IOException {
|
||||
List<HeldExtension> extensions = 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) {
|
||||
for (String nextName : theObject.keySet()) {
|
||||
if ("resourceType".equals(nextName)) {
|
||||
|
@ -653,7 +743,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
}
|
||||
|
||||
if (elementId != null) {
|
||||
IElement object = theState.getObject();
|
||||
IElement object = (IElement) theState.getObject();
|
||||
if (object instanceof IIdentifiableElement) {
|
||||
((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) {
|
||||
// TODO: use theIsModifier
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
JsonObject nextExtObj = array.getJsonObject(i);
|
||||
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) {
|
||||
if (isNotBlank(theLink.getValue())) {
|
||||
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,
|
||||
List<HeldExtension> modifierExtensions) throws IOException {
|
||||
private void writeExtensionsAsDirectChild(IResource theResource, JsonGenerator theEventWriter, RuntimeResourceDefinition resDef, List<HeldExtension> extensions, List<HeldExtension> modifierExtensions) throws IOException {
|
||||
if (extensions.isEmpty() == false) {
|
||||
theEventWriter.writeStartArray("extension");
|
||||
for (HeldExtension next : extensions) {
|
||||
|
@ -871,7 +919,9 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
if (def.getChildType() == ChildTypeEnum.RESOURCE_BLOCK) {
|
||||
extractAndWriteExtensionsAsDirectChild(myValue, theEventWriter, def, theResDef, theResource);
|
||||
} 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());
|
||||
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.model.api.BaseBundle;
|
||||
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.ExtensionDt;
|
||||
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.ISupportsUndeclaredExtensions;
|
||||
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.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.XhtmlDt;
|
||||
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 FhirContext myContext;
|
||||
|
@ -151,6 +152,12 @@ class ParserState<T extends IElement> {
|
|||
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 {
|
||||
|
||||
private BaseBundle myInstance;
|
||||
|
@ -189,9 +196,9 @@ class ParserState<T extends IElement> {
|
|||
|
||||
private int myCatState = STATE_NONE;
|
||||
|
||||
private BundleCategory myInstance;
|
||||
private Tag myInstance;
|
||||
|
||||
public AtomCategoryState(BundleCategory theEntry) {
|
||||
public AtomCategoryState(Tag theEntry) {
|
||||
super(null);
|
||||
myInstance = theEntry;
|
||||
}
|
||||
|
@ -205,7 +212,10 @@ class ParserState<T extends IElement> {
|
|||
} else if ("scheme".equals(theName)) {
|
||||
myInstance.setScheme(theValue);
|
||||
} 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) {
|
||||
case STATE_LABEL:
|
||||
myInstance.setLabel(theValue);
|
||||
|
@ -306,12 +316,39 @@ class ParserState<T extends IElement> {
|
|||
if (myEntry.getUpdated().isEmpty() == false) {
|
||||
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()) {
|
||||
String subStr = "/" + Constants.PARAM_HISTORY + "/";
|
||||
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) {
|
||||
startIndex = startIndex + subStr.length();
|
||||
startIndex = startIndex + subStrVid.length();
|
||||
int endIndex = linkSelfValue.indexOf('?', startIndex);
|
||||
if (endIndex == -1) {
|
||||
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);
|
||||
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
|
||||
}
|
||||
|
||||
protected IElement getCurrentElement() {
|
||||
protected Object getCurrentElement() {
|
||||
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 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 int myDepth;
|
||||
private XhtmlDt myDt;
|
||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.parser;
|
|||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
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.RuntimeResourceDefinition;
|
||||
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.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IElement;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
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.NarrativeDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
|
||||
|
@ -104,9 +106,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
@Override
|
||||
public void encodeBundleToWriter(Bundle theBundle, Writer theWriter) throws DataFormatException {
|
||||
try {
|
||||
XMLStreamWriter eventWriter;
|
||||
eventWriter = myXmlOutputFactory.createXMLStreamWriter(theWriter);
|
||||
eventWriter = decorateStreamWriter(eventWriter);
|
||||
XMLStreamWriter eventWriter = createXmlWriter(theWriter);
|
||||
|
||||
eventWriter.writeStartElement("feed");
|
||||
eventWriter.writeDefaultNamespace(ATOM_NS);
|
||||
|
@ -147,7 +147,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
writeOptionalTagWithTextNode(eventWriter, "published", nextEntry.getPublished());
|
||||
|
||||
if (nextEntry.getCategories() != null) {
|
||||
for (BundleCategory next : nextEntry.getCategories()) {
|
||||
for (Tag next : nextEntry.getCategories()) {
|
||||
eventWriter.writeStartElement("category");
|
||||
eventWriter.writeAttribute("term", defaultString(next.getTerm()));
|
||||
eventWriter.writeAttribute("label", defaultString(next.getLabel()));
|
||||
|
@ -155,7 +155,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
eventWriter.writeEndElement();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!nextEntry.getLinkSelf().isEmpty()) {
|
||||
writeAtomLink(eventWriter, "self", nextEntry.getLinkSelf());
|
||||
}
|
||||
|
@ -193,11 +193,10 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void encodeResourceToWriter(IResource theResource, Writer stringWriter) throws DataFormatException {
|
||||
public void encodeResourceToWriter(IResource theResource, Writer theWriter) throws DataFormatException {
|
||||
XMLStreamWriter eventWriter;
|
||||
try {
|
||||
eventWriter = myXmlOutputFactory.createXMLStreamWriter(stringWriter);
|
||||
eventWriter = decorateStreamWriter(eventWriter);
|
||||
eventWriter = createXmlWriter(theWriter);
|
||||
|
||||
encodeResourceToXmlStreamWriter(theResource, eventWriter, false);
|
||||
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
|
||||
public <T extends IResource> Bundle parseBundle(Class<T> theResourceType, Reader theReader) {
|
||||
XMLEventReader streamReader;
|
||||
public void encodeTagListToWriter(TagList theTagList, Writer theWriter) throws IOException {
|
||||
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) {
|
||||
throw new DataFormatException(e);
|
||||
} catch (FactoryConfigurationError 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
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;
|
||||
try {
|
||||
streamReader = myXmlInputFactory.createXMLEventReader(theReader);
|
||||
|
@ -230,14 +280,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
} catch (FactoryConfigurationError e) {
|
||||
throw new ConfigurationException("Failed to initialize STaX event factory", e);
|
||||
}
|
||||
|
||||
return parseResource(theResourceType, streamReader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IParser setPrettyPrint(boolean thePrettyPrint) {
|
||||
myPrettyPrint = thePrettyPrint;
|
||||
return this;
|
||||
return streamReader;
|
||||
}
|
||||
|
||||
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 {
|
||||
while (streamReader.hasNext()) {
|
||||
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 {
|
||||
theEventWriter.writeStartElement(theElementName);
|
||||
if (StringUtils.isNotBlank(theIdDt.getValue())) {
|
||||
|
@ -654,4 +691,12 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
}
|
||||
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;
|
||||
HttpResponse response;
|
||||
try {
|
||||
httpRequest = clientInvocation.asHttpRequest(myUrlBase, createExtraParams());
|
||||
httpRequest = clientInvocation.asHttpRequest(myUrlBase, createExtraParams(), getEncoding());
|
||||
response = myClient.execute(httpRequest);
|
||||
} catch (DataFormatException e) {
|
||||
throw new FhirClientConnectionException(e);
|
||||
|
@ -179,9 +179,9 @@ public abstract class BaseClient {
|
|||
HashMap<String, List<String>> retVal = new LinkedHashMap<String, List<String>>();
|
||||
|
||||
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) {
|
||||
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList(Constants.CT_JSON));
|
||||
retVal.put(Constants.PARAM_FORMAT, Collections.singletonList("json"));
|
||||
}
|
||||
|
||||
if (isPrettyPrint()) {
|
||||
|
@ -229,7 +229,7 @@ public abstract class BaseClient {
|
|||
return new StringReader("");
|
||||
}
|
||||
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);
|
||||
charset = ct.getCharset();
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ import org.apache.http.Header;
|
|||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
||||
public abstract class BaseClientInvocation {
|
||||
|
||||
private List<Header> myHeaders;
|
||||
|
@ -49,8 +51,11 @@ public abstract class BaseClientInvocation {
|
|||
* The FHIR server base url (with a trailing "/")
|
||||
* @param theExtraParams
|
||||
* 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) {
|
||||
boolean first = theWithQuestionMark;
|
||||
|
|
|
@ -20,48 +20,52 @@ package ca.uhn.fhir.rest.client;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
|
||||
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.TagList;
|
||||
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.EncodingEnum;
|
||||
|
||||
public abstract class BaseClientInvocationWithContents extends BaseClientInvocation {
|
||||
|
||||
private final Bundle myBundle;
|
||||
private final FhirContext myContext;
|
||||
private final IResource myResource;
|
||||
private String myUrlExtension;
|
||||
|
||||
public BaseClientInvocationWithContents(FhirContext theContext, Bundle theBundle) {
|
||||
super();
|
||||
myContext = theContext;
|
||||
myResource = null;
|
||||
myBundle = theBundle;
|
||||
}
|
||||
private final String myUrlExtension;
|
||||
private final TagList myTagList;
|
||||
|
||||
public BaseClientInvocationWithContents(FhirContext theContext, IResource theResource, String theUrlExtension) {
|
||||
super();
|
||||
myContext = theContext;
|
||||
myResource = theResource;
|
||||
myBundle = null;
|
||||
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
|
||||
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();
|
||||
b.append(theUrlBase);
|
||||
if (!theUrlBase.endsWith("/")) {
|
||||
|
@ -71,11 +75,26 @@ public abstract class BaseClientInvocationWithContents extends BaseClientInvocat
|
|||
|
||||
appendExtraParamsWithQuestionMark(theExtraParams, b, true);
|
||||
|
||||
String url = b.toString();
|
||||
String contents = myContext.newXmlParser().encodeResourceToString(myResource);
|
||||
StringEntity entity = new StringEntity(contents, ContentType.create(Constants.CT_FHIR_XML, "UTF-8"));
|
||||
IParser parser;
|
||||
String contentType;
|
||||
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);
|
||||
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.rest.client.api.IRestfulClient;
|
||||
import ca.uhn.fhir.rest.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
||||
public class ClientInvocationHandler extends BaseClient implements InvocationHandler {
|
||||
|
||||
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>();
|
||||
|
||||
public ClientInvocationHandler(HttpClient theClient, FhirContext theContext, String theUrlBase, Class<? extends IRestfulClient> theClientType) {
|
||||
super(theClient, theUrlBase);
|
||||
|
||||
|
||||
try {
|
||||
myMethodToReturnValue.put(theClientType.getMethod("getFhirContext"), theContext);
|
||||
myMethodToReturnValue.put(theClientType.getMethod("getHttpClient"), theClient);
|
||||
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) {
|
||||
throw new ConfigurationException("Failed to find methods on client. This is a HAPI bug!", e);
|
||||
} catch (SecurityException e) {
|
||||
|
@ -63,11 +71,39 @@ public class ClientInvocationHandler extends BaseClient implements InvocationHan
|
|||
}
|
||||
|
||||
BaseMethodBinding binding = myBindings.get(theMethod);
|
||||
BaseClientInvocation clientInvocation = binding.invokeClient(theArgs);
|
||||
|
||||
return invokeClient(binding, clientInvocation);
|
||||
if (binding != null) {
|
||||
BaseClientInvocation clientInvocation = binding.invokeClient(theArgs);
|
||||
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.HttpRequestBase;
|
||||
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
||||
public class DeleteClientInvocation extends BaseClientInvocation {
|
||||
|
||||
private String myUrlPath;
|
||||
|
@ -37,7 +39,7 @@ public class DeleteClientInvocation extends BaseClientInvocation {
|
|||
}
|
||||
|
||||
@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();
|
||||
b.append(theUrlBase);
|
||||
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) {
|
||||
GetClientInvocation invocation = ReadMethodBinding.createReadInvocation(theId, toResourceName(theType));
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
|
@ -92,12 +92,11 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
return resp;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public MethodOutcome delete(final Class<? extends IResource> theType, IdDt theId) {
|
||||
DeleteClientInvocation invocation = DeleteMethodBinding.createDeleteInvocation(toResourceName(theType), theId);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
GetClientInvocation invocation = ReadMethodBinding.createVReadInvocation(theId, theVersionId, toResourceName(theType));
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
|
@ -159,7 +158,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
|
||||
GetClientInvocation invocation = SearchMethodBinding.createSearchInvocation(toResourceName(theType), params);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
|
@ -179,12 +178,12 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
public MethodOutcome create(IResource theResource) {
|
||||
BaseClientInvocation invocation = CreateMethodBinding.createCreateInvocation(theResource, myContext);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||
final String resourceName = def.getName();
|
||||
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
@Override
|
||||
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) {
|
||||
BaseClientInvocation invocation = UpdateMethodBinding.createUpdateInvocation(theResource, theIdDt, null, myContext);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||
final String resourceName = def.getName();
|
||||
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
@Override
|
||||
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) {
|
||||
BaseClientInvocation invocation = ValidateMethodBinding.createValidateInvocation(theResource, null, myContext);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||
final String resourceName = def.getName();
|
||||
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
@Override
|
||||
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) {
|
||||
GetClientInvocation invocation = HistoryMethodBinding.createHistoryInvocation(toResourceName(theType), theIdDt);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
|
@ -267,7 +266,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
public Conformance conformance() {
|
||||
GetClientInvocation invocation = ConformanceMethodBinding.createConformanceInvocation();
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams());
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding());
|
||||
}
|
||||
|
||||
IClientResponseHandler binding = new IClientResponseHandler() {
|
||||
|
|
|
@ -32,6 +32,8 @@ import org.apache.http.client.methods.HttpGet;
|
|||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
|
||||
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 {
|
||||
|
||||
|
@ -42,13 +44,12 @@ public class GetClientInvocation extends BaseClientInvocation {
|
|||
myParameters = theParameters;
|
||||
myUrlPath = StringUtils.join(theUrlFragments, '/');
|
||||
}
|
||||
|
||||
|
||||
public GetClientInvocation(String theUrlPath) {
|
||||
myParameters = new HashMap<String, List<String>>();
|
||||
myUrlPath = theUrlPath;
|
||||
}
|
||||
|
||||
|
||||
public GetClientInvocation(String... theUrlFragments) {
|
||||
myParameters = new HashMap<String, List<String>>();
|
||||
myUrlPath = StringUtils.join(theUrlFragments, '/');
|
||||
|
@ -63,7 +64,7 @@ public class GetClientInvocation extends BaseClientInvocation {
|
|||
}
|
||||
|
||||
@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();
|
||||
b.append(theUrlBase);
|
||||
if (!theUrlBase.endsWith("/")) {
|
||||
|
@ -76,29 +77,35 @@ public class GetClientInvocation extends BaseClientInvocation {
|
|||
if (next.getValue() == null || next.getValue().isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String nextKey = next.getKey();
|
||||
for (String nextValue : next.getValue()) {
|
||||
if (first) {
|
||||
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);
|
||||
}
|
||||
first = addQueryParameter(b, first, nextKey, nextValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
appendExtraParamsWithQuestionMark(theExtraParams, b, first);
|
||||
|
||||
|
||||
HttpGet retVal = new HttpGet(b.toString());
|
||||
super.addHeadersToRequest(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 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.TagList;
|
||||
|
||||
public class PostClientInvocation extends BaseClientInvocationWithContents {
|
||||
|
||||
public PostClientInvocation(FhirContext theContext, Bundle theBundle) {
|
||||
super(theContext, theBundle);
|
||||
}
|
||||
|
||||
public PostClientInvocation(FhirContext theContext, IResource theResource, String theUrlExtension) {
|
||||
super(theContext, theResource, theUrlExtension);
|
||||
}
|
||||
|
||||
|
||||
public PostClientInvocation(FhirContext theContext, TagList theTagList, String... theUrlExtension) {
|
||||
super(theContext, theTagList, theUrlExtension);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected HttpPost createRequest(String url, StringEntity theEntity) {
|
||||
HttpPost retVal = new HttpPost(url);
|
||||
|
|
|
@ -25,15 +25,10 @@ import org.apache.http.client.methods.HttpRequestBase;
|
|||
import org.apache.http.entity.StringEntity;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
|
||||
public class PutClientInvocation extends BaseClientInvocationWithContents {
|
||||
|
||||
public PutClientInvocation(FhirContext theContext, Bundle theBundle) {
|
||||
super(theContext, theBundle);
|
||||
}
|
||||
|
||||
public PutClientInvocation(FhirContext theContext, IResource theResource, String theUrlExtension) {
|
||||
super(theContext, theResource, theUrlExtension);
|
||||
}
|
||||
|
|
|
@ -28,8 +28,15 @@ import ca.uhn.fhir.rest.server.EncodingEnum;
|
|||
|
||||
public interface IRestfulClient {
|
||||
|
||||
/**
|
||||
* Returns the FHIR context associated with this client
|
||||
*/
|
||||
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();
|
||||
|
||||
/**
|
||||
|
@ -37,6 +44,9 @@ public interface IRestfulClient {
|
|||
* queries. This means that the client will append the "_format" param
|
||||
* to GET methods (read/search/etc), and will add an appropriate header for
|
||||
* 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);
|
||||
|
||||
|
@ -44,6 +54,8 @@ public interface IRestfulClient {
|
|||
* 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
|
||||
* 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);
|
||||
|
||||
|
|
|
@ -25,8 +25,6 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
|||
public class NonFhirResponseException extends BaseServerResponseException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final String myContentType;
|
||||
private final String myResponseText;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -37,18 +35,8 @@ public class NonFhirResponseException extends BaseServerResponseException {
|
|||
* @param theStatusCode
|
||||
* @param theContentType
|
||||
*/
|
||||
public NonFhirResponseException(String theMessage, String theContentType, int theStatusCode, String theResponseText) {
|
||||
public NonFhirResponseException(int theStatusCode, String 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%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
@ -32,7 +34,6 @@ import java.util.Enumeration;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
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.model.api.Bundle;
|
||||
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.RestfulOperationTypeEnum;
|
||||
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.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.Metadata;
|
||||
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.exceptions.BaseServerResponseException;
|
||||
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;
|
||||
|
||||
public abstract class BaseMethodBinding implements IClientResponseHandler {
|
||||
|
@ -72,12 +85,12 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
private List<IParameter> myParameters;
|
||||
private Object myProvider;
|
||||
|
||||
public BaseMethodBinding(Method theMethod, FhirContext theConetxt, Object theProvider) {
|
||||
public BaseMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
assert theMethod != null;
|
||||
assert theConetxt != null;
|
||||
assert theContext != null;
|
||||
|
||||
myMethod = theMethod;
|
||||
myContext = theConetxt;
|
||||
myContext = theContext;
|
||||
myProvider = theProvider;
|
||||
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 boolean matches(Request theRequest);
|
||||
public abstract boolean incomingServerRequestMatchesMethod(Request theRequest);
|
||||
|
||||
protected IParser createAppropriateParser(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode) throws IOException {
|
||||
IParser parser;
|
||||
if (Constants.CT_ATOM_XML.equals(theResponseMimeType)) {
|
||||
parser = getContext().newXmlParser();
|
||||
} else if (Constants.CT_FHIR_XML.equals(theResponseMimeType)) {
|
||||
parser = getContext().newXmlParser();
|
||||
} 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));
|
||||
protected IParser createAppropriateParserForParsingResponse(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode) {
|
||||
EncodingEnum encoding = EncodingEnum.forContentType(theResponseMimeType);
|
||||
if (encoding==null) {
|
||||
NonFhirResponseException ex = new NonFhirResponseException(theResponseStatusCode, "Response contains non-FHIR content-type: " + theResponseMimeType);
|
||||
populateException(ex, theResponseReader);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
IParser parser=encoding.newParser(getContext());
|
||||
return parser;
|
||||
}
|
||||
|
||||
|
@ -134,10 +139,10 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
return myParameters;
|
||||
}
|
||||
|
||||
protected Object invokeServerMethod(Object theResourceProvider, Object[] theMethodParams) {
|
||||
protected Object invokeServerMethod(Object[] theMethodParams) {
|
||||
try {
|
||||
Method method = getMethod();
|
||||
return method.invoke(theResourceProvider, theMethodParams);
|
||||
return method.invoke(getProvider(), theMethodParams);
|
||||
} catch (InvocationTargetException e) {
|
||||
if (e.getCause() instanceof BaseServerResponseException) {
|
||||
throw (BaseServerResponseException) e.getCause();
|
||||
|
@ -164,8 +169,11 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
Delete delete = theMethod.getAnnotation(Delete.class);
|
||||
History history = theMethod.getAnnotation(History.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 (!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;
|
||||
}
|
||||
|
||||
|
@ -180,7 +188,11 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
}
|
||||
|
||||
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
|
||||
} else if (Bundle.class.equals(returnTypeFromMethod)) {
|
||||
// returns a bundle
|
||||
|
@ -190,7 +202,7 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
returnTypeFromMethod = ReflectionUtil.getGenericCollectionTypeOfMethodReturnType(theMethod);
|
||||
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)
|
||||
+ " - 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 {
|
||||
if (!IResource.class.equals(returnTypeFromMethod) && !verifyIsValidResourceReturnType(returnTypeFromMethod)) {
|
||||
|
@ -214,6 +226,12 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
returnTypeFromAnnotation = update.type();
|
||||
} else if (validate != null) {
|
||||
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) {
|
||||
|
@ -259,6 +277,12 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
return new HistoryMethodBinding(theMethod, theContext, theProvider);
|
||||
} else if (validate != null) {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
public static EncodingEnum determineResponseEncoding(HttpServletRequest theRequest, Map<String, String[]> theParams) {
|
||||
String[] format = theParams.remove(Constants.PARAM_FORMAT);
|
||||
public static EncodingEnum determineResponseEncoding(Request theReq) {
|
||||
String[] format = theReq.getParameters().remove(Constants.PARAM_FORMAT);
|
||||
if (format != null) {
|
||||
for (String nextFormat : format) {
|
||||
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) {
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
EncodingEnum retVal = Constants.FORMAT_VAL_TO_ENCODING.get(acceptValues.nextElement());
|
||||
|
@ -308,6 +332,27 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
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) {
|
||||
Object obj1 = null;
|
||||
for (Object object : theAnnotations) {
|
||||
|
@ -349,6 +394,18 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
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 {
|
||||
if (response == null) {
|
||||
return Collections.emptyList();
|
||||
|
@ -377,4 +434,47 @@ public abstract class BaseMethodBinding implements IClientResponseHandler {
|
|||
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%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PushbackReader;
|
||||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
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.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.valueset.RestfulOperationTypeEnum;
|
||||
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.client.BaseClientInvocation;
|
||||
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.EncodingEnum;
|
||||
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;
|
||||
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 {
|
||||
private static final String LABEL = "label=\"";
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class);
|
||||
private static final String SCHEME = "scheme=\"";
|
||||
|
||||
private boolean myReturnVoid;
|
||||
|
||||
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
|
||||
public Object invokeClient(String theResponseMimeType, Reader theResponseReader, int theResponseStatusCode, Map<String, List<String>> theHeaders) throws IOException, BaseServerResponseException {
|
||||
switch (theResponseStatusCode) {
|
||||
|
@ -85,51 +104,38 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
|||
}
|
||||
MethodOutcome retVal = process2xxResponse(getContext(), getResourceName(), theResponseStatusCode, theResponseMimeType, theResponseReader, theHeaders);
|
||||
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:
|
||||
throw new UnclassifiedServerFailureException(theResponseStatusCode, IOUtils.toString(theResponseReader));
|
||||
throw processNon2xxResponseAndReturnExceptionToThrow(theResponseStatusCode, theResponseMimeType, theResponseReader);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
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());
|
||||
IResource resource;
|
||||
if (requestContainsResource()) {
|
||||
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 {
|
||||
resource = null;
|
||||
}
|
||||
|
||||
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, resource);
|
||||
}
|
||||
|
||||
Object[] params = createParametersForServerRequest(theRequest, resource);
|
||||
addParametersForServerRequest(theRequest, params);
|
||||
|
||||
MethodOutcome response;
|
||||
try {
|
||||
response = (MethodOutcome) invokeServerMethod(getProvider(), params);
|
||||
response = (MethodOutcome) invokeServerMethod(params);
|
||||
} catch (InternalErrorException e) {
|
||||
ourLog.error("Internal error during method invocation", e);
|
||||
streamOperationOutcome(e, theServer, encoding, theResponse, theRequest);
|
||||
|
@ -183,10 +189,6 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
|||
// getMethod().in
|
||||
}
|
||||
|
||||
public boolean isReturnVoid() {
|
||||
return myReturnVoid;
|
||||
}
|
||||
|
||||
/*
|
||||
* @Override public void invokeServer(RestfulServer theServer, Request
|
||||
* theRequest, HttpServletResponse theResponse) throws
|
||||
|
@ -232,23 +234,8 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
|||
* writer.close(); } // getMethod().in }
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean matches(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;
|
||||
public boolean isReturnVoid() {
|
||||
return myReturnVoid;
|
||||
}
|
||||
|
||||
private void addLocationHeader(Request theRequest, HttpServletResponse theResponse, MethodOutcome response) {
|
||||
|
@ -265,6 +252,78 @@ public abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBindin
|
|||
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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,13 +22,19 @@ package ca.uhn.fhir.rest.method;
|
|||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
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.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.client.BaseClientInvocation;
|
||||
import ca.uhn.fhir.rest.param.IParameter;
|
||||
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.exceptions.InternalErrorException;
|
||||
|
||||
|
@ -88,7 +94,18 @@ abstract class BaseOutcomeReturningMethodBindingWithResourceParam extends BaseOu
|
|||
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%
|
||||
*/
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.*;
|
||||
|
||||
import java.io.IOException;
|
||||
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.rest.client.exceptions.InvalidResponseException;
|
||||
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.RestfulServer;
|
||||
|
@ -92,8 +91,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
} else if (Bundle.class.isAssignableFrom(methodReturnType)) {
|
||||
myMethodReturnType = MethodReturnTypeEnum.BUNDLE;
|
||||
} else {
|
||||
throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: "
|
||||
+ theMethod.getDeclaringClass().getCanonicalName());
|
||||
throw new ConfigurationException("Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
||||
}
|
||||
|
||||
myResourceType = theReturnResourceType;
|
||||
|
@ -119,14 +117,14 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
|
||||
@Override
|
||||
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()) {
|
||||
case BUNDLE: {
|
||||
Bundle bundle;
|
||||
if (myResourceType != null) {
|
||||
bundle = parser.parseBundle(myResourceType, theResponseReader);
|
||||
}else {
|
||||
} else {
|
||||
bundle = parser.parseBundle(theResponseReader);
|
||||
}
|
||||
switch (getMethodReturnType()) {
|
||||
|
@ -157,10 +155,11 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
List<String> lmHeaders = theHeaders.get(Constants.HEADER_LAST_MODIFIED_LOWERCASE);
|
||||
if (lmHeaders != null && lmHeaders.size() > 0 && StringUtils.isNotBlank(lmHeaders.get(0))) {
|
||||
try {
|
||||
InstantDt lmValue = new InstantDt(lmHeaders.get(0));
|
||||
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, lmValue);
|
||||
InstantDt lmValue = new InstantDt(lmHeaders.get(0));
|
||||
resource.getResourceMetadata().put(ResourceMetadataKeyEnum.UPDATED, lmValue);
|
||||
} 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!");
|
||||
}
|
||||
|
||||
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
|
||||
public void invokeServer(RestfulServer theServer, Request theRequest, HttpServletResponse theResponse) throws BaseServerResponseException, IOException {
|
||||
|
||||
// Pretty print
|
||||
boolean prettyPrint = prettyPrintResponse(theRequest);
|
||||
|
||||
|
||||
// Narrative mode
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
String[] narrative = requestParams.remove(Constants.PARAM_NARRATIVE);
|
||||
|
@ -199,7 +198,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
}
|
||||
|
||||
// Determine response encoding
|
||||
EncodingEnum responseEncoding = determineResponseEncoding(theRequest.getServletRequest(), requestParams);
|
||||
EncodingEnum responseEncoding = determineResponseEncoding(theRequest);
|
||||
|
||||
// Is this request coming from a browser
|
||||
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()) {
|
||||
case BUNDLE:
|
||||
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) {
|
||||
Object retValObj = theResourceMetadata.get(theKey);
|
||||
if (retValObj == null) {
|
||||
|
@ -251,8 +249,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
return (IdDt) retValObj;
|
||||
}
|
||||
}
|
||||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
|
||||
+ IdDt.class.getCanonicalName());
|
||||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + IdDt.class.getCanonicalName());
|
||||
}
|
||||
|
||||
private InstantDt getInstantFromMetadataOrNullIfNone(Map<ResourceMetadataKeyEnum, Object> theResourceMetadata, ResourceMetadataKeyEnum theKey) {
|
||||
|
@ -268,8 +265,21 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
return (InstantDt) retValObj;
|
||||
}
|
||||
}
|
||||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected "
|
||||
+ InstantDt.class.getCanonicalName());
|
||||
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + 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) {
|
||||
|
@ -286,8 +296,8 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
return parser.setPrettyPrint(thePrettyPrint).setSuppressNarratives(theNarrativeMode == NarrativeModeEnum.SUPPRESS);
|
||||
}
|
||||
|
||||
private void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, List<IResource> theResult, EncodingEnum theResponseEncoding, String theServerBase,
|
||||
String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
|
||||
private void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, List<IResource> theResult, EncodingEnum theResponseEncoding, String theServerBase, String theCompleteUrl, boolean thePrettyPrint, boolean theRequestIsBrowser,
|
||||
NarrativeModeEnum theNarrativeMode) throws IOException {
|
||||
assert !theServerBase.endsWith("/");
|
||||
|
||||
theHttpResponse.setStatus(200);
|
||||
|
@ -300,7 +310,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
theHttpResponse.setContentType(theResponseEncoding.getBundleContentType());
|
||||
}
|
||||
|
||||
theHttpResponse.setCharacterEncoding("UTF-8");
|
||||
theHttpResponse.setCharacterEncoding(Constants.CHARSET_UTF_8);
|
||||
|
||||
theServer.addHeadersToResponse(theHttpResponse);
|
||||
|
||||
|
@ -325,7 +335,6 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
RuntimeResourceDefinition def = getContext().getResourceDefinition(next);
|
||||
|
||||
if (next.getId() != null && StringUtils.isNotBlank(next.getId().getValue())) {
|
||||
|
@ -341,10 +350,10 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
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
|
||||
|| getSystemOperationType() == RestfulOperationSystemEnum.HISTORY_SYSTEM) {
|
||||
if (getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_INSTANCE || getResourceOperationType() == RestfulOperationTypeEnum.HISTORY_TYPE || getSystemOperationType() == RestfulOperationSystemEnum.HISTORY_SYSTEM) {
|
||||
IdDt versionId = getIdFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.VERSION_ID);
|
||||
if (versionId != null) {
|
||||
b.append('/');
|
||||
|
@ -368,6 +377,13 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
entry.setUpdated(updated);
|
||||
}
|
||||
|
||||
TagList tagList = getTagListFromMetadataOrNullIfNone(next.getResourceMetadata(), ResourceMetadataKeyEnum.TAG_LIST);
|
||||
if (tagList!=null) {
|
||||
for (Tag nextTag : tagList) {
|
||||
entry.addCategory(nextTag);
|
||||
}
|
||||
}
|
||||
|
||||
boolean haveQ = false;
|
||||
if (thePrettyPrint) {
|
||||
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,
|
||||
boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
|
||||
private void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, NarrativeModeEnum theNarrativeMode) throws IOException {
|
||||
|
||||
theHttpResponse.setStatus(200);
|
||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||
|
@ -417,7 +432,9 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
} else {
|
||||
theHttpResponse.setContentType(theResponseEncoding.getResourceContentType());
|
||||
}
|
||||
theHttpResponse.setCharacterEncoding("UTF-8");
|
||||
theHttpResponse.setCharacterEncoding(Constants.CHARSET_UTF_8);
|
||||
|
||||
theServer.addHeadersToResponse(theHttpResponse);
|
||||
|
||||
InstantDt lastUpdated = getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), ResourceMetadataKeyEnum.UPDATED);
|
||||
if (lastUpdated != null) {
|
||||
|
@ -432,7 +449,7 @@ abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PrintWriter writer = theHttpResponse.getWriter();
|
||||
try {
|
||||
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.method.SearchMethodBinding.RequestType;
|
||||
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.InvalidRequestException;
|
||||
|
||||
public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding {
|
||||
|
||||
|
@ -71,19 +71,13 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<IResource> invokeServer(Object theResourceProvider, Request theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException {
|
||||
IResource conf;
|
||||
try {
|
||||
conf = (Conformance) invokeServerMethod(theResourceProvider, theMethodParams);
|
||||
} catch (Exception e) {
|
||||
throw new InternalErrorException("Failed to call access method", e);
|
||||
}
|
||||
|
||||
public List<IResource> invokeServer(Request theRequest, Object[] theMethodParams) throws BaseServerResponseException {
|
||||
IResource conf = (IResource) invokeServerMethod(theMethodParams);
|
||||
return Collections.singletonList(conf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Request theRequest) {
|
||||
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||
if (theRequest.getRequestType() == RequestType.OPTIONS) {
|
||||
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.method.SearchMethodBinding.RequestType;
|
||||
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.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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");
|
||||
|
@ -118,12 +118,6 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
|||
return myResourceName;
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public boolean matches(Request theRequest) {
|
||||
// // TODO Auto-generated method stub
|
||||
// return super.matches(theRequest);
|
||||
// }
|
||||
|
||||
@Override
|
||||
public BaseClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||
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.GetClientInvocation;
|
||||
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.IResourceProvider;
|
||||
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) {
|
||||
super(toReturnType(theMethod, theProvider), theMethod, theConetxt, theProvider);
|
||||
|
||||
myIdParamIndex = Util.findIdParameterIndex(theMethod);
|
||||
myIdParamIndex = ParameterUtil.findIdParameterIndex(theMethod);
|
||||
|
||||
History historyAnnotation = theMethod.getAnnotation(History.class);
|
||||
Class<? extends IResource> type = historyAnnotation.type();
|
||||
|
@ -142,12 +143,12 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@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) {
|
||||
theMethodParams[myIdParamIndex] = theRequest.getId();
|
||||
}
|
||||
|
||||
Object response = invokeServerMethod(theResourceProvider, theMethodParams);
|
||||
Object response = invokeServerMethod(theMethodParams);
|
||||
|
||||
List<IResource> resources = toResourceList(response);
|
||||
int index=0;
|
||||
|
@ -168,7 +169,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
// ObjectUtils.equals is replaced by a JDK7 method..
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public boolean matches(Request theRequest) {
|
||||
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||
if (!Constants.PARAM_HISTORY.equals(theRequest.getOperation())) {
|
||||
return false;
|
||||
}
|
||||
|
@ -185,7 +186,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (theRequest.getVersion() != null && !theRequest.getVersion().isEmpty()) {
|
||||
if (theRequest.getVersionId() != null && !theRequest.getVersionId().isEmpty()) {
|
||||
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.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.exceptions.InternalErrorException;
|
||||
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");
|
||||
|
||||
Integer idIndex = Util.findIdParameterIndex(theMethod);
|
||||
Integer versionIdIndex = Util.findVersionIdParameterIndex(theMethod);
|
||||
Integer idIndex = ParameterUtil.findIdParameterIndex(theMethod);
|
||||
Integer versionIdIndex = ParameterUtil.findVersionIdParameterIndex(theMethod);
|
||||
|
||||
myIdIndex = idIndex;
|
||||
myVersionIdIndex = versionIdIndex;
|
||||
|
@ -71,7 +72,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Request theRequest) {
|
||||
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||
if (!theRequest.getResourceName().equals(getResourceName())) {
|
||||
return false;
|
||||
}
|
||||
|
@ -80,7 +81,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if ((theRequest.getVersion() == null) != (myVersionIdIndex == null)) {
|
||||
if ((theRequest.getVersionId() == null) != (myVersionIdIndex == null)) {
|
||||
return false;
|
||||
}
|
||||
if (theRequest.getId() == null) {
|
||||
|
@ -106,13 +107,13 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@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();
|
||||
if (myVersionIdIndex != null) {
|
||||
theMethodParams[myVersionIdIndex] = theRequest.getVersion();
|
||||
theMethodParams[myVersionIdIndex] = theRequest.getVersionId();
|
||||
}
|
||||
|
||||
Object response = invokeServerMethod(theResourceProvider, theMethodParams);
|
||||
Object response = invokeServerMethod(theMethodParams);
|
||||
|
||||
return toResourceList(response);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ public class Request {
|
|||
private Map<String, String[]> myParameters;
|
||||
private RequestType myRequestType;
|
||||
private String myResourceName;
|
||||
private String mySecondaryOperation;
|
||||
private HttpServletRequest myServletRequest;
|
||||
private HttpServletResponse myServletResponse;
|
||||
private IdDt myVersion;
|
||||
|
@ -77,6 +78,10 @@ public class Request {
|
|||
return myResourceName;
|
||||
}
|
||||
|
||||
public String getSecondaryOperation() {
|
||||
return mySecondaryOperation;
|
||||
}
|
||||
|
||||
public HttpServletRequest getServletRequest() {
|
||||
return myServletRequest;
|
||||
}
|
||||
|
@ -85,7 +90,7 @@ public class Request {
|
|||
return myServletResponse;
|
||||
}
|
||||
|
||||
public IdDt getVersion() {
|
||||
public IdDt getVersionId() {
|
||||
return myVersion;
|
||||
}
|
||||
|
||||
|
@ -121,6 +126,10 @@ public class Request {
|
|||
myResourceName = theResourceName;
|
||||
}
|
||||
|
||||
public void setSecondaryOperation(String theSecondaryOperation) {
|
||||
mySecondaryOperation = theSecondaryOperation;
|
||||
}
|
||||
|
||||
public void setServletRequest(HttpServletRequest theRequest) {
|
||||
myServletRequest = theRequest;
|
||||
}
|
||||
|
|
|
@ -103,24 +103,24 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@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.getVersion() == null;
|
||||
assert theRequest.getVersionId() == null;
|
||||
|
||||
Object response = invokeServerMethod(theResourceProvider, theMethodParams);
|
||||
Object response = invokeServerMethod(theMethodParams);
|
||||
|
||||
return toResourceList(response);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Request theRequest) {
|
||||
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||
if (!theRequest.getResourceName().equals(getResourceName())) {
|
||||
ourLog.trace("Method {} doesn't match because resource name {} != {}", getMethod().getName(), theRequest.getResourceName(), getResourceName());
|
||||
return false;
|
||||
}
|
||||
if (theRequest.getId() != null || theRequest.getVersion() != null) {
|
||||
ourLog.trace("Method {} doesn't match because ID or Version are not null: {} - {}", theRequest.getId(), theRequest.getVersion());
|
||||
if (theRequest.getId() != null || theRequest.getVersionId() != null) {
|
||||
ourLog.trace("Method {} doesn't match because ID or Version are not null: {} - {}", theRequest.getId(), theRequest.getVersionId());
|
||||
return false;
|
||||
}
|
||||
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.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.exceptions.InvalidRequestException;
|
||||
|
||||
|
@ -53,11 +54,11 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
|||
public UpdateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(theMethod, theContext, Update.class, theProvider);
|
||||
|
||||
myIdParameterIndex = Util.findIdParameterIndex(theMethod);
|
||||
myIdParameterIndex = ParameterUtil.findIdParameterIndex(theMethod);
|
||||
if (myIdParameterIndex == null) {
|
||||
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
|
||||
|
@ -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();
|
||||
if (myVersionIdParameterIndex != null) {
|
||||
theParams[myVersionIdParameterIndex] = theRequest.getVersion();
|
||||
theParams[myVersionIdParameterIndex] = theRequest.getVersionId();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,10 +143,10 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Request theRequest) {
|
||||
if (super.matches(theRequest)) {
|
||||
public boolean incomingServerRequestMatchesMethod(Request theRequest) {
|
||||
if (super.incomingServerRequestMatchesMethod(theRequest)) {
|
||||
if (myVersionIdParameterIndex != null) {
|
||||
if (theRequest.getVersion() == null) {
|
||||
if (theRequest.getVersionId() == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,15 +21,10 @@ package ca.uhn.fhir.rest.method;
|
|||
*/
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.HashMap;
|
||||
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.
|
||||
*/
|
||||
|
@ -38,32 +33,6 @@ class Util {
|
|||
// 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) {
|
||||
try {
|
||||
|
|
|
@ -34,6 +34,7 @@ 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;
|
||||
|
||||
public class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceParam {
|
||||
|
@ -43,7 +44,7 @@ public class ValidateMethodBinding extends BaseOutcomeReturningMethodBindingWith
|
|||
public ValidateMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(theMethod, theContext, Validate.class, theProvider);
|
||||
|
||||
myIdParameterIndex = Util.findIdParameterIndex(theMethod);
|
||||
myIdParameterIndex = ParameterUtil.findIdParameterIndex(theMethod);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,7 +27,6 @@ import java.util.Map;
|
|||
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
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.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.lang.reflect.Method;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
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.TagList;
|
||||
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.IntegerDt;
|
||||
import ca.uhn.fhir.rest.annotation.Count;
|
||||
|
@ -59,6 +61,84 @@ import ca.uhn.fhir.util.ReflectionUtil;
|
|||
|
||||
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")
|
||||
public static List<IParameter> getResourceParameters(Method theMethod) {
|
||||
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<?>> innerCollectionType = null;
|
||||
if (TagList.class.isAssignableFrom(parameterType)) {
|
||||
param = new TagListParameter();
|
||||
// TagList is handled directly within the method bindings
|
||||
param=new NullParameter();
|
||||
} else {
|
||||
if (Collection.class.isAssignableFrom(parameterType)) {
|
||||
innerCollectionType = (Class<? extends java.util.Collection<?>>) parameterType;
|
||||
|
@ -158,19 +239,6 @@ public class ParameterUtil {
|
|||
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) {
|
||||
if (theArgument instanceof InstantDt) {
|
||||
return (InstantDt) theArgument;
|
||||
|
@ -184,37 +252,6 @@ public class ParameterUtil {
|
|||
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) {
|
||||
if (theArgument instanceof IntegerDt) {
|
||||
return (IntegerDt) theArgument;
|
||||
|
@ -225,28 +262,32 @@ public class ParameterUtil {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static Set<Class<?>> getBindableIntegerTypes() {
|
||||
// TODO: make this constant
|
||||
HashSet<Class<?>> retVal = new HashSet<Class<?>>();
|
||||
retVal.add(IntegerDt.class);
|
||||
retVal.add(Integer.class);
|
||||
return retVal;
|
||||
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 Object fromInteger(Class<?> theType, IntegerDt theArgument) {
|
||||
if (theType.equals(IntegerDt.class)) {
|
||||
if (theArgument == null) {
|
||||
return new IntegerDt();
|
||||
private static Integer findParamAnnotationIndex(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;
|
||||
}
|
||||
}
|
||||
return theArgument;
|
||||
paramIndex++;
|
||||
}
|
||||
if (theType.equals(Integer.class)) {
|
||||
if (theArgument == null) {
|
||||
return null;
|
||||
}
|
||||
return theArgument.getValue();
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid Integer type:" + theType);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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 HEADER_CATEGORY = "Category";
|
||||
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 {
|
||||
Map<String, EncodingEnum> valToEncoding = new HashMap<String, EncodingEnum>();
|
||||
|
|
|
@ -52,6 +52,16 @@ public enum EncodingEnum {
|
|||
ourContentTypeToEncoding.put(next.getResourceContentType(), 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;
|
||||
|
|
|
@ -52,7 +52,7 @@ public class ResourceBinding {
|
|||
|
||||
ourLog.debug("Looking for a handler for {}", theRequest);
|
||||
for (BaseMethodBinding rm : methods) {
|
||||
if (rm.matches(theRequest)) {
|
||||
if (rm.incomingServerRequestMatchesMethod(theRequest)) {
|
||||
ourLog.debug("Handler {} matches", rm);
|
||||
return rm;
|
||||
} else {
|
||||
|
|
|
@ -63,6 +63,7 @@ public class RestfulServer extends HttpServlet {
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private FhirContext myFhirContext;
|
||||
private ResourceBinding myNullResourceBinding = new ResourceBinding();
|
||||
private Collection<Object> myPlainProviders;
|
||||
private Map<String, ResourceBinding> myResourceNameToProvider = new HashMap<String, ResourceBinding>();
|
||||
private Collection<IResourceProvider> myResourceProviders;
|
||||
|
@ -70,12 +71,10 @@ public class RestfulServer extends HttpServlet {
|
|||
private BaseMethodBinding myServerConformanceMethod;
|
||||
private Object myServerConformanceProvider;
|
||||
private String myServerName = "HAPI FHIR Server";
|
||||
private String myServerVersion = VersionUtil.getVersion(); // defaults to
|
||||
// HAPI version
|
||||
private boolean myUseBrowserFriendlyContentTypes;
|
||||
private ResourceBinding myNullResourceBinding = new ResourceBinding();
|
||||
|
||||
/** This is configurable but by default we jsut use HAPI version */
|
||||
private String myServerVersion = VersionUtil.getVersion();
|
||||
private boolean myStarted;
|
||||
private boolean myUseBrowserFriendlyContentTypes;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -90,22 +89,22 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* This method is called prior to sending a response to incoming requests. It is
|
||||
* used to add custom headers.
|
||||
* This method is called prior to sending a response to incoming requests.
|
||||
* It is used to add custom headers.
|
||||
* <p>
|
||||
* Use caution if overriding this method: it is recommended to call
|
||||
* <code>super.addHeadersToResponse</code> to avoid inadvertantly
|
||||
* disabling functionality.
|
||||
* </p>
|
||||
* <code>super.addHeadersToResponse</code> to avoid inadvertantly disabling
|
||||
* functionality.
|
||||
* </p>
|
||||
*/
|
||||
public void addHeadersToResponse(HttpServletResponse theHttpResponse) {
|
||||
theHttpResponse.addHeader("X-PoweredBy", "HAPI FHIR " + VersionUtil.getVersion() + " RESTful Server");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link FhirContext} associated with this server. For efficient processing,
|
||||
* resource providers and plain providers should generally use this context
|
||||
* if one is needed, as opposed to creating their own.
|
||||
* Gets the {@link FhirContext} associated with this server. For efficient
|
||||
* processing, resource providers and plain providers should generally use
|
||||
* this context if one is needed, as opposed to creating their own.
|
||||
*/
|
||||
public FhirContext getFhirContext() {
|
||||
return myFhirContext;
|
||||
|
@ -177,10 +176,10 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
/**
|
||||
* Initializes the server. Note that this method is final to avoid accidentally
|
||||
* introducing bugs in implementations, but subclasses may put initialization code in
|
||||
* {@link #initialize()}, which is called immediately before beginning initialization of
|
||||
* the restful server's internal init.
|
||||
* Initializes the server. Note that this method is final to avoid
|
||||
* accidentally introducing bugs in implementations, but subclasses may put
|
||||
* initialization code in {@link #initialize()}, which is called immediately
|
||||
* before beginning initialization of the restful server's internal init.
|
||||
*/
|
||||
@Override
|
||||
public final void init() throws ServletException {
|
||||
|
@ -190,7 +189,7 @@ public class RestfulServer extends HttpServlet {
|
|||
|
||||
mySecurityManager = getSecurityManager();
|
||||
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();
|
||||
|
@ -233,19 +232,13 @@ public class RestfulServer extends HttpServlet {
|
|||
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() {
|
||||
return myUseBrowserFriendlyContentTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the non-resource specific providers which implement method calls on
|
||||
* this server.
|
||||
* this server.
|
||||
*
|
||||
* @see #setResourceProviders(Collection)
|
||||
*/
|
||||
|
@ -253,6 +246,16 @@ public class RestfulServer extends HttpServlet {
|
|||
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
|
||||
* this server
|
||||
|
@ -336,10 +339,16 @@ public class RestfulServer extends HttpServlet {
|
|||
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 {
|
||||
|
||||
ourLog.info("Scanning type for RESTful methods: {}", theProvider.getClass());
|
||||
|
||||
|
||||
Class<?> clazz = theProvider.getClass();
|
||||
Class<?> supertype = clazz.getSuperclass();
|
||||
if (!Object.class.equals(supertype)) {
|
||||
|
@ -373,7 +382,7 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
|
||||
resourceBinding.addMethod(foundMethodBinding);
|
||||
ourLog.info(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
|
||||
ourLog.debug(" * Method: {}#{} is a handler", theProvider.getClass(), m.getName());
|
||||
} else {
|
||||
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) {
|
||||
Class<?> clazz = theSystemProvider.getClass();
|
||||
|
||||
|
||||
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
|
||||
// serializing responses from this server, or <code>null</code> (which is
|
||||
|
@ -433,6 +437,11 @@ public class RestfulServer extends HttpServlet {
|
|||
// myNarrativeGenerator = theNarrativeGenerator;
|
||||
// }
|
||||
|
||||
@Override
|
||||
protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.DELETE, request, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
|
||||
handleRequest(SearchMethodBinding.RequestType.GET, request, response);
|
||||
|
@ -473,10 +482,12 @@ public class RestfulServer extends HttpServlet {
|
|||
servletContextPath = servletPath;
|
||||
}
|
||||
|
||||
ourLog.info("Request FullPath: {}", requestFullPath);
|
||||
ourLog.info("Servlet Path: {}", servletPath);
|
||||
ourLog.info("Request Url: {}", requestUrl);
|
||||
ourLog.info("Context Path: {}", servletContextPath);
|
||||
if (ourLog.isTraceEnabled()) {
|
||||
ourLog.trace("Request FullPath: {}", requestFullPath);
|
||||
ourLog.trace("Servlet Path: {}", servletPath);
|
||||
ourLog.trace("Request Url: {}", requestUrl);
|
||||
ourLog.trace("Context Path: {}", servletContextPath);
|
||||
}
|
||||
|
||||
servletPath = servletContextPath;
|
||||
|
||||
|
@ -539,7 +550,12 @@ public class RestfulServer extends HttpServlet {
|
|||
|
||||
if (tok.hasMoreTokens()) {
|
||||
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) {
|
||||
throw new InvalidRequestException("URL Path contains two operations (part beginning with _): " + requestPath);
|
||||
}
|
||||
|
@ -547,14 +563,23 @@ public class RestfulServer extends HttpServlet {
|
|||
}
|
||||
}
|
||||
|
||||
if (tok.hasMoreTokens()) {
|
||||
String nextString = tok.nextToken();
|
||||
versionId = new IdDt(nextString);
|
||||
}
|
||||
// Secondary is for things like ..../_tags/_delete
|
||||
String secondaryOperation=null;
|
||||
|
||||
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");
|
||||
if (contentLocation!=null) {
|
||||
if (contentLocation != null) {
|
||||
int idx = contentLocation.indexOf("/_history/");
|
||||
if (idx != -1) {
|
||||
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...
|
||||
|
||||
|
@ -571,6 +595,7 @@ public class RestfulServer extends HttpServlet {
|
|||
r.setId(id);
|
||||
r.setVersion(versionId);
|
||||
r.setOperation(operation);
|
||||
r.setSecondaryOperation(secondaryOperation);
|
||||
r.setParameters(params);
|
||||
r.setRequestType(theRequestType);
|
||||
if ("application/x-www-form-urlencoded".equals(theRequest.getContentType())) {
|
||||
|
|
|
@ -26,6 +26,12 @@ import ca.uhn.fhir.model.dstu.resource.OperationOutcome;
|
|||
* #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 {
|
||||
|
||||
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 String myResponseBody;
|
||||
private String myResponseMimeType;
|
||||
|
||||
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() {
|
||||
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
|
||||
*/
|
||||
|
@ -118,6 +151,22 @@ public abstract class BaseServerResponseException extends RuntimeException {
|
|||
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) {
|
||||
if (ourStatusCodeToExceptionType.containsKey(theStatusCode)) {
|
||||
try {
|
||||
|
@ -146,4 +195,5 @@ public abstract class BaseServerResponseException extends RuntimeException {
|
|||
}
|
||||
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 {
|
||||
|
||||
|
|
|
@ -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
|
||||
* per the specifications), as opposed to the {@link InvalidRequestException} which indicates
|
||||
* 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
|
||||
*/
|
||||
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 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) {
|
||||
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];
|
||||
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
||||
ParameterizedType pt = ((ParameterizedType) firstArg);
|
||||
|
@ -60,7 +64,11 @@ public class ReflectionUtil {
|
|||
@SuppressWarnings({ "unused", "rawtypes" })
|
||||
public static Class<?> getGenericCollectionTypeOfMethodReturnType(Method theMethod) {
|
||||
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];
|
||||
if (ParameterizedType.class.isAssignableFrom(firstArg.getClass())) {
|
||||
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.IRestfulClientFactory;
|
||||
import ca.uhn.fhir.rest.client.api.IBasicClient;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
||||
public class ClientExamples {
|
||||
|
||||
public interface PatientClient extends IBasicClient {
|
||||
public interface IPatientClient extends IBasicClient {
|
||||
// nothing yet
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void createSecurity() {
|
||||
{
|
||||
//START SNIPPET: security
|
||||
// Create a context and get the client factory so it can be configured
|
||||
FhirContext ctx = new FhirContext();
|
||||
|
@ -32,9 +34,25 @@ builder.addInterceptorFirst(new HttpBasicAuthInterceptor(username, password));
|
|||
clientFactory.setHttpClient(builder.build());
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
||||
/******************************/
|
||||
{
|
||||
//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 {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
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.PathSpecification;
|
||||
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.annotation.TagListParam;
|
||||
import ca.uhn.fhir.model.dstu.composite.CodingDt;
|
||||
import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
|
||||
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.Organization;
|
||||
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.QuantityCompararatorEnum;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
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.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.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.IncludeParam;
|
||||
|
@ -508,8 +515,146 @@ System.out.println(ctx.newXmlParser().encodeResourceToString(metadata));
|
|||
//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 name="Configuring the HTTP Client">
|
||||
<section name="Configuring the Client">
|
||||
|
||||
<p>
|
||||
The client uses <a href="http://hc.apache.org/httpcomponents-client-ga/">Apache HTTP Client</a>
|
||||
|
@ -155,6 +155,28 @@
|
|||
|
||||
</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>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -14,15 +14,23 @@
|
|||
Jump To...
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="#operations">Operations</a></li>
|
||||
<li><a href="#exceptions">Exceptions</a></li>
|
||||
<li>
|
||||
<a href="#operations">Operations</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#exceptions">Exceptions</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#tags">Tags</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
RESTful Clients and Servers both share the same
|
||||
method pattern, with one key difference: A client
|
||||
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
|
||||
implementations
|
||||
to actually provide those resources.
|
||||
|
@ -34,9 +42,9 @@
|
|||
implementations, but client methods will follow the same patterns.
|
||||
</p>
|
||||
|
||||
<a name="operations"/>
|
||||
<a name="operations" />
|
||||
</section>
|
||||
|
||||
|
||||
<section name="Operations">
|
||||
<p>
|
||||
The following table lists the operations supported by
|
||||
|
@ -93,7 +101,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="#type_search">Type - Create</a>
|
||||
<a href="#type_create">Type - Create</a>
|
||||
</td>
|
||||
<td>
|
||||
Create a new resource with a server assigned id
|
||||
|
@ -101,7 +109,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="#instance_update">Type - Search</a>
|
||||
<a href="#type_search">Type - Search</a>
|
||||
<macro name="toc">
|
||||
<param name="section" value="8" />
|
||||
<param name="fromDepth" value="2" />
|
||||
|
@ -159,6 +167,14 @@
|
|||
Search across all resource types based on some filter criteria
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="#tags">Tag Operations</a>
|
||||
</td>
|
||||
<td>
|
||||
Search across all resource types based on some filter criteria
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -195,6 +211,16 @@
|
|||
<code>http://fhir.example.com/Patient/111</code>
|
||||
</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" />
|
||||
</section>
|
||||
|
||||
|
@ -210,7 +236,7 @@
|
|||
<b>vread</b>
|
||||
</a>
|
||||
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
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/annotation/VersionIdParam.html">@VersionIdParam</a>
|
||||
annotation.
|
||||
|
@ -226,6 +252,13 @@
|
|||
<br />
|
||||
<code>http://fhir.example.com/Patient/111/_history/2</code>
|
||||
</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" />
|
||||
</section>
|
||||
|
@ -465,7 +498,8 @@
|
|||
<p>
|
||||
This annotation takes a "name" parameter which specifies the parameter's
|
||||
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
|
||||
classes.
|
||||
</p>
|
||||
|
@ -490,10 +524,10 @@
|
|||
interface.
|
||||
</p>
|
||||
<p>
|
||||
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)
|
||||
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
|
||||
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)
|
||||
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
|
||||
identifier (e.g. search for an MRN).
|
||||
</p>
|
||||
|
||||
|
@ -509,7 +543,7 @@
|
|||
</p>
|
||||
|
||||
</subsection>
|
||||
|
||||
|
||||
<subsection name="Search Parameters: Date (Simple)">
|
||||
|
||||
<p>
|
||||
|
@ -597,32 +631,41 @@
|
|||
<param name="id" value="dateClient" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
|
||||
<h4>Unbounded Ranges</h4>
|
||||
|
||||
|
||||
<p>
|
||||
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
|
||||
only a start date and no end date, or vice versa.
|
||||
only a start date and no end
|
||||
date, or vice versa.
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
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.
|
||||
<br/>
|
||||
<code>http://fhir.example.com/Observation?subject.identifier=7000135&date=>=2011-01-01</code><br/>
|
||||
<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),
|
||||
the <code>getLowerBound()</code> or <code>getUpperBound()</code> property of the
|
||||
<code>DateRangeParam</code> object will be set to <code>null</code>.
|
||||
the
|
||||
<code>getLowerBound()</code>
|
||||
or
|
||||
<code>getUpperBound()</code>
|
||||
property of the
|
||||
<code>DateRangeParam</code>
|
||||
object will be set to
|
||||
<code>null</code>
|
||||
.
|
||||
</p>
|
||||
|
||||
</subsection>
|
||||
|
||||
|
||||
<subsection name="Combining Multiple Parameters">
|
||||
|
||||
<p>
|
||||
Search methods may take multiple parameters, and these parameters
|
||||
may (or may not) be optional.
|
||||
may (or may not) be optional.
|
||||
aaaa
|
||||
To add a second required parameter, annotate the
|
||||
parameter with
|
||||
|
@ -761,7 +804,7 @@
|
|||
which is more convenient if only a single include (or null for none)
|
||||
is all that is required.
|
||||
</p>
|
||||
|
||||
|
||||
<macro name="snippet">
|
||||
<param name="id" value="pathSpecSimple" />
|
||||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
|
@ -847,7 +890,8 @@
|
|||
</li>
|
||||
<li>
|
||||
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
|
||||
will be returned to the client if it exists.
|
||||
</li>
|
||||
|
@ -867,7 +911,7 @@
|
|||
<br />
|
||||
<code>http://fhir.example.com/Patient/_validate</code>
|
||||
</p>
|
||||
|
||||
|
||||
<a name="system_conformance" />
|
||||
</section>
|
||||
|
||||
|
@ -987,38 +1031,54 @@
|
|||
annotated with the
|
||||
<a href="./apidocs/ca/uhn/fhir/rest/annotation/IdParam.html">@IdParam</a>
|
||||
annotation, indicating the ID of the resource for which to return history.
|
||||
<ul><li>
|
||||
For a server
|
||||
implementation, the method must either be defined in a
|
||||
<a href="./doc_rest_server.html#resource_providers">resource provider</a>
|
||||
or have a <code>type()</code> value in the @History annotation if it is
|
||||
defined in a
|
||||
<a href="./doc_rest_server.html#plain_providers">plain provider</a>.
|
||||
</li></ul>
|
||||
<ul>
|
||||
<li>
|
||||
For a server
|
||||
implementation, the method must either be defined in a
|
||||
<a href="./doc_rest_server.html#resource_providers">resource provider</a>
|
||||
or have a
|
||||
<code>type()</code>
|
||||
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>
|
||||
For a
|
||||
<b>Type History</b>
|
||||
method, the method must not have any @IdParam parameter.
|
||||
<ul><li>
|
||||
For a server
|
||||
implementation, the method must either be defined in a
|
||||
<a href="./doc_rest_server.html#resource_providers">resource provider</a>
|
||||
or have a <code>type()</code> value in the @History annotation if it is
|
||||
defined in a
|
||||
<a href="./doc_rest_server.html#plain_providers">plain provider</a>.
|
||||
</li></ul>
|
||||
<ul>
|
||||
<li>
|
||||
For a server
|
||||
implementation, the method must either be defined in a
|
||||
<a href="./doc_rest_server.html#resource_providers">resource provider</a>
|
||||
or have a
|
||||
<code>type()</code>
|
||||
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>
|
||||
For a
|
||||
<b>Server History</b>
|
||||
method, the method must not have any @IdParam parameter, and
|
||||
must not have a <code>type()</code> value specified in
|
||||
the @History annotation.
|
||||
<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>
|
||||
must not have a
|
||||
<code>type()</code>
|
||||
value specified in
|
||||
the @History annotation.
|
||||
<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>
|
||||
</ul>
|
||||
<p>
|
||||
|
@ -1039,34 +1099,155 @@
|
|||
<param name="file" value="src/site/example/java/example/RestfulPatientResourceProviderMore.java" />
|
||||
</macro>
|
||||
|
||||
<a name="exceptions"/>
|
||||
<a name="exceptions" />
|
||||
</section>
|
||||
|
||||
<!-- ****************************************************************** -->
|
||||
<!-- ****************************************************************** -->
|
||||
<!-- ****************************************************************** -->
|
||||
|
||||
<section name="Exceptions">
|
||||
|
||||
|
||||
<p>
|
||||
When implementing a server operation, there are a number of failure conditions
|
||||
specified. For example, an
|
||||
<a href="#instance_read">Instance Read</a> request might specify an unknown
|
||||
resource ID, or a <a href="#type_create">Type Create</a> request might contain an
|
||||
specified. For example, an
|
||||
<a href="#instance_read">Instance Read</a>
|
||||
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.
|
||||
</p>
|
||||
<p>
|
||||
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>
|
||||
which represent specific HTTP failure codes.
|
||||
</p>
|
||||
<p>
|
||||
See the
|
||||
See the
|
||||
<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
|
||||
signature.
|
||||
</p>
|
||||
|
||||
|
||||
<a name="tags" />
|
||||
</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>
|
||||
|
||||
</document>
|
||||
|
|
|
@ -100,7 +100,7 @@ public class ResourceWithExtensionsA extends BaseResource {
|
|||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return false; // TODO: implement
|
||||
return false; // not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -110,7 +110,7 @@ public class ResourceWithExtensionsA extends BaseResource {
|
|||
|
||||
@Override
|
||||
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
|
||||
public boolean isEmpty() {
|
||||
return false; // TODO: implement
|
||||
return false; // not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -169,7 +169,7 @@ public class ResourceWithExtensionsA extends BaseResource {
|
|||
|
||||
@Override
|
||||
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
|
||||
public boolean isEmpty() {
|
||||
return false; // TODO: implement
|
||||
return false; // not implemented
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -214,7 +214,7 @@ public class ResourceWithExtensionsA extends BaseResource {
|
|||
|
||||
@Override
|
||||
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 java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Date;
|
||||
|
||||
|
@ -77,7 +76,7 @@ public class DefaultThymeleafNarrativeGeneratorTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testGenerateDiagnosticReportWithObservations() throws DataFormatException, IOException {
|
||||
public void testGenerateDiagnosticReportWithObservations() throws DataFormatException {
|
||||
DiagnosticReport value = new DiagnosticReport();
|
||||
|
||||
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.BundleEntry;
|
||||
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.Extension;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
|
@ -50,19 +51,73 @@ import ca.uhn.fhir.narrative.INarrativeGenerator;
|
|||
public class JsonParserTest {
|
||||
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
|
||||
public void testParseFuroreMetadataWithExtraElements() throws IOException {
|
||||
String msg = IOUtils.toString(JsonParserTest.class.getResourceAsStream("/furore-conformance.json"));
|
||||
public void testTagList() {
|
||||
|
||||
//@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
|
||||
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
|
||||
public void testEncodeContainedResources() throws IOException {
|
||||
|
||||
|
@ -462,69 +155,10 @@ public class JsonParserTest {
|
|||
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 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 {
|
||||
public void testEncodeContainedResourcesMore() {
|
||||
|
||||
DiagnosticReport rpt = new DiagnosticReport();
|
||||
Specimen spm = new Specimen();
|
||||
|
@ -548,7 +182,85 @@ public class JsonParserTest {
|
|||
}
|
||||
|
||||
@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();
|
||||
obs.setValue(new DecimalDt(112.22));
|
||||
|
||||
|
@ -560,6 +272,47 @@ public class JsonParserTest {
|
|||
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
|
||||
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.ExtensionDt;
|
||||
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.HumanNameDt;
|
||||
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.primitive.DateTimeDt;
|
||||
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.XhtmlDt;
|
||||
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
|
||||
public void testTotalResultsUsingOldNamespace() {
|
||||
|
||||
|
@ -541,7 +585,8 @@ public class XmlParserTest {
|
|||
" <name>HL7, Inc (FHIR Project)</name>\n" +
|
||||
" <uri>http://hl7.org/fhir</uri>\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" +
|
||||
" <ValueSet xmlns=\"http://hl7.org/fhir\">\n" +
|
||||
" <text>\n" +
|
||||
|
@ -589,10 +634,29 @@ public class XmlParserTest {
|
|||
BundleEntry entry = bundle.getEntries().get(0);
|
||||
assertEquals("HL7, Inc (FHIR Project)", entry.getAuthorName().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();
|
||||
assertEquals("LOINC Codes for Cholesterol", resource.getName().getValue());
|
||||
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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.rest.client;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
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.IntegerDt;
|
||||
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.IncludeParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
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.api.MethodOutcome;
|
||||
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.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceVersionConflictException;
|
||||
|
||||
public class ClientTest {
|
||||
|
@ -107,11 +106,29 @@ public class ClientTest {
|
|||
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
|
||||
* of an OperationOutcome. We just need to ignore it.
|
||||
* Some servers (older ones?) return the resourcde you created instead of an
|
||||
* OperationOutcome. We just need to ignore it.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateWithResourceResponse() throws Exception {
|
||||
|
@ -135,14 +152,7 @@ public class ClientTest {
|
|||
assertEquals("100", response.getId().getValue());
|
||||
assertEquals("200", response.getVersionId().getValue());
|
||||
}
|
||||
|
||||
|
||||
public interface CreateWithTagList extends IBasicClient
|
||||
{
|
||||
@Create
|
||||
public MethodOutcome createPatient(@ResourceParam Patient thePatient, TagList theTagList);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
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.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.add(new Tag("Dog", "DogLabel", (String)null));
|
||||
tagList.add(new Tag("Dog", "DogLabel", (String) null));
|
||||
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());
|
||||
HttpPost post = (HttpPost) capt.getValue();
|
||||
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
||||
assertEquals("100", response.getId().getValue());
|
||||
assertEquals("200", response.getVersionId().getValue());
|
||||
|
||||
|
||||
Header[] headers = post.getHeaders("Category");
|
||||
assertEquals(2, headers.length);
|
||||
assertEquals("Dog; label=\"DogLabel\"", headers[0].getValue());
|
||||
assertEquals("Cat; label=\"CatLabel\"; scheme=\"http://cats\"", headers[1].getValue());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateBad() throws Exception {
|
||||
assertEquals("Dog; label=\"DogLabel\"", headers[0].getValue());
|
||||
assertEquals("Cat; label=\"CatLabel\"; scheme=\"http://cats\"", headers[1].getValue());
|
||||
|
||||
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
|
||||
|
@ -398,7 +389,6 @@ public class ClientTest {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testHistoryServer() throws Exception {
|
||||
InstantDt date1 = new InstantDt(new Date(20000L));
|
||||
|
@ -470,7 +460,7 @@ public class ClientTest {
|
|||
assertEquals(updExpected.getValueAsString(), updActualBundle.getValueAsString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testHistoryWithParams() throws Exception {
|
||||
|
||||
|
@ -488,17 +478,19 @@ public class ClientTest {
|
|||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||
|
||||
|
||||
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());
|
||||
|
||||
String expectedDateString= new InstantDt(new InstantDt("2012-01-02T00:01:02").getValue()).getValueAsString(); // ensures the local timezone
|
||||
expectedDateString=expectedDateString.replace(":", "%3A");
|
||||
String expectedDateString = new InstantDt(new InstantDt("2012-01-02T00:01:02").getValue()).getValueAsString(); // ensures
|
||||
// the
|
||||
// local
|
||||
// timezone
|
||||
expectedDateString = expectedDateString.replace(":", "%3A");
|
||||
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));
|
||||
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
|
||||
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")));
|
||||
|
||||
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"));
|
||||
|
||||
assertEquals("http://foo/Patient/111", capt.getValue().getURI().toString());
|
||||
|
@ -547,10 +556,35 @@ public class ClientTest {
|
|||
|
||||
InstantDt lm = (InstantDt) response.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
|
||||
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
|
||||
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")));
|
||||
|
||||
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"));
|
||||
|
||||
assertEquals("http://foo/Patient/111", capt.getValue().getURI().toString());
|
||||
|
@ -584,52 +619,9 @@ public class ClientTest {
|
|||
|
||||
InstantDt lm = (InstantDt) response.getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED);
|
||||
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
|
||||
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
|
||||
public void testSearchByToken() throws Exception {
|
||||
|
||||
|
@ -718,7 +681,6 @@ public class ClientTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
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());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchNamedQueryNoParams() throws Exception {
|
||||
|
||||
|
@ -775,7 +737,7 @@ public class ClientTest {
|
|||
assertEquals("http://foo/Patient?_query=someQueryOneParam¶m1=BB", capt.getValue().getURI().toString());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchWithCustomType() throws Exception {
|
||||
|
||||
|
@ -813,8 +775,37 @@ public class ClientTest {
|
|||
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
|
||||
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
|
||||
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")));
|
||||
|
||||
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"));
|
||||
|
||||
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) };
|
||||
}
|
||||
|
||||
@ResourceDef(name="Patient")
|
||||
public static class CustomPatient extends Patient
|
||||
{
|
||||
@ResourceDef(name = "Patient")
|
||||
public static class CustomPatient extends Patient {
|
||||
// nothing
|
||||
}
|
||||
|
||||
public interface ITestClientWithCustomType extends IBasicClient {
|
||||
@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 {
|
||||
@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 {
|
||||
@Search()
|
||||
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) {
|
||||
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.List;
|
||||
|
||||
import org.hamcrest.core.StringContains;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
@ -59,6 +58,7 @@ public class DocumentationTest {
|
|||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static class SearchProvider {
|
||||
|
||||
@Search(type = Patient.class)
|
||||
|
|
|
@ -921,11 +921,8 @@ public class ResfulServerMethodTest {
|
|||
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")));
|
||||
|
||||
HttpResponse status = ourClient.execute(httpPost);
|
||||
|
||||
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||
assertEquals("http://localhost:" + ourPort + "/DiagnosticReport/001/_history/002", status.getFirstHeader("Location").getValue());
|
||||
|
||||
ourClient.execute(httpPost);
|
||||
fail();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1199,12 +1196,11 @@ public class ResfulServerMethodTest {
|
|||
return DiagnosticReport.class;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@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 version = theVersionId;
|
||||
myLastTags = theTagList;
|
||||
myLastTags = (TagList) theDr.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||
return new MethodOutcome(id, version);
|
||||
}
|
||||
|
||||
|
@ -1212,12 +1208,11 @@ public class ResfulServerMethodTest {
|
|||
return myLastTags;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@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 version = new IdDt("002");
|
||||
myLastTags=theTagList;
|
||||
myLastTags = (TagList) theDr.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||
return new MethodOutcome(id, version);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ public class ResourceMethodTest {
|
|||
inputParams.add("firstName");
|
||||
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
|
||||
|
@ -61,7 +61,7 @@ public class ResourceMethodTest {
|
|||
|
||||
Set<String> inputParams = new HashSet<String>();
|
||||
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
|
||||
|
@ -78,7 +78,7 @@ public class ResourceMethodTest {
|
|||
inputParams.add("firstName");
|
||||
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
|
||||
|
@ -96,7 +96,7 @@ public class ResourceMethodTest {
|
|||
inputParams.add("lastName");
|
||||
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
|
||||
|
@ -115,6 +115,6 @@ public class ResourceMethodTest {
|
|||
inputParams.add("mrn");
|
||||
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();
|
||||
List<Object> values = terser.getValues(resource, path);
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
<persistence-unit name="FHIR_UT" transaction-type="RESOURCE_LOCAL">
|
||||
<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>
|
||||
<properties>
|
||||
<property name="hibernate.connection.url" value="jdbc:hsqldb:mem:unit-testing-jpa" />
|
||||
|
|
|
@ -60,13 +60,13 @@ public class FhirResourceDaoTest {
|
|||
|
||||
|
||||
@Test
|
||||
public void testCreateWithTags() {
|
||||
public void testTagsWithCreateAndReadAndSearch() {
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier("urn:system", "001");
|
||||
patient.addIdentifier("urn:system", "testTagsWithCreateAndReadAndSearch");
|
||||
patient.addName().addFamily("Tester").addGiven("Joe");
|
||||
TagList tagList= new TagList();
|
||||
tagList.addTag("Dog", "Puppies", null);
|
||||
tagList.addTag("Cat", "Kittens", null);
|
||||
tagList.addTag("Cat", "Kittens", "http://foo");
|
||||
patient.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
|
||||
|
||||
MethodOutcome outcome = ourPatientDao.create(patient);
|
||||
|
@ -77,10 +77,27 @@ public class FhirResourceDaoTest {
|
|||
TagList published = (TagList) retrieved.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||
assertEquals(2, published.size());
|
||||
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());
|
||||
|
||||
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
|
||||
public void testSearchAll() {
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue