Fix #65 - Correctly parse and encode extensions on non-repeatable
primitive fields
This commit is contained in:
parent
81f9e492f3
commit
81851f4808
|
@ -22,9 +22,12 @@ package ca.uhn.fhir.model.api;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.IBaseResource;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
|
||||||
public interface IFhirVersion {
|
public interface IFhirVersion {
|
||||||
|
@ -37,7 +40,7 @@ public interface IFhirVersion {
|
||||||
|
|
||||||
IResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase);
|
IResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase);
|
||||||
|
|
||||||
Object createServerConformanceProvider(RestfulServer theRestfulServer);
|
IServerConformanceProvider<? extends IBaseResource> createServerConformanceProvider(RestfulServer theRestfulServer);
|
||||||
|
|
||||||
String getPathToSchemaDefinitions();
|
String getPathToSchemaDefinitions();
|
||||||
|
|
||||||
|
|
|
@ -288,20 +288,20 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
writeTagWithTextNode(theEventWriter, "instant", nextEntry.getDeletedAt());
|
writeTagWithTextNode(theEventWriter, "instant", nextEntry.getDeletedAt());
|
||||||
}
|
}
|
||||||
|
|
||||||
// linkStarted = false;
|
// linkStarted = false;
|
||||||
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
|
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
|
||||||
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
|
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
|
||||||
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "search", nextEntry.getLinkSearch(), linkStarted);
|
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "search", nextEntry.getLinkSearch(), linkStarted);
|
||||||
// if (linkStarted) {
|
// if (linkStarted) {
|
||||||
// theEventWriter.writeEnd();
|
// theEventWriter.writeEnd();
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// writeOptionalTagWithTextNode(theEventWriter, "updated", nextEntry.getUpdated());
|
// writeOptionalTagWithTextNode(theEventWriter, "updated", nextEntry.getUpdated());
|
||||||
// writeOptionalTagWithTextNode(theEventWriter, "published", nextEntry.getPublished());
|
// writeOptionalTagWithTextNode(theEventWriter, "published", nextEntry.getPublished());
|
||||||
//
|
//
|
||||||
// writeCategories(theEventWriter, nextEntry.getCategories());
|
// writeCategories(theEventWriter, nextEntry.getCategories());
|
||||||
//
|
//
|
||||||
// writeAuthor(nextEntry, theEventWriter);
|
// writeAuthor(nextEntry, theEventWriter);
|
||||||
|
|
||||||
IResource resource = nextEntry.getResource();
|
IResource resource = nextEntry.getResource();
|
||||||
if (resource != null && !resource.isEmpty() && !deleted) {
|
if (resource != null && !resource.isEmpty() && !deleted) {
|
||||||
|
@ -532,7 +532,12 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (extensions.size() > 0 || modifierExtensions.size() > 0) {
|
if (extensions.size() > 0 || modifierExtensions.size() > 0) {
|
||||||
|
if (inArray) {
|
||||||
|
// If this is a repeatable field, the extensions go in an array too
|
||||||
theEventWriter.writeStartArray('_' + currentChildName);
|
theEventWriter.writeStartArray('_' + currentChildName);
|
||||||
|
} else {
|
||||||
|
theEventWriter.writeStartObject('_' + currentChildName);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < valueIdx; i++) {
|
for (int i = 0; i < valueIdx; i++) {
|
||||||
boolean haveContent = false;
|
boolean haveContent = false;
|
||||||
|
@ -552,11 +557,15 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
if (!haveContent) {
|
if (!haveContent) {
|
||||||
theEventWriter.writeNull();
|
theEventWriter.writeNull();
|
||||||
} else {
|
} else {
|
||||||
|
if (inArray) {
|
||||||
theEventWriter.writeStartObject();
|
theEventWriter.writeStartObject();
|
||||||
|
}
|
||||||
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, heldExts, heldModExts, null);
|
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, heldExts, heldModExts, null);
|
||||||
|
if (inArray) {
|
||||||
theEventWriter.writeEnd();
|
theEventWriter.writeEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
theEventWriter.writeEnd();
|
theEventWriter.writeEnd();
|
||||||
}
|
}
|
||||||
|
@ -735,11 +744,23 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseAlternates(JsonValue theAlternateVal, ParserState<?> theState) {
|
private void parseAlternates(JsonValue theAlternateVal, ParserState<?> theState, String theElementName) {
|
||||||
if (theAlternateVal == null || theAlternateVal.getValueType() == ValueType.NULL) {
|
if (theAlternateVal == null || theAlternateVal.getValueType() == ValueType.NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (theAlternateVal instanceof JsonArray) {
|
||||||
|
JsonArray array = (JsonArray) theAlternateVal;
|
||||||
|
if (array.size() > 1) {
|
||||||
|
throw new DataFormatException("Unexpected array of length " + array.size() + " (expected 0 or 1) for element: " + theElementName);
|
||||||
|
}
|
||||||
|
if (array.size() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parseAlternates(array.getJsonObject(0), theState, theElementName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
boolean newerThanDstu1 = myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1);
|
boolean newerThanDstu1 = myContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU1);
|
||||||
JsonObject alternate = (JsonObject) theAlternateVal;
|
JsonObject alternate = (JsonObject) theAlternateVal;
|
||||||
for (Entry<String, JsonValue> nextEntry : alternate.entrySet()) {
|
for (Entry<String, JsonValue> nextEntry : alternate.entrySet()) {
|
||||||
|
@ -880,7 +901,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonValue nextVal = theObject.get(nextName);
|
JsonValue nextVal = theObject.get(nextName);
|
||||||
parseChildren(theState, nextName, nextVal, null);
|
parseChildren(theState, nextName, nextVal, null, null);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -926,9 +947,10 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonValue nextVal = theObject.get(nextName);
|
JsonValue nextVal = theObject.get(nextName);
|
||||||
JsonValue alternateVal = theObject.get('_' + nextName);
|
String alternateName = '_' + nextName;
|
||||||
|
JsonValue alternateVal = theObject.get(alternateName);
|
||||||
|
|
||||||
parseChildren(theState, nextName, nextVal, alternateVal);
|
parseChildren(theState, nextName, nextVal, alternateVal, alternateName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -942,7 +964,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseChildren(ParserState<?> theState, String theName, JsonValue theJsonVal, JsonValue theAlternateVal) {
|
private void parseChildren(ParserState<?> theState, String theName, JsonValue theJsonVal, JsonValue theAlternateVal, String theAlternateName) {
|
||||||
switch (theJsonVal.getValueType()) {
|
switch (theJsonVal.getValueType()) {
|
||||||
case ARRAY: {
|
case ARRAY: {
|
||||||
JsonArray nextArray = (JsonArray) theJsonVal;
|
JsonArray nextArray = (JsonArray) theJsonVal;
|
||||||
|
@ -953,13 +975,13 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
if (nextAlternateArray != null) {
|
if (nextAlternateArray != null) {
|
||||||
nextAlternate = nextAlternateArray.get(i);
|
nextAlternate = nextAlternateArray.get(i);
|
||||||
}
|
}
|
||||||
parseChildren(theState, theName, nextObject, nextAlternate);
|
parseChildren(theState, theName, nextObject, nextAlternate, theAlternateName);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case OBJECT: {
|
case OBJECT: {
|
||||||
theState.enteringNewElement(null, theName);
|
theState.enteringNewElement(null, theName);
|
||||||
parseAlternates(theAlternateVal, theState);
|
parseAlternates(theAlternateVal, theState, theAlternateName);
|
||||||
JsonObject nextObject = (JsonObject) theJsonVal;
|
JsonObject nextObject = (JsonObject) theJsonVal;
|
||||||
boolean preResource = false;
|
boolean preResource = false;
|
||||||
if (theState.isPreResource()) {
|
if (theState.isPreResource()) {
|
||||||
|
@ -981,7 +1003,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
JsonString nextValStr = (JsonString) theJsonVal;
|
JsonString nextValStr = (JsonString) theJsonVal;
|
||||||
theState.enteringNewElement(null, theName);
|
theState.enteringNewElement(null, theName);
|
||||||
theState.attributeValue("value", nextValStr.getString());
|
theState.attributeValue("value", nextValStr.getString());
|
||||||
parseAlternates(theAlternateVal, theState);
|
parseAlternates(theAlternateVal, theState, theAlternateName);
|
||||||
theState.endingElement();
|
theState.endingElement();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -990,7 +1012,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
case TRUE:
|
case TRUE:
|
||||||
theState.enteringNewElement(null, theName);
|
theState.enteringNewElement(null, theName);
|
||||||
theState.attributeValue("value", theJsonVal.toString());
|
theState.attributeValue("value", theJsonVal.toString());
|
||||||
parseAlternates(theAlternateVal, theState);
|
parseAlternates(theAlternateVal, theState, theAlternateName);
|
||||||
theState.endingElement();
|
theState.endingElement();
|
||||||
break;
|
break;
|
||||||
case NULL:
|
case NULL:
|
||||||
|
@ -1015,7 +1037,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
parseExtension(theState, jsonVal, true);
|
parseExtension(theState, jsonVal, true);
|
||||||
} else {
|
} else {
|
||||||
JsonValue jsonVal = nextExtObj.get(next);
|
JsonValue jsonVal = nextExtObj.get(next);
|
||||||
parseChildren(theState, next, jsonVal, null);
|
parseChildren(theState, next, jsonVal, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
theState.endingElement();
|
theState.endingElement();
|
||||||
|
@ -1039,7 +1061,7 @@ public class JsonParser extends BaseParser implements IParser {
|
||||||
JsonArray arrayValue = (JsonArray) jsonVal;
|
JsonArray arrayValue = (JsonArray) jsonVal;
|
||||||
parseExtensionInDstu2Style(theModifier, theState, extUrl, nextKey, arrayValue);
|
parseExtensionInDstu2Style(theModifier, theState, extUrl, nextKey, arrayValue);
|
||||||
} else {
|
} else {
|
||||||
parseChildren(theState, nextKey, jsonVal, null);
|
parseChildren(theState, nextKey, jsonVal, null, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -815,21 +815,36 @@ public class XmlParser extends BaseParser implements IParser {
|
||||||
theEventWriter.writeEndElement();
|
theEventWriter.writeEndElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IBaseResource theResource, XMLStreamWriter theWriter, List<ExtensionDt> extensions, String tagName,
|
private void encodeUndeclaredExtensions(RuntimeResourceDefinition theResDef, IBaseResource theResource, XMLStreamWriter theWriter, List<ExtensionDt> theExtensions, String tagName,
|
||||||
boolean theIncludedResource) throws XMLStreamException, DataFormatException {
|
boolean theIncludedResource) throws XMLStreamException, DataFormatException {
|
||||||
for (ExtensionDt next : extensions) {
|
for (ExtensionDt next : theExtensions) {
|
||||||
theWriter.writeStartElement(tagName);
|
theWriter.writeStartElement(tagName);
|
||||||
theWriter.writeAttribute("url", next.getUrl().getValue());
|
theWriter.writeAttribute("url", next.getUrl().getValue());
|
||||||
|
|
||||||
if (next.getValue() != null) {
|
if (next.getValue() != null) {
|
||||||
IElement nextValue = next.getValue();
|
IElement value = next.getValue();
|
||||||
|
// RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
|
||||||
|
// String childName = extDef.getChildNameByDatatype(nextValue.getClass());
|
||||||
|
// if (childName == null) {
|
||||||
|
// throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + nextValue.getClass().getCanonicalName());
|
||||||
|
// }
|
||||||
|
// BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(nextValue.getClass());
|
||||||
|
//
|
||||||
|
//
|
||||||
RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
|
RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
|
||||||
String childName = extDef.getChildNameByDatatype(nextValue.getClass());
|
String childName = extDef.getChildNameByDatatype(value.getClass());
|
||||||
|
BaseRuntimeElementDefinition<?> childDef;
|
||||||
if (childName == null) {
|
if (childName == null) {
|
||||||
throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + nextValue.getClass().getCanonicalName());
|
childDef = myContext.getElementDefinition(value.getClass());
|
||||||
|
if (childDef == null) {
|
||||||
|
throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName());
|
||||||
|
} else {
|
||||||
|
childName = RuntimeChildUndeclaredExtensionDefinition.createExtensionChildName(childDef);
|
||||||
}
|
}
|
||||||
BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(nextValue.getClass());
|
} else {
|
||||||
encodeChildElementToStreamWriter(theResDef, theResource, theWriter, nextValue, childName, childDef, null, theIncludedResource);
|
childDef = extDef.getChildElementDefinitionByDatatype(value.getClass());
|
||||||
|
}
|
||||||
|
encodeChildElementToStreamWriter(theResDef, theResource, theWriter, value, childName, childDef, null, theIncludedResource);
|
||||||
}
|
}
|
||||||
|
|
||||||
// child extensions
|
// child extensions
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.IBaseResource;
|
||||||
|
|
||||||
|
public interface IServerConformanceProvider<T extends IBaseResource> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Actually create and return the conformance statement
|
||||||
|
*
|
||||||
|
* See the class documentation for an important note if you are extending this class
|
||||||
|
*/
|
||||||
|
public abstract T getServerConformance(HttpServletRequest theRequest);
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import org.hamcrest.core.StringContains;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.dstu.composite.QuantityDt;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.dstu.valueset.QuantityCompararatorEnum;
|
||||||
|
|
||||||
|
public class MultiVersionXmlParserTest {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MultiVersionXmlParserTest.class);
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncodeExtensionFromDifferentVersion() {
|
||||||
|
Patient p = new Patient();
|
||||||
|
p.addIdentifier("urn:sys", "001");
|
||||||
|
p.addUndeclaredExtension(false, "http://foo#ext", new QuantityDt(QuantityCompararatorEnum.LESSTHAN, 2.2, "g/L"));
|
||||||
|
|
||||||
|
String str;
|
||||||
|
str = FhirContext.forDstu1().newXmlParser().encodeResourceToString(p);
|
||||||
|
ourLog.info(str);
|
||||||
|
assertThat(str,StringContains.containsString("<extension url=\"http://foo#ext\"><valueQuantity><value value=\"2.2\"/><comparator value=\"<\"/><units value=\"g/L\"/></valueQuantity></extension>"));
|
||||||
|
|
||||||
|
str = FhirContext.forDev().newXmlParser().encodeResourceToString(p);
|
||||||
|
ourLog.info(str);
|
||||||
|
assertThat(str,StringContains.containsString("<extension url=\"http://foo#ext\"><valueQuantity><value value=\"2.2\"/><comparator value=\"<\"/><units value=\"g/L\"/></valueQuantity></extension>"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -26,5 +26,15 @@
|
||||||
<attribute name="maven.pomderived" value="true"/>
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
</attributes>
|
</attributes>
|
||||||
</classpathentry>
|
</classpathentry>
|
||||||
|
<classpathentry kind="src" output="target/classes" path="target/generated-sources/tinder">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
|
<classpathentry excluding="**" kind="src" output="target/classes" path="target/generated-resources/tinder">
|
||||||
|
<attributes>
|
||||||
|
<attribute name="maven.pomderived" value="true"/>
|
||||||
|
</attributes>
|
||||||
|
</classpathentry>
|
||||||
<classpathentry kind="output" path="target/classes"/>
|
<classpathentry kind="output" path="target/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|
|
@ -2,5 +2,7 @@
|
||||||
<wb-module deploy-name="hapi-fhir-jpaserver-base">
|
<wb-module deploy-name="hapi-fhir-jpaserver-base">
|
||||||
<wb-resource deploy-path="/" source-path="/src/main/java"/>
|
<wb-resource deploy-path="/" source-path="/src/main/java"/>
|
||||||
<wb-resource deploy-path="/" source-path="/src/main/resources"/>
|
<wb-resource deploy-path="/" source-path="/src/main/resources"/>
|
||||||
|
<wb-resource deploy-path="/" source-path="/target/generated-sources/tinder"/>
|
||||||
|
<wb-resource deploy-path="/" source-path="/target/generated-resources/tinder"/>
|
||||||
</wb-module>
|
</wb-module>
|
||||||
</project-modules>
|
</project-modules>
|
||||||
|
|
|
@ -0,0 +1,81 @@
|
||||||
|
package ca.uhn.fhir.jpa.provider;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
|
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||||
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
|
import ca.uhn.fhir.model.dev.resource.Conformance;
|
||||||
|
import ca.uhn.fhir.model.dev.resource.Conformance.Rest;
|
||||||
|
import ca.uhn.fhir.model.dev.resource.Conformance.RestResource;
|
||||||
|
import ca.uhn.fhir.model.dev.resource.Conformance.RestResourceSearchParam;
|
||||||
|
import ca.uhn.fhir.model.dev.valueset.ResourceTypeEnum;
|
||||||
|
import ca.uhn.fhir.model.dev.valueset.SearchParamTypeEnum;
|
||||||
|
import ca.uhn.fhir.model.primitive.BoundCodeDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.DecimalDt;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
|
import ca.uhn.fhir.rest.server.provider.dev.ServerConformanceProvider;
|
||||||
|
import ca.uhn.fhir.util.ExtensionConstants;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
public class JpaConformanceProviderDev extends ServerConformanceProvider {
|
||||||
|
|
||||||
|
private String myImplementationDescription;
|
||||||
|
private IFhirSystemDao mySystemDao;
|
||||||
|
private volatile Conformance myCachedValue;
|
||||||
|
private RestfulServer myRestfulServer;
|
||||||
|
|
||||||
|
public JpaConformanceProviderDev(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
|
||||||
|
super(theRestfulServer);
|
||||||
|
myRestfulServer = theRestfulServer;
|
||||||
|
mySystemDao = theSystemDao;
|
||||||
|
super.setCache(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Conformance getServerConformance(HttpServletRequest theRequest) {
|
||||||
|
Conformance retVal = myCachedValue;
|
||||||
|
|
||||||
|
Map<String, Long> counts = mySystemDao.getResourceCounts();
|
||||||
|
|
||||||
|
FhirContext ctx = myRestfulServer.getFhirContext();
|
||||||
|
|
||||||
|
retVal = super.getServerConformance(theRequest);
|
||||||
|
for (Rest nextRest : retVal.getRest()) {
|
||||||
|
for (RestResource nextResource : nextRest.getResource()) {
|
||||||
|
|
||||||
|
// Add resource counts
|
||||||
|
Long count = counts.get(nextResource.getTypeElement().getValueAsString());
|
||||||
|
if (count != null) {
|
||||||
|
nextResource.addUndeclaredExtension(false, ExtensionConstants.CONF_RESOURCE_COUNT, new DecimalDt(count));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add chained params
|
||||||
|
for (RestResourceSearchParam nextParam : nextResource.getSearchParam()) {
|
||||||
|
if (nextParam.getTypeElement().getValueAsEnum() == SearchParamTypeEnum.REFERENCE) {
|
||||||
|
List<BoundCodeDt<ResourceTypeEnum>> targets = nextParam.getTarget();
|
||||||
|
for (BoundCodeDt<ResourceTypeEnum> next : targets) {
|
||||||
|
RuntimeResourceDefinition def = ctx.getResourceDefinition(next.getValue());
|
||||||
|
for (RuntimeSearchParam nextChainedParam : def.getSearchParams()) {
|
||||||
|
nextParam.addChain(nextChainedParam.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
retVal.getImplementation().setDescription(myImplementationDescription);
|
||||||
|
myCachedValue = retVal;
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImplementationDescription(String theImplDesc) {
|
||||||
|
myImplementationDescription = theImplDesc;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,14 +21,14 @@ import ca.uhn.fhir.util.ExtensionConstants;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
public class JpaConformanceProvider extends ServerConformanceProvider {
|
public class JpaConformanceProviderDstu1 extends ServerConformanceProvider {
|
||||||
|
|
||||||
private String myImplementationDescription;
|
private String myImplementationDescription;
|
||||||
private IFhirSystemDao mySystemDao;
|
private IFhirSystemDao mySystemDao;
|
||||||
private volatile Conformance myCachedValue;
|
private volatile Conformance myCachedValue;
|
||||||
private RestfulServer myRestfulServer;
|
private RestfulServer myRestfulServer;
|
||||||
|
|
||||||
public JpaConformanceProvider(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
|
public JpaConformanceProviderDstu1(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
|
||||||
super(theRestfulServer);
|
super(theRestfulServer);
|
||||||
myRestfulServer = theRestfulServer;
|
myRestfulServer = theRestfulServer;
|
||||||
mySystemDao = theSystemDao;
|
mySystemDao = theSystemDao;
|
|
@ -151,11 +151,18 @@
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!--
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-dbcp</groupId>
|
<groupId>commons-dbcp</groupId>
|
||||||
<artifactId>commons-dbcp</artifactId>
|
<artifactId>commons-dbcp</artifactId>
|
||||||
<version>1.4</version>
|
<version>1.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-dbcp2</artifactId>
|
||||||
|
<version>2.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Only required for CORS support -->
|
<!-- Only required for CORS support -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -10,7 +10,8 @@ import org.springframework.web.context.ContextLoaderListener;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProvider;
|
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDev;
|
||||||
|
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu1;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||||
|
@ -32,63 +33,56 @@ public class TestRestfulServer extends RestfulServer {
|
||||||
protected void initialize() throws ServletException {
|
protected void initialize() throws ServletException {
|
||||||
super.initialize();
|
super.initialize();
|
||||||
|
|
||||||
// try {
|
// Get the spring context from the web container (it's declared in web.xml)
|
||||||
// ourLog.info("Creating database");
|
|
||||||
// DriverManager.getConnection("jdbc:derby:directory:" + System.getProperty("fhir.db.location") + ";create=true");
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// ourLog.error("Failed to create database: {}",e);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// myAppCtx = new ClassPathXmlApplicationContext("fhir-spring-uhnfhirtest-config.xml", "hapi-jpaserver-springbeans.xml");
|
|
||||||
|
|
||||||
// myAppCtx = new FileSystemXmlApplicationContext(
|
|
||||||
// "WEB-INF/hapi-fhir-server-database-config.xml",
|
|
||||||
// "WEB-INF/hapi-fhir-server-config.xml"
|
|
||||||
// );
|
|
||||||
|
|
||||||
String fhirVersionParam = getInitParameter("FhirVersion");
|
|
||||||
if (StringUtils.isBlank(fhirVersionParam)) {
|
|
||||||
fhirVersionParam="DSTU1";
|
|
||||||
}
|
|
||||||
|
|
||||||
myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext();
|
myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext();
|
||||||
|
|
||||||
|
// These two parmeters are also declared in web.xml
|
||||||
|
String implDesc = getInitParameter("ImplementationDescription");
|
||||||
|
String fhirVersionParam = getInitParameter("FhirVersion");
|
||||||
|
if (StringUtils.isBlank(fhirVersionParam)) {
|
||||||
|
fhirVersionParam = "DSTU1";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depending on the version this server is supporing, we will
|
||||||
|
// retrieve all the appropriate resource providers and the
|
||||||
|
// conformance provider
|
||||||
List<IResourceProvider> beans;
|
List<IResourceProvider> beans;
|
||||||
JpaSystemProvider systemProvider;
|
JpaSystemProvider systemProvider;
|
||||||
IFhirSystemDao systemDao;
|
IFhirSystemDao systemDao;
|
||||||
switch (fhirVersionParam.trim().toUpperCase()) {
|
switch (fhirVersionParam.trim().toUpperCase()) {
|
||||||
case "DSTU":
|
case "DSTU":
|
||||||
case "DSTU1":
|
case "DSTU1": {
|
||||||
setFhirContext(FhirContext.forDstu1());
|
setFhirContext(FhirContext.forDstu1());
|
||||||
beans = myAppCtx.getBean("myResourceProvidersDstu1", List.class);
|
beans = myAppCtx.getBean("myResourceProvidersDstu1", List.class);
|
||||||
systemProvider = myAppCtx.getBean("mySystemProviderDstu1", JpaSystemProvider.class);
|
systemProvider = myAppCtx.getBean("mySystemProviderDstu1", JpaSystemProvider.class);
|
||||||
systemDao = myAppCtx.getBean("mySystemDaoDstu1", IFhirSystemDao.class);
|
systemDao = myAppCtx.getBean("mySystemDaoDstu1", IFhirSystemDao.class);
|
||||||
|
JpaConformanceProviderDstu1 confProvider = new JpaConformanceProviderDstu1(this, systemDao);
|
||||||
|
confProvider.setImplementationDescription(implDesc);
|
||||||
|
setServerConformanceProvider(confProvider);
|
||||||
break;
|
break;
|
||||||
case "DEV":
|
}
|
||||||
|
case "DEV": {
|
||||||
setFhirContext(FhirContext.forDev());
|
setFhirContext(FhirContext.forDev());
|
||||||
beans = myAppCtx.getBean("myResourceProvidersDev", List.class);
|
beans = myAppCtx.getBean("myResourceProvidersDev", List.class);
|
||||||
systemProvider = myAppCtx.getBean("mySystemProviderDev", JpaSystemProvider.class);
|
systemProvider = myAppCtx.getBean("mySystemProviderDev", JpaSystemProvider.class);
|
||||||
systemDao = myAppCtx.getBean("mySystemDaoDev", IFhirSystemDao.class);
|
systemDao = myAppCtx.getBean("mySystemDaoDev", IFhirSystemDao.class);
|
||||||
|
JpaConformanceProviderDev confProvider = new JpaConformanceProviderDev(this, systemDao);
|
||||||
|
confProvider.setImplementationDescription(implDesc);
|
||||||
|
setServerConformanceProvider(confProvider);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
throw new ServletException("Unknown FHIR version specified in init-param[FhirVersion]: " + fhirVersionParam);
|
throw new ServletException("Unknown FHIR version specified in init-param[FhirVersion]: " + fhirVersionParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
FhirContext ctx = getFhirContext();
|
|
||||||
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
|
||||||
|
|
||||||
for (IResourceProvider nextResourceProvider : beans) {
|
for (IResourceProvider nextResourceProvider : beans) {
|
||||||
ourLog.info(" * Have resource provider for: {}", nextResourceProvider.getResourceType().getSimpleName());
|
ourLog.info(" * Have resource provider for: {}", nextResourceProvider.getResourceType().getSimpleName());
|
||||||
}
|
}
|
||||||
setResourceProviders(beans);
|
setResourceProviders(beans);
|
||||||
setPlainProviders(systemProvider);
|
setPlainProviders(systemProvider);
|
||||||
|
|
||||||
String implDesc = getInitParameter("ImplementationDescription");
|
FhirContext ctx = getFhirContext();
|
||||||
|
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||||
JpaConformanceProvider confProvider = new JpaConformanceProvider(this, systemDao);
|
|
||||||
confProvider.setImplementationDescription(implDesc);
|
|
||||||
setServerConformanceProvider(confProvider);
|
|
||||||
|
|
||||||
setUseBrowserFriendlyContentTypes(true);
|
setUseBrowserFriendlyContentTypes(true);
|
||||||
|
|
||||||
|
@ -111,14 +105,14 @@ public class TestRestfulServer extends RestfulServer {
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
super.destroy();
|
super.destroy();
|
||||||
|
|
||||||
// myAppCtx.close();
|
// myAppCtx.close();
|
||||||
//
|
//
|
||||||
// try {
|
// try {
|
||||||
// ourLog.info("Shutting down derby");
|
// ourLog.info("Shutting down derby");
|
||||||
// DriverManager.getConnection("jdbc:derby:directory:" + System.getProperty("fhir.db.location") + ";shutdown=true");
|
// DriverManager.getConnection("jdbc:derby:directory:" + System.getProperty("fhir.db.location") + ";shutdown=true");
|
||||||
// } catch (Exception e) {
|
// } catch (Exception e) {
|
||||||
// ourLog.info("Failed to create database: {}",e.getMessage());
|
// ourLog.info("Failed to create database: {}",e.getMessage());
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,10 @@
|
||||||
-->
|
-->
|
||||||
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyTenSevenDialect" />
|
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyTenSevenDialect" />
|
||||||
<property name="hibernate.hbm2ddl.auto" value="update" />
|
<property name="hibernate.hbm2ddl.auto" value="update" />
|
||||||
|
<!--
|
||||||
<property name="hibernate.connection.username" value="sa" />
|
<property name="hibernate.connection.username" value="sa" />
|
||||||
<property name="hibernate.connection.password" value="" />
|
<property name="hibernate.connection.password" value="" />
|
||||||
|
-->
|
||||||
<property name="hibernate.jdbc.batch_size" value="0" />
|
<property name="hibernate.jdbc.batch_size" value="0" />
|
||||||
<property name="hibernate.cache.use_minimal_puts" value="false" />
|
<property name="hibernate.cache.use_minimal_puts" value="false" />
|
||||||
<property name="hibernate.show_sql" value="false" />
|
<property name="hibernate.show_sql" value="false" />
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<bean id="dbServer" class="ca.uhn.fhirtest.DerbyNetworkServer">
|
<bean id="dbServer" class="ca.uhn.fhirtest.DerbyNetworkServer">
|
||||||
</bean>
|
</bean>
|
||||||
|
|
||||||
<bean depends-on="dbServer" id="myPersistenceDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
|
<bean depends-on="dbServer" id="myPersistenceDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
|
||||||
<!-- ;create=true /opt/glassfish/glassfish4/glassfish/nodes/localhost-domain1/fhirtest/fhirdb -->
|
<!-- ;create=true /opt/glassfish/glassfish4/glassfish/nodes/localhost-domain1/fhirtest/fhirdb -->
|
||||||
<!-- <property name="url" value="jdbc:hsqldb:hsql://localhost/uhnfhirdb"/>-->
|
<!-- <property name="url" value="jdbc:hsqldb:hsql://localhost/uhnfhirdb"/>-->
|
||||||
<!-- <property name="url" value="jdbc:derby:directory:#{systemproperties['fhir.db.location']};create=true" /> -->
|
<!-- <property name="url" value="jdbc:derby:directory:#{systemproperties['fhir.db.location']};create=true" /> -->
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
/.settings/
|
/.settings/
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
|
/target/
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class FhirDev implements IFhirVersion {
|
||||||
private String myId;
|
private String myId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object createServerConformanceProvider(RestfulServer theServer) {
|
public ServerConformanceProvider createServerConformanceProvider(RestfulServer theServer) {
|
||||||
return new ServerConformanceProvider(theServer);
|
return new ServerConformanceProvider(theServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
|
@ -58,6 +60,7 @@ import ca.uhn.fhir.rest.method.IParameter;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.SearchParameter;
|
import ca.uhn.fhir.rest.method.SearchParameter;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||||
import ca.uhn.fhir.rest.server.ResourceBinding;
|
import ca.uhn.fhir.rest.server.ResourceBinding;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
@ -71,7 +74,7 @@ import ca.uhn.fhir.util.ExtensionConstants;
|
||||||
* <code>false</code>. This means that if you are adding anything to the returned conformance instance on each call you should call <code>setCache(false)</code> in your provider constructor.
|
* <code>false</code>. This means that if you are adding anything to the returned conformance instance on each call you should call <code>setCache(false)</code> in your provider constructor.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class ServerConformanceProvider {
|
public class ServerConformanceProvider implements IServerConformanceProvider<Conformance> {
|
||||||
|
|
||||||
private boolean myCache = true;
|
private boolean myCache = true;
|
||||||
private volatile Conformance myConformance;
|
private volatile Conformance myConformance;
|
||||||
|
@ -90,13 +93,9 @@ public class ServerConformanceProvider {
|
||||||
return myPublisher;
|
return myPublisher;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Actually create and return the conformance statement
|
|
||||||
*
|
|
||||||
* See the class documentation for an important note if you are extending this class
|
|
||||||
*/
|
|
||||||
@Metadata
|
@Metadata
|
||||||
public Conformance getServerConformance() {
|
public Conformance getServerConformance(HttpServletRequest theRequest) {
|
||||||
if (myConformance != null && myCache) {
|
if (myConformance != null && myCache) {
|
||||||
return myConformance;
|
return myConformance;
|
||||||
}
|
}
|
||||||
|
@ -136,7 +135,7 @@ public class ServerConformanceProvider {
|
||||||
String resourceName = next.getResourceName();
|
String resourceName = next.getResourceName();
|
||||||
RuntimeResourceDefinition def = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
|
RuntimeResourceDefinition def = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
|
||||||
resource.getTypeElement().setValue(def.getName());
|
resource.getTypeElement().setValue(def.getName());
|
||||||
resource.getProfile().setReference(new IdDt(def.getResourceProfile()));
|
resource.getProfile().setReference(new IdDt(def.getResourceProfile(myRestfulServer.getServerBaseForRequest(theRequest))));
|
||||||
|
|
||||||
TreeSet<String> includes = new TreeSet<String>();
|
TreeSet<String> includes = new TreeSet<String>();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package ca.uhn.fhir.parser;
|
package ca.uhn.fhir.parser;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
import net.sf.json.JSON;
|
import net.sf.json.JSON;
|
||||||
import net.sf.json.JSONSerializer;
|
import net.sf.json.JSONSerializer;
|
||||||
import net.sf.json.JsonConfig;
|
import net.sf.json.JsonConfig;
|
||||||
|
@ -11,16 +15,17 @@ import org.junit.Test;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
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.ResourceMetadataKeyEnum;
|
||||||
import ca.uhn.fhir.model.dev.resource.MedicationPrescription;
|
import ca.uhn.fhir.model.dev.resource.MedicationPrescription;
|
||||||
import ca.uhn.fhir.model.dev.resource.Patient;
|
import ca.uhn.fhir.model.dev.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.dev.resource.QuestionnaireAnswers;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Binary;
|
import ca.uhn.fhir.model.dstu.resource.Binary;
|
||||||
import ca.uhn.fhir.model.primitive.BooleanDt;
|
import ca.uhn.fhir.model.primitive.BooleanDt;
|
||||||
import ca.uhn.fhir.model.primitive.CodeDt;
|
import ca.uhn.fhir.model.primitive.CodeDt;
|
||||||
import ca.uhn.fhir.model.primitive.DateDt;
|
import ca.uhn.fhir.model.primitive.DateDt;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||||
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
|
||||||
public class JsonParserTest {
|
public class JsonParserTest {
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(JsonParserTest.class);
|
||||||
|
@ -37,6 +42,23 @@ public class JsonParserTest {
|
||||||
assertEquals("{\"resourceType\":\"Binary\",\"id\":\"11\",\"meta\":{\"versionId\":\"22\"},\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}", val);
|
assertEquals("{\"resourceType\":\"Binary\",\"id\":\"11\",\"meta\":{\"versionId\":\"22\"},\"contentType\":\"foo\",\"content\":\"AQIDBA==\"}", val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #65
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testJsonPrimitiveWithExtensionEncoding() {
|
||||||
|
|
||||||
|
QuestionnaireAnswers parsed = new QuestionnaireAnswers();
|
||||||
|
parsed.getGroup().setLinkId("value123");
|
||||||
|
parsed.getGroup().getLinkIdElement().addUndeclaredExtension(false, "http://123", new StringDt("HELLO"));
|
||||||
|
|
||||||
|
String encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(parsed);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
assertThat(encoded, containsString("{\"linkId\":\"value123\",\"_linkId\":{\"http://123\":[{\"valueString\":\"HELLO\"}]}}"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParsePatientInBundle() {
|
public void testParsePatientInBundle() {
|
||||||
|
|
||||||
|
|
|
@ -4,11 +4,16 @@ import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.servlet.ServletConfig;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
@ -46,7 +51,7 @@ public class ServerConformanceProviderTest {
|
||||||
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
||||||
rs.setServerConformanceProvider(sc);
|
rs.setServerConformanceProvider(sc);
|
||||||
|
|
||||||
rs.init(null);
|
rs.init(createServletConfig());
|
||||||
|
|
||||||
boolean found=false;
|
boolean found=false;
|
||||||
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
|
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
|
||||||
|
@ -60,7 +65,7 @@ public class ServerConformanceProviderTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertTrue(found);
|
assertTrue(found);
|
||||||
Conformance conformance = sc.getServerConformance();
|
Conformance conformance = sc.getServerConformance(createHttpServletRequest());
|
||||||
|
|
||||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||||
ourLog.info(conf);
|
ourLog.info(conf);
|
||||||
|
@ -80,11 +85,11 @@ public class ServerConformanceProviderTest {
|
||||||
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
||||||
rs.setServerConformanceProvider(sc);
|
rs.setServerConformanceProvider(sc);
|
||||||
|
|
||||||
rs.init(null);
|
rs.init(createServletConfig());
|
||||||
|
|
||||||
Conformance conformance = sc.getServerConformance();
|
Conformance conformance = sc.getServerConformance(createHttpServletRequest());
|
||||||
|
|
||||||
myCtx.newValidator().validate(conformance);
|
assertTrue(myCtx.newValidator().validateWithResult(conformance).isSuccessful());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,7 +103,7 @@ public class ServerConformanceProviderTest {
|
||||||
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
||||||
rs.setServerConformanceProvider(sc);
|
rs.setServerConformanceProvider(sc);
|
||||||
|
|
||||||
rs.init(null);
|
rs.init(createServletConfig());
|
||||||
|
|
||||||
boolean found=false;
|
boolean found=false;
|
||||||
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
|
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
|
||||||
|
@ -113,7 +118,7 @@ public class ServerConformanceProviderTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue(found);
|
assertTrue(found);
|
||||||
Conformance conformance = sc.getServerConformance();
|
Conformance conformance = sc.getServerConformance(createHttpServletRequest());
|
||||||
String conf = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
String conf = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||||
ourLog.info(conf);
|
ourLog.info(conf);
|
||||||
|
|
||||||
|
@ -131,9 +136,9 @@ public class ServerConformanceProviderTest {
|
||||||
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
||||||
rs.setServerConformanceProvider(sc);
|
rs.setServerConformanceProvider(sc);
|
||||||
|
|
||||||
rs.init(null);
|
rs.init(createServletConfig());
|
||||||
|
|
||||||
Conformance conformance = sc.getServerConformance();
|
Conformance conformance = sc.getServerConformance(createHttpServletRequest());
|
||||||
String conf = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
String conf = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||||
ourLog.info(conf);
|
ourLog.info(conf);
|
||||||
|
|
||||||
|
@ -171,7 +176,6 @@ public class ServerConformanceProviderTest {
|
||||||
/**
|
/**
|
||||||
* Created by dsotnikov on 2/25/2014.
|
* Created by dsotnikov on 2/25/2014.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public static class MultiOptionalProvider {
|
public static class MultiOptionalProvider {
|
||||||
|
|
||||||
@Search(type = Patient.class)
|
@Search(type = Patient.class)
|
||||||
|
@ -203,4 +207,19 @@ public class ServerConformanceProviderTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private HttpServletRequest createHttpServletRequest() {
|
||||||
|
HttpServletRequest req = mock(HttpServletRequest.class);
|
||||||
|
when(req.getRequestURI()).thenReturn("/FhirStorm/fhir/Patient/_search");
|
||||||
|
when(req.getServletPath()).thenReturn("/fhir");
|
||||||
|
when(req.getRequestURL()).thenReturn(new StringBuffer().append("http://fhirstorm.dyndns.org:8080/FhirStorm/fhir/Patient/_search"));
|
||||||
|
when(req.getContextPath()).thenReturn("/FhirStorm");
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ServletConfig createServletConfig() {
|
||||||
|
ServletConfig sc = mock(ServletConfig.class);
|
||||||
|
when (sc.getServletContext()).thenReturn(null);
|
||||||
|
return sc;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,3 +6,4 @@
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
/target/
|
/target/
|
||||||
|
/target/
|
||||||
|
|
|
@ -70,6 +70,7 @@ import ca.uhn.fhir.model.dstu.valueset.DataTypeEnum;
|
||||||
import ca.uhn.fhir.model.dstu.valueset.SlicingRulesEnum;
|
import ca.uhn.fhir.model.dstu.valueset.SlicingRulesEnum;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
|
import ca.uhn.fhir.rest.server.provider.ServerConformanceProvider;
|
||||||
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
|
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
|
||||||
|
@ -81,7 +82,7 @@ public class FhirDstu1 implements IFhirVersion {
|
||||||
private String myId;
|
private String myId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object createServerConformanceProvider(RestfulServer theServer) {
|
public ServerConformanceProvider createServerConformanceProvider(RestfulServer theServer) {
|
||||||
return new ServerConformanceProvider(theServer);
|
return new ServerConformanceProvider(theServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ import ca.uhn.fhir.rest.method.IParameter;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.SearchParameter;
|
import ca.uhn.fhir.rest.method.SearchParameter;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||||
import ca.uhn.fhir.rest.server.ResourceBinding;
|
import ca.uhn.fhir.rest.server.ResourceBinding;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.util.ExtensionConstants;
|
import ca.uhn.fhir.util.ExtensionConstants;
|
||||||
|
@ -71,7 +72,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
* <code>setCache(false)</code> in your provider constructor.
|
* <code>setCache(false)</code> in your provider constructor.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class ServerConformanceProvider {
|
public class ServerConformanceProvider implements IServerConformanceProvider<Conformance> {
|
||||||
|
|
||||||
private boolean myCache = true;
|
private boolean myCache = true;
|
||||||
private volatile Conformance myConformance;
|
private volatile Conformance myConformance;
|
||||||
|
@ -106,7 +107,7 @@ public class ServerConformanceProvider {
|
||||||
|
|
||||||
retVal.setPublisher(myPublisher);
|
retVal.setPublisher(myPublisher);
|
||||||
retVal.setDate(DateTimeDt.withCurrentTime());
|
retVal.setDate(DateTimeDt.withCurrentTime());
|
||||||
retVal.setFhirVersion("0.80"); // TODO: pull from model
|
retVal.setFhirVersion("0.0.82-3059"); // TODO: pull from model
|
||||||
retVal.setAcceptUnknown(false); // TODO: make this configurable - this is a fairly big effort since the parser needs to be modified to actually allow it
|
retVal.setAcceptUnknown(false); // TODO: make this configurable - this is a fairly big effort since the parser needs to be modified to actually allow it
|
||||||
|
|
||||||
retVal.getImplementation().setDescription(myRestfulServer.getImplementationDescription());
|
retVal.getImplementation().setDescription(myRestfulServer.getImplementationDescription());
|
||||||
|
|
|
@ -49,6 +49,7 @@ import ca.uhn.fhir.model.dstu.resource.Organization;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Patient;
|
import ca.uhn.fhir.model.dstu.resource.Patient;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Profile;
|
import ca.uhn.fhir.model.dstu.resource.Profile;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Query;
|
import ca.uhn.fhir.model.dstu.resource.Query;
|
||||||
|
import ca.uhn.fhir.model.dstu.resource.Questionnaire;
|
||||||
import ca.uhn.fhir.model.dstu.resource.Specimen;
|
import ca.uhn.fhir.model.dstu.resource.Specimen;
|
||||||
import ca.uhn.fhir.model.dstu.resource.ValueSet;
|
import ca.uhn.fhir.model.dstu.resource.ValueSet;
|
||||||
import ca.uhn.fhir.model.dstu.resource.ValueSet.Define;
|
import ca.uhn.fhir.model.dstu.resource.ValueSet.Define;
|
||||||
|
@ -89,6 +90,23 @@ public class JsonParserTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* #65
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testJsonPrimitiveWithExtensionEncoding() {
|
||||||
|
String res = "{\"resourceType\":\"Questionnaire\",\"status\":\"draft\",\"authored\":\"2014-10-30T14:15:00\",\"subject\":{\"reference\":\"http://www.hl7.org/fhir/Patient/1\"},\"author\":{\"reference\":\"http://www.hl7.org/fhir/Practitioner/1\"},\"name\":{\"text\":\"WDHB Friends and Family Test\"},\"group\":{\"header\":\"Note: This is an anonymous survey, which means you cannot be identified.\",\"_header\":[{\"extension\":[{\"url\":\"http://hl7.org/fhir/Profile/iso-21090#language\",\"valueCode\":\"en\"},{\"url\":\"http://hl7.org/fhir/Profile/iso-21090#string-translation\",\"valueString\":\"è«\\u008b注æ\\u0084\\u008fï¼\\u009aè¿\\u0099æ\\u0098¯ä¸\\u0080个å\\u008c¿å\\u0090\\u008dè°\\u0083æ\\u009f¥ï¼\\u008c被è°\\u0083æ\\u009f¥äººå°\\u0086ä¸\\u008dä¼\\u009a被è¯\\u0086å\\u0088«å\\u0087ºæ\\u009d¥ã\\u0080\\u0082\"},{\"url\":\"http://hl7.org/fhir/Profile/iso-21090#string-translation\",\"valueString\":\"ì\\u009dµëª\\u0085ì\\u009c¼ë¡\\u009c í\\u0095\\u0098ë\\u008a\\u0094 ì\\u0084¤ë¬¸ì¡°ì\\u0082¬ì\\u009d´ë¯\\u0080ë¡\\u009c ì\\u009e\\u0091ì\\u0084±ì\\u009e\\u0090ê°\\u0080 ë\\u0088\\u0084구ì\\u009d¸ì§\\u0080 ë°\\u009dí\\u0098\\u0080ì§\\u0080ì§\\u0080 ì\\u0095\\u008aì\\u008aµë\\u008b\\u0088ë\\u008b¤.\"}]}],\"question\":[{\"extension\":[{\"url\":\"http://hl7.org/fhir/questionnaire-extensions#answerFormat\",\"valueCode\":\"single-choice\"}],\"text\":\"Are you a patient?\",\"options\":{\"reference\":\"#question1\"}}]}}";
|
||||||
|
Questionnaire parsed = ourCtx.newJsonParser().parseResource(Questionnaire.class, res);
|
||||||
|
assertEquals("Note: This is an anonymous survey, which means you cannot be identified.", parsed.getGroup().getHeader().getValue());
|
||||||
|
assertEquals(1, parsed.getGroup().getHeader().getUndeclaredExtensionsByUrl("http://hl7.org/fhir/Profile/iso-21090#language").size());
|
||||||
|
|
||||||
|
String encoded = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(parsed);
|
||||||
|
ourLog.info(encoded);
|
||||||
|
assertThat(encoded, containsString("\"_header\":{"));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeNonContained() {
|
public void testEncodeNonContained() {
|
||||||
Organization org = new Organization();
|
Organization org = new Organization();
|
||||||
|
@ -193,7 +211,7 @@ public class JsonParserTest {
|
||||||
|
|
||||||
encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
|
encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
|
||||||
ourLog.info(encoded);
|
ourLog.info(encoded);
|
||||||
assertEquals(encoded, "{\"resourceType\":\"Conformance\",\"_acceptUnknown\":[{\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"AAA\"}]}]}");
|
assertEquals(encoded, "{\"resourceType\":\"Conformance\",\"_acceptUnknown\":{\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"AAA\"}]}}");
|
||||||
|
|
||||||
// Now with a value
|
// Now with a value
|
||||||
ourLog.info("---------------");
|
ourLog.info("---------------");
|
||||||
|
@ -207,7 +225,7 @@ public class JsonParserTest {
|
||||||
|
|
||||||
encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
|
encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
|
||||||
ourLog.info(encoded);
|
ourLog.info(encoded);
|
||||||
assertEquals(encoded, "{\"resourceType\":\"Conformance\",\"acceptUnknown\":true,\"_acceptUnknown\":[{\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"AAA\"}]}]}");
|
assertEquals(encoded, "{\"resourceType\":\"Conformance\",\"acceptUnknown\":true,\"_acceptUnknown\":{\"extension\":[{\"url\":\"http://foo\",\"valueString\":\"AAA\"}]}}");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaConformanceProvider;
|
import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu1;
|
||||||
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
|
||||||
import ca.uhn.fhir.jpa.rp.dev.DiagnosticReportResourceProvider;
|
import ca.uhn.fhir.jpa.rp.dev.DiagnosticReportResourceProvider;
|
||||||
import ca.uhn.fhir.jpa.rp.dev.ObservationResourceProvider;
|
import ca.uhn.fhir.jpa.rp.dev.ObservationResourceProvider;
|
||||||
|
|
|
@ -43,7 +43,7 @@ public class FhirDev implements IFhirVersion {
|
||||||
private String myId;
|
private String myId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object createServerConformanceProvider(RestfulServer theServer) {
|
public ServerConformanceProvider createServerConformanceProvider(RestfulServer theServer) {
|
||||||
return new ServerConformanceProvider(theServer);
|
return new ServerConformanceProvider(theServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ public class FhirDstu1 implements IFhirVersion {
|
||||||
private String myId;
|
private String myId;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object createServerConformanceProvider(RestfulServer theServer) {
|
public ServerConformanceProvider createServerConformanceProvider(RestfulServer theServer) {
|
||||||
return new ServerConformanceProvider(theServer);
|
return new ServerConformanceProvider(theServer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ import ca.uhn.fhir.rest.method.IParameter;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.SearchParameter;
|
import ca.uhn.fhir.rest.method.SearchParameter;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||||
import ca.uhn.fhir.rest.server.ResourceBinding;
|
import ca.uhn.fhir.rest.server.ResourceBinding;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.util.ExtensionConstants;
|
import ca.uhn.fhir.util.ExtensionConstants;
|
||||||
|
@ -71,7 +72,7 @@ import javax.servlet.http.HttpServletRequest;
|
||||||
* <code>setCache(false)</code> in your provider constructor.
|
* <code>setCache(false)</code> in your provider constructor.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class ServerConformanceProvider {
|
public class ServerConformanceProvider implements IServerConformanceProvider<Conformance> {
|
||||||
|
|
||||||
private boolean myCache = true;
|
private boolean myCache = true;
|
||||||
private volatile Conformance myConformance;
|
private volatile Conformance myConformance;
|
||||||
|
|
|
@ -28,6 +28,8 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||||
|
@ -54,6 +56,7 @@ import ca.uhn.fhir.rest.method.IParameter;
|
||||||
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
import ca.uhn.fhir.rest.method.SearchMethodBinding;
|
||||||
import ca.uhn.fhir.rest.method.SearchParameter;
|
import ca.uhn.fhir.rest.method.SearchParameter;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||||
import ca.uhn.fhir.rest.server.ResourceBinding;
|
import ca.uhn.fhir.rest.server.ResourceBinding;
|
||||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||||
import ca.uhn.fhir.util.ExtensionConstants;
|
import ca.uhn.fhir.util.ExtensionConstants;
|
||||||
|
@ -68,7 +71,7 @@ import ca.uhn.fhir.util.ExtensionConstants;
|
||||||
* <code>setCache(false)</code> in your provider constructor.
|
* <code>setCache(false)</code> in your provider constructor.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class ServerConformanceProvider {
|
public class ServerConformanceProvider implements IServerConformanceProvider<Conformance> {
|
||||||
|
|
||||||
private boolean myCache = true;
|
private boolean myCache = true;
|
||||||
private volatile Conformance myConformance;
|
private volatile Conformance myConformance;
|
||||||
|
@ -94,7 +97,7 @@ public class ServerConformanceProvider {
|
||||||
* See the class documentation for an important note if you are extending this class
|
* See the class documentation for an important note if you are extending this class
|
||||||
*/
|
*/
|
||||||
@Metadata
|
@Metadata
|
||||||
public Conformance getServerConformance() {
|
public Conformance getServerConformance(HttpServletRequest theReq) {
|
||||||
if (myConformance != null && myCache) {
|
if (myConformance != null && myCache) {
|
||||||
return myConformance;
|
return myConformance;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,13 @@
|
||||||
Support for DSTU2 features introduced: New Bundle encoding style, as well as new
|
Support for DSTU2 features introduced: New Bundle encoding style, as well as new
|
||||||
extension encoding in JSON.
|
extension encoding in JSON.
|
||||||
</action>
|
</action>
|
||||||
|
<action type="fix" issue="65">
|
||||||
|
Fix an issue encoding extensions on primitive types in JSON. Previously the "_value" object
|
||||||
|
would be an array even if the field it was extending was not repeatable. This is not correct
|
||||||
|
according to the specification, nor can HAPI's parser parse this correctly. The encoder
|
||||||
|
has been corrected, and the parser has been adjusted to be able to handle resources with
|
||||||
|
extensions encoded in this way. Thanks to Mohammad Jafari for reporting!
|
||||||
|
</action>
|
||||||
<action type="add">
|
<action type="add">
|
||||||
Library now checks if custom resource types can be instantiated on startup
|
Library now checks if custom resource types can be instantiated on startup
|
||||||
(e.g. because they don't have a no-argument constructor) in order to
|
(e.g. because they don't have a no-argument constructor) in order to
|
||||||
|
|
|
@ -0,0 +1,656 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<document xmlns="http://maven.apache.org/changes/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 ./changes.xsd">
|
||||||
|
<properties>
|
||||||
|
<author>James Agnew</author>
|
||||||
|
<title>HAPI FHIR Changelog</title>
|
||||||
|
</properties>
|
||||||
|
<body>
|
||||||
|
<release version="0.9" date="TBA">
|
||||||
|
<action type="add">
|
||||||
|
Support for DSTU2 features introduced: New Bundle encoding style, as well as new
|
||||||
|
extension encoding in JSON.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Library now checks if custom resource types can be instantiated on startup
|
||||||
|
(e.g. because they don't have a no-argument constructor) in order to
|
||||||
|
avoid failing later
|
||||||
|
</action>
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<action type="add">
|
||||||
|
Bump a few dependency JARs to the latest versions in Maven POM:
|
||||||
|
<![CDATA[
|
||||||
|
<ul>
|
||||||
|
<li>SLF4j (in base module) - Bumped to 1.7.9</li>
|
||||||
|
<li>Apache HTTPClient (in base module) - Bumped to 4.3.6</li>
|
||||||
|
<li>Hibernate (in JPA module) - Bumped to 4.3.7</li>
|
||||||
|
</ul>
|
||||||
|
]]>
|
||||||
|
=======
|
||||||
|
<action type="fix" issue="67">
|
||||||
|
IdDt failed to recognize local identifiers containing fragments that look like
|
||||||
|
real identifiers as being local identifiers even though they started with '#'.
|
||||||
|
For example, a local resource reference of "#aa/_history/aa" would be incorrectly
|
||||||
|
parsed as a non-local reference.
|
||||||
|
Thanks to Mohammad Jafari for reporting!
|
||||||
|
>>>>>>> 31d61100db41590b9760daf9e0942ad553ff69e2
|
||||||
|
</action>
|
||||||
|
</release>
|
||||||
|
<release version="0.8" date="2014-Dec-17">
|
||||||
|
<action type="add">
|
||||||
|
<![CDATA[<b>API CHANGE:</b>]]> The "FHIR structures" for DSTU1 (the classes which model the
|
||||||
|
resources and composite datatypes) have been moved out of the core JAR into their
|
||||||
|
own JAR, in order to allow support for DEV resources, and DSTU2 resources when thast
|
||||||
|
version is finalized. See
|
||||||
|
<![CDATA[<a href="./doc_upgrading.html">upgrading</a>]]>
|
||||||
|
for more information.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
<![CDATA[
|
||||||
|
<b>Deprocated API Removal</b>: The following classes (which were deprocated previously)
|
||||||
|
have now been removed:
|
||||||
|
<ul>
|
||||||
|
<li><b>ISecurityManager</b>: If you are using this class, the same functionality
|
||||||
|
is available through the more general purpose
|
||||||
|
<a href="http://jamesagnew.github.io/hapi-fhir/doc_rest_server_interceptor.html">server interceptor</a>
|
||||||
|
capabilities.
|
||||||
|
<li><b>CodingListParam</b>: This class was made redundant by the
|
||||||
|
<a href="http://jamesagnew.github.io/hapi-fhir/apidocs/ca/uhn/fhir/rest/param/TokenOrListParam.html">TokenOrListParam</a>
|
||||||
|
class, which can be used in its place.
|
||||||
|
</ul>
|
||||||
|
]]>
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
<![CDATA[
|
||||||
|
<b>API Change</b>: The IResource#getResourceMetadata() method has been changed
|
||||||
|
from returning
|
||||||
|
<code>Map<ResourceMetadataKeyEnum<?>, Object><code>
|
||||||
|
to returning a new type called
|
||||||
|
<code>ResourceMetadataMap</code>. This new type implements
|
||||||
|
<code>Map<ResourceMetadataKeyEnum<?>, Object><code>
|
||||||
|
itself, so this change should not break existing code, but may
|
||||||
|
require a clean build in order to run correctly.
|
||||||
|
]]>
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="38" dev="wdebeau1">
|
||||||
|
Profile generation on the server was not working due to IdDt being
|
||||||
|
incorrectly used. Thanks to Bill de Beaubien for the pull request!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="42" dev="wdebeau1">
|
||||||
|
Profiles did not generate correctly if a resource definition class had a
|
||||||
|
defined extension which was of a composite type. Thanks to Bill de Beaubien for the pull request!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="44" dev="petromykhailysyn">
|
||||||
|
Remove unnecessary IOException from narrative generator API. Thanks to
|
||||||
|
Petro Mykhailysyn for the pull request!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="48" dev="wdebeau1">
|
||||||
|
Introduced a new
|
||||||
|
<![CDATA[<code>@ProvidesResources</code>]]> annotation which can be added to
|
||||||
|
resource provider and servers to allow them to declare additional resource
|
||||||
|
classes they are able to serve. This is useful if you have a server which can
|
||||||
|
serve up multiple classes for the same resource type (e.g. a server that sometimes
|
||||||
|
returns a default Patient, but sometimes uses a custom subclass).
|
||||||
|
Thanks to Bill de Beaubien for the pull request!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="49" dev="wdebeau1">
|
||||||
|
Introduced a new
|
||||||
|
<![CDATA[<code>@Destroy</code>]]> annotation which can be added to
|
||||||
|
a resource provider method. This method will be called by the server when it
|
||||||
|
is being closed/destroyed (e.g. when the application is being undeployed, the
|
||||||
|
container is being shut down, etc.)
|
||||||
|
Thanks to Bill de Beaubien for the pull request!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add a new method <![CDATA[handleException]]> to the server interceptor
|
||||||
|
framework which allows interceptors to be notified of any exceptions and
|
||||||
|
runtime errors within server methods. Interceptors may optionally also
|
||||||
|
override the default error handling behaviour of the RestfulServer.
|
||||||
|
</action>
|
||||||
|
<action dev="wdebeau1" type="add">
|
||||||
|
Add constants to BaseResource for the "_id" search parameter which all resources
|
||||||
|
should support.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
DateRangeParam parameters on the server now return correct
|
||||||
|
<![CDATA[<code>getLowerBoundAsInstant()</code>]]>
|
||||||
|
and
|
||||||
|
<![CDATA[<code>getUpperBoundAsInstant()</code>]]>
|
||||||
|
values if a single unqualified value is passed in. For example, if
|
||||||
|
a query containing
|
||||||
|
<![CDATA[<code>&birthdate=2012-10-01</code>]]>
|
||||||
|
is received, previously these two methods would both return the same
|
||||||
|
value, but with this fix
|
||||||
|
<![CDATA[<code>getUpperBoundAsInstant()</code>]]>
|
||||||
|
now returns the instant at 23:59:59.9999.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Resource fields with a type of "*" (or Any) sometimes failed to parse if a
|
||||||
|
value type of "code" was used. Thanks to Bill de Beaubien for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="add" dev="lmds">
|
||||||
|
Remove dependency on JAXB libraries, which were used to parse and encode
|
||||||
|
dates and times (even in the JSON parser). JAXB is built in to most JDKs
|
||||||
|
but the version bundled with IBM's JDK is flaky and resulted in a number
|
||||||
|
of problems when deploying to Websphere.
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="50" dev="jjathman">
|
||||||
|
Primitive datatypes now preserve their original string value when parsing resources,
|
||||||
|
as well as containing the "parsed value". For instance, a DecimalDt field value of
|
||||||
|
<![CDATA[<code>1.0000</code>]]> will be parsed into the corresponding
|
||||||
|
decimal value, but will also retain the original value with the corresponding
|
||||||
|
level of precision. This allows vadliator rules to be applied to
|
||||||
|
original values as received "over the wire", such as well formatted but
|
||||||
|
invalid dates, e.g. "2001-15-01". Thanks to Joe Athman for reporting and
|
||||||
|
helping to come up with a fix!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
When using Generic Client, if performing a
|
||||||
|
<![CDATA[create]]> or <![CDATA[update]]> operation using a String as the resource body,
|
||||||
|
the client will auto-detect the FHIR encoding style and send an appropriate
|
||||||
|
<![CDATA[Content-Type]]> header.
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="52">
|
||||||
|
JPA module (and public HAPI-FHIR test server) were unable to process resource types
|
||||||
|
where at least one search parameter has no path specified. These now correctly save
|
||||||
|
(although the server does not yet process these params, and it should). Thanks to
|
||||||
|
GitHub user shvoidlee for reporting and help with analysis!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Generic/Fluent Client "create" and "update" method requests were not setting a content type header
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="53" dev="petromykhailysyn">
|
||||||
|
DateDt left precision value as <![CDATA[null]]> in the constructor
|
||||||
|
<![CDATA[DateDt(Date)]]>.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
RESTful server now doesn't overwrite resource IDs if they are absolute. In other words, if
|
||||||
|
a server's Resource Provider returns a resource with ID "Patient/123" it will be translated to
|
||||||
|
"[base url]/Patient/123" but if the RP returns ID "http://foo/Patient/123" the ID will be
|
||||||
|
returned exactly as is. Thanks to Bill de Beaubien for the suggestion!
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="55">
|
||||||
|
JPA module Transaction operation was not correctly replacing logical IDs
|
||||||
|
beginning with "cid:" with server assigned IDs, as required by the
|
||||||
|
specification.
|
||||||
|
</action>
|
||||||
|
<action type="fix" dev="tahurac">
|
||||||
|
<![CDATA[FhirTerser]]> did not visit or find children in contained resources when
|
||||||
|
searching a resource. This caused server implementations to not always return contained
|
||||||
|
resources when they are included with a resource being returned.
|
||||||
|
</action>
|
||||||
|
<action type="add" dev="lmds">
|
||||||
|
Add a method <![CDATA[String IResource#getResourceName()]]> which returns the name of the
|
||||||
|
resource in question (e.g. "Patient", or "Observation"). This is intended as a
|
||||||
|
convenience to users.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Do not strip version from resource references in resources returned
|
||||||
|
from server search methods. Thanks to Bill de Beaubien for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix" dev="jjathman" issue="54">
|
||||||
|
Correct an issue with the validator where changes to the underlying
|
||||||
|
OperationOutcome produced by a validation cycle cause the validation
|
||||||
|
results to be incorrect.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Client interceptors registered to an interface based client instance
|
||||||
|
were applied to other client instances for the same client interface as well. (Issue
|
||||||
|
did not affect generic/fluent clients)
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="57">
|
||||||
|
DateDt, DateTimeDt and types InstantDt types now do not throw an exception
|
||||||
|
if they are used to parse a value with the wrong level of precision for
|
||||||
|
the given type but do throw an exception if the wrong level of precision
|
||||||
|
is passed into their constructors.<![CDATA[<br/><br/>]]>
|
||||||
|
This means that HAPI FHIR can now successfully parse resources from external
|
||||||
|
sources that have the wrong level of precision, but will generate a validation
|
||||||
|
error if the resource is validated. Thanks to Alexander Kley for the suggestion!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Encoding a Binary resource without a content type set should not result in a NullPointerException. Thanks
|
||||||
|
to Alexander Kley for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Server gives a more helpful error message if multiple IResourceProvider implementations
|
||||||
|
are provided for the same resource type. Thanks to wanghaisheng for the idea!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="61">
|
||||||
|
Bring DSTU1 resource definitions up to version 0.0.82-2929<![CDATA[<br/>]]>
|
||||||
|
Bring DEV resource definitions up to 0.4.0-3775<![CDATA[<br/>]]>
|
||||||
|
Thanks to crinacimpian for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="62">
|
||||||
|
JPA server did not correctly process _include requests if included
|
||||||
|
resources were present with a non-numeric identifier. Thanks to
|
||||||
|
Bill de Beaubien for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="60">
|
||||||
|
Client requests which include a resource/bundle body (e.g. create,
|
||||||
|
update, transaction) were not including a charset in the content type
|
||||||
|
header, leading to servers incorrectly assuming ISO-8859/1. Thanks to
|
||||||
|
shvoidlee for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="59" dev="wdebeau1">
|
||||||
|
Clean up the way that Profile resources are automatically exported
|
||||||
|
by the server for custom resource profile classes. See the
|
||||||
|
<![CDATA[<a href="http://jamesagnew.github.io/hapi-fhir/apidocs/ca/uhn/fhir/model/api/annotation/ResourceDef.html">@ResourceDef</a>]]>
|
||||||
|
JavaDoc for information on how this works.
|
||||||
|
</action>
|
||||||
|
</release>
|
||||||
|
<release version="0.7" date="2014-Oct-23">
|
||||||
|
<action type="add" issue="30">
|
||||||
|
<![CDATA[<b>API CHANGE:</b>]]> The TagList class previously implemented ArrayList semantics,
|
||||||
|
but this has been replaced with LinkedHashMap semantics. This means that the list of
|
||||||
|
tags will no longer accept duplicate tags, but that tag order will still be
|
||||||
|
preserved. Thanks to Bill de Beaubien for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="33">
|
||||||
|
Server was incorrectly including contained resources being returned as both contained resources, and as
|
||||||
|
top-level resources in the returned bundle for search operations.
|
||||||
|
Thanks to Bill de Beaubien for reporting! This also fixes Issue #20, thanks to
|
||||||
|
lephty for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="add" dev="suranga">
|
||||||
|
Documentation fixes
|
||||||
|
</action>
|
||||||
|
<action type="add" dev="dougmartin">
|
||||||
|
Add a collection of new methods on the generic client which support the
|
||||||
|
<![CDATA[
|
||||||
|
<b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#read(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">read</a></b>,
|
||||||
|
<b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#vread(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">read</a></b>,
|
||||||
|
and <b><a href="./apidocs/ca/uhn/fhir/rest/client/IGenericClient.html#search(java.lang.Class,%20ca.uhn.fhir.model.primitive.UriDt)">search</a></b>
|
||||||
|
]]>
|
||||||
|
operations using an absolute URL. This allows developers to perform these operations using
|
||||||
|
URLs they obtained from other sources (or external resource references within resources). In
|
||||||
|
addition, the existing read/vread operations will now access absolute URL references if
|
||||||
|
they are passed in. Thanks to Doug Martin of the Regenstrief Center for Biomedical Informatics
|
||||||
|
for contributing this implementation!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server implementation was not correctly figuring out its own FHIR Base URL when deployed
|
||||||
|
on Amazon Web Service server. Thanks to Jeffrey Ting and Bill De Beaubien of
|
||||||
|
Systems Made Simple for their help in figuring out this issue!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
XML Parser failed to encode fields with both a resource reference child and
|
||||||
|
a primitive type child. Thanks to Jeffrey Ting and Bill De Beaubien of
|
||||||
|
Systems Made Simple for their help in figuring out this issue!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
HAPI now runs successfully on Servlet 2.5 containers (such as Tomcat 6). Thanks to
|
||||||
|
Bernard Gitaadji for reporting and diagnosing the issue!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Summary (in the bundle entry) is now encoded by the XML and JSON parsers if supplied. Thanks to David Hay of
|
||||||
|
Orion Health for reporting this!
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="24">
|
||||||
|
Conformance profiles which are automatically generated by the server were missing a few mandatory elements,
|
||||||
|
which meant that the profile did not correctly validate. Thanks to Bill de Beaubien of Systems Made Simple
|
||||||
|
for reporting this!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
XHTML (in narratives) containing escapable characters (e.g. < or ") will now always have those characters
|
||||||
|
escaped properly in encoded messages.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Resources containing entities which are not valid in basic XML (e.g. &sect;) will have those
|
||||||
|
entities converted to their equivalent unicode characters when resources are encoded, since FHIR does
|
||||||
|
not allow extended entities in resource instances.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add a new client interceptor which adds HTTP Authorization Bearer Tokens (for use with OAUTH2 servers)
|
||||||
|
to client requests.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Add phloc-commons dependency explicitly, which resolves an issue building HAPI from source on
|
||||||
|
some platforms. Thanks to Odysseas Pentakalos for the patch!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
HAPI now logs a single line indicating the StAX implementation being used upon the
|
||||||
|
first time an XML parser is created.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Update methods on the server did not return a "content-location" header, but
|
||||||
|
only a "location" header. Both are required according to the FHIR specification.
|
||||||
|
Thanks to Bill de Beaubien of Systems Made Simple for reporting this!
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="26" dev="akley">
|
||||||
|
Parser failed to correctly read contained Binary resources. Thanks to Alexander Kley for
|
||||||
|
the patch!
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="29" dev="akley">
|
||||||
|
Calling encode multiple times on a resource with contained resources caused the contained
|
||||||
|
resources to be re-added (and the actual message to grow) with each encode pass. Thanks to
|
||||||
|
Alexander Kley for the test case!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
JSON-encoded contained resources with the incorrect "_id" element (which should be "id", but some
|
||||||
|
incorrect examples exist on the FHIR specification) now parse correctly. In other words, HAPI
|
||||||
|
previously only accepted the correct "id" element, but now it also accepts the incorrect
|
||||||
|
"_id" element just to be more lenient.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Several unit tests failed on Windows (or any platform with non UTF-8 default encoding). This may
|
||||||
|
have also caused resource validation to fail occasionally on these platforms as well.
|
||||||
|
Thanks to Bill de Beaubien for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
toString() method on TokenParam was incorrectly showing the system as the value.
|
||||||
|
Thanks to Bill de Beaubien for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="update">
|
||||||
|
Documentation on contained resources contained a typo and did not actually produce contained resources. Thanks
|
||||||
|
to David Hay of Orion Health for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="31" dev="preston">
|
||||||
|
Add a
|
||||||
|
<![CDATA[<a href="https://www.vagrantup.com/">Vagrant</a>]]>
|
||||||
|
based environment (basically a fully built, self contained development environment) for
|
||||||
|
trying out the HAPI server modules. Thanks to Preston Lee for the pull request, and for
|
||||||
|
offering to maintain this!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="32" dev="jjathman">
|
||||||
|
Change validation API so that it uses a return type instead of exceptions to communicate
|
||||||
|
validation failures. Thanks to Joe Athman for the pull request!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="35" dev="petromykhailysyn">
|
||||||
|
Add a client interceptor which adds an HTTP cookie to each client request. Thanks to
|
||||||
|
Petro Mykhailysyn for the pull request!
|
||||||
|
</action>
|
||||||
|
</release>
|
||||||
|
<release version="0.6" date="2014-Sep-08" description="This release brings a number of new features and bug fixes!">
|
||||||
|
<!--
|
||||||
|
<action type="add">
|
||||||
|
Allow generic client ... OAUTH
|
||||||
|
</action>
|
||||||
|
-->
|
||||||
|
<action type="add">
|
||||||
|
Add server interceptor framework, and new interceptor for logging incoming
|
||||||
|
requests.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add server validation framework for validating resources against the FHIR schemas and schematrons
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Tester UI created double _format and _pretty param entries in searches. Thanks to Gered King of University
|
||||||
|
Health Network for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="4">
|
||||||
|
Create method was incorrectly returning an HTTP 204 on sucessful completion, but
|
||||||
|
should be returning an HTTP 200 per the FHIR specification. Thanks to wanghaisheng
|
||||||
|
for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
FHIR Tester UI now correctly sends UTF-8 charset in responses so that message payloads containing
|
||||||
|
non US-ASCII characters will correctly display in the browser
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
JSON parser was incorrectly encoding extensions on composite elements outside the element itself
|
||||||
|
(as is done correctly for non-composite elements) instead of inside of them. Thanks to David Hay of
|
||||||
|
Orion for reporting this!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Contained/included resource instances received by a client are now automatically
|
||||||
|
added to any ResourceReferenceDt instancea in other resources which reference them.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add documentation on how to use eBay CORS Filter to support Cross Origin Resource
|
||||||
|
Sharing (CORS) to server. CORS support that was built in to the server itself has
|
||||||
|
been removed, as it did not work correctly (and was reinventing a wheel that others
|
||||||
|
have done a great job inventing). Thanks to Peter Bernhardt of Relay Health for all the assistance
|
||||||
|
in testing this!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
IResource interface did not expose the getLanguage/setLanguage methods from BaseResource,
|
||||||
|
so the resource language was difficult to access.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
JSON Parser now gives a more friendly error message if it tries to parse JSON with invalid use
|
||||||
|
of single quotes
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Transaction server method is now allowed to return an OperationOutcome in addition to the
|
||||||
|
incoming resources. The public test server now does this in order to return status information
|
||||||
|
about the transaction processing.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Update method in the server can now flag (via a field on the MethodOutcome object being returned)
|
||||||
|
that the result was actually a creation, and Create method can indicate that it was actually an
|
||||||
|
update. This has no effect other than to switch between the HTTP 200 and HTTP 201 status codes on the
|
||||||
|
response, but this may be useful in some circumstances.
|
||||||
|
</action>
|
||||||
|
<action type="fix" dev="tahurac">
|
||||||
|
Annotation client search methods with a specific resource type (e.g. List<Patient> search())
|
||||||
|
won't return any resources that aren't of the correct type that are received in a response
|
||||||
|
bundle (generally these are referenced resources, so they are populated in the reference fields instead).
|
||||||
|
Thanks to Tahura Chaudhry of University Health Network for the unit test!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Added narrative generator template for OperationOutcome resource
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Date/time types did not correctly parse values in the format "yyyymmdd" (although the FHIR-defined format
|
||||||
|
is "yyyy-mm-dd" anyhow, and this is correctly handled). Thanks to Jeffrey Ting of Systems Made Simple
|
||||||
|
for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server search method for an unnamed query gets called if the client requests a named query
|
||||||
|
with the same parameter list. Thanks to Neal Acharya of University Health Network for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Category header (for tags) is correctly read in client for "read" operation
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Transaction method in server can now have parameter type Bundle instead of
|
||||||
|
List<IResource>
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
HAPI parsers now use field access to get/set values instead of method accessors and mutators.
|
||||||
|
This should give a small performance boost.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
JSON parser encodes resource references incorrectly, using the name "resource" instead
|
||||||
|
of the name "reference" for the actual reference. Thanks to
|
||||||
|
Ricky Nguyen for reporting and tracking down the issue!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Rename NotImpementedException to NotImplementedException (to correct typo)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server setUseBrowserFriendlyContentType setting also respected for errors (e.g. OperationOutcome with 4xx/5xx status)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Fix performance issue in date/time datatypes where pattern matchers were not static
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server now gives a more helpful error message if a @Read method has a search parameter (which is invalid, but
|
||||||
|
previously lead to a very unhelpful error message). Thanks to Tahura Chaudhry of UHN for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Resource of type "List" failed to parse from a bundle correctly. Thanks to David Hay of Orion Health
|
||||||
|
for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
QuantityParam correctly encodes approximate (~) prefix to values
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="14">
|
||||||
|
If a server defines a method with parameter "_id", incoming search requests for that method may
|
||||||
|
get delegated to the wrong method. Thanks to Neal Acharya for reporting!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
SecurityEvent.Object structural element has been renamed to
|
||||||
|
SecurityEvent.ObjectElement to avoid conflicting names with the
|
||||||
|
java Object class. Thanks to Laurie Macdougall-Sookraj of UHN for
|
||||||
|
reporting!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Text/narrative blocks that were created with a non-empty
|
||||||
|
namespace prefix (e.g. <xhtml:div xmlns:xhtml="...">...</xhtml:div>)
|
||||||
|
failed to encode correctly (prefix was missing in encoded resource)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Resource references previously encoded their children (display and reference)
|
||||||
|
in the wrong order so references with both would fail schema validation.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
SecurityEvent resource's enums now use friendly enum names instead of the unfriendly
|
||||||
|
numeric code values. Thanks to Laurie MacDougall-Sookraj of UHN for the
|
||||||
|
suggestion!
|
||||||
|
</action>
|
||||||
|
</release>
|
||||||
|
<release version="0.5" date="2014-Jul-30">
|
||||||
|
<action type="add">
|
||||||
|
HAPI has a number of RESTful method parameter types that have similar but not identical
|
||||||
|
purposes and confusing names. A cleanup has been undertaken to clean this up.
|
||||||
|
This means that a number of existing classes
|
||||||
|
have been deprocated in favour of new naming schemes.
|
||||||
|
<![CDATA[<br/><br/>]]>
|
||||||
|
All annotation-based clients and all server search method parameters are now named
|
||||||
|
(type)Param, for example: StringParam, TokenParam, etc.
|
||||||
|
<![CDATA[<br/><br/>]]>
|
||||||
|
All generic/fluent client method parameters are now named
|
||||||
|
(type)ClientParam, for example: StringClientParam, TokenClientParam, etc.
|
||||||
|
<![CDATA[<br/><br/>]]>
|
||||||
|
All renamed classes have been retained and deprocated, so this change should not cause any issues
|
||||||
|
for existing applications but those applications should be refactored to use the
|
||||||
|
new parameters when possible.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Allow server methods to return wildcard generic types (e.g. List<? extends IResource>)
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Search parameters are not properly escaped and unescaped. E.g. for a token parameter such as
|
||||||
|
"&identifier=system|codepart1\|codepart2"
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add support for OPTIONS verb (which returns the server conformance statement)
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add support for CORS headers in server
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Bump SLF4j dependency to latest version (1.7.7)
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add interceptor framework for clients (annotation based and generic), and add interceptors
|
||||||
|
for configurable logging, capturing requests and responses, and HTTP basic auth.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Transaction client invocations with XML encoding were using the wrong content type ("application/xml+fhir" instead
|
||||||
|
of the correct "application/atom+xml"). Thanks to David Hay of Orion Health for surfacing this one!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Bundle entries now support a link type of "search". Thanks to David Hay for the suggestion!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="1">
|
||||||
|
If a client receives a non 2xx response (e.g. HTTP 500) and the response body is a text/plain message or
|
||||||
|
an OperationOutcome resource, include the message in the exception message so that it will be
|
||||||
|
more conveniently displayed in logs and other places. Thanks to Neal Acharya for the suggestion!
|
||||||
|
</action>
|
||||||
|
<action type="add" issue="2">
|
||||||
|
Read invocations in the client now process the "Content-Location" header and use it to
|
||||||
|
populate the ID of the returned resource. Thanks to Neal Acharya for the suggestion!
|
||||||
|
</action>
|
||||||
|
<action type="fix" issue="3">
|
||||||
|
Fix issue where vread invocations on server incorrectly get routed to instance history method if one is
|
||||||
|
defined. Thanks to Neal Acharya from UHN for surfacing this one!
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Binary reads on a server not include the Content-Disposition header, to prevent HTML in binary
|
||||||
|
blobs from being used for nefarious purposes. See
|
||||||
|
<![CDATA[<a href="http://gforge.hl7.org/gf/project/fhir/tracker/?action=TrackerItemEdit&tracker_id=677&tracker_item_id=3298">FHIR Tracker Bug 3298</a>]]>
|
||||||
|
for more information.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Support has been added for using an HTTP proxy for outgoing requests.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Fix: Primitive extensions declared against custom resource types
|
||||||
|
are encoded even if they have no value. Thanks to David Hay of Orion for
|
||||||
|
reporting this!
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Fix: RESTful server deployed to a location where the URL to access it contained a
|
||||||
|
space (e.g. a WAR file with a space in the name) failed to work correctly.
|
||||||
|
Thanks to David Hay of Orion for reporting this!
|
||||||
|
</action>
|
||||||
|
</release>
|
||||||
|
<release version="0.4" date="2014-Jul-13">
|
||||||
|
<action type="add">
|
||||||
|
<![CDATA[<b>BREAKING CHANGE:</b>]]>: IdDt has been modified so that it
|
||||||
|
contains a partial or complete resource identity. Previously it contained
|
||||||
|
only the simple alphanumeric id of the resource (the part at the end of the "read" URL for
|
||||||
|
that resource) but it can now contain a complete URL or even a partial URL (e.g. "Patient/123")
|
||||||
|
and can optionally contain a version (e.g. "Patient/123/_history/456"). New methods have
|
||||||
|
been added to this datatype which provide just the numeric portion. See the JavaDoc
|
||||||
|
for more information.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
<![CDATA[<b>API CHANGE:</b>]]>: Most elements in the HAPI FHIR model contain
|
||||||
|
a getId() and setId() method. This method is confusing because it is only actually used
|
||||||
|
for IDREF elements (which are rare) but its name makes it easy to confuse with more
|
||||||
|
important identifiers. For this reason, these methods have been deprocated and replaced with
|
||||||
|
get/setElementSpecificId() methods. The old methods will be removed at some point. Resource
|
||||||
|
types are unchanged and retain their get/setId methods.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Allow use of QuantityDt as a service parameter to support the "quantity" type. Previously
|
||||||
|
QuantityDt did not implement IQueryParameterType so it was not valid, and there was no way to
|
||||||
|
support quantity search parameters on the server (e.g. Observation.value-quantity)
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Introduce StringParameter type which can be used as a RESTful operation search parameter
|
||||||
|
type. StringParameter allows ":exact" matches to be specified in clients, and handled in servers.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Parsers (XML/JSON) now support deleted entries in bundles
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Transaction method now supported in servers
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Support for Binary resources added (in servers, clients, parsers, etc.)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Support for Query resources fixed (in parser)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Nested contained resources (e.g. encoding a resource with a contained resource that itself contains a resource)
|
||||||
|
now parse and encode correctly, meaning that all contained resources are placed in the "contained" element
|
||||||
|
of the root resource, and the parser looks in the root resource for all container levels when stitching
|
||||||
|
contained resources back together.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server methods with @Include parameter would sometimes fail when no _include was actually
|
||||||
|
specified in query strings.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Client requests for IdentifierDt types (such as Patient.identifier) did not create the correct
|
||||||
|
query string if the system is null.
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Add support for paging responses from RESTful servers.
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Don't fail on narrative blocks in JSON resources with only an XML declaration but no content (these are
|
||||||
|
produced by the Health Intersections server)
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server now automatically compresses responses if the client indicates support
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Server failed to support optional parameters when type is String and :exact qualifier is used
|
||||||
|
</action>
|
||||||
|
<action type="fix">
|
||||||
|
Read method in client correctly populated resource ID in returned object
|
||||||
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
Support added for deleted-entry by/name, by/email, and comment from Tombstones spec
|
||||||
|
</action>
|
||||||
|
</release>
|
||||||
|
<release version="0.3" date="2014-May-12" description="This release corrects lots of bugs and introduces the fluent client mode">
|
||||||
|
</release>
|
||||||
|
</body>
|
||||||
|
</document>
|
Loading…
Reference in New Issue