Add new interceptor method which enabled interceptors to modify response
This commit is contained in:
parent
0677f35847
commit
f976b7bf7e
|
@ -0,0 +1,121 @@
|
|||
package example;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import ca.uhn.fhir.util.ResourceReferenceInfo;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.Resource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class Copier {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(Copier.class);
|
||||
|
||||
public static void main(String[] args) {
|
||||
FhirContext ctx = FhirContext.forDstu3();
|
||||
IGenericClient source = ctx.newRestfulGenericClient("http://localhost:8080/baseDstu3");
|
||||
IGenericClient target = ctx.newRestfulGenericClient("https://try.smilecdr.com:8000");
|
||||
|
||||
List<String> resType = Arrays.asList(
|
||||
"Patient", "Organization", "Encounter", "Procedure",
|
||||
"Observation", "ResearchSubject", "Specimen",
|
||||
"ResearchStudy", "Location", "Practitioner"
|
||||
);
|
||||
|
||||
List<IBaseResource> queued = new ArrayList<>();
|
||||
Set<String> sent = new HashSet<>();
|
||||
for (String next : resType) {
|
||||
copy(ctx, source, target, next, queued, sent);
|
||||
}
|
||||
|
||||
while (queued.size() > 0) {
|
||||
ourLog.info("Have {} queued resources to deliver", queued.size());
|
||||
|
||||
for (IBaseResource nextQueued : new ArrayList<>(queued)) {
|
||||
|
||||
String missingRef = null;
|
||||
for (ResourceReferenceInfo nextRefInfo : ctx.newTerser().getAllResourceReferences(nextQueued)) {
|
||||
String nextRef = nextRefInfo.getResourceReference().getReferenceElement().getValue();
|
||||
if (isNotBlank(nextRef) && !sent.contains(nextRef)) {
|
||||
missingRef = nextRef;
|
||||
}
|
||||
}
|
||||
if (missingRef != null) {
|
||||
ourLog.info("Can't send {} because of missing ref {}", nextQueued.getIdElement().getIdPart(), missingRef);
|
||||
continue;
|
||||
}
|
||||
|
||||
IIdType newId = target
|
||||
.update()
|
||||
.resource(nextQueued)
|
||||
.execute()
|
||||
.getId();
|
||||
|
||||
ourLog.info("Copied resource {} and got ID {}", nextQueued.getIdElement().getValue(), newId);
|
||||
sent.add(nextQueued.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
queued.remove(nextQueued);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static void copy(FhirContext theCtx, IGenericClient theSource, IGenericClient theTarget, String theResType, List<IBaseResource> theQueued, Set<String> theSent) {
|
||||
Bundle received = theSource
|
||||
.search()
|
||||
.forResource(theResType)
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
copy(theCtx, theTarget, theResType, theQueued, theSent, received);
|
||||
|
||||
while (received.getLink("next") != null) {
|
||||
ourLog.info("Fetching next page...");
|
||||
received = theSource.loadPage().next(received).execute();
|
||||
copy(theCtx, theTarget, theResType, theQueued, theSent, received);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void copy(FhirContext theCtx, IGenericClient theTarget, String theResType, List<IBaseResource> theQueued, Set<String> theSent, Bundle theReceived) {
|
||||
for (Bundle.BundleEntryComponent nextEntry : theReceived.getEntry()) {
|
||||
Resource nextResource = nextEntry.getResource();
|
||||
nextResource.setId(theResType + "/" + "CR-" + nextResource.getIdElement().getIdPart());
|
||||
|
||||
boolean haveUnsentReference = false;
|
||||
for (ResourceReferenceInfo nextRefInfo : theCtx.newTerser().getAllResourceReferences(nextResource)) {
|
||||
IIdType nextRef = nextRefInfo.getResourceReference().getReferenceElement();
|
||||
if (nextRef.hasIdPart()) {
|
||||
String newRef = nextRef.getResourceType() + "/" + "CR-" + nextRef.getIdPart();
|
||||
ourLog.info("Changing reference {} to {}", nextRef.getValue(), newRef);
|
||||
nextRefInfo.getResourceReference().setReference(newRef);
|
||||
if (!theSent.contains(newRef)) {
|
||||
haveUnsentReference = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (haveUnsentReference) {
|
||||
ourLog.info("Queueing {} for delivery after", nextResource.getId());
|
||||
theQueued.add(nextResource);
|
||||
continue;
|
||||
}
|
||||
|
||||
IIdType newId = theTarget
|
||||
.update()
|
||||
.resource(nextResource)
|
||||
.execute()
|
||||
.getId();
|
||||
|
||||
ourLog.info("Copied resource {} and got ID {}", nextResource.getId(), newId);
|
||||
theSent.add(nextResource.getIdElement().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -82,6 +82,21 @@ public enum FhirVersionEnum {
|
|||
return ordinal() >= theVersion.ordinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link FhirVersionEnum} which corresponds to a specific version of
|
||||
* FHIR. Partial version strings (e.g. "3.0") are acceptable.
|
||||
*
|
||||
* @return Returns null if no version exists matching the given string
|
||||
*/
|
||||
public static FhirVersionEnum forVersionString(String theVersionString) {
|
||||
for (FhirVersionEnum next : values()) {
|
||||
if (next.getFhirVersionString().startsWith(theVersionString)) {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isEquivalentTo(FhirVersionEnum theVersion) {
|
||||
if (this.equals(theVersion)) {
|
||||
return true;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
|
@ -11,6 +12,108 @@
|
|||
<artifactId>hapi-fhir-converter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-base</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Server -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-server</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu2.1</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu3</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-r4</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Testing -->
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-client</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.xmlunit</groupId>
|
||||
<artifactId>xmlunit-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlets</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-servlet</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-server</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-util</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-webapp</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
<artifactId>jetty-http</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<name>HAPI FHIR - Converter</name>
|
||||
|
||||
<build>
|
||||
|
@ -21,48 +124,4 @@
|
|||
</resource>
|
||||
</resources>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-base</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu2.1</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-dstu3</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-structures-r4</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
|
||||
<version>3.3.0-SNAPSHOT</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
package ca.uhn.hapi.converters.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.InterceptorAdapter;
|
||||
import org.hl7.fhir.convertors.VersionConvertor_30_40;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
public class VersionedApiConverterInterceptor extends InterceptorAdapter {
|
||||
private VersionConvertor_30_40 myVersionConvertor_30_40 = new VersionConvertor_30_40();
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
String accept = defaultString(theServletRequest.getHeader(Constants.HEADER_ACCEPT));
|
||||
StringTokenizer tok = new StringTokenizer(accept, ";");
|
||||
String wantVersionString = null;
|
||||
while (tok.hasMoreTokens()) {
|
||||
String next = tok.nextToken().trim();
|
||||
if (next.startsWith("fhir-version=")) {
|
||||
wantVersionString = next.substring("fhir-version=".length()).trim();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FhirVersionEnum wantVersion = null;
|
||||
if (isNotBlank(wantVersionString)) {
|
||||
wantVersion = FhirVersionEnum.forVersionString(wantVersionString);
|
||||
}
|
||||
FhirVersionEnum haveVersion = theResponseObject.getStructureFhirVersionEnum();
|
||||
|
||||
IBaseResource converted = null;
|
||||
try {
|
||||
if (wantVersion == FhirVersionEnum.R4 && haveVersion == FhirVersionEnum.DSTU3) {
|
||||
converted = myVersionConvertor_30_40.convertResource((org.hl7.fhir.dstu3.model.Resource) theResponseObject);
|
||||
} else if (wantVersion == FhirVersionEnum.DSTU3 && haveVersion == FhirVersionEnum.R4) {
|
||||
converted = myVersionConvertor_30_40.convertResource((org.hl7.fhir.r4.model.Resource) theResponseObject);
|
||||
} else if (wantVersion == FhirVersionEnum.DSTU3 && haveVersion == FhirVersionEnum.R4) {
|
||||
converted = myVersionConvertor_30_40.convertResource((org.hl7.fhir.r4.model.Resource) theResponseObject);
|
||||
}
|
||||
} catch (FHIRException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
|
||||
if (converted != null) {
|
||||
Set<SummaryEnum> objects = Collections.emptySet();
|
||||
try {
|
||||
RestfulServerUtils.streamResponseAsResource(theRequestDetails.getServer(), converted, objects, 200, "OK", false, false, theRequestDetails, null, null);
|
||||
return false;
|
||||
} catch (IOException e) {
|
||||
throw new InternalErrorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,276 @@
|
|||
package ca.uhn.hapi.converters.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.SearchStyleEnum;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.gclient.StringClientParam;
|
||||
import ca.uhn.fhir.rest.param.TokenAndListParam;
|
||||
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.util.PortUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hl7.fhir.dstu3.model.Bundle;
|
||||
import org.hl7.fhir.dstu3.model.HumanName;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class SearchDstu3Test {
|
||||
|
||||
private static CloseableHttpClient ourClient;
|
||||
private static FhirContext ourCtx = FhirContext.forDstu3();
|
||||
private static TokenAndListParam ourIdentifiers;
|
||||
private static String ourLastMethod;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SearchDstu3Test.class);
|
||||
private static int ourPort;
|
||||
|
||||
private static Server ourServer;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
ourLastMethod = null;
|
||||
ourIdentifiers = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchNormal() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
try {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
|
||||
assertEquals("search", ourLastMethod);
|
||||
|
||||
assertEquals("foo", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getSystem());
|
||||
assertEquals("bar", ourIdentifiers.getValuesAsQueryTokens().get(0).getValuesAsQueryTokens().get(0).getValue());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithInvalidChain() throws Exception {
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier.chain=foo%7Cbar");
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
try {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(400, status.getStatusLine().getStatusCode());
|
||||
|
||||
OperationOutcome oo = (OperationOutcome) ourCtx.newJsonParser().parseResource(responseContent);
|
||||
assertEquals(
|
||||
"Invalid search parameter \"identifier.chain\". Parameter contains a chain (.chain) and chains are not supported for this parameter (chaining is only allowed on reference parameters)",
|
||||
oo.getIssueFirstRep().getDiagnostics());
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testPagingPreservesEncodingJson() throws Exception {
|
||||
HttpGet httpGet;
|
||||
String linkNext;
|
||||
Bundle bundle;
|
||||
|
||||
// Initial search
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=json");
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=json"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPagingPreservesEncodingApplicationJsonFhir() throws Exception {
|
||||
HttpGet httpGet;
|
||||
String linkNext;
|
||||
Bundle bundle;
|
||||
|
||||
// Initial search
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?identifier=foo%7Cbar&_format=" + Constants.CT_FHIR_JSON_NEW);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW)));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW)));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW)));
|
||||
|
||||
// Fetch the next page
|
||||
httpGet = new HttpGet(linkNext);
|
||||
bundle = executeAndReturnLinkNext(httpGet, EncodingEnum.JSON);
|
||||
linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertThat(linkNext, containsString("_format=" + UrlUtil.escapeUrlParam(Constants.CT_FHIR_JSON_NEW)));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private Bundle executeAndReturnLinkNext(HttpGet httpGet, EncodingEnum theExpectEncoding) throws IOException {
|
||||
CloseableHttpResponse status = ourClient.execute(httpGet);
|
||||
Bundle bundle;
|
||||
try {
|
||||
String responseContent = IOUtils.toString(status.getEntity().getContent(), StandardCharsets.UTF_8);
|
||||
ourLog.info(responseContent);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
EncodingEnum ct = EncodingEnum.forContentType(status.getEntity().getContentType().getValue().replaceAll(";.*", "").trim());
|
||||
assertEquals(theExpectEncoding, ct);
|
||||
bundle = ct.newParser(ourCtx).parseResource(Bundle.class, responseContent);
|
||||
assertEquals(10, bundle.getEntry().size());
|
||||
String linkNext = bundle.getLink(Constants.LINK_NEXT).getUrl();
|
||||
assertNotNull(linkNext);
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchWithPostAndInvalidParameters() {
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:" + ourPort);
|
||||
LoggingInterceptor interceptor = new LoggingInterceptor();
|
||||
interceptor.setLogRequestSummary(true);
|
||||
interceptor.setLogRequestBody(true);
|
||||
interceptor.setLogRequestHeaders(false);
|
||||
interceptor.setLogResponseBody(false);
|
||||
interceptor.setLogResponseHeaders(false);
|
||||
interceptor.setLogResponseSummary(false);
|
||||
client.registerInterceptor(interceptor);
|
||||
try {
|
||||
client
|
||||
.search()
|
||||
.forResource(Patient.class)
|
||||
.where(new StringClientParam("foo").matches().value("bar"))
|
||||
.prettyPrint()
|
||||
.usingStyle(SearchStyleEnum.POST)
|
||||
.returnBundle(Bundle.class)
|
||||
.encodedJson()
|
||||
.execute();
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertThat(e.getMessage(), containsString("Invalid request: The FHIR endpoint on this server does not know how to handle POST operation[Patient/_search] with parameters [[_pretty, foo]]"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() throws Exception {
|
||||
ourServer.stop();
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
ourPort = PortUtil.findFreePort();
|
||||
ourServer = new Server(ourPort);
|
||||
|
||||
DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider();
|
||||
|
||||
ServletHandler proxyHandler = new ServletHandler();
|
||||
RestfulServer servlet = new RestfulServer(ourCtx);
|
||||
servlet.setDefaultResponseEncoding(EncodingEnum.JSON);
|
||||
servlet.setPagingProvider(new FifoMemoryPagingProvider(10));
|
||||
|
||||
servlet.setResourceProviders(patientProvider);
|
||||
ServletHolder servletHolder = new ServletHolder(servlet);
|
||||
proxyHandler.addServletWithMapping(servletHolder, "/*");
|
||||
ourServer.setHandler(proxyHandler);
|
||||
ourServer.start();
|
||||
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS);
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setConnectionManager(connectionManager);
|
||||
ourClient = builder.build();
|
||||
|
||||
}
|
||||
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<? extends IBaseResource> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Search()
|
||||
public List search(
|
||||
@RequiredParam(name = Patient.SP_IDENTIFIER) TokenAndListParam theIdentifiers) {
|
||||
ourLastMethod = "search";
|
||||
ourIdentifiers = theIdentifiers;
|
||||
ArrayList<Patient> retVal = new ArrayList<Patient>();
|
||||
|
||||
for (int i = 0; i < 200; i++) {
|
||||
Patient patient = new Patient();
|
||||
patient.addName(new HumanName().setFamily("FAMILY"));
|
||||
patient.getIdElement().setValue("Patient/" + i);
|
||||
retVal.add(patient);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -19,6 +19,7 @@ import ca.uhn.fhir.util.TestUtil;
|
|||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mockito.internal.util.collections.ListUtil;
|
||||
import org.thymeleaf.util.ListUtils;
|
||||
|
@ -53,6 +54,9 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void testOverrideAndDisableBuiltInSearchParametersWithOverridingEnabled() {
|
||||
myDaoConfig.setDefaultSearchParamsCanBeOverridden(true);
|
||||
|
@ -883,6 +887,53 @@ public class FhirResourceDaoDstu2SearchCustomSearchParamTest extends BaseJpaDstu
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testSearchForStringOnIdentifierWithSpecificSystem() {
|
||||
|
||||
SearchParameter fooSp = new SearchParameter();
|
||||
fooSp.setBase(ResourceTypeEnum.PATIENT);
|
||||
fooSp.setCode("foo");
|
||||
fooSp.setType(SearchParamTypeEnum.STRING);
|
||||
fooSp.setXpath("Patient.identifier.where(system = 'http://AAA').value");
|
||||
fooSp.setXpathUsage(XPathUsageTypeEnum.NORMAL);
|
||||
fooSp.setStatus(ConformanceResourceStatusEnum.ACTIVE);
|
||||
IIdType spId = mySearchParameterDao.create(fooSp, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
|
||||
mySearchParamRegsitry.forceRefresh();
|
||||
|
||||
Patient pat = new Patient();
|
||||
pat.addIdentifier().setSystem("http://AAA").setValue("BAR678");
|
||||
pat.setGender(AdministrativeGenderEnum.MALE);
|
||||
IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient pat2 = new Patient();
|
||||
pat2.addIdentifier().setSystem("http://BBB").setValue("BAR678");
|
||||
pat2.setGender(AdministrativeGenderEnum.FEMALE);
|
||||
myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap map;
|
||||
IBundleProvider results;
|
||||
List<String> foundResources;
|
||||
|
||||
// Partial match
|
||||
map = new SearchParameterMap();
|
||||
map.add("foo", new StringParam("bar"));
|
||||
results = myPatientDao.search(map);
|
||||
foundResources = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(foundResources, contains(patId.getValue()));
|
||||
|
||||
// Non match
|
||||
map = new SearchParameterMap();
|
||||
map.add("foo", new StringParam("zzz"));
|
||||
results = myPatientDao.search(map);
|
||||
foundResources = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(foundResources, empty());
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSearchWithCustomParam() {
|
||||
|
||||
|
|
|
@ -913,6 +913,51 @@ public class FhirResourceDaoR4SearchCustomSearchParamTest extends BaseJpaR4Test
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchForStringOnIdentifierWithSpecificSystem() {
|
||||
|
||||
SearchParameter fooSp = new SearchParameter();
|
||||
fooSp.addBase("Patient");
|
||||
fooSp.setCode("foo");
|
||||
fooSp.setType(org.hl7.fhir.r4.model.Enumerations.SearchParamType.STRING);
|
||||
fooSp.setTitle("FOO SP");
|
||||
fooSp.setExpression("Patient.identifier.where(system = 'http://AAA').value");
|
||||
fooSp.setXpathUsage(org.hl7.fhir.r4.model.SearchParameter.XPathUsageType.NORMAL);
|
||||
fooSp.setStatus(org.hl7.fhir.r4.model.Enumerations.PublicationStatus.ACTIVE);
|
||||
IIdType spId = mySearchParameterDao.create(fooSp, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
mySearchParamRegsitry.forceRefresh();
|
||||
|
||||
Patient pat = new Patient();
|
||||
pat.addIdentifier().setSystem("http://AAA").setValue("BAR678");
|
||||
pat.setGender(AdministrativeGender.MALE);
|
||||
IIdType patId = myPatientDao.create(pat, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
Patient pat2 = new Patient();
|
||||
pat2.addIdentifier().setSystem("http://BBB").setValue("BAR678");
|
||||
pat2.setGender(AdministrativeGender.FEMALE);
|
||||
myPatientDao.create(pat2, mySrd).getId().toUnqualifiedVersionless();
|
||||
|
||||
SearchParameterMap map;
|
||||
IBundleProvider results;
|
||||
List<String> foundResources;
|
||||
|
||||
// Partial match
|
||||
map = new SearchParameterMap();
|
||||
map.add("foo", new StringParam("bar"));
|
||||
results = myPatientDao.search(map);
|
||||
foundResources = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(foundResources, contains(patId.getValue()));
|
||||
|
||||
// Non match
|
||||
map = new SearchParameterMap();
|
||||
map.add("foo", new StringParam("zzz"));
|
||||
results = myPatientDao.search(map);
|
||||
foundResources = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(foundResources, empty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchWithCustomParam() {
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
|
@ -67,12 +68,14 @@ public class ResourceProviderInterceptorDstu2Test extends BaseResourceProviderDs
|
|||
when(myServerInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class))).thenReturn(true);
|
||||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
|
||||
when(myJpaServerInterceptor.handleException(any(RequestDetails.class), any(BaseServerResponseException.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myJpaServerInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myJpaServerInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myJpaServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class))).thenReturn(true);
|
||||
when(myJpaServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myJpaServerInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
|
||||
myDaoConfig.getInterceptors().add(myDaoInterceptor);
|
||||
ourRestServer.registerInterceptor(myServerInterceptor);
|
||||
|
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.parser.IParser;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
|
@ -89,6 +90,7 @@ public class ResourceProviderInterceptorDstu3Test extends BaseResourceProviderDs
|
|||
when(myServerInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class))).thenReturn(true);
|
||||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -4,6 +4,7 @@ import ca.uhn.fhir.parser.IParser;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
|
@ -84,6 +85,7 @@ public class ResourceProviderInterceptorR4Test extends BaseResourceProviderR4Tes
|
|||
when(myServerInterceptor.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class))).thenReturn(true);
|
||||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myServerInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -38,6 +38,7 @@ import org.apache.http.entity.ContentType;
|
|||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -131,6 +132,68 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelfReferentialInclude() {
|
||||
Location loc1 = new Location();
|
||||
loc1.setName("loc1");
|
||||
IIdType loc1id = myClient.create().resource(loc1).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
Location loc2 = new Location();
|
||||
loc2.setName("loc2");
|
||||
IIdType loc2id = myClient.create().resource(loc2).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
loc1 = new Location();
|
||||
loc1.setId(loc1id);
|
||||
loc1.setName("loc1");
|
||||
loc1.getPartOf().setReference(loc2id.getValue());
|
||||
myClient.update().resource(loc1).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
loc2 = new Location();
|
||||
loc2.setId(loc2id);
|
||||
loc2.setName("loc2");
|
||||
loc2.getPartOf().setReference(loc1id.getValue());
|
||||
myClient.update().resource(loc2).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
IBaseBundle result = myClient
|
||||
.search()
|
||||
.forResource(Location.class)
|
||||
.where(Location.NAME.matches().value("loc1"))
|
||||
.include(Location.INCLUDE_PARTOF.asRecursive())
|
||||
.execute();
|
||||
assertThat(toUnqualifiedVersionlessIdValues(result), contains(loc1id.getValue(), loc2id.getValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelfReferentialRevInclude() {
|
||||
Location loc1 = new Location();
|
||||
loc1.setName("loc1");
|
||||
IIdType loc1id = myClient.create().resource(loc1).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
Location loc2 = new Location();
|
||||
loc2.setName("loc2");
|
||||
IIdType loc2id = myClient.create().resource(loc2).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
loc1 = new Location();
|
||||
loc1.setId(loc1id);
|
||||
loc1.setName("loc1");
|
||||
loc1.getPartOf().setReference(loc2id.getValue());
|
||||
myClient.update().resource(loc1).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
loc2 = new Location();
|
||||
loc2.setId(loc2id);
|
||||
loc2.setName("loc2");
|
||||
loc2.getPartOf().setReference(loc1id.getValue());
|
||||
myClient.update().resource(loc2).execute().getId().toUnqualifiedVersionless();
|
||||
|
||||
IBaseBundle result = myClient
|
||||
.search()
|
||||
.forResource(Location.class)
|
||||
.where(Location.NAME.matches().value("loc1"))
|
||||
.revInclude(Location.INCLUDE_PARTOF.asRecursive())
|
||||
.execute();
|
||||
assertThat(toUnqualifiedVersionlessIdValues(result), contains(loc1id.getValue(), loc2id.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* See #484
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package ca.uhn.fhir.rest.api.server;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
/**
|
||||
* @see ca.uhn.fhir.rest.server.interceptor.IServerInterceptor
|
||||
*/
|
||||
public class ResponseDetails {
|
||||
|
||||
private IBaseResource myResponseResource;
|
||||
private int myResponseCode;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResponseDetails() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResponseDetails(IBaseResource theResponseResource) {
|
||||
setResponseResource(theResponseResource);
|
||||
}
|
||||
|
||||
public int getResponseCode() {
|
||||
return myResponseCode;
|
||||
}
|
||||
|
||||
public void setResponseCode(int theResponseCode) {
|
||||
myResponseCode = theResponseCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource which will be returned to the client
|
||||
*/
|
||||
public IBaseResource getResponseResource() {
|
||||
return myResponseResource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource which will be returned to the client
|
||||
*/
|
||||
public void setResponseResource(IBaseResource theResponseResource) {
|
||||
myResponseResource = theResponseResource;
|
||||
}
|
||||
|
||||
}
|
|
@ -581,7 +581,7 @@ public class RestfulServerUtils {
|
|||
return streamResponseAsResource(theServer, theResource, theSummaryMode, stausCode, null, theAddContentLocationHeader, respondGzip, theRequestDetails, null, null);
|
||||
}
|
||||
|
||||
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set<SummaryEnum> theSummaryMode, int theStausCode, String theStatusMessage,
|
||||
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set<SummaryEnum> theSummaryMode, int theStatusCode, String theStatusMessage,
|
||||
boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType<Date> theOperationResourceLastUpdated)
|
||||
throws IOException {
|
||||
IRestfulResponse response = theRequestDetails.getResponse();
|
||||
|
@ -636,7 +636,7 @@ public class RestfulServerUtils {
|
|||
}
|
||||
}
|
||||
|
||||
return response.sendAttachmentResponse(bin, theStausCode, contentType);
|
||||
return response.sendAttachmentResponse(bin, theStatusCode, contentType);
|
||||
}
|
||||
|
||||
// Ok, we're not serving a binary resource, so apply default encoding
|
||||
|
@ -683,7 +683,7 @@ public class RestfulServerUtils {
|
|||
}
|
||||
String charset = Constants.CHARSET_NAME_UTF8;
|
||||
|
||||
Writer writer = response.getResponseWriter(theStausCode, theStatusMessage, contentType, charset, respondGzip);
|
||||
Writer writer = response.getResponseWriter(theStatusCode, theStatusMessage, contentType, charset, respondGzip);
|
||||
if (theResource == null) {
|
||||
// No response is being returned
|
||||
} else if (encodingDomainResourceAsText && theResource instanceof IResource) {
|
||||
|
@ -693,7 +693,7 @@ public class RestfulServerUtils {
|
|||
parser.encodeResourceToWriter(theResource, writer);
|
||||
}
|
||||
//FIXME resource leak
|
||||
return response.sendWriterResponse(theStausCode, contentType, charset, writer);
|
||||
return response.sendWriterResponse(theStatusCode, contentType, charset, writer);
|
||||
}
|
||||
|
||||
public static Integer tryToExtractNamedParameter(RequestDetails theRequest, String theParamName) {
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server.interceptor;
|
|||
* 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.
|
||||
|
@ -20,26 +20,29 @@ package ca.uhn.fhir.rest.server.interceptor;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.server.IRestfulServerDefaults;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
||||
|
@ -64,52 +67,42 @@ public interface IServerInterceptor {
|
|||
* OperationOutcome}. Implementations may also choose to provide their own response to the client. In this case, they
|
||||
* should return <code>false</code>, to indicate that they have handled the request and processing should stop.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param theRequestDetails
|
||||
* A bean containing details about the request that is about to be processed, including details such as the
|
||||
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
|
||||
* pulled out of the {@link javax.servlet.http.HttpServletRequest servlet request}. Note that the bean
|
||||
* properties are not all guaranteed to be populated, depending on how early during processing the
|
||||
* exception occurred.
|
||||
* @param theServletRequest
|
||||
* The incoming request
|
||||
* @param theServletResponse
|
||||
* The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link javax.servlet.http.HttpServletResponse#getWriter()}) but in that case it is important to return
|
||||
* <code>false</code> to indicate that the server itself should not also provide a response.
|
||||
* @param theRequestDetails A bean containing details about the request that is about to be processed, including details such as the
|
||||
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
|
||||
* pulled out of the {@link javax.servlet.http.HttpServletRequest servlet request}. Note that the bean
|
||||
* properties are not all guaranteed to be populated, depending on how early during processing the
|
||||
* exception occurred.
|
||||
* @param theServletRequest The incoming request
|
||||
* @param theServletResponse The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link javax.servlet.http.HttpServletResponse#getWriter()}) but in that case it is important to return
|
||||
* <code>false</code> to indicate that the server itself should not also provide a response.
|
||||
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
|
||||
* will be called.
|
||||
* @throws ServletException
|
||||
* If this exception is thrown, it will be re-thrown up to the container for handling.
|
||||
* @throws IOException
|
||||
* If this exception is thrown, it will be re-thrown up to the container for handling.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
|
||||
* will be called.
|
||||
* @throws ServletException If this exception is thrown, it will be re-thrown up to the container for handling.
|
||||
* @throws IOException If this exception is thrown, it will be re-thrown up to the container for handling.
|
||||
*/
|
||||
boolean handleException(RequestDetails theRequestDetails, BaseServerResponseException theException, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws ServletException, IOException;
|
||||
throws ServletException, IOException;
|
||||
|
||||
/**
|
||||
* This method is called just before the actual implementing server method is invoked.
|
||||
*
|
||||
* @param theRequestDetails
|
||||
* A bean containing details about the request that is about to be processed, including details such as the
|
||||
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
|
||||
* pulled out of the {@link HttpServletRequest servlet request}.
|
||||
* @param theRequest
|
||||
* The incoming request
|
||||
* @param theResponse
|
||||
* The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* to indicate that the server itself should not also provide a response.
|
||||
* @param theRequestDetails A bean containing details about the request that is about to be processed, including details such as the
|
||||
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
|
||||
* pulled out of the {@link HttpServletRequest servlet request}.
|
||||
* @param theRequest The incoming request
|
||||
* @param theResponse The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* to indicate that the server itself should not also provide a response.
|
||||
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
|
||||
* will be called.
|
||||
* @throws AuthenticationException
|
||||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
|
||||
* will be called.
|
||||
* @throws AuthenticationException This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException;
|
||||
|
||||
|
@ -124,12 +117,10 @@ public interface IServerInterceptor {
|
|||
* This method may however throw a subclass of {@link BaseServerResponseException}, and processing
|
||||
* will be aborted with an appropriate error returned to the client.
|
||||
* </p>
|
||||
*
|
||||
* @param theOperation
|
||||
* The type of operation that the FHIR server has determined that the client is trying to invoke
|
||||
* @param theProcessedRequest
|
||||
* An object which will be populated with the details which were extracted from the raw request by the
|
||||
* server, e.g. the FHIR operation type and the parsed resource body (if any).
|
||||
*
|
||||
* @param theOperation The type of operation that the FHIR server has determined that the client is trying to invoke
|
||||
* @param theProcessedRequest An object which will be populated with the details which were extracted from the raw request by the
|
||||
* server, e.g. the FHIR operation type and the parsed resource body (if any).
|
||||
*/
|
||||
void incomingRequestPreHandled(RestOperationTypeEnum theOperation, ActionRequestDetails theProcessedRequest);
|
||||
|
||||
|
@ -139,17 +130,15 @@ public interface IServerInterceptor {
|
|||
* <p>
|
||||
* Note that any exceptions thrown by this method will not be trapped by HAPI (they will be passed up to the server)
|
||||
* </p>
|
||||
*
|
||||
* @param theRequest
|
||||
* The incoming request
|
||||
* @param theResponse
|
||||
* The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* to indicate that the server itself should not also provide a response.
|
||||
*
|
||||
* @param theRequest The incoming request
|
||||
* @param theResponse The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* to indicate that the server itself should not also provide a response.
|
||||
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
|
||||
* will be called.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
|
||||
* will be called.
|
||||
*/
|
||||
boolean incomingRequestPreProcessed(HttpServletRequest theRequest, HttpServletResponse theResponse);
|
||||
|
||||
|
@ -180,30 +169,52 @@ public interface IServerInterceptor {
|
|||
/**
|
||||
* This method is called after the server implementation method has been called, but before any attempt to stream the
|
||||
* response back to the client.
|
||||
*
|
||||
* @param theRequestDetails
|
||||
* A bean containing details about the request that is about to be processed, including details such as the
|
||||
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
|
||||
* pulled out of the {@link HttpServletRequest servlet request}.
|
||||
* @param theResponseObject
|
||||
* The actual object which is being streamed to the client as a response. This may be
|
||||
* <code>null</code> if the response does not include a resource.
|
||||
* @param theServletRequest
|
||||
* The incoming request
|
||||
* @param theServletResponse
|
||||
* The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* to indicate that the server itself should not also provide a response.
|
||||
*
|
||||
* @param theRequestDetails A bean containing details about the request that is about to be processed, including details such as the
|
||||
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
|
||||
* pulled out of the {@link HttpServletRequest servlet request}.
|
||||
* @param theResponseObject The actual object which is being streamed to the client as a response. This may be
|
||||
* <code>null</code> if the response does not include a resource.
|
||||
* @param theServletRequest The incoming request
|
||||
* @param theServletResponse The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* to indicate that the server itself should not also provide a response.
|
||||
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
|
||||
* will be called.
|
||||
* @throws AuthenticationException
|
||||
* This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
|
||||
* will be called.
|
||||
* @throws AuthenticationException This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
* @deprecated As of HAPI FHIR 3.3.0, this method has been deprecated in
|
||||
* favour of {@link #outgoingResponse(RequestDetails, ResponseDetails, HttpServletRequest, HttpServletResponse)}
|
||||
* and will be removed in a future version of HAPI FHIR.
|
||||
*/
|
||||
boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws AuthenticationException;
|
||||
throws AuthenticationException;
|
||||
|
||||
/**
|
||||
* This method is called after the server implementation method has been called, but before any attempt to stream the
|
||||
* response back to the client.
|
||||
*
|
||||
* @param theRequestDetails A bean containing details about the request that is about to be processed, including details such as the
|
||||
* resource type and logical ID (if any) and other FHIR-specific aspects of the request which have been
|
||||
* pulled out of the {@link HttpServletRequest servlet request}.
|
||||
* @param theResponseDetails This object contains details about the response, including
|
||||
* the actual payload that will be returned
|
||||
* @param theServletRequest The incoming request
|
||||
* @param theServletResponse The response. Note that interceptors may choose to provide a response (i.e. by calling
|
||||
* {@link HttpServletResponse#getWriter()}) but in that case it is important to return <code>false</code>
|
||||
* to indicate that the server itself should not also provide a response.
|
||||
* @return Return <code>true</code> if processing should continue normally. This is generally the right thing to do.
|
||||
* If your interceptor is providing a response rather than letting HAPI handle the response normally, you
|
||||
* must return <code>false</code>. In this case, no further processing will occur and no further interceptors
|
||||
* will be called.
|
||||
* @throws AuthenticationException This exception may be thrown to indicate that the interceptor has detected an unauthorized access
|
||||
* attempt. If thrown, processing will stop and an HTTP 401 will be returned to the client.
|
||||
*/
|
||||
boolean outgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws AuthenticationException;
|
||||
|
||||
|
||||
/**
|
||||
* Use {@link #outgoingResponse(RequestDetails, IBaseResource, HttpServletRequest, HttpServletResponse)} instead
|
||||
|
@ -237,11 +248,11 @@ public interface IServerInterceptor {
|
|||
* OperationOutcome}. Implementations may also choose to provide their own response to the client. In this case, they
|
||||
* should return a non-<code>null</code>, to indicate that they have handled the request and processing should stop.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return Returns the new exception to use for processing, or <code>null</code> if this interceptor is not trying to
|
||||
* modify the exception. For example, if this interceptor has nothing to do with exception processing, it
|
||||
* should always return <code>null</code>. If this interceptor adds an OperationOutcome to the exception, it
|
||||
* should return an exception.
|
||||
* modify the exception. For example, if this interceptor has nothing to do with exception processing, it
|
||||
* should always return <code>null</code>. If this interceptor adds an OperationOutcome to the exception, it
|
||||
* should return an exception.
|
||||
*/
|
||||
BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException;
|
||||
|
||||
|
@ -252,9 +263,8 @@ public interface IServerInterceptor {
|
|||
* Note that this individual interceptors will have this method called in the reverse order from the order in
|
||||
* which the interceptors were registered with the server.
|
||||
* </p>
|
||||
*
|
||||
* @param theRequestDetails
|
||||
* The request itself
|
||||
*
|
||||
* @param theRequestDetails The request itself
|
||||
*/
|
||||
void processingCompletedNormally(ServletRequestDetails theRequestDetails);
|
||||
|
||||
|
@ -300,11 +310,9 @@ public interface IServerInterceptor {
|
|||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param theRequestDetails
|
||||
* The request details to wrap
|
||||
* @param theId
|
||||
* The ID of the resource being created (note that the ID should have the resource type populated)
|
||||
*
|
||||
* @param theRequestDetails The request details to wrap
|
||||
* @param theId The ID of the resource being created (note that the ID should have the resource type populated)
|
||||
*/
|
||||
public ActionRequestDetails(RequestDetails theRequestDetails, IIdType theId) {
|
||||
this(theRequestDetails, theId.getResourceType(), theId);
|
||||
|
|
|
@ -26,6 +26,7 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.model.api.TagList;
|
||||
|
@ -85,6 +86,11 @@ public class InterceptorAdapter implements IServerInterceptor {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject) {
|
||||
ServletRequestDetails details = (ServletRequestDetails) theRequestDetails;
|
||||
|
|
|
@ -5,6 +5,7 @@ import ca.uhn.fhir.rest.api.Constants;
|
|||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding;
|
||||
|
@ -38,9 +39,9 @@ import static org.apache.commons.lang3.StringUtils.*;
|
|||
* 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.
|
||||
|
@ -301,7 +302,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
|
||||
throws AuthenticationException {
|
||||
|
||||
/*
|
||||
|
@ -365,7 +366,7 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
return super.outgoingResponse(theRequestDetails, theResponseObject, theServletRequest, theServletResponse);
|
||||
}
|
||||
|
||||
streamResponse(theRequestDetails, theServletResponse, theResponseObject, theServletRequest, 200);
|
||||
streamResponse(theRequestDetails, theServletResponse, theResponseObject.getResponseResource(), theServletRequest, 200);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server.method;
|
|||
* 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.
|
||||
|
@ -20,38 +20,30 @@ package ca.uhn.fhir.rest.server.method;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
import ca.uhn.fhir.rest.api.server.IRestfulResponse;
|
||||
import ca.uhn.fhir.rest.api.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
import ca.uhn.fhir.rest.api.RequestTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.rest.api.server.IRestfulResponse;
|
||||
import ca.uhn.fhir.rest.api.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<MethodOutcome> {
|
||||
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class);
|
||||
|
@ -89,34 +81,34 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
|
||||
private int getOperationStatus(MethodOutcome response) {
|
||||
switch (getRestOperationType()) {
|
||||
case CREATE:
|
||||
if (response == null) {
|
||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null, which is not allowed for create operation");
|
||||
}
|
||||
if (response.getCreated() == null || Boolean.TRUE.equals(response.getCreated())) {
|
||||
return Constants.STATUS_HTTP_201_CREATED;
|
||||
}
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
|
||||
case UPDATE:
|
||||
if (response == null || response.getCreated() == null || Boolean.FALSE.equals(response.getCreated())) {
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
}
|
||||
return Constants.STATUS_HTTP_201_CREATED;
|
||||
|
||||
case VALIDATE:
|
||||
case DELETE:
|
||||
default:
|
||||
if (response == null) {
|
||||
if (isReturnVoid() == false) {
|
||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null");
|
||||
case CREATE:
|
||||
if (response == null) {
|
||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null, which is not allowed for create operation");
|
||||
}
|
||||
return Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||
}
|
||||
if (response.getOperationOutcome() == null) {
|
||||
return Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||
}
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
if (response.getCreated() == null || Boolean.TRUE.equals(response.getCreated())) {
|
||||
return Constants.STATUS_HTTP_201_CREATED;
|
||||
}
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
|
||||
case UPDATE:
|
||||
if (response == null || response.getCreated() == null || Boolean.FALSE.equals(response.getCreated())) {
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
}
|
||||
return Constants.STATUS_HTTP_201_CREATED;
|
||||
|
||||
case VALIDATE:
|
||||
case DELETE:
|
||||
default:
|
||||
if (response == null) {
|
||||
if (isReturnVoid() == false) {
|
||||
throw new InternalErrorException("Method " + getMethod().getName() + " in type " + getMethod().getDeclaringClass().getCanonicalName() + " returned null");
|
||||
}
|
||||
return Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||
}
|
||||
if (response.getOperationOutcome() == null) {
|
||||
return Constants.STATUS_HTTP_204_NO_CONTENT;
|
||||
}
|
||||
return Constants.STATUS_HTTP_200_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,17 +128,17 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
if (getMatchingOperation() != null && !getMatchingOperation().equals(theRequest.getOperation())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Note: Technically this will match an update (PUT) method even if
|
||||
* there is no ID in the URL - We allow this here because there is no
|
||||
* better match for that, and this allows the update/PUT method to give
|
||||
* a helpful error if the client has forgotten to include the
|
||||
* a helpful error if the client has forgotten to include the
|
||||
* ID in the URL.
|
||||
*
|
||||
*
|
||||
* It's also needed for conditional update..
|
||||
*/
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -162,14 +154,14 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
*/
|
||||
MethodOutcome response;
|
||||
Object methodReturn = invokeServerMethod(theServer, theRequest, params);
|
||||
|
||||
|
||||
if (methodReturn instanceof IBaseOperationOutcome) {
|
||||
response = new MethodOutcome();
|
||||
response.setOperationOutcome((IBaseOperationOutcome) methodReturn);
|
||||
} else {
|
||||
response = (MethodOutcome) methodReturn;
|
||||
}
|
||||
|
||||
|
||||
if (response != null && response.getId() != null && response.getId().hasResourceType()) {
|
||||
if (getContext().getResourceDefinition(response.getId().getResourceType()) == null) {
|
||||
throw new InternalErrorException("Server method returned invalid resource ID: " + response.getId().getValue());
|
||||
|
@ -181,6 +173,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
|
||||
return returnResponse(theServer, theRequest, response, outcome, resource);
|
||||
}
|
||||
|
||||
public boolean isReturnVoid() {
|
||||
return myReturnVoid;
|
||||
}
|
||||
|
@ -206,26 +199,42 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
}
|
||||
}
|
||||
|
||||
ResponseDetails responseDetails = new ResponseDetails();
|
||||
responseDetails.setResponseResource(outcome);
|
||||
responseDetails.setResponseCode(operationStatus);
|
||||
|
||||
HttpServletRequest servletRequest = null;
|
||||
HttpServletResponse servletResponse = null;
|
||||
if (theRequest instanceof ServletRequestDetails) {
|
||||
servletRequest = ((ServletRequestDetails) theRequest).getServletRequest();
|
||||
servletResponse = ((ServletRequestDetails) theRequest).getServletResponse();
|
||||
}
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, outcome);
|
||||
if (!continueProcessing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
continueProcessing = next.outgoingResponse(theRequest, responseDetails, servletRequest, servletResponse);
|
||||
if (!continueProcessing) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
IRestfulResponse restfulResponse = theRequest.getResponse();
|
||||
|
||||
|
||||
if (response != null) {
|
||||
if (response.getResource() != null) {
|
||||
restfulResponse.setOperationResourceLastUpdated(RestfulServerUtils.extractLastUpdatedFromResource(response.getResource()));
|
||||
}
|
||||
|
||||
|
||||
IIdType responseId = response.getId();
|
||||
if (responseId != null && responseId.getResourceType() == null && responseId.hasIdPart()) {
|
||||
responseId = responseId.withResourceType(getResourceName());
|
||||
}
|
||||
|
||||
|
||||
if (responseId != null) {
|
||||
String serverBase = theRequest.getFhirServerBase();
|
||||
responseId = RestfulServerUtils.fullyQualifyResourceIdOrReturnNull(theServer, resource, serverBase, responseId);
|
||||
|
@ -236,35 +245,9 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
||||
Set<SummaryEnum> summaryMode = Collections.emptySet();
|
||||
|
||||
return restfulResponse.streamResponseAsResource(outcome, prettyPrint, summaryMode, operationStatus, null, theRequest.isRespondGzip(), true);
|
||||
// return theRequest.getResponse().returnResponse(ParseAction.create(outcome), operationStatus, allowPrefer, response, getResourceName());
|
||||
return restfulResponse.streamResponseAsResource(responseDetails.getResponseResource(), prettyPrint, summaryMode, responseDetails.getResponseCode(), null, theRequest.isRespondGzip(), true);
|
||||
}
|
||||
|
||||
protected void streamOperationOutcome(BaseServerResponseException theE, RestfulServer theServer, EncodingEnum theEncodingNotNull, HttpServletResponse theResponse, RequestDetails theRequest) throws IOException {
|
||||
theResponse.setStatus(theE.getStatusCode());
|
||||
|
||||
theServer.addHeadersToResponse(theResponse);
|
||||
|
||||
if (theE.getOperationOutcome() != null) {
|
||||
theResponse.setContentType(theEncodingNotNull.getResourceContentType());
|
||||
IParser parser = theEncodingNotNull.newParser(theServer.getFhirContext());
|
||||
parser.setPrettyPrint(RestfulServerUtils.prettyPrintResponse(theServer, theRequest));
|
||||
Writer writer = theResponse.getWriter();
|
||||
try {
|
||||
parser.encodeResourceToWriter(theE.getOperationOutcome(), writer);
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
} else {
|
||||
theResponse.setContentType(Constants.CT_TEXT);
|
||||
Writer writer = theResponse.getWriter();
|
||||
try {
|
||||
writer.append(theE.getMessage());
|
||||
} finally {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void parseContentLocation(FhirContext theContext, MethodOutcome theOutcomeToPopulate, String theLocationHeader) {
|
||||
if (StringUtils.isBlank(theLocationHeader)) {
|
||||
|
|
|
@ -1,6 +1,41 @@
|
|||
package ca.uhn.fhir.rest.server.method;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
|
@ -10,9 +45,9 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
* 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.
|
||||
|
@ -20,33 +55,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
import ca.uhn.fhir.util.CollectionUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.*;
|
||||
import ca.uhn.fhir.rest.api.server.*;
|
||||
import ca.uhn.fhir.rest.server.IPagingProvider;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils;
|
||||
import ca.uhn.fhir.rest.server.RestfulServerUtils.ResponseEncoding;
|
||||
import ca.uhn.fhir.rest.server.exceptions.*;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.ResponseHighlighterInterceptor;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
|
||||
public abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Object> {
|
||||
protected static final Set<String> ALLOWED_PARAMS;
|
||||
|
@ -83,7 +91,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||
if (collectionType != null) {
|
||||
if (!Object.class.equals(collectionType) && !IBaseResource.class.isAssignableFrom(collectionType)) {
|
||||
throw new ConfigurationException(
|
||||
"Method " + theMethod.getDeclaringClass().getSimpleName() + "#" + theMethod.getName() + " returns an invalid collection generic type: " + collectionType);
|
||||
"Method " + theMethod.getDeclaringClass().getSimpleName() + "#" + theMethod.getName() + " returns an invalid collection generic type: " + collectionType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,7 +107,7 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||
myMethodReturnType = MethodReturnTypeEnum.METHOD_OUTCOME;
|
||||
} else {
|
||||
throw new ConfigurationException(
|
||||
"Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
||||
"Invalid return type '" + methodReturnType.getCanonicalName() + "' on method '" + theMethod.getName() + "' on type: " + theMethod.getDeclaringClass().getCanonicalName());
|
||||
}
|
||||
|
||||
if (theReturnResourceType != null) {
|
||||
|
@ -115,154 +123,8 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||
|
||||
}
|
||||
|
||||
public MethodReturnTypeEnum getMethodReturnType() {
|
||||
return myMethodReturnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceName() {
|
||||
return myResourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the response is a bundle, this type will be placed in the root of the bundle (can be null)
|
||||
*/
|
||||
protected abstract BundleTypeEnum getResponseBundleType();
|
||||
|
||||
public abstract ReturnTypeEnum getReturnType();
|
||||
|
||||
@Override
|
||||
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
|
||||
IBaseResource response = doInvokeServer(theServer, theRequest);
|
||||
|
||||
Set<SummaryEnum> summaryMode = RestfulServerUtils.determineSummaryMode(theRequest);
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, response);
|
||||
if (!continueProcessing) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
||||
|
||||
return theRequest.getResponse().streamResponseAsResource(response, prettyPrint, summaryMode, Constants.STATUS_HTTP_200_OK, null, theRequest.isRespondGzip(), isAddContentLocationHeader());
|
||||
|
||||
}
|
||||
|
||||
public IBaseResource doInvokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) {
|
||||
Object[] params = createMethodParams(theRequest);
|
||||
|
||||
Object resultObj = invokeServer(theServer, theRequest, params);
|
||||
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest);
|
||||
|
||||
final IBaseResource responseObject;
|
||||
|
||||
switch (getReturnType()) {
|
||||
case BUNDLE: {
|
||||
|
||||
/*
|
||||
* Figure out the self-link for this request
|
||||
*/
|
||||
String serverBase = theRequest.getServerBaseForRequest();
|
||||
String linkSelf;
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(serverBase);
|
||||
if (isNotBlank(theRequest.getRequestPath())) {
|
||||
b.append('/');
|
||||
b.append(theRequest.getRequestPath());
|
||||
}
|
||||
// For POST the URL parameters get jumbled with the post body parameters so don't include them, they might be huge
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
boolean first = true;
|
||||
Map<String, String[]> parameters = theRequest.getParameters();
|
||||
for (String nextParamName : new TreeSet<>(parameters.keySet())) {
|
||||
for (String nextParamValue : parameters.get(nextParamName)) {
|
||||
if (first) {
|
||||
b.append('?');
|
||||
first = false;
|
||||
} else {
|
||||
b.append('&');
|
||||
}
|
||||
b.append(UrlUtil.escapeUrlParam(nextParamName));
|
||||
b.append('=');
|
||||
b.append(UrlUtil.escapeUrlParam(nextParamValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
linkSelf = b.toString();
|
||||
|
||||
if (getMethodReturnType() == MethodReturnTypeEnum.BUNDLE_RESOURCE) {
|
||||
IBaseResource resource;
|
||||
IPrimitiveType<Date> lastUpdated;
|
||||
if (resultObj instanceof IBundleProvider) {
|
||||
IBundleProvider result = (IBundleProvider) resultObj;
|
||||
resource = result.getResources(0, 1).get(0);
|
||||
lastUpdated = result.getPublished();
|
||||
} else {
|
||||
resource = (IBaseResource) resultObj;
|
||||
lastUpdated = theServer.getFhirContext().getVersion().getLastUpdated(resource);
|
||||
}
|
||||
|
||||
/*
|
||||
* We assume that the bundle we got back from the handling method may not have everything populated (e.g. self links, bundle type, etc) so we do that here.
|
||||
*/
|
||||
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
|
||||
bundleFactory.initializeWithBundleResource(resource);
|
||||
bundleFactory.addRootPropertiesToBundle(null, theRequest.getFhirServerBase(), linkSelf, null, null, count, getResponseBundleType(), lastUpdated);
|
||||
|
||||
responseObject = resource;
|
||||
} else {
|
||||
Set<Include> includes = getRequestIncludesFromParams(params);
|
||||
|
||||
IBundleProvider result = (IBundleProvider) resultObj;
|
||||
if (count == null) {
|
||||
count = result.preferredPageSize();
|
||||
}
|
||||
|
||||
Integer offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_PAGINGOFFSET);
|
||||
if (offsetI == null || offsetI < 0) {
|
||||
offsetI = 0;
|
||||
}
|
||||
|
||||
Integer resultSize = result.size();
|
||||
int start;
|
||||
if (resultSize != null) {
|
||||
start = Math.max(0, Math.min(offsetI, resultSize - 1));
|
||||
} else {
|
||||
start = offsetI;
|
||||
}
|
||||
|
||||
ResponseEncoding responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding());
|
||||
EncodingEnum linkEncoding = theRequest.getParameters().containsKey(Constants.PARAM_FORMAT) && responseEncoding != null ? responseEncoding.getEncoding() : null;
|
||||
|
||||
responseObject = createBundleFromBundleProvider(theServer, theRequest, count, linkSelf, includes, result, start, getResponseBundleType(), linkEncoding, null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RESOURCE: {
|
||||
IBundleProvider result = (IBundleProvider) resultObj;
|
||||
if (result.size() == 0) {
|
||||
throw new ResourceNotFoundException(theRequest.getId());
|
||||
} else if (result.size() > 1) {
|
||||
throw new InternalErrorException("Method returned multiple resources");
|
||||
}
|
||||
|
||||
IBaseResource resource = result.getResources(0, 1).get(0);
|
||||
responseObject = resource;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalStateException(); // should not happen
|
||||
}
|
||||
return responseObject;
|
||||
}
|
||||
|
||||
protected IBaseResource createBundleFromBundleProvider(IRestfulServer<?> theServer, RequestDetails theRequest, Integer theLimit, String theLinkSelf, Set<Include> theIncludes,
|
||||
IBundleProvider theResult, int theOffset, BundleTypeEnum theBundleType, EncodingEnum theLinkEncoding, String theSearchId) {
|
||||
IBundleProvider theResult, int theOffset, BundleTypeEnum theBundleType, EncodingEnum theLinkEncoding, String theSearchId) {
|
||||
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
|
||||
|
||||
int numToReturn;
|
||||
|
@ -369,6 +231,172 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||
|
||||
}
|
||||
|
||||
public IBaseResource doInvokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) {
|
||||
Object[] params = createMethodParams(theRequest);
|
||||
|
||||
Object resultObj = invokeServer(theServer, theRequest, params);
|
||||
|
||||
Integer count = RestfulServerUtils.extractCountParameter(theRequest);
|
||||
|
||||
final IBaseResource responseObject;
|
||||
|
||||
switch (getReturnType()) {
|
||||
case BUNDLE: {
|
||||
|
||||
/*
|
||||
* Figure out the self-link for this request
|
||||
*/
|
||||
String serverBase = theRequest.getServerBaseForRequest();
|
||||
String linkSelf;
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(serverBase);
|
||||
if (isNotBlank(theRequest.getRequestPath())) {
|
||||
b.append('/');
|
||||
b.append(theRequest.getRequestPath());
|
||||
}
|
||||
// For POST the URL parameters get jumbled with the post body parameters so don't include them, they might be huge
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
boolean first = true;
|
||||
Map<String, String[]> parameters = theRequest.getParameters();
|
||||
for (String nextParamName : new TreeSet<>(parameters.keySet())) {
|
||||
for (String nextParamValue : parameters.get(nextParamName)) {
|
||||
if (first) {
|
||||
b.append('?');
|
||||
first = false;
|
||||
} else {
|
||||
b.append('&');
|
||||
}
|
||||
b.append(UrlUtil.escapeUrlParam(nextParamName));
|
||||
b.append('=');
|
||||
b.append(UrlUtil.escapeUrlParam(nextParamValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
linkSelf = b.toString();
|
||||
|
||||
if (getMethodReturnType() == MethodReturnTypeEnum.BUNDLE_RESOURCE) {
|
||||
IBaseResource resource;
|
||||
IPrimitiveType<Date> lastUpdated;
|
||||
if (resultObj instanceof IBundleProvider) {
|
||||
IBundleProvider result = (IBundleProvider) resultObj;
|
||||
resource = result.getResources(0, 1).get(0);
|
||||
lastUpdated = result.getPublished();
|
||||
} else {
|
||||
resource = (IBaseResource) resultObj;
|
||||
lastUpdated = theServer.getFhirContext().getVersion().getLastUpdated(resource);
|
||||
}
|
||||
|
||||
/*
|
||||
* We assume that the bundle we got back from the handling method may not have everything populated (e.g. self links, bundle type, etc) so we do that here.
|
||||
*/
|
||||
IVersionSpecificBundleFactory bundleFactory = theServer.getFhirContext().newBundleFactory();
|
||||
bundleFactory.initializeWithBundleResource(resource);
|
||||
bundleFactory.addRootPropertiesToBundle(null, theRequest.getFhirServerBase(), linkSelf, null, null, count, getResponseBundleType(), lastUpdated);
|
||||
|
||||
responseObject = resource;
|
||||
} else {
|
||||
Set<Include> includes = getRequestIncludesFromParams(params);
|
||||
|
||||
IBundleProvider result = (IBundleProvider) resultObj;
|
||||
if (count == null) {
|
||||
count = result.preferredPageSize();
|
||||
}
|
||||
|
||||
Integer offsetI = RestfulServerUtils.tryToExtractNamedParameter(theRequest, Constants.PARAM_PAGINGOFFSET);
|
||||
if (offsetI == null || offsetI < 0) {
|
||||
offsetI = 0;
|
||||
}
|
||||
|
||||
Integer resultSize = result.size();
|
||||
int start;
|
||||
if (resultSize != null) {
|
||||
start = Math.max(0, Math.min(offsetI, resultSize - 1));
|
||||
} else {
|
||||
start = offsetI;
|
||||
}
|
||||
|
||||
ResponseEncoding responseEncoding = RestfulServerUtils.determineResponseEncodingNoDefault(theRequest, theServer.getDefaultResponseEncoding());
|
||||
EncodingEnum linkEncoding = theRequest.getParameters().containsKey(Constants.PARAM_FORMAT) && responseEncoding != null ? responseEncoding.getEncoding() : null;
|
||||
|
||||
responseObject = createBundleFromBundleProvider(theServer, theRequest, count, linkSelf, includes, result, start, getResponseBundleType(), linkEncoding, null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RESOURCE: {
|
||||
IBundleProvider result = (IBundleProvider) resultObj;
|
||||
if (result.size() == 0) {
|
||||
throw new ResourceNotFoundException(theRequest.getId());
|
||||
} else if (result.size() > 1) {
|
||||
throw new InternalErrorException("Method returned multiple resources");
|
||||
}
|
||||
|
||||
IBaseResource resource = result.getResources(0, 1).get(0);
|
||||
responseObject = resource;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new IllegalStateException(); // should not happen
|
||||
}
|
||||
return responseObject;
|
||||
}
|
||||
|
||||
public MethodReturnTypeEnum getMethodReturnType() {
|
||||
return myMethodReturnType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourceName() {
|
||||
return myResourceName;
|
||||
}
|
||||
|
||||
protected void setResourceName(String theResourceName) {
|
||||
myResourceName = theResourceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the response is a bundle, this type will be placed in the root of the bundle (can be null)
|
||||
*/
|
||||
protected abstract BundleTypeEnum getResponseBundleType();
|
||||
|
||||
public abstract ReturnTypeEnum getReturnType();
|
||||
|
||||
@Override
|
||||
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
|
||||
|
||||
IBaseResource response = doInvokeServer(theServer, theRequest);
|
||||
|
||||
Set<SummaryEnum> summaryMode = RestfulServerUtils.determineSummaryMode(theRequest);
|
||||
|
||||
ResponseDetails responseDetails = new ResponseDetails();
|
||||
responseDetails.setResponseResource(response);
|
||||
responseDetails.setResponseCode(Constants.STATUS_HTTP_200_OK);
|
||||
|
||||
HttpServletRequest servletRequest = null;
|
||||
HttpServletResponse servletResponse = null;
|
||||
if (theRequest instanceof ServletRequestDetails) {
|
||||
servletRequest = ((ServletRequestDetails) theRequest).getServletRequest();
|
||||
servletResponse = ((ServletRequestDetails) theRequest).getServletResponse();
|
||||
}
|
||||
|
||||
for (int i = theServer.getInterceptors().size() - 1; i >= 0; i--) {
|
||||
IServerInterceptor next = theServer.getInterceptors().get(i);
|
||||
boolean continueProcessing = next.outgoingResponse(theRequest, response);
|
||||
if (!continueProcessing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
continueProcessing = next.outgoingResponse(theRequest, responseDetails, servletRequest, servletResponse);
|
||||
if (!continueProcessing) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
boolean prettyPrint = RestfulServerUtils.prettyPrintResponse(theServer, theRequest);
|
||||
|
||||
return theRequest.getResponse().streamResponseAsResource(responseDetails.getResponseResource(), prettyPrint, summaryMode, responseDetails.getResponseCode(), null, theRequest.isRespondGzip(), isAddContentLocationHeader());
|
||||
|
||||
}
|
||||
|
||||
public abstract Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest, Object[] theMethodParams) throws InvalidRequestException, InternalErrorException;
|
||||
|
||||
/**
|
||||
|
@ -378,10 +406,6 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
|
|||
return true;
|
||||
}
|
||||
|
||||
protected void setResourceName(String theResourceName) {
|
||||
myResourceName = theResourceName;
|
||||
}
|
||||
|
||||
public enum MethodReturnTypeEnum {
|
||||
BUNDLE,
|
||||
BUNDLE_PROVIDER,
|
||||
|
|
|
@ -15,6 +15,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
|
@ -66,10 +67,12 @@ public class InterceptorDstu2_1Test {
|
|||
when(myInterceptor1.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||
|
||||
when(myInterceptor2.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
|
||||
//@formatter:off
|
||||
String input =
|
||||
"{\n" +
|
||||
|
@ -107,7 +110,9 @@ public class InterceptorDstu2_1Test {
|
|||
order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(opTypeCapt.capture(), arTypeCapt.capture());
|
||||
order.verify(myInterceptor2, times(1)).incomingRequestPreHandled(any(RestOperationTypeEnum.class), any(ActionRequestDetails.class));
|
||||
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class));
|
||||
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(IResource.class));
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
|
||||
// Avoid concurrency issues
|
||||
Thread.sleep(500);
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.concurrent.TimeUnit;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
|
@ -153,7 +154,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServletRequest(req);
|
||||
|
||||
// true means it decided to not handle the request..
|
||||
assertTrue(ic.outgoingResponse(reqDetails, resource, req, resp));
|
||||
assertTrue(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
|
||||
}
|
||||
|
||||
|
@ -408,7 +409,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServletRequest(req);
|
||||
|
||||
// false means it decided to handle the request..
|
||||
assertFalse(ic.outgoingResponse(reqDetails, resource, req, resp));
|
||||
assertFalse(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -442,7 +443,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServletRequest(req);
|
||||
|
||||
// false means it decided to handle the request..
|
||||
assertFalse(ic.outgoingResponse(reqDetails, resource, req, resp));
|
||||
assertFalse(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -475,7 +476,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServletRequest(req);
|
||||
|
||||
// true means it decided to not handle the request..
|
||||
assertTrue(ic.outgoingResponse(reqDetails, resource, req, resp));
|
||||
assertTrue(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
|
||||
}
|
||||
|
||||
|
@ -504,7 +505,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServer(new RestfulServer(ourCtx));
|
||||
reqDetails.setServletRequest(req);
|
||||
|
||||
assertFalse(ic.outgoingResponse(reqDetails, resource, req, resp));
|
||||
assertFalse(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
|
||||
String output = sw.getBuffer().toString();
|
||||
ourLog.info(output);
|
||||
|
@ -540,7 +541,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServer(new RestfulServer(ourCtx));
|
||||
reqDetails.setServletRequest(req);
|
||||
|
||||
assertFalse(ic.outgoingResponse(reqDetails, resource, req, resp));
|
||||
assertFalse(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
|
||||
String output = sw.getBuffer().toString();
|
||||
ourLog.info(output);
|
||||
|
@ -579,7 +580,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServer(server);
|
||||
reqDetails.setServletRequest(req);
|
||||
|
||||
assertFalse(ic.outgoingResponse(reqDetails, resource, req, resp));
|
||||
assertFalse(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
|
||||
String output = sw.getBuffer().toString();
|
||||
ourLog.info(output);
|
||||
|
@ -615,7 +616,7 @@ public class ResponseHighlightingInterceptorTest {
|
|||
reqDetails.setServletRequest(req);
|
||||
|
||||
// True here means the interceptor didn't handle the request, because HTML wasn't the top ranked accept header
|
||||
assertTrue(ic.outgoingResponse(reqDetails, resource, req, resp));
|
||||
assertTrue(ic.outgoingResponse(reqDetails, new ResponseDetails(resource), req, resp));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -9,6 +9,7 @@ import ca.uhn.fhir.rest.annotation.*;
|
|||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
|
@ -213,6 +214,7 @@ public class ServerActionInterceptorTest {
|
|||
when(ourInterceptor.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(IBaseResource.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(ourInterceptor.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
}
|
||||
|
||||
public static class PlainProvider {
|
||||
|
|
|
@ -2,13 +2,12 @@ package ca.uhn.fhir.rest.server;
|
|||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Validate;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
|
@ -19,6 +18,8 @@ import ca.uhn.fhir.util.PortUtil;
|
|||
import ca.uhn.fhir.util.TestUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
|
@ -28,7 +29,7 @@ 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.hamcrest.core.StringContains;
|
||||
import org.hl7.fhir.dstu3.model.IdType;
|
||||
import org.hl7.fhir.dstu3.model.OperationOutcome;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
@ -38,9 +39,11 @@ import org.mockito.InOrder;
|
|||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsString;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
@ -81,6 +84,35 @@ public class InterceptorDstu3Test {
|
|||
"}";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testModifyResponse() throws IOException {
|
||||
InterceptorAdapter interceptor = new InterceptorAdapter() {
|
||||
@Override
|
||||
public boolean outgoingResponse(RequestDetails theRequestDetails, ResponseDetails theResponseDetails, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse) throws AuthenticationException {
|
||||
Patient retVal = new Patient();
|
||||
retVal.setId(theResponseDetails.getResponseResource().getIdElement());
|
||||
retVal.addName().setFamily("NAME1");
|
||||
theResponseDetails.setResponseResource(retVal);
|
||||
theResponseDetails.setResponseCode(202);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
ourServlet.registerInterceptor(interceptor);
|
||||
try {
|
||||
|
||||
HttpGet get = new HttpGet("http://localhost:" + ourPort + "/Patient/1");
|
||||
try (CloseableHttpResponse status = ourClient.execute(get)) {
|
||||
String response = IOUtils.toString(status.getEntity().getContent(), Constants.CHARSET_UTF8);
|
||||
assertThat(response, containsString("NAME1"));
|
||||
assertEquals(202, status.getStatusLine().getStatusCode());
|
||||
assertEquals("Accepted", status.getStatusLine().getReasonPhrase());
|
||||
}
|
||||
|
||||
} finally {
|
||||
ourServlet.unregisterInterceptor(interceptor);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResourceResponseIncluded() throws Exception {
|
||||
ourServlet.setInterceptors(myInterceptor1, myInterceptor2);
|
||||
|
@ -88,9 +120,11 @@ public class InterceptorDstu3Test {
|
|||
when(myInterceptor1.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor2.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||
when(myInterceptor2.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
|
||||
String input = createInput();
|
||||
|
||||
|
@ -109,7 +143,9 @@ public class InterceptorDstu3Test {
|
|||
order.verify(myInterceptor1, times(1)).incomingRequestPreHandled(opTypeCapt.capture(), arTypeCapt.capture());
|
||||
order.verify(myInterceptor2, times(1)).incomingRequestPreHandled(any(RestOperationTypeEnum.class), any(ActionRequestDetails.class));
|
||||
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(IBaseResource.class));
|
||||
order.verify(myInterceptor2, times(1)).outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(IBaseResource.class));
|
||||
order.verify(myInterceptor1, times(1)).outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
|
||||
// Avoid concurrency issues
|
||||
Thread.sleep(500);
|
||||
|
@ -123,21 +159,6 @@ public class InterceptorDstu3Test {
|
|||
assertNotNull(arTypeCapt.getValue().getResource());
|
||||
}
|
||||
|
||||
public void testModifyResponse() {
|
||||
InterceptorAdapter interceptor = new InterceptorAdapter(){
|
||||
@Override
|
||||
public boolean incomingRequestPostProcessed(RequestDetails theRequestDetails, HttpServletRequest theRequest, HttpServletResponse theResponse) throws AuthenticationException {
|
||||
ServletRequestDetails srd = (ServletRequestDetails)theRequestDetails;
|
||||
String input = new String(srd.loadRequestContents(), Constants.CHARSET_UTF8);
|
||||
assertThat(input, StringContains.containsString("\"active\":true"));
|
||||
|
||||
String newInput = createInput().replace("true", "false");
|
||||
srd.setRequestContents(newInput.getBytes(Constants.CHARSET_UTF8));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseWithNothing() throws Exception {
|
||||
ourServlet.setInterceptors(myInterceptor1);
|
||||
|
@ -145,6 +166,7 @@ public class InterceptorDstu3Test {
|
|||
when(myInterceptor1.incomingRequestPreProcessed(any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.incomingRequestPostProcessed(any(RequestDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(IResource.class))).thenReturn(true);
|
||||
when(myInterceptor1.outgoingResponse(any(RequestDetails.class), any(ResponseDetails.class), any(HttpServletRequest.class), any(HttpServletResponse.class))).thenReturn(true);
|
||||
|
||||
String input = createInput();
|
||||
|
||||
|
@ -240,7 +262,6 @@ public class InterceptorDstu3Test {
|
|||
|
||||
public static class DummyPatientResourceProvider implements IResourceProvider {
|
||||
|
||||
|
||||
@Create()
|
||||
public MethodOutcome create(@ResourceParam Patient theResource) {
|
||||
ourLastPatient = theResource;
|
||||
|
@ -252,6 +273,14 @@ public class InterceptorDstu3Test {
|
|||
return Patient.class;
|
||||
}
|
||||
|
||||
@Read
|
||||
public Patient read(@IdParam IdType theId) {
|
||||
Patient retVal = new Patient();
|
||||
retVal.setId(theId);
|
||||
retVal.addName().setFamily("NAME0");
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Validate()
|
||||
public MethodOutcome validate(@ResourceParam Patient theResource) {
|
||||
return new MethodOutcome();
|
||||
|
|
|
@ -8,15 +8,15 @@
|
|||
<body>
|
||||
<release version="3.3.0" date="TBD">
|
||||
<action type="add">
|
||||
This release corrects an ineffiency in the JPA Server, but requires a schema
|
||||
This release corrects an inefficiency in the JPA Server, but requires a schema
|
||||
change in order to update. Prior to this version of HAPI FHIR, a CLOB column
|
||||
containing the complete resource body was stored in two
|
||||
tables: HFJ_RESOURCE and HFJ_RES_VER. Because the same content was stored in two
|
||||
places, the database consumed more space than is needed to.
|
||||
<![CDATA[<br/><br/>]]>
|
||||
In order to reduce this duplication, the columns have been removed from the
|
||||
HFJ_RESOURCE column. This means that on any database that is being upgraded
|
||||
to HAPI FHIR 3.2.0, you will need to remove the columns
|
||||
In order to reduce this duplication, two columns have been removed from the
|
||||
HFJ_RESOURCE table. This means that on any database that is being upgraded
|
||||
to HAPI FHIR 3.3.0+, you will need to remove the columns
|
||||
<![CDATA[<code>RES_TEXT</code> and <code>RES_ENCODING</code>]]> (or
|
||||
set them to nullable if you want an easy means of rolling back). Naturally
|
||||
you should back your database up prior to making this change.
|
||||
|
@ -24,7 +24,7 @@
|
|||
<action type="fix">
|
||||
Fix a crash in the JSON parser when parsing extensions on repeatable
|
||||
elements (e.g. Patient.address.line) where there is an extension on the
|
||||
first repetion but not on subsequent repetitions of the
|
||||
first repetition but not on subsequent repetitions of the
|
||||
repeatable primitive. Thanks to Igor Sirkovich for providing a
|
||||
test case!
|
||||
</action>
|
||||
|
@ -32,6 +32,19 @@
|
|||
Fix an issue where the JPA server crashed while attempting to normalize string values
|
||||
containing Korean text. Thanks to GitHub user @JoonggeonLee for reporting!
|
||||
</action>
|
||||
<action type="add">
|
||||
A new method overload has been added to IServerInterceptor:
|
||||
<![CDATA[
|
||||
<code>outgoingResponse(RequestDetails, ResponseDetails, HttpServletRequest, HttpServletResponse)
|
||||
]]>. This new method allows an interceptor to completely replace
|
||||
the resource being returned with a different resource instance, or
|
||||
to modify the HTTP Status Code being returned. All other "outgoingResponse"
|
||||
methods have been deprecated and are recommended to be migrated
|
||||
to the new method. This new method (with its RequestDetails and ResponseDetails
|
||||
parameters) should be flexible enough to
|
||||
accommodate future needs which means that this should be the last
|
||||
time we have to change it.
|
||||
</action>
|
||||
</release>
|
||||
<release version="3.2.0" date="2018-01-13">
|
||||
<action type="add">
|
||||
|
|
Loading…
Reference in New Issue