From c95af33c4523f4824333529252adb15fce5de230 Mon Sep 17 00:00:00 2001
From: Dmytro Rud
Date: Mon, 23 Dec 2024 10:29:53 +0100
Subject: [PATCH] #458: Possibility to set outgoing HTTP headers in FHIR
consumers
---
.../ihe/fhir/AbstractBundleProvider.java | 16 ++++---
.../ihe/fhir/AbstractPlainProvider.java | 34 +++++++++-----
.../commons/ihe/fhir/EagerBundleProvider.java | 9 ++--
.../ipf/commons/ihe/fhir/FhirProvider.java | 15 +++++++
.../commons/ihe/fhir/LazyBundleProvider.java | 11 +++--
.../ipf/commons/ihe/fhir/RequestConsumer.java | 36 ++++++++-------
.../commons/ihe/fhir/SharedFhirProvider.java | 8 +++-
.../ihe/fhir/EagerBundleProviderTest.java | 7 +--
.../ihe/fhir/LazyBundleProviderTest.java | 23 +++++-----
.../camel/ihe/fhir/core/FhirConsumer.java | 44 +++++++++++--------
.../chppqm/LoggingInterceptorFactory.java | 33 ++++++++++++++
.../ihe/fhir/chppqm/chppq3/ChPpq3Test.java | 5 ++-
.../chppqm/chppq3/ChPpq3TestRouteBuilder.java | 4 +-
.../ihe/fhir/chppqm/chppq5/ChPpq5Test.java | 2 +-
.../chppqm/chppq5/ChPpq5TestRouteBuilder.java | 3 ++
.../r4/chppqm/src/test/resources/ch-ppq5.xml | 2 +
.../r4/chppqm/src/test/resources/log4j2.xml | 1 +
src/site/changes.xml | 3 ++
18 files changed, 179 insertions(+), 77 deletions(-)
create mode 100644 platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/LoggingInterceptorFactory.java
diff --git a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/AbstractBundleProvider.java b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/AbstractBundleProvider.java
index fad921c496..0a146ddf66 100644
--- a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/AbstractBundleProvider.java
+++ b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/AbstractBundleProvider.java
@@ -18,6 +18,7 @@
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
+import jakarta.servlet.http.HttpServletResponse;
import org.hl7.fhir.instance.model.api.IBaseResource;
import java.util.HashMap;
@@ -35,16 +36,18 @@ public abstract class AbstractBundleProvider implements IBundleProvider {
private final Object payload;
private final Map headers;
private final boolean sort;
+ protected final HttpServletResponse httpServletResponse;
- public AbstractBundleProvider(RequestConsumer consumer, Object payload, Map headers) {
- this(consumer, false, payload, headers);
+ public AbstractBundleProvider(RequestConsumer consumer, Object payload, Map headers, HttpServletResponse httpServletResponse) {
+ this(consumer, false, payload, headers, httpServletResponse);
}
- public AbstractBundleProvider(RequestConsumer consumer, boolean sort, Object payload, Map headers) {
+ public AbstractBundleProvider(RequestConsumer consumer, boolean sort, Object payload, Map headers, HttpServletResponse httpServletResponse) {
this.consumer = consumer;
this.payload = payload;
this.headers = headers;
this.sort = sort;
+ this.httpServletResponse = httpServletResponse;
}
@Override
@@ -57,8 +60,11 @@ public Integer preferredPageSize() {
return null;
}
- protected List obtainResources(Object payload, Map parameters) {
- return consumer.handleBundleRequest(payload, parameters);
+ protected List obtainResources(Object payload, Map inHeaders) {
+ HashMap outHeaders = new HashMap<>();
+ List resources = consumer.handleBundleRequest(payload, inHeaders, outHeaders);
+ FhirProvider.processOutHeaders(outHeaders, httpServletResponse);
+ return resources;
}
protected RequestConsumer getConsumer() {
diff --git a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/AbstractPlainProvider.java b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/AbstractPlainProvider.java
index 90937acf29..520d19dffe 100644
--- a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/AbstractPlainProvider.java
+++ b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/AbstractPlainProvider.java
@@ -26,6 +26,8 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
+
+import java.util.HashMap;
import java.util.List;
import java.util.Optional;
@@ -91,8 +93,11 @@ protected final R requestResource(
RequestDetails requestDetails) {
var consumer = getRequestConsumer(requestDetails).orElseThrow(() ->
new IllegalStateException("Consumer is not initialized"));
- var headers = enrichParameters(parameters, httpServletRequest, requestDetails);
- return consumer.handleResourceRequest(payload, headers, resultType);
+ var inHeaders = enrichParameters(parameters, httpServletRequest, requestDetails);
+ var outHeaders = new HashMap();
+ R resource = consumer.handleResourceRequest(payload, inHeaders, outHeaders, resultType);
+ processOutHeaders(outHeaders, httpServletResponse);
+ return resource;
}
/**
@@ -114,17 +119,20 @@ protected final List requestBundle(
RequestDetails requestDetails) {
var consumer = getRequestConsumer(requestDetails).orElseThrow(() ->
new IllegalStateException("Consumer is not initialized"));
- var headers = enrichParameters(parameters, httpServletRequest, requestDetails);
+ var inHeaders = enrichParameters(parameters, httpServletRequest, requestDetails);
if (resourceType != null) {
- headers.put(Constants.FHIR_RESOURCE_TYPE_HEADER, resourceType);
+ inHeaders.put(Constants.FHIR_RESOURCE_TYPE_HEADER, resourceType);
}
- return consumer.handleBundleRequest(payload, headers);
+ var outHeaders = new HashMap();
+ List resources = consumer.handleBundleRequest(payload, inHeaders, outHeaders);
+ processOutHeaders(outHeaders, httpServletResponse);
+ return resources;
}
/**
* Requests a {@link IBundleProvider} that takes over the responsibility to fetch requested
* bundles. The type of the returned {@link IBundleProvider} instance is determined
- * by the {@link #consumer RequestConsumer} impelmentation.
+ * by the {@link #consumer RequestConsumer} implementation.
*
* @param payload FHIR request resource (often null)
* @param searchParameters FHIR search parameters
@@ -142,11 +150,12 @@ protected final IBundleProvider requestBundleProvider(
RequestDetails requestDetails) {
var consumer = getRequestConsumer(requestDetails).orElseThrow(() ->
new IllegalStateException("Consumer is not initialized"));
- var headers = enrichParameters(searchParameters, httpServletRequest, requestDetails);
+ var inHeaders = enrichParameters(searchParameters, httpServletRequest, requestDetails);
if (resourceType != null) {
- headers.put(Constants.FHIR_RESOURCE_TYPE_HEADER, resourceType);
+ inHeaders.put(Constants.FHIR_RESOURCE_TYPE_HEADER, resourceType);
}
- return consumer.handleBundleProviderRequest(payload, headers);
+ IBundleProvider bundleProvider = consumer.handleBundleProviderRequest(payload, inHeaders, httpServletResponse);
+ return bundleProvider;
}
/**
@@ -166,8 +175,11 @@ protected final MethodOutcome requestAction(
RequestDetails requestDetails) {
var consumer = getRequestConsumer(requestDetails).orElseThrow(() ->
new IllegalStateException("Consumer is not initialized"));
- var headers = enrichParameters(parameters, httpServletRequest, requestDetails);
- return consumer.handleAction(payload, headers);
+ var inHeaders = enrichParameters(parameters, httpServletRequest, requestDetails);
+ var outHeaders = new HashMap();
+ MethodOutcome outcome = consumer.handleAction(payload, inHeaders, outHeaders);
+ processOutHeaders(outHeaders, httpServletResponse);
+ return outcome;
}
diff --git a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/EagerBundleProvider.java b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/EagerBundleProvider.java
index f2aac672a8..1749213f02 100644
--- a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/EagerBundleProvider.java
+++ b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/EagerBundleProvider.java
@@ -16,6 +16,7 @@
package org.openehealth.ipf.commons.ihe.fhir;
+import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -33,12 +34,12 @@ public class EagerBundleProvider extends AbstractBundleProvider {
private transient List resources;
- public EagerBundleProvider(RequestConsumer consumer, Object payload, Map headers) {
- this(consumer, false, payload, headers);
+ public EagerBundleProvider(RequestConsumer consumer, Object payload, Map headers, HttpServletResponse httpServletResponse) {
+ this(consumer, false, payload, headers, httpServletResponse);
}
- public EagerBundleProvider(RequestConsumer consumer, boolean sort, Object payload, Map headers) {
- super(consumer, sort, payload, headers);
+ public EagerBundleProvider(RequestConsumer consumer, boolean sort, Object payload, Map headers, HttpServletResponse httpServletResponse) {
+ super(consumer, sort, payload, headers, httpServletResponse);
}
@Override
diff --git a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/FhirProvider.java b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/FhirProvider.java
index 5351d0f82e..ad0b958781 100644
--- a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/FhirProvider.java
+++ b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/FhirProvider.java
@@ -20,6 +20,8 @@
import ca.uhn.fhir.rest.api.server.RequestDetails;
import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
import java.io.Serializable;
import java.util.*;
@@ -140,4 +142,17 @@ private static Map> extractHttpHeaders(HttpServletRequest h
}
return result;
}
+
+ public static void processOutHeaders(Map outHeaders, HttpServletResponse httpServletResponse) {
+ var httpHeadersObject = outHeaders.get(Constants.HTTP_OUTGOING_HEADERS);
+ if (httpHeadersObject instanceof Map) {
+ var headers = (Map>) httpHeadersObject;
+ for (var entry : headers.entrySet()) {
+ for (var value : entry.getValue()) {
+ httpServletResponse.addHeader(entry.getKey(), value);
+ }
+ }
+ }
+ }
+
}
diff --git a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/LazyBundleProvider.java b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/LazyBundleProvider.java
index 8d108385ed..55eb15f1b3 100644
--- a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/LazyBundleProvider.java
+++ b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/LazyBundleProvider.java
@@ -20,6 +20,7 @@
import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.TreeRangeSet;
+import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.slf4j.Logger;
@@ -65,9 +66,10 @@ public class LazyBundleProvider extends AbstractBundleProvider {
* @param cacheResults cache results. So far, only the result set size is cached
* @param payload incoming payload
* @param headers incoming headers
+ * @param httpServletResponse HTTP servlet response
*/
- public LazyBundleProvider(RequestConsumer consumer, boolean cacheResults, Object payload, Map headers) {
- this(consumer, cacheResults, false, payload, headers);
+ public LazyBundleProvider(RequestConsumer consumer, boolean cacheResults, Object payload, Map headers, HttpServletResponse httpServletResponse) {
+ this(consumer, cacheResults, false, payload, headers, httpServletResponse);
}
/**
@@ -78,9 +80,10 @@ public LazyBundleProvider(RequestConsumer consumer, boolean cacheResults, Object
* @param sort sort results
* @param payload incoming payload
* @param headers incoming headers
+ * @param httpServletResponse HTTP servlet response
*/
- public LazyBundleProvider(RequestConsumer consumer, boolean cacheResults, boolean sort, Object payload, Map headers) {
- super(consumer, sort, payload, headers);
+ public LazyBundleProvider(RequestConsumer consumer, boolean cacheResults, boolean sort, Object payload, Map headers, HttpServletResponse httpServletResponse) {
+ super(consumer, sort, payload, headers, httpServletResponse);
this.cacheResults = cacheResults;
}
diff --git a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/RequestConsumer.java b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/RequestConsumer.java
index 138812a897..82951b872c 100644
--- a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/RequestConsumer.java
+++ b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/RequestConsumer.java
@@ -20,6 +20,7 @@
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
+import jakarta.servlet.http.HttpServletResponse;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -37,7 +38,7 @@
* on searches.
*
* - {@link Constants#FHIR_REQUEST_SIZE_ONLY}: if this entry is present, the requester expects only
- * the result size to be returned in an parameter entry with the same name and calls {@link #handleSizeRequest(Object, Map)}
+ * the result size to be returned in an parameter entry with the same name and calls {@link #handleSizeRequest(Object, Map, Map)}
* to do so. If possible, implementations should only request the result size from the backend rather than
* a complete result set.
* - {@link Constants#FHIR_FROM_INDEX} and {@link Constants#FHIR_TO_INDEX}: if these entries are present,
@@ -71,22 +72,24 @@ default boolean test(RequestDetails requestDetails) {
/**
* Handles a Create / Update / Validate / Delete action request.
*
- * @param payload request payload
- * @param headers request parameters, e.g. search parameters
+ * @param payload request payload
+ * @param inHeaders request parameters, e.g. search parameters
+ * @param outHeaders map where Camel response headers will be copied into
* @return result of the action execution
*/
- MethodOutcome handleAction(Object payload, Map headers);
+ MethodOutcome handleAction(Object payload, Map inHeaders, Map outHeaders);
/**
* Handles the request for a resource
*
* @param payload request payload
- * @param headers request parameters, e.g. search parameters
+ * @param inHeaders request parameters, e.g. search parameters
+ * @param outHeaders map where Camel response headers will be copied into
* @param resultType type of the returned resource
* @param type of the returned resource
* @return resource to be returned
*/
- R handleResourceRequest(Object payload, Map headers, Class resultType);
+ R handleResourceRequest(Object payload, Map inHeaders, Map outHeaders, Class resultType);
/**
* Handles the (search) request for a bundle, effectively being a list of resources.
@@ -96,32 +99,35 @@ default boolean test(RequestDetails requestDetails) {
* indicating that only a part of the result is requested. Implementations must return only the requested entries.
*
*
- * @param payload request payload
- * @param headers request parameters, e.g. search parameters or
- * @param type of the returned resources contained in the bundle
+ * @param payload request payload
+ * @param inHeaders request parameters, e.g. search parameters or
+ * @param outHeaders map where Camel response headers will be copied into
+ * @param type of the returned resources contained in the bundle
* @return list of resources to be returned
*/
- List handleBundleRequest(Object payload, Map headers);
+ List handleBundleRequest(Object payload, Map inHeaders, Map outHeaders);
/**
* Handles the request for a bundle provider, effectively constructing a list of resources. The returned
* IBundleProvider takes over the responsibility to fetch the required subset of the result, usually
- * by indirectly calling {@link #handleBundleRequest(Object, Map)} as required.
+ * by indirectly calling {@link #handleBundleRequest(Object, Map, Map)} as required.
*
* @param payload request payload
* @param headers request parameters, e.g. search parameters
+ * @param httpServletResponse HTTP servlet response
* @return a bundle provider
*/
- IBundleProvider handleBundleProviderRequest(Object payload, Map headers);
+ IBundleProvider handleBundleProviderRequest(Object payload, Map headers, HttpServletResponse httpServletResponse);
/**
* Handles transaction requests
*
- * @param payload request payload
- * @param headers request parameters
+ * @param payload request payload
+ * @param inHeaders request parameters
+ * @param outHeaders map where Camel response headers will be copied into
* @return transaction response bundle
*/
- T handleTransactionRequest(Object payload, Map headers, Class bundleClass);
+ T handleTransactionRequest(Object payload, Map inHeaders, Map outHeaders, Class bundleClass);
/**
* Optional method that request the result size of a bundle request. Only used for lazy
diff --git a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/SharedFhirProvider.java b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/SharedFhirProvider.java
index 3bb0814b87..841576b3ba 100644
--- a/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/SharedFhirProvider.java
+++ b/commons/ihe/fhir/core/src/main/java/org/openehealth/ipf/commons/ihe/fhir/SharedFhirProvider.java
@@ -26,6 +26,7 @@
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Optional;
@@ -94,8 +95,11 @@ protected final T requestTransaction(
RequestDetails requestDetails) {
var consumer = getRequestConsumer(requestDetails).orElseThrow(() ->
new InvalidRequestException("Request does not match any consumer or consumers are not initialized"));
- var headers = enrichParameters(null, httpServletRequest, requestDetails);
- return consumer.handleTransactionRequest(payload, headers, bundleClass);
+ var inHeaders = enrichParameters(null, httpServletRequest, requestDetails);
+ var outHeaders = new HashMap();
+ T bundle = consumer.handleTransactionRequest(payload, inHeaders, outHeaders, bundleClass);
+ processOutHeaders(outHeaders, httpServletResponse);
+ return bundle;
}
/**
diff --git a/commons/ihe/fhir/core/src/test/java/org/openehealth/ipf/commons/ihe/fhir/EagerBundleProviderTest.java b/commons/ihe/fhir/core/src/test/java/org/openehealth/ipf/commons/ihe/fhir/EagerBundleProviderTest.java
index 91dd5573ce..d4304ab971 100644
--- a/commons/ihe/fhir/core/src/test/java/org/openehealth/ipf/commons/ihe/fhir/EagerBundleProviderTest.java
+++ b/commons/ihe/fhir/core/src/test/java/org/openehealth/ipf/commons/ihe/fhir/EagerBundleProviderTest.java
@@ -16,6 +16,7 @@
package org.openehealth.ipf.commons.ihe.fhir;
+import io.undertow.servlet.spec.HttpServletResponseImpl;
import org.easymock.EasyMock;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -48,12 +49,12 @@ public void setup() {
}
var payload = new Object();
var headers = new HashMap();
- bundleProvider = new EagerBundleProvider(requestConsumer, payload, headers);
+ bundleProvider = new EagerBundleProvider(requestConsumer, payload, headers, new HttpServletResponseImpl(null, null));
}
@Test
public void testGetSize() {
- EasyMock.expect(requestConsumer.handleBundleRequest(bundleProvider.getPayload(), bundleProvider.getHeaders())).andReturn(response);
+ EasyMock.expect(requestConsumer.handleBundleRequest(bundleProvider.getPayload(), bundleProvider.getHeaders(), new HashMap<>())).andReturn(response);
EasyMock.replay(requestConsumer);
assertEquals(response.size(), bundleProvider.size().intValue());
EasyMock.verify(requestConsumer);
@@ -61,7 +62,7 @@ public void testGetSize() {
@Test
public void testGetResources() {
- EasyMock.expect(requestConsumer.handleBundleRequest(bundleProvider.getPayload(), bundleProvider.getHeaders())).andReturn(response);
+ EasyMock.expect(requestConsumer.handleBundleRequest(bundleProvider.getPayload(), bundleProvider.getHeaders(), new HashMap<>())).andReturn(response);
EasyMock.replay(requestConsumer);
var result = bundleProvider.getResources(10, 30);
Assertions.assertEquals(response.subList(10, 30), result);
diff --git a/commons/ihe/fhir/core/src/test/java/org/openehealth/ipf/commons/ihe/fhir/LazyBundleProviderTest.java b/commons/ihe/fhir/core/src/test/java/org/openehealth/ipf/commons/ihe/fhir/LazyBundleProviderTest.java
index 6cc56fdb86..26367a0932 100644
--- a/commons/ihe/fhir/core/src/test/java/org/openehealth/ipf/commons/ihe/fhir/LazyBundleProviderTest.java
+++ b/commons/ihe/fhir/core/src/test/java/org/openehealth/ipf/commons/ihe/fhir/LazyBundleProviderTest.java
@@ -16,6 +16,7 @@
package org.openehealth.ipf.commons.ihe.fhir;
+import io.undertow.servlet.spec.HttpServletResponseImpl;
import org.easymock.EasyMock;
import org.easymock.IArgumentMatcher;
import org.hl7.fhir.dstu3.model.Patient;
@@ -50,7 +51,7 @@ public void setup() {
}
var payload = new Object();
var headers = new HashMap();
- bundleProvider = new LazyBundleProvider(requestConsumer, true, payload, headers);
+ bundleProvider = new LazyBundleProvider(requestConsumer, true, payload, headers, new HttpServletResponseImpl(null, null));
}
@Test
@@ -63,7 +64,7 @@ public void testGetSize() {
@Test
public void testGetResources() {
- EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(10, 30)))
+ EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(10, 30), eq(new HashMap<>())))
.andReturn(response.subList(10, 30));
EasyMock.replay(requestConsumer);
var result = bundleProvider.getResources(10, 30);
@@ -74,7 +75,7 @@ public void testGetResources() {
@Test
public void testGetResourcesAlreadyCached() {
- EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(10, 30)))
+ EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(10, 30), eq(new HashMap<>())))
.andReturn(response.subList(10, 30));
EasyMock.replay(requestConsumer);
@@ -89,9 +90,9 @@ public void testGetResourcesAlreadyCached() {
@Test
public void testGetResourcesPartlyCached1() {
- EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(10, 30)))
+ EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(10, 30), eq(new HashMap<>())))
.andReturn(response.subList(10, 30));
- EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(30, 40)))
+ EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(30, 40), eq(new HashMap<>())))
.andReturn(response.subList(30, 40));
EasyMock.replay(requestConsumer);
@@ -107,11 +108,11 @@ public void testGetResourcesPartlyCached1() {
@Test
public void testGetResourcesPartlyCached2() {
- EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(10, 30)))
+ EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(10, 30), eq(new HashMap<>())))
.andReturn(response.subList(10, 30));
- EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(5, 10)))
+ EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(5, 10), eq(new HashMap<>())))
.andReturn(response.subList(5, 10));
- EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(30, 35)))
+ EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(30, 35), eq(new HashMap<>())))
.andReturn(response.subList(30, 35));
EasyMock.replay(requestConsumer);
@@ -127,11 +128,11 @@ public void testGetResourcesPartlyCached2() {
@Test
public void testGetResourcesPartlyCached3() {
- EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(10, 20)))
+ EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(10, 20), eq(new HashMap<>())))
.andReturn(response.subList(10, 20));
- EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(30, 40)))
+ EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(30, 40), eq(new HashMap<>())))
.andReturn(response.subList(30, 40));
- EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(20, 30)))
+ EasyMock.expect(requestConsumer.handleBundleRequest(eq(bundleProvider.getPayload()), hasRequestSublistParameters(20, 30), eq(new HashMap<>())))
.andReturn(response.subList(20, 30));
EasyMock.replay(requestConsumer);
diff --git a/platform-camel/ihe/fhir/core/src/main/java/org/openehealth/ipf/platform/camel/ihe/fhir/core/FhirConsumer.java b/platform-camel/ihe/fhir/core/src/main/java/org/openehealth/ipf/platform/camel/ihe/fhir/core/FhirConsumer.java
index e11129d887..a909690511 100644
--- a/platform-camel/ihe/fhir/core/src/main/java/org/openehealth/ipf/platform/camel/ihe/fhir/core/FhirConsumer.java
+++ b/platform-camel/ihe/fhir/core/src/main/java/org/openehealth/ipf/platform/camel/ihe/fhir/core/FhirConsumer.java
@@ -23,6 +23,7 @@
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
+import jakarta.servlet.http.HttpServletResponse;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.SuspendableService;
@@ -94,14 +95,15 @@ public FhirEndpoint> getEndpoi
* (and potentially handled) request further down a Camel route.
*
* @param payload FHIR request content
- * @param headers headers
+ * @param inHeaders request parameters, e.g. search parameters
+ * @param outHeaders map where Camel response headers will be copied into
* @param resultClass class of the result resource
* @param Resource type being returned
* @return result of processing the FHIR request in Camel
*/
@Override
- public final R handleResourceRequest(Object payload, Map headers, Class resultClass) {
- Object result = handleInRoute(payload, headers, resultClass);
+ public final R handleResourceRequest(Object payload, Map inHeaders, Map outHeaders, Class resultClass) {
+ Object result = handleInRoute(payload, inHeaders, outHeaders, resultClass);
if (result == null) return null;
if (resultClass.isAssignableFrom(result.getClass())) {
return resultClass.cast(result);
@@ -113,28 +115,29 @@ public final R handleResourceRequest(Object payload, M
/**
* @param payload request payload
- * @param headers request parameters, e.g. search parameters
+ * @param inHeaders request parameters, e.g. search parameters
+ * @param outHeaders map where Camel response headers will be copied into
* @param resource type
* @return list of resources to be packaged into a bundle
*/
@Override
- public List handleBundleRequest(Object payload, Map headers) {
- return handleInRoute(payload, headers, List.class);
+ public List handleBundleRequest(Object payload, Map inHeaders, Map outHeaders) {
+ return handleInRoute(payload, inHeaders, outHeaders, List.class);
}
@Override
- public IBundleProvider handleBundleProviderRequest(Object payload, Map headers) {
- return getBundleProvider(payload, headers);
+ public IBundleProvider handleBundleProviderRequest(Object payload, Map headers, HttpServletResponse httpServletResponse) {
+ return getBundleProvider(payload, headers, httpServletResponse);
}
@Override
- public T handleTransactionRequest(Object payload, Map headers, Class bundleClass) {
- return handleInRoute(payload, headers, bundleClass);
+ public T handleTransactionRequest(Object payload, Map inHeaders, Map outHeaders, Class bundleClass) {
+ return handleInRoute(payload, inHeaders, outHeaders, bundleClass);
}
@Override
- public MethodOutcome handleAction(Object payload, Map headers) {
- return handleInRoute(payload, headers, MethodOutcome.class);
+ public MethodOutcome handleAction(Object payload, Map inHeaders, Map outHeaders) {
+ return handleInRoute(payload, inHeaders, outHeaders, MethodOutcome.class);
}
@Override
@@ -157,12 +160,13 @@ public boolean supportsLazyLoading() {
* Forwards the request to be handled into a Camel route
*
* @param payload request payload, will become the Camel message body
- * @param headers request parameters, will be added to the Camel headers
+ * @param inHeaders request parameters, will be added to the Camel headers
+ * @param outHeaders map where Camel response headers will be copied into
* @param resultClass expected body type to be returned
* @return request result, type-converted into the required result class
*/
- protected T handleInRoute(Object payload, Map headers, Class resultClass) {
- var exchange = runRoute(payload, headers);
+ protected T handleInRoute(Object payload, Map inHeaders, Map outHeaders, Class resultClass) {
+ var exchange = runRoute(payload, inHeaders);
var resultMessage = exchange.getMessage();
if (resultMessage.getBody() instanceof List && IBaseResource.class.isAssignableFrom(resultClass)) {
var singletonList = (List)resultMessage.getBody();
@@ -171,6 +175,7 @@ protected T handleInRoute(Object payload, Map headers, Class
}
resultMessage.setBody(singletonList.isEmpty() ? null : singletonList.get(0));
}
+ outHeaders.putAll(resultMessage.getHeaders());
return getEndpoint().getCamelContext().getTypeConverter().convertTo(resultClass, exchange, resultMessage.getBody());
}
@@ -215,16 +220,19 @@ protected Exchange runRoute(Object payload, Map headers) {
* @param headers request headers
* @return resulting bundle provider
*/
- protected IBundleProvider getBundleProvider(Object payload, Map headers) {
+ protected IBundleProvider getBundleProvider(Object payload, Map headers, HttpServletResponse httpServletResponse) {
var endpointConfiguration = getEndpoint().getInterceptableConfiguration();
return supportsLazyLoading() ?
new LazyBundleProvider(this,
endpointConfiguration.isCacheBundles(),
endpointConfiguration.isSort(),
payload,
- headers) :
+ headers,
+ httpServletResponse) :
new EagerBundleProvider(this,
endpointConfiguration.isSort(),
- payload, headers);
+ payload,
+ headers,
+ httpServletResponse);
}
}
diff --git a/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/LoggingInterceptorFactory.java b/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/LoggingInterceptorFactory.java
new file mode 100644
index 0000000000..3f66e46449
--- /dev/null
+++ b/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/LoggingInterceptorFactory.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2024 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.openehealth.ipf.platform.camel.ihe.fhir.chppqm;
+
+import ca.uhn.fhir.rest.client.api.IClientInterceptor;
+import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
+import org.apache.camel.Exchange;
+import org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirEndpoint;
+import org.openehealth.ipf.platform.camel.ihe.fhir.core.HapiClientInterceptorFactory;
+
+public class LoggingInterceptorFactory implements HapiClientInterceptorFactory {
+
+ @Override
+ public IClientInterceptor newInstance(FhirEndpoint endpoint, Exchange exchange) {
+ LoggingInterceptor interceptor = new LoggingInterceptor(false);
+ interceptor.setLogResponseHeaders(true);
+ return interceptor;
+ }
+
+}
diff --git a/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq3/ChPpq3Test.java b/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq3/ChPpq3Test.java
index 1fa684e1a3..e5d809d189 100644
--- a/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq3/ChPpq3Test.java
+++ b/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq3/ChPpq3Test.java
@@ -37,8 +37,7 @@
import java.util.List;
import java.util.Map;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.*;
import static org.openehealth.ipf.commons.ihe.fhir.chppqm.ChPpqmConsentCreator.create201Consent;
import static org.openehealth.ipf.commons.ihe.fhir.chppqm.ChPpqmConsentCreator.createUuid;
@@ -90,6 +89,8 @@ public void testCreate1() throws Exception {
MethodOutcome methodOutcome = exchange.getMessage().getMandatoryBody(MethodOutcome.class);
assertTrue(methodOutcome.getCreated());
+ Map> httpHeaders = methodOutcome.getResponseHeaders();
+ assertEquals(List.of("value1", "value2"), httpHeaders.get("responseheader2"));
List auditMessages = auditSender.getMessages();
assertEquals(2, auditMessages.size());
diff --git a/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq3/ChPpq3TestRouteBuilder.java b/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq3/ChPpq3TestRouteBuilder.java
index a60d2fc19d..607e89a0d9 100644
--- a/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq3/ChPpq3TestRouteBuilder.java
+++ b/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq3/ChPpq3TestRouteBuilder.java
@@ -47,7 +47,9 @@ public void configure() {
//Consent request = exchange.getMessage().getMandatoryBody(Consent.class);
log.info("Method = {}", exchange.getMessage().getHeader(Constants.HTTP_METHOD));
exchange.getMessage().setBody(new MethodOutcome(new IdType(UUID.randomUUID().toString())));
- exchange.getMessage().setHeader(Constants.HTTP_OUTGOING_HEADERS, Map.of("TraceParent", List.of(TRACE_CONTEXT_ID)));
+ exchange.getMessage().setHeader(Constants.HTTP_OUTGOING_HEADERS, Map.of(
+ "TraceParent", List.of(TRACE_CONTEXT_ID),
+ "ResponseHeader2", List.of("value1", "value2")));
})
.process(itiResponseValidator());
}
diff --git a/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq5/ChPpq5Test.java b/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq5/ChPpq5Test.java
index 3f53fe4422..1ac5b5c3f9 100644
--- a/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq5/ChPpq5Test.java
+++ b/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq5/ChPpq5Test.java
@@ -92,7 +92,7 @@ public void test2() throws Exception {
exchange.getMessage().setBody(criteria);
exchange.getMessage().setHeader(Constants.HTTP_METHOD, "POST");
exchange.getMessage().setHeader(Constants.HTTP_OUTGOING_HEADERS, Map.of("Connection", List.of("close")));
- exchange = producerTemplate.send("ch-ppq5://localhost:" + DEMO_APP_PORT, exchange);
+ exchange = producerTemplate.send("ch-ppq5://localhost:" + DEMO_APP_PORT + "?hapiClientInterceptorFactories=#loggingInterceptorFactory", exchange);
Exception exception = Exchanges.extractException(exchange);
if (exception != null) {
throw exception;
diff --git a/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq5/ChPpq5TestRouteBuilder.java b/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq5/ChPpq5TestRouteBuilder.java
index f0c6db1049..20671a72f4 100644
--- a/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq5/ChPpq5TestRouteBuilder.java
+++ b/platform-camel/ihe/fhir/r4/chppqm/src/test/java/org/openehealth/ipf/platform/camel/ihe/fhir/chppqm/chppq5/ChPpq5TestRouteBuilder.java
@@ -23,6 +23,7 @@
import org.openehealth.ipf.platform.camel.core.adapter.ValidatorAdapter;
import java.util.List;
+import java.util.Map;
import static org.openehealth.ipf.commons.ihe.fhir.chppqm.ChPpqmConsentCreator.*;
import static org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirCamelValidators.*;
@@ -45,6 +46,8 @@ public void configure() {
consents.forEach(consent -> consent.setId(createUuid()));
exchange.getMessage().setBody(consents);
+ exchange.getMessage().setHeader(Constants.HTTP_OUTGOING_HEADERS, Map.of(
+ "ResponseHeader2", List.of("value1", "value2")));
})
.process(itiResponseValidator());
}
diff --git a/platform-camel/ihe/fhir/r4/chppqm/src/test/resources/ch-ppq5.xml b/platform-camel/ihe/fhir/r4/chppqm/src/test/resources/ch-ppq5.xml
index a18d9bcef6..7ad5b2231e 100644
--- a/platform-camel/ihe/fhir/r4/chppqm/src/test/resources/ch-ppq5.xml
+++ b/platform-camel/ihe/fhir/r4/chppqm/src/test/resources/ch-ppq5.xml
@@ -25,4 +25,6 @@ http://www.springframework.org/schema/beans/spring-beans.xsd">
class="org.openehealth.ipf.platform.camel.ihe.fhir.chppqm.chppq5.ChPpq5TestRouteBuilder">
+
+
\ No newline at end of file
diff --git a/platform-camel/ihe/fhir/r4/chppqm/src/test/resources/log4j2.xml b/platform-camel/ihe/fhir/r4/chppqm/src/test/resources/log4j2.xml
index 8a26fafae2..f0bc1ecbab 100644
--- a/platform-camel/ihe/fhir/r4/chppqm/src/test/resources/log4j2.xml
+++ b/platform-camel/ihe/fhir/r4/chppqm/src/test/resources/log4j2.xml
@@ -26,6 +26,7 @@
+
diff --git a/src/site/changes.xml b/src/site/changes.xml
index 38da2ae700..0e891f4655 100644
--- a/src/site/changes.xml
+++ b/src/site/changes.xml
@@ -34,6 +34,9 @@
Added missing type converter for PDQv3 continuation protocol.
+
+ Added a possibility to set outgoing HTTP headers in FHIR consumers.
+
Add the possibility to use custom additional URL parameters on a ITI SOAP calls, using
the new interceptor type CustomUrlParametersOutInterceptor.