Allow to set additional Http headers directly on the IClientExecutable
This commit is contained in:
parent
66a669949d
commit
cd8b2feb2f
|
@ -88,6 +88,21 @@ public interface IClientExecutable<T extends IClientExecutable<?, Y>, Y> {
|
|||
*/
|
||||
T encodedXml();
|
||||
|
||||
/**
|
||||
* Set a HTTP header not explicitly defined in FHIR but commonly used in real-world scenarios. One
|
||||
* important example is to set the Authorization header (e.g. Basic Auth or OAuth2-based Bearer auth),
|
||||
* which tends to be cumbersome using {@link ca.uhn.fhir.rest.client.api.IClientInterceptor IClientInterceptors},
|
||||
* particularly when REST clients shall be reused and are thus supposed to remain stateless.
|
||||
* <p>It is the responsibility of the caller to care for proper encoding of the header value, e.g.
|
||||
* using Base64.</p>
|
||||
* <p>This is a short-cut alternative to using a corresponding client interceptor</p>
|
||||
*
|
||||
* @param theHeaderName header name
|
||||
* @param theHeaderValue header value
|
||||
* @return
|
||||
*/
|
||||
T withAdditionalHeader(String theHeaderName, String theHeaderValue);
|
||||
|
||||
/**
|
||||
* Actually execute the client operation
|
||||
*/
|
||||
|
|
|
@ -138,7 +138,7 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
public <T extends IBaseResource> T fetchResourceFromUrl(Class<T> theResourceType, String theUrl) {
|
||||
BaseHttpClientInvocation clientInvocation = new HttpGetClientInvocation(getFhirContext(), theUrl);
|
||||
ResourceResponseHandler<T> binding = new ResourceResponseHandler<T>(theResourceType);
|
||||
return invokeClient(getFhirContext(), binding, clientInvocation, null, false, false, null, null, null, null);
|
||||
return invokeClient(getFhirContext(), binding, clientInvocation, null, false, false, null, null, null, null, null);
|
||||
}
|
||||
|
||||
void forceConformanceCheck() {
|
||||
|
@ -215,11 +215,12 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
}
|
||||
|
||||
<T> T invokeClient(FhirContext theContext, IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation, boolean theLogRequestAndResponse) {
|
||||
return invokeClient(theContext, binding, clientInvocation, null, null, theLogRequestAndResponse, null, null, null, null);
|
||||
return invokeClient(theContext, binding, clientInvocation, null, null, theLogRequestAndResponse, null, null, null, null, null);
|
||||
}
|
||||
|
||||
<T> T invokeClient(FhirContext theContext, IClientResponseHandler<T> binding, BaseHttpClientInvocation clientInvocation, EncodingEnum theEncoding, Boolean thePrettyPrint,
|
||||
boolean theLogRequestAndResponse, SummaryEnum theSummaryMode, Set<String> theSubsetElements, CacheControlDirective theCacheControlDirective, String theCustomAcceptHeader) {
|
||||
boolean theLogRequestAndResponse, SummaryEnum theSummaryMode, Set<String> theSubsetElements, CacheControlDirective theCacheControlDirective, String theCustomAcceptHeader,
|
||||
Map<String, List<String>> theCustomHeaders) {
|
||||
|
||||
if (!myDontValidateConformance) {
|
||||
myFactory.validateServerBaseIfConfiguredToDoSo(myUrlBase, myClient, this);
|
||||
|
@ -280,6 +281,14 @@ public abstract class BaseClient implements IRestfulClient {
|
|||
}
|
||||
}
|
||||
|
||||
if (theCustomHeaders != null) {
|
||||
for (Map.Entry<String, List<String>> customHeader: theCustomHeaders.entrySet()) {
|
||||
for (String value: customHeader.getValue()) {
|
||||
httpRequest.addHeader(customHeader.getKey(), value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theLogRequestAndResponse) {
|
||||
ourLog.info("Client invoking: {}", httpRequest);
|
||||
String body = httpRequest.getRequestBodyFromStream();
|
||||
|
|
|
@ -54,7 +54,6 @@ import org.hl7.fhir.instance.model.api.*;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
|
@ -98,7 +97,8 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
}
|
||||
|
||||
private <T extends IBaseResource> T doReadOrVRead(final Class<T> theType, IIdType theId, boolean theVRead, ICallable<T> theNotModifiedHandler, String theIfVersionMatches, Boolean thePrettyPrint,
|
||||
SummaryEnum theSummary, EncodingEnum theEncoding, Set<String> theSubsetElements, String theCustomAcceptHeaderValue) {
|
||||
SummaryEnum theSummary, EncodingEnum theEncoding, Set<String> theSubsetElements, String theCustomAcceptHeaderValue,
|
||||
Map<String, List<String>> theCustomHeaders) {
|
||||
String resName = toResourceName(theType);
|
||||
IIdType id = theId;
|
||||
if (!id.hasBaseUrl()) {
|
||||
|
@ -131,10 +131,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
ResourceResponseHandler<T> binding = new ResourceResponseHandler<>(theType, (Class<? extends IBaseResource>) null, id, allowHtmlResponse);
|
||||
|
||||
if (theNotModifiedHandler == null) {
|
||||
return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements, null, theCustomAcceptHeaderValue);
|
||||
return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements, null, theCustomAcceptHeaderValue, theCustomHeaders);
|
||||
}
|
||||
try {
|
||||
return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements, null, theCustomAcceptHeaderValue);
|
||||
return invokeClient(myContext, binding, invocation, theEncoding, thePrettyPrint, myLogRequestAndResponse, theSummary, theSubsetElements, null, theCustomAcceptHeaderValue, theCustomHeaders);
|
||||
} catch (NotModifiedException e) {
|
||||
return theNotModifiedHandler.call();
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
@Override
|
||||
public <T extends IBaseResource> T read(final Class<T> theType, UriDt theUrl) {
|
||||
IdDt id = theUrl instanceof IdDt ? ((IdDt) theUrl) : new IdDt(theUrl);
|
||||
return doReadOrVRead(theType, id, false, null, null, false, null, null, null, null);
|
||||
return doReadOrVRead(theType, id, false, null, null, false, null, null, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -303,10 +303,10 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
|
||||
@Override
|
||||
public <T extends IBaseResource> T vread(final Class<T> theType, IdDt theId) {
|
||||
if (theId.hasVersionIdPart() == false) {
|
||||
if (!theId.hasVersionIdPart()) {
|
||||
throw new IllegalArgumentException(myContext.getLocalizer().getMessage(I18N_NO_VERSION_ID_FOR_VREAD, theId.getValue()));
|
||||
}
|
||||
return doReadOrVRead(theType, theId, true, null, null, false, null, null, null, null);
|
||||
return doReadOrVRead(theType, theId, true, null, null, false, null, null, null, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -327,15 +327,17 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
Boolean myPrettyPrint;
|
||||
SummaryEnum mySummaryMode;
|
||||
CacheControlDirective myCacheControlDirective;
|
||||
Map<String, List<String>> myCustomHeaderValues = new HashMap<>();
|
||||
private String myCustomAcceptHeaderValue;
|
||||
private List<Class<? extends IBaseResource>> myPreferResponseTypes;
|
||||
private boolean myQueryLogRequestAndResponse;
|
||||
private HashSet<String> mySubsetElements;
|
||||
private Set<String> mySubsetElements;
|
||||
|
||||
public String getCustomAcceptHeaderValue() {
|
||||
return myCustomAcceptHeaderValue;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T accept(String theHeaderValue) {
|
||||
myCustomAcceptHeaderValue = theHeaderValue;
|
||||
|
@ -350,6 +352,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
return (T) this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T cacheControl(CacheControlDirective theCacheControlDirective) {
|
||||
myCacheControlDirective = theCacheControlDirective;
|
||||
|
@ -367,6 +370,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
return (T) this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T encoded(EncodingEnum theEncoding) {
|
||||
Validate.notNull(theEncoding, "theEncoding must not be null");
|
||||
|
@ -388,6 +392,18 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
return (T) this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T withAdditionalHeader(String theHeaderName, String theHeaderValue) {
|
||||
Objects.requireNonNull(theHeaderName, "headerName cannot be null");
|
||||
Objects.requireNonNull(theHeaderValue, "headerValue cannot be null");
|
||||
if (!myCustomHeaderValues.containsKey(theHeaderName)) {
|
||||
myCustomHeaderValues.put(theHeaderName, new ArrayList<>());
|
||||
}
|
||||
myCustomHeaderValues.get(theHeaderName).add(theHeaderValue);
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
protected EncodingEnum getParamEncoding() {
|
||||
return myParamEncoding;
|
||||
}
|
||||
|
@ -403,7 +419,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
return toTypeList(theDefault);
|
||||
}
|
||||
|
||||
protected HashSet<String> getSubsetElements() {
|
||||
protected Set<String> getSubsetElements() {
|
||||
return mySubsetElements;
|
||||
}
|
||||
|
||||
|
@ -412,7 +428,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
myLastRequest = theInvocation.asHttpRequest(getServerBase(), theParams, getEncoding(), myPrettyPrint);
|
||||
}
|
||||
|
||||
Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse, mySummaryMode, mySubsetElements, myCacheControlDirective, myCustomAcceptHeaderValue);
|
||||
Z resp = invokeClient(myContext, theHandler, theInvocation, myParamEncoding, myPrettyPrint, myQueryLogRequestAndResponse || myLogRequestAndResponse, mySummaryMode, mySubsetElements, myCacheControlDirective, myCustomAcceptHeaderValue, myCustomHeaderValues);
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
@ -612,7 +628,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
Validate.notNull(theResource, "theResource can not be null");
|
||||
IIdType id = theResource.getIdElement();
|
||||
Validate.notNull(id, "theResource.getIdElement() can not be null");
|
||||
if (id.hasResourceType() == false || id.hasIdPart() == false) {
|
||||
if (!id.hasResourceType() || !id.hasIdPart()) {
|
||||
throw new IllegalArgumentException("theResource.getId() must contain a resource type and logical ID at a minimum (e.g. Patient/1234), found: " + id.getValue());
|
||||
}
|
||||
myId = id;
|
||||
|
@ -622,7 +638,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
@Override
|
||||
public IDeleteTyped resourceById(IIdType theId) {
|
||||
Validate.notNull(theId, "theId can not be null");
|
||||
if (theId.hasResourceType() == false || theId.hasIdPart() == false) {
|
||||
if (!theId.hasResourceType() || !theId.hasIdPart()) {
|
||||
throw new IllegalArgumentException("theId must contain a resource type and logical ID at a minimum (e.g. Patient/1234)found: " + theId.getValue());
|
||||
}
|
||||
myId = theId;
|
||||
|
@ -773,7 +789,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
|
||||
@Override
|
||||
public IHistoryUntyped onInstance(IIdType theId) {
|
||||
if (theId.hasResourceType() == false) {
|
||||
if (!theId.hasResourceType()) {
|
||||
throw new IllegalArgumentException("Resource ID does not have a resource type: " + theId.getValue());
|
||||
}
|
||||
myId = theId;
|
||||
|
@ -849,7 +865,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
continue;
|
||||
}
|
||||
String relation = ((IPrimitiveType<?>) rel.get(0)).getValueAsString();
|
||||
if (theWantRel.equals(relation) || (theWantRel == PREVIOUS && PREV.equals(relation))) {
|
||||
if (theWantRel.equals(relation) || (PREVIOUS.equals(theWantRel) && PREV.equals(relation))) {
|
||||
List<IBase> urls = linkDef.getChildByName("url").getAccessor().getValues(nextLink);
|
||||
if (urls == null || urls.isEmpty()) {
|
||||
continue;
|
||||
|
@ -1514,9 +1530,9 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
@Override
|
||||
public Object execute() {// AAA
|
||||
if (myId.hasVersionIdPart()) {
|
||||
return doReadOrVRead(myType.getImplementingClass(), myId, true, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements(), getCustomAcceptHeaderValue());
|
||||
return doReadOrVRead(myType.getImplementingClass(), myId, true, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements(), getCustomAcceptHeaderValue(), myCustomHeaderValues);
|
||||
}
|
||||
return doReadOrVRead(myType.getImplementingClass(), myId, false, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements(), getCustomAcceptHeaderValue());
|
||||
return doReadOrVRead(myType.getImplementingClass(), myId, false, myNotModifiedHandler, myIfVersionMatches, myPrettyPrint, mySummaryMode, myParamEncoding, getSubsetElements(), getCustomAcceptHeaderValue(), myCustomHeaderValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2256,7 +2272,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
|
|||
|
||||
private static void addParam(Map<String, List<String>> params, String parameterName, String parameterValue) {
|
||||
if (!params.containsKey(parameterName)) {
|
||||
params.put(parameterName, new ArrayList<>());
|
||||
params.put(parameterName, new ArrayList<String>());
|
||||
}
|
||||
params.get(parameterName).add(parameterValue);
|
||||
}
|
||||
|
|
|
@ -33,12 +33,15 @@ import java.util.Objects;
|
|||
|
||||
/**
|
||||
* This interceptor adds arbitrary header values to requests made by the client.
|
||||
*
|
||||
* This is now also possible directly on the Fluent Client API by calling
|
||||
* {@link ca.uhn.fhir.rest.gclient.IClientExecutable#withAdditionalHeader(String, String)}
|
||||
*/
|
||||
public class AdditionalRequestHeadersInterceptor implements IClientInterceptor {
|
||||
private final Map<String, List<String>> additionalHttpHeaders = new HashMap<>();
|
||||
|
||||
public AdditionalRequestHeadersInterceptor() {
|
||||
this(new HashMap<String, List<String>>());
|
||||
this(new HashMap<>());
|
||||
}
|
||||
|
||||
public AdditionalRequestHeadersInterceptor(Map<String, List<String>> additionalHttpHeaders) {
|
||||
|
@ -84,7 +87,7 @@ public class AdditionalRequestHeadersInterceptor implements IClientInterceptor {
|
|||
*/
|
||||
private List<String> getHeaderValues(String headerName) {
|
||||
if (additionalHttpHeaders.get(headerName) == null) {
|
||||
additionalHttpHeaders.put(headerName, new ArrayList<String>());
|
||||
additionalHttpHeaders.put(headerName, new ArrayList<>());
|
||||
}
|
||||
return additionalHttpHeaders.get(headerName);
|
||||
}
|
||||
|
|
|
@ -315,7 +315,7 @@ public class GenericClientTest {
|
|||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
MethodOutcome outcome = client.create().resource(p1).execute();
|
||||
MethodOutcome outcome = client.create().resource(p1).withAdditionalHeader("myHeaderName", "myHeaderValue").execute();
|
||||
assertEquals("44", outcome.getId().getIdPart());
|
||||
assertEquals("22", outcome.getId().getVersionIdPart());
|
||||
|
||||
|
@ -325,6 +325,7 @@ public class GenericClientTest {
|
|||
assertEquals("POST", capt.getValue().getMethod());
|
||||
assertEquals(1, capt.getAllValues().get(count).getHeaders(Constants.HEADER_CONTENT_TYPE).length);
|
||||
assertEquals(EncodingEnum.XML.getResourceContentTypeNonLegacy() + Constants.HEADER_SUFFIX_CT_UTF_8, capt.getAllValues().get(count).getFirstHeader(Constants.HEADER_CONTENT_TYPE).getValue());
|
||||
assertEquals("myHeaderValue", capt.getValue().getFirstHeader("myHeaderName").getValue());
|
||||
count++;
|
||||
|
||||
/*
|
||||
|
@ -415,17 +416,20 @@ public class GenericClientTest {
|
|||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
OperationOutcome outcome = (OperationOutcome) client.delete().resourceById("Patient", "123").execute();
|
||||
OperationOutcome outcome = (OperationOutcome) client.delete().resourceById("Patient", "123")
|
||||
.withAdditionalHeader("myHeaderName", "myHeaderValue").execute();
|
||||
|
||||
assertEquals("http://example.com/fhir/Patient/123", capt.getValue().getURI().toString());
|
||||
assertEquals("DELETE", capt.getValue().getMethod());
|
||||
assertEquals("testDelete01", outcome.getIssueFirstRep().getLocation().get(0).getValue());
|
||||
assertEquals("myHeaderValue", capt.getValue().getFirstHeader("myHeaderName").getValue());
|
||||
|
||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader("LKJHLKJGLKJKLL"), Charset.forName("UTF-8")));
|
||||
outcome = (OperationOutcome) client.delete().resourceById(new IdType("Location", "123", "456")).prettyPrint().encodedJson().execute();
|
||||
|
||||
assertEquals("http://example.com/fhir/Location/123?_pretty=true", capt.getAllValues().get(1).getURI().toString());
|
||||
assertEquals("DELETE", capt.getValue().getMethod());
|
||||
|
||||
assertEquals(null, outcome);
|
||||
|
||||
}
|
||||
|
@ -455,8 +459,10 @@ public class GenericClientTest {
|
|||
.history()
|
||||
.onServer()
|
||||
.andReturnBundle(Bundle.class)
|
||||
.withAdditionalHeader("myHeaderName", "myHeaderValue")
|
||||
.execute();
|
||||
assertEquals("http://example.com/fhir/_history", capt.getAllValues().get(idx).getURI().toString());
|
||||
assertEquals("myHeaderValue", capt.getValue().getFirstHeader("myHeaderName").getValue());
|
||||
assertEquals(1, response.getEntry().size());
|
||||
idx++;
|
||||
|
||||
|
@ -464,9 +470,13 @@ public class GenericClientTest {
|
|||
.history()
|
||||
.onType(Patient.class)
|
||||
.andReturnBundle(Bundle.class)
|
||||
.withAdditionalHeader("myHeaderName", "myHeaderValue1")
|
||||
.withAdditionalHeader("myHeaderName", "myHeaderValue2")
|
||||
.execute();
|
||||
assertEquals("http://example.com/fhir/Patient/_history", capt.getAllValues().get(idx).getURI().toString());
|
||||
assertEquals(1, response.getEntry().size());
|
||||
assertEquals("myHeaderValue1", capt.getValue().getHeaders("myHeaderName")[0].getValue());
|
||||
assertEquals("myHeaderValue2", capt.getValue().getHeaders("myHeaderName")[1].getValue());
|
||||
idx++;
|
||||
|
||||
response = client
|
||||
|
@ -1145,10 +1155,12 @@ public class GenericClientTest {
|
|||
.forResource(Patient.class)
|
||||
.withTag("urn:foo", "123")
|
||||
.withTag("urn:bar", "456")
|
||||
.withAdditionalHeader("myHeaderName", "myHeaderValue")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertEquals("http://example.com/fhir/Patient?_tag=urn%3Afoo%7C123&_tag=urn%3Abar%7C456", capt.getValue().getURI().toString());
|
||||
assertEquals("myHeaderValue", capt.getValue().getFirstHeader("myHeaderName").getValue());
|
||||
|
||||
}
|
||||
|
||||
|
@ -1169,10 +1181,12 @@ public class GenericClientTest {
|
|||
Bundle response = client.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.IDENTIFIER.exactly().systemAndCode("http://example.com/fhir", "ZZZ"))
|
||||
.withAdditionalHeader("myHeaderName", "myHeaderValue")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertEquals("http://example.com/fhir/Patient?identifier=http%3A%2F%2Fexample.com%2Ffhir%7CZZZ", capt.getValue().getURI().toString());
|
||||
assertEquals("myHeaderValue", capt.getValue().getFirstHeader("myHeaderName").getValue());
|
||||
|
||||
when(myHttpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8")));
|
||||
response = client.search()
|
||||
|
|
Loading…
Reference in New Issue