Add support for a NarrativeModeEnum parameter to interact with
_narrative in the request URL
This commit is contained in:
parent
c484425c78
commit
690bcb4b68
|
@ -94,7 +94,7 @@
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<version>${maven_assembly_plugin_version}</version>
|
<version>2.5.3</version>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<phase>package</phase>
|
<phase>package</phase>
|
||||||
|
@ -104,8 +104,8 @@
|
||||||
<configuration>
|
<configuration>
|
||||||
<attach>true</attach>
|
<attach>true</attach>
|
||||||
<descriptors>
|
<descriptors>
|
||||||
<descriptor>${project.basedir}/src/assembly/android-sources.xml</descriptor>
|
<descriptor>/Users/t3903uhn/git/hapi-fhir/hapi-fhir-android/src/assembly/android-sources.xml</descriptor>
|
||||||
<descriptor>${project.basedir}/src/assembly/android-javadoc.xml</descriptor>
|
<descriptor>/Users/t3903uhn/git/hapi-fhir/hapi-fhir-android/src/assembly/android-javadoc.xml</descriptor>
|
||||||
</descriptors>
|
</descriptors>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package ca.uhn.fhir.rest.method;
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.*;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PushbackReader;
|
import java.io.PushbackReader;
|
||||||
|
@ -77,6 +78,7 @@ import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
import ca.uhn.fhir.rest.server.IDynamicSearchResourceProvider;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
|
||||||
import ca.uhn.fhir.rest.server.SearchParameterMap;
|
import ca.uhn.fhir.rest.server.SearchParameterMap;
|
||||||
import ca.uhn.fhir.util.ReflectionUtil;
|
import ca.uhn.fhir.util.ReflectionUtil;
|
||||||
|
|
||||||
|
@ -342,6 +344,8 @@ public class MethodUtil {
|
||||||
param = new ServletRequestParameter();
|
param = new ServletRequestParameter();
|
||||||
} else if (parameterType.equals(HttpServletResponse.class) || parameterType.equals(ServletResponse.class)) {
|
} else if (parameterType.equals(HttpServletResponse.class) || parameterType.equals(ServletResponse.class)) {
|
||||||
param = new ServletResponseParameter();
|
param = new ServletResponseParameter();
|
||||||
|
} else if (parameterType.equals(NarrativeModeEnum.class)) {
|
||||||
|
param = new NarrativeModeParameter();
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < annotations.length && param == null; i++) {
|
for (int i = 0; i < annotations.length && param == null; i++) {
|
||||||
Annotation nextAnnotation = annotations[i];
|
Annotation nextAnnotation = annotations[i];
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package ca.uhn.fhir.rest.method;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* #%L
|
||||||
|
* HAPI FHIR - Core Library
|
||||||
|
* %%
|
||||||
|
* Copyright (C) 2014 - 2015 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.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.IBaseResource;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
|
class NarrativeModeParameter implements IParameter {
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletResponseParameter.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void translateClientArgumentIntoQueryArgument(FhirContext theContext, Object theSourceClientArgument, Map<String, List<String>> theTargetQueryArguments, IBaseResource theTargetResource)
|
||||||
|
throws InternalErrorException {
|
||||||
|
if (theSourceClientArgument != null) {
|
||||||
|
NarrativeModeEnum n = (NarrativeModeEnum) theSourceClientArgument;
|
||||||
|
theTargetQueryArguments.put(Constants.PARAM_NARRATIVE, Collections.singletonList(n.name().toLowerCase()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object translateQueryParametersIntoServerArgument(Request theRequest, Object theRequestContents) throws InternalErrorException, InvalidRequestException {
|
||||||
|
String val = theRequest.getServletRequest().getParameter(Constants.PARAM_NARRATIVE);
|
||||||
|
if (val != null) {
|
||||||
|
try {
|
||||||
|
return NarrativeModeEnum.valueOfCaseInsensitive(val);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
ourLog.debug("Invalid {} parameger: {}", Constants.PARAM_NARRATIVE, val);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -70,8 +70,8 @@ public class RestfulServerUtils {
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip,
|
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
||||||
String theServerBase) throws IOException {
|
boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip, String theServerBase) throws IOException {
|
||||||
theHttpResponse.setStatus(stausCode);
|
theHttpResponse.setStatus(stausCode);
|
||||||
|
|
||||||
if (theResource.getId() != null && theResource.getId().hasIdPart() && isNotBlank(theServerBase)) {
|
if (theResource.getId() != null && theResource.getId().hasIdPart() && isNotBlank(theServerBase)) {
|
||||||
|
@ -267,13 +267,17 @@ public class RestfulServerUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static RestfulServer.NarrativeModeEnum determineNarrativeMode(RequestDetails theRequest) {
|
public static RestfulServer.NarrativeModeEnum determineNarrativeMode(RequestDetails theRequest) {
|
||||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||||
String[] narrative = requestParams.remove(Constants.PARAM_NARRATIVE);
|
String[] narrative = requestParams.remove(Constants.PARAM_NARRATIVE);
|
||||||
RestfulServer.NarrativeModeEnum narrativeMode = null;
|
RestfulServer.NarrativeModeEnum narrativeMode = null;
|
||||||
if (narrative != null && narrative.length > 0) {
|
if (narrative != null && narrative.length > 0) {
|
||||||
narrativeMode = RestfulServer.NarrativeModeEnum.valueOfCaseInsensitive(narrative[0]);
|
try {
|
||||||
|
narrativeMode = RestfulServer.NarrativeModeEnum.valueOfCaseInsensitive(narrative[0]);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
ourLog.debug("Invalid {} parameger: {}", Constants.PARAM_NARRATIVE, narrative[0]);
|
||||||
|
narrativeMode = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (narrativeMode == null) {
|
if (narrativeMode == null) {
|
||||||
narrativeMode = RestfulServer.NarrativeModeEnum.NORMAL;
|
narrativeMode = RestfulServer.NarrativeModeEnum.NORMAL;
|
||||||
|
@ -282,13 +286,12 @@ public class RestfulServerUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine whether a response should be given in JSON or XML format based on the incoming HttpServletRequest's
|
* Determine whether a response should be given in JSON or XML format based on the incoming HttpServletRequest's <code>"_format"</code> parameter and <code>"Accept:"</code> HTTP header.
|
||||||
* <code>"_format"</code> parameter and <code>"Accept:"</code> HTTP header.
|
|
||||||
*/
|
*/
|
||||||
public static EncodingEnum determineResponseEncodingWithDefault(RestfulServer theServer, HttpServletRequest theReq) {
|
public static EncodingEnum determineResponseEncodingWithDefault(RestfulServer theServer, HttpServletRequest theReq) {
|
||||||
EncodingEnum retVal = determineResponseEncodingNoDefault(theReq);
|
EncodingEnum retVal = determineResponseEncodingNoDefault(theReq);
|
||||||
if (retVal == null) {
|
if (retVal == null) {
|
||||||
retVal =theServer.getDefaultResponseEncoding();
|
retVal = theServer.getDefaultResponseEncoding();
|
||||||
}
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
@ -347,12 +350,13 @@ public class RestfulServerUtils {
|
||||||
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_COUNT);
|
return RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_COUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, Bundle bundle, EncodingEnum theResponseEncoding, String theServerBase, boolean thePrettyPrint, RestfulServer.NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, boolean theRequestIsBrowser) throws IOException {
|
public static void streamResponseAsBundle(RestfulServer theServer, HttpServletResponse theHttpResponse, Bundle bundle, EncodingEnum theResponseEncoding, String theServerBase,
|
||||||
|
boolean thePrettyPrint, RestfulServer.NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, boolean theRequestIsBrowser) throws IOException {
|
||||||
assert !theServerBase.endsWith("/");
|
assert !theServerBase.endsWith("/");
|
||||||
|
|
||||||
theHttpResponse.setStatus(200);
|
theHttpResponse.setStatus(200);
|
||||||
|
|
||||||
EncodingEnum responseEncoding = theResponseEncoding!= null? theResponseEncoding : theServer.getDefaultResponseEncoding();
|
EncodingEnum responseEncoding = theResponseEncoding != null ? theResponseEncoding : theServer.getDefaultResponseEncoding();
|
||||||
|
|
||||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||||
theHttpResponse.setContentType(responseEncoding.getBrowserFriendlyBundleContentType());
|
theHttpResponse.setContentType(responseEncoding.getBrowserFriendlyBundleContentType());
|
||||||
|
@ -383,10 +387,11 @@ public class RestfulServerUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint, boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, String theServerBase)
|
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
||||||
throws IOException {
|
boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, String theServerBase) throws IOException {
|
||||||
int stausCode = 200;
|
int stausCode = 200;
|
||||||
RestfulServerUtils.streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip, theServerBase);
|
RestfulServerUtils.streamResponseAsResource(theServer, theHttpResponse, theResource, theResponseEncoding, thePrettyPrint, theRequestIsBrowser, theNarrativeMode, stausCode, theRespondGzip,
|
||||||
|
theServerBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void validateResourceListNotNull(List<IResource> theResourceList) {
|
public static void validateResourceListNotNull(List<IResource> theResourceList) {
|
||||||
|
@ -395,6 +400,4 @@ public class RestfulServerUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,7 @@ import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
import ca.uhn.fhir.rest.server.Constants;
|
import ca.uhn.fhir.rest.server.Constants;
|
||||||
import ca.uhn.fhir.rest.server.EncodingEnum;
|
import ca.uhn.fhir.rest.server.EncodingEnum;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||||
|
@ -148,7 +149,7 @@ public class ClientTest {
|
||||||
HttpPost post = (HttpPost) capt.getValue();
|
HttpPost post = (HttpPost) capt.getValue();
|
||||||
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
||||||
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
|
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
|
||||||
assertEquals(EncodingEnum.XML.getResourceContentType()+Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(0).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(0).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||||
assertEquals("200", response.getId().getVersionIdPart());
|
assertEquals("200", response.getId().getVersionIdPart());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,8 +174,7 @@ public class ClientTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Some servers (older ones?) return the resourcde you created instead of an OperationOutcome. We just need to
|
* Some servers (older ones?) return the resourcde you created instead of an OperationOutcome. We just need to ignore it.
|
||||||
* ignore it.
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testCreateWithResourceResponse() throws Exception {
|
public void testCreateWithResourceResponse() throws Exception {
|
||||||
|
@ -533,12 +533,12 @@ public class ClientTest {
|
||||||
|
|
||||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T12:01:02"), new IntegerDt(12));
|
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T12:01:02"), new IntegerDt(12));
|
||||||
assertThat(capt.getAllValues().get(0).getURI().toString(), containsString("http://foo/Patient/111/_history?"));
|
assertThat(capt.getAllValues().get(0).getURI().toString(), containsString("http://foo/Patient/111/_history?"));
|
||||||
assertThat(capt.getAllValues().get(0).getURI().toString(), containsString("_since="+expectedDateString.replaceAll("\\..*", "")));
|
assertThat(capt.getAllValues().get(0).getURI().toString(), containsString("_since=" + expectedDateString.replaceAll("\\..*", "")));
|
||||||
assertThat(capt.getAllValues().get(0).getURI().toString(), containsString("_count=12"));
|
assertThat(capt.getAllValues().get(0).getURI().toString(), containsString("_count=12"));
|
||||||
|
|
||||||
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T12:01:02").getValue(), new IntegerDt(12).getValue());
|
client.getHistoryPatientInstance(new IdDt("111"), new InstantDt("2012-01-02T12:01:02").getValue(), new IntegerDt(12).getValue());
|
||||||
assertThat(capt.getAllValues().get(1).getURI().toString(), containsString("http://foo/Patient/111/_history?"));
|
assertThat(capt.getAllValues().get(1).getURI().toString(), containsString("http://foo/Patient/111/_history?"));
|
||||||
assertThat(capt.getAllValues().get(1).getURI().toString(), containsString("_since="+expectedDateString));
|
assertThat(capt.getAllValues().get(1).getURI().toString(), containsString("_since=" + expectedDateString));
|
||||||
assertThat(capt.getAllValues().get(1).getURI().toString(), containsString("_count=12"));
|
assertThat(capt.getAllValues().get(1).getURI().toString(), containsString("_count=12"));
|
||||||
|
|
||||||
client.getHistoryPatientInstance(new IdDt("111"), null, new IntegerDt(12));
|
client.getHistoryPatientInstance(new IdDt("111"), null, new IntegerDt(12));
|
||||||
|
@ -592,7 +592,8 @@ public class ClientTest {
|
||||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
Header[] headers = new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"), new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
|
Header[] headers = new Header[] { new BasicHeader(Constants.HEADER_LAST_MODIFIED, "Wed, 15 Nov 1995 04:58:08 GMT"),
|
||||||
|
new BasicHeader(Constants.HEADER_CONTENT_LOCATION, "http://foo.com/Patient/123/_history/2333"),
|
||||||
new BasicHeader(Constants.HEADER_CATEGORY, "http://foo/tagdefinition.html; scheme=\"http://hl7.org/fhir/tag\"; label=\"Some tag\"") };
|
new BasicHeader(Constants.HEADER_CATEGORY, "http://foo/tagdefinition.html; scheme=\"http://hl7.org/fhir/tag\"; label=\"Some tag\"") };
|
||||||
|
|
||||||
when(httpResponse.getAllHeaders()).thenReturn(headers);
|
when(httpResponse.getAllHeaders()).thenReturn(headers);
|
||||||
|
@ -653,7 +654,6 @@ public class ClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadFailureInternalError() throws Exception {
|
public void testReadFailureInternalError() throws Exception {
|
||||||
|
|
||||||
|
@ -1085,7 +1085,7 @@ public class ClientTest {
|
||||||
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
||||||
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
|
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
|
||||||
assertEquals("200", response.getId().getVersionIdPart());
|
assertEquals("200", response.getId().getVersionIdPart());
|
||||||
assertEquals(EncodingEnum.XML.getResourceContentType()+Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(0).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
assertEquals(EncodingEnum.XML.getResourceContentType() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(0).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1213,6 +1213,36 @@ public class ClientTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNarrativeModeParam() throws Exception {
|
||||||
|
final String msg = getPatientFeedWithOneResult();
|
||||||
|
|
||||||
|
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||||
|
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
|
||||||
|
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||||
|
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
|
||||||
|
when(httpResponse.getEntity().getContent()).thenAnswer(new Answer<InputStream>() {
|
||||||
|
@Override
|
||||||
|
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||||
|
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ITestClientWithNarrativeParam client = ctx.newRestfulClient(ITestClientWithNarrativeParam.class, "http://foo");
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
Patient response = client.getPatients(null);
|
||||||
|
assertEquals("http://foo/Patient", capt.getAllValues().get(idx).getURI().toString());
|
||||||
|
assertNotNull(response);
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
response = client.getPatients(NarrativeModeEnum.ONLY);
|
||||||
|
assertEquals("http://foo/Patient?_narrative=only", capt.getAllValues().get(idx).getURI().toString());
|
||||||
|
assertNotNull(response);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private Header[] toHeaderArray(String theName, String theValue) {
|
private Header[] toHeaderArray(String theName, String theValue) {
|
||||||
return new Header[] { new BasicHeader(theName, theValue) };
|
return new Header[] { new BasicHeader(theName, theValue) };
|
||||||
}
|
}
|
||||||
|
@ -1240,4 +1270,10 @@ public class ClientTest {
|
||||||
@Search()
|
@Search()
|
||||||
public Patient getPatientWithIncludes(@RequiredParam(name = "withIncludes") StringParam theString, @IncludeParam String theInclude);
|
public Patient getPatientWithIncludes(@RequiredParam(name = "withIncludes") StringParam theString, @IncludeParam String theInclude);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface ITestClientWithNarrativeParam extends IBasicClient {
|
||||||
|
@Search()
|
||||||
|
public Patient getPatients(NarrativeModeEnum theNarrativeMode);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,10 @@ import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.servlet.ServletHandler;
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
import org.eclipse.jetty.servlet.ServletHolder;
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.google.common.net.UrlEscapers;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.Bundle;
|
import ca.uhn.fhir.model.api.Bundle;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
@ -41,7 +40,6 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Read;
|
|
||||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.client.IGenericClient;
|
import ca.uhn.fhir.rest.client.IGenericClient;
|
||||||
|
@ -50,8 +48,11 @@ import ca.uhn.fhir.rest.param.StringOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.StringParam;
|
import ca.uhn.fhir.rest.param.StringParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
import ca.uhn.fhir.rest.param.TokenOrListParam;
|
||||||
import ca.uhn.fhir.rest.param.TokenParam;
|
import ca.uhn.fhir.rest.param.TokenParam;
|
||||||
|
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
|
||||||
import ca.uhn.fhir.util.PortUtil;
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
|
||||||
|
import com.google.common.net.UrlEscapers;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by dsotnikov on 2/25/2014.
|
* Created by dsotnikov on 2/25/2014.
|
||||||
*/
|
*/
|
||||||
|
@ -63,6 +64,12 @@ public class SearchTest {
|
||||||
private static int ourPort;
|
private static int ourPort;
|
||||||
|
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
|
private static NarrativeModeEnum ourLastNarrativeMode;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void before() {
|
||||||
|
ourLastNarrativeMode=null;
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncodeConvertsReferencesToRelative() throws Exception {
|
public void testEncodeConvertsReferencesToRelative() throws Exception {
|
||||||
|
@ -78,6 +85,36 @@ public class SearchTest {
|
||||||
assertEquals("Organization/555", ref);
|
assertEquals("Organization/555", ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNarrativeParamNone() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithNarrative");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals(null, ourLastNarrativeMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNarrativeParamPopulated() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithNarrative&_narrative=ONly");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals(NarrativeModeEnum.ONLY, ourLastNarrativeMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNarrativeParamPopulatedInvalid() throws Exception {
|
||||||
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithNarrative&_narrative=BLAH");
|
||||||
|
HttpResponse status = ourClient.execute(httpGet);
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals(null, ourLastNarrativeMode);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOmitEmptyOptionalParam() throws Exception {
|
public void testOmitEmptyOptionalParam() throws Exception {
|
||||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=");
|
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=");
|
||||||
|
@ -328,6 +365,14 @@ public class SearchTest {
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Search(queryName="searchWithNarrative")
|
||||||
|
public Patient searchWithNarrative(NarrativeModeEnum theNarrativeMode) {
|
||||||
|
ourLastNarrativeMode = theNarrativeMode;
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setId("Patient/1/_history/1");
|
||||||
|
return patient;
|
||||||
|
}
|
||||||
|
|
||||||
@Search(queryName="searchWithRef")
|
@Search(queryName="searchWithRef")
|
||||||
public Patient searchWithRef() {
|
public Patient searchWithRef() {
|
||||||
Patient patient = new Patient();
|
Patient patient = new Patient();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package ca.uhn.fhir.rest.server;
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -10,7 +12,6 @@ import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.http.HttpResponse;
|
import org.apache.http.HttpResponse;
|
||||||
import org.apache.http.client.methods.HttpGet;
|
import org.apache.http.client.methods.HttpGet;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.client.methods.HttpPut;
|
|
||||||
import org.apache.http.entity.ContentType;
|
import org.apache.http.entity.ContentType;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
@ -26,13 +27,6 @@ import org.junit.Test;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.model.api.IResource;
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
|
||||||
import ca.uhn.fhir.model.api.TagList;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.DiagnosticOrder;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.DiagnosticReport;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.OperationOutcome;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Organization;
|
|
||||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
import ca.uhn.fhir.model.primitive.IdDt;
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
import ca.uhn.fhir.model.primitive.StringDt;
|
import ca.uhn.fhir.model.primitive.StringDt;
|
||||||
|
@ -42,7 +36,6 @@ import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Search;
|
import ca.uhn.fhir.rest.annotation.Search;
|
||||||
import ca.uhn.fhir.rest.annotation.Update;
|
|
||||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
import ca.uhn.fhir.util.PortUtil;
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
|
||||||
|
@ -51,10 +44,10 @@ import ca.uhn.fhir.util.PortUtil;
|
||||||
*/
|
*/
|
||||||
public class CreateConditionalTest {
|
public class CreateConditionalTest {
|
||||||
private static CloseableHttpClient ourClient;
|
private static CloseableHttpClient ourClient;
|
||||||
|
|
||||||
private static String ourLastConditionalUrl;
|
private static String ourLastConditionalUrl;
|
||||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateConditionalTest.class);
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateConditionalTest.class);
|
||||||
private static int ourPort;
|
private static int ourPort;
|
||||||
|
|
||||||
private static Server ourServer;
|
private static Server ourServer;
|
||||||
private static IdDt ourLastId;
|
private static IdDt ourLastId;
|
||||||
private static IdDt ourLastIdParam;
|
private static IdDt ourLastIdParam;
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
package ca.uhn.fhir.rest.server;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.client.methods.HttpPost;
|
||||||
|
import org.apache.http.entity.ContentType;
|
||||||
|
import org.apache.http.entity.StringEntity;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHandler;
|
||||||
|
import org.eclipse.jetty.servlet.ServletHolder;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
|
import ca.uhn.fhir.model.api.IResource;
|
||||||
|
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||||
|
import ca.uhn.fhir.model.primitive.IdDt;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Create;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||||
|
import ca.uhn.fhir.rest.annotation.Update;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.util.PortUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by dsotnikov on 2/25/2014.
|
||||||
|
*/
|
||||||
|
public class PreferTest {
|
||||||
|
private static CloseableHttpClient ourClient;
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PreferTest.class);
|
||||||
|
private static int ourPort;
|
||||||
|
private static Server ourServer;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateWithNoPrefer() throws Exception {
|
||||||
|
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setValue("002");
|
||||||
|
|
||||||
|
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient");
|
||||||
|
httpPost.setEntity(new StringEntity(new FhirContext().newXmlParser().encodeResourceToString(patient), ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
|
||||||
|
|
||||||
|
HttpResponse status = ourClient.execute(httpPost);
|
||||||
|
|
||||||
|
String responseContent = IOUtils.toString(status.getEntity().getContent());
|
||||||
|
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||||
|
|
||||||
|
ourLog.info("Response was:\n{}", responseContent);
|
||||||
|
|
||||||
|
assertEquals(201, status.getStatusLine().getStatusCode());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("location").getValue());
|
||||||
|
assertEquals("http://localhost:" + ourPort + "/Patient/001/_history/002", status.getFirstHeader("content-location").getValue());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void afterClass() throws Exception {
|
||||||
|
ourServer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void beforeClass() throws Exception {
|
||||||
|
ourPort = PortUtil.findFreePort();
|
||||||
|
ourServer = new Server(ourPort);
|
||||||
|
|
||||||
|
PatientProvider patientProvider = new PatientProvider();
|
||||||
|
|
||||||
|
ServletHandler proxyHandler = new ServletHandler();
|
||||||
|
RestfulServer servlet = new RestfulServer();
|
||||||
|
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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PatientProvider implements IResourceProvider {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IResource> getResourceType() {
|
||||||
|
return Patient.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Create()
|
||||||
|
public MethodOutcome createPatient(@ResourceParam Patient thePatient) {
|
||||||
|
MethodOutcome retVal = new MethodOutcome(new IdDt("Patient/001/_history/002"));
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Update()
|
||||||
|
public MethodOutcome updatePatient(@ResourceParam Patient thePatient, @IdParam IdDt theIdParam) {
|
||||||
|
return new MethodOutcome(new IdDt("Patient/001/_history/002"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -76,6 +76,16 @@
|
||||||
Remove Eclipse and IntelliJ artifacts (.project, *.iml, etc) from version control. Thanks
|
Remove Eclipse and IntelliJ artifacts (.project, *.iml, etc) from version control. Thanks
|
||||||
to Doug Martin for the suggestion!
|
to Doug Martin for the suggestion!
|
||||||
</action>
|
</action>
|
||||||
|
<action type="add">
|
||||||
|
REST server methods may now have a parameter of
|
||||||
|
type NarrativeModeEnum which will be populated with
|
||||||
|
the value of the _narrative URL parameter
|
||||||
|
if one was supplied. Annotation client methods
|
||||||
|
may also include a parameter of this type, and it
|
||||||
|
will be used to populate this parameter on the request
|
||||||
|
URL if it is not null. Thanks to Neal Acharya for the
|
||||||
|
idea!
|
||||||
|
</action>
|
||||||
</release>
|
</release>
|
||||||
<release version="0.9" date="2015-Mar-14">
|
<release version="0.9" date="2015-Mar-14">
|
||||||
<action type="add">
|
<action type="add">
|
||||||
|
|
|
@ -1006,7 +1006,7 @@
|
||||||
<a href="./apidocs/ca/uhn/fhir/model/api/annotation/Description.html">@Description</a>
|
<a href="./apidocs/ca/uhn/fhir/model/api/annotation/Description.html">@Description</a>
|
||||||
annotation. This annotation allows you to add a description of the method
|
annotation. This annotation allows you to add a description of the method
|
||||||
and the individual parameters. These descriptions will be placed in the
|
and the individual parameters. These descriptions will be placed in the
|
||||||
server's metadata statement, which can be helpful to anyone who is developing
|
server's conformance statement, which can be helpful to anyone who is developing
|
||||||
software against your server.
|
software against your server.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
@ -350,6 +350,19 @@
|
||||||
|
|
||||||
</subsection>
|
</subsection>
|
||||||
|
|
||||||
|
<subsection name="Accessing the _narrative parameter value">
|
||||||
|
|
||||||
|
<p>
|
||||||
|
There are different ways of
|
||||||
|
<a href="./doc_narrative.html">generating narratives</a> for use on your server. HAPI's Server
|
||||||
|
also provides a non-standard parameter called <code>_narrative</code> which can be used to
|
||||||
|
control narrative behavour. If you add a parameter to any server (or annotation client) method
|
||||||
|
with a type of <code>NarrativeModeEnum</code>, the value will be populated with the value
|
||||||
|
of this URL parameter.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
</subsection>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
|
|
Loading…
Reference in New Issue