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>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>${maven_assembly_plugin_version}</version>
|
||||
<version>2.5.3</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
|
@ -104,8 +104,8 @@
|
|||
<configuration>
|
||||
<attach>true</attach>
|
||||
<descriptors>
|
||||
<descriptor>${project.basedir}/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-sources.xml</descriptor>
|
||||
<descriptor>/Users/t3903uhn/git/hapi-fhir/hapi-fhir-android/src/assembly/android-javadoc.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
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.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.EncodingEnum;
|
||||
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.util.ReflectionUtil;
|
||||
|
||||
|
@ -342,6 +344,8 @@ public class MethodUtil {
|
|||
param = new ServletRequestParameter();
|
||||
} else if (parameterType.equals(HttpServletResponse.class) || parameterType.equals(ServletResponse.class)) {
|
||||
param = new ServletResponseParameter();
|
||||
} else if (parameterType.equals(NarrativeModeEnum.class)) {
|
||||
param = new NarrativeModeParameter();
|
||||
} else {
|
||||
for (int i = 0; i < annotations.length && param == null; 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
|
||||
}
|
||||
|
||||
}
|
|
@ -56,7 +56,7 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
|||
|
||||
public class RestfulServerUtils {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(RestfulServerUtils.class);
|
||||
|
||||
|
||||
static Integer tryToExtractNamedParameter(HttpServletRequest theRequest, String name) {
|
||||
String countString = theRequest.getParameter(name);
|
||||
Integer count = null;
|
||||
|
@ -70,29 +70,29 @@ public class RestfulServerUtils {
|
|||
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,
|
||||
String theServerBase) throws IOException {
|
||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
||||
boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, int stausCode, boolean theRespondGzip, String theServerBase) throws IOException {
|
||||
theHttpResponse.setStatus(stausCode);
|
||||
|
||||
|
||||
if (theResource.getId() != null && theResource.getId().hasIdPart() && isNotBlank(theServerBase)) {
|
||||
String resName = theServer.getFhirContext().getResourceDefinition(theResource).getName();
|
||||
IdDt fullId = theResource.getId().withServerBase(theServerBase, resName);
|
||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_LOCATION, fullId.getValue());
|
||||
}
|
||||
|
||||
|
||||
if (theServer.getETagSupport() == ETagSupportEnum.ENABLED) {
|
||||
if (theResource.getId().hasVersionIdPart()) {
|
||||
theHttpResponse.addHeader(Constants.HEADER_ETAG, "W/\"" + theResource.getId().getVersionIdPart() + '"');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (theServer.getAddProfileTag() != AddProfileTagEnum.NEVER) {
|
||||
RuntimeResourceDefinition def = theServer.getFhirContext().getResourceDefinition(theResource);
|
||||
if (theServer.getAddProfileTag() == AddProfileTagEnum.ALWAYS || !def.isStandardProfile()) {
|
||||
addProfileToBundleEntry(theServer.getFhirContext(), theResource, theServerBase);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (theResource instanceof IBaseBinary && theResponseEncoding == null) {
|
||||
IBaseBinary bin = (IBaseBinary) theResource;
|
||||
if (isNotBlank(bin.getContentType())) {
|
||||
|
@ -103,20 +103,20 @@ public class RestfulServerUtils {
|
|||
if (bin.getContent() == null || bin.getContent().length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Force binary resources to download - This is a security measure to prevent
|
||||
// malicious images or HTML blocks being served up as content.
|
||||
theHttpResponse.addHeader(Constants.HEADER_CONTENT_DISPOSITION, "Attachment;");
|
||||
|
||||
|
||||
theHttpResponse.setContentLength(bin.getContent().length);
|
||||
ServletOutputStream oos = theHttpResponse.getOutputStream();
|
||||
oos.write(bin.getContent());
|
||||
oos.close();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
EncodingEnum responseEncoding = theResponseEncoding != null ? theResponseEncoding : theServer.getDefaultResponseEncoding();
|
||||
|
||||
|
||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||
theHttpResponse.setContentType(responseEncoding.getBrowserFriendlyBundleContentType());
|
||||
} else if (theNarrativeMode == RestfulServer.NarrativeModeEnum.ONLY) {
|
||||
|
@ -125,14 +125,14 @@ public class RestfulServerUtils {
|
|||
theHttpResponse.setContentType(responseEncoding.getResourceContentType());
|
||||
}
|
||||
theHttpResponse.setCharacterEncoding(Constants.CHARSETNAME_UTF_8);
|
||||
|
||||
|
||||
theServer.addHeadersToResponse(theHttpResponse);
|
||||
|
||||
|
||||
InstantDt lastUpdated = ResourceMetadataKeyEnum.UPDATED.get(theResource);
|
||||
if (lastUpdated != null && lastUpdated.isEmpty() == false) {
|
||||
theHttpResponse.addHeader(Constants.HEADER_LAST_MODIFIED, DateUtils.formatDate(lastUpdated.getValue()));
|
||||
}
|
||||
|
||||
|
||||
TagList list = (TagList) theResource.getResourceMetadata().get(ResourceMetadataKeyEnum.TAG_LIST);
|
||||
if (list != null) {
|
||||
for (Tag tag : list) {
|
||||
|
@ -141,7 +141,7 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Writer writer = getWriter(theHttpResponse, theRespondGzip);
|
||||
try {
|
||||
if (theNarrativeMode == RestfulServer.NarrativeModeEnum.ONLY) {
|
||||
|
@ -253,13 +253,13 @@ public class RestfulServerUtils {
|
|||
}
|
||||
|
||||
public static void addProfileToBundleEntry(FhirContext theContext, IResource theResource, String theServerBase) {
|
||||
|
||||
|
||||
TagList tl = ResourceMetadataKeyEnum.TAG_LIST.get(theResource);
|
||||
if (tl == null) {
|
||||
tl = new TagList();
|
||||
ResourceMetadataKeyEnum.TAG_LIST.put(theResource, tl);
|
||||
}
|
||||
|
||||
|
||||
RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(theResource);
|
||||
String profile = nextDef.getResourceProfile(theServerBase);
|
||||
if (isNotBlank(profile)) {
|
||||
|
@ -267,13 +267,17 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
public static RestfulServer.NarrativeModeEnum determineNarrativeMode(RequestDetails theRequest) {
|
||||
Map<String, String[]> requestParams = theRequest.getParameters();
|
||||
String[] narrative = requestParams.remove(Constants.PARAM_NARRATIVE);
|
||||
RestfulServer.NarrativeModeEnum narrativeMode = null;
|
||||
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) {
|
||||
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
|
||||
* <code>"_format"</code> parameter and <code>"Accept:"</code> HTTP header.
|
||||
* 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.
|
||||
*/
|
||||
public static EncodingEnum determineResponseEncodingWithDefault(RestfulServer theServer, HttpServletRequest theReq) {
|
||||
EncodingEnum retVal = determineResponseEncodingNoDefault(theReq);
|
||||
if (retVal == null) {
|
||||
retVal =theServer.getDefaultResponseEncoding();
|
||||
retVal = theServer.getDefaultResponseEncoding();
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
@ -317,7 +320,7 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Enumeration<String> acceptValues = theReq.getHeaders(Constants.HEADER_ACCEPT);
|
||||
if (acceptValues != null) {
|
||||
while (acceptValues.hasMoreElements()) {
|
||||
|
@ -347,13 +350,14 @@ public class RestfulServerUtils {
|
|||
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("/");
|
||||
|
||||
|
||||
theHttpResponse.setStatus(200);
|
||||
|
||||
EncodingEnum responseEncoding = theResponseEncoding!= null? theResponseEncoding : theServer.getDefaultResponseEncoding();
|
||||
|
||||
|
||||
EncodingEnum responseEncoding = theResponseEncoding != null ? theResponseEncoding : theServer.getDefaultResponseEncoding();
|
||||
|
||||
if (theRequestIsBrowser && theServer.isUseBrowserFriendlyContentTypes()) {
|
||||
theHttpResponse.setContentType(responseEncoding.getBrowserFriendlyBundleContentType());
|
||||
} else if (theNarrativeMode == RestfulServer.NarrativeModeEnum.ONLY) {
|
||||
|
@ -361,11 +365,11 @@ public class RestfulServerUtils {
|
|||
} else {
|
||||
theHttpResponse.setContentType(responseEncoding.getBundleContentType());
|
||||
}
|
||||
|
||||
|
||||
theHttpResponse.setCharacterEncoding(Constants.CHARSETNAME_UTF_8);
|
||||
|
||||
|
||||
theServer.addHeadersToResponse(theHttpResponse);
|
||||
|
||||
|
||||
Writer writer = RestfulServerUtils.getWriter(theHttpResponse, theRespondGzip);
|
||||
try {
|
||||
if (theNarrativeMode == RestfulServer.NarrativeModeEnum.ONLY) {
|
||||
|
@ -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)
|
||||
throws IOException {
|
||||
public static void streamResponseAsResource(RestfulServer theServer, HttpServletResponse theHttpResponse, IResource theResource, EncodingEnum theResponseEncoding, boolean thePrettyPrint,
|
||||
boolean theRequestIsBrowser, RestfulServer.NarrativeModeEnum theNarrativeMode, boolean theRespondGzip, String theServerBase) throws IOException {
|
||||
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) {
|
||||
|
@ -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.server.Constants;
|
||||
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.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
|
@ -148,7 +149,7 @@ public class ClientTest {
|
|||
HttpPost post = (HttpPost) capt.getValue();
|
||||
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -173,8 +174,7 @@ public class ClientTest {
|
|||
}
|
||||
|
||||
/**
|
||||
* Some servers (older ones?) return the resourcde you created instead of an OperationOutcome. We just need to
|
||||
* ignore it.
|
||||
* Some servers (older ones?) return the resourcde you created instead of an OperationOutcome. We just need to ignore it.
|
||||
*/
|
||||
@Test
|
||||
public void testCreateWithResourceResponse() throws Exception {
|
||||
|
@ -509,7 +509,7 @@ public class ClientTest {
|
|||
|
||||
@Test
|
||||
public void testHistoryWithParams() throws Exception {
|
||||
|
||||
|
||||
//@formatter:off
|
||||
final String msg = "<feed xmlns=\"http://www.w3.org/2005/Atom\"><title/><id>6c1d93be-027f-468d-9d47-f826cd15cf42</id><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history\"/><link rel=\"fhir-base\" href=\"http://localhost:51698\"/><os:totalResults xmlns:os=\"http://a9.com/-/spec/opensearch/1.1/\">2</os:totalResults><published>2014-04-13T18:24:50-04:00</published><author><name>ca.uhn.fhir.rest.method.HistoryMethodBinding</name></author><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:20.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/1\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"OlderFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry><entry><title>Patient 222</title><id>222</id><updated>1969-12-31T19:00:30.000-05:00</updated><published>1969-12-31T19:00:10.000-05:00</published><link rel=\"self\" href=\"http://localhost:51698/Patient/222/_history/2\"/><content type=\"text/xml\"><Patient xmlns=\"http://hl7.org/fhir\"><identifier><use value=\"official\"/><system value=\"urn:hapitest:mrns\"/><value value=\"00001\"/></identifier><name><family value=\"NewerFamily\"/><given value=\"PatientOne\"/></name><gender><text value=\"M\"/></gender></Patient></content></entry></feed>";
|
||||
//@formatter:on
|
||||
|
@ -528,17 +528,17 @@ public class ClientTest {
|
|||
ITestClient client = ctx.newRestfulClient(ITestClient.class, "http://foo");
|
||||
|
||||
// ensures the local timezone
|
||||
String expectedDateString = new InstantDt(new InstantDt("2012-01-02T12:01:02").getValue()).getValueAsString();
|
||||
String expectedDateString = new InstantDt(new InstantDt("2012-01-02T12:01:02").getValue()).getValueAsString();
|
||||
expectedDateString = expectedDateString.replace(":", "%3A").replace("+", "%2B");
|
||||
|
||||
|
||||
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("_since="+expectedDateString.replaceAll("\\..*", "")));
|
||||
assertThat(capt.getAllValues().get(0).getURI().toString(), containsString("_since=" + expectedDateString.replaceAll("\\..*", "")));
|
||||
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());
|
||||
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"));
|
||||
|
||||
client.getHistoryPatientInstance(new IdDt("111"), null, new IntegerDt(12));
|
||||
|
@ -562,7 +562,7 @@ public class ClientTest {
|
|||
// error message to tell the user why the method isn't working
|
||||
FhirContext ctx = new FhirContext();
|
||||
ctx.getRestfulClientFactory().setServerValidationModeEnum(ServerValidationModeEnum.NEVER);
|
||||
|
||||
|
||||
ClientWithoutAnnotation client = ctx.newRestfulClient(ClientWithoutAnnotation.class, "http://wildfhir.aegis.net/fhir");
|
||||
|
||||
try {
|
||||
|
@ -592,7 +592,8 @@ public class ClientTest {
|
|||
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"));
|
||||
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\"") };
|
||||
|
||||
when(httpResponse.getAllHeaders()).thenReturn(headers);
|
||||
|
@ -653,7 +654,6 @@ public class ClientTest {
|
|||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testReadFailureInternalError() throws Exception {
|
||||
|
||||
|
@ -1085,7 +1085,7 @@ public class ClientTest {
|
|||
assertThat(IOUtils.toString(post.getEntity().getContent()), StringContains.containsString("<Patient"));
|
||||
assertEquals("http://example.com/fhir/Patient/100/_history/200", response.getId().getValue());
|
||||
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) {
|
||||
return new Header[] { new BasicHeader(theName, theValue) };
|
||||
}
|
||||
|
@ -1240,4 +1270,10 @@ public class ClientTest {
|
|||
@Search()
|
||||
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.ServletHolder;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.net.UrlEscapers;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
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.rest.annotation.IdParam;
|
||||
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.Search;
|
||||
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.TokenOrListParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer.NarrativeModeEnum;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
|
||||
import com.google.common.net.UrlEscapers;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
*/
|
||||
|
@ -63,7 +64,13 @@ public class SearchTest {
|
|||
private static int ourPort;
|
||||
|
||||
private static Server ourServer;
|
||||
private static NarrativeModeEnum ourLastNarrativeMode;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourLastNarrativeMode=null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeConvertsReferencesToRelative() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_query=searchWithRef");
|
||||
|
@ -78,6 +85,36 @@ public class SearchTest {
|
|||
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
|
||||
public void testOmitEmptyOptionalParam() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=");
|
||||
|
@ -327,7 +364,15 @@ public class SearchTest {
|
|||
retVal.add(patient);
|
||||
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")
|
||||
public Patient searchWithRef() {
|
||||
Patient patient = new Patient();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
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.List;
|
||||
|
@ -10,7 +12,6 @@ import org.apache.commons.io.IOUtils;
|
|||
import org.apache.http.HttpResponse;
|
||||
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.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
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.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.primitive.IdDt;
|
||||
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.ResourceParam;
|
||||
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.util.PortUtil;
|
||||
|
||||
|
@ -51,10 +44,10 @@ import ca.uhn.fhir.util.PortUtil;
|
|||
*/
|
||||
public class CreateConditionalTest {
|
||||
private static CloseableHttpClient ourClient;
|
||||
|
||||
private static String ourLastConditionalUrl;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(CreateConditionalTest.class);
|
||||
private static int ourPort;
|
||||
|
||||
private static Server ourServer;
|
||||
private static IdDt ourLastId;
|
||||
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
|
||||
to Doug Martin for the suggestion!
|
||||
</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 version="0.9" date="2015-Mar-14">
|
||||
<action type="add">
|
||||
|
|
|
@ -1006,7 +1006,7 @@
|
|||
<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
|
||||
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.
|
||||
</p>
|
||||
|
||||
|
|
|
@ -350,6 +350,19 @@
|
|||
|
||||
</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>
|
||||
|
||||
<!--
|
||||
|
|
Loading…
Reference in New Issue