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