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 org.hl7.fhir.instance.model.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.IServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
|
||||
public interface IFhirVersion {
|
||||
|
@ -37,7 +40,7 @@ public interface IFhirVersion {
|
|||
|
||||
IResource generateProfile(RuntimeResourceDefinition theRuntimeResourceDefinition, String theServerBase);
|
||||
|
||||
Object createServerConformanceProvider(RestfulServer theRestfulServer);
|
||||
IServerConformanceProvider<? extends IBaseResource> createServerConformanceProvider(RestfulServer theRestfulServer);
|
||||
|
||||
String getPathToSchemaDefinitions();
|
||||
|
||||
|
|
|
@ -288,20 +288,20 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
writeTagWithTextNode(theEventWriter, "instant", nextEntry.getDeletedAt());
|
||||
}
|
||||
|
||||
// linkStarted = false;
|
||||
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
|
||||
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
|
||||
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "search", nextEntry.getLinkSearch(), linkStarted);
|
||||
// if (linkStarted) {
|
||||
// theEventWriter.writeEnd();
|
||||
// }
|
||||
//
|
||||
// writeOptionalTagWithTextNode(theEventWriter, "updated", nextEntry.getUpdated());
|
||||
// writeOptionalTagWithTextNode(theEventWriter, "published", nextEntry.getPublished());
|
||||
//
|
||||
// writeCategories(theEventWriter, nextEntry.getCategories());
|
||||
//
|
||||
// writeAuthor(nextEntry, theEventWriter);
|
||||
// linkStarted = false;
|
||||
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "self", nextEntry.getLinkSelf(), linkStarted);
|
||||
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "alternate", nextEntry.getLinkAlternate(), linkStarted);
|
||||
// linkStarted = writeAtomLinkInDstu1Format(theEventWriter, "search", nextEntry.getLinkSearch(), linkStarted);
|
||||
// if (linkStarted) {
|
||||
// theEventWriter.writeEnd();
|
||||
// }
|
||||
//
|
||||
// writeOptionalTagWithTextNode(theEventWriter, "updated", nextEntry.getUpdated());
|
||||
// writeOptionalTagWithTextNode(theEventWriter, "published", nextEntry.getPublished());
|
||||
//
|
||||
// writeCategories(theEventWriter, nextEntry.getCategories());
|
||||
//
|
||||
// writeAuthor(nextEntry, theEventWriter);
|
||||
|
||||
IResource resource = nextEntry.getResource();
|
||||
if (resource != null && !resource.isEmpty() && !deleted) {
|
||||
|
@ -532,7 +532,12 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
}
|
||||
|
||||
if (extensions.size() > 0 || modifierExtensions.size() > 0) {
|
||||
theEventWriter.writeStartArray('_' + currentChildName);
|
||||
if (inArray) {
|
||||
// If this is a repeatable field, the extensions go in an array too
|
||||
theEventWriter.writeStartArray('_' + currentChildName);
|
||||
} else {
|
||||
theEventWriter.writeStartObject('_' + currentChildName);
|
||||
}
|
||||
|
||||
for (int i = 0; i < valueIdx; i++) {
|
||||
boolean haveContent = false;
|
||||
|
@ -552,9 +557,13 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
if (!haveContent) {
|
||||
theEventWriter.writeNull();
|
||||
} else {
|
||||
theEventWriter.writeStartObject();
|
||||
if (inArray) {
|
||||
theEventWriter.writeStartObject();
|
||||
}
|
||||
writeExtensionsAsDirectChild(theResource, theEventWriter, theResDef, heldExts, heldModExts, null);
|
||||
theEventWriter.writeEnd();
|
||||
if (inArray) {
|
||||
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) {
|
||||
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);
|
||||
JsonObject alternate = (JsonObject) theAlternateVal;
|
||||
for (Entry<String, JsonValue> nextEntry : alternate.entrySet()) {
|
||||
|
@ -880,7 +901,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
}
|
||||
|
||||
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 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()) {
|
||||
case ARRAY: {
|
||||
JsonArray nextArray = (JsonArray) theJsonVal;
|
||||
|
@ -953,13 +975,13 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
if (nextAlternateArray != null) {
|
||||
nextAlternate = nextAlternateArray.get(i);
|
||||
}
|
||||
parseChildren(theState, theName, nextObject, nextAlternate);
|
||||
parseChildren(theState, theName, nextObject, nextAlternate, theAlternateName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OBJECT: {
|
||||
theState.enteringNewElement(null, theName);
|
||||
parseAlternates(theAlternateVal, theState);
|
||||
parseAlternates(theAlternateVal, theState, theAlternateName);
|
||||
JsonObject nextObject = (JsonObject) theJsonVal;
|
||||
boolean preResource = false;
|
||||
if (theState.isPreResource()) {
|
||||
|
@ -981,7 +1003,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
JsonString nextValStr = (JsonString) theJsonVal;
|
||||
theState.enteringNewElement(null, theName);
|
||||
theState.attributeValue("value", nextValStr.getString());
|
||||
parseAlternates(theAlternateVal, theState);
|
||||
parseAlternates(theAlternateVal, theState, theAlternateName);
|
||||
theState.endingElement();
|
||||
break;
|
||||
}
|
||||
|
@ -990,7 +1012,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
case TRUE:
|
||||
theState.enteringNewElement(null, theName);
|
||||
theState.attributeValue("value", theJsonVal.toString());
|
||||
parseAlternates(theAlternateVal, theState);
|
||||
parseAlternates(theAlternateVal, theState, theAlternateName);
|
||||
theState.endingElement();
|
||||
break;
|
||||
case NULL:
|
||||
|
@ -1015,7 +1037,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
parseExtension(theState, jsonVal, true);
|
||||
} else {
|
||||
JsonValue jsonVal = nextExtObj.get(next);
|
||||
parseChildren(theState, next, jsonVal, null);
|
||||
parseChildren(theState, next, jsonVal, null, null);
|
||||
}
|
||||
}
|
||||
theState.endingElement();
|
||||
|
@ -1039,7 +1061,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
JsonArray arrayValue = (JsonArray) jsonVal;
|
||||
parseExtensionInDstu2Style(theModifier, theState, extUrl, nextKey, arrayValue);
|
||||
} else {
|
||||
parseChildren(theState, nextKey, jsonVal, null);
|
||||
parseChildren(theState, nextKey, jsonVal, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1371,9 +1393,9 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
|
||||
boolean noValue = value == null || value.isEmpty();
|
||||
if (noValue && ext.getAllUndeclaredExtensions().isEmpty()) {
|
||||
|
||||
|
||||
ourLog.debug("Extension with URL[{}] has no value", extensionUrl);
|
||||
|
||||
|
||||
} else if (noValue) {
|
||||
|
||||
BaseRuntimeElementDefinition<?> elemDef = null;
|
||||
|
@ -1381,7 +1403,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
extractAndWriteExtensionsAsDirectChild(ext, theEventWriter, elemDef, resDef, theResource, extensionUrl);
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
RuntimeChildUndeclaredExtensionDefinition extDef = myContext.getRuntimeChildUndeclaredExtensionDefinition();
|
||||
String childName = extDef.getChildNameByDatatype(value.getClass());
|
||||
BaseRuntimeElementDefinition<?> childDef;
|
||||
|
@ -1396,7 +1418,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
childDef = extDef.getChildElementDefinitionByDatatype(value.getClass());
|
||||
}
|
||||
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true);
|
||||
|
||||
|
||||
}
|
||||
|
||||
theEventWriter.writeEnd();
|
||||
|
|
|
@ -815,21 +815,36 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
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 {
|
||||
for (ExtensionDt next : extensions) {
|
||||
for (ExtensionDt next : theExtensions) {
|
||||
theWriter.writeStartElement(tagName);
|
||||
theWriter.writeAttribute("url", next.getUrl().getValue());
|
||||
|
||||
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();
|
||||
String childName = extDef.getChildNameByDatatype(nextValue.getClass());
|
||||
String childName = extDef.getChildNameByDatatype(value.getClass());
|
||||
BaseRuntimeElementDefinition<?> childDef;
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
childDef = extDef.getChildElementDefinitionByDatatype(value.getClass());
|
||||
}
|
||||
BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(nextValue.getClass());
|
||||
encodeChildElementToStreamWriter(theResDef, theResource, theWriter, nextValue, childName, childDef, null, theIncludedResource);
|
||||
encodeChildElementToStreamWriter(theResDef, theResource, theWriter, value, childName, childDef, null, theIncludedResource);
|
||||
}
|
||||
|
||||
// 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"/>
|
||||
</attributes>
|
||||
</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"/>
|
||||
</classpath>
|
||||
|
|
|
@ -2,5 +2,7 @@
|
|||
<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/resources"/>
|
||||
<wb-resource deploy-path="/" source-path="/target/generated-sources/tinder"/>
|
||||
<wb-resource deploy-path="/" source-path="/target/generated-resources/tinder"/>
|
||||
</wb-module>
|
||||
</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;
|
||||
|
||||
public class JpaConformanceProvider extends ServerConformanceProvider {
|
||||
public class JpaConformanceProviderDstu1 extends ServerConformanceProvider {
|
||||
|
||||
private String myImplementationDescription;
|
||||
private IFhirSystemDao mySystemDao;
|
||||
private volatile Conformance myCachedValue;
|
||||
private RestfulServer myRestfulServer;
|
||||
|
||||
public JpaConformanceProvider(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
|
||||
public JpaConformanceProviderDstu1(RestfulServer theRestfulServer, IFhirSystemDao theSystemDao) {
|
||||
super(theRestfulServer);
|
||||
myRestfulServer = theRestfulServer;
|
||||
mySystemDao = theSystemDao;
|
|
@ -41,14 +41,14 @@
|
|||
<dependency>
|
||||
<groupId>com.phloc</groupId>
|
||||
<artifactId>phloc-schematron</artifactId>
|
||||
<version>${phloc_schematron_version}</version>
|
||||
<version>${phloc_schematron_version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.phloc</groupId>
|
||||
<artifactId>phloc-commons</artifactId>
|
||||
<version>${phloc_commons_version}</version>
|
||||
<version>${phloc_commons_version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-test</artifactId>
|
||||
|
@ -151,11 +151,18 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
<dependency>
|
||||
<groupId>commons-dbcp</groupId>
|
||||
<artifactId>commons-dbcp</artifactId>
|
||||
<version>1.4</version>
|
||||
</dependency>
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
<version>2.0.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Only required for CORS support -->
|
||||
<dependency>
|
||||
|
|
|
@ -10,7 +10,8 @@ import org.springframework.web.context.ContextLoaderListener;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
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.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||
|
@ -22,103 +23,96 @@ import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
|
|||
public class TestRestfulServer extends RestfulServer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TestRestfulServer.class);
|
||||
|
||||
private ApplicationContext myAppCtx;
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void initialize() throws ServletException {
|
||||
super.initialize();
|
||||
|
||||
// try {
|
||||
// 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"
|
||||
// );
|
||||
// Get the spring context from the web container (it's declared in web.xml)
|
||||
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";
|
||||
fhirVersionParam = "DSTU1";
|
||||
}
|
||||
|
||||
myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext();
|
||||
|
||||
|
||||
// Depending on the version this server is supporing, we will
|
||||
// retrieve all the appropriate resource providers and the
|
||||
// conformance provider
|
||||
List<IResourceProvider> beans;
|
||||
JpaSystemProvider systemProvider;
|
||||
IFhirSystemDao systemDao;
|
||||
switch (fhirVersionParam.trim().toUpperCase()) {
|
||||
case "DSTU":
|
||||
case "DSTU1":
|
||||
case "DSTU1": {
|
||||
setFhirContext(FhirContext.forDstu1());
|
||||
beans = myAppCtx.getBean("myResourceProvidersDstu1", List.class);
|
||||
systemProvider = myAppCtx.getBean("mySystemProviderDstu1", JpaSystemProvider.class);
|
||||
systemDao = myAppCtx.getBean("mySystemDaoDstu1", IFhirSystemDao.class);
|
||||
JpaConformanceProviderDstu1 confProvider = new JpaConformanceProviderDstu1(this, systemDao);
|
||||
confProvider.setImplementationDescription(implDesc);
|
||||
setServerConformanceProvider(confProvider);
|
||||
break;
|
||||
case "DEV":
|
||||
}
|
||||
case "DEV": {
|
||||
setFhirContext(FhirContext.forDev());
|
||||
beans = myAppCtx.getBean("myResourceProvidersDev", List.class);
|
||||
systemProvider = myAppCtx.getBean("mySystemProviderDev", JpaSystemProvider.class);
|
||||
systemDao = myAppCtx.getBean("mySystemDaoDev", IFhirSystemDao.class);
|
||||
JpaConformanceProviderDev confProvider = new JpaConformanceProviderDev(this, systemDao);
|
||||
confProvider.setImplementationDescription(implDesc);
|
||||
setServerConformanceProvider(confProvider);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new ServletException("Unknown FHIR version specified in init-param[FhirVersion]: " + fhirVersionParam);
|
||||
}
|
||||
|
||||
FhirContext ctx = getFhirContext();
|
||||
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
|
||||
for (IResourceProvider nextResourceProvider : beans) {
|
||||
ourLog.info(" * Have resource provider for: {}", nextResourceProvider.getResourceType().getSimpleName());
|
||||
}
|
||||
setResourceProviders(beans);
|
||||
setPlainProviders(systemProvider);
|
||||
|
||||
String implDesc = getInitParameter("ImplementationDescription");
|
||||
|
||||
JpaConformanceProvider confProvider = new JpaConformanceProvider(this, systemDao);
|
||||
confProvider.setImplementationDescription(implDesc);
|
||||
setServerConformanceProvider(confProvider);
|
||||
|
||||
FhirContext ctx = getFhirContext();
|
||||
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
setUseBrowserFriendlyContentTypes(true);
|
||||
|
||||
|
||||
String baseUrl = System.getProperty("fhir.baseurl");
|
||||
if (StringUtils.isBlank(baseUrl)) {
|
||||
throw new ServletException("Missing system property: fhir.baseurl");
|
||||
}
|
||||
|
||||
|
||||
setServerAddressStrategy(new HardcodedServerAddressStrategy(baseUrl));
|
||||
setPagingProvider(new FifoMemoryPagingProvider(10));
|
||||
|
||||
|
||||
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();
|
||||
loggingInterceptor.setLoggerName("fhirtest.access");
|
||||
loggingInterceptor.setMessageFormat("Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}]");
|
||||
this.registerInterceptor(loggingInterceptor);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
|
||||
// myAppCtx.close();
|
||||
//
|
||||
// try {
|
||||
// ourLog.info("Shutting down derby");
|
||||
// DriverManager.getConnection("jdbc:derby:directory:" + System.getProperty("fhir.db.location") + ";shutdown=true");
|
||||
// } catch (Exception e) {
|
||||
// ourLog.info("Failed to create database: {}",e.getMessage());
|
||||
// }
|
||||
|
||||
// myAppCtx.close();
|
||||
//
|
||||
// try {
|
||||
// ourLog.info("Shutting down derby");
|
||||
// DriverManager.getConnection("jdbc:derby:directory:" + System.getProperty("fhir.db.location") + ";shutdown=true");
|
||||
// } catch (Exception e) {
|
||||
// ourLog.info("Failed to create database: {}",e.getMessage());
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,8 +28,10 @@
|
|||
-->
|
||||
<property name="hibernate.dialect" value="org.hibernate.dialect.DerbyTenSevenDialect" />
|
||||
<property name="hibernate.hbm2ddl.auto" value="update" />
|
||||
<!--
|
||||
<property name="hibernate.connection.username" value="sa" />
|
||||
<property name="hibernate.connection.password" value="" />
|
||||
-->
|
||||
<property name="hibernate.jdbc.batch_size" value="0" />
|
||||
<property name="hibernate.cache.use_minimal_puts" value="false" />
|
||||
<property name="hibernate.show_sql" value="false" />
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<bean id="dbServer" class="ca.uhn.fhirtest.DerbyNetworkServer">
|
||||
</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 -->
|
||||
<!-- <property name="url" value="jdbc:hsqldb:hsql://localhost/uhnfhirdb"/>-->
|
||||
<!-- <property name="url" value="jdbc:derby:directory:#{systemproperties['fhir.db.location']};create=true" /> -->
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
<property name="servers">
|
||||
<list>
|
||||
<value>home , DSTU1 , UHN/HAPI Server (DSTU1 FHIR) , http://fhirtest.uhn.ca/baseDstu1</value>
|
||||
<value>home_dev , DEV , UHN/HAPI Server (DSTU2 FHIR) , http://fhirtest.uhn.ca/baseDev</value>
|
||||
<value>home_dev , DEV , UHN/HAPI Server (DSTU2 FHIR) , http://fhirtest.uhn.ca/baseDev</value>
|
||||
<value>hi , DSTU1 , Health Intersections (DSTU1 FHIR) , http://fhir.healthintersections.com.au/open</value>
|
||||
<value>hidev , DEV , Health Intersections (DSTU2 FHIR) , http://fhir-dev.healthintersections.com.au/open</value>
|
||||
<value>hidev , DEV , Health Intersections (DSTU2 FHIR) , http://fhir-dev.healthintersections.com.au/open</value>
|
||||
<value>furore , DSTU1 , Spark - Furore , http://spark.furore.com/fhir</value>
|
||||
<value>blaze , DSTU1 , Blaze (Orion Health) , https://fhir.orionhealth.com/blaze/fhir</value>
|
||||
<value>oridashi , DSTU1 , Oridashi , http://demo.oridashi.com.au:8190</value>
|
||||
|
|
|
@ -2,3 +2,4 @@
|
|||
/.settings/
|
||||
.classpath
|
||||
.project
|
||||
/target/
|
||||
|
|
|
@ -41,7 +41,7 @@ public class FhirDev implements IFhirVersion {
|
|||
private String myId;
|
||||
|
||||
@Override
|
||||
public Object createServerConformanceProvider(RestfulServer theServer) {
|
||||
public ServerConformanceProvider createServerConformanceProvider(RestfulServer theServer) {
|
||||
return new ServerConformanceProvider(theServer);
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
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.SearchParameter;
|
||||
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.RestfulServer;
|
||||
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.
|
||||
* </p>
|
||||
*/
|
||||
public class ServerConformanceProvider {
|
||||
public class ServerConformanceProvider implements IServerConformanceProvider<Conformance> {
|
||||
|
||||
private boolean myCache = true;
|
||||
private volatile Conformance myConformance;
|
||||
|
@ -90,13 +93,9 @@ public class ServerConformanceProvider {
|
|||
return myPublisher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually create and return the conformance statement
|
||||
*
|
||||
* See the class documentation for an important note if you are extending this class
|
||||
*/
|
||||
@Override
|
||||
@Metadata
|
||||
public Conformance getServerConformance() {
|
||||
public Conformance getServerConformance(HttpServletRequest theRequest) {
|
||||
if (myConformance != null && myCache) {
|
||||
return myConformance;
|
||||
}
|
||||
|
@ -136,7 +135,7 @@ public class ServerConformanceProvider {
|
|||
String resourceName = next.getResourceName();
|
||||
RuntimeResourceDefinition def = myRestfulServer.getFhirContext().getResourceDefinition(resourceName);
|
||||
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>();
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
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.JSONSerializer;
|
||||
import net.sf.json.JsonConfig;
|
||||
|
@ -11,16 +15,17 @@ import org.junit.Test;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
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.dev.resource.MedicationPrescription;
|
||||
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.primitive.BooleanDt;
|
||||
import ca.uhn.fhir.model.primitive.CodeDt;
|
||||
import ca.uhn.fhir.model.primitive.DateDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
|
||||
public class JsonParserTest {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* #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
|
||||
public void testParsePatientInBundle() {
|
||||
|
||||
|
|
|
@ -4,11 +4,16 @@ import static org.hamcrest.Matchers.containsString;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
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.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
|
@ -46,7 +51,7 @@ public class ServerConformanceProviderTest {
|
|||
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(null);
|
||||
rs.init(createServletConfig());
|
||||
|
||||
boolean found=false;
|
||||
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
|
||||
|
@ -60,7 +65,7 @@ public class ServerConformanceProviderTest {
|
|||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
Conformance conformance = sc.getServerConformance();
|
||||
Conformance conformance = sc.getServerConformance(createHttpServletRequest());
|
||||
|
||||
String conf = myCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
@ -80,11 +85,11 @@ public class ServerConformanceProviderTest {
|
|||
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
||||
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);
|
||||
rs.setServerConformanceProvider(sc);
|
||||
|
||||
rs.init(null);
|
||||
rs.init(createServletConfig());
|
||||
|
||||
boolean found=false;
|
||||
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
|
||||
|
@ -113,7 +118,7 @@ public class ServerConformanceProviderTest {
|
|||
}
|
||||
|
||||
assertTrue(found);
|
||||
Conformance conformance = sc.getServerConformance();
|
||||
Conformance conformance = sc.getServerConformance(createHttpServletRequest());
|
||||
String conf = new FhirContext().newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance);
|
||||
ourLog.info(conf);
|
||||
|
||||
|
@ -131,9 +136,9 @@ public class ServerConformanceProviderTest {
|
|||
ServerConformanceProvider sc = new ServerConformanceProvider(rs);
|
||||
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);
|
||||
ourLog.info(conf);
|
||||
|
||||
|
@ -171,7 +176,6 @@ public class ServerConformanceProviderTest {
|
|||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static class MultiOptionalProvider {
|
||||
|
||||
@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
|
||||
.project
|
||||
/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.primitive.IdDt;
|
||||
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.provider.ServerConformanceProvider;
|
||||
import ca.uhn.fhir.rest.server.provider.ServerProfileProvider;
|
||||
|
@ -81,7 +82,7 @@ public class FhirDstu1 implements IFhirVersion {
|
|||
private String myId;
|
||||
|
||||
@Override
|
||||
public Object createServerConformanceProvider(RestfulServer theServer) {
|
||||
public ServerConformanceProvider createServerConformanceProvider(RestfulServer 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.SearchParameter;
|
||||
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.RestfulServer;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
|
@ -71,7 +72,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
* <code>setCache(false)</code> in your provider constructor.
|
||||
* </p>
|
||||
*/
|
||||
public class ServerConformanceProvider {
|
||||
public class ServerConformanceProvider implements IServerConformanceProvider<Conformance> {
|
||||
|
||||
private boolean myCache = true;
|
||||
private volatile Conformance myConformance;
|
||||
|
@ -106,7 +107,7 @@ public class ServerConformanceProvider {
|
|||
|
||||
retVal.setPublisher(myPublisher);
|
||||
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.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.Profile;
|
||||
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.ValueSet;
|
||||
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
|
||||
public void testEncodeNonContained() {
|
||||
Organization org = new Organization();
|
||||
|
@ -193,7 +211,7 @@ public class JsonParserTest {
|
|||
|
||||
encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
|
||||
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
|
||||
ourLog.info("---------------");
|
||||
|
@ -207,7 +225,7 @@ public class JsonParserTest {
|
|||
|
||||
encoded = ourCtx.newJsonParser().setPrettyPrint(false).encodeResourceToString(c);
|
||||
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.jpa.dao.IFhirResourceDao;
|
||||
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.rp.dev.DiagnosticReportResourceProvider;
|
||||
import ca.uhn.fhir.jpa.rp.dev.ObservationResourceProvider;
|
||||
|
|
|
@ -43,7 +43,7 @@ public class FhirDev implements IFhirVersion {
|
|||
private String myId;
|
||||
|
||||
@Override
|
||||
public Object createServerConformanceProvider(RestfulServer theServer) {
|
||||
public ServerConformanceProvider createServerConformanceProvider(RestfulServer theServer) {
|
||||
return new ServerConformanceProvider(theServer);
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ public class FhirDstu1 implements IFhirVersion {
|
|||
private String myId;
|
||||
|
||||
@Override
|
||||
public Object createServerConformanceProvider(RestfulServer theServer) {
|
||||
public ServerConformanceProvider createServerConformanceProvider(RestfulServer 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.SearchParameter;
|
||||
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.RestfulServer;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
|
@ -71,7 +72,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
* <code>setCache(false)</code> in your provider constructor.
|
||||
* </p>
|
||||
*/
|
||||
public class ServerConformanceProvider {
|
||||
public class ServerConformanceProvider implements IServerConformanceProvider<Conformance> {
|
||||
|
||||
private boolean myCache = true;
|
||||
private volatile Conformance myConformance;
|
||||
|
|
|
@ -28,6 +28,8 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
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.SearchParameter;
|
||||
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.RestfulServer;
|
||||
import ca.uhn.fhir.util.ExtensionConstants;
|
||||
|
@ -68,7 +71,7 @@ import ca.uhn.fhir.util.ExtensionConstants;
|
|||
* <code>setCache(false)</code> in your provider constructor.
|
||||
* </p>
|
||||
*/
|
||||
public class ServerConformanceProvider {
|
||||
public class ServerConformanceProvider implements IServerConformanceProvider<Conformance> {
|
||||
|
||||
private boolean myCache = true;
|
||||
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
|
||||
*/
|
||||
@Metadata
|
||||
public Conformance getServerConformance() {
|
||||
public Conformance getServerConformance(HttpServletRequest theReq) {
|
||||
if (myConformance != null && myCache) {
|
||||
return myConformance;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,13 @@
|
|||
Support for DSTU2 features introduced: New Bundle encoding style, as well as new
|
||||
extension encoding in JSON.
|
||||
</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">
|
||||
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
|
||||
|
|
|
@ -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