Merge branch 'master' of github.com:jamesagnew/hapi-fhir
This commit is contained in:
commit
714adcd4be
|
@ -10,6 +10,8 @@ import java.util.Set;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
|
@ -108,6 +110,23 @@ public List<Organization> getAllOrganizations() {
|
|||
}
|
||||
//END SNIPPET: searchAll
|
||||
|
||||
//START SNIPPET: updateEtag
|
||||
@Update
|
||||
public MethodOutcome update(@IdParam IdType theId, @ResourceParam Patient thePatient) {
|
||||
String resourceId = theId.getIdPart();
|
||||
String versionId = theId.getVersionIdPart(); // this will contain the ETag
|
||||
|
||||
String currentVersion = "1"; // populate this with the current version
|
||||
|
||||
if (!versionId.equals(currentVersion)) {
|
||||
throw new ResourceVersionConflictException("Expected version " + currentVersion);
|
||||
}
|
||||
|
||||
// ... perform the update ...
|
||||
return new MethodOutcome();
|
||||
|
||||
}
|
||||
//END SNIPPET: updateEtag
|
||||
|
||||
//START SNIPPET: summaryAndElements
|
||||
@Search
|
||||
|
|
|
@ -35,10 +35,6 @@
|
|||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.codehaus.woodstox</groupId>
|
||||
<artifactId>woodstox-core-asl</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -59,6 +55,11 @@
|
|||
<version>2.1-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.woodstox</groupId>
|
||||
<artifactId>woodstox-core-asl</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient-android</artifactId>
|
||||
|
@ -75,10 +76,6 @@
|
|||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
|
@ -98,25 +95,59 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
<!--
|
||||
<classpathDependencyScopeExclude>compile+runtime+test+provided</classpathDependencyScopeExclude>
|
||||
<additionalClasspathElements>
|
||||
<additionalClasspathElement>${project.build.directory}/hapi-fhir-android-${project.version}-dstu2.jar</additionalClasspathElement>
|
||||
</additionalClasspathElements>
|
||||
<classpathDependencyExcludes>
|
||||
<classpathDependencyExclude>ca.uhn.hapi.fhir:hapi-fhir-base</classpathDependencyExclude>
|
||||
<classpathDependencyExclude>org.codehaus.woodstox:*</classpathDependencyExclude>
|
||||
<classpathDependencyExclude>javax.json:*</classpathDependencyExclude>
|
||||
<classpathDependencyExclude>org.glassfish:javax.json</classpathDependencyExclude>
|
||||
</classpathDependencyExcludes>
|
||||
-->
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>dstu2_shade</id>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Dstu2ShadeIT.java</include>
|
||||
</includes>
|
||||
<additionalClasspathElements>
|
||||
<additionalClasspathElement>${basedir}/target/hapi-fhir-android-${project.version}-dstu2.jar</additionalClasspathElement>
|
||||
</additionalClasspathElements>
|
||||
<classpathDependencyExcludes>
|
||||
<classpathDependencyExclude>ca.uhn.hapi.fhir:*</classpathDependencyExclude>
|
||||
<classpathDependencyExclude>org.codehaus.woodstox:woodstox-core-asl</classpathDependencyExclude>
|
||||
<classpathDependencyExclude>org.codehaus.woodstox:stax2-api</classpathDependencyExclude>
|
||||
</classpathDependencyExcludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>dstu2</id>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Dstu2IT.java</include>
|
||||
</includes>
|
||||
<classpathDependencyExcludes>
|
||||
<classpathDependencyExclude>org.codehaus.woodstox:woodstox-core-asl</classpathDependencyExclude>
|
||||
<classpathDependencyExclude>org.codehaus.woodstox:stax2-api</classpathDependencyExclude>
|
||||
</classpathDependencyExcludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>dstu3</id>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Dstu3IT.java</include>
|
||||
</includes>
|
||||
<classpathDependencyExcludes>
|
||||
<classpathDependencyExclude>org.codehaus.woodstox:woodstox-core-asl</classpathDependencyExclude>
|
||||
<classpathDependencyExclude>org.codehaus.woodstox:stax2-api</classpathDependencyExclude>
|
||||
</classpathDependencyExcludes>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
@ -130,6 +161,9 @@
|
|||
<artifactSet>
|
||||
<includes combine.children="append">
|
||||
<include>ca.uhn.hapi.fhir:hapi-fhir-base</include>
|
||||
<include>org.codehaus.woodstox:woodstox-core-asl</include>
|
||||
<include>javax.xml.stream:stax-api</include>
|
||||
<include>org.codehaus.woodstox:stax2-api</include>
|
||||
</includes>
|
||||
</artifactSet>
|
||||
<relocations>
|
||||
|
@ -141,6 +175,10 @@
|
|||
<pattern>javax.json</pattern>
|
||||
<shadedPattern>ca.uhn.fhir.repackage.javax.json</shadedPattern>
|
||||
</relocation>
|
||||
<relocation>
|
||||
<pattern>com.ctc.wstx.stax</pattern>
|
||||
<shadedPattern>ca.uhn.fhir.repackage.com.ctc.wstx.stax</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<filters combine.children="append">
|
||||
<!-- Exclude server side stuff, except exceptions which are used clientside -->
|
||||
|
@ -182,8 +220,8 @@
|
|||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</goals>
|
||||
</execution>
|
||||
<!-- dstu jar -->
|
||||
<execution>
|
||||
<id>dstu</id>
|
||||
|
@ -261,6 +299,27 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<classFolders>
|
||||
<classFolder>${basedir}/target/classes</classFolder>
|
||||
</classFolders>
|
||||
<sourceFolders>
|
||||
<sourceFolder>${basedir}/src/main/java</sourceFolder>
|
||||
</sourceFolders>
|
||||
<dumpOnExit>true</dumpOnExit>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-prepare-agent</id>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
package ca.uhn.fhir.android;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.filefilter.WildcardFileFilter;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.dstu2.composite.QuantityDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
|
||||
|
||||
public class BuiltJarDstu2IT {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BuiltJarDstu2IT.class);
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
System.setProperty("javax.xml.stream.XMLInputFactory", "FOO");
|
||||
System.setProperty("javax.xml.stream.XMLOutputFactory", "FOO");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParserXml() throws Exception {
|
||||
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("system");
|
||||
|
||||
try {
|
||||
ctx.newXmlParser().encodeResourceToString(p);
|
||||
fail();
|
||||
} catch (ca.uhn.fhir.context.ConfigurationException e) {
|
||||
assertEquals("Unable to initialize StAX - XML processing is disabled",e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParserJson() {
|
||||
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
|
||||
Observation o = new Observation();
|
||||
o.getCode().setText("TEXT");
|
||||
o.setValue(new QuantityDt(123));
|
||||
o.addIdentifier().setSystem("system");
|
||||
|
||||
String str = ctx.newJsonParser().encodeResourceToString(o);
|
||||
Observation p2 = ctx.newJsonParser().parseResource(Observation.class, str);
|
||||
|
||||
assertEquals("TEXT", p2.getCode().getText());
|
||||
|
||||
QuantityDt dt = (QuantityDt) p2.getValue();
|
||||
dt.getComparatorElement().getValueAsEnum();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple client test - We try to connect to a server that doesn't exist, but
|
||||
* if we at least get the right exception it means we made it up to the HTTP/network stack
|
||||
*
|
||||
* Disabled for now - TODO: add the old version of the apache client (the one that
|
||||
* android uses) and see if this passes
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void testClient() {
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
try {
|
||||
IGenericClient client = ctx.newRestfulGenericClient("http://127.0.0.1:44442/SomeBase");
|
||||
client.conformance();
|
||||
} catch (FhirClientConnectionException e) {
|
||||
// this is good
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Android does not like duplicate entries in the JAR
|
||||
*/
|
||||
@Test
|
||||
public void testJarContents() throws Exception {
|
||||
String wildcard = "hapi-fhir-android-*.jar";
|
||||
Collection<File> files = FileUtils.listFiles(new File("target"), new WildcardFileFilter(wildcard), null);
|
||||
if (files.isEmpty()) {
|
||||
throw new Exception("No files matching " + wildcard);
|
||||
}
|
||||
|
||||
for (File file : files) {
|
||||
if (file.getName().endsWith("sources.jar")) {
|
||||
continue;
|
||||
}
|
||||
if (file.getName().endsWith("javadoc.jar")) {
|
||||
continue;
|
||||
}
|
||||
if (file.getName().contains("original.jar")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ourLog.info("Testing file: {}", file);
|
||||
|
||||
ZipFile zip = new ZipFile(file);
|
||||
|
||||
int totalClasses = 0;
|
||||
int totalMethods = 0;
|
||||
TreeSet<ClassMethodCount> topMethods = new TreeSet<ClassMethodCount>();
|
||||
|
||||
try {
|
||||
Set<String> names = new HashSet<String>();
|
||||
for (Enumeration<? extends ZipEntry> iter = zip.entries(); iter.hasMoreElements();) {
|
||||
ZipEntry next = iter.nextElement();
|
||||
String nextName = next.getName();
|
||||
if (!names.add(nextName)) {
|
||||
throw new Exception("File " + file + " contains duplicate contents: " + nextName);
|
||||
}
|
||||
|
||||
if (nextName.contains("$") == false) {
|
||||
if (nextName.endsWith(".class")) {
|
||||
String className = nextName.replace("/", ".").replace(".class", "");
|
||||
try {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
int methodCount = clazz.getMethods().length;
|
||||
topMethods.add(new ClassMethodCount(className, methodCount));
|
||||
totalClasses++;
|
||||
totalMethods += methodCount;
|
||||
} catch (NoClassDefFoundError e) {
|
||||
// ignore
|
||||
} catch (ClassNotFoundException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("File {} contains {} entries", file, names.size());
|
||||
ourLog.info("Total classes {} - Total methods {}", totalClasses, totalMethods);
|
||||
ourLog.info("Top classes {}", new ArrayList<ClassMethodCount>(topMethods).subList(Math.max(0, topMethods.size() - 10), topMethods.size()));
|
||||
|
||||
} finally {
|
||||
zip.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClassMethodCount implements Comparable<ClassMethodCount> {
|
||||
|
||||
private String myClassName;
|
||||
private int myMethodCount;
|
||||
|
||||
public ClassMethodCount(String theClassName, int theMethodCount) {
|
||||
myClassName = theClassName;
|
||||
myMethodCount = theMethodCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myClassName + "[" + myMethodCount + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ClassMethodCount theO) {
|
||||
return myMethodCount - theO.myMethodCount;
|
||||
}
|
||||
|
||||
public String getClassName() {
|
||||
return myClassName;
|
||||
}
|
||||
|
||||
public void setClassName(String theClassName) {
|
||||
myClassName = theClassName;
|
||||
}
|
||||
|
||||
public int getMethodCount() {
|
||||
return myMethodCount;
|
||||
}
|
||||
|
||||
public void setMethodCount(int theMethodCount) {
|
||||
myMethodCount = theMethodCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -19,52 +19,41 @@ import ca.uhn.fhir.model.dstu2.resource.Patient;
|
|||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
|
||||
|
||||
public class BuiltJarIT {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BuiltJarIT.class);
|
||||
public class BuiltJarDstu2ShadeIT {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BuiltJarDstu2ShadeIT.class);
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
System.setProperty("javax.xml.stream.XMLInputFactory", "com.ctc.wstx.stax.WstxInputFactory");
|
||||
System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParserXml() throws Exception {
|
||||
try {
|
||||
Class.forName("com.ctc.wstx.stax.WstxOutputFactory");
|
||||
} catch (ClassNotFoundException e) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
|
||||
|
||||
Patient p = new Patient();
|
||||
p.addIdentifier().setSystem("system");
|
||||
|
||||
|
||||
String str = ctx.newXmlParser().encodeResourceToString(p);
|
||||
Patient p2 = ctx.newXmlParser().parseResource(Patient.class, str);
|
||||
|
||||
|
||||
assertEquals("system", p2.getIdentifierFirstRep().getSystemElement().getValueAsString());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testParserJson() {
|
||||
|
||||
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
|
||||
|
||||
Observation o = new Observation();
|
||||
o.getCode().setText("TEXT");
|
||||
o.setValue(new QuantityDt(123));
|
||||
o.addIdentifier().setSystem("system");
|
||||
|
||||
|
||||
String str = ctx.newJsonParser().encodeResourceToString(o);
|
||||
Observation p2 = ctx.newJsonParser().parseResource(Observation.class, str);
|
||||
|
||||
|
||||
assertEquals("TEXT", p2.getCode().getText());
|
||||
|
||||
|
||||
QuantityDt dt = (QuantityDt) p2.getValue();
|
||||
dt.getComparatorElement().getValueAsEnum();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,7 +72,7 @@ public class BuiltJarIT {
|
|||
// this is good
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Android does not like duplicate entries in the JAR
|
||||
*/
|
||||
|
@ -105,15 +94,15 @@ public class BuiltJarIT {
|
|||
if (file.getName().contains("original.jar")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
ourLog.info("Testing file: {}", file);
|
||||
|
||||
ZipFile zip = new ZipFile(file);
|
||||
|
||||
|
||||
int totalClasses = 0;
|
||||
int totalMethods = 0;
|
||||
TreeSet<ClassMethodCount> topMethods = new TreeSet<ClassMethodCount>();
|
||||
|
||||
|
||||
try {
|
||||
Set<String> names = new HashSet<String>();
|
||||
for (Enumeration<? extends ZipEntry> iter = zip.entries(); iter.hasMoreElements();) {
|
||||
|
@ -122,16 +111,16 @@ public class BuiltJarIT {
|
|||
if (!names.add(nextName)) {
|
||||
throw new Exception("File " + file + " contains duplicate contents: " + nextName);
|
||||
}
|
||||
|
||||
|
||||
if (nextName.contains("$") == false) {
|
||||
if (nextName.endsWith(".class")) {
|
||||
String className = nextName.replace("/", ".").replace(".class", "");
|
||||
try {
|
||||
Class<?> clazz = Class.forName(className);
|
||||
int methodCount = clazz.getMethods().length;
|
||||
topMethods.add(new ClassMethodCount(className, methodCount));
|
||||
totalClasses++;
|
||||
totalMethods += methodCount;
|
||||
Class<?> clazz = Class.forName(className);
|
||||
int methodCount = clazz.getMethods().length;
|
||||
topMethods.add(new ClassMethodCount(className, methodCount));
|
||||
totalClasses++;
|
||||
totalMethods += methodCount;
|
||||
} catch (NoClassDefFoundError e) {
|
||||
// ignore
|
||||
} catch (ClassNotFoundException e) {
|
||||
|
@ -140,11 +129,11 @@ public class BuiltJarIT {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ourLog.info("File {} contains {} entries", file, names.size());
|
||||
ourLog.info("Total classes {} - Total methods {}", totalClasses, totalMethods);
|
||||
ourLog.info("Top classes {}", new ArrayList<ClassMethodCount>(topMethods).subList(Math.max(0,topMethods.size() - 10), topMethods.size()));
|
||||
|
||||
ourLog.info("Top classes {}", new ArrayList<ClassMethodCount>(topMethods).subList(Math.max(0, topMethods.size() - 10), topMethods.size()));
|
||||
|
||||
} finally {
|
||||
zip.close();
|
||||
}
|
||||
|
@ -155,7 +144,7 @@ public class BuiltJarIT {
|
|||
|
||||
private String myClassName;
|
||||
private int myMethodCount;
|
||||
|
||||
|
||||
public ClassMethodCount(String theClassName, int theMethodCount) {
|
||||
myClassName = theClassName;
|
||||
myMethodCount = theMethodCount;
|
||||
|
@ -186,7 +175,7 @@ public class BuiltJarIT {
|
|||
public void setMethodCount(int theMethodCount) {
|
||||
myMethodCount = theMethodCount;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,996 @@
|
|||
package ca.uhn.fhir.android.client;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.input.ReaderInputStream;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.ProtocolVersion;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicStatusLine;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
|
||||
import org.junit.*;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
|
||||
import ca.uhn.fhir.model.primitive.DateTimeDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
|
||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||
import ca.uhn.fhir.rest.client.interceptor.UserInfoInterceptor;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import ca.uhn.fhir.util.VersionUtil;
|
||||
|
||||
public class GenericClientDstu3IT {
|
||||
|
||||
|
||||
private static FhirContext ourCtx;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu3IT.class);
|
||||
private int myAnswerCount;
|
||||
private HttpClient myHttpClient;
|
||||
private HttpResponse myHttpResponse;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
|
||||
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
|
||||
myAnswerCount = 0;
|
||||
|
||||
}
|
||||
|
||||
private String expectedUserAgent() {
|
||||
return "HAPI-FHIR/" + VersionUtil.getVersion() + " (FHIR Client; FHIR " + FhirVersionEnum.DSTU3.getFhirVersionString() + "/DSTU3; apache)";
|
||||
}
|
||||
|
||||
|
||||
private String extractBodyAsString(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
|
||||
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent(), "UTF-8");
|
||||
return body;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: narratives don't work without stax
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testBinaryCreateWithFhirContentType() throws Exception {
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
|
||||
OperationOutcome conf = new OperationOutcome();
|
||||
conf.getText().setDivAsString("OK!");
|
||||
|
||||
final String respString = p.encodeResourceToString(conf);
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.getText().setDivAsString("A PATIENT");
|
||||
|
||||
Binary bin = new Binary();
|
||||
bin.setContent(ourCtx.newJsonParser().encodeResourceToString(pt).getBytes("UTF-8"));
|
||||
bin.setContentType(Constants.CT_FHIR_JSON);
|
||||
client.create().resource(bin).execute();
|
||||
|
||||
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
|
||||
|
||||
assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
validateUserAgent(capt);
|
||||
|
||||
assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||
Binary output = ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt));
|
||||
assertEquals(Constants.CT_FHIR_JSON, output.getContentType());
|
||||
|
||||
Patient outputPt = (Patient) ourCtx.newJsonParser().parseResource(new String(output.getContent(), "UTF-8"));
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">A PATIENT</div>", outputPt.getText().getDivAsString());
|
||||
}
|
||||
|
||||
/**
|
||||
* See #150
|
||||
*/
|
||||
@Test
|
||||
public void testNullAndEmptyParamValuesAreIgnored() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = prepareClientForSearchResponse();
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
client
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.FAMILY.matches().value((String)null))
|
||||
.and(Patient.BIRTHDATE.exactly().day((Date)null))
|
||||
.and(Patient.GENDER.exactly().code((String)null))
|
||||
.and(Patient.ORGANIZATION.hasId((String)null))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
|
||||
assertEquals("http://example.com/fhir/Patient?_format=json", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TODO: narratives don't work without stax
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testBinaryCreateWithNoContentType() throws Exception {
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
|
||||
OperationOutcome conf = new OperationOutcome();
|
||||
conf.getText().setDivAsString("OK!");
|
||||
|
||||
final String respString = p.encodeResourceToString(conf);
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
Binary bin = new Binary();
|
||||
bin.setContent(new byte[] { 0, 1, 2, 3, 4 });
|
||||
client.create().resource(bin).execute();
|
||||
|
||||
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
|
||||
|
||||
assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
validateUserAgent(capt);
|
||||
|
||||
assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testClientFailures() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenThrow(IllegalStateException.class, RuntimeException.class, Exception.class);
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
try {
|
||||
client.read().resource(Patient.class).withId("1").execute();
|
||||
fail();
|
||||
} catch (FhirClientConnectionException e) {
|
||||
assertEquals("java.lang.IllegalStateException", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
client.read().resource(Patient.class).withId("1").execute();
|
||||
fail();
|
||||
} catch (RuntimeException e) {
|
||||
assertEquals("java.lang.RuntimeException", e.toString());
|
||||
}
|
||||
|
||||
try {
|
||||
client.read().resource(Patient.class).withId("1").execute();
|
||||
fail();
|
||||
} catch (FhirClientConnectionException e) {
|
||||
assertEquals("java.lang.Exception", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TODO: narratives don't work without stax
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testCreateWithPreferRepresentationServerReturnsResource() throws Exception {
|
||||
final IParser p = ourCtx.newJsonParser();
|
||||
|
||||
final Patient resp1 = new Patient();
|
||||
resp1.getText().setDivAsString("FINAL VALUE");
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
||||
@Override
|
||||
public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
|
||||
}
|
||||
});
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
myAnswerCount++;
|
||||
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.getText().setDivAsString("A PATIENT");
|
||||
|
||||
MethodOutcome outcome = client.create().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute();
|
||||
|
||||
assertEquals(1, myAnswerCount);
|
||||
assertNull(outcome.getOperationOutcome());
|
||||
assertNotNull(outcome.getResource());
|
||||
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
|
||||
|
||||
assertEquals(myAnswerCount, capt.getAllValues().size());
|
||||
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testForceConformance() throws Exception {
|
||||
final IParser p = ourCtx.newJsonParser();
|
||||
|
||||
final Conformance conf = new Conformance();
|
||||
conf.setCopyright("COPY");
|
||||
|
||||
final Patient patient = new Patient();
|
||||
patient.addName().addFamily("FAM");
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
private int myCount = 0;
|
||||
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
final String respString;
|
||||
if (myCount == 1 || myCount == 2) {
|
||||
ourLog.info("Encoding patient");
|
||||
respString = p.encodeResourceToString(patient);
|
||||
} else {
|
||||
ourLog.info("Encoding conformance");
|
||||
respString = p.encodeResourceToString(conf);
|
||||
}
|
||||
myCount++;
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://testForceConformance.com/fhir");
|
||||
|
||||
client.read().resource("Patient").withId("1").execute();
|
||||
assertEquals(2, capt.getAllValues().size());
|
||||
assertEquals("http://testForceConformance.com/fhir/metadata?_format=json", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals("http://testForceConformance.com/fhir/Patient/1?_format=json", capt.getAllValues().get(1).getURI().toASCIIString());
|
||||
|
||||
client.read().resource("Patient").withId("1").execute();
|
||||
assertEquals(3, capt.getAllValues().size());
|
||||
assertEquals("http://testForceConformance.com/fhir/Patient/1?_format=json", capt.getAllValues().get(2).getURI().toASCIIString());
|
||||
|
||||
client.forceConformanceCheck();
|
||||
assertEquals(4, capt.getAllValues().size());
|
||||
assertEquals("http://testForceConformance.com/fhir/metadata?_format=json", capt.getAllValues().get(3).getURI().toASCIIString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttp499() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 499, "Wacky Message"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader("HELLO"), StandardCharsets.UTF_8);
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
try {
|
||||
client.read().resource(Patient.class).withId("1").execute();
|
||||
fail();
|
||||
} catch (UnclassifiedServerFailureException e) {
|
||||
assertEquals("ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException: HTTP 499 Wacky Message", e.toString());
|
||||
assertEquals("HELLO", e.getResponseBody());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttp501() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 501, "Not Implemented"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader("not implemented"), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
try {
|
||||
client.read().resource(Patient.class).withId("1").execute();
|
||||
fail();
|
||||
} catch (NotImplementedOperationException e) {
|
||||
assertEquals("HTTP 501 Not Implemented", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testInvalidConformanceCall() {
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
try {
|
||||
client.conformance();
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Must call fetchConformance() instead of conformance() for RI/STU3+ structures", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testPutDoesntForceAllIdsJson() throws Exception {
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setId("PATIENT1");
|
||||
patient.addName().addFamily("PATIENT1");
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.setId("BUNDLE1");
|
||||
bundle.addEntry().setResource(patient);
|
||||
|
||||
final String encoded = p.encodeResourceToString(bundle);
|
||||
assertEquals("{\"resourceType\":\"Bundle\",\"id\":\"BUNDLE1\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"PATIENT1\",\"name\":[{\"family\":[\"PATIENT1\"]}]}}]}", encoded);
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(encoded), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
//@formatter:off
|
||||
client
|
||||
.update()
|
||||
.resource(bundle)
|
||||
.prefer(PreferReturnEnum.REPRESENTATION)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
//@formatter:on
|
||||
|
||||
HttpPut httpRequest = (HttpPut) capt.getValue();
|
||||
assertEquals("http://example.com/fhir/Bundle/BUNDLE1?_format=json", httpRequest.getURI().toASCIIString());
|
||||
|
||||
String requestString = IOUtils.toString(httpRequest.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertEquals(encoded, requestString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseHasContentTypeMissing() throws Exception {
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
Patient patient = new Patient();
|
||||
patient.addName().addFamily("FAM");
|
||||
final String respString = p.encodeResourceToString(patient);
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(null);
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
try {
|
||||
client.read().resource(Patient.class).withId("1").execute();
|
||||
fail();
|
||||
} catch (NonFhirResponseException e) {
|
||||
assertEquals("Response contains no Content-Type", e.getMessage());
|
||||
}
|
||||
|
||||
// Patient resp = client.read().resource(Patient.class).withId("1").execute();
|
||||
// assertEquals("FAM", resp.getNameFirstRep().getFamilyAsSingleString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseHasContentTypeNonFhir() throws Exception {
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
Patient patient = new Patient();
|
||||
patient.addName().addFamily("FAM");
|
||||
final String respString = p.encodeResourceToString(patient);
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", "text/plain"));
|
||||
// when(myHttpResponse.getEntity().getContentType()).thenReturn(null);
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
try {
|
||||
client.read().resource(Patient.class).withId("1").execute();
|
||||
fail();
|
||||
} catch (NonFhirResponseException e) {
|
||||
assertEquals("Response contains non FHIR Content-Type 'text/plain' : {\"resourceType\":\"Patient\",\"name\":[{\"family\":[\"FAM\"]}]}", e.getMessage());
|
||||
}
|
||||
|
||||
// Patient resp = client.read().resource(Patient.class).withId("1").execute();
|
||||
// assertEquals("FAM", resp.getNameFirstRep().getFamilyAsSingleString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByDate() throws Exception {
|
||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
int idx = 0;
|
||||
|
||||
DateTimeDt now = DateTimeDt.withCurrentTime();
|
||||
String dateString = now.getValueAsString().substring(0, 10);
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.BIRTHDATE.after().day(dateString))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient?birthdate=gt"+dateString + "&_format=json", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchByString() throws Exception {
|
||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.NAME.matches().value("AAA"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient?name=AAA&_format=json", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.NAME.matches().value(new StringDt("AAA")))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient?name=AAA&_format=json", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.NAME.matches().values("AAA", "BBB"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient?name=AAA,BBB&_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.NAME.matches().values(Arrays.asList("AAA", "BBB")))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient?name=AAA,BBB&_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.NAME.matchesExactly().value("AAA"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient?name%3Aexact=AAA&_format=json", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.NAME.matchesExactly().value(new StringDt("AAA")))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Patient?name%3Aexact=AAA&_format=json", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByUrl() throws Exception {
|
||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Device")
|
||||
.where(Device.URL.matches().value("http://foo.com"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Device?url=http://foo.com&_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
|
||||
idx++;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptHeaderWithEncodingSpecified() throws Exception {
|
||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
client.setEncoding(EncodingEnum.JSON);
|
||||
client.search()
|
||||
.forResource("Device")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Device?_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
|
||||
assertEquals("application/fhir+json;q=1.0, application/json+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.setEncoding(EncodingEnum.XML);
|
||||
client.search()
|
||||
.forResource("Device")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Device?_format=xml", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
|
||||
assertEquals("application/fhir+xml;q=1.0, application/xml+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
|
||||
idx++;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchForUnknownType() throws Exception {
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
try {
|
||||
client.search(new UriDt("?aaaa"));
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Unable to determine the resource type from the given URI: ?aaaa", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private ArgumentCaptor<HttpUriRequest> prepareClientForSearchResponse() throws IOException, ClientProtocolException {
|
||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
return capt;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testTransactionWithInvalidBody() throws Exception {
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
// Transaction
|
||||
try {
|
||||
client.transaction().withBundle("FOO");
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage());
|
||||
}
|
||||
|
||||
// Create
|
||||
try {
|
||||
client.create().resource("FOO").execute();
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage());
|
||||
}
|
||||
|
||||
// Update
|
||||
try {
|
||||
client.update().resource("FOO").execute();
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage());
|
||||
}
|
||||
|
||||
// Validate
|
||||
try {
|
||||
client.validate().resource("FOO").execute();
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("Unable to determing encoding of request (body does not appear to be valid XML or JSON)", e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: narratives don't work without stax
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testUpdateById() throws Exception {
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
|
||||
OperationOutcome conf = new OperationOutcome();
|
||||
conf.getText().setDivAsString("OK!");
|
||||
|
||||
final String respString = p.encodeResourceToString(conf);
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.setId("222");
|
||||
pt.getText().setDivAsString("A PATIENT");
|
||||
|
||||
client.update().resource(pt).withId("111").execute();
|
||||
|
||||
ourLog.info(Arrays.asList(capt.getAllValues().get(0).getAllHeaders()).toString());
|
||||
|
||||
assertEquals("http://example.com/fhir/Patient/111", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
validateUserAgent(capt);
|
||||
|
||||
assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||
String body = extractBodyAsString(capt);
|
||||
assertThat(body, containsString("<id value=\"111\"/>"));
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: narratives don't work without stax
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testUpdateWithPreferRepresentationServerReturnsOO() throws Exception {
|
||||
final IParser p = ourCtx.newJsonParser();
|
||||
|
||||
final OperationOutcome resp0 = new OperationOutcome();
|
||||
resp0.getText().setDivAsString("OK!");
|
||||
|
||||
final Patient resp1 = new Patient();
|
||||
resp1.getText().setDivAsString("FINAL VALUE");
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
||||
@Override
|
||||
public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
|
||||
}
|
||||
});
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
if (myAnswerCount++ == 0) {
|
||||
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
|
||||
} else {
|
||||
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.setId("Patient/222");
|
||||
pt.getText().setDivAsString("A PATIENT");
|
||||
|
||||
MethodOutcome outcome = client.update().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute();
|
||||
|
||||
assertEquals(2, myAnswerCount);
|
||||
assertNotNull(outcome.getOperationOutcome());
|
||||
assertNotNull(outcome.getResource());
|
||||
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div>", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString());
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
|
||||
|
||||
assertEquals(myAnswerCount, capt.getAllValues().size());
|
||||
assertEquals("http://example.com/fhir/Patient/222", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals("http://foo.com/base/Patient/222/_history/3", capt.getAllValues().get(1).getURI().toASCIIString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithPreferRepresentationServerReturnsResource() throws Exception {
|
||||
final IParser p = ourCtx.newJsonParser();
|
||||
|
||||
final Patient resp1 = new Patient();
|
||||
resp1.setActive(true);
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
||||
@Override
|
||||
public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new Header[] { new BasicHeader(Constants.HEADER_LOCATION, "http://foo.com/base/Patient/222/_history/3") };
|
||||
}
|
||||
});
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
myAnswerCount++;
|
||||
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.setId("Patient/222");
|
||||
pt.getText().setDivAsString("A PATIENT");
|
||||
|
||||
MethodOutcome outcome = client.update().resource(pt).prefer(PreferReturnEnum.REPRESENTATION).execute();
|
||||
|
||||
assertEquals(1, myAnswerCount);
|
||||
assertNull(outcome.getOperationOutcome());
|
||||
assertNotNull(outcome.getResource());
|
||||
|
||||
assertEquals(true, ((Patient) outcome.getResource()).getActive());
|
||||
|
||||
assertEquals(myAnswerCount, capt.getAllValues().size());
|
||||
assertEquals("http://example.com/fhir/Patient/222?_format=json", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUserAgentForConformance() throws Exception {
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
|
||||
Conformance conf = new Conformance();
|
||||
conf.setCopyright("COPY");
|
||||
|
||||
final String respString = p.encodeResourceToString(conf);
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
client.fetchConformance().ofType(Conformance.class).execute();
|
||||
assertEquals("http://example.com/fhir/metadata?_format=json", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
validateUserAgent(capt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TODO: narratives don't work without stax
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void testValidate() throws Exception {
|
||||
final IParser p = ourCtx.newXmlParser();
|
||||
|
||||
final OperationOutcome resp0 = new OperationOutcome();
|
||||
resp0.getText().setDivAsString("OK!");
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getAllHeaders()).thenAnswer(new Answer<Header[]>() {
|
||||
@Override
|
||||
public Header[] answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new Header[] {};
|
||||
}
|
||||
});
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp0)), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.setId("Patient/222");
|
||||
pt.getText().setDivAsString("A PATIENT");
|
||||
|
||||
MethodOutcome outcome = client.validate().resource(pt).execute();
|
||||
|
||||
assertNotNull(outcome.getOperationOutcome());
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK!</div>", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString());
|
||||
|
||||
}
|
||||
|
||||
private void validateUserAgent(ArgumentCaptor<HttpUriRequest> capt) {
|
||||
assertEquals(1, capt.getAllValues().get(0).getHeaders("User-Agent").length);
|
||||
assertEquals(expectedUserAgent(), capt.getAllValues().get(0).getHeaders("User-Agent")[0].getValue());
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
|
||||
// Force StAX to fail like it will on android
|
||||
System.setProperty("javax.xml.stream.XMLInputFactory", "FOO");
|
||||
System.setProperty("javax.xml.stream.XMLOutputFactory", "FOO");
|
||||
|
||||
ourCtx = FhirContext.forDstu3();
|
||||
}
|
||||
|
||||
}
|
|
@ -30,7 +30,7 @@
|
|||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
<attribute name="owner.project.facets" value="java"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
|
|
|
@ -6,13 +6,8 @@ org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annota
|
|||
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
|
||||
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.doc.comment.support=enabled
|
||||
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
|
||||
<!-- JSON -->
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.7</version>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.7</version>
|
||||
</dependency>
|
||||
|
||||
<!-- XML -->
|
||||
|
|
|
@ -29,69 +29,23 @@ import java.io.PushbackReader;
|
|||
import java.io.Reader;
|
||||
import java.io.Writer;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBooleanDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseDecimalDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseExtension;
|
||||
import org.hl7.fhir.instance.model.api.IBaseHasExtensions;
|
||||
import org.hl7.fhir.instance.model.api.IBaseHasModifierExtensions;
|
||||
import org.hl7.fhir.instance.model.api.IBaseIntegerDatatype;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IDomainResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.INarrative;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonNull;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.google.gson.*;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition.ChildTypeEnum;
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.context.RuntimeChildContainedResources;
|
||||
import ca.uhn.fhir.context.RuntimeChildDeclaredExtensionDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeChildNarrativeDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeChildUndeclaredExtensionDefinition;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.BaseBundle;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.BundleEntry;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
import ca.uhn.fhir.model.api.IPrimitiveDatatype;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ISupportsUndeclaredExtensions;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.Tag;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.api.*;
|
||||
import ca.uhn.fhir.model.api.annotation.Child;
|
||||
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
|
||||
import ca.uhn.fhir.model.base.composite.BaseContainedDt;
|
||||
import ca.uhn.fhir.model.primitive.DecimalDt;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.IntegerDt;
|
||||
import ca.uhn.fhir.model.primitive.StringDt;
|
||||
import ca.uhn.fhir.model.primitive.*;
|
||||
import ca.uhn.fhir.narrative.INarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.util.ElementUtil;
|
||||
|
@ -170,6 +124,16 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void addToHeldIds(int theValueIdx, ArrayList<String> theListToAddTo, String theId) {
|
||||
theListToAddTo.ensureCapacity(theValueIdx);
|
||||
while (theListToAddTo.size() <= theValueIdx) {
|
||||
theListToAddTo.add(null);
|
||||
}
|
||||
if (theListToAddTo.get(theValueIdx) == null) {
|
||||
theListToAddTo.set(theValueIdx, theId);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertObjectOfType(JsonElement theResourceTypeObj, Object theValueType, String thePosition) {
|
||||
// if (theResourceTypeObj == null) {
|
||||
// throw new DataFormatException("Invalid JSON content detected, missing required element: '" + thePosition + "'");
|
||||
|
@ -180,6 +144,16 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
// }
|
||||
}
|
||||
|
||||
private void beginArray(JsonWriter theEventWriter, String arrayName) throws IOException {
|
||||
theEventWriter.name(arrayName);
|
||||
theEventWriter.beginArray();
|
||||
}
|
||||
|
||||
private void beginObject(JsonWriter theEventWriter, String arrayName) throws IOException {
|
||||
theEventWriter.name(arrayName);
|
||||
theEventWriter.beginObject();
|
||||
}
|
||||
|
||||
private JsonWriter createJsonWriter(Writer theWriter) {
|
||||
JsonWriter retVal = new JsonWriter(theWriter);
|
||||
retVal.setSerializeNulls(true);
|
||||
|
@ -205,7 +179,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
JsonWriter eventWriter = createJsonWriter(theWriter);
|
||||
|
||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(theResource);
|
||||
encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null, false);
|
||||
encodeResourceToJsonStreamWriter(resDef, theResource, eventWriter, null, false, false);
|
||||
eventWriter.flush();
|
||||
}
|
||||
|
||||
|
@ -234,37 +208,6 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private JsonObject parse(Reader theReader) {
|
||||
|
||||
PushbackReader pbr = new PushbackReader(theReader);
|
||||
JsonObject object;
|
||||
try {
|
||||
while(true) {
|
||||
int nextInt;
|
||||
nextInt = pbr.read();
|
||||
if (nextInt == -1) {
|
||||
throw new DataFormatException("Did not find any content to parse");
|
||||
}
|
||||
if (nextInt == '{') {
|
||||
pbr.unread('{');
|
||||
break;
|
||||
}
|
||||
if (Character.isWhitespace(nextInt)) {
|
||||
continue;
|
||||
}
|
||||
throw new DataFormatException("Content does not appear to be FHIR JSON, first non-whitespace character was: '" + (char)nextInt + "' (must be '{')");
|
||||
}
|
||||
|
||||
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
|
||||
|
||||
object = gson.fromJson(pbr, JsonObject.class);
|
||||
} catch (Exception e) {
|
||||
throw new DataFormatException("Failed to parse JSON content, error was: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
private void encodeBundleToWriterInDstu1Format(Bundle theBundle, JsonWriter theEventWriter) throws IOException {
|
||||
theEventWriter.beginObject();
|
||||
|
||||
|
@ -320,7 +263,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
IResource resource = nextEntry.getResource();
|
||||
if (resource != null && !resource.isEmpty() && !deleted) {
|
||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
|
||||
encodeResourceToJsonStreamWriter(resDef, resource, theEventWriter, "content", false);
|
||||
encodeResourceToJsonStreamWriter(resDef, resource, theEventWriter, "content", false, true);
|
||||
}
|
||||
|
||||
if (nextEntry.getSummary().isEmpty() == false) {
|
||||
|
@ -374,7 +317,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
IResource resource = nextEntry.getResource();
|
||||
if (resource != null && !resource.isEmpty() && !deleted) {
|
||||
RuntimeResourceDefinition resDef = myContext.getResourceDefinition(resource);
|
||||
encodeResourceToJsonStreamWriter(resDef, resource, theEventWriter, "resource", false);
|
||||
encodeResourceToJsonStreamWriter(resDef, resource, theEventWriter, "resource", false, true);
|
||||
}
|
||||
|
||||
if (nextEntry.getSearchMode().isEmpty() == false || nextEntry.getScore().isEmpty() == false) {
|
||||
|
@ -551,7 +494,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
case RESOURCE:
|
||||
IBaseResource resource = (IBaseResource) theNextValue;
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(resource);
|
||||
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, false);
|
||||
encodeResourceToJsonStreamWriter(def, resource, theEventWriter, theChildName, false, true);
|
||||
break;
|
||||
case UNDECL_EXT:
|
||||
default:
|
||||
|
@ -560,23 +503,6 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
|
||||
}
|
||||
|
||||
private void write(JsonWriter theEventWriter, String theChildName, Boolean theValue) throws IOException {
|
||||
if (theValue != null) {
|
||||
theEventWriter.name(theChildName);
|
||||
theEventWriter.value(theValue.booleanValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void write(JsonWriter theEventWriter, String theChildName, BigDecimal theDecimalValue) throws IOException {
|
||||
theEventWriter.name(theChildName);
|
||||
theEventWriter.value(theDecimalValue);
|
||||
}
|
||||
|
||||
private void write(JsonWriter theEventWriter, String theChildName, Integer theValue) throws IOException {
|
||||
theEventWriter.name(theChildName);
|
||||
theEventWriter.value(theValue);
|
||||
}
|
||||
|
||||
private void encodeCompositeElementChildrenToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theElement, JsonWriter theEventWriter, boolean theContainedResource, CompositeChildElement theParent) throws IOException {
|
||||
|
||||
{
|
||||
|
@ -796,23 +722,13 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void addToHeldIds(int theValueIdx, ArrayList<String> theListToAddTo, String theId) {
|
||||
theListToAddTo.ensureCapacity(theValueIdx);
|
||||
while (theListToAddTo.size() <= theValueIdx) {
|
||||
theListToAddTo.add(null);
|
||||
}
|
||||
if (theListToAddTo.get(theValueIdx) == null) {
|
||||
theListToAddTo.set(theValueIdx, theId);
|
||||
}
|
||||
}
|
||||
|
||||
private void encodeCompositeElementToStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, IBase theNextValue, JsonWriter theEventWriter, boolean theContainedResource, CompositeChildElement theParent) throws IOException, DataFormatException {
|
||||
|
||||
writeCommentsPreAndPost(theNextValue, theEventWriter);
|
||||
encodeCompositeElementChildrenToStreamWriter(theResDef, theResource, theNextValue, theEventWriter, theContainedResource, theParent);
|
||||
}
|
||||
|
||||
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonWriter theEventWriter, String theObjectNameOrNull, boolean theContainedResource) throws IOException {
|
||||
private void encodeResourceToJsonStreamWriter(RuntimeResourceDefinition theResDef, IBaseResource theResource, JsonWriter theEventWriter, String theObjectNameOrNull, boolean theContainedResource, boolean theSubResource) throws IOException {
|
||||
IIdType resourceId = null;
|
||||
// if (theResource instanceof IResource) {
|
||||
// IResource res = (IResource) theResource;
|
||||
|
@ -843,7 +759,7 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
if (!theContainedResource) {
|
||||
if (super.shouldEncodeResourceId(theResource) == false) {
|
||||
resourceId = null;
|
||||
} else if (getEncodeForceResourceId() != null) {
|
||||
} else if (!theSubResource && getEncodeForceResourceId() != null) {
|
||||
resourceId = getEncodeForceResourceId();
|
||||
}
|
||||
}
|
||||
|
@ -1083,6 +999,37 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
return (JsonArray) object;
|
||||
}
|
||||
|
||||
private JsonObject parse(Reader theReader) {
|
||||
|
||||
PushbackReader pbr = new PushbackReader(theReader);
|
||||
JsonObject object;
|
||||
try {
|
||||
while(true) {
|
||||
int nextInt;
|
||||
nextInt = pbr.read();
|
||||
if (nextInt == -1) {
|
||||
throw new DataFormatException("Did not find any content to parse");
|
||||
}
|
||||
if (nextInt == '{') {
|
||||
pbr.unread('{');
|
||||
break;
|
||||
}
|
||||
if (Character.isWhitespace(nextInt)) {
|
||||
continue;
|
||||
}
|
||||
throw new DataFormatException("Content does not appear to be FHIR JSON, first non-whitespace character was: '" + (char)nextInt + "' (must be '{')");
|
||||
}
|
||||
|
||||
Gson gson = newGson();
|
||||
|
||||
object = gson.fromJson(pbr, JsonObject.class);
|
||||
} catch (Exception e) {
|
||||
throw new DataFormatException("Failed to parse JSON content, error was: " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
private void parseAlternates(JsonElement theAlternateVal, ParserState<?> theState, String theElementName) {
|
||||
if (theAlternateVal == null || theAlternateVal instanceof JsonNull) {
|
||||
return;
|
||||
|
@ -1404,6 +1351,17 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
return state.getObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IParser setPrettyPrint(boolean thePrettyPrint) {
|
||||
myPrettyPrint = thePrettyPrint;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void write(JsonWriter theEventWriter, String theChildName, BigDecimal theDecimalValue) throws IOException {
|
||||
theEventWriter.name(theChildName);
|
||||
theEventWriter.value(theDecimalValue);
|
||||
}
|
||||
|
||||
// private void parseExtensionInDstu2Style(boolean theModifier, ParserState<?> theState, String
|
||||
// theParentExtensionUrl, String theExtensionUrl, JsonArray theValues) {
|
||||
// String extUrl = UrlUtil.constructAbsoluteUrl(theParentExtensionUrl, theExtensionUrl);
|
||||
|
@ -1430,10 +1388,16 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
// theState.endingElement();
|
||||
// }
|
||||
|
||||
@Override
|
||||
public IParser setPrettyPrint(boolean thePrettyPrint) {
|
||||
myPrettyPrint = thePrettyPrint;
|
||||
return this;
|
||||
private void write(JsonWriter theEventWriter, String theChildName, Boolean theValue) throws IOException {
|
||||
if (theValue != null) {
|
||||
theEventWriter.name(theChildName);
|
||||
theEventWriter.value(theValue.booleanValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void write(JsonWriter theEventWriter, String theChildName, Integer theValue) throws IOException {
|
||||
theEventWriter.name(theChildName);
|
||||
theEventWriter.value(theValue);
|
||||
}
|
||||
|
||||
private boolean writeAtomLinkInDstu1Format(JsonWriter theEventWriter, String theRel, StringDt theLink, boolean theStarted) throws IOException {
|
||||
|
@ -1481,16 +1445,6 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void beginArray(JsonWriter theEventWriter, String arrayName) throws IOException {
|
||||
theEventWriter.name(arrayName);
|
||||
theEventWriter.beginArray();
|
||||
}
|
||||
|
||||
private void beginObject(JsonWriter theEventWriter, String arrayName) throws IOException {
|
||||
theEventWriter.name(arrayName);
|
||||
theEventWriter.beginObject();
|
||||
}
|
||||
|
||||
private void writeCategories(JsonWriter theEventWriter, TagList categories) throws IOException {
|
||||
if (categories != null && categories.size() > 0) {
|
||||
theEventWriter.name("category");
|
||||
|
@ -1586,13 +1540,23 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
// }
|
||||
}
|
||||
|
||||
public static Gson newGson() {
|
||||
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
|
||||
return gson;
|
||||
}
|
||||
|
||||
private static void write(JsonWriter theWriter, String theName, String theValue) throws IOException {
|
||||
theWriter.name(theName);
|
||||
theWriter.value(theValue);
|
||||
}
|
||||
|
||||
private class HeldExtension implements Comparable<HeldExtension> {
|
||||
|
||||
private CompositeChildElement myChildElem;
|
||||
private RuntimeChildDeclaredExtensionDefinition myDef;
|
||||
private boolean myModifier;
|
||||
private IBaseExtension<?, ?> myUndeclaredExtension;
|
||||
private IBase myValue;
|
||||
private CompositeChildElement myChildElem;
|
||||
|
||||
public HeldExtension(IBaseExtension<?, ?> theUndeclaredExtension, boolean theModifier, CompositeChildElement theChildElem) {
|
||||
assert theUndeclaredExtension != null;
|
||||
|
@ -1697,10 +1661,5 @@ public class JsonParser extends BaseParser implements IParser {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
private static void write(JsonWriter theWriter, String theName, String theValue) throws IOException {
|
||||
theWriter.name(theName);
|
||||
theWriter.value(theValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -1870,13 +1871,17 @@ class ParserState<T> {
|
|||
} else if (theLocalPart.equals("profile")) {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<IdDt> profiles = (List<IdDt>) myMap.get(ResourceMetadataKeyEnum.PROFILES);
|
||||
if (profiles == null) {
|
||||
profiles = new ArrayList<IdDt>();
|
||||
myMap.put(ResourceMetadataKeyEnum.PROFILES, profiles);
|
||||
List<IdDt> newProfiles;
|
||||
if (profiles != null) {
|
||||
newProfiles = new ArrayList<IdDt>(profiles.size() + 1);
|
||||
newProfiles.addAll(profiles);
|
||||
} else {
|
||||
newProfiles = new ArrayList<IdDt>(1);
|
||||
}
|
||||
IdDt profile = new IdDt();
|
||||
push(new PrimitiveState(getPreResourceState(), profile));
|
||||
profiles.add(profile);
|
||||
newProfiles.add(profile);
|
||||
myMap.put(ResourceMetadataKeyEnum.PROFILES, Collections.unmodifiableList(newProfiles));
|
||||
} else if (theLocalPart.equals("tag")) {
|
||||
TagList tagList = (TagList) myMap.get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||
if (tagList == null) {
|
||||
|
|
|
@ -174,7 +174,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
try {
|
||||
eventWriter = createXmlWriter(theWriter);
|
||||
|
||||
encodeResourceToXmlStreamWriter(theResource, eventWriter, false);
|
||||
encodeResourceToXmlStreamWriter(theResource, eventWriter, false, false);
|
||||
eventWriter.flush();
|
||||
} catch (XMLStreamException e) {
|
||||
throw new ConfigurationException("Failed to initialize STaX event factory", e);
|
||||
|
@ -384,7 +384,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
if (resource != null && !resource.isEmpty() && !deleted) {
|
||||
eventWriter.writeStartElement("content");
|
||||
eventWriter.writeAttribute("type", "text/xml");
|
||||
encodeResourceToXmlStreamWriter(resource, eventWriter, false);
|
||||
encodeResourceToXmlStreamWriter(resource, eventWriter, false, true);
|
||||
eventWriter.writeEndElement(); // content
|
||||
} else {
|
||||
ourLog.debug("Bundle entry contains null resource");
|
||||
|
@ -447,7 +447,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
IResource resource = nextEntry.getResource();
|
||||
if (resource != null && !resource.isEmpty() && !deleted) {
|
||||
theEventWriter.writeStartElement("resource");
|
||||
encodeResourceToXmlStreamWriter(resource, theEventWriter, false);
|
||||
encodeResourceToXmlStreamWriter(resource, theEventWriter, false, true);
|
||||
theEventWriter.writeEndElement(); // content
|
||||
} else {
|
||||
ourLog.debug("Bundle entry contains null resource");
|
||||
|
@ -557,7 +557,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
case RESOURCE: {
|
||||
theEventWriter.writeStartElement(childName);
|
||||
IBaseResource resource = (IBaseResource) theElement;
|
||||
encodeResourceToXmlStreamWriter(resource, theEventWriter, false);
|
||||
encodeResourceToXmlStreamWriter(resource, theEventWriter, false, true);
|
||||
theEventWriter.writeEndElement();
|
||||
break;
|
||||
}
|
||||
|
@ -711,7 +711,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
}
|
||||
}
|
||||
|
||||
private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource) throws XMLStreamException, DataFormatException {
|
||||
private void encodeResourceToXmlStreamWriter(IBaseResource theResource, XMLStreamWriter theEventWriter, boolean theIncludedResource, boolean theSubResource) throws XMLStreamException, DataFormatException {
|
||||
IIdType resourceId = null;
|
||||
|
||||
if (StringUtils.isNotBlank(theResource.getIdElement().getIdPart())) {
|
||||
|
@ -727,7 +727,7 @@ public class XmlParser extends BaseParser implements IParser {
|
|||
if (!theIncludedResource) {
|
||||
if (super.shouldEncodeResourceId(theResource) == false) {
|
||||
resourceId = null;
|
||||
} else if (getEncodeForceResourceId() != null) {
|
||||
} else if (theSubResource == false && getEncodeForceResourceId() != null) {
|
||||
resourceId = getEncodeForceResourceId();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
|
||||
/**
|
||||
|
@ -47,5 +49,5 @@ public @interface Delete {
|
|||
* for client implementations.
|
||||
*/
|
||||
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
|
||||
Class<? extends IResource> type() default IResource.class;
|
||||
Class<? extends IBaseResource> type() default IBaseResource.class;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
package ca.uhn.fhir.rest.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
/**
|
||||
* RESTful method annotation to be used for the proposed FHIR
|
||||
* PATCH method
|
||||
*
|
||||
* <p>
|
||||
* Patch is used to apply a differential to a resource in either
|
||||
* XML or JSON format
|
||||
* </p>
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.METHOD })
|
||||
public @interface Patch {
|
||||
|
||||
/**
|
||||
* The return type for this search method. This generally does not need
|
||||
* to be populated for a server implementation, since servers will return
|
||||
* only one resource per class, but generally does need to be populated
|
||||
* for client implementations.
|
||||
*/
|
||||
// NB: Read, Search (maybe others) share this annotation, so update the javadocs everywhere
|
||||
Class<? extends IBaseResource> type() default IBaseResource.class;
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package ca.uhn.fhir.rest.api;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.annotation.Patch;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
|
||||
/**
|
||||
* Parameter type for methods annotated with {@link Patch}
|
||||
*/
|
||||
public enum PatchTypeEnum {
|
||||
|
||||
JSON_PATCH(Constants.CT_JSON_PATCH), XML_PATCH(Constants.CT_XML_PATCH);
|
||||
|
||||
private final String myContentType;
|
||||
|
||||
PatchTypeEnum(String theContentType) {
|
||||
myContentType = theContentType;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return myContentType;
|
||||
}
|
||||
|
||||
}
|
|
@ -130,7 +130,12 @@ public enum RestOperationTypeEnum {
|
|||
/**
|
||||
* $meta-delete extended operation
|
||||
*/
|
||||
META_DELETE("$meta-delete"),
|
||||
META_DELETE("$meta-delete"),
|
||||
|
||||
/**
|
||||
* Patch operation
|
||||
*/
|
||||
PATCH("patch"),
|
||||
|
||||
;
|
||||
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package ca.uhn.fhir.rest.client;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
|
@ -22,33 +20,20 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBase;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.context.*;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
|
@ -67,6 +52,7 @@ import ca.uhn.fhir.rest.server.Constants;
|
|||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.util.OperationOutcomeUtil;
|
||||
import ca.uhn.fhir.util.XmlUtil;
|
||||
|
||||
public abstract class BaseClient implements IRestfulClient {
|
||||
|
||||
|
@ -105,6 +91,11 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
if ("true".equals(System.getProperty(HAPI_CLIENT_KEEPRESPONSES))) {
|
||||
setKeepResponses(true);
|
||||
}
|
||||
|
||||
if (XmlUtil.isStaxPresent() == false) {
|
||||
myEncoding = EncodingEnum.JSON;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected Map<String, List<String>> createExtraParams() {
|
||||
|
|
|
@ -69,6 +69,7 @@ import ca.uhn.fhir.model.primitive.UriDt;
|
|||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
import ca.uhn.fhir.rest.api.SortOrderEnum;
|
||||
import ca.uhn.fhir.rest.api.SortSpec;
|
||||
|
@ -107,6 +108,11 @@ import ca.uhn.fhir.rest.gclient.IOperationUntyped;
|
|||
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInput;
|
||||
import ca.uhn.fhir.rest.gclient.IOperationUntypedWithInputAndPartialOutput;
|
||||
import ca.uhn.fhir.rest.gclient.IParam;
|
||||
import ca.uhn.fhir.rest.gclient.IPatch;
|
||||
import ca.uhn.fhir.rest.gclient.IPatchExecutable;
|
||||
import ca.uhn.fhir.rest.gclient.IPatchTyped;
|
||||
import ca.uhn.fhir.rest.gclient.IPatchWithQuery;
|
||||
import ca.uhn.fhir.rest.gclient.IPatchWithQueryTyped;
|
||||
import ca.uhn.fhir.rest.gclient.IQuery;
|
||||
import ca.uhn.fhir.rest.gclient.IRead;
|
||||
import ca.uhn.fhir.rest.gclient.IReadExecutable;
|
||||
|
@ -519,6 +525,32 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
|
||||
return new ArrayList<IBaseResource>(resp.toListOfResources());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public IPatch patch() {
|
||||
return new PatchInternal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome patch(IdDt theIdDt, IBaseResource theResource) {
|
||||
BaseHttpClientInvocation invocation = MethodUtil.createUpdateInvocation(theResource, null, theIdDt, myContext);
|
||||
if (isKeepResponses()) {
|
||||
myLastRequest = invocation.asHttpRequest(getServerBase(), createExtraParams(), getEncoding(), isPrettyPrint());
|
||||
}
|
||||
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
|
||||
final String resourceName = def.getName();
|
||||
|
||||
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName);
|
||||
MethodOutcome resp = invokeClient(myContext, binding, invocation, myLogRequestAndResponse);
|
||||
return resp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome patch(String theId, IBaseResource theResource) {
|
||||
return update(new IdDt(theId), theResource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IUpdate update() {
|
||||
|
@ -2287,6 +2319,139 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
private class PatchInternal extends BaseClientExecutable<IPatchExecutable, MethodOutcome> implements IPatch, IPatchTyped, IPatchExecutable, IPatchWithQuery, IPatchWithQueryTyped {
|
||||
|
||||
private CriterionList myCriterionList;
|
||||
private IIdType myId;
|
||||
private PreferReturnEnum myPrefer;
|
||||
private IBaseResource myResource;
|
||||
private String myResourceBody;
|
||||
private String mySearchUrl;
|
||||
private PatchTypeEnum myPatchType;
|
||||
private String myPatchBody;
|
||||
|
||||
@Override
|
||||
public IPatchWithQueryTyped and(ICriterion<?> theCriterion) {
|
||||
myCriterionList.add((ICriterionInternal) theCriterion);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPatchWithQuery conditional() {
|
||||
myCriterionList = new CriterionList();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPatchTyped conditionalByUrl(String theSearchUrl) {
|
||||
mySearchUrl = validateAndEscapeConditionalUrl(theSearchUrl);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodOutcome execute() {
|
||||
if (myResource == null) {
|
||||
myResource = parseResourceBody(myResourceBody);
|
||||
}
|
||||
|
||||
// If an explicit encoding is chosen, we will re-serialize to ensure the right encoding
|
||||
if (getParamEncoding() != null) {
|
||||
myResourceBody = null;
|
||||
}
|
||||
|
||||
if (myPatchType == null) {
|
||||
throw new InvalidRequestException("No patch type supplied, cannot invoke server");
|
||||
}
|
||||
if (myPatchBody == null) {
|
||||
throw new InvalidRequestException("No patch body supplied, cannot invoke server");
|
||||
}
|
||||
|
||||
|
||||
if (myId == null) {
|
||||
myId = myResource.getIdElement();
|
||||
}
|
||||
|
||||
if (myId == null || myId.hasIdPart() == false) {
|
||||
throw new InvalidRequestException("No ID supplied for resource to update, can not invoke server");
|
||||
}
|
||||
BaseHttpClientInvocation invocation = MethodUtil.createPatchInvocation(myContext, myId, myPatchType, myPatchBody);
|
||||
|
||||
addPreferHeader(myPrefer, invocation);
|
||||
|
||||
RuntimeResourceDefinition def = myContext.getResourceDefinition(myResource);
|
||||
final String resourceName = def.getName();
|
||||
|
||||
OutcomeResponseHandler binding = new OutcomeResponseHandler(resourceName, myPrefer);
|
||||
|
||||
Map<String, List<String>> params = new HashMap<String, List<String>>();
|
||||
return invoke(params, binding, invocation);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPatchExecutable prefer(PreferReturnEnum theReturn) {
|
||||
myPrefer = theReturn;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPatchTyped resource(IBaseResource theResource) {
|
||||
Validate.notNull(theResource, "Resource can not be null");
|
||||
myResource = theResource;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPatchTyped resource(String theResourceBody) {
|
||||
Validate.notBlank(theResourceBody, "Body can not be null or blank");
|
||||
myResourceBody = theResourceBody;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPatchWithQueryTyped where(ICriterion<?> theCriterion) {
|
||||
myCriterionList.add((ICriterionInternal) theCriterion);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPatchExecutable withId(IIdType theId) {
|
||||
if (theId == null) {
|
||||
throw new NullPointerException("theId can not be null");
|
||||
}
|
||||
if (theId.hasIdPart() == false) {
|
||||
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId.getValue());
|
||||
}
|
||||
myId = theId;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPatchExecutable withId(String theId) {
|
||||
if (theId == null) {
|
||||
throw new NullPointerException("theId can not be null");
|
||||
}
|
||||
if (isBlank(theId)) {
|
||||
throw new NullPointerException("theId must not be blank and must contain an ID, found: " + theId);
|
||||
}
|
||||
myId = new IdDt(theId);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPatchTyped patchType(PatchTypeEnum patchType) {
|
||||
myPatchType = patchType;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPatchTyped patchBody(String patchBody) {
|
||||
myPatchBody = patchBody;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class UpdateInternal extends BaseClientExecutable<IUpdateExecutable, MethodOutcome> implements IUpdate, IUpdateTyped, IUpdateExecutable, IUpdateWithQuery, IUpdateWithQueryTyped {
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ import ca.uhn.fhir.rest.gclient.IGetTags;
|
|||
import ca.uhn.fhir.rest.gclient.IHistory;
|
||||
import ca.uhn.fhir.rest.gclient.IMeta;
|
||||
import ca.uhn.fhir.rest.gclient.IOperation;
|
||||
import ca.uhn.fhir.rest.gclient.IPatch;
|
||||
import ca.uhn.fhir.rest.gclient.IRead;
|
||||
import ca.uhn.fhir.rest.gclient.ITransaction;
|
||||
import ca.uhn.fhir.rest.gclient.IUntypedQuery;
|
||||
|
@ -253,6 +254,35 @@ public interface IGenericClient extends IRestfulClient {
|
|||
@Override
|
||||
void registerInterceptor(IClientInterceptor theInterceptor);
|
||||
|
||||
|
||||
/**
|
||||
* Fluent method for the "patch" operation, which performs a logical patch on a server resource
|
||||
*/
|
||||
IPatch patch();
|
||||
|
||||
/**
|
||||
* Implementation of the "instance patch" method.
|
||||
*
|
||||
* @param theId
|
||||
* The ID to update
|
||||
* @param theResource
|
||||
* The new resource body
|
||||
* @return An outcome containing the results and possibly the new version ID
|
||||
*/
|
||||
MethodOutcome patch(IdDt theId, IBaseResource theResource);
|
||||
|
||||
/**
|
||||
* Implementation of the "instance update" method.
|
||||
*
|
||||
* @param theId
|
||||
* The ID to update
|
||||
* @param theResource
|
||||
* The new resource body
|
||||
* @return An outcome containing the results and possibly the new version ID
|
||||
*/
|
||||
MethodOutcome patch(String theId, IBaseResource theResource);
|
||||
|
||||
|
||||
/**
|
||||
* Search for resources matching a given set of criteria. Searching is a very powerful
|
||||
* feature in FHIR with many features for specifying exactly what should be seaerched for
|
||||
|
|
|
@ -30,12 +30,7 @@ import org.apache.http.HttpEntity;
|
|||
import org.apache.http.NameValuePair;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpOptions;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.HttpRequestBase;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBinary;
|
||||
|
@ -188,6 +183,10 @@ public class ApacheHttpClient implements IHttpClient {
|
|||
switch (myRequestType) {
|
||||
case DELETE:
|
||||
return new HttpDelete(url);
|
||||
case PATCH:
|
||||
HttpPatch httpPatch = new HttpPatch(url);
|
||||
httpPatch.setEntity(theEntity);
|
||||
return httpPatch;
|
||||
case OPTIONS:
|
||||
return new HttpOptions(url);
|
||||
case POST:
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package ca.uhn.fhir.rest.gclient;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
public interface IPatch {
|
||||
|
||||
IPatchTyped resource(IBaseResource theResource);
|
||||
|
||||
IPatchTyped resource(String theResourceBody);
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package ca.uhn.fhir.rest.gclient;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
|
||||
public interface IPatchExecutable extends IClientExecutable<IPatchExecutable, MethodOutcome>{
|
||||
|
||||
/**
|
||||
* Add a <code>Prefer</code> header to the request, which requests that the server include
|
||||
* or suppress the resource body as a part of the result. If a resource is returned by the server
|
||||
* it will be parsed an accessible to the client via {@link MethodOutcome#getResource()}
|
||||
*
|
||||
* @since HAPI 1.1
|
||||
*/
|
||||
IPatchExecutable prefer(PreferReturnEnum theReturn);
|
||||
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package ca.uhn.fhir.rest.gclient;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
|
||||
public interface IPatchTyped extends IPatchExecutable {
|
||||
|
||||
IPatchExecutable withId(IIdType theId);
|
||||
|
||||
IPatchExecutable withId(String theId);
|
||||
|
||||
/**
|
||||
* Specifies that the update should be performed as a conditional create
|
||||
* against a given search URL.
|
||||
*
|
||||
* @param theSearchUrl The search URL to use. The format of this URL should be of the form <code>[ResourceType]?[Parameters]</code>,
|
||||
* for example: <code>Patient?name=Smith&identifier=13.2.4.11.4%7C847366</code>
|
||||
* @since HAPI 0.9 / FHIR DSTU 2
|
||||
*/
|
||||
IPatchTyped conditionalByUrl(String theSearchUrl);
|
||||
|
||||
/**
|
||||
* @since HAPI 0.9 / FHIR DSTU 2
|
||||
*/
|
||||
IPatchWithQuery conditional();
|
||||
|
||||
/**
|
||||
* Specifies the format of the patch (either XML or JSON)
|
||||
* @param patchType
|
||||
*/
|
||||
IPatchTyped patchType(PatchTypeEnum patchType);
|
||||
|
||||
/**
|
||||
* The body of the patch document serialized in either XML or JSON which conforms to
|
||||
* http://jsonpatch.com/ or http://tools.ietf.org/html/rfc5261
|
||||
* @param patchBody
|
||||
*/
|
||||
IPatchTyped patchBody(String patchBody);
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package ca.uhn.fhir.rest.gclient;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
|
||||
public interface IPatchWithQuery extends IBaseQuery<IPatchWithQueryTyped> {
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
package ca.uhn.fhir.rest.gclient;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
public interface IPatchWithQueryTyped extends IPatchTyped, IPatchWithQuery {
|
||||
|
||||
}
|
|
@ -211,8 +211,12 @@ abstract class BaseHttpClientInvocationWithContents extends BaseHttpClientInvoca
|
|||
private String getContentType(EncodingEnum encoding) {
|
||||
if (myBundle != null || (getContext().getVersion().getVersion() == FhirVersionEnum.DSTU1 && ((myContents != null && myContentsIsBundle) || myResources != null))) {
|
||||
return encoding.getBundleContentType();
|
||||
} else {
|
||||
} else if (getContext().getVersion().getVersion().isOlderThan(FhirVersionEnum.DSTU3)) {
|
||||
// application/xml+fhir
|
||||
return encoding.getResourceContentType();
|
||||
} else {
|
||||
// application/fhir+xml
|
||||
return encoding.getResourceContentTypeNonLegacy();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,20 +46,7 @@ import ca.uhn.fhir.model.api.Include;
|
|||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.annotation.AddTags;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.Delete;
|
||||
import ca.uhn.fhir.rest.annotation.DeleteTags;
|
||||
import ca.uhn.fhir.rest.annotation.GetPage;
|
||||
import ca.uhn.fhir.rest.annotation.GetTags;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.Metadata;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.annotation.Transaction;
|
||||
import ca.uhn.fhir.rest.annotation.Update;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
|
@ -387,9 +374,10 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||
Transaction transaction = theMethod.getAnnotation(Transaction.class);
|
||||
Operation operation = theMethod.getAnnotation(Operation.class);
|
||||
GetPage getPage = theMethod.getAnnotation(GetPage.class);
|
||||
Patch patch = theMethod.getAnnotation(Patch.class);
|
||||
|
||||
// ** if you add another annotation above, also add it to the next line:
|
||||
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags, transaction, operation, getPage)) {
|
||||
if (!verifyMethodHasZeroOrOneOperationAnnotation(theMethod, read, search, conformance, create, update, delete, history, validate, getTags, addTags, deleteTags, transaction, operation, getPage, patch)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -414,7 +402,7 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||
throw new ConfigurationException("Method '" + theMethod.getName() + "' from type " + theMethod.getDeclaringClass().getCanonicalName() + " is annotated with @"
|
||||
+ GetTags.class.getSimpleName() + " but does not return type " + TagList.class.getName());
|
||||
}
|
||||
} else if (MethodOutcome.class.equals(returnTypeFromMethod)) {
|
||||
} else if (MethodOutcome.class.isAssignableFrom(returnTypeFromMethod)) {
|
||||
// returns a method outcome
|
||||
} else if (IBundleProvider.class.equals(returnTypeFromMethod)) {
|
||||
// returns a bundle provider
|
||||
|
@ -448,6 +436,8 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||
returnTypeFromAnnotation = history.type();
|
||||
} else if (delete != null) {
|
||||
returnTypeFromAnnotation = delete.type();
|
||||
} else if (patch != null) {
|
||||
returnTypeFromAnnotation = patch.type();
|
||||
} else if (create != null) {
|
||||
returnTypeFromAnnotation = create.type();
|
||||
} else if (update != null) {
|
||||
|
@ -513,6 +503,8 @@ public abstract class BaseMethodBinding<T> implements IClientResponseHandler<T>
|
|||
return new UpdateMethodBinding(theMethod, theContext, theProvider);
|
||||
} else if (delete != null) {
|
||||
return new DeleteMethodBinding(theMethod, theContext, theProvider);
|
||||
} else if (patch != null) {
|
||||
return new PatchMethodBinding(theMethod, theContext, theProvider);
|
||||
} else if (history != null) {
|
||||
return new HistoryMethodBinding(theMethod, theContext, theProvider);
|
||||
} else if (validate != null) {
|
||||
|
|
|
@ -184,8 +184,15 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
* on them
|
||||
*/
|
||||
MethodOutcome response;
|
||||
response = (MethodOutcome) invokeServerMethod(theServer, theRequest, params);
|
||||
|
||||
Object methodReturn = invokeServerMethod(theServer, theRequest, params);
|
||||
|
||||
if (methodReturn instanceof IBaseOperationOutcome) {
|
||||
response = new MethodOutcome();
|
||||
response.setOperationOutcome((IBaseOperationOutcome) methodReturn);
|
||||
} else {
|
||||
response = (MethodOutcome) methodReturn;
|
||||
}
|
||||
|
||||
if (response != null && response.getId() != null && response.getId().hasResourceType()) {
|
||||
if (getContext().getResourceDefinition(response.getId().getResourceType()) == null) {
|
||||
throw new InternalErrorException("Server method returned invalid resource ID: " + response.getId().getValue());
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package ca.uhn.fhir.rest.method;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.rest.annotation.Delete;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.VersionIdParam;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
||||
public abstract class BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody extends BaseOutcomeReturningMethodBinding {
|
||||
|
||||
private String myResourceName;
|
||||
private Integer myIdParameterIndex;
|
||||
|
||||
public BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody(Method theMethod, FhirContext theContext, Object theProvider, Class<?> theMethodAnnotationType, Class<? extends IBaseResource> theResourceTypeFromAnnotation) {
|
||||
super(theMethod, theContext, theMethodAnnotationType, theProvider);
|
||||
|
||||
Class<? extends IBaseResource> resourceType = theResourceTypeFromAnnotation;
|
||||
if (resourceType != IBaseResource.class) {
|
||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(resourceType);
|
||||
myResourceName = def.getName();
|
||||
} else {
|
||||
if (theProvider != null && theProvider instanceof IResourceProvider) {
|
||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(((IResourceProvider) theProvider).getResourceType());
|
||||
myResourceName = def.getName();
|
||||
} else {
|
||||
throw new ConfigurationException(
|
||||
"Can not determine resource type for method '" + theMethod.getName() + "' on type " + theMethod.getDeclaringClass().getCanonicalName() + " - Did you forget to include the resourceType() value on the @" + Delete.class.getSimpleName() + " method annotation?");
|
||||
}
|
||||
}
|
||||
|
||||
myIdParameterIndex = MethodUtil.findIdParameterIndex(theMethod, getContext());
|
||||
if (myIdParameterIndex == null) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has no parameter annotated with the @" + IdParam.class.getSimpleName() + " annotation");
|
||||
}
|
||||
|
||||
Integer versionIdParameterIndex = MethodUtil.findVersionIdParameterIndex(theMethod);
|
||||
if (versionIdParameterIndex != null) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has a parameter annotated with the @" + VersionIdParam.class.getSimpleName() + " annotation but delete methods may not have this annotation");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceName() {
|
||||
return myResourceName;
|
||||
}
|
||||
|
||||
protected Integer getIdParameterIndex() {
|
||||
return myIdParameterIndex;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -10,7 +10,7 @@ package ca.uhn.fhir.rest.method;
|
|||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -28,59 +28,19 @@ import java.util.Set;
|
|||
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.annotation.Delete;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.VersionIdParam;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
||||
|
||||
private String myResourceName;
|
||||
private Integer myIdParameterIndex;
|
||||
public class DeleteMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody {
|
||||
|
||||
public DeleteMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(theMethod, theContext, Delete.class, theProvider);
|
||||
|
||||
Delete deleteAnnotation = theMethod.getAnnotation(Delete.class);
|
||||
Class<? extends IResource> resourceType = deleteAnnotation.type();
|
||||
if (resourceType != IResource.class) {
|
||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(resourceType);
|
||||
myResourceName = def.getName();
|
||||
} else {
|
||||
if (theProvider != null && theProvider instanceof IResourceProvider) {
|
||||
RuntimeResourceDefinition def = theContext.getResourceDefinition(((IResourceProvider) theProvider).getResourceType());
|
||||
myResourceName = def.getName();
|
||||
} else {
|
||||
throw new ConfigurationException(
|
||||
"Can not determine resource type for method '" + theMethod.getName() + "' on type " + theMethod.getDeclaringClass().getCanonicalName() + " - Did you forget to include the resourceType() value on the @" + Delete.class.getSimpleName() + " method annotation?");
|
||||
}
|
||||
}
|
||||
|
||||
myIdParameterIndex = MethodUtil.findIdParameterIndex(theMethod, getContext());
|
||||
if (myIdParameterIndex == null) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has no parameter annotated with the @" + IdParam.class.getSimpleName() + " annotation");
|
||||
}
|
||||
|
||||
Integer versionIdParameterIndex = MethodUtil.findVersionIdParameterIndex(theMethod);
|
||||
if (versionIdParameterIndex != null) {
|
||||
throw new ConfigurationException("Method '" + theMethod.getName() + "' on type '" + theMethod.getDeclaringClass().getCanonicalName() + "' has a parameter annotated with the @" + VersionIdParam.class.getSimpleName() + " annotation but delete methods may not have this annotation");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean allowVoidReturnType() {
|
||||
return true;
|
||||
super(theMethod, theContext, theProvider, Delete.class, theMethod.getAnnotation(Delete.class).type());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -102,13 +62,13 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String getResourceName() {
|
||||
return myResourceName;
|
||||
protected boolean allowVoidReturnType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||
IdDt idDt = (IdDt) theArgs[myIdParameterIndex];
|
||||
IIdType idDt = (IIdType) theArgs[getIdParameterIndex()];
|
||||
if (idDt == null) {
|
||||
throw new NullPointerException("ID can not be null");
|
||||
}
|
||||
|
@ -136,7 +96,7 @@ public class DeleteMethodBinding extends BaseOutcomeReturningMethodBinding {
|
|||
|
||||
@Override
|
||||
protected void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams) {
|
||||
theParams[myIdParameterIndex] = theRequest.getId();
|
||||
theParams[getIdParameterIndex()] = theRequest.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -67,4 +67,10 @@ public class HttpDeleteClientInvocation extends BaseHttpClientInvocation {
|
|||
return createHttpRequest(b.toString(), theEncoding, RequestTypeEnum.DELETE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IHttpRequest createHttpRequest(String theUrl, EncodingEnum theEncoding, RequestTypeEnum theRequestType) {
|
||||
// TODO Auto-generated method stub
|
||||
return super.createHttpRequest(theUrl, theEncoding, theRequestType);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
package ca.uhn.fhir.rest.method;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpClient;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpRequest;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
||||
public class HttpPatchClientInvocation extends BaseHttpClientInvocation {
|
||||
|
||||
private String myUrlPath;
|
||||
private Map<String, List<String>> myParams;
|
||||
private String myContents;
|
||||
private String myContentType;
|
||||
|
||||
public HttpPatchClientInvocation(FhirContext theContext, IIdType theId, String theContentType, String theContents) {
|
||||
super(theContext);
|
||||
myUrlPath = theId.toUnqualifiedVersionless().getValue();
|
||||
myContentType = theContentType;
|
||||
myContents = theContents;
|
||||
}
|
||||
//
|
||||
// public HttpDeleteClientInvocation(FhirContext theContext, String theSearchUrl) {
|
||||
// super(theContext);
|
||||
// myUrlPath = theSearchUrl;
|
||||
// }
|
||||
//
|
||||
// public HttpDeleteClientInvocation(FhirContext theContext, String theResourceType, Map<String, List<String>> theParams) {
|
||||
// super(theContext);
|
||||
// myUrlPath = theResourceType;
|
||||
// myParams = theParams;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public IHttpRequest asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theUrlBase);
|
||||
if (!theUrlBase.endsWith("/")) {
|
||||
b.append('/');
|
||||
}
|
||||
b.append(myUrlPath);
|
||||
|
||||
appendExtraParamsWithQuestionMark(myParams, b, b.indexOf("?") == -1);
|
||||
appendExtraParamsWithQuestionMark(theExtraParams, b, b.indexOf("?") == -1);
|
||||
|
||||
|
||||
|
||||
return createHttpRequest(b.toString(), theEncoding, RequestTypeEnum.PATCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IHttpRequest createHttpRequest(String theUrl, EncodingEnum theEncoding, RequestTypeEnum theRequestType) {
|
||||
IHttpClient httpClient = getRestfulClientFactory().getHttpClient(new StringBuilder(theUrl), null, null, theRequestType, getHeaders());
|
||||
return httpClient.createByteRequest(getContext(), myContents, myContentType, null);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -22,10 +22,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
|||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.method.OperationParameter.IOperationParamConverter;
|
||||
import ca.uhn.fhir.rest.param.*;
|
||||
|
@ -158,6 +155,12 @@ public class MethodUtil {
|
|||
retVal.setIfNoneExistString(theIfNoneExistUrl);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static HttpPatchClientInvocation createPatchInvocation(FhirContext theContext, IIdType theId, PatchTypeEnum thePatchType, String theBody) {
|
||||
return PatchMethodBinding.createPatchInvocation(theContext, theId, thePatchType, theBody);
|
||||
}
|
||||
|
||||
/** End Patch **/
|
||||
|
||||
public static HttpPutClientInvocation createUpdateInvocation(FhirContext theContext, IBaseResource theResource, String theResourceBody, Map<String, List<String>> theMatchParams) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
|
@ -188,6 +191,7 @@ public class MethodUtil {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
public static HttpPutClientInvocation createUpdateInvocation(FhirContext theContext, IBaseResource theResource, String theResourceBody, String theMatchUrl) {
|
||||
HttpPutClientInvocation retVal;
|
||||
if (StringUtils.isBlank(theResourceBody)) {
|
||||
|
@ -364,6 +368,8 @@ public class MethodUtil {
|
|||
param = new RequestOperationCallbackParameter();
|
||||
} else if (parameterType.equals(SummaryEnum.class)) {
|
||||
param = new SummaryEnumParameter();
|
||||
} else if (parameterType.equals(PatchTypeEnum.class)) {
|
||||
param = new PatchTypeParameter();
|
||||
} else {
|
||||
for (int i = 0; i < annotations.length && param == null; i++) {
|
||||
Annotation nextAnnotation = annotations[i];
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
package ca.uhn.fhir.rest.method;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.rest.annotation.Patch;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.client.BaseHttpClientInvocation;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
/**
|
||||
* Base class for an operation that has a resource type but not a resource body in the
|
||||
* request body
|
||||
*
|
||||
*/
|
||||
public class PatchMethodBinding extends BaseOutcomeReturningMethodBindingWithResourceIdButNoResourceBody {
|
||||
|
||||
private int myPatchTypeParameterIndex = -1;
|
||||
private int myResourceParamIndex;
|
||||
|
||||
public PatchMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(theMethod, theContext, theProvider, Patch.class, theMethod.getAnnotation(Patch.class).type());
|
||||
|
||||
for (ListIterator<Class<?>> iter = Arrays.asList(theMethod.getParameterTypes()).listIterator(); iter.hasNext();) {
|
||||
int nextIndex = iter.nextIndex();
|
||||
Class<?> next = iter.next();
|
||||
if (next.equals(PatchTypeEnum.class)) {
|
||||
myPatchTypeParameterIndex = nextIndex;
|
||||
}
|
||||
for (Annotation nextAnnotation : theMethod.getParameterAnnotations()[nextIndex]) {
|
||||
if (nextAnnotation instanceof ResourceParam) {
|
||||
myResourceParamIndex = nextIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (myPatchTypeParameterIndex == -1) {
|
||||
throw new ConfigurationException("Method has no parameter of type " + PatchTypeEnum.class.getName() + " - " + theMethod.toString());
|
||||
}
|
||||
if (myResourceParamIndex == -1) {
|
||||
throw new ConfigurationException("Method has no parameter with @" + ResourceParam.class.getSimpleName() + " annotation - " + theMethod.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
boolean retVal = super.incomingServerRequestMatchesMethod(theRequest);
|
||||
if (retVal) {
|
||||
PatchTypeParameter.getTypeForRequestOrThrowInvalidRequestException(theRequest);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestOperationTypeEnum getRestOperationType() {
|
||||
return RestOperationTypeEnum.PATCH;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<RequestTypeEnum> provideAllowableRequestTypes() {
|
||||
return Collections.singleton(RequestTypeEnum.PATCH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
|
||||
StringBuilder urlExtension = new StringBuilder();
|
||||
urlExtension.append(getContext().getResourceDefinition(theResource).getName());
|
||||
|
||||
return new HttpPostClientInvocation(getContext(), theResource, urlExtension.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean allowVoidReturnType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseHttpClientInvocation invokeClient(Object[] theArgs) throws InternalErrorException {
|
||||
IIdType idDt = (IIdType) theArgs[getIdParameterIndex()];
|
||||
if (idDt == null) {
|
||||
throw new NullPointerException("ID can not be null");
|
||||
}
|
||||
|
||||
if (idDt.hasResourceType() == false) {
|
||||
idDt = idDt.withResourceType(getResourceName());
|
||||
} else if (getResourceName().equals(idDt.getResourceType()) == false) {
|
||||
throw new InvalidRequestException("ID parameter has the wrong resource type, expected '" + getResourceName() + "', found: " + idDt.getResourceType());
|
||||
}
|
||||
|
||||
PatchTypeEnum patchType = (PatchTypeEnum) theArgs[myPatchTypeParameterIndex];
|
||||
String body = (String) theArgs[myResourceParamIndex];
|
||||
|
||||
HttpPatchClientInvocation retVal = createPatchInvocation(getContext(), idDt, patchType, body);
|
||||
|
||||
for (int idx = 0; idx < theArgs.length; idx++) {
|
||||
IParameter nextParam = getParameters().get(idx);
|
||||
nextParam.translateClientArgumentIntoQueryArgument(getContext(), theArgs[idx], null, null);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static HttpPatchClientInvocation createPatchInvocation(FhirContext theContext, IIdType theId, PatchTypeEnum thePatchType, String theBody) {
|
||||
HttpPatchClientInvocation retVal = new HttpPatchClientInvocation(theContext, theId, thePatchType.getContentType(), theBody);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addParametersForServerRequest(RequestDetails theRequest, Object[] theParams) {
|
||||
IIdType id = theRequest.getId();
|
||||
id = UpdateMethodBinding.applyETagAsVersion(theRequest, id);
|
||||
theParams[getIdParameterIndex()] = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMatchingOperation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package ca.uhn.fhir.rest.method;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Core Library
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2016 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
||||
class PatchTypeParameter implements IParameter {
|
||||
@Override
|
||||
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException {
|
||||
// nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
|
||||
return getTypeForRequestOrThrowInvalidRequestException(theRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
public static PatchTypeEnum getTypeForRequestOrThrowInvalidRequestException(RequestDetails theRequest) {
|
||||
String contentTypeAll = defaultString(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE));
|
||||
String contentType = contentTypeAll;
|
||||
int semiColonIdx = contentType.indexOf(';');
|
||||
if (semiColonIdx != -1) {
|
||||
contentType = contentTypeAll.substring(0, semiColonIdx);
|
||||
}
|
||||
contentType = contentType.trim();
|
||||
if (Constants.CT_JSON_PATCH.equals(contentType)) {
|
||||
return PatchTypeEnum.JSON_PATCH;
|
||||
} else if (Constants.CT_XML_PATCH.equals(contentType)) {
|
||||
return PatchTypeEnum.XML_PATCH;
|
||||
} else {
|
||||
throw new InvalidRequestException("Invalid Content-Type for PATCH operation: " + contentTypeAll);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -35,6 +35,10 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
|||
class ServletRequestParameter implements IParameter {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletRequestParameter.class);
|
||||
|
||||
ServletRequestParameter() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource) throws InternalErrorException {
|
||||
/*
|
||||
|
|
|
@ -69,13 +69,7 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
|||
}
|
||||
}
|
||||
|
||||
String ifMatchValue = theRequest.getHeader(Constants.HEADER_IF_MATCH);
|
||||
if (isNotBlank(ifMatchValue)) {
|
||||
ifMatchValue = MethodUtil.parseETagValue(ifMatchValue);
|
||||
if (id != null && id.hasVersionIdPart() == false) {
|
||||
id = id.withVersion(ifMatchValue);
|
||||
}
|
||||
}
|
||||
id = applyETagAsVersion(theRequest, id);
|
||||
|
||||
if (theRequest.getId() != null && theRequest.getId().hasVersionIdPart() == false) {
|
||||
if (id != null && id.hasVersionIdPart()) {
|
||||
|
@ -94,6 +88,17 @@ public class UpdateMethodBinding extends BaseOutcomeReturningMethodBindingWithRe
|
|||
super.addParametersForServerRequest(theRequest, theParams);
|
||||
}
|
||||
|
||||
public static IIdType applyETagAsVersion(RequestDetails theRequest, IIdType id) {
|
||||
String ifMatchValue = theRequest.getHeader(Constants.HEADER_IF_MATCH);
|
||||
if (isNotBlank(ifMatchValue)) {
|
||||
ifMatchValue = MethodUtil.parseETagValue(ifMatchValue);
|
||||
if (id != null && id.hasVersionIdPart() == false) {
|
||||
id = id.withVersion(ifMatchValue);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BaseHttpClientInvocation createClientInvocation(Object[] theArgs, IResource theResource) {
|
||||
IdDt idDt = (IdDt) theArgs[myIdParameterIndex];
|
||||
|
|
|
@ -63,6 +63,8 @@ public class Constants {
|
|||
public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";
|
||||
public static final String HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY = CT_FHIR_XML + ";q=1.0, " + CT_FHIR_JSON + ";q=1.0";
|
||||
public static final String HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY = CT_FHIR_XML_NEW + ";q=1.0, " + CT_FHIR_JSON_NEW + ";q=1.0, " + HEADER_ACCEPT_VALUE_XML_OR_JSON_LEGACY.replace("1.0", "0.9");
|
||||
public static final String HEADER_ACCEPT_VALUE_XML_NON_LEGACY = CT_FHIR_XML_NEW + ";q=1.0, " + CT_FHIR_XML + ";q=0.9";
|
||||
public static final String HEADER_ACCEPT_VALUE_JSON_NON_LEGACY = CT_FHIR_JSON_NEW + ";q=1.0, " + CT_FHIR_JSON + ";q=0.9";
|
||||
public static final String HEADER_ALLOW = "Allow";
|
||||
public static final String HEADER_AUTHORIZATION = "Authorization";
|
||||
public static final String HEADER_AUTHORIZATION_VALPREFIX_BASIC = "Basic ";
|
||||
|
@ -166,6 +168,8 @@ public class Constants {
|
|||
public static final String TAG_SUBSETTED_SYSTEM = "http://hl7.org/fhir/v3/ObservationValue";
|
||||
public static final String URL_TOKEN_HISTORY = "_history";
|
||||
public static final String URL_TOKEN_METADATA = "metadata";
|
||||
public static final String CT_JSON_PATCH = "application/json-patch+json";
|
||||
public static final String CT_XML_PATCH = "application/xml-patch+xml";
|
||||
|
||||
static {
|
||||
CHARSET_UTF8 = Charset.forName(CHARSET_NAME_UTF8);
|
||||
|
|
|
@ -29,15 +29,8 @@ import java.lang.annotation.Annotation;
|
|||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.jar.Manifest;
|
||||
|
@ -78,14 +71,16 @@ import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
|
|||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.CoverageIgnore;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import ca.uhn.fhir.util.UrlPathTokenizer;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import ca.uhn.fhir.util.VersionUtil;
|
||||
import ca.uhn.fhir.util.*;
|
||||
|
||||
public class RestfulServer extends HttpServlet implements IRestfulServer<ServletRequestDetails> {
|
||||
|
||||
/**
|
||||
* All incoming requests will have an attribute added to {@link HttpServletRequest#getAttribute(String)}
|
||||
* with this key. The value will be a Java {@link Date} with the time that request processing began.
|
||||
*/
|
||||
public static final String REQUEST_START_TIME = RestfulServer.class.getName() + "REQUEST_START_TIME";
|
||||
|
||||
/**
|
||||
* Default setting for {@link #setETagSupport(ETagSupportEnum) ETag Support}: {@link ETagSupportEnum#ENABLED}
|
||||
*/
|
||||
|
@ -658,6 +653,11 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
*/
|
||||
resourceMethod.invokeServer(this, requestDetails);
|
||||
|
||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = getInterceptors().get(i);
|
||||
next.processingCompletedNormally(requestDetails);
|
||||
}
|
||||
|
||||
} catch (NotModifiedException e) {
|
||||
|
||||
for (int i = getInterceptors().size() - 1; i >= 0; i--) {
|
||||
|
@ -1163,6 +1163,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
|
||||
@Override
|
||||
protected void service(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
|
||||
theReq.setAttribute(REQUEST_START_TIME, new Date());
|
||||
|
||||
RequestTypeEnum method;
|
||||
try {
|
||||
method = RequestTypeEnum.valueOf(theReq.getMethod());
|
||||
|
|
|
@ -787,9 +787,17 @@ public class RestfulServerUtils {
|
|||
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_OR_JSON_NON_LEGACY);
|
||||
}
|
||||
} else if (theEncoding == EncodingEnum.JSON) {
|
||||
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON);
|
||||
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2) == false) {
|
||||
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_JSON);
|
||||
} else {
|
||||
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY);
|
||||
}
|
||||
} else if (theEncoding == EncodingEnum.XML) {
|
||||
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML);
|
||||
if (theContext.getVersion().getVersion().isNewerThan(FhirVersionEnum.DSTU2) == false) {
|
||||
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.CT_FHIR_XML);
|
||||
} else {
|
||||
theHttpRequest.addHeader(Constants.HEADER_ACCEPT, Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ package ca.uhn.fhir.rest.server.interceptor;
|
|||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -41,8 +41,10 @@ import ca.uhn.fhir.rest.annotation.Search;
|
|||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
||||
/**
|
||||
* Provides methods to intercept requests and responses. Note that implementations of this interface may wish to use
|
||||
|
@ -88,7 +90,8 @@ public interface IServerInterceptor {
|
|||
* @throws IOException
|
||||
* If this exception is thrown, it will be re-thrown up to the container for handling.
|
||||
*/
|
||||
boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException, IOException;
|
||||
boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws ServletException, IOException;
|
||||
|
||||
/**
|
||||
* This method is called just before the actual implementing server method is invoked.
|
||||
|
@ -118,10 +121,10 @@ public interface IServerInterceptor {
|
|||
|
||||
/**
|
||||
* Invoked before an incoming request is processed. Note that this method is called
|
||||
* after the server has begin preparing the response to the incoming client request.
|
||||
* after the server has begin preparing the response to the incoming client request.
|
||||
* As such, it is not able to supply a response to the incoming request in the way that
|
||||
* {@link #incomingRequestPreHandled(RestOperationTypeEnum, ActionRequestDetails)} and
|
||||
* {@link #incomingRequestPostProcessed(RequestDetails, HttpServletRequest, HttpServletResponse)}
|
||||
* {@link #incomingRequestPostProcessed(RequestDetails, HttpServletRequest, HttpServletResponse)}
|
||||
* are.
|
||||
* <p>
|
||||
* This method may however throw a subclass of {@link BaseServerResponseException}, and processing
|
||||
|
@ -292,7 +295,8 @@ public interface IServerInterceptor {
|
|||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException;
|
||||
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* This method is called after the server implementation method has been called, but before any attempt to stream the
|
||||
|
@ -364,6 +368,18 @@ public interface IServerInterceptor {
|
|||
*/
|
||||
BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException;
|
||||
|
||||
/**
|
||||
* This method is called after all processing is completed for a request, but only if the
|
||||
* request completes normally (i.e. no exception is thrown).
|
||||
* <p>
|
||||
* Note that this individual interceptors will have this method called in the reverse order from the order in
|
||||
* which the interceptors were registered with the server.
|
||||
* </p>
|
||||
* @param theRequestDetails
|
||||
* The request itself
|
||||
*/
|
||||
void processingCompletedNormally(ServletRequestDetails theRequestDetails);
|
||||
|
||||
public static class ActionRequestDetails {
|
||||
private final FhirContext myContext;
|
||||
private final IIdType myId;
|
||||
|
@ -400,20 +416,22 @@ public interface IServerInterceptor {
|
|||
myResource = theResource;
|
||||
}
|
||||
|
||||
public ActionRequestDetails(RequestDetails theRequestDetails, String theResourceType, IIdType theId) {
|
||||
this(theRequestDetails, theRequestDetails.getServer().getFhirContext(), theResourceType, theId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theRequestDetails The request details to wrap
|
||||
* @param theId The ID of the resource being created (note that the ID should have the resource type populated)
|
||||
* @param theRequestDetails
|
||||
* The request details to wrap
|
||||
* @param theId
|
||||
* The ID of the resource being created (note that the ID should have the resource type populated)
|
||||
*/
|
||||
public ActionRequestDetails(RequestDetails theRequestDetails, IIdType theId) {
|
||||
this(theRequestDetails, theId.getResourceType(), theId);
|
||||
}
|
||||
|
||||
public ActionRequestDetails(RequestDetails theRequestDetails, String theResourceType, IIdType theId) {
|
||||
this(theRequestDetails, theRequestDetails.getServer().getFhirContext(), theResourceType, theId);
|
||||
}
|
||||
|
||||
public FhirContext getContext() {
|
||||
return myContext;
|
||||
}
|
||||
|
@ -454,14 +472,14 @@ public interface IServerInterceptor {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the same map which was
|
||||
* Returns the same map which was
|
||||
*/
|
||||
public Map<Object, Object> getUserData() {
|
||||
return myRequestDetails.getUserData();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method may be invoked by user code to notify interceptors that a nested
|
||||
* This method may be invoked by user code to notify interceptors that a nested
|
||||
* operation is being invoked which is denoted by this request details.
|
||||
*/
|
||||
public void notifyIncomingRequestPreHandled(RestOperationTypeEnum theOperationType) {
|
||||
|
|
|
@ -10,7 +10,7 @@ package ca.uhn.fhir.rest.server.interceptor;
|
|||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -38,13 +38,13 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
|||
|
||||
/**
|
||||
* Base class for {@link IServerInterceptor} implementations. Provides a No-op implementation
|
||||
* of all methods, always returning <code>true</code>
|
||||
* of all methods, always returning <code>true</code>
|
||||
*/
|
||||
public class InterceptorAdapter implements IServerInterceptor {
|
||||
|
||||
@Override
|
||||
public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws ServletException,
|
||||
IOException {
|
||||
public boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws ServletException, IOException {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -64,52 +64,60 @@ public class InterceptorAdapter implements IServerInterceptor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(theRequestDetails, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle bundle) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(details, bundle, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws AuthenticationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, Bundle bundle) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(details, bundle, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(theRequestDetails, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
return true;
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(details, theResponseObject, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(details, theResponseObject, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws AuthenticationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(details, theResponseObject, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws AuthenticationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
return outgoingResponse(details, theResponseObject, details.getServletRequest(), details.getServletResponse());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processingCompletedNormally(ServletRequestDetails theRequestDetails) {
|
||||
// nothing
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.io.IOException;
|
|||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.Date;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -41,11 +42,14 @@ import org.slf4j.Logger;
|
|||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
||||
/**
|
||||
* Server interceptor which logs each request using a defined format
|
||||
|
@ -74,7 +78,7 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
|||
* </tr>
|
||||
* <tr>
|
||||
* <td>${remoteAddr}</td>
|
||||
* <td>The originaring IP of the request</td>
|
||||
* <td>The originating IP of the request</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>${requestHeader.XXXX}</td>
|
||||
|
@ -115,12 +119,21 @@ import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
|||
* </tr>
|
||||
* </table>
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO: implement this, but it needs the logging to happen at the end
|
||||
* <tr>
|
||||
* <td>${processingTimeMillis}</td>
|
||||
* <td>The number of milliseconds spent processing this request</td>
|
||||
* </tr>
|
||||
|
||||
*/
|
||||
public class LoggingInterceptor extends InterceptorAdapter {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LoggingInterceptor.class);
|
||||
|
||||
private String myErrorMessageFormat = "ERROR - ${idOrResourceName}";
|
||||
private boolean myLogExceptions;
|
||||
private String myErrorMessageFormat = "ERROR - ${operationType} - ${idOrResourceName}";
|
||||
private boolean myLogExceptions = true;
|
||||
private Logger myLogger = ourLog;
|
||||
private String myMessageFormat = "${operationType} - ${idOrResourceName}";
|
||||
|
||||
|
@ -146,18 +159,16 @@ public class LoggingInterceptor extends InterceptorAdapter {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingRequestPostProcessed(final RequestDetails theRequestDetails, final HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
|
||||
|
||||
@Override
|
||||
public void processingCompletedNormally(ServletRequestDetails theRequestDetails) {
|
||||
// Perform any string substitutions from the message format
|
||||
StrLookup<?> lookup = new MyLookup(theRequest, theRequestDetails);
|
||||
StrLookup<?> lookup = new MyLookup(theRequestDetails.getServletRequest(), theRequestDetails);
|
||||
StrSubstitutor subs = new StrSubstitutor(lookup, "${", "}", '\\');
|
||||
|
||||
// Actuall log the line
|
||||
String line = subs.replace(myMessageFormat);
|
||||
myLogger.info(line);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -308,10 +319,16 @@ public class LoggingInterceptor extends InterceptorAdapter {
|
|||
EncodingEnum encoding = EncodingEnum.forContentType(contentType);
|
||||
if (encoding != null) {
|
||||
byte[] requestContents = myRequestDetails.loadRequestContents();
|
||||
return new String(requestContents, Charsets.UTF_8);
|
||||
return new String(requestContents, Constants.CHARSET_UTF8);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
} else if ("processingTimeMillis".equals(theKey)) {
|
||||
Date startTime = (Date) myRequest.getAttribute(RestfulServer.REQUEST_START_TIME);
|
||||
if (startTime != null) {
|
||||
long time = System.currentTimeMillis() - startTime.getTime();
|
||||
return Long.toString(time);
|
||||
}
|
||||
}
|
||||
|
||||
return "!VAL!";
|
||||
|
|
|
@ -24,10 +24,12 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
@ -39,6 +41,7 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
|||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
|
@ -206,7 +209,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
return super.handleException(theRequestDetails, theException, theServletRequest, theServletResponse);
|
||||
}
|
||||
|
||||
streamResponse(theRequestDetails, theServletResponse, theException.getOperationOutcome());
|
||||
streamResponse(theRequestDetails, theServletResponse, theException.getOperationOutcome(), theServletRequest);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -276,12 +279,12 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse);
|
||||
}
|
||||
|
||||
streamResponse(theRequestDetails, theServletResponse, theResponseObject);
|
||||
streamResponse(theRequestDetails, theServletResponse, theResponseObject, theServletRequest);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void streamResponse(RequestDetails theRequestDetails, HttpServletResponse theServletResponse, IBaseResource resource) {
|
||||
private void streamResponse(RequestDetails theRequestDetails, HttpServletResponse theServletResponse, IBaseResource resource, ServletRequest theServletRequest) {
|
||||
IParser p;
|
||||
Map<String, String[]> parameters = theRequestDetails.getParameters();
|
||||
if (parameters.containsKey(Constants.PARAM_FORMAT)) {
|
||||
|
@ -360,6 +363,16 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
b.append("<a href=\"");
|
||||
b.append(createLinkHref(parameters, Constants.FORMATS_HTML_XML));
|
||||
b.append("\">HTML XML</a>.");
|
||||
|
||||
Date startTime = (Date) theServletRequest.getAttribute(RestfulServer.REQUEST_START_TIME);
|
||||
if (startTime != null) {
|
||||
long time = System.currentTimeMillis() - startTime.getTime();
|
||||
b.append(" Response generated in ");
|
||||
b.append(time);
|
||||
b.append("ms.");
|
||||
}
|
||||
|
||||
|
||||
b.append("</p>");
|
||||
|
||||
b.append("\n");
|
||||
|
|
|
@ -19,26 +19,12 @@ package ca.uhn.fhir.util;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Reader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.io.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.stream.FactoryConfigurationError;
|
||||
import javax.xml.stream.XMLEventReader;
|
||||
import javax.xml.stream.XMLEventWriter;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLResolver;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import javax.xml.stream.*;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.codehaus.stax2.XMLOutputFactory2;
|
||||
|
@ -47,6 +33,7 @@ import org.codehaus.stax2.io.EscapingWriterFactory;
|
|||
import com.ctc.wstx.api.WstxInputProperties;
|
||||
import com.ctc.wstx.stax.WstxOutputFactory;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.util.jar.DependencyLogFactory;
|
||||
import ca.uhn.fhir.util.jar.IDependencyLog;
|
||||
|
||||
|
@ -62,6 +49,7 @@ public class XmlUtil {
|
|||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(XmlUtil.class);
|
||||
private static Throwable ourNextException;
|
||||
private static volatile XMLOutputFactory ourOutputFactory;
|
||||
private static Boolean ourStaxPresent;
|
||||
private static final Map<String, Integer> VALID_ENTITY_NAMES;
|
||||
private static final ExtendedEntityReplacingXmlResolver XML_RESOLVER = new ExtendedEntityReplacingXmlResolver();
|
||||
|
||||
|
@ -1528,8 +1516,8 @@ public class XmlUtil {
|
|||
// ok
|
||||
}
|
||||
|
||||
XMLOutputFactory outputFactory = XMLOutputFactory.newInstance();
|
||||
|
||||
XMLOutputFactory outputFactory = newOutputFactory();
|
||||
|
||||
if (!ourHaveLoggedStaxImplementation) {
|
||||
logStaxImplementation(outputFactory.getClass());
|
||||
}
|
||||
|
@ -1601,8 +1589,7 @@ public class XmlUtil {
|
|||
// ok
|
||||
}
|
||||
|
||||
XMLInputFactory inputFactory;
|
||||
inputFactory = XMLInputFactory.newInstance();
|
||||
XMLInputFactory inputFactory = newInputFactory();
|
||||
|
||||
if (!ourHaveLoggedStaxImplementation) {
|
||||
logStaxImplementation(inputFactory.getClass());
|
||||
|
@ -1645,7 +1632,6 @@ public class XmlUtil {
|
|||
return ourInputFactory;
|
||||
}
|
||||
|
||||
|
||||
private static XMLOutputFactory getOrCreateOutputFactory() throws FactoryConfigurationError {
|
||||
if (ourOutputFactory == null) {
|
||||
ourOutputFactory = createOutputFactory();
|
||||
|
@ -1661,6 +1647,29 @@ public class XmlUtil {
|
|||
ourHaveLoggedStaxImplementation = true;
|
||||
}
|
||||
|
||||
|
||||
static XMLInputFactory newInputFactory() throws FactoryConfigurationError {
|
||||
XMLInputFactory inputFactory;
|
||||
try {
|
||||
inputFactory = XMLInputFactory.newInstance();
|
||||
throwUnitTestExceptionIfConfiguredToDoSo();
|
||||
} catch (Throwable e) {
|
||||
throw new ConfigurationException("Unable to initialize StAX - XML processing is disabled", e);
|
||||
}
|
||||
return inputFactory;
|
||||
}
|
||||
|
||||
static XMLOutputFactory newOutputFactory() throws FactoryConfigurationError {
|
||||
XMLOutputFactory outputFactory;
|
||||
try {
|
||||
outputFactory = XMLOutputFactory.newInstance();
|
||||
throwUnitTestExceptionIfConfiguredToDoSo();
|
||||
} catch (Throwable e) {
|
||||
throw new ConfigurationException("Unable to initialize StAX - XML processing is disabled", e);
|
||||
}
|
||||
return outputFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* FOR UNIT TESTS ONLY - Throw this exception for the next operation
|
||||
*/
|
||||
|
@ -1691,6 +1700,26 @@ public class XmlUtil {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will return <code>true</code> if a StAX XML parsing library is present
|
||||
* on the classpath
|
||||
*/
|
||||
public static boolean isStaxPresent() {
|
||||
Boolean retVal = ourStaxPresent;
|
||||
if (retVal == null) {
|
||||
try {
|
||||
newInputFactory();
|
||||
ourStaxPresent = Boolean.TRUE;
|
||||
retVal = Boolean.TRUE;
|
||||
} catch (ConfigurationException e) {
|
||||
ourLog.info("StAX not detected on classpath, XML processing will be disabled");
|
||||
ourStaxPresent = Boolean.FALSE;
|
||||
retVal = Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
public static class MyEscaper implements EscapingWriterFactory {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -180,7 +180,9 @@ public class App {
|
|||
try {
|
||||
String[] args = Arrays.asList(theArgs).subList(1, theArgs.length).toArray(new String[theArgs.length - 1]);
|
||||
parsedOptions = parser.parse(options, args, true);
|
||||
|
||||
if (parsedOptions.getArgList().isEmpty()==false) {
|
||||
throw new ParseException("Unrecognized argument: " + parsedOptions.getArgList().get(0).toString());
|
||||
}
|
||||
|
||||
// Actually execute the command
|
||||
command.run(parsedOptions);
|
||||
|
|
|
@ -232,6 +232,7 @@
|
|||
<include>hapi-fhir-structures-dstu3/target/jacoco.exec</include>
|
||||
<include>hapi-fhir-jpaserver-base/target/jacoco.exec</include>
|
||||
<include>hapi-fhir-client-okhttp/target/jacoco.exec</include>
|
||||
<include>hapi-fhir-android/target/jacoco.exec</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
|
|
@ -57,7 +57,7 @@ import ca.uhn.fhir.rest.server.IRestfulServer;
|
|||
* @author Peter Van Houte | peter.vanhoute@agfa.com | Agfa Healthcare
|
||||
*/
|
||||
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_PLAIN })
|
||||
@Consumes({ MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
|
||||
@Consumes({ MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML, Constants.CT_FHIR_JSON_NEW, Constants.CT_FHIR_XML_NEW })
|
||||
@Interceptors(JaxRsExceptionInterceptor.class)
|
||||
public abstract class AbstractJaxRsResourceProvider<R extends IBaseResource> extends AbstractJaxRsProvider
|
||||
|
||||
|
|
|
@ -267,7 +267,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
client.create().resource(p).execute();
|
||||
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri);
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
|
@ -277,7 +277,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
client.create().resource(p).execute();
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
String body = ourRequestBodyString;
|
||||
assertThat(body, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertThat(body, not(containsString("123")));
|
||||
|
@ -302,7 +302,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
client.create().resource(p).conditionalByUrl("Patient?name=foo").execute();
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri);
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue());
|
||||
|
@ -311,7 +311,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
client.create().resource(p).conditionalByUrl("Patient?name=http://foo|bar").execute();
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri);
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue());
|
||||
|
@ -320,7 +320,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
client.create().resource(p).conditional().where(Patient.NAME.matches().value("foo")).execute();
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri);
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestFirstHeaders.get(Constants.HEADER_IF_NONE_EXIST).getValue());
|
||||
|
@ -344,7 +344,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
client.create(p);
|
||||
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient", ourRequestUri);
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
|
@ -839,7 +839,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
//@formatter:on
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri);
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(ourRequestBodyString, reqString);
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
assertEquals(1, resp.getParameter().size());
|
||||
|
@ -875,7 +875,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri);
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"name1\"/><valueString value=\"value1\"/></parameter><parameter><name value=\"name2\"/><valueString value=\"value1\"/></parameter></Parameters>", (ourRequestBodyString));
|
||||
|
||||
|
@ -896,7 +896,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri);
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
assertEquals("<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"name1\"/><valueIdentifier><system value=\"system1\"/><value value=\"value1\"/></valueIdentifier></parameter><parameter><name value=\"name2\"/><valueString value=\"value1\"/></parameter></Parameters>",
|
||||
(ourRequestBodyString));
|
||||
|
@ -918,7 +918,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri);
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
assertEquals(
|
||||
"<Parameters xmlns=\"http://hl7.org/fhir\"><parameter><name value=\"name1\"/><valueIdentifier><system value=\"system1\"/><value value=\"value1\"/></valueIdentifier></parameter><parameter><name value=\"name2\"/><resource><Patient xmlns=\"http://hl7.org/fhir\"><active value=\"true\"/></Patient></resource></parameter></Parameters>",
|
||||
|
@ -1049,7 +1049,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri);
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(ourRequestBodyString, reqString);
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
|
||||
|
@ -1064,7 +1064,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri);
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(ourRequestBodyString, reqString);
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
|
||||
|
@ -1079,7 +1079,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri);
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(ourRequestBodyString, reqString);
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
|
||||
|
@ -1122,7 +1122,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
assertEquals("http://localhost:" + ourPort + "/fhir/$SOMEOPERATION", ourRequestUri);
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(ourRequestBodyString, reqString);
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
|
||||
|
@ -1137,7 +1137,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient/$SOMEOPERATION", ourRequestUri);
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(ourRequestBodyString, reqString);
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
|
||||
|
@ -1152,7 +1152,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123/$SOMEOPERATION", ourRequestUri);
|
||||
assertEquals(respString, p.encodeResourceToString(resp));
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(ourRequestBodyString, reqString);
|
||||
assertEquals("POST", ourRequestMethod);
|
||||
|
||||
|
@ -1644,7 +1644,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
assertEquals("name=james", ourRequestBodyString);
|
||||
|
||||
assertEquals("application/x-www-form-urlencoded", ourRequestContentType);
|
||||
assertEquals(Constants.CT_FHIR_JSON, ourRequestFirstHeaders.get("Accept").getValue());
|
||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, ourRequestFirstHeaders.get("Accept").getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1843,7 +1843,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri);
|
||||
assertThat(response, containsString("\"Bundle\""));
|
||||
assertEquals("application/json+fhir;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue());
|
||||
assertEquals("application/fhir+json;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue());
|
||||
|
||||
//@formatter:off
|
||||
response = client.transaction()
|
||||
|
@ -1853,7 +1853,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
//@formatter:on
|
||||
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/", ourRequestUri);
|
||||
assertEquals("application/xml+fhir;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue());
|
||||
assertEquals("application/fhir+xml;charset=UTF-8", ourRequestFirstHeaders.get("Content-Type").getValue());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1913,7 +1913,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
client.update().resource(p).conditionalByUrl("Patient?name=foo").execute();
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("PUT", ourRequestMethod);
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri);
|
||||
|
@ -1921,7 +1921,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
client.update().resource(p).conditionalByUrl("Patient?name=http://foo|bar").execute();
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("PUT", ourRequestMethod);
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=http%3A//foo%7Cbar", ourRequestUri);
|
||||
|
@ -1929,7 +1929,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditionalByUrl("Patient?name=foo").execute();
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("PUT", ourRequestMethod);
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo", ourRequestUri);
|
||||
|
@ -1937,7 +1937,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
client.update().resource(p).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).execute();
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("PUT", ourRequestMethod);
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri);
|
||||
|
@ -1945,7 +1945,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
client.update().resource(ourCtx.newXmlParser().encodeResourceToString(p)).conditional().where(Patient.NAME.matches().value("foo")).and(Patient.ADDRESS.matches().value("AAA|BBB")).execute();
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("PUT", ourRequestMethod);
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient?name=foo&address=AAA%5C%7CBBB", ourRequestUri);
|
||||
|
@ -1968,7 +1968,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
client.update(new IdType("Patient/123").getValue(), p);
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri);
|
||||
assertEquals("PUT", ourRequestMethod);
|
||||
|
@ -1976,7 +1976,7 @@ public class GenericJaxRsClientDstu3Test {
|
|||
|
||||
client.update("123", p);
|
||||
assertEquals(1, ourRequestHeaders.get(Constants.HEADER_CONTENT_TYPE).size());
|
||||
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, ourRequestFirstHeaders.get(Constants.HEADER_CONTENT_TYPE).getValue().replace(";char", "; char"));
|
||||
assertThat(ourRequestBodyString, containsString("<family value=\"FOOFAMILY\"/>"));
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/Patient/123", ourRequestUri);
|
||||
assertEquals("PUT", ourRequestMethod);
|
||||
|
|
|
@ -48,7 +48,7 @@ import ca.uhn.fhir.rest.server.IPagingProvider;
|
|||
*/
|
||||
@Path(TestJaxRsMockPatientRestProviderDstu3.PATH)
|
||||
@Stateless
|
||||
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML })
|
||||
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML, Constants.CT_FHIR_JSON_NEW, Constants.CT_FHIR_XML_NEW })
|
||||
@Interceptors(JaxRsExceptionInterceptor.class)
|
||||
public class TestJaxRsMockPatientRestProviderDstu3 extends AbstractJaxRsResourceProvider<Patient> {
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
ca.uhn.fhir.jpa.entity.ResourceTable/
|
||||
ca.*/
|
||||
target/
|
||||
/bin
|
||||
nohup.out
|
||||
|
|
Binary file not shown.
|
@ -99,7 +99,18 @@
|
|||
<groupId>org.jscience</groupId>
|
||||
<artifactId>jscience</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- Patch Dependencies -->
|
||||
<dependency>
|
||||
<groupId>net.riotopsys</groupId>
|
||||
<artifactId>json_patch</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.dnault</groupId>
|
||||
<artifactId>xml-patch</artifactId>
|
||||
<version>0.3.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- FHIR RI is pulled in for UCUM support, but we don't want any of its dependencies. -->
|
||||
<!-- <dependency> <groupId>me.fhir</groupId> <artifactId>fhir-dstu1</artifactId> <version>0.0.81.2489</version> <exclusions> <exclusion> <artifactId>Saxon-HE</artifactId> <groupId>net.sf.saxon</groupId>
|
||||
</exclusion> <exclusion> <artifactId>commons-discovery</artifactId> <groupId>commons-discovery</groupId> </exclusion> <exclusion> <artifactId>commons-codec</artifactId> <groupId>commons-codec</groupId>
|
||||
|
|
|
@ -22,28 +22,15 @@ package ca.uhn.fhir.jpa.dao;
|
|||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.TypedQuery;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
@ -57,33 +44,21 @@ import ca.uhn.fhir.context.RuntimeSearchParam;
|
|||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedSearchParamUriDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.ISearchResultDao;
|
||||
import ca.uhn.fhir.jpa.entity.BaseHasResource;
|
||||
import ca.uhn.fhir.jpa.entity.BaseTag;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.entity.TagDefinition;
|
||||
import ca.uhn.fhir.jpa.entity.TagTypeEnum;
|
||||
import ca.uhn.fhir.jpa.entity.*;
|
||||
import ca.uhn.fhir.jpa.interceptor.IJpaServerInterceptor;
|
||||
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
|
||||
import ca.uhn.fhir.jpa.util.DeleteConflict;
|
||||
import ca.uhn.fhir.jpa.util.StopWatch;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.jpa.util.jsonpatch.JsonPatchUtils;
|
||||
import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils;
|
||||
import ca.uhn.fhir.model.api.*;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.method.RestSearchParameterTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.*;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
|
@ -205,7 +180,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
final ResourceTable entity = readEntityLatestVersion(theId);
|
||||
if (theId.hasVersionIdPart() && Long.parseLong(theId.getVersionIdPart()) != entity.getVersion()) {
|
||||
throw new InvalidRequestException("Trying to delete " + theId + " but this is not the current version");
|
||||
throw new ResourceVersionConflictException("Trying to delete " + theId + " but this is not the current version");
|
||||
}
|
||||
|
||||
validateOkToDelete(deleteConflicts, entity);
|
||||
|
@ -1063,7 +1038,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
|
||||
if (resourceId.hasVersionIdPart() && Long.parseLong(resourceId.getVersionIdPart()) != entity.getVersion()) {
|
||||
throw new InvalidRequestException("Trying to update " + resourceId + " but this is not the current version");
|
||||
throw new ResourceVersionConflictException("Trying to update " + resourceId + " but this is not the current version");
|
||||
}
|
||||
|
||||
if (resourceId.hasResourceType() && !resourceId.getResourceType().equals(getResourceName())) {
|
||||
|
@ -1100,6 +1075,31 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
return outcome;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DaoMethodOutcome patch(IIdType theId, PatchTypeEnum thePatchType, String thePatchBody, RequestDetails theRequestDetails) {
|
||||
ResourceTable entityToUpdate = readEntityLatestVersion(theId);
|
||||
if (theId.hasVersionIdPart()) {
|
||||
if (theId.getVersionIdPartAsLong() != entityToUpdate.getVersion()) {
|
||||
throw new ResourceVersionConflictException("Version " + theId.getVersionIdPart() + " is not the most recent version of this resource, unable to apply patch");
|
||||
}
|
||||
}
|
||||
|
||||
validateResourceType(entityToUpdate);
|
||||
|
||||
IBaseResource resourceToUpdate = toResource(entityToUpdate, false);
|
||||
IBaseResource destination;
|
||||
if (thePatchType == PatchTypeEnum.JSON_PATCH) {
|
||||
destination = JsonPatchUtils.apply(getContext(), resourceToUpdate, thePatchBody);
|
||||
} else {
|
||||
destination = XmlPatchUtils.apply(getContext(), resourceToUpdate, thePatchBody);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
T destinationCasted = (T) destination;
|
||||
return update(destinationCasted, null, true, theRequestDetails);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public DaoMethodOutcome update(T theResource, String theMatchUrl, RequestDetails theRequestDetails) {
|
||||
return update(theResource, theMatchUrl, true, theRequestDetails);
|
||||
|
|
|
@ -36,6 +36,7 @@ import ca.uhn.fhir.jpa.util.DeleteConflict;
|
|||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
|
@ -210,6 +211,8 @@ public interface IFhirResourceDao<T extends IBaseResource> extends IDao {
|
|||
*/
|
||||
MethodOutcome validate(T theResource, IIdType theId, String theRawResource, EncodingEnum theEncoding, ValidationModeEnum theMode, String theProfile, RequestDetails theRequestDetails);
|
||||
|
||||
DaoMethodOutcome patch(IIdType theId, PatchTypeEnum thePatchType, String thePatchBody, RequestDetails theRequestDetails);
|
||||
|
||||
// /**
|
||||
// * Invoke the everything operation
|
||||
// */
|
||||
|
|
|
@ -28,14 +28,11 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.springframework.beans.factory.annotation.Required;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.DaoMethodOutcome;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.rest.annotation.At;
|
||||
import ca.uhn.fhir.rest.annotation.GetTags;
|
||||
import ca.uhn.fhir.rest.annotation.History;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.Since;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
|
@ -128,6 +125,16 @@ public abstract class BaseJpaResourceProvider<T extends IBaseResource> extends B
|
|||
}
|
||||
}
|
||||
|
||||
@Patch
|
||||
public DaoMethodOutcome patch(HttpServletRequest theRequest, @IdParam IIdType theId, RequestDetails theRequestDetails, @ResourceParam String theBody, PatchTypeEnum thePatchType) {
|
||||
startRequest(theRequest);
|
||||
try {
|
||||
return myDao.patch(theId, thePatchType, theBody, theRequestDetails);
|
||||
} finally {
|
||||
endRequest(theRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@Required
|
||||
public void setDao(IFhirResourceDao<T> theDao) {
|
||||
myDao = theDao;
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package ca.uhn.fhir.jpa.util.jsonpatch;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.riotopsys.json_patch.JsonPath;
|
||||
import net.riotopsys.json_patch.operation.AbsOperation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class CopyOperation extends AbsOperation {
|
||||
|
||||
public JsonPath mySourcePath;
|
||||
|
||||
public CopyOperation(JsonPath theTargetPath, JsonPath theSourcePath) {
|
||||
mySourcePath = theSourcePath;
|
||||
this.path = theTargetPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOperationName() {
|
||||
return "copy";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement apply(JsonElement original) {
|
||||
JsonElement result = duplicate(original);
|
||||
|
||||
JsonElement item = path.head().navigate(result);
|
||||
JsonElement data = mySourcePath.head().navigate(original);
|
||||
|
||||
if (item.isJsonObject()) {
|
||||
item.getAsJsonObject().add(path.tail(), data);
|
||||
} else if (item.isJsonArray()) {
|
||||
|
||||
JsonArray array = item.getAsJsonArray();
|
||||
|
||||
int index = (path.tail().equals("-")) ? array.size() : Integer.valueOf(path.tail());
|
||||
|
||||
List<JsonElement> temp = new ArrayList<JsonElement>();
|
||||
|
||||
Iterator<JsonElement> iter = array.iterator();
|
||||
while (iter.hasNext()) {
|
||||
JsonElement stuff = iter.next();
|
||||
iter.remove();
|
||||
temp.add(stuff);
|
||||
}
|
||||
|
||||
temp.add(index, data);
|
||||
|
||||
for (JsonElement stuff : temp) {
|
||||
array.add(stuff);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package ca.uhn.fhir.jpa.util.jsonpatch;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.JsonParser;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import net.riotopsys.json_patch.JsonPatch;
|
||||
import net.riotopsys.json_patch.JsonPath;
|
||||
import net.riotopsys.json_patch.operation.AddOperation;
|
||||
import net.riotopsys.json_patch.operation.MoveOperation;
|
||||
import net.riotopsys.json_patch.operation.RemoveOperation;
|
||||
import net.riotopsys.json_patch.operation.ReplaceOperation;
|
||||
|
||||
public class JsonPatchUtils {
|
||||
|
||||
public static <T extends IBaseResource> T apply(FhirContext theCtx, T theResourceToUpdate, String thePatchBody) {
|
||||
JsonPatch parsedPatch = new JsonPatch();
|
||||
|
||||
// Parse the patch
|
||||
Gson gson = JsonParser.newGson();
|
||||
JsonElement jsonElement = gson.fromJson(thePatchBody, JsonElement.class);
|
||||
JsonArray array = jsonElement.getAsJsonArray();
|
||||
for (JsonElement nextElement : array) {
|
||||
JsonObject nextElementAsObject = (JsonObject) nextElement;
|
||||
|
||||
String opName = nextElementAsObject.get("op").getAsString();
|
||||
if ("add".equals(opName)) {
|
||||
AddOperation op = new AddOperation(toPath(nextElementAsObject), nextElementAsObject.get("value"));
|
||||
parsedPatch.add(op);
|
||||
} else if ("remove".equals(opName)) {
|
||||
RemoveOperation op = new RemoveOperation(toPath(nextElementAsObject));
|
||||
parsedPatch.add(op);
|
||||
} else if ("replace".equals(opName)) {
|
||||
ReplaceOperation op = new ReplaceOperation(toPath(nextElementAsObject), nextElementAsObject.get("value"));
|
||||
parsedPatch.add(op);
|
||||
} else if ("copy".equals(opName)) {
|
||||
CopyOperation op = new CopyOperation(toPath(nextElementAsObject), toFromPath(nextElementAsObject));
|
||||
parsedPatch.add(op);
|
||||
} else if ("move".equals(opName)) {
|
||||
MoveOperation op = new MoveOperation(toPath(nextElementAsObject), toFromPath(nextElementAsObject));
|
||||
parsedPatch.add(op);
|
||||
} else {
|
||||
throw new InvalidRequestException("Invalid JSON PATCH operation: " + opName);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<T> clazz = (Class<T>) theResourceToUpdate.getClass();
|
||||
|
||||
JsonElement originalJsonDocument = gson.fromJson(theCtx.newJsonParser().encodeResourceToString(theResourceToUpdate), JsonElement.class);
|
||||
JsonElement target = parsedPatch.apply(originalJsonDocument);
|
||||
T retVal = theCtx.newJsonParser().parseResource(clazz, gson.toJson(target));
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private static JsonPath toFromPath(JsonObject nextElementAsObject) {
|
||||
return new JsonPath(nextElementAsObject.get("from").getAsString());
|
||||
}
|
||||
|
||||
private static JsonPath toPath(JsonObject nextElementAsObject) {
|
||||
return new JsonPath(nextElementAsObject.get("path").getAsString());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package ca.uhn.fhir.jpa.util.xmlpatch;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import com.github.dnault.xmlpatch.Patcher;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
|
||||
public class XmlPatchUtils {
|
||||
|
||||
public static <T extends IBaseResource> T apply(FhirContext theCtx, T theResourceToUpdate, String thePatchBody) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<T> clazz = (Class<T>) theResourceToUpdate.getClass();
|
||||
|
||||
String inputResource = theCtx.newXmlParser().encodeResourceToString(theResourceToUpdate);
|
||||
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
try {
|
||||
Patcher.patch(new ByteArrayInputStream(inputResource.getBytes(Constants.CHARSET_UTF8)), new ByteArrayInputStream(thePatchBody.getBytes(Constants.CHARSET_UTF8)), result);
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
String resultString = new String(result.toByteArray(), Constants.CHARSET_UTF8);
|
||||
T retVal = theCtx.newXmlParser().parseResource(clazz, resultString);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -880,7 +880,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
|
|||
try {
|
||||
myPatientDao.delete(id2, mySrd);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
} catch (ResourceVersionConflictException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
|
|
|
@ -1046,7 +1046,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
|
|||
try {
|
||||
myPatientDao.delete(id2, mySrd);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
} catch (ResourceVersionConflictException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
|
|
|
@ -36,83 +36,39 @@ import org.apache.commons.io.IOUtils;
|
|||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.*;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.hl7.fhir.dstu3.model.AuditEvent;
|
||||
import org.hl7.fhir.dstu3.model.BaseResource;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.*;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.BundleType;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode;
|
||||
import org.hl7.fhir.dstu3.model.CodeSystem;
|
||||
import org.hl7.fhir.dstu3.model.CodeType;
|
||||
import org.hl7.fhir.dstu3.model.Coding;
|
||||
import org.hl7.fhir.dstu3.model.Condition;
|
||||
import org.hl7.fhir.dstu3.model.DateTimeType;
|
||||
import org.hl7.fhir.dstu3.model.DateType;
|
||||
import org.hl7.fhir.dstu3.model.Device;
|
||||
import org.hl7.fhir.dstu3.model.DiagnosticRequest;
|
||||
import org.hl7.fhir.dstu3.model.DocumentManifest;
|
||||
import org.hl7.fhir.dstu3.model.DocumentReference;
|
||||
import org.hl7.fhir.dstu3.model.Encounter;
|
||||
import org.hl7.fhir.dstu3.model.Encounter.EncounterLocationComponent;
|
||||
import org.hl7.fhir.dstu3.model.Encounter.EncounterStatus;
|
||||
import org.hl7.fhir.dstu3.model.Enumerations.AdministrativeGender;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.ImagingStudy;
|
||||
import org.hl7.fhir.dstu3.model.InstantType;
|
||||
import org.hl7.fhir.dstu3.model.Location;
|
||||
import org.hl7.fhir.dstu3.model.Medication;
|
||||
import org.hl7.fhir.dstu3.model.MedicationOrder;
|
||||
import org.hl7.fhir.dstu3.model.Meta;
|
||||
import org.hl7.fhir.dstu3.model.Narrative.NarrativeStatus;
|
||||
import org.hl7.fhir.dstu3.model.Observation;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.Organization;
|
||||
import org.hl7.fhir.dstu3.model.Parameters;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.Period;
|
||||
import org.hl7.fhir.dstu3.model.Practitioner;
|
||||
import org.hl7.fhir.dstu3.model.Questionnaire;
|
||||
import org.hl7.fhir.dstu3.model.Questionnaire.QuestionnaireItemType;
|
||||
import org.hl7.fhir.dstu3.model.QuestionnaireResponse;
|
||||
import org.hl7.fhir.dstu3.model.Reference;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.dstu3.model.Subscription;
|
||||
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionChannelType;
|
||||
import org.hl7.fhir.dstu3.model.Subscription.SubscriptionStatus;
|
||||
import org.hl7.fhir.dstu3.model.TemporalPrecisionEnum;
|
||||
import org.hl7.fhir.dstu3.model.UnsignedIntType;
|
||||
import org.hl7.fhir.dstu3.model.ValueSet;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.NestedExceptionUtils;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
import ca.uhn.fhir.model.primitive.UriDt;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.rest.api.ValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||
import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.*;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
|
@ -130,7 +86,6 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchPagingKeepsOldSearches() throws Exception {
|
||||
String methodName = "testSearchPagingKeepsOldSearches";
|
||||
|
@ -148,16 +103,10 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
patient.addName().addFamily(methodName).addGiven("Joe");
|
||||
myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
List<String> linkNext = Lists.newArrayList();
|
||||
for (int i = 0 ; i < 100; i++) {
|
||||
Bundle bundle = ourClient
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(Patient.NAME.matches().value("testSearchPagingKeepsOldSearches"))
|
||||
.count(5)
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
List<String> linkNext = Lists.newArrayList();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
Bundle bundle = ourClient.search().forResource(Patient.class).where(Patient.NAME.matches().value("testSearchPagingKeepsOldSearches")).count(5).returnBundle(Bundle.class).execute();
|
||||
assertTrue(isNotBlank(bundle.getLink("next").getUrl()));
|
||||
assertEquals(5, bundle.getEntry().size());
|
||||
linkNext.add(bundle.getLink("next").getUrl());
|
||||
|
@ -171,7 +120,129 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testPatchUsingJsonPatch() throws Exception {
|
||||
String methodName = "testPatchUsingJsonPatch";
|
||||
IIdType pid1;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.setActive(true);
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("0");
|
||||
patient.addName().addFamily(methodName).addGiven("Joe");
|
||||
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
HttpPatch patch = new HttpPatch(ourServerBase + "/Patient/" + pid1.getIdPart());
|
||||
patch.setEntity(new StringEntity("[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]", ContentType.parse(Constants.CT_JSON_PATCH + Constants.CHARSET_UTF8_CTSUFFIX)));
|
||||
|
||||
CloseableHttpResponse response = ourHttpClient.execute(patch);
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertThat(responseString, containsString("<OperationOutcome"));
|
||||
assertThat(responseString, containsString("INFORMATION"));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
Patient newPt = ourClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
||||
assertEquals("2", newPt.getIdElement().getVersionIdPart());
|
||||
assertEquals(false, newPt.getActive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatchUsingJsonPatchWithContentionCheckGood() throws Exception {
|
||||
String methodName = "testPatchUsingJsonPatchWithContentionCheckGood";
|
||||
IIdType pid1;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.setActive(true);
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("0");
|
||||
patient.addName().addFamily(methodName).addGiven("Joe");
|
||||
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
HttpPatch patch = new HttpPatch(ourServerBase + "/Patient/" + pid1.getIdPart());
|
||||
patch.setEntity(new StringEntity("[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]", ContentType.parse(Constants.CT_JSON_PATCH + Constants.CHARSET_UTF8_CTSUFFIX)));
|
||||
patch.addHeader("If-Match", "W/\"1\"");
|
||||
|
||||
CloseableHttpResponse response = ourHttpClient.execute(patch);
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertThat(responseString, containsString("<OperationOutcome"));
|
||||
assertThat(responseString, containsString("INFORMATION"));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
Patient newPt = ourClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
||||
assertEquals("2", newPt.getIdElement().getVersionIdPart());
|
||||
assertEquals(false, newPt.getActive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatchUsingJsonPatchWithContentionCheckBad() throws Exception {
|
||||
String methodName = "testPatchUsingJsonPatchWithContentionCheckBad";
|
||||
IIdType pid1;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.setActive(true);
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("0");
|
||||
patient.addName().addFamily(methodName).addGiven("Joe");
|
||||
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
HttpPatch patch = new HttpPatch(ourServerBase + "/Patient/" + pid1.getIdPart());
|
||||
patch.setEntity(new StringEntity("[ { \"op\":\"replace\", \"path\":\"/active\", \"value\":false } ]", ContentType.parse(Constants.CT_JSON_PATCH + Constants.CHARSET_UTF8_CTSUFFIX)));
|
||||
patch.addHeader("If-Match", "W/\"9\"");
|
||||
|
||||
CloseableHttpResponse response = ourHttpClient.execute(patch);
|
||||
try {
|
||||
assertEquals(409, response.getStatusLine().getStatusCode());
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertThat(responseString, containsString("<OperationOutcome"));
|
||||
assertThat(responseString, containsString("<diagnostics value=\"Version 9 is not the most recent version of this resource, unable to apply patch\"/>"));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
Patient newPt = ourClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
||||
assertEquals("1", newPt.getIdElement().getVersionIdPart());
|
||||
assertEquals(true, newPt.getActive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatchUsingXmlPatch() throws Exception {
|
||||
String methodName = "testPatchUsingXmlPatch";
|
||||
IIdType pid1;
|
||||
{
|
||||
Patient patient = new Patient();
|
||||
patient.setActive(true);
|
||||
patient.addIdentifier().setSystem("urn:system").setValue("0");
|
||||
patient.addName().addFamily(methodName).addGiven("Joe");
|
||||
pid1 = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
}
|
||||
|
||||
HttpPatch patch = new HttpPatch(ourServerBase + "/Patient/" + pid1.getIdPart());
|
||||
String patchString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><diff xmlns:fhir=\"http://hl7.org/fhir\"><replace sel=\"fhir:Patient/fhir:active/@value\">false</replace></diff>";
|
||||
patch.setEntity(new StringEntity(patchString, ContentType.parse(Constants.CT_XML_PATCH + Constants.CHARSET_UTF8_CTSUFFIX)));
|
||||
|
||||
CloseableHttpResponse response = ourHttpClient.execute(patch);
|
||||
try {
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertThat(responseString, containsString("<OperationOutcome"));
|
||||
assertThat(responseString, containsString("INFORMATION"));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
Patient newPt = ourClient.read().resource(Patient.class).withId(pid1.getIdPart()).execute();
|
||||
assertEquals("2", newPt.getIdElement().getVersionIdPart());
|
||||
assertEquals(false, newPt.getActive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasParameter() throws Exception {
|
||||
IIdType pid0;
|
||||
|
@ -227,7 +298,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
patient.getName().get(0).getFamily().get(0).setValue(methodName + "_i");
|
||||
ids.add(myPatientDao.update(patient, mySrd).getId().toUnqualified().getValue());
|
||||
}
|
||||
|
||||
|
||||
List<String> idValues;
|
||||
|
||||
idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/Patient/" + id.getIdPart() + "/_history?_at=gt" + toStr(preDates.get(0)) + "&_at=lt" + toStr(preDates.get(3)));
|
||||
|
@ -238,7 +309,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/_history?_at=gt" + toStr(preDates.get(0)) + "&_at=lt" + toStr(preDates.get(3)));
|
||||
assertThat(idValues.toString(), idValues, contains(ids.get(2), ids.get(1), ids.get(0)));
|
||||
|
||||
|
||||
idValues = searchAndReturnUnqualifiedIdValues(ourServerBase + "/_history?_at=gt2060");
|
||||
assertThat(idValues.toString(), idValues, empty());
|
||||
|
||||
|
@ -493,13 +564,13 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
org.setName("ORG");
|
||||
IIdType orgId = ourClient.create().resource(org).execute().getId();
|
||||
assertEquals("1", orgId.getVersionIdPart());
|
||||
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
|
||||
patient.getManagingOrganization().setReference(orgId.toUnqualified().getValue());
|
||||
IIdType patientId = ourClient.create().resource(patient).execute().getId();
|
||||
assertEquals("1", patientId.getVersionIdPart());
|
||||
|
||||
|
||||
AuditEvent ae = new org.hl7.fhir.dstu3.model.AuditEvent();
|
||||
ae.addEntity().getReference().setReference(patientId.toUnqualified().getValue());
|
||||
IIdType aeId = ourClient.create().resource(ae).execute().getId();
|
||||
|
@ -508,11 +579,11 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
patient = ourClient.read().resource(Patient.class).withId(patientId).execute();
|
||||
assertTrue(patient.getManagingOrganization().getReferenceElement().hasIdPart());
|
||||
assertFalse(patient.getManagingOrganization().getReferenceElement().hasVersionIdPart());
|
||||
|
||||
|
||||
ae = ourClient.read().resource(AuditEvent.class).withId(aeId).execute();
|
||||
assertTrue(ae.getEntityFirstRep().getReference().getReferenceElement().hasIdPart());
|
||||
assertTrue(ae.getEntityFirstRep().getReference().getReferenceElement().hasVersionIdPart());
|
||||
|
||||
|
||||
}
|
||||
|
||||
// private void delete(String theResourceType, String theParamName, String theParamValue) {
|
||||
|
@ -624,7 +695,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
String resource = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(pt);
|
||||
|
||||
ourLog.info("Input: {}", resource);
|
||||
|
||||
|
||||
HttpPost post = new HttpPost(ourServerBase + "/Patient");
|
||||
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
|
@ -642,7 +713,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
assertEquals("1", id.getVersionIdPart());
|
||||
assertNotEquals("AAA", id.getIdPart());
|
||||
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Patient/" + id.getIdPart());
|
||||
response = ourHttpClient.execute(get);
|
||||
try {
|
||||
|
@ -666,7 +737,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
String resource = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(pt);
|
||||
|
||||
ourLog.info("Input: {}", resource);
|
||||
|
||||
|
||||
HttpPost post = new HttpPost(ourServerBase + "/Patient");
|
||||
post.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(post);
|
||||
|
@ -684,7 +755,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
assertEquals("1", id.getVersionIdPart());
|
||||
assertNotEquals("AAA", id.getIdPart());
|
||||
|
||||
|
||||
HttpPut put = new HttpPut(ourServerBase + "/Patient/" + id.getIdPart() + "/_history/1");
|
||||
put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
response = ourHttpClient.execute(put);
|
||||
|
@ -701,7 +772,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
assertEquals("2", id.getVersionIdPart());
|
||||
assertNotEquals("AAA", id.getIdPart());
|
||||
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Patient/" + id.getIdPart());
|
||||
response = ourHttpClient.execute(get);
|
||||
try {
|
||||
|
@ -894,7 +965,8 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
String encoded = myFhirCtx.newXmlParser().encodeResourceToString(response);
|
||||
ourLog.info(encoded);
|
||||
assertThat(encoded, containsString("<issue><severity value=\"information\"/><code value=\"informational\"/><diagnostics value=\"Successfully deleted Patient?identifier=testDeleteConditionalMultiple resource(s) in 2ms\"/></issue>"));
|
||||
assertThat(encoded, containsString(
|
||||
"<issue><severity value=\"information\"/><code value=\"informational\"/><diagnostics value=\"Successfully deleted Patient?identifier=testDeleteConditionalMultiple resource(s) in 2ms\"/></issue>"));
|
||||
try {
|
||||
ourClient.read().resource("Patient").withId(id1).execute();
|
||||
fail();
|
||||
|
@ -912,7 +984,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
@Test
|
||||
public void testDeleteConditionalNoMatches() throws Exception {
|
||||
String methodName = "testDeleteConditionalNoMatches";
|
||||
|
||||
|
||||
HttpDelete delete = new HttpDelete(ourServerBase + "/Patient?identifier=" + methodName);
|
||||
CloseableHttpResponse resp = ourHttpClient.execute(delete);
|
||||
try {
|
||||
|
@ -920,7 +992,8 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
String response = IOUtils.toString(resp.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(response);
|
||||
assertEquals(200, resp.getStatusLine().getStatusCode());
|
||||
assertThat(response, containsString("<issue><severity value=\"warning\"/><code value=\"not-found\"/><diagnostics value=\"Unable to find resource matching URL "Patient?identifier=testDeleteConditionalNoMatches". Deletion failed.\"/></issue>"));
|
||||
assertThat(response, containsString(
|
||||
"<issue><severity value=\"warning\"/><code value=\"not-found\"/><diagnostics value=\"Unable to find resource matching URL "Patient?identifier=testDeleteConditionalNoMatches". Deletion failed.\"/></issue>"));
|
||||
} finally {
|
||||
IOUtils.closeQuietly(resp);
|
||||
}
|
||||
|
@ -1648,10 +1721,18 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
|
||||
assertEquals(id.withVersion("2").getValue(), history.getEntry().get(1).getResource().getId());
|
||||
assertEquals(HTTPVerb.DELETE, history.getEntry().get(1).getRequest().getMethodElement().getValue());
|
||||
assertEquals("http://localhost:" + ourPort + "/fhir/context/Patient/" + id.getIdPart() + "/_history/2", history.getEntry().get(1).getRequest().getUrl());
|
||||
assertEquals(0, ((Patient) history.getEntry().get(1).getResource()).getName().size());
|
||||
|
||||
assertEquals(id.withVersion("1").getValue(), history.getEntry().get(2).getResource().getId());
|
||||
assertEquals(1, ((Patient) history.getEntry().get(2).getResource()).getName().size());
|
||||
|
||||
try {
|
||||
myBundleDao.validate(history, null, null, null, null, null, mySrd);
|
||||
} catch (PreconditionFailedException e) {
|
||||
ourLog.info(myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(e.getOperationOutcome()));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2569,6 +2650,47 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateWithETag() throws IOException, Exception {
|
||||
String methodName = "testUpdateWithETag";
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.addName().addFamily(methodName);
|
||||
IIdType id = ourClient.create().resource(pt).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
pt.addName().addFamily("FAM2");
|
||||
String resource = myFhirCtx.newXmlParser().encodeResourceToString(pt);
|
||||
|
||||
HttpPut put = new HttpPut(ourServerBase + "/Patient/" + id.getIdPart());
|
||||
put.addHeader(Constants.HEADER_IF_MATCH, "W/\"44\"");
|
||||
put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
CloseableHttpResponse response = ourHttpClient.execute(put);
|
||||
try {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseString);
|
||||
assertEquals(409, response.getStatusLine().getStatusCode());
|
||||
OperationOutcome oo = myFhirCtx.newXmlParser().parseResource(OperationOutcome.class, responseString);
|
||||
assertThat(oo.getIssue().get(0).getDiagnostics(), containsString("Trying to update Patient/" + id.getIdPart() + "/_history/44 but this is not the current version"));
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
// Now a good one
|
||||
put = new HttpPut(ourServerBase + "/Patient/" + id.getIdPart());
|
||||
put.addHeader(Constants.HEADER_IF_MATCH, "W/\"1\"");
|
||||
put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||
response = ourHttpClient.execute(put);
|
||||
try {
|
||||
String responseString = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseString);
|
||||
assertEquals(200, response.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUpdateInvalidReference2() throws IOException, Exception {
|
||||
String methodName = "testUpdateInvalidReference2";
|
||||
|
@ -2915,7 +3037,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
|
|||
patient.addCommunication().setPreferred(true); // missing language
|
||||
|
||||
IIdType id = myPatientDao.create(patient, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
|
||||
HttpGet get = new HttpGet(ourServerBase + "/Patient/" + id.getIdPart() + "/$validate");
|
||||
CloseableHttpResponse response = ourHttpClient.execute(get);
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package ca.uhn.fhir.jpa.util.jsonpatch;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class JsonPatchTest {
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
|
||||
@Test
|
||||
public void testPatchReplace() {
|
||||
Patient p = new Patient();
|
||||
p.setActive(true);
|
||||
|
||||
//@formatter:off
|
||||
String patchBody = "[\n" +
|
||||
" { \"op\":\"replace\", \"path\":\"/active\", \"value\":false }\n" +
|
||||
"]";
|
||||
//@formatter:on
|
||||
|
||||
Patient dest = JsonPatchUtils.apply(ourCtx, p, patchBody);
|
||||
assertEquals(false, dest.getActive());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package ca.uhn.fhir.jpa.util.xmlpatch;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.util.xmlpatch.XmlPatchUtils;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class XmlPatchTest {
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
|
||||
@Test
|
||||
public void testPatchReplace() {
|
||||
Patient p = new Patient();
|
||||
p.setActive(true);
|
||||
|
||||
//@formatter:off
|
||||
String patchBody = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><diff xmlns:fhir=\"http://hl7.org/fhir\"><replace sel=\"fhir:Patient/fhir:active/@value\">false</replace></diff>";
|
||||
//@formatter:on
|
||||
|
||||
Patient dest = XmlPatchUtils.apply(ourCtx, p, patchBody);
|
||||
assertEquals(false, dest.getActive());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,284 @@
|
|||
{
|
||||
"resourceType": "Bundle",
|
||||
"id": "2016-09-13T14:16:23.533-04:00",
|
||||
"type": "transaction",
|
||||
"entry": [
|
||||
{
|
||||
"fullUrl": "http://64.87.15.70:8087/server-web/baseDstu3/Patient/sisansarahId",
|
||||
"resource": {
|
||||
"resourceType": "Patient",
|
||||
"id": "sisansarahId",
|
||||
"identifier": [
|
||||
{
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://hl7.org/fhir/v2/0203",
|
||||
"code": "MR"
|
||||
}
|
||||
]
|
||||
},
|
||||
"system": "urn:oid:1.2.3.4.5.6.7.8.10",
|
||||
"value": "sisansarahId"
|
||||
}
|
||||
],
|
||||
"name": [
|
||||
{
|
||||
"family": [
|
||||
"Sisansarah Lorianthah"
|
||||
],
|
||||
"given": [
|
||||
"Piggy"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"url": "Patient/1-sisansarahId"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fullUrl": "http://64.87.15.70:8087/server-web/baseDstu3/Device/ecde3d4e58532d31",
|
||||
"resource": {
|
||||
"resourceType": "Device",
|
||||
"id": "ecde3d4e58532d31",
|
||||
"identifier": [
|
||||
{
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"value": "ecde3d4e58532d31"
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"code": "531981",
|
||||
"display": "MDC_MOC_VMS_MDS_AHD"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"url": "Device/1-ecde3d4e58532d31"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fullUrl": "http://64.87.15.70:8087/server-web/baseDstu3/DeviceComponent/AHD-ecde3d4e58532d31",
|
||||
"resource": {
|
||||
"resourceType": "DeviceComponent",
|
||||
"id": "AHD-ecde3d4e58532d31",
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"code": "531981",
|
||||
"display": "MDC_MOC_VMS_MDS_AHD"
|
||||
}
|
||||
]
|
||||
},
|
||||
"identifier": {
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"value": "ecde3d4e58532d31"
|
||||
},
|
||||
"lastSystemChange": "2016-09-13T14:16:23-04:00",
|
||||
"source": {
|
||||
"reference": "Device/ecde3d4e58532d31"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"url": "DeviceComponent/1-AHD-ecde3d4e58532d31"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fullUrl": "http://64.87.15.70:8087/server-web/baseDstu3/Device/006019fffe31b743",
|
||||
"resource": {
|
||||
"resourceType": "Device",
|
||||
"id": "006019fffe31b743",
|
||||
"identifier": [
|
||||
{
|
||||
"use": "official",
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"value": "006019fffe31b743",
|
||||
"assigner": {
|
||||
"reference": "IEEE"
|
||||
}
|
||||
},
|
||||
{
|
||||
"use": "official",
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"value": "00:60:19:31:B7:43",
|
||||
"assigner": {
|
||||
"reference": "Bluetooth SIG"
|
||||
}
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"code": "528401",
|
||||
"display": "MDC_DEV_SPEC_PROFILE_GLUCOSE"
|
||||
}
|
||||
]
|
||||
},
|
||||
"manufacturer": "Roche",
|
||||
"model": "502"
|
||||
},
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"url": "Device/1-006019fffe31b743"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fullUrl": "http://64.87.15.70:8087/server-web/baseDstu3/DeviceComponent/Agent-006019fffe31b743",
|
||||
"resource": {
|
||||
"resourceType": "DeviceComponent",
|
||||
"id": "Agent-006019fffe31b743",
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"code": "65573",
|
||||
"display": "MDC_MOC_VMS_MDS_SIMP"
|
||||
}
|
||||
]
|
||||
},
|
||||
"identifier": {
|
||||
"use": "official",
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"value": "006019fffe31b743",
|
||||
"assigner": {
|
||||
"reference": "IEEE"
|
||||
}
|
||||
},
|
||||
"lastSystemChange": "2016-09-13T14:16:23-04:00",
|
||||
"source": {
|
||||
"reference": "Device/006019fffe31b743"
|
||||
},
|
||||
"productionSpecification": [
|
||||
{
|
||||
"specType": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"code": "531972",
|
||||
"display": "MDC_ID_PROD_SPEC_SERIAL"
|
||||
}
|
||||
]
|
||||
},
|
||||
"productionSpec": "01161027"
|
||||
},
|
||||
{
|
||||
"specType": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"code": "531976",
|
||||
"display": "MDC_ID_PROD_SPEC_FW"
|
||||
}
|
||||
]
|
||||
},
|
||||
"productionSpec": "5.87"
|
||||
}
|
||||
]
|
||||
},
|
||||
"request": {
|
||||
"method": "PUT",
|
||||
"url": "DeviceComponent/1-Agent-006019fffe31b743"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fullUrl": "http://64.87.15.70:8087/server-web/baseDstu3/Observation/OBR_1:1.0.0.1",
|
||||
"resource": {
|
||||
"resourceType": "Observation",
|
||||
"id": "OBR_1:1.0.0.1",
|
||||
"status": "final",
|
||||
"code": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"code": "160368",
|
||||
"display": "MDC_CONC_GLU_UNDETERMINED_PLASMA"
|
||||
},
|
||||
{
|
||||
"system": "http://hl7.org/fhir/data-types",
|
||||
"code": "Quantity",
|
||||
"display": "Value is a Numeric Data Type integer"
|
||||
}
|
||||
]
|
||||
},
|
||||
"subject": {
|
||||
"reference": "Patient/sisansarahId"
|
||||
},
|
||||
"effectiveDateTime": "2016-08-01T17:19:58-04:00",
|
||||
"performer": [
|
||||
{
|
||||
"reference": "Patient/sisansarahId"
|
||||
}
|
||||
],
|
||||
"valueQuantity": {
|
||||
"value": 105,
|
||||
"unit": "mg/dl",
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"code": "264274"
|
||||
},
|
||||
"device": {
|
||||
"reference": "Device/006019fffe31b743"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "Observation/1-OBR_1:1.0.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fullUrl": "http://64.87.15.70:8087/server-web/baseDstu3/Observation/OBR_1:1.0.0.2",
|
||||
"resource": {
|
||||
"resourceType": "Observation",
|
||||
"id": "OBR_1:1.0.0.2",
|
||||
"status": "final",
|
||||
"code": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"code": "8417864",
|
||||
"display": "MDC_CTXT_GLU_MEAL"
|
||||
},
|
||||
{
|
||||
"code": "code",
|
||||
"display": "Value is an MDC code"
|
||||
}
|
||||
]
|
||||
},
|
||||
"subject": {
|
||||
"reference": "Patient/sisansarahId"
|
||||
},
|
||||
"effectiveDateTime": "2016-08-01T17:19:58-04:00",
|
||||
"performer": [
|
||||
{
|
||||
"reference": "Patient/sisansarahId"
|
||||
}
|
||||
],
|
||||
"valueCodeableConcept": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "urn:iso:std:iso:11073:10101",
|
||||
"code": "62032",
|
||||
"display": "MDC_CTXT_GLU_MEAL_POSTPRANDIAL"
|
||||
}
|
||||
]
|
||||
},
|
||||
"device": {
|
||||
"reference": "Device/006019fffe31b743"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"method": "POST",
|
||||
"url": "Observation/1-OBR_1:1.0.0.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" output="target/classes" path="src/main/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
|
||||
<attributes>
|
||||
<attribute name="optional" value="true"/>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="maven.pomderived" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
128
hapi-fhir-jpaserver-examples/hapi-fhir-jpaserver-example-postgres/.gitignore
vendored
Normal file
128
hapi-fhir-jpaserver-examples/hapi-fhir-jpaserver-example-postgres/.gitignore
vendored
Normal file
|
@ -0,0 +1,128 @@
|
|||
/target
|
||||
/jpaserver_derby_files
|
||||
*.log
|
||||
ca.uhn.fhir.jpa.entity.ResourceTable/
|
||||
|
||||
# Created by https://www.gitignore.io
|
||||
|
||||
### Java ###
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
|
||||
### Maven ###
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
|
||||
|
||||
### Vim ###
|
||||
[._]*.s[a-w][a-z]
|
||||
[._]s[a-w][a-z]
|
||||
*.un~
|
||||
Session.vim
|
||||
.netrwhist
|
||||
*~
|
||||
|
||||
|
||||
### Intellij ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
|
||||
|
||||
*.iml
|
||||
|
||||
## Directory-based project format:
|
||||
.idea/
|
||||
# if you remove the above rule, at least ignore the following:
|
||||
|
||||
# User-specific stuff:
|
||||
# .idea/workspace.xml
|
||||
# .idea/tasks.xml
|
||||
# .idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
# .idea/dataSources.ids
|
||||
# .idea/dataSources.xml
|
||||
# .idea/sqlDataSources.xml
|
||||
# .idea/dynamic.xml
|
||||
# .idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
# .idea/gradle.xml
|
||||
# .idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
# .idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
|
||||
|
||||
|
||||
### Eclipse ###
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.loadpath
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
|
@ -0,0 +1,5 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
|
||||
org.eclipse.jdt.core.compiler.compliance=1.6
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.6
|
|
@ -0,0 +1,4 @@
|
|||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
|
@ -0,0 +1,235 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<!--
|
||||
Note: HAPI projects use the "hapi-fhir" POM as their base to provide easy management.
|
||||
You do not need to use this in your own projects, so the "parent" tag and it's
|
||||
contents below may be removed
|
||||
if you are using this file as a basis for your own project.
|
||||
-->
|
||||
<parent>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
<relativePath>../../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<groupId>ca.uhn.hapi.fhir.example</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-example-postgres</artifactId>
|
||||
<packaging>war</packaging>
|
||||
|
||||
<name>HAPI FHIR JPA Server - Example (Postgres)</name>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!-- This dependency includes the core HAPI-FHIR classes -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-base</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- This dependency includes the JPA server itself, which is packaged separately from the rest of HAPI FHIR -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpaserver-base</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- This dependency is used for the "FHIR Tester" web app overlay -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-testpage-overlay</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
<type>war</type>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-testpage-overlay</artifactId>
|
||||
<version>2.1-SNAPSHOT</version>
|
||||
<classifier>classes</classifier>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- HAPI-FHIR uses Logback for logging support. The logback library is included automatically by Maven as a part of the hapi-fhir-base dependency, but you also need to include a logging library. Logback
|
||||
is used here, but log4j would also be fine. -->
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Needed for JEE/Servlet support -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- If you are using HAPI narrative generation, you will need to include Thymeleaf as well. Otherwise the following can be omitted. -->
|
||||
<dependency>
|
||||
<groupId>org.thymeleaf</groupId>
|
||||
<artifactId>thymeleaf</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Used for CORS support -->
|
||||
<dependency>
|
||||
<groupId>org.ebaysf.web</groupId>
|
||||
<artifactId>cors-filter</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>servlet-api</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Web is used to deploy the server to a web container. -->
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- You may not need this if you are deploying to an application server which provides database connection pools itself. -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-dbcp2</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- This example uses Derby embedded database. If you are using another database such as Mysql or Oracle, you may omit the following dependencies and replace them with an appropriate database client
|
||||
dependency for your database platform. -->
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derby</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derbynet</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.derby</groupId>
|
||||
<artifactId>derbyclient</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- The following dependencies are only needed for automated unit tests, you do not neccesarily need them to run the example. -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlets</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-webapp</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.phloc</groupId>
|
||||
<artifactId>phloc-schematron</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
<version>9.4.1210.jre7</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
<!-- Tells Maven to name the generated WAR file as hapi-fhir-jpaserver-example.war -->
|
||||
<finalName>hapi-fhir-jpaserver-example</finalName>
|
||||
|
||||
<!-- The following is not required for the application to build, but allows you to test it by issuing "mvn jetty:run" from the command line. -->
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<webApp>
|
||||
<contextPath>/hapi-fhir-jpaserver-example</contextPath>
|
||||
<allowDuplicateFragmentNames>true</allowDuplicateFragmentNames>
|
||||
</webApp>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
|
||||
<plugins>
|
||||
<!-- Tell Maven which Java source version you want to use -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.6</source>
|
||||
<target>1.6</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- The configuration here tells the WAR plugin to include the FHIR Tester overlay. You can omit it if you are not using that feature. -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-war-plugin</artifactId>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifestEntries>
|
||||
<Build-Time>${maven.build.timestamp}</Build-Time>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
<overlays>
|
||||
<overlay>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-testpage-overlay</artifactId>
|
||||
</overlay>
|
||||
</overlays>
|
||||
<webXml>src/main/webapp/WEB-INF/web.xml</webXml>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- This plugin is just a part of the HAPI internal build process, you do not need to incude it in your own projects -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<!-- This is to run the integration tests -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<configuration>
|
||||
<redirectTestOutputToFile>true</redirectTestOutputToFile>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>integration-test</goal>
|
||||
<goal>verify</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<description>a</description>
|
||||
</project>
|
|
@ -0,0 +1,31 @@
|
|||
Running hapi-fhir-jpaserver-example in Tomcat from IntelliJ
|
||||
|
||||
Install Tomcat.
|
||||
|
||||
Make sure you have Tomcat set up in IntelliJ.
|
||||
File->Settings->Build, Execution, Deployment->Application Servers
|
||||
Click +
|
||||
Select "Tomcat Server"
|
||||
Enter the path to your tomcat deployment for both Tomcat Home (IntelliJ will fill in base directory for you)
|
||||
|
||||
Add a Run Configuration for running hapi-fhir-jpaserver-example under Tomcat
|
||||
Run->Edit Configurations
|
||||
Click the green +
|
||||
Select Tomcat Server, Local
|
||||
Change the name to whatever you wish
|
||||
Uncheck the "After launch" checkbox
|
||||
On the "Deployment" tab, click the green +
|
||||
Select "Artifact"
|
||||
Select "hapi-fhir-jpaserver-example:war"
|
||||
In "Application context" type /hapi
|
||||
|
||||
Run the configuration
|
||||
You should now have an "Application Servers" in the list of windows at the bottom.
|
||||
Click it.
|
||||
Select your server, and click the green triangle (or the bug if you want to debug)
|
||||
Wait for the console output to stop
|
||||
|
||||
Point your browser (or fiddler, or what have you) to
|
||||
http://localhost:8080/hapi/base/Patient
|
||||
|
||||
You should get an empty bundle back.
|
|
@ -0,0 +1,129 @@
|
|||
package ca.uhn.fhir.jpa.demo;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.apache.commons.dbcp2.BasicDataSource;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowire;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
|
||||
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.util.SubscriptionsRequireManualActivationInterceptorDstu3;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
|
||||
/**
|
||||
* This class isn't used by default by the example, but
|
||||
* you can use it as a config if you want to support DSTU3
|
||||
* instead of DSTU2 in your server.
|
||||
*
|
||||
* See https://github.com/jamesagnew/hapi-fhir/issues/278
|
||||
*/
|
||||
@Configuration
|
||||
@EnableTransactionManagement()
|
||||
public class FhirServerConfig extends BaseJavaConfigDstu3 {
|
||||
|
||||
/**
|
||||
* Configure FHIR properties around the the JPA server via this bean
|
||||
*/
|
||||
@Bean()
|
||||
public DaoConfig daoConfig() {
|
||||
DaoConfig retVal = new DaoConfig();
|
||||
retVal.setSubscriptionEnabled(true);
|
||||
retVal.setSubscriptionPollDelay(5000);
|
||||
retVal.setSubscriptionPurgeInactiveAfterMillis(DateUtils.MILLIS_PER_HOUR);
|
||||
retVal.setAllowMultipleDelete(true);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following bean configures the database connection. The 'url' property value of "jdbc:derby:directory:jpaserver_derby_files;create=true" indicates that the server should save resources in a
|
||||
* directory called "jpaserver_derby_files".
|
||||
*
|
||||
* A URL to a remote database could also be placed here, along with login credentials and other properties supported by BasicDataSource.
|
||||
*/
|
||||
@Bean(destroyMethod = "close")
|
||||
public DataSource dataSource() {
|
||||
BasicDataSource retVal = new BasicDataSource();
|
||||
retVal.setDriver(new org.postgresql.Driver());
|
||||
retVal.setUrl("jdbc:postgresql://localhost:5432/hapi");
|
||||
retVal.setUsername("hapi");
|
||||
retVal.setPassword("mysecretpassword");
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Bean()
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
LocalContainerEntityManagerFactoryBean retVal = new LocalContainerEntityManagerFactoryBean();
|
||||
retVal.setPersistenceUnitName("HAPI_PU");
|
||||
retVal.setDataSource(dataSource());
|
||||
retVal.setPackagesToScan("ca.uhn.fhir.jpa.entity");
|
||||
retVal.setPersistenceProvider(new HibernatePersistenceProvider());
|
||||
retVal.setJpaProperties(jpaProperties());
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private Properties jpaProperties() {
|
||||
Properties extraProperties = new Properties();
|
||||
extraProperties.put("hibernate.dialect", org.hibernate.dialect.PostgreSQL94Dialect.class.getName());
|
||||
extraProperties.put("hibernate.format_sql", "true");
|
||||
extraProperties.put("hibernate.show_sql", "false");
|
||||
extraProperties.put("hibernate.hbm2ddl.auto", "update");
|
||||
extraProperties.put("hibernate.jdbc.batch_size", "20");
|
||||
extraProperties.put("hibernate.cache.use_query_cache", "false");
|
||||
extraProperties.put("hibernate.cache.use_second_level_cache", "false");
|
||||
extraProperties.put("hibernate.cache.use_structured_entries", "false");
|
||||
extraProperties.put("hibernate.cache.use_minimal_puts", "false");
|
||||
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
|
||||
extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
|
||||
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
|
||||
// extraProperties.put("hibernate.search.default.worker.execution", "async");
|
||||
return extraProperties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do some fancy logging to create a nice access log that has details about each incoming request.
|
||||
*/
|
||||
public IServerInterceptor loggingInterceptor() {
|
||||
LoggingInterceptor retVal = new LoggingInterceptor();
|
||||
retVal.setLoggerName("fhirtest.access");
|
||||
retVal.setMessageFormat(
|
||||
"Path[${servletPath}] Source[${requestHeader.x-forwarded-for}] Operation[${operationType} ${operationName} ${idOrResourceName}] UA[${requestHeader.user-agent}] Params[${requestParameters}] ResponseEncoding[${responseEncodingNoDefault}]");
|
||||
retVal.setLogExceptions(true);
|
||||
retVal.setErrorMessageFormat("ERROR - ${requestVerb} ${requestUrl}");
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* This interceptor adds some pretty syntax highlighting in responses when a browser is detected
|
||||
*/
|
||||
@Bean(autowire = Autowire.BY_TYPE)
|
||||
public IServerInterceptor responseHighlighterInterceptor() {
|
||||
ResponseHighlighterInterceptor retVal = new ResponseHighlighterInterceptor();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Bean(autowire = Autowire.BY_TYPE)
|
||||
public IServerInterceptor subscriptionSecurityInterceptor() {
|
||||
SubscriptionsRequireManualActivationInterceptorDstu3 retVal = new SubscriptionsRequireManualActivationInterceptorDstu3();
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Bean()
|
||||
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
|
||||
JpaTransactionManager retVal = new JpaTransactionManager();
|
||||
retVal.setEntityManagerFactory(entityManagerFactory);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
package ca.uhn.fhir.jpa.demo;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.to.FhirTesterMvcConfig;
|
||||
import ca.uhn.fhir.to.TesterConfig;
|
||||
|
||||
//@formatter:off
|
||||
/**
|
||||
* This spring config file configures the web testing module. It serves two
|
||||
* purposes:
|
||||
* 1. It imports FhirTesterMvcConfig, which is the spring config for the
|
||||
* tester itself
|
||||
* 2. It tells the tester which server(s) to talk to, via the testerConfig()
|
||||
* method below
|
||||
*/
|
||||
@Configuration
|
||||
@Import(FhirTesterMvcConfig.class)
|
||||
public class FhirTesterConfig {
|
||||
|
||||
/**
|
||||
* This bean tells the testing webpage which servers it should configure itself
|
||||
* to communicate with. In this example we configure it to talk to the local
|
||||
* server, as well as one public server. If you are creating a project to
|
||||
* deploy somewhere else, you might choose to only put your own server's
|
||||
* address here.
|
||||
*
|
||||
* Note the use of the ${serverBase} variable below. This will be replaced with
|
||||
* the base URL as reported by the server itself. Often for a simple Tomcat
|
||||
* (or other container) installation, this will end up being something
|
||||
* like "http://localhost:8080/hapi-fhir-jpaserver-example". If you are
|
||||
* deploying your server to a place with a fully qualified domain name,
|
||||
* you might want to use that instead of using the variable.
|
||||
*/
|
||||
@Bean
|
||||
public TesterConfig testerConfig() {
|
||||
TesterConfig retVal = new TesterConfig();
|
||||
retVal
|
||||
.addServer()
|
||||
.withId("home")
|
||||
.withFhirVersion(FhirVersionEnum.DSTU3)
|
||||
.withBaseUrl("${serverBase}/baseDstu3")
|
||||
.withName("Local Tester");
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
//@formatter:on
|
|
@ -0,0 +1,124 @@
|
|||
package ca.uhn.fhir.jpa.demo;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import org.hl7.fhir.dstu3.model.Meta;
|
||||
import org.springframework.web.context.ContextLoaderListener;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3;
|
||||
import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider;
|
||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||
import ca.uhn.fhir.rest.server.ETagSupportEnum;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
|
||||
public class JpaServerDemo extends RestfulServer {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private WebApplicationContext myAppCtx;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void initialize() throws ServletException {
|
||||
super.initialize();
|
||||
|
||||
/*
|
||||
* We want to support FHIR DSTU3 format. This means that the server
|
||||
* will use the DSTU3 bundle format and other DSTU3 encoding changes.
|
||||
*
|
||||
* If you want to use DSTU1 instead, change the following line, and change the 3 occurrences of dstu2 in web.xml to dstu1
|
||||
*/
|
||||
setFhirContext(FhirContext.forDstu3());
|
||||
|
||||
// Get the spring context from the web container (it's declared in web.xml)
|
||||
myAppCtx = ContextLoaderListener.getCurrentWebApplicationContext();
|
||||
|
||||
/*
|
||||
* The BaseJavaConfigDstu3.java class is a spring configuration
|
||||
* file which is automatically generated as a part of hapi-fhir-jpaserver-base and
|
||||
* contains bean definitions for a resource provider for each resource type
|
||||
*/
|
||||
List<IResourceProvider> beans = myAppCtx.getBean("myResourceProvidersDstu3", List.class);
|
||||
setResourceProviders(beans);
|
||||
|
||||
/*
|
||||
* The system provider implements non-resource-type methods, such as
|
||||
* transaction, and global history.
|
||||
*/
|
||||
setPlainProviders(myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class));
|
||||
|
||||
/*
|
||||
* The conformance provider exports the supported resources, search parameters, etc for
|
||||
* this server. The JPA version adds resource counts to the exported statement, so it
|
||||
* is a nice addition.
|
||||
*/
|
||||
IFhirSystemDao<org.hl7.fhir.dstu3.model.Bundle, Meta> systemDao = myAppCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class);
|
||||
JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class));
|
||||
confProvider.setImplementationDescription("Example Server");
|
||||
setServerConformanceProvider(confProvider);
|
||||
|
||||
/*
|
||||
* Enable ETag Support (this is already the default)
|
||||
*/
|
||||
setETagSupport(ETagSupportEnum.ENABLED);
|
||||
|
||||
/*
|
||||
* This server tries to dynamically generate narratives
|
||||
*/
|
||||
FhirContext ctx = getFhirContext();
|
||||
ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator());
|
||||
|
||||
/*
|
||||
* Default to JSON and pretty printing
|
||||
*/
|
||||
setDefaultPrettyPrint(true);
|
||||
setDefaultResponseEncoding(EncodingEnum.JSON);
|
||||
|
||||
/*
|
||||
* -- New in HAPI FHIR 1.5 --
|
||||
* This configures the server to page search results to and from
|
||||
* the database, instead of only paging them to memory. This may mean
|
||||
* a performance hit when performing searches that return lots of results,
|
||||
* but makes the server much more scalable.
|
||||
*/
|
||||
setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class));
|
||||
|
||||
/*
|
||||
* Load interceptors for the server from Spring (these are defined in FhirServerConfig.java)
|
||||
*/
|
||||
Collection<IServerInterceptor> interceptorBeans = myAppCtx.getBeansOfType(IServerInterceptor.class).values();
|
||||
for (IServerInterceptor interceptor : interceptorBeans) {
|
||||
this.registerInterceptor(interceptor);
|
||||
}
|
||||
|
||||
/*
|
||||
* If you are hosting this server at a specific DNS name, the server will try to
|
||||
* figure out the FHIR base URL based on what the web container tells it, but
|
||||
* this doesn't always work. If you are setting links in your search bundles that
|
||||
* just refer to "localhost", you might want to use a server address strategy:
|
||||
*/
|
||||
//setServerAddressStrategy(new HardcodedServerAddressStrategy("http://mydomain.com/fhir/baseDstu2"));
|
||||
|
||||
/*
|
||||
* If you are using DSTU3+, you may want to add a terminology uploader, which allows
|
||||
* uploading of external terminologies such as Snomed CT. Note that this uploader
|
||||
* does not have any security attached (any anonymous user may use it by default)
|
||||
* so it is a potential security vulnerability. Consider using an AuthorizationInterceptor
|
||||
* with this feature.
|
||||
*/
|
||||
registerProvider(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<configuration scan="true" scanPeriod="30 seconds">
|
||||
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
|
||||
<level>INFO</level>
|
||||
</filter>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} [%file:%line] %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head th:include="tmpl-head :: head">
|
||||
<title>About This Server</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<form action="" method="get" id="outerForm">
|
||||
<div th:replace="tmpl-navbar-top :: top" ></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div th:replace="tmpl-navbar-left :: left" ></div>
|
||||
|
||||
<div class="col-sm-9 col-sm-offset-3 col-md-9 col-md-offset-3 main">
|
||||
|
||||
<div th:replace="tmpl-banner :: banner"></div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">About This Server</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="pull-right">
|
||||
<object data="img/fhirtest-architecture.svg" width="383" height="369" type="image/svg+xml"></object>
|
||||
</div>
|
||||
<p>
|
||||
This server provides a nearly complete implementation of the FHIR Specification
|
||||
using a 100% open source software stack. It is hosted by University Health Network.
|
||||
</p>
|
||||
<p>
|
||||
The architecture in use here is shown in the image on the right. This server is built
|
||||
from a number of modules of the
|
||||
<a href="https://github.com/jamesagnew/hapi-fhir/">HAPI FHIR</a>
|
||||
project, which is a 100% open-source (Apache 2.0 Licensed) Java based
|
||||
implementation of the FHIR specification.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">Data On This Server</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<p>
|
||||
This server is regularly loaded with a standard set of test data sourced
|
||||
from UHN's own testing environment. Do not use this server to store any data
|
||||
that you will need later, as we will be regularly resetting it.
|
||||
</p>
|
||||
<p>
|
||||
This is not a production server and it provides no privacy. Do not store any
|
||||
confidential data here.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div th:replace="tmpl-footer :: footer" ></div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<div th:fragment="footer">
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-1395874-6', 'auto');
|
||||
ga('require', 'displayfeatures');
|
||||
ga('require', 'linkid', 'linkid.js');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</div>
|
||||
</html>
|
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<div th:fragment="banner" class="well">
|
||||
<th:block th:if="${serverId} == 'home'">
|
||||
<p>
|
||||
This is the home for the FHIR test server operated by
|
||||
<a href="http://uhn.ca">University Health Network</a>. This server
|
||||
(and the testing application you are currently using to access it)
|
||||
is entirely built using
|
||||
<a href="https://github.com/jamesagnew/hapi-fhir">HAPI-FHIR</a>,
|
||||
a 100% open-source Java implementation of the
|
||||
<a href="http://hl7.org/implement/standards/fhir/">FHIR specification</a>.
|
||||
</p>
|
||||
<p>
|
||||
Here are some things you might wish to try:
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
View a
|
||||
<a href="http://fhirtest.uhn.ca/search?serverId=home&encoding=json&pretty=true&resource=Patient&param.0.type=string&param.0.name=_id&param.0.0=&resource-search-limit=">list of patients</a>
|
||||
on this server.
|
||||
</li>
|
||||
<li>
|
||||
Construct a
|
||||
<a href="http://fhirtest.uhn.ca/resource?serverId=home&encoding=json&pretty=true&resource=Patient">search query</a>
|
||||
on this server.
|
||||
</li>
|
||||
<li>
|
||||
Access a
|
||||
<a href="http://fhirtest.uhn.ca/home?serverId=furore">different server</a>
|
||||
(use the <b>Server</b> menu at the top of the page to see a list of public FHIR servers)
|
||||
</li>
|
||||
</ul>
|
||||
</th:block>
|
||||
<th:block th:if="${serverId} != 'home'">
|
||||
<p>
|
||||
You are accessing the public FHIR server
|
||||
<b th:text="${baseName}"/>. This server is hosted elsewhere on the internet
|
||||
but is being accessed using the HAPI client implementation.
|
||||
</p>
|
||||
</th:block>
|
||||
<p>
|
||||
<b style="color: red;">
|
||||
<span class="glyphicon glyphicon-warning-sign"/>
|
||||
This is not a production server!
|
||||
</b>
|
||||
Do not store any information here that contains personal health information
|
||||
or any other confidential information. This server will be regularly purged
|
||||
and reloaded with fixed test data.
|
||||
</p>
|
||||
</div>
|
||||
</html>
|
|
@ -0,0 +1,108 @@
|
|||
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="3.0"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee ./xsd/web-app_3_0.xsd">
|
||||
|
||||
<listener>
|
||||
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
|
||||
</listener>
|
||||
<context-param>
|
||||
<param-name>contextClass</param-name>
|
||||
<param-value>
|
||||
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
|
||||
</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>
|
||||
ca.uhn.fhir.jpa.demo.FhirServerConfig
|
||||
</param-value>
|
||||
</context-param>
|
||||
|
||||
<!-- Servlets -->
|
||||
|
||||
<servlet>
|
||||
<servlet-name>spring</servlet-name>
|
||||
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
|
||||
<init-param>
|
||||
<param-name>contextClass</param-name>
|
||||
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>contextConfigLocation</param-name>
|
||||
<param-value>ca.uhn.fhir.jpa.demo.FhirTesterConfig</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>2</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>fhirServlet</servlet-name>
|
||||
<servlet-class>ca.uhn.fhir.jpa.demo.JpaServerDemo</servlet-class>
|
||||
<init-param>
|
||||
<param-name>ImplementationDescription</param-name>
|
||||
<param-value>FHIR JPA Server</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>FhirVersion</param-name>
|
||||
<param-value>DSTU2</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>fhirServlet</servlet-name>
|
||||
<url-pattern>/baseDstu3/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<servlet-mapping>
|
||||
<servlet-name>spring</servlet-name>
|
||||
<url-pattern>/</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
|
||||
|
||||
<!-- This filters provide support for Cross Origin Resource Sharing (CORS) -->
|
||||
<filter>
|
||||
<filter-name>CORS Filter</filter-name>
|
||||
<filter-class>org.ebaysf.web.cors.CORSFilter</filter-class>
|
||||
<init-param>
|
||||
<description>A comma separated list of allowed origins. Note: An '*' cannot be used for an allowed origin when using credentials.</description>
|
||||
<param-name>cors.allowed.origins</param-name>
|
||||
<param-value>*</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<description>A comma separated list of HTTP verbs, using which a CORS request can be made.</description>
|
||||
<param-name>cors.allowed.methods</param-name>
|
||||
<param-value>GET,POST,PUT,DELETE,OPTIONS</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<description>A comma separated list of allowed headers when making a non simple CORS request.</description>
|
||||
<param-name>cors.allowed.headers</param-name>
|
||||
<param-value>X-FHIR-Starter,Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<description>A comma separated list non-standard response headers that will be exposed to XHR2 object.</description>
|
||||
<param-name>cors.exposed.headers</param-name>
|
||||
<param-value>Location,Content-Location</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<description>A flag that suggests if CORS is supported with cookies</description>
|
||||
<param-name>cors.support.credentials</param-name>
|
||||
<param-value>true</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<description>A flag to control logging</description>
|
||||
<param-name>cors.logging.enabled</param-name>
|
||||
<param-value>true</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<description>Indicates how long (in seconds) the results of a preflight request can be cached in a preflight result cache.</description>
|
||||
<param-name>cors.preflight.maxage</param-name>
|
||||
<param-value>300</param-value>
|
||||
</init-param>
|
||||
</filter>
|
||||
<filter-mapping>
|
||||
<filter-name>CORS Filter</filter-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</filter-mapping>
|
||||
|
||||
|
||||
</web-app>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,389 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:javaee="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified"
|
||||
version="2.2">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
||||
|
||||
Copyright 2003-2009 Sun Microsystems, Inc. All rights reserved.
|
||||
|
||||
The contents of this file are subject to the terms of either the
|
||||
GNU General Public License Version 2 only ("GPL") or the Common
|
||||
Development and Distribution License("CDDL") (collectively, the
|
||||
"License"). You may not use this file except in compliance with
|
||||
the License. You can obtain a copy of the License at
|
||||
https://glassfish.dev.java.net/public/CDDL+GPL.html or
|
||||
glassfish/bootstrap/legal/LICENSE.txt. See the License for the
|
||||
specific language governing permissions and limitations under the
|
||||
License.
|
||||
|
||||
When distributing the software, include this License Header
|
||||
Notice in each file and include the License file at
|
||||
glassfish/bootstrap/legal/LICENSE.txt. Sun designates this
|
||||
particular file as subject to the "Classpath" exception as
|
||||
provided by Sun in the GPL Version 2 section of the License file
|
||||
that accompanied this code. If applicable, add the following
|
||||
below the License Header, with the fields enclosed by brackets []
|
||||
replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]"
|
||||
|
||||
Contributor(s):
|
||||
|
||||
If you wish your version of this file to be governed by only the
|
||||
CDDL or only the GPL Version 2, indicate your decision by adding
|
||||
"[Contributor] elects to include this software in this
|
||||
distribution under the [CDDL or GPL Version 2] license." If you
|
||||
don't indicate a single choice of license, a recipient has the
|
||||
option to distribute your version of this file under either the
|
||||
CDDL, the GPL Version 2 or to extend the choice of license to its
|
||||
licensees as provided above. However, if you add GPL Version 2
|
||||
code and therefore, elected the GPL Version 2 license, then the
|
||||
option applies only if the new code is made subject to such
|
||||
option by the copyright holder.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
This is the XML Schema for the JSP 2.2 deployment descriptor
|
||||
types. The JSP 2.2 schema contains all the special
|
||||
structures and datatypes that are necessary to use JSP files
|
||||
from a web application.
|
||||
|
||||
The contents of this schema is used by the web-common_3_0.xsd
|
||||
file to define JSP specific content.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The following conventions apply to all Java EE
|
||||
deployment descriptor elements unless indicated otherwise.
|
||||
|
||||
- In elements that specify a pathname to a file within the
|
||||
same JAR file, relative filenames (i.e., those not
|
||||
starting with "/") are considered relative to the root of
|
||||
the JAR file's namespace. Absolute filenames (i.e., those
|
||||
starting with "/") also specify names in the root of the
|
||||
JAR file's namespace. In general, relative names are
|
||||
preferred. The exception is .war files where absolute
|
||||
names are preferred for consistency with the Servlet API.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:include schemaLocation="javaee_6.xsd"/>
|
||||
|
||||
|
||||
<!-- **************************************************** -->
|
||||
|
||||
<xsd:complexType name="jsp-configType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The jsp-configType is used to provide global configuration
|
||||
information for the JSP files in a web application. It has
|
||||
two subelements, taglib and jsp-property-group.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="taglib"
|
||||
type="javaee:taglibType"
|
||||
minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
<xsd:element name="jsp-property-group"
|
||||
type="javaee:jsp-property-groupType"
|
||||
minOccurs="0"
|
||||
maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id"
|
||||
type="xsd:ID"/>
|
||||
</xsd:complexType>
|
||||
|
||||
|
||||
<!-- **************************************************** -->
|
||||
|
||||
<xsd:complexType name="jsp-fileType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The jsp-file element contains the full path to a JSP file
|
||||
within the web application beginning with a `/'.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleContent>
|
||||
<xsd:restriction base="javaee:pathType"/>
|
||||
</xsd:simpleContent>
|
||||
</xsd:complexType>
|
||||
|
||||
|
||||
<!-- **************************************************** -->
|
||||
|
||||
<xsd:complexType name="jsp-property-groupType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The jsp-property-groupType is used to group a number of
|
||||
files so they can be given global property information.
|
||||
All files so described are deemed to be JSP files. The
|
||||
following additional properties can be described:
|
||||
|
||||
- Control whether EL is ignored.
|
||||
- Control whether scripting elements are invalid.
|
||||
- Indicate pageEncoding information.
|
||||
- Indicate that a resource is a JSP document (XML).
|
||||
- Prelude and Coda automatic includes.
|
||||
- Control whether the character sequence #{ is allowed
|
||||
when used as a String literal.
|
||||
- Control whether template text containing only
|
||||
whitespaces must be removed from the response output.
|
||||
- Indicate the default contentType information.
|
||||
- Indicate the default buffering model for JspWriter
|
||||
- Control whether error should be raised for the use of
|
||||
undeclared namespaces in a JSP page.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:group ref="javaee:descriptionGroup"/>
|
||||
<xsd:element name="url-pattern"
|
||||
type="javaee:url-patternType"
|
||||
maxOccurs="unbounded"/>
|
||||
<xsd:element name="el-ignored"
|
||||
type="javaee:true-falseType"
|
||||
minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
Can be used to easily set the isELIgnored
|
||||
property of a group of JSP pages. By default, the
|
||||
EL evaluation is enabled for Web Applications using
|
||||
a Servlet 2.4 or greater web.xml, and disabled
|
||||
otherwise.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="page-encoding"
|
||||
type="javaee:string"
|
||||
minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The valid values of page-encoding are those of the
|
||||
pageEncoding page directive. It is a
|
||||
translation-time error to name different encodings
|
||||
in the pageEncoding attribute of the page directive
|
||||
of a JSP page and in a JSP configuration element
|
||||
matching the page. It is also a translation-time
|
||||
error to name different encodings in the prolog
|
||||
or text declaration of a document in XML syntax and
|
||||
in a JSP configuration element matching the document.
|
||||
It is legal to name the same encoding through
|
||||
mulitple mechanisms.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="scripting-invalid"
|
||||
type="javaee:true-falseType"
|
||||
minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
Can be used to easily disable scripting in a
|
||||
group of JSP pages. By default, scripting is
|
||||
enabled.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="is-xml"
|
||||
type="javaee:true-falseType"
|
||||
minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
If true, denotes that the group of resources
|
||||
that match the URL pattern are JSP documents,
|
||||
and thus must be interpreted as XML documents.
|
||||
If false, the resources are assumed to not
|
||||
be JSP documents, unless there is another
|
||||
property group that indicates otherwise.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="include-prelude"
|
||||
type="javaee:pathType"
|
||||
minOccurs="0"
|
||||
maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The include-prelude element is a context-relative
|
||||
path that must correspond to an element in the
|
||||
Web Application. When the element is present,
|
||||
the given path will be automatically included (as
|
||||
in an include directive) at the beginning of each
|
||||
JSP page in this jsp-property-group.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="include-coda"
|
||||
type="javaee:pathType"
|
||||
minOccurs="0"
|
||||
maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The include-coda element is a context-relative
|
||||
path that must correspond to an element in the
|
||||
Web Application. When the element is present,
|
||||
the given path will be automatically included (as
|
||||
in an include directive) at the end of each
|
||||
JSP page in this jsp-property-group.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="deferred-syntax-allowed-as-literal"
|
||||
type="javaee:true-falseType"
|
||||
minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The character sequence #{ is reserved for EL expressions.
|
||||
Consequently, a translation error occurs if the #{
|
||||
character sequence is used as a String literal, unless
|
||||
this element is enabled (true). Disabled (false) by
|
||||
default.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="trim-directive-whitespaces"
|
||||
type="javaee:true-falseType"
|
||||
minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
Indicates that template text containing only whitespaces
|
||||
must be removed from the response output. It has no
|
||||
effect on JSP documents (XML syntax). Disabled (false)
|
||||
by default.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="default-content-type"
|
||||
type="javaee:string"
|
||||
minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The valid values of default-content-type are those of the
|
||||
contentType page directive. It specifies the default
|
||||
response contentType if the page directive does not include
|
||||
a contentType attribute.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="buffer"
|
||||
type="javaee:string"
|
||||
minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The valid values of buffer are those of the
|
||||
buffer page directive. It specifies if buffering should be
|
||||
used for the output to response, and if so, the size of the
|
||||
buffer to use.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="error-on-undeclared-namespace"
|
||||
type="javaee:true-falseType"
|
||||
minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The default behavior when a tag with unknown namespace is used
|
||||
in a JSP page (regular syntax) is to silently ignore it. If
|
||||
set to true, then an error must be raised during the translation
|
||||
time when an undeclared tag is used in a JSP page. Disabled
|
||||
(false) by default.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id"
|
||||
type="xsd:ID"/>
|
||||
</xsd:complexType>
|
||||
|
||||
|
||||
<!-- **************************************************** -->
|
||||
|
||||
<xsd:complexType name="taglibType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The taglibType defines the syntax for declaring in
|
||||
the deployment descriptor that a tag library is
|
||||
available to the application. This can be done
|
||||
to override implicit map entries from TLD files and
|
||||
from the container.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="taglib-uri"
|
||||
type="javaee:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
A taglib-uri element describes a URI identifying a
|
||||
tag library used in the web application. The body
|
||||
of the taglib-uri element may be either an
|
||||
absolute URI specification, or a relative URI.
|
||||
There should be no entries in web.xml with the
|
||||
same taglib-uri value.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element name="taglib-location"
|
||||
type="javaee:pathType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
the taglib-location element contains the location
|
||||
(as a resource relative to the root of the web
|
||||
application) where to find the Tag Library
|
||||
Description file for the tag library.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="id"
|
||||
type="xsd:ID"/>
|
||||
</xsd:complexType>
|
||||
|
||||
</xsd:schema>
|
|
@ -0,0 +1,272 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"
|
||||
targetNamespace="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:javaee="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified"
|
||||
version="3.0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
||||
|
||||
Copyright 2003-2009 Sun Microsystems, Inc. All rights reserved.
|
||||
|
||||
The contents of this file are subject to the terms of either the
|
||||
GNU General Public License Version 2 only ("GPL") or the Common
|
||||
Development and Distribution License("CDDL") (collectively, the
|
||||
"License"). You may not use this file except in compliance with
|
||||
the License. You can obtain a copy of the License at
|
||||
https://glassfish.dev.java.net/public/CDDL+GPL.html or
|
||||
glassfish/bootstrap/legal/LICENSE.txt. See the License for the
|
||||
specific language governing permissions and limitations under the
|
||||
License.
|
||||
|
||||
When distributing the software, include this License Header
|
||||
Notice in each file and include the License file at
|
||||
glassfish/bootstrap/legal/LICENSE.txt. Sun designates this
|
||||
particular file as subject to the "Classpath" exception as
|
||||
provided by Sun in the GPL Version 2 section of the License file
|
||||
that accompanied this code. If applicable, add the following
|
||||
below the License Header, with the fields enclosed by brackets []
|
||||
replaced by your own identifying information:
|
||||
"Portions Copyrighted [year] [name of copyright owner]"
|
||||
|
||||
Contributor(s):
|
||||
|
||||
If you wish your version of this file to be governed by only the
|
||||
CDDL or only the GPL Version 2, indicate your decision by adding
|
||||
"[Contributor] elects to include this software in this
|
||||
distribution under the [CDDL or GPL Version 2] license." If you
|
||||
don't indicate a single choice of license, a recipient has the
|
||||
option to distribute your version of this file under either the
|
||||
CDDL, the GPL Version 2 or to extend the choice of license to its
|
||||
licensees as provided above. However, if you add GPL Version 2
|
||||
code and therefore, elected the GPL Version 2 license, then the
|
||||
option applies only if the new code is made subject to such
|
||||
option by the copyright holder.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[[
|
||||
This is the XML Schema for the Servlet 3.0 deployment descriptor.
|
||||
The deployment descriptor must be named "WEB-INF/web.xml" in the
|
||||
web application's war file. All Servlet deployment descriptors
|
||||
must indicate the web application schema by using the Java EE
|
||||
namespace:
|
||||
|
||||
http://java.sun.com/xml/ns/javaee
|
||||
|
||||
and by indicating the version of the schema by
|
||||
using the version element as shown below:
|
||||
|
||||
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="..."
|
||||
version="3.0">
|
||||
...
|
||||
</web-app>
|
||||
|
||||
The instance documents may indicate the published version of
|
||||
the schema using the xsi:schemaLocation attribute for Java EE
|
||||
namespace with the following location:
|
||||
|
||||
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd
|
||||
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The following conventions apply to all Java EE
|
||||
deployment descriptor elements unless indicated otherwise.
|
||||
|
||||
- In elements that specify a pathname to a file within the
|
||||
same JAR file, relative filenames (i.e., those not
|
||||
starting with "/") are considered relative to the root of
|
||||
the JAR file's namespace. Absolute filenames (i.e., those
|
||||
starting with "/") also specify names in the root of the
|
||||
JAR file's namespace. In general, relative names are
|
||||
preferred. The exception is .war files where absolute
|
||||
names are preferred for consistency with the Servlet API.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:include schemaLocation="web-common_3_0.xsd"/>
|
||||
|
||||
|
||||
<!-- **************************************************** -->
|
||||
|
||||
<xsd:element name="web-app"
|
||||
type="javaee:web-appType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The web-app element is the root of the deployment
|
||||
descriptor for a web application. Note that the sub-elements
|
||||
of this element can be in the arbitrary order. Because of
|
||||
that, the multiplicity of the elements of distributable,
|
||||
session-config, welcome-file-list, jsp-config, login-config,
|
||||
and locale-encoding-mapping-list was changed from "?" to "*"
|
||||
in this schema. However, the deployment descriptor instance
|
||||
file must not contain multiple elements of session-config,
|
||||
jsp-config, and login-config. When there are multiple elements of
|
||||
welcome-file-list or locale-encoding-mapping-list, the container
|
||||
must concatenate the element contents. The multiple occurence
|
||||
of the element distributable is redundant and the container
|
||||
treats that case exactly in the same way when there is only
|
||||
one distributable.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:unique name="web-common-servlet-name-uniqueness">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The servlet element contains the name of a servlet.
|
||||
The name must be unique within the web application.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:selector xpath="javaee:servlet"/>
|
||||
<xsd:field xpath="javaee:servlet-name"/>
|
||||
</xsd:unique>
|
||||
<xsd:unique name="web-common-filter-name-uniqueness">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The filter element contains the name of a filter.
|
||||
The name must be unique within the web application.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:selector xpath="javaee:filter"/>
|
||||
<xsd:field xpath="javaee:filter-name"/>
|
||||
</xsd:unique>
|
||||
<xsd:unique name="web-common-ejb-local-ref-name-uniqueness">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The ejb-local-ref-name element contains the name of an EJB
|
||||
reference. The EJB reference is an entry in the web
|
||||
application's environment and is relative to the
|
||||
java:comp/env context. The name must be unique within
|
||||
the web application.
|
||||
|
||||
It is recommended that name is prefixed with "ejb/".
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:selector xpath="javaee:ejb-local-ref"/>
|
||||
<xsd:field xpath="javaee:ejb-ref-name"/>
|
||||
</xsd:unique>
|
||||
<xsd:unique name="web-common-ejb-ref-name-uniqueness">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The ejb-ref-name element contains the name of an EJB
|
||||
reference. The EJB reference is an entry in the web
|
||||
application's environment and is relative to the
|
||||
java:comp/env context. The name must be unique within
|
||||
the web application.
|
||||
|
||||
It is recommended that name is prefixed with "ejb/".
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:selector xpath="javaee:ejb-ref"/>
|
||||
<xsd:field xpath="javaee:ejb-ref-name"/>
|
||||
</xsd:unique>
|
||||
<xsd:unique name="web-common-resource-env-ref-uniqueness">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The resource-env-ref-name element specifies the name of
|
||||
a resource environment reference; its value is the
|
||||
environment entry name used in the web application code.
|
||||
The name is a JNDI name relative to the java:comp/env
|
||||
context and must be unique within a web application.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:selector xpath="javaee:resource-env-ref"/>
|
||||
<xsd:field xpath="javaee:resource-env-ref-name"/>
|
||||
</xsd:unique>
|
||||
<xsd:unique name="web-common-message-destination-ref-uniqueness">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The message-destination-ref-name element specifies the name of
|
||||
a message destination reference; its value is the
|
||||
environment entry name used in the web application code.
|
||||
The name is a JNDI name relative to the java:comp/env
|
||||
context and must be unique within a web application.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:selector xpath="javaee:message-destination-ref"/>
|
||||
<xsd:field xpath="javaee:message-destination-ref-name"/>
|
||||
</xsd:unique>
|
||||
<xsd:unique name="web-common-res-ref-name-uniqueness">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The res-ref-name element specifies the name of a
|
||||
resource manager connection factory reference. The name
|
||||
is a JNDI name relative to the java:comp/env context.
|
||||
The name must be unique within a web application.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:selector xpath="javaee:resource-ref"/>
|
||||
<xsd:field xpath="javaee:res-ref-name"/>
|
||||
</xsd:unique>
|
||||
<xsd:unique name="web-common-env-entry-name-uniqueness">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The env-entry-name element contains the name of a web
|
||||
application's environment entry. The name is a JNDI
|
||||
name relative to the java:comp/env context. The name
|
||||
must be unique within a web application.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:selector xpath="javaee:env-entry"/>
|
||||
<xsd:field xpath="javaee:env-entry-name"/>
|
||||
</xsd:unique>
|
||||
<xsd:key name="web-common-role-name-key">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
A role-name-key is specified to allow the references
|
||||
from the security-role-refs.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:selector xpath="javaee:security-role"/>
|
||||
<xsd:field xpath="javaee:role-name"/>
|
||||
</xsd:key>
|
||||
<xsd:keyref name="web-common-role-name-references"
|
||||
refer="javaee:web-common-role-name-key">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
|
||||
The keyref indicates the references from
|
||||
security-role-ref to a specified role-name.
|
||||
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:selector xpath="javaee:servlet/javaee:security-role-ref"/>
|
||||
<xsd:field xpath="javaee:role-link"/>
|
||||
</xsd:keyref>
|
||||
</xsd:element>
|
||||
|
||||
</xsd:schema>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,287 @@
|
|||
<?xml version='1.0'?>
|
||||
<?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
|
||||
<xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
|
||||
xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns ="http://www.w3.org/1999/xhtml"
|
||||
xml:lang="en">
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
<h1>About the XML namespace</h1>
|
||||
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
This schema document describes the XML namespace, in a form
|
||||
suitable for import by other schema documents.
|
||||
</p>
|
||||
<p>
|
||||
See <a href="http://www.w3.org/XML/1998/namespace.html">
|
||||
http://www.w3.org/XML/1998/namespace.html</a> and
|
||||
<a href="http://www.w3.org/TR/REC-xml">
|
||||
http://www.w3.org/TR/REC-xml</a> for information
|
||||
about this namespace.
|
||||
</p>
|
||||
<p>
|
||||
Note that local names in this namespace are intended to be
|
||||
defined only by the World Wide Web Consortium or its subgroups.
|
||||
The names currently defined in this namespace are listed below.
|
||||
They should not be used with conflicting semantics by any Working
|
||||
Group, specification, or document instance.
|
||||
</p>
|
||||
<p>
|
||||
See further below in this document for more information about <a
|
||||
href="#usage">how to refer to this schema document from your own
|
||||
XSD schema documents</a> and about <a href="#nsversioning">the
|
||||
namespace-versioning policy governing this schema document</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:attribute name="lang">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
|
||||
<h3>lang (as an attribute name)</h3>
|
||||
<p>
|
||||
denotes an attribute whose value
|
||||
is a language code for the natural language of the content of
|
||||
any element; its value is inherited. This name is reserved
|
||||
by virtue of its definition in the XML specification.</p>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<h4>Notes</h4>
|
||||
<p>
|
||||
Attempting to install the relevant ISO 2- and 3-letter
|
||||
codes as the enumerated possible values is probably never
|
||||
going to be a realistic possibility.
|
||||
</p>
|
||||
<p>
|
||||
See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
|
||||
http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
|
||||
and the IANA language subtag registry at
|
||||
<a href="http://www.iana.org/assignments/language-subtag-registry">
|
||||
http://www.iana.org/assignments/language-subtag-registry</a>
|
||||
for further information.
|
||||
</p>
|
||||
<p>
|
||||
The union allows for the 'un-declaration' of xml:lang with
|
||||
the empty string.
|
||||
</p>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:union memberTypes="xs:language">
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value=""/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:union>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="space">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
|
||||
<h3>space (as an attribute name)</h3>
|
||||
<p>
|
||||
denotes an attribute whose
|
||||
value is a keyword indicating what whitespace processing
|
||||
discipline is intended for the content of the element; its
|
||||
value is inherited. This name is reserved by virtue of its
|
||||
definition in the XML specification.</p>
|
||||
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
<xs:simpleType>
|
||||
<xs:restriction base="xs:NCName">
|
||||
<xs:enumeration value="default"/>
|
||||
<xs:enumeration value="preserve"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
|
||||
<h3>base (as an attribute name)</h3>
|
||||
<p>
|
||||
denotes an attribute whose value
|
||||
provides a URI to be used as the base for interpreting any
|
||||
relative URIs in the scope of the element on which it
|
||||
appears; its value is inherited. This name is reserved
|
||||
by virtue of its definition in the XML Base specification.</p>
|
||||
|
||||
<p>
|
||||
See <a
|
||||
href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
|
||||
for information about this attribute.
|
||||
</p>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attribute name="id" type="xs:ID">
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
|
||||
<h3>id (as an attribute name)</h3>
|
||||
<p>
|
||||
denotes an attribute whose value
|
||||
should be interpreted as if declared to be of type ID.
|
||||
This name is reserved by virtue of its definition in the
|
||||
xml:id specification.</p>
|
||||
|
||||
<p>
|
||||
See <a
|
||||
href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
|
||||
for information about this attribute.
|
||||
</p>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:attribute>
|
||||
|
||||
<xs:attributeGroup name="specialAttrs">
|
||||
<xs:attribute ref="xml:base"/>
|
||||
<xs:attribute ref="xml:lang"/>
|
||||
<xs:attribute ref="xml:space"/>
|
||||
<xs:attribute ref="xml:id"/>
|
||||
</xs:attributeGroup>
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div>
|
||||
|
||||
<h3>Father (in any context at all)</h3>
|
||||
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
denotes Jon Bosak, the chair of
|
||||
the original XML Working Group. This name is reserved by
|
||||
the following decision of the W3C XML Plenary and
|
||||
XML Coordination groups:
|
||||
</p>
|
||||
<blockquote>
|
||||
<p>
|
||||
In appreciation for his vision, leadership and
|
||||
dedication the W3C XML Plenary on this 10th day of
|
||||
February, 2000, reserves for Jon Bosak in perpetuity
|
||||
the XML name "xml:Father".
|
||||
</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div xml:id="usage" id="usage">
|
||||
<h2><a name="usage">About this schema document</a></h2>
|
||||
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
This schema defines attributes and an attribute group suitable
|
||||
for use by schemas wishing to allow <code>xml:base</code>,
|
||||
<code>xml:lang</code>, <code>xml:space</code> or
|
||||
<code>xml:id</code> attributes on elements they define.
|
||||
</p>
|
||||
<p>
|
||||
To enable this, such a schema must import this schema for
|
||||
the XML namespace, e.g. as follows:
|
||||
</p>
|
||||
<pre>
|
||||
<schema . . .>
|
||||
. . .
|
||||
<import namespace="http://www.w3.org/XML/1998/namespace"
|
||||
schemaLocation="http://www.w3.org/2001/xml.xsd"/>
|
||||
</pre>
|
||||
<p>
|
||||
or
|
||||
</p>
|
||||
<pre>
|
||||
<import namespace="http://www.w3.org/XML/1998/namespace"
|
||||
schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
|
||||
</pre>
|
||||
<p>
|
||||
Subsequently, qualified reference to any of the attributes or the
|
||||
group defined below will have the desired effect, e.g.
|
||||
</p>
|
||||
<pre>
|
||||
<type . . .>
|
||||
. . .
|
||||
<attributeGroup ref="xml:specialAttrs"/>
|
||||
</pre>
|
||||
<p>
|
||||
will define a type which will schema-validate an instance element
|
||||
with any of those attributes.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
<xs:annotation>
|
||||
<xs:documentation>
|
||||
<div id="nsversioning" xml:id="nsversioning">
|
||||
<h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
|
||||
<div class="bodytext">
|
||||
<p>
|
||||
In keeping with the XML Schema WG's standard versioning
|
||||
policy, this schema document will persist at
|
||||
<a href="http://www.w3.org/2009/01/xml.xsd">
|
||||
http://www.w3.org/2009/01/xml.xsd</a>.
|
||||
</p>
|
||||
<p>
|
||||
At the date of issue it can also be found at
|
||||
<a href="http://www.w3.org/2001/xml.xsd">
|
||||
http://www.w3.org/2001/xml.xsd</a>.
|
||||
</p>
|
||||
<p>
|
||||
The schema document at that URI may however change in the future,
|
||||
in order to remain compatible with the latest version of XML
|
||||
Schema itself, or with the XML namespace itself. In other words,
|
||||
if the XML Schema or XML namespaces change, the version of this
|
||||
document at <a href="http://www.w3.org/2001/xml.xsd">
|
||||
http://www.w3.org/2001/xml.xsd
|
||||
</a>
|
||||
will change accordingly; the version at
|
||||
<a href="http://www.w3.org/2009/01/xml.xsd">
|
||||
http://www.w3.org/2009/01/xml.xsd
|
||||
</a>
|
||||
will not change.
|
||||
</p>
|
||||
<p>
|
||||
Previous dated (and unchanging) versions of this schema
|
||||
document are at:
|
||||
</p>
|
||||
<ul>
|
||||
<li><a href="http://www.w3.org/2009/01/xml.xsd">
|
||||
http://www.w3.org/2009/01/xml.xsd</a></li>
|
||||
<li><a href="http://www.w3.org/2007/08/xml.xsd">
|
||||
http://www.w3.org/2007/08/xml.xsd</a></li>
|
||||
<li><a href="http://www.w3.org/2004/10/xml.xsd">
|
||||
http://www.w3.org/2004/10/xml.xsd</a></li>
|
||||
<li><a href="http://www.w3.org/2001/03/xml.xsd">
|
||||
http://www.w3.org/2001/03/xml.xsd</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</xs:documentation>
|
||||
</xs:annotation>
|
||||
|
||||
</xs:schema>
|
||||
|
|
@ -3,18 +3,17 @@ package ca.uhn.fhir.rest.client;
|
|||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.mockito.Matchers.argThat;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.*;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
@ -29,18 +28,13 @@ import ca.uhn.fhir.rest.server.IResourceProvider;
|
|||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ch.qos.logback.classic.BasicConfigurator;
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.joran.JoranConfigurator;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.classic.spi.LoggingEvent;
|
||||
import ch.qos.logback.classic.util.LogbackMDCAdapter;
|
||||
import ch.qos.logback.core.Appender;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public class LoggingInterceptorTest {
|
||||
|
||||
private static FhirContext ourCtx = FhirContext.forDstu1();
|
||||
|
|
|
@ -43,6 +43,7 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
|||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
|
@ -87,6 +88,10 @@ public class InterceptorTest {
|
|||
|
||||
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class));
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class));
|
||||
|
||||
order.verify(myInterceptor2, times(1)).processingCompletedNormally(any(ServletRequestDetails.class));
|
||||
order.verify(myInterceptor1, times(1)).processingCompletedNormally(any(ServletRequestDetails.class));
|
||||
|
||||
verifyNoMoreInteractions(myInterceptor1);
|
||||
verifyNoMoreInteractions(myInterceptor2);
|
||||
}
|
||||
|
|
|
@ -1,14 +1,45 @@
|
|||
package ca.uhn.fhir.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import javax.xml.stream.FactoryConfigurationError;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class XmlUtilTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateInputFactoryWithException() {
|
||||
XmlUtil.setThrowExceptionForUnitTest(new Error("FOO ERROR"));
|
||||
try {
|
||||
XmlUtil.newInputFactory();
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
assertEquals("Unable to initialize StAX - XML processing is disabled", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateOutputFactoryWithException() {
|
||||
XmlUtil.setThrowExceptionForUnitTest(new Error("FOO ERROR"));
|
||||
try {
|
||||
XmlUtil.newOutputFactory();
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
assertEquals("Unable to initialize StAX - XML processing is disabled", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
XmlUtil.setThrowExceptionForUnitTest(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateReader() throws Exception {
|
||||
XmlUtil.createXmlReader(new StringReader("<a/>"));
|
||||
|
@ -24,7 +55,6 @@ public class XmlUtilTest {
|
|||
XmlUtil.createXmlStreamWriter(new StringWriter());
|
||||
}
|
||||
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
|
|
|
@ -425,6 +425,38 @@ public class XmlParserDstu2Test {
|
|||
ourCtx = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* See #448
|
||||
*/
|
||||
@Test
|
||||
public void testParseWithMultipleProfiles() {
|
||||
ourCtx = FhirContext.forDstu2();
|
||||
ourCtx.setDefaultTypeForProfile(CustomObservationDstu2.PROFILE, CustomObservationDstu2.class);
|
||||
ourCtx.setDefaultTypeForProfile(CustomDiagnosticReportDstu2.PROFILE, CustomDiagnosticReportDstu2.class);
|
||||
|
||||
//@formatter:off
|
||||
String input =
|
||||
"<DiagnosticReport xmlns=\"http://hl7.org/fhir\">" +
|
||||
"<meta>" +
|
||||
"<profile value=\"" + CustomDiagnosticReportDstu2.PROFILE + "\"/>" +
|
||||
"<profile value=\"http://custom_DiagnosticReport2\"/>" +
|
||||
"<profile value=\"http://custom_DiagnosticReport3\"/>" +
|
||||
"</meta>" +
|
||||
"<status value=\"final\"/>" +
|
||||
"</DiagnosticReport>";
|
||||
//@formatter:on
|
||||
|
||||
IParser parser = ourCtx.newXmlParser();
|
||||
CustomDiagnosticReportDstu2 dr = (CustomDiagnosticReportDstu2) parser.parseResource(input);
|
||||
assertEquals(DiagnosticReportStatusEnum.FINAL, dr.getStatusElement().getValueAsEnum());
|
||||
|
||||
List<IdDt> profiles = ResourceMetadataKeyEnum.PROFILES.get(dr);
|
||||
assertEquals(3, profiles.size());
|
||||
|
||||
ourCtx = null;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testEncodeAndParseContainedNonCustomTypes() {
|
||||
ourCtx = FhirContext.forDstu2();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
@ -8,12 +9,7 @@ import static org.junit.Assert.assertThat;
|
|||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -80,7 +76,7 @@ public class InterceptorUserDataMapDstu2Test {
|
|||
@Before
|
||||
public void beforePurgeMap() {
|
||||
myMap = null;
|
||||
myMapCheckMethods= new HashSet<String>();
|
||||
myMapCheckMethods= new LinkedHashSet<String>();
|
||||
}
|
||||
|
||||
|
||||
|
@ -96,7 +92,7 @@ public class InterceptorUserDataMapDstu2Test {
|
|||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info(myMapCheckMethods.toString());
|
||||
assertThat(myMapCheckMethods, containsInAnyOrder("incomingRequestPreHandled", "handleException", "incomingRequestPostProcessed", "preProcessOutgoingException"));
|
||||
assertThat(myMapCheckMethods, containsInAnyOrder("incomingRequestPostProcessed", "incomingRequestPreHandled", "preProcessOutgoingException", "handleException"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -111,7 +107,7 @@ public class InterceptorUserDataMapDstu2Test {
|
|||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info(myMapCheckMethods.toString());
|
||||
assertThat(myMapCheckMethods, containsInAnyOrder("incomingRequestPreHandled", "incomingRequestPostProcessed", "outgoingResponse"));
|
||||
assertThat(myMapCheckMethods, contains("incomingRequestPostProcessed", "incomingRequestPreHandled", "outgoingResponse", "processingCompletedNormally"));
|
||||
}
|
||||
|
||||
protected void updateMapUsing(Map<Object, Object> theUserData, Method theMethod) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
@ -59,9 +60,6 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
public class LoggingInterceptorDstu2Test {
|
||||
|
||||
private static CloseableHttpClient ourClient;
|
||||
|
@ -70,6 +68,7 @@ public class LoggingInterceptorDstu2Test {
|
|||
private static Server ourServer;
|
||||
private static RestfulServer servlet;
|
||||
private IServerInterceptor myInterceptor;
|
||||
private static int ourDelayMs;
|
||||
private static Exception ourThrowException;
|
||||
|
||||
@Before
|
||||
|
@ -77,6 +76,7 @@ public class LoggingInterceptorDstu2Test {
|
|||
myInterceptor = mock(IServerInterceptor.class);
|
||||
servlet.setInterceptors(Collections.singletonList(myInterceptor));
|
||||
ourThrowException = null;
|
||||
ourDelayMs=0;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -98,9 +98,8 @@ public class LoggingInterceptorDstu2Test {
|
|||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(logger, times(2)).info(captor.capture());
|
||||
assertThat(captor.getAllValues().get(0), StringContains.containsString("read - Patient/EX"));
|
||||
assertThat(captor.getAllValues().get(1), StringContains.containsString("ERROR - GET http://localhost:" + ourPort + "/Patient/EX"));
|
||||
verify(logger, times(1)).info(captor.capture());
|
||||
assertThat(captor.getAllValues().get(0), StringContains.containsString("ERROR - GET http://localhost:" + ourPort + "/Patient/EX"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -135,6 +134,10 @@ public class LoggingInterceptorDstu2Test {
|
|||
HttpResponse status = ourClient.execute(httpGet);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
// The server finishes the response and closes the connection, then runs the final interceptor so technically
|
||||
// we could get here before the interceptor has fired
|
||||
Thread.sleep(100);
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(logger, times(1)).info(captor.capture());
|
||||
assertEquals("extended-operation-instance - $everything - Patient/123", captor.getValue());
|
||||
|
@ -160,6 +163,31 @@ public class LoggingInterceptorDstu2Test {
|
|||
assertEquals("read - - Patient/1 - ", captor.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProcessingTime() throws Exception {
|
||||
ourDelayMs = 110;
|
||||
|
||||
LoggingInterceptor interceptor = new LoggingInterceptor();
|
||||
interceptor.setMessageFormat("${processingTimeMillis}");
|
||||
servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor));
|
||||
|
||||
Logger logger = mock(Logger.class);
|
||||
interceptor.setLogger(logger);
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
// The server finishes the response and closes the connection, then runs the final interceptor so technically
|
||||
// we could get here before the interceptor has fired
|
||||
Thread.sleep(100);
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(logger, times(1)).info(captor.capture());
|
||||
assertThat(captor.getValue(), matchesPattern("[0-9]{3}"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestBodyReadWithContentTypeHeader() throws Exception {
|
||||
|
||||
|
@ -201,6 +229,10 @@ public class LoggingInterceptorDstu2Test {
|
|||
HttpResponse status = ourClient.execute(httpPost);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
// The server finishes the response and closes the connection, then runs the final interceptor so technically
|
||||
// we could get here before the interceptor has fired
|
||||
Thread.sleep(100);
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(logger, times(1)).info(captor.capture());
|
||||
assertEquals("create - - Patient - <Patient xmlns=\"http://hl7.org/fhir\"><identifier><value value=\"VAL\"/></identifier></Patient>", captor.getValue());
|
||||
|
@ -211,6 +243,7 @@ public class LoggingInterceptorDstu2Test {
|
|||
|
||||
LoggingInterceptor interceptor = new LoggingInterceptor();
|
||||
interceptor.setMessageFormat("${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}");
|
||||
interceptor.setErrorMessageFormat("ERROR - ${operationType} - ${operationName} - ${idOrResourceName} - ${requestBodyFhir}");
|
||||
servlet.setInterceptors(Collections.singletonList((IServerInterceptor) interceptor));
|
||||
|
||||
Logger logger = mock(Logger.class);
|
||||
|
@ -228,9 +261,13 @@ public class LoggingInterceptorDstu2Test {
|
|||
HttpResponse status = ourClient.execute(httpPost);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
// The server finishes the response and closes the connection, then runs the final interceptor so technically
|
||||
// we could get here before the interceptor has fired
|
||||
Thread.sleep(100);
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(logger, times(1)).info(captor.capture());
|
||||
assertEquals("create - - Patient - <Patient xmlns=\"http://hl7.org/fhir\"><identifier><value value=\"VAL\"/></identifier></Patient>", captor.getValue());
|
||||
assertEquals("ERROR - create - - Patient - <Patient xmlns=\"http://hl7.org/fhir\"><identifier><value value=\"VAL\"/></identifier></Patient>", captor.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -265,7 +302,11 @@ public class LoggingInterceptorDstu2Test {
|
|||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$everything");
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
|
||||
// The server finishes the response and closes the connection, then runs the final interceptor so technically
|
||||
// we could get here before the interceptor has fired
|
||||
Thread.sleep(100);
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(logger, times(1)).info(captor.capture());
|
||||
assertEquals("extended-operation-type - $everything - Patient", captor.getValue());
|
||||
|
@ -284,6 +325,10 @@ public class LoggingInterceptorDstu2Test {
|
|||
HttpResponse status = ourClient.execute(httpGet);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
// The server finishes the response and closes the connection, then runs the final interceptor so technically
|
||||
// we could get here before the interceptor has fired
|
||||
Thread.sleep(100);
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(logger, times(1)).info(captor.capture());
|
||||
assertThat(captor.getValue(), StringContains.containsString("read - Patient/1"));
|
||||
|
@ -303,6 +348,10 @@ public class LoggingInterceptorDstu2Test {
|
|||
HttpResponse status = ourClient.execute(httpGet);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
// The server finishes the response and closes the connection, then runs the final interceptor so technically
|
||||
// we could get here before the interceptor has fired
|
||||
Thread.sleep(100);
|
||||
|
||||
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
|
||||
verify(logger, times(1)).info(captor.capture());
|
||||
assertThat(captor.getValue(), StringContains.containsString("search-type - Patient - ?_id=1"));
|
||||
|
@ -386,7 +435,11 @@ public class LoggingInterceptorDstu2Test {
|
|||
* @return The resource
|
||||
*/
|
||||
@Read()
|
||||
public Patient getResourceById(@IdParam IdDt theId) {
|
||||
public Patient getResourceById(@IdParam IdDt theId) throws InterruptedException {
|
||||
if (ourDelayMs>0) {
|
||||
Thread.sleep(ourDelayMs);
|
||||
}
|
||||
|
||||
if (theId.getIdPart().equals("EX")) {
|
||||
throw new InvalidRequestException("FOO");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.stringContainsInOrder;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
@ -14,6 +15,7 @@ import static org.mockito.Mockito.when;
|
|||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
|
@ -87,12 +89,26 @@ public class ResponseHighlightingInterceptorTest {
|
|||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testForceResponseTime() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1?_format=html/json");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("text/html;charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
assertThat(responseContent.replace('\n', ' ').replace('\r', ' '), matchesPattern(".*Response generated in [0-9]+ms.*"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInvalidResource() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Foobar/123");
|
||||
httpGet.addHeader("Accept", "text/html");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Resp: {}", responseContent);
|
||||
|
@ -457,7 +473,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithWildcardRetVal&_summary=count");
|
||||
httpGet.addHeader("Accept", "html");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
||||
ourLog.info("Resp: {}", responseContent);
|
||||
|
@ -501,7 +517,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
httpGet.addHeader("Accept", Constants.CT_FHIR_JSON);
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_FHIR_JSON + ";charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
|
@ -515,7 +531,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_FHIR_JSON + ";charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
|
@ -527,7 +543,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_FHIR_JSON + ";charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
|
@ -539,7 +555,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_FHIR_JSON + ";charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
|
@ -552,7 +568,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals(Constants.CT_FHIR_JSON + ";charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
|
@ -567,7 +583,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
httpGet.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.1");
|
||||
|
||||
HttpResponse status = ourClient.execute(httpGet);
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("text/html;charset=utf-8", status.getFirstHeader("content-type").getValue().replace(" ", "").toLowerCase());
|
||||
|
|
|
@ -21,13 +21,7 @@ package org.hl7.fhir.dstu3.hapi.rest.server;
|
|||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
|
@ -35,25 +29,17 @@ import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
|
|||
import org.hl7.fhir.dstu3.model.Bundle.BundleLinkComponent;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.HTTPVerb;
|
||||
import org.hl7.fhir.dstu3.model.Bundle.SearchEntryMode;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
import org.hl7.fhir.dstu3.model.DomainResource;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.Resource;
|
||||
import org.hl7.fhir.instance.model.api.*;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.rest.server.BundleInclusionRule;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.server.IVersionSpecificBundleFactory;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.*;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
|
||||
|
@ -135,6 +121,7 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
|
|||
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(next);
|
||||
if (httpVerb != null) {
|
||||
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
|
||||
entry.getRequest().getUrlElement().setValue(next.getId());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,7 +148,7 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
|
|||
|
||||
for (IBaseResource next : theResult) {
|
||||
if (next.getIdElement().isEmpty() == false) {
|
||||
addedResourceIds.add((IdType) next.getIdElement());
|
||||
addedResourceIds.add(next.getIdElement());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -221,11 +208,14 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
|
|||
|
||||
BundleEntryComponent entry = myBundle.addEntry().setResource((Resource) next);
|
||||
Resource nextAsResource = (Resource)next;
|
||||
IIdType id = populateBundleEntryFullUrl(next, entry);
|
||||
String httpVerb = ResourceMetadataKeyEnum.ENTRY_TRANSACTION_METHOD.get(nextAsResource);
|
||||
if (httpVerb != null) {
|
||||
entry.getRequest().getMethodElement().setValueAsString(httpVerb);
|
||||
if (id != null) {
|
||||
entry.getRequest().setUrl(id.getValue());
|
||||
}
|
||||
}
|
||||
populateBundleEntryFullUrl(next, entry);
|
||||
|
||||
String searchMode = ResourceMetadataKeyEnum.ENTRY_SEARCH_MODE.get(nextAsResource);
|
||||
if (searchMode != null) {
|
||||
|
@ -244,16 +234,19 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
|
|||
|
||||
}
|
||||
|
||||
private void populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
|
||||
private IIdType populateBundleEntryFullUrl(IBaseResource next, BundleEntryComponent entry) {
|
||||
IIdType idElement = null;
|
||||
if (next.getIdElement().hasBaseUrl()) {
|
||||
entry.setFullUrl(next.getIdElement().toVersionless().getValue());
|
||||
idElement = next.getIdElement();
|
||||
entry.setFullUrl(idElement.toVersionless().getValue());
|
||||
} else {
|
||||
if (isNotBlank(myBase) && next.getIdElement().hasIdPart()) {
|
||||
IIdType id = next.getIdElement().toVersionless();
|
||||
id = id.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
|
||||
entry.setFullUrl(id.getValue());
|
||||
idElement = next.getIdElement();
|
||||
idElement = idElement.withServerBase(myBase, myContext.getResourceDefinition(next).getName());
|
||||
entry.setFullUrl(idElement.toVersionless().getValue());
|
||||
}
|
||||
}
|
||||
return idElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -392,7 +385,7 @@ public class Dstu3BundleFactory implements IVersionSpecificBundleFactory {
|
|||
Resource next = (Resource) nextBaseRes;
|
||||
BundleEntryComponent nextEntry = myBundle.addEntry();
|
||||
|
||||
nextEntry.setResource((Resource) next);
|
||||
nextEntry.setResource(next);
|
||||
if (next.getIdElement().isEmpty()) {
|
||||
nextEntry.getRequest().setMethod(HTTPVerb.POST);
|
||||
} else {
|
||||
|
|
|
@ -223,8 +223,8 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
|
|||
retVal.setKind(ConformanceStatementKind.INSTANCE);
|
||||
retVal.getSoftware().setName(myServerConfiguration.getServerName());
|
||||
retVal.getSoftware().setVersion(myServerConfiguration.getServerVersion());
|
||||
retVal.addFormat(Constants.CT_FHIR_XML);
|
||||
retVal.addFormat(Constants.CT_FHIR_JSON);
|
||||
retVal.addFormat(Constants.CT_FHIR_XML_NEW);
|
||||
retVal.addFormat(Constants.CT_FHIR_JSON_NEW);
|
||||
retVal.setStatus(ConformanceResourceStatus.ACTIVE);
|
||||
|
||||
ConformanceRestComponent rest = retVal.addRest();
|
||||
|
|
|
@ -66,8 +66,8 @@ public class ClientMimetypeDstu3Test {
|
|||
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
|
||||
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
assertEquals("application/xml+fhir", capt.getAllValues().get(0).getFirstHeader("accept").getValue());
|
||||
assertEquals(Constants.CT_FHIR_XML_NEW, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
|
||||
assertEquals("<Patient xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">A PATIENT</div></text></Patient>", extractBodyAsString(capt));
|
||||
}
|
||||
|
||||
|
@ -86,8 +86,8 @@ public class ClientMimetypeDstu3Test {
|
|||
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
|
||||
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
assertEquals("application/xml+fhir", capt.getAllValues().get(0).getFirstHeader("accept").getValue());
|
||||
assertEquals(Constants.CT_FHIR_XML_NEW, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
|
||||
assertEquals("<Patient xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">A PATIENT</div></text></Patient>", extractBodyAsString(capt));
|
||||
}
|
||||
|
||||
|
@ -106,8 +106,8 @@ public class ClientMimetypeDstu3Test {
|
|||
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
|
||||
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals(Constants.CT_FHIR_JSON, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
assertEquals("application/json+fhir", capt.getAllValues().get(0).getFirstHeader("accept").getValue());
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
|
||||
assertEquals("{\"resourceType\":\"Patient\",\"text\":{\"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">A PATIENT</div>\"}}", extractBodyAsString(capt));
|
||||
}
|
||||
|
||||
|
@ -126,8 +126,8 @@ public class ClientMimetypeDstu3Test {
|
|||
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">FINAL VALUE</div>", ((Patient) outcome.getResource()).getText().getDivAsString());
|
||||
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals(Constants.CT_FHIR_JSON, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
assertEquals("application/json+fhir", capt.getAllValues().get(0).getFirstHeader("accept").getValue());
|
||||
assertEquals(Constants.CT_FHIR_JSON_NEW, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_JSON_NON_LEGACY, capt.getAllValues().get(0).getFirstHeader("accept").getValue());
|
||||
assertEquals("{\"resourceType\":\"Patient\",\"text\":{\"div\":\"<div xmlns=\\\"http://www.w3.org/1999/xhtml\\\">A PATIENT</div>\"}}", extractBodyAsString(capt));
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -26,6 +27,7 @@ import org.apache.http.ProtocolVersion;
|
|||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicStatusLine;
|
||||
|
@ -62,6 +64,7 @@ import ca.uhn.fhir.rest.client.interceptor.CookieInterceptor;
|
|||
import ca.uhn.fhir.rest.client.interceptor.UserInfoInterceptor;
|
||||
import ca.uhn.fhir.rest.param.ParamPrefixEnum;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnclassifiedServerFailureException;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
@ -201,8 +204,8 @@ public class GenericClientDstu3Test {
|
|||
assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
validateUserAgent(capt);
|
||||
|
||||
assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||
Binary output = ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt));
|
||||
assertEquals(Constants.CT_FHIR_JSON, output.getContentType());
|
||||
|
||||
|
@ -293,8 +296,8 @@ public class GenericClientDstu3Test {
|
|||
assertEquals("http://example.com/fhir/Binary", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
validateUserAgent(capt);
|
||||
|
||||
assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||
assertArrayEquals(new byte[] { 0, 1, 2, 3, 4 }, ourCtx.newXmlParser().parseResource(Binary.class, extractBodyAsString(capt)).getContent());
|
||||
|
||||
}
|
||||
|
@ -407,7 +410,7 @@ public class GenericClientDstu3Test {
|
|||
|
||||
assertEquals(myAnswerCount, capt.getAllValues().size());
|
||||
assertEquals("http://example.com/fhir/Patient", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
assertEquals(Constants.CT_FHIR_XML_NEW, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
|
||||
assertEquals("http://foo.com/base/Patient/222/_history/3", capt.getAllValues().get(1).getURI().toASCIIString());
|
||||
}
|
||||
|
@ -735,6 +738,93 @@ public class GenericClientDstu3Test {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutDoesntForceAllIdsXml() throws Exception {
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setId("PATIENT1");
|
||||
patient.addName().addFamily("PATIENT1");
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.setId("BUNDLE1");
|
||||
bundle.addEntry().setResource(patient);
|
||||
|
||||
final String encoded = p.encodeResourceToString(bundle);
|
||||
assertEquals("<Bundle xmlns=\"http://hl7.org/fhir\"><id value=\"BUNDLE1\"/><entry><resource><Patient xmlns=\"http://hl7.org/fhir\"><id value=\"PATIENT1\"/><name><family value=\"PATIENT1\"/></name></Patient></resource></entry></Bundle>", encoded);
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(encoded), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
client
|
||||
.update()
|
||||
.resource(bundle)
|
||||
.prefer(PreferReturnEnum.REPRESENTATION)
|
||||
.execute();
|
||||
|
||||
HttpPut httpRequest = (HttpPut) capt.getValue();
|
||||
assertEquals("http://example.com/fhir/Bundle/BUNDLE1", httpRequest.getURI().toASCIIString());
|
||||
|
||||
String requestString = IOUtils.toString(httpRequest.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertEquals(encoded, requestString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPutDoesntForceAllIdsJson() throws Exception {
|
||||
IParser p = ourCtx.newJsonParser();
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.setId("PATIENT1");
|
||||
patient.addName().addFamily("PATIENT1");
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.setId("BUNDLE1");
|
||||
bundle.addEntry().setResource(patient);
|
||||
|
||||
final String encoded = p.encodeResourceToString(bundle);
|
||||
assertEquals("{\"resourceType\":\"Bundle\",\"id\":\"BUNDLE1\",\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"PATIENT1\",\"name\":[{\"family\":[\"PATIENT1\"]}]}}]}", encoded);
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON_NEW + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(encoded), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
client
|
||||
.update()
|
||||
.resource(bundle)
|
||||
.prefer(PreferReturnEnum.REPRESENTATION)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
|
||||
HttpPut httpRequest = (HttpPut) capt.getValue();
|
||||
assertEquals("http://example.com/fhir/Bundle/BUNDLE1", httpRequest.getURI().toASCIIString());
|
||||
|
||||
String requestString = IOUtils.toString(httpRequest.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
assertEquals(encoded, requestString);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseHasContentTypeMissing() throws Exception {
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
|
@ -1194,6 +1284,48 @@ public class GenericClientDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptHeaderWithEncodingSpecified() throws Exception {
|
||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
client.setEncoding(EncodingEnum.JSON);
|
||||
client.search()
|
||||
.forResource("Device")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Device?_format=json", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
|
||||
assertEquals("application/fhir+json;q=1.0, application/json+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.setEncoding(EncodingEnum.XML);
|
||||
client.search()
|
||||
.forResource("Device")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Device?_format=xml", UrlUtil.unescape(capt.getAllValues().get(idx).getURI().toString()));
|
||||
assertEquals("application/fhir+xml;q=1.0, application/xml+fhir;q=0.9", capt.getAllValues().get(idx).getFirstHeader(Constants.HEADER_ACCEPT).getValue());
|
||||
idx++;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchForUnknownType() throws Exception {
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
@ -1368,8 +1500,8 @@ public class GenericClientDstu3Test {
|
|||
assertEquals("http://example.com/fhir/Patient/111", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
validateUserAgent(capt);
|
||||
|
||||
assertEquals("application/xml+fhir;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||
assertEquals(Constants.CT_FHIR_XML, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||
assertEquals("application/fhir+xml;charset=utf-8", capt.getAllValues().get(0).getHeaders("Content-Type")[0].getValue().toLowerCase().replace(" ", ""));
|
||||
assertEquals(Constants.HEADER_ACCEPT_VALUE_XML_NON_LEGACY, capt.getAllValues().get(0).getHeaders("Accept")[0].getValue());
|
||||
String body = extractBodyAsString(capt);
|
||||
assertThat(body, containsString("<id value=\"111\"/>"));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
package ca.uhn.fhir.rest.client;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.io.input.ReaderInputStream;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.ProtocolVersion;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.apache.http.message.BasicStatusLine;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.junit.*;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Patch;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
import ca.uhn.fhir.rest.server.Constants;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class PatchClientDstu3Test {
|
||||
public interface IClientType extends IRestfulClient {
|
||||
|
||||
@Patch(type=Patient.class)
|
||||
MethodOutcome patch(@IdParam IdType theId, @ResourceParam String theBody, PatchTypeEnum thePatchType);
|
||||
|
||||
}
|
||||
|
||||
private static FhirContext ourCtx;
|
||||
private HttpClient myHttpClient;
|
||||
private HttpResponse myHttpResponse;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
|
||||
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonPatchAnnotation() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = prepareResponse();
|
||||
|
||||
IClientType client = ourCtx.newRestfulClient(IClientType.class, "http://example.com/fhir");
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.getText().setDivAsString("A PATIENT");
|
||||
|
||||
MethodOutcome outcome = client.patch(new IdType("Patient/123"), "{}", PatchTypeEnum.JSON_PATCH);
|
||||
|
||||
assertEquals("PATCH", capt.getAllValues().get(0).getMethod());
|
||||
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
assertEquals("{}", extractBodyAsString(capt));
|
||||
assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK</div>", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testJsonPatchFluent() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = prepareResponse();
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
Patient pt = new Patient();
|
||||
pt.getText().setDivAsString("A PATIENT");
|
||||
|
||||
// MethodOutcome outcome = client.patch().resource("").
|
||||
|
||||
// patch(new IdType("Patient/123"), "{}", PatchTypeEnum.JSON_PATCH);
|
||||
|
||||
// assertEquals("PATCH", capt.getAllValues().get(0).getMethod());
|
||||
// assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(0).getURI().toASCIIString());
|
||||
// assertEquals(Constants.CT_JSON_PATCH, capt.getAllValues().get(0).getFirstHeader("content-type").getValue().replaceAll(";.*", ""));
|
||||
// assertEquals("{}", extractBodyAsString(capt));
|
||||
// assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\">OK</div>", ((OperationOutcome) outcome.getOperationOutcome()).getText().getDivAsString());
|
||||
}
|
||||
|
||||
|
||||
private String extractBodyAsString(ArgumentCaptor<HttpUriRequest> capt) throws IOException {
|
||||
String body = IOUtils.toString(((HttpEntityEnclosingRequestBase) capt.getAllValues().get(0)).getEntity().getContent(), "UTF-8");
|
||||
return body;
|
||||
}
|
||||
|
||||
private ArgumentCaptor<HttpUriRequest> prepareResponse() throws IOException, ClientProtocolException {
|
||||
final IParser p = ourCtx.newXmlParser();
|
||||
|
||||
final OperationOutcome resp1 = new OperationOutcome();
|
||||
resp1.getText().setDivAsString("OK");
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML_NEW + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(p.encodeResourceToString(resp1)), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
return capt;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
ourCtx = FhirContext.forDstu3();
|
||||
}
|
||||
|
||||
}
|
|
@ -44,6 +44,7 @@ import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
|||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
|
@ -112,6 +113,8 @@ public class InterceptorDstu3Test {
|
|||
order.verify(myInterceptor2, times(1)).incomingRequestPreHandled(any(RestOperationTypeEnum.class), any(ActionRequestDetails.class));
|
||||
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class));
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class));
|
||||
order.verify(myInterceptor2, times(1)).processingCompletedNormally(any(ServletRequestDetails.class));
|
||||
order.verify(myInterceptor1, times(1)).processingCompletedNormally(any(ServletRequestDetails.class));
|
||||
verifyNoMoreInteractions(myInterceptor1);
|
||||
verifyNoMoreInteractions(myInterceptor2);
|
||||
|
||||
|
|
|
@ -0,0 +1,191 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPatch;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Patch;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.api.PatchTypeEnum;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
|
||||
public class PatchDstu3Test {
|
||||
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PatchDstu3Test.class);
|
||||
private static int ourPort;
|
||||
private static Server ourServer;
|
||||
private static String ourLastMethod;
|
||||
private static PatchTypeEnum ourLastPatchType;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourLastMethod = null;
|
||||
ourLastBody = null;
|
||||
ourLastId = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatchValidJson() throws Exception {
|
||||
String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]";
|
||||
HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123");
|
||||
httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH)));
|
||||
CloseableHttpResponse status = ourClient.execute(httpPatch);
|
||||
|
||||
try {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">OK</div></text></OperationOutcome>", responseContent);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
assertEquals("patientPatch", ourLastMethod);
|
||||
assertEquals("Patient/123", ourLastId.getValue());
|
||||
assertEquals(requestContents, ourLastBody);
|
||||
assertEquals(PatchTypeEnum.JSON_PATCH, ourLastPatchType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatchValidXml() throws Exception {
|
||||
String requestContents = "<root/>";
|
||||
HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123");
|
||||
httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_XML_PATCH)));
|
||||
CloseableHttpResponse status = ourClient.execute(httpPatch);
|
||||
|
||||
try {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertEquals("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><text><div xmlns=\"http://www.w3.org/1999/xhtml\">OK</div></text></OperationOutcome>", responseContent);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
assertEquals("patientPatch", ourLastMethod);
|
||||
assertEquals("Patient/123", ourLastId.getValue());
|
||||
assertEquals(requestContents, ourLastBody);
|
||||
assertEquals(PatchTypeEnum.XML_PATCH, ourLastPatchType);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatchValidJsonWithCharset() throws Exception {
|
||||
String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]";
|
||||
HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123");
|
||||
httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse(Constants.CT_JSON_PATCH + Constants.CHARSET_UTF8_CTSUFFIX)));
|
||||
CloseableHttpResponse status = ourClient.execute(httpPatch);
|
||||
|
||||
try {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
assertEquals("patientPatch", ourLastMethod);
|
||||
assertEquals("Patient/123", ourLastId.getValue());
|
||||
assertEquals(requestContents, ourLastBody);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPatchInvalidMimeType() throws Exception {
|
||||
String requestContents = "[ { \"op\": \"add\", \"path\": \"/a/b/c\", \"value\": [ \"foo\", \"bar\" ] } ]";
|
||||
HttpPatch httpPatch = new HttpPatch("http://localhost:" + ourPort + "/Patient/123");
|
||||
httpPatch.setEntity(new StringEntity(requestContents, ContentType.parse("text/plain; charset=UTF-8")));
|
||||
CloseableHttpResponse status = ourClient.execute(httpPatch);
|
||||
|
||||
try {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
assertEquals("<OperationOutcome xmlns=\"http://hl7.org/fhir\"><issue><severity value=\"error\"/><code value=\"processing\"/><diagnostics value=\"Invalid Content-Type for PATCH operation: text/plain; charset=UTF-8\"/></issue></OperationOutcome>", responseContent);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = PortUtil.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
|
||||
|
||||
servlet.setResourceProviders(patientProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
private static String ourLastBody;
|
||||
private static IdType ourLastId;
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
@Patch
|
||||
public OperationOutcome patientPatch(@IdParam IdType theId, PatchTypeEnum thePatchType, @ResourceParam String theBody) {
|
||||
ourLastMethod = "patientPatch";
|
||||
ourLastBody = theBody;
|
||||
ourLastId = theId;
|
||||
ourLastPatchType = thePatchType;
|
||||
OperationOutcome retVal = new OperationOutcome();
|
||||
retVal.getText().setDivAsString("<div>OK</div>");
|
||||
return retVal;
|
||||
}
|
||||
//@formatter:on
|
||||
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue