Add support for a NarrativeModeEnum parameter to interact with

_narrative in the request URL
This commit is contained in:
James Agnew 2015-03-27 16:52:48 -04:00
parent c484425c78
commit 690bcb4b68
11 changed files with 358 additions and 68 deletions

View File

@ -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>

View File

@ -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];

View File

@ -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
}
}

View File

@ -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) {
try {
narrativeMode = RestfulServer.NarrativeModeEnum.valueOfCaseInsensitive(narrative[0]); 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,8 +286,7 @@ 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);
@ -347,7 +350,8 @@ 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);
@ -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 {
} }
} }
} }

View File

@ -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;
@ -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 {
@ -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 {
@ -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);
}
} }

View File

@ -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();

View File

@ -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;

View File

@ -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"));
}
}
}

View File

@ -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">

View File

@ -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>

View File

@ -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>
<!-- <!--