diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
index 737e82ec5ee..277ff97ab41 100644
--- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
+++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/interceptor/api/Pointcut.java
@@ -1885,6 +1885,9 @@ public enum Pointcut implements IPointcut {
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
*
+ *
+ * ca.uhn.fhir.rest.api.server.storage.TransactionDetails - The outer transaction details object (since 8.8.0)
+ *
*
*
* Hooks must return an instance of ca.uhn.fhir.jpa.dao.TransactionPrePartitionResponse.
@@ -1896,7 +1899,8 @@ public enum Pointcut implements IPointcut {
"ca.uhn.fhir.jpa.dao.TransactionPrePartitionResponse",
"ca.uhn.fhir.rest.api.server.RequestDetails",
"ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
- "org.hl7.fhir.instance.model.api.IBaseBundle"),
+ "org.hl7.fhir.instance.model.api.IBaseBundle",
+ "ca.uhn.fhir.rest.api.server.storage.TransactionDetails"),
/**
* Storage Hook:
@@ -1920,6 +1924,9 @@ public enum Pointcut implements IPointcut {
* pulled out of the servlet request. This parameter is identical to the RequestDetails parameter above but will
* only be populated when operating in a RestfulServer implementation. It is provided as a convenience.
*
+ *
+ * ca.uhn.fhir.rest.api.server.storage.TransactionDetails - The outer transaction details object (since 8.8.0)
+ *
*
*
* Hooks should return void.
@@ -1932,7 +1939,8 @@ public enum Pointcut implements IPointcut {
void.class,
"org.hl7.fhir.instance.model.api.IBaseBundle",
"ca.uhn.fhir.rest.api.server.RequestDetails",
- "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails"),
+ "ca.uhn.fhir.rest.server.servlet.ServletRequestDetails",
+ "ca.uhn.fhir.rest.api.server.storage.TransactionDetails"),
/**
* Storage Hook:
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/8_8_0/7341-make-transactiondetails-available-to-interceptor.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/8_8_0/7341-make-transactiondetails-available-to-interceptor.yaml
new file mode 100644
index 00000000000..1e83eaf01fa
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/8_8_0/7341-make-transactiondetails-available-to-interceptor.yaml
@@ -0,0 +1,6 @@
+---
+type: add
+issue: 7341
+title: "When processing a FHIR transaction, the TransactionDetails object
+ is now created earlier and made available to interceptor hooks. Thanks
+ to Elliott Lavy for the contribution!"
diff --git a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/TransactionPartitionProcessorTest.java b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/TransactionPartitionProcessorTest.java
index 20e6a0f3b15..9bb93bb72ba 100644
--- a/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/TransactionPartitionProcessorTest.java
+++ b/hapi-fhir-jpaserver-test-r5/src/test/java/ca/uhn/fhir/jpa/dao/TransactionPartitionProcessorTest.java
@@ -4,6 +4,7 @@
import ca.uhn.fhir.interceptor.executor.InterceptorService;
import ca.uhn.fhir.jpa.dao.r5.FhirSystemDaoTransactionPartitionR5Test;
import ca.uhn.fhir.model.api.StorageResponseCodeEnum;
+import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.test.utilities.ITestDataBuilder;
import ca.uhn.fhir.util.BundleBuilder;
import org.hl7.fhir.instance.model.api.IBaseResource;
@@ -46,7 +47,8 @@ public class TransactionPartitionProcessorTest implements ITestDataBuilder {
@BeforeEach
public void beforeEach() {
myInterceptorBroadcaster.registerInterceptor(myInterceptor);
- mySvc = new TransactionPartitionProcessor<>(myTransactionProcessor, myFhirContext, newSrd(), false, myInterceptorBroadcaster, "transaction");
+ mySvc = new TransactionPartitionProcessor<>(myTransactionProcessor, myFhirContext, newSrd(), false, myInterceptorBroadcaster,
+ "transaction", new TransactionDetails());
}
@Test
diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java
index 0db933b3658..ab591985405 100644
--- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java
+++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/BaseTransactionProcessor.java
@@ -240,6 +240,8 @@ public BUNDLE transaction(
RequestDetails theRequestDetails, BUNDLE theRequest, boolean theNestedMode) {
String actionName = "Transaction";
+ TransactionDetails transactionDetails = new TransactionDetails();
+
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, theRequestDetails);
@@ -248,7 +250,8 @@ public BUNDLE transaction(
HookParams params = new HookParams()
.add(RequestDetails.class, theRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, theRequestDetails)
- .add(IBaseBundle.class, theRequest);
+ .add(IBaseBundle.class, theRequest)
+ .add(TransactionDetails.class, transactionDetails);
compositeBroadcaster.callHooks(Pointcut.STORAGE_TRANSACTION_PROCESSING, params);
}
@@ -256,10 +259,17 @@ public BUNDLE transaction(
// Interceptor call: STORAGE_TRANSACTION_PRE_PARTITION
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_TRANSACTION_PRE_PARTITION)) {
response = new TransactionPartitionProcessor(
- this, myContext, theRequestDetails, theNestedMode, compositeBroadcaster, actionName)
+ this,
+ myContext,
+ theRequestDetails,
+ theNestedMode,
+ compositeBroadcaster,
+ actionName,
+ transactionDetails)
.execute(theRequest);
} else {
- response = processTransactionAsSubRequest(theRequestDetails, theRequest, actionName, theNestedMode);
+ response = processTransactionAsSubRequest(
+ theRequestDetails, transactionDetails, theRequest, actionName, theNestedMode);
}
List entries = myVersionAdapter.getEntries(response);
@@ -398,12 +408,6 @@ private Date getLastModified(IBaseResource theRes) {
return theRes.getMeta().getLastUpdated();
}
- IBaseBundle processTransactionAsSubRequest(
- RequestDetails theRequestDetails, IBaseBundle theRequest, String theActionName, boolean theNestedMode) {
- return processTransactionAsSubRequest(
- theRequestDetails, new TransactionDetails(), theRequest, theActionName, theNestedMode);
- }
-
IBaseBundle processTransactionAsSubRequest(
RequestDetails theRequestDetails,
TransactionDetails theTransactionDetails,
@@ -2615,17 +2619,20 @@ private void processBatchEntry() {
IInterceptorBroadcaster compositeBroadcaster =
CompositeInterceptorBroadcaster.newCompositeBroadcaster(myInterceptorBroadcaster, myRequestDetails);
+ TransactionDetails transactionDetails = new TransactionDetails();
+
// Interceptor call: STORAGE_TRANSACTION_PROCESSING
if (compositeBroadcaster.hasHooks(Pointcut.STORAGE_TRANSACTION_PROCESSING)) {
HookParams params = new HookParams()
.add(RequestDetails.class, myRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, myRequestDetails)
- .add(IBaseBundle.class, subRequestBundle);
+ .add(IBaseBundle.class, subRequestBundle)
+ .add(TransactionDetails.class, transactionDetails);
compositeBroadcaster.callHooks(Pointcut.STORAGE_TRANSACTION_PROCESSING, params);
}
IBaseBundle nextResponseBundle = processTransactionAsSubRequest(
- myRequestDetails, subRequestBundle, "Batch sub-request", myNestedMode);
+ myRequestDetails, transactionDetails, subRequestBundle, "Batch sub-request", myNestedMode);
IBase subResponseEntry =
(IBase) myVersionAdapter.getEntries(nextResponseBundle).get(0);
diff --git a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/TransactionPartitionProcessor.java b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/TransactionPartitionProcessor.java
index 5f03e60b0b6..2b334508183 100644
--- a/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/TransactionPartitionProcessor.java
+++ b/hapi-fhir-storage/src/main/java/ca/uhn/fhir/jpa/dao/TransactionPartitionProcessor.java
@@ -67,6 +67,7 @@ public class TransactionPartitionProcessor {
private final String myActionName;
private final FhirContext myFhirContext;
private final BaseTransactionProcessor myTransactionProcessor;
+ private final TransactionDetails myTransactionDetails;
/**
* Constructor
@@ -77,13 +78,15 @@ public TransactionPartitionProcessor(
RequestDetails theRequestDetails,
boolean theNestedMode,
IInterceptorBroadcaster theCompositeBroadcaster,
- String theActionName) {
+ String theActionName,
+ TransactionDetails theTransactionDetails) {
myTransactionProcessor = theTransactionProcessor;
myFhirContext = theFhirContext;
myRequestDetails = theRequestDetails;
myNestedMode = theNestedMode;
myInterceptorBroadcaster = theCompositeBroadcaster;
myActionName = theActionName;
+ myTransactionDetails = theTransactionDetails;
}
/**
@@ -105,7 +108,8 @@ public BUNDLE execute(BUNDLE theRequest) {
HookParams hookParams = new HookParams()
.add(RequestDetails.class, myRequestDetails)
.addIfMatchesType(ServletRequestDetails.class, myRequestDetails)
- .add(IBaseBundle.class, theRequest);
+ .add(IBaseBundle.class, theRequest)
+ .add(TransactionDetails.class, myTransactionDetails);
TransactionPrePartitionResponse partitionResponse =
(TransactionPrePartitionResponse) myInterceptorBroadcaster.callHooksAndReturnObject(
Pointcut.STORAGE_TRANSACTION_PRE_PARTITION, hookParams);
@@ -197,7 +201,7 @@ private BUNDLE processPartitionedBundles(BUNDLE theOriginalBundle, List