Merge pull request #706 from InfiniteLoop90/master

Fixes #705 - Client correctly validates the server's conformance statement FHIR version
This commit is contained in:
James Agnew 2017-08-13 11:03:52 -04:00 committed by GitHub
commit 72be7ab795
8 changed files with 26 additions and 117 deletions

View File

@ -42,7 +42,7 @@ public enum FhirVersionEnum {
DSTU2_1("org.hl7.fhir.dstu2016may.hapi.ctx.FhirDstu2_1", null, true, new Version("1.4.0")), DSTU2_1("org.hl7.fhir.dstu2016may.hapi.ctx.FhirDstu2_1", null, true, new Version("1.4.0")),
DSTU3("org.hl7.fhir.dstu3.hapi.ctx.FhirDstu3", null, true, new Dstu3Version()); DSTU3("org.hl7.fhir.dstu3.hapi.ctx.FhirDstu3", null, true, new Version("3.0.1"));
private final FhirVersionEnum myEquivalent; private final FhirVersionEnum myEquivalent;
private final boolean myIsRi; private final boolean myIsRi;
@ -138,24 +138,4 @@ public enum FhirVersionEnum {
String provideVersion(); String provideVersion();
} }
private static class Dstu3Version implements IVersionProvider {
public Dstu3Version() {
try {
Class<?> c = Class.forName("org.hl7.fhir.dstu3.model.Constants");
myVersion = (String) c.getDeclaredField("VERSION").get(null);
} catch (Exception e) {
myVersion = "UNKNOWN";
}
}
private String myVersion;
@Override
public String provideVersion() {
return myVersion;
}
}
} }

View File

@ -325,13 +325,14 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
if (StringUtils.isBlank(serverFhirVersionString)) { if (StringUtils.isBlank(serverFhirVersionString)) {
// we'll be lenient and accept this // we'll be lenient and accept this
} else { } else {
//FIXME null access on serverFhirVersionString if (serverFhirVersionString.equals(FhirVersionEnum.DSTU1.getFhirVersionString())) {
if (serverFhirVersionString.startsWith("0.80") || serverFhirVersionString.startsWith("0.0.8")) {
serverFhirVersionEnum = FhirVersionEnum.DSTU1; serverFhirVersionEnum = FhirVersionEnum.DSTU1;
} else if (serverFhirVersionString.startsWith("0.4")) { } else if (serverFhirVersionString.equals(FhirVersionEnum.DSTU2.getFhirVersionString())) {
serverFhirVersionEnum = FhirVersionEnum.DSTU2;
} else if (serverFhirVersionString.startsWith("0.5")) {
serverFhirVersionEnum = FhirVersionEnum.DSTU2; serverFhirVersionEnum = FhirVersionEnum.DSTU2;
} else if (serverFhirVersionString.equals(FhirVersionEnum.DSTU2_1.getFhirVersionString())) {
serverFhirVersionEnum = FhirVersionEnum.DSTU2_1;
} else if (serverFhirVersionString.equals(FhirVersionEnum.DSTU3.getFhirVersionString())) {
serverFhirVersionEnum = FhirVersionEnum.DSTU3;
} else { } else {
// we'll be lenient and accept this // we'll be lenient and accept this
ourLog.debug("Server conformance statement indicates unknown FHIR version: {}", serverFhirVersionString); ourLog.debug("Server conformance statement indicates unknown FHIR version: {}", serverFhirVersionString);

View File

@ -31,6 +31,7 @@ import java.util.TreeSet;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -125,7 +126,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.setPublisher(myPublisher); retVal.setPublisher(myPublisher);
retVal.setDate(conformanceDate()); retVal.setDate(conformanceDate());
retVal.setFhirVersion("0.0.82-3059"); // TODO: pull from model retVal.setFhirVersion(FhirVersionEnum.DSTU1.getFhirVersionString());
retVal.setAcceptUnknown(false); // TODO: make this configurable - this is a fairly big effort since the parser needs to be modified to actually allow it retVal.setAcceptUnknown(false); // TODO: make this configurable - this is a fairly big effort since the parser needs to be modified to actually allow it
retVal.getImplementation().setDescription(myServerConfiguration.getImplementationDescription()); retVal.getImplementation().setDescription(myServerConfiguration.getImplementationDescription());

View File

@ -52,7 +52,7 @@ public class ClientServerValidationDstu1Test {
@Test @Test
public void testServerReturnsAppropriateVersionDstu() throws Exception { public void testServerReturnsAppropriateVersionDstu() throws Exception {
Conformance conf = new Conformance(); Conformance conf = new Conformance();
conf.setFhirVersion("0.0.8"); conf.setFhirVersion("0.0.82");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf); final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -89,7 +89,7 @@ public class ClientServerValidationDstu1Test {
@Test @Test
public void testServerReturnsWrongVersionDstu() throws Exception { public void testServerReturnsWrongVersionDstu() throws Exception {
Conformance conf = new Conformance(); Conformance conf = new Conformance();
conf.setFhirVersion("0.4.0"); conf.setFhirVersion("1.0.2");
String msg = myCtx.newXmlParser().encodeResourceToString(conf); String msg = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -105,7 +105,7 @@ public class ClientServerValidationDstu1Test {
myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/1")); myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/1"));
fail(); fail();
} catch (FhirClientInappropriateForServerException e) { } catch (FhirClientInappropriateForServerException e) {
assertThat(e.toString(), containsString("The server at base URL \"http://foo/metadata\" returned a conformance statement indicating that it supports FHIR version \"0.4.0\" which corresponds to DSTU2, but this client is configured to use DSTU1 (via the FhirContext)")); assertThat(e.toString(), containsString("The server at base URL \"http://foo/metadata\" returned a conformance statement indicating that it supports FHIR version \"1.0.2\" which corresponds to DSTU2, but this client is configured to use DSTU1 (via the FhirContext)"));
} }
} }

View File

@ -38,6 +38,7 @@ import java.util.TreeSet;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
@ -196,7 +197,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.setPublisher(myPublisher); retVal.setPublisher(myPublisher);
retVal.setDate(conformanceDate()); retVal.setDate(conformanceDate());
retVal.setFhirVersion("1.0.2"); // TODO: pull from model retVal.setFhirVersion(FhirVersionEnum.DSTU2.getFhirVersionString());
retVal.setAcceptUnknown(UnknownContentCodeEnum.UNKNOWN_EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser retVal.setAcceptUnknown(UnknownContentCodeEnum.UNKNOWN_EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser
// needs to be modified to actually allow it // needs to be modified to actually allow it

View File

@ -70,7 +70,7 @@ public class ClientServerValidationDstu2Test {
@Test @Test
public void testClientUsesInterceptors() throws Exception { public void testClientUsesInterceptors() throws Exception {
Conformance conf = new Conformance(); Conformance conf = new Conformance();
conf.setFhirVersion("0.5.0"); conf.setFhirVersion("1.0.2");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf); final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -112,7 +112,7 @@ public class ClientServerValidationDstu2Test {
@Test @Test
public void testForceConformanceCheck() throws Exception { public void testForceConformanceCheck() throws Exception {
Conformance conf = new Conformance(); Conformance conf = new Conformance();
conf.setFhirVersion("0.5.0"); conf.setFhirVersion("1.0.2");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf); final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -181,47 +181,9 @@ public class ClientServerValidationDstu2Test {
} }
@Test @Test
public void testServerReturnsAppropriateVersionForDstu2_040() throws Exception { public void testServerReturnsAppropriateVersionForDstu2() throws Exception {
Conformance conf = new Conformance(); Conformance conf = new Conformance();
conf.setFhirVersion("0.5.0"); conf.setFhirVersion("1.0.2");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
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<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
if (myFirstResponse) {
myFirstResponse = false;
return new ReaderInputStream(new StringReader(confResource), Charset.forName("UTF-8"));
} else {
return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(new Patient())), Charset.forName("UTF-8"));
}
}
});
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
myCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.ONCE);
IGenericClient client = myCtx.newRestfulGenericClient("http://foo");
// don't load the conformance until the first time the client is actually used
assertTrue(myFirstResponse);
client.read(new UriDt("http://foo/Patient/123"));
assertFalse(myFirstResponse);
myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123"));
myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123"));
// Conformance only loaded once, then 3 reads
verify(myHttpClient, times(4)).execute(Matchers.any(HttpUriRequest.class));
}
@Test
public void testServerReturnsAppropriateVersionForDstu2_050() throws Exception {
Conformance conf = new Conformance();
conf.setFhirVersion("0.5.0");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf); final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -259,7 +221,7 @@ public class ClientServerValidationDstu2Test {
@Test @Test
public void testServerReturnsWrongVersionForDstu2() throws Exception { public void testServerReturnsWrongVersionForDstu2() throws Exception {
Conformance conf = new Conformance(); Conformance conf = new Conformance();
conf.setFhirVersion("0.80"); conf.setFhirVersion("0.0.82");
String msg = myCtx.newXmlParser().encodeResourceToString(conf); String msg = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -276,7 +238,7 @@ public class ClientServerValidationDstu2Test {
fail(); fail();
} catch (FhirClientInappropriateForServerException e) { } catch (FhirClientInappropriateForServerException e) {
String out = e.toString(); String out = e.toString();
String want = "The server at base URL \"http://foo/metadata\" returned a conformance statement indicating that it supports FHIR version \"0.80\" which corresponds to DSTU1, but this client is configured to use DSTU2 (via the FhirContext)"; String want = "The server at base URL \"http://foo/metadata\" returned a conformance statement indicating that it supports FHIR version \"0.0.82\" which corresponds to DSTU1, but this client is configured to use DSTU2 (via the FhirContext)";
ourLog.info(out); ourLog.info(out);
ourLog.info(want); ourLog.info(want);
assertThat(out, containsString(want)); assertThat(out, containsString(want));

View File

@ -43,6 +43,7 @@ import java.util.jar.Manifest;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.Conformance; import org.hl7.fhir.instance.model.Conformance;
import org.hl7.fhir.instance.model.Conformance.ConditionalDeleteStatus; import org.hl7.fhir.instance.model.Conformance.ConditionalDeleteStatus;
@ -192,7 +193,7 @@ public class ServerConformanceProvider implements IServerConformanceProvider<Con
retVal.setPublisher(myPublisher); retVal.setPublisher(myPublisher);
retVal.setDate(conformanceDate()); retVal.setDate(conformanceDate());
retVal.setFhirVersion("1.0.2"); // TODO: pull from model retVal.setFhirVersion(FhirVersionEnum.DSTU2_HL7ORG.getFhirVersionString());
retVal.setAcceptUnknown(UnknownContentCode.EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser retVal.setAcceptUnknown(UnknownContentCode.EXTENSIONS); // TODO: make this configurable - this is a fairly big effort since the parser
// needs to be modified to actually allow it // needs to be modified to actually allow it

View File

@ -56,46 +56,9 @@ public class ClientServerValidationTestHl7OrgDstu2 {
} }
@Test @Test
public void testServerReturnsAppropriateVersionForDstu2_040() throws Exception { public void testServerReturnsAppropriateVersionForDstu2() throws Exception {
Conformance conf = new Conformance(); Conformance conf = new Conformance();
conf.setFhirVersion("0.5.0"); conf.setFhirVersion("1.0.2");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
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<InputStream>() {
@Override
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
if (myFirstResponse) {
myFirstResponse=false;
return new ReaderInputStream(new StringReader(confResource), Charset.forName("UTF-8"));
} else {
return new ReaderInputStream(new StringReader(myCtx.newXmlParser().encodeResourceToString(new Patient())), Charset.forName("UTF-8"));
}
}});
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
myCtx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.ONCE);
IGenericClient client = myCtx.newRestfulGenericClient("http://foo");
// don't load the conformance until the first time the client is actually used
assertTrue(myFirstResponse);
client.read(new UriDt("http://foo/Patient/123"));
assertFalse(myFirstResponse);
myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123"));
myCtx.newRestfulGenericClient("http://foo").read(new UriDt("http://foo/Patient/123"));
// Conformance only loaded once, then 3 reads
verify(myHttpClient, times(4)).execute(Matchers.any(HttpUriRequest.class));
}
@Test
public void testServerReturnsAppropriateVersionForDstu2_050() throws Exception {
Conformance conf = new Conformance();
conf.setFhirVersion("0.5.0");
final String confResource = myCtx.newXmlParser().encodeResourceToString(conf); final String confResource = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -132,7 +95,7 @@ public class ClientServerValidationTestHl7OrgDstu2 {
@Test @Test
public void testServerReturnsWrongVersionForDstu2() throws Exception { public void testServerReturnsWrongVersionForDstu2() throws Exception {
Conformance conf = new Conformance(); Conformance conf = new Conformance();
conf.setFhirVersion("0.80"); conf.setFhirVersion("0.0.82");
String msg = myCtx.newXmlParser().encodeResourceToString(conf); String msg = myCtx.newXmlParser().encodeResourceToString(conf);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class); ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
@ -149,7 +112,7 @@ public class ClientServerValidationTestHl7OrgDstu2 {
fail(); fail();
} catch (FhirClientInappropriateForServerException e) { } catch (FhirClientInappropriateForServerException e) {
String out = e.toString(); String out = e.toString();
String want = "The server at base URL \"http://foo/metadata\" returned a conformance statement indicating that it supports FHIR version \"0.80\" which corresponds to DSTU1, but this client is configured to use DSTU2_HL7ORG (via the FhirContext)"; String want = "The server at base URL \"http://foo/metadata\" returned a conformance statement indicating that it supports FHIR version \"0.0.82\" which corresponds to DSTU1, but this client is configured to use DSTU2_HL7ORG (via the FhirContext)";
ourLog.info(out); ourLog.info(out);
ourLog.info(want); ourLog.info(want);
assertThat(out, containsString(want)); assertThat(out, containsString(want));