This is an automated email from the ASF dual-hosted git repository.

exceptionfactory pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 67ec0bb9dc NIFI-14412 Added Flow Action Reporter Interface to 
Framework API (#9837)
67ec0bb9dc is described below

commit 67ec0bb9dc542ed383471f7d7270c6129f6362d2
Author: arturchyzy <[email protected]>
AuthorDate: Mon Apr 14 19:09:56 2025 +0200

    NIFI-14412 Added Flow Action Reporter Interface to Framework API (#9837)
    
    Co-authored-by: David Handermann <[email protected]>
    Signed-off-by: David Handermann <[email protected]>
---
 .../java/org/apache/nifi/action/FlowAction.java    | 30 +++++++
 .../apache/nifi/action/FlowActionAttribute.java    | 54 +++++++++++++
 .../org/apache/nifi/action/FlowActionReporter.java | 48 +++++++++++
 .../FlowActionReporterConfigurationContext.java    | 38 +++++++++
 .../FlowActionReporterCreationException.java       | 32 ++++++++
 .../apache/nifi/admin/action/ActionConverter.java  | 34 ++++++++
 .../admin/action/ActionToFlowActionConverter.java  | 93 ++++++++++++++++++++++
 .../nifi/admin/action/StandardFlowAction.java      | 32 ++++++++
 .../admin/service/EntityStoreAuditService.java     | 24 +++++-
 .../admin/service/EntityStoreAuditServiceTest.java | 37 ++++++++-
 ...dardFlowActionReporterConfigurationContext.java | 44 ++++++++++
 .../configuration/FlowControllerConfiguration.java | 30 +++++++
 .../nar/StandardExtensionDiscoveringManager.java   |  2 +
 .../apache/nifi/web/NiFiWebApiConfiguration.java   | 18 +++--
 14 files changed, 505 insertions(+), 11 deletions(-)

diff --git 
a/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowAction.java 
b/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowAction.java
new file mode 100644
index 0000000000..4344e07644
--- /dev/null
+++ b/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowAction.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.nifi.action;
+
+import java.util.Map;
+
+/**
+ * An interface that represents an action that can be taken on a flow. Please 
check {@link FlowActionAttributes} for the common names of the attributes that 
can be used in the action.
+ */
+public interface FlowAction {
+    /**
+     * Return the action attributes
+     * @return the action attributes
+     */
+    Map<String, String> getAttributes();
+}
diff --git 
a/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionAttribute.java
 
b/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionAttribute.java
new file mode 100644
index 0000000000..b74a035920
--- /dev/null
+++ 
b/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionAttribute.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.nifi.action;
+
+/**
+ * Flow Action attributes for consistent naming
+ */
+public enum FlowActionAttribute {
+
+    ACTION_ID("action.id"),
+    ACTION_TIMESTAMP("action.timestamp"),
+    ACTION_USER_IDENTITY("action.userIdentity"),
+    ACTION_SOURCE_ID("action.sourceId"),
+    ACTION_SOURCE_TYPE("action.sourceType"),
+    ACTION_OPERATION("action.operation"),
+
+    ACTION_DETAILS_NAME("actionDetails.name"),
+    ACTION_DETAILS_SOURCE_ID("actionDetails.sourceId"),
+    ACTION_DETAILS_SOURCE_TYPE("actionDetails.sourceType"),
+    ACTION_DETAILS_DESTINATION_ID("actionDetails.destinationId"),
+    ACTION_DETAILS_DESTINATION_TYPE("actionDetails.destinationType"),
+    ACTION_DETAILS_RELATIONSHIP("actionDetails.relationship"),
+    ACTION_DETAILS_GROUP_ID("actionDetails.groupId"),
+    ACTION_DETAILS_PREVIOUS_GROUP_ID("actionDetails.previousGroupId"),
+    ACTION_DETAILS_END_DATE("actionDetails.endDate"),
+
+    COMPONENT_DETAILS_TYPE("componentDetails.type"),
+    COMPONENT_DETAILS_URI("componentDetails.uri");
+
+    private final String key;
+
+    FlowActionAttribute(String key) {
+        this.key = key;
+    }
+
+    public String key() {
+        return key;
+    }
+
+}
diff --git 
a/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionReporter.java
 
b/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionReporter.java
new file mode 100644
index 0000000000..d650202026
--- /dev/null
+++ 
b/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionReporter.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.nifi.action;
+
+import java.io.Closeable;
+import java.util.Collection;
+
+/**
+ * A {@link FlowActionReporter} is responsible for reporting {@link 
FlowAction}s.
+ * Provides {@link FlowActionReporterConfigurationContext} to allow for 
configuration and initialization.
+ * Uses {@link #close()} to allow for cleanup.
+ */
+public interface FlowActionReporter extends Closeable {
+    /**
+     * Initialization method for the reporter. This method is called just 
after reporter is created.
+     * @param context reporter configuration context
+     * @throws FlowActionReporterCreationException if case of any error during 
initialization
+     */
+    default void onConfigured(FlowActionReporterConfigurationContext context) 
throws FlowActionReporterCreationException {
+    }
+
+    /**
+     * Reports a collection of {@link FlowAction}s.
+     * @param actions the collection of {@link FlowAction}s to report
+     */
+    void reportFlowActions(Collection<FlowAction> actions);
+
+    /**
+     * Close method for releasing resources when shutting down the 
application. This method is called just before the reporter is destroyed.
+     */
+    @Override
+    default void close() {
+    }
+}
diff --git 
a/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionReporterConfigurationContext.java
 
b/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionReporterConfigurationContext.java
new file mode 100644
index 0000000000..832d15e85b
--- /dev/null
+++ 
b/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionReporterConfigurationContext.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.nifi.action;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.X509TrustManager;
+import java.util.Optional;
+
+/**
+ * A context that will be passed to the reporter in order to obtain 
configuration.
+ */
+public interface FlowActionReporterConfigurationContext {
+    /**
+     * Retrieves SSLContext if configured in application properties
+     * @return SSLContext
+     */
+    Optional<SSLContext> getSSLContext();
+
+    /**
+     * Retrieves the trust manager if configured in application properties
+     * @return X509TrustManager
+     */
+    Optional<X509TrustManager> getTrustManager();
+}
diff --git 
a/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionReporterCreationException.java
 
b/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionReporterCreationException.java
new file mode 100644
index 0000000000..3bf2aa7f2c
--- /dev/null
+++ 
b/nifi-framework-api/src/main/java/org/apache/nifi/action/FlowActionReporterCreationException.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.nifi.action;
+
+/**
+ * An exception that will be thrown if a reporter can not be created.
+ */
+public class FlowActionReporterCreationException extends RuntimeException {
+
+    public FlowActionReporterCreationException(String message) {
+        super(message);
+    }
+
+    public FlowActionReporterCreationException(String message, Throwable 
cause) {
+        super(message, cause);
+    }
+
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/action/ActionConverter.java
 
b/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/action/ActionConverter.java
new file mode 100644
index 0000000000..bae3be4573
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/action/ActionConverter.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.nifi.admin.action;
+
+import org.apache.nifi.action.Action;
+import org.apache.nifi.action.FlowAction;
+
+/**
+ * Converter for converting an action to a flow action. Used in the reporting 
flow actions process.
+ */
+@FunctionalInterface
+public interface ActionConverter {
+    /**
+     * Convert Action to Flow Action with attributes
+     *
+     * @param action Action to be converted
+     * @return Flow Action
+     */
+    FlowAction convert(Action action);
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/action/ActionToFlowActionConverter.java
 
b/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/action/ActionToFlowActionConverter.java
new file mode 100644
index 0000000000..ab5e12e1cd
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/action/ActionToFlowActionConverter.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.nifi.admin.action;
+
+import org.apache.nifi.action.Action;
+import org.apache.nifi.action.FlowAction;
+import org.apache.nifi.action.FlowActionAttribute;
+import org.apache.nifi.action.component.details.ComponentDetails;
+import org.apache.nifi.action.component.details.ExtensionDetails;
+import org.apache.nifi.action.component.details.RemoteProcessGroupDetails;
+import org.apache.nifi.action.details.ActionDetails;
+import org.apache.nifi.action.details.ConfigureDetails;
+import org.apache.nifi.action.details.ConnectDetails;
+import org.apache.nifi.action.details.MoveDetails;
+import org.apache.nifi.action.details.PurgeDetails;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Converts an {@link Action} to a {@link FlowAction} using {@link 
FlowActionAttribute} attributes.
+ */
+public class ActionToFlowActionConverter implements ActionConverter {
+
+    @Override
+    public FlowAction convert(final Action action) {
+        final Map<String, String> attributes = new HashMap<>();
+        populateActionAttributes(action, attributes);
+        populateActionDetailsAttributes(action.getActionDetails(), attributes);
+        populateComponentDetailsProperties(action.getComponentDetails(), 
attributes);
+        return new StandardFlowAction(attributes);
+    }
+
+    private void populateActionAttributes(final Action action, final 
Map<String, String> attributes) {
+        attributes.put(FlowActionAttribute.ACTION_ID.key(), 
String.valueOf(action.getId()));
+        attributes.put(FlowActionAttribute.ACTION_TIMESTAMP.key(), 
action.getTimestamp().toInstant().toString());
+        attributes.put(FlowActionAttribute.ACTION_USER_IDENTITY.key(), 
action.getUserIdentity());
+        attributes.put(FlowActionAttribute.ACTION_SOURCE_ID.key(), 
action.getSourceId());
+        attributes.put(FlowActionAttribute.ACTION_SOURCE_TYPE.key(), 
action.getSourceType().name());
+        attributes.put(FlowActionAttribute.ACTION_OPERATION.key(), 
action.getOperation().name());
+    }
+
+    private void populateActionDetailsAttributes(final ActionDetails 
actionDetails, final Map<String, String> attributes) {
+        switch (actionDetails) {
+            case ConfigureDetails configureDetails -> attributes.put(
+                FlowActionAttribute.ACTION_DETAILS_NAME.key(), 
configureDetails.getName()
+            );
+            case ConnectDetails connectDetails -> {
+                
attributes.put(FlowActionAttribute.ACTION_DETAILS_SOURCE_ID.key(), 
connectDetails.getSourceId());
+                
attributes.put(FlowActionAttribute.ACTION_DETAILS_SOURCE_TYPE.key(), 
connectDetails.getSourceType().name());
+                
attributes.put(FlowActionAttribute.ACTION_DETAILS_DESTINATION_ID.key(), 
connectDetails.getDestinationId());
+                
attributes.put(FlowActionAttribute.ACTION_DETAILS_DESTINATION_TYPE.key(), 
connectDetails.getDestinationType().name());
+                
attributes.put(FlowActionAttribute.ACTION_DETAILS_RELATIONSHIP.key(), 
connectDetails.getRelationship());
+            }
+            case MoveDetails moveDetails -> {
+                
attributes.put(FlowActionAttribute.ACTION_DETAILS_GROUP_ID.key(), 
moveDetails.getGroupId());
+                
attributes.put(FlowActionAttribute.ACTION_DETAILS_PREVIOUS_GROUP_ID.key(), 
moveDetails.getPreviousGroupId());
+            }
+            case PurgeDetails purgeDetails -> attributes.put(
+                FlowActionAttribute.ACTION_DETAILS_END_DATE.key(), 
purgeDetails.getEndDate().toInstant().toString()
+            );
+            case null, default -> {
+            }
+        }
+    }
+
+    private void populateComponentDetailsProperties(final ComponentDetails 
componentDetails, final Map<String, String> attributes) {
+        switch (componentDetails) {
+            case ExtensionDetails extensionDetails -> attributes.put(
+                FlowActionAttribute.COMPONENT_DETAILS_TYPE.key(), 
extensionDetails.getType()
+            );
+            case RemoteProcessGroupDetails remoteProcessGroupDetails -> 
attributes.put(
+                FlowActionAttribute.COMPONENT_DETAILS_URI.key(), 
remoteProcessGroupDetails.getUri()
+            );
+            case null, default -> {
+            }
+        }
+    }
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/action/StandardFlowAction.java
 
b/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/action/StandardFlowAction.java
new file mode 100644
index 0000000000..86f635d188
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/action/StandardFlowAction.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.nifi.admin.action;
+
+import org.apache.nifi.action.FlowAction;
+
+import java.util.Map;
+
+/**
+ * A standard implementation of the {@link FlowAction} interface allowing to 
store {@link String} key and values.
+ */
+public record StandardFlowAction(Map<String, String> attributes) implements 
FlowAction {
+
+    @Override
+    public Map<String, String> getAttributes() {
+        return Map.copyOf(attributes);
+    }
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/EntityStoreAuditService.java
 
b/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/EntityStoreAuditService.java
index 1152ddc40a..04929025da 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/EntityStoreAuditService.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-administration/src/main/java/org/apache/nifi/admin/service/EntityStoreAuditService.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.admin.service;
 
+import jakarta.annotation.Nullable;
 import jetbrains.exodus.entitystore.Entity;
 import jetbrains.exodus.entitystore.EntityId;
 import jetbrains.exodus.entitystore.EntityIterable;
@@ -27,6 +28,7 @@ import jetbrains.exodus.env.EnvironmentConfig;
 import jetbrains.exodus.env.Environments;
 import org.apache.nifi.action.Action;
 import org.apache.nifi.action.Component;
+import org.apache.nifi.action.FlowActionReporter;
 import org.apache.nifi.action.FlowChangeAction;
 import org.apache.nifi.action.Operation;
 import org.apache.nifi.action.component.details.ComponentDetails;
@@ -43,6 +45,7 @@ import org.apache.nifi.action.details.FlowChangeMoveDetails;
 import org.apache.nifi.action.details.FlowChangePurgeDetails;
 import org.apache.nifi.action.details.MoveDetails;
 import org.apache.nifi.action.details.PurgeDetails;
+import org.apache.nifi.admin.action.ActionConverter;
 import org.apache.nifi.admin.service.entity.ActionEntity;
 import org.apache.nifi.admin.service.entity.ActionLink;
 import org.apache.nifi.admin.service.entity.ConfigureDetailsEntity;
@@ -93,15 +96,24 @@ public class EntityStoreAuditService implements 
AuditService, Closeable {
 
     private final Environment environment;
 
+    private final ActionConverter actionConverter;
+
+    @Nullable
+    private final FlowActionReporter flowActionReporter;
+
     /**
      * Entity Store Audit Service constructor with required properties for 
persistent location
      *
-     * @param directory Persistent Entity Store directory
+     * @param directory          Persistent Entity Store directory
+     * @param flowActionReporter Flow Action Reporter
+     * @param actionConverter    Action Converter
      */
-    public EntityStoreAuditService(final File directory) {
+    public EntityStoreAuditService(final File directory, final ActionConverter 
actionConverter, @Nullable final FlowActionReporter flowActionReporter) {
         environment = loadEnvironment(directory);
         entityStore = PersistentEntityStores.newInstance(environment);
         logger.info("Environment configured with directory [{}]", directory);
+        this.actionConverter = actionConverter;
+        this.flowActionReporter = flowActionReporter;
     }
 
     /**
@@ -119,6 +131,14 @@ public class EntityStoreAuditService implements 
AuditService, Closeable {
             }
             logger.debug("Actions added [{}]", actions.size());
         });
+        if (flowActionReporter != null) {
+            flowActionReporter.reportFlowActions(
+                actions.stream()
+                    .map(actionConverter::convert)
+                    .toList()
+            );
+        }
+
     }
 
     /**
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/EntityStoreAuditServiceTest.java
 
b/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/EntityStoreAuditServiceTest.java
index 203271e090..da1018a70a 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/EntityStoreAuditServiceTest.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-administration/src/test/java/org/apache/nifi/admin/service/EntityStoreAuditServiceTest.java
@@ -18,6 +18,8 @@ package org.apache.nifi.admin.service;
 
 import org.apache.nifi.action.Action;
 import org.apache.nifi.action.Component;
+import org.apache.nifi.action.FlowAction;
+import org.apache.nifi.action.FlowActionReporter;
 import org.apache.nifi.action.FlowChangeAction;
 import org.apache.nifi.action.Operation;
 import org.apache.nifi.action.details.ActionDetails;
@@ -26,6 +28,7 @@ import 
org.apache.nifi.action.details.FlowChangeConfigureDetails;
 import org.apache.nifi.action.details.FlowChangeConnectDetails;
 import org.apache.nifi.action.details.FlowChangePurgeDetails;
 import org.apache.nifi.action.details.PurgeDetails;
+import org.apache.nifi.admin.action.ActionConverter;
 import org.apache.nifi.history.History;
 import org.apache.nifi.history.HistoryQuery;
 import org.apache.nifi.history.PreviousValue;
@@ -40,6 +43,7 @@ import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -103,14 +107,18 @@ class EntityStoreAuditServiceTest {
 
     private static final String SORT_ASCENDING = "ASC";
 
+    private static final ActionConverter actionConverter = action -> 
(FlowAction) Map::of;
+
     @TempDir
     File directory;
 
     private EntityStoreAuditService service;
 
+    InMemoryFlowActionReporter flowActionReporter = new 
InMemoryFlowActionReporter();
+
     @BeforeEach
     void setService() {
-        service = new EntityStoreAuditService(directory);
+        service = new EntityStoreAuditService(directory, actionConverter, 
flowActionReporter);
     }
 
     @AfterEach
@@ -133,7 +141,7 @@ class EntityStoreAuditServiceTest {
         }
 
         // Create Service with corrupted directory
-        service = new EntityStoreAuditService(directory);
+        service = new EntityStoreAuditService(directory, actionConverter, 
flowActionReporter);
         final Action action = newAction();
         final Collection<Action> actions = Collections.singletonList(action);
         service.addActions(actions);
@@ -237,7 +245,7 @@ class EntityStoreAuditServiceTest {
         final History actionsHistory = service.getActions(historyQuery);
 
         assertNotNull(actionsHistory);
-        assertEquals(actionsHistory.getTotal(), 1);
+        assertEquals(1, actionsHistory.getTotal());
         assertNotNull(actionsHistory.getLastRefreshed());
 
         final Collection<Action> actionsFound = actionsHistory.getActions();
@@ -460,6 +468,29 @@ class EntityStoreAuditServiceTest {
         assertConnectDetailsFound(connectDetails, actionDetails);
     }
 
+    @Test
+    void shouldReportActions() {
+        final List<Action> actions = new ArrayList<>();
+        actions.add(newAction());
+
+        service.addActions(actions);
+
+        assertEquals(1, flowActionReporter.getReportedActions().size());
+    }
+
+    private static class InMemoryFlowActionReporter implements 
FlowActionReporter {
+        List<FlowAction> reportedActions = new ArrayList<>();
+
+        @Override
+        public void reportFlowActions(final Collection<FlowAction> actions) {
+            reportedActions.addAll(actions);
+        }
+
+        List<FlowAction> getReportedActions() {
+            return reportedActions;
+        }
+    }
+
     private FlowChangeAction newAction() {
         final FlowChangeAction action = new FlowChangeAction();
         action.setTimestamp(ACTION_TIMESTAMP);
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/action/StandardFlowActionReporterConfigurationContext.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/action/StandardFlowActionReporterConfigurationContext.java
new file mode 100644
index 0000000000..74dc2479dd
--- /dev/null
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-components/src/main/java/org/apache/nifi/action/StandardFlowActionReporterConfigurationContext.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.apache.nifi.action;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.X509TrustManager;
+import java.util.Optional;
+
+/**
+ * A context object that provides the configuration for a FlowActionReporter.
+ */
+public class StandardFlowActionReporterConfigurationContext implements 
FlowActionReporterConfigurationContext {
+    private final SSLContext sslContext;
+    private final X509TrustManager trustManager;
+
+    public StandardFlowActionReporterConfigurationContext(final SSLContext 
sslContext, final X509TrustManager trustManager) {
+        this.sslContext = sslContext;
+        this.trustManager = trustManager;
+    }
+
+    @Override
+    public Optional<SSLContext> getSSLContext() {
+        return Optional.ofNullable(sslContext);
+    }
+
+    @Override
+    public Optional<X509TrustManager> getTrustManager() {
+        return Optional.ofNullable(trustManager);
+    }
+}
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/framework/configuration/FlowControllerConfiguration.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/framework/configuration/FlowControllerConfiguration.java
index abc8509a8f..1d3d6c6e13 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/framework/configuration/FlowControllerConfiguration.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/framework/configuration/FlowControllerConfiguration.java
@@ -16,6 +16,9 @@
  */
 package org.apache.nifi.framework.configuration;
 
+import org.apache.nifi.action.FlowActionReporter;
+import org.apache.nifi.action.FlowActionReporterConfigurationContext;
+import org.apache.nifi.action.StandardFlowActionReporterConfigurationContext;
 import org.apache.nifi.admin.service.AuditService;
 import org.apache.nifi.asset.AssetComponentManager;
 import org.apache.nifi.asset.AssetManager;
@@ -83,6 +86,8 @@ import java.util.concurrent.TimeUnit;
 @Configuration
 public class FlowControllerConfiguration {
 
+    private static final String FLOW_ACTION_REPORTER_IMPLEMENTATION = 
"nifi.flow.action.reporter.implementation";
+
     private NiFiProperties properties;
 
     private ExtensionDiscoveringManager extensionManager;
@@ -471,4 +476,29 @@ public class FlowControllerConfiguration {
     public AssetComponentManager affectedComponentManager() throws Exception {
         return new StandardAssetComponentManager(flowController());
     }
+
+    /**
+     * Flow Action Reporter configured from NiFi Application Properties
+     *
+     * @return Flow Action Reporter
+     */
+    @Bean
+    public FlowActionReporter flowActionReporter() {
+        final FlowActionReporter flowActionReporter;
+
+        final String configuredClassName = 
properties.getProperty(FLOW_ACTION_REPORTER_IMPLEMENTATION);
+        if (configuredClassName == null || configuredClassName.isBlank()) {
+            flowActionReporter = null;
+        } else {
+            try {
+                flowActionReporter = 
NarThreadContextClassLoader.createInstance(extensionManager, 
configuredClassName, FlowActionReporter.class, properties);
+                final FlowActionReporterConfigurationContext 
configurationContext = new 
StandardFlowActionReporterConfigurationContext(sslContext, trustManager);
+                flowActionReporter.onConfigured(configurationContext);
+            } catch (final Exception e) {
+                throw new IllegalStateException("Failed to create 
FlowActionReporter with class [%s]".formatted(configuredClassName), e);
+            }
+        }
+
+        return flowActionReporter;
+    }
 }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/StandardExtensionDiscoveringManager.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/StandardExtensionDiscoveringManager.java
index 735716640b..6e9a6293d2 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/StandardExtensionDiscoveringManager.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-nar-utils/src/main/java/org/apache/nifi/nar/StandardExtensionDiscoveringManager.java
@@ -16,6 +16,7 @@
  */
 package org.apache.nifi.nar;
 
+import org.apache.nifi.action.FlowActionReporter;
 import org.apache.nifi.annotation.behavior.RequiresInstanceClassLoading;
 import org.apache.nifi.asset.AssetManager;
 import org.apache.nifi.authentication.LoginIdentityProvider;
@@ -132,6 +133,7 @@ public class StandardExtensionDiscoveringManager implements 
ExtensionDiscovering
         definitionMap.put(PythonBridge.class, new HashSet<>());
         definitionMap.put(NarPersistenceProvider.class, new HashSet<>());
         definitionMap.put(AssetManager.class, new HashSet<>());
+        definitionMap.put(FlowActionReporter.class, new HashSet<>());
 
         additionalExtensionTypes.forEach(type -> 
definitionMap.putIfAbsent(type, new HashSet<>()));
     }
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
index d5c8bb9376..daa6429a93 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/src/main/java/org/apache/nifi/web/NiFiWebApiConfiguration.java
@@ -16,6 +16,10 @@
  */
 package org.apache.nifi.web;
 
+import jakarta.annotation.Nullable;
+import org.apache.nifi.admin.action.ActionConverter;
+import org.apache.nifi.admin.action.ActionToFlowActionConverter;
+import org.apache.nifi.action.FlowActionReporter;
 import org.apache.nifi.admin.service.AuditService;
 import org.apache.nifi.admin.service.EntityStoreAuditService;
 import 
org.apache.nifi.framework.configuration.ApplicationPropertiesConfiguration;
@@ -36,9 +40,9 @@ import java.net.URISyntaxException;
  * Web Application Spring Configuration
  */
 @ComponentScan(basePackageClasses = {
-        ApplicationPropertiesConfiguration.class,
-        WebSecurityConfiguration.class,
-        WebApplicationConfiguration.class
+    ApplicationPropertiesConfiguration.class,
+    WebSecurityConfiguration.class,
+    WebApplicationConfiguration.class
 })
 @Configuration
 public class NiFiWebApiConfiguration {
@@ -67,13 +71,15 @@ public class NiFiWebApiConfiguration {
     /**
      * Audit Service implementation from nifi-administration
      *
-     * @param properties NiFi Properties
+     * @param properties                 NiFi Properties
+     * @param flowActionReporter         Flow Action Reporter can be null
      * @return Audit Service implementation using Persistent Entity Store
      */
     @Bean
-    public AuditService auditService(final NiFiProperties properties) {
+    public AuditService auditService(final NiFiProperties properties, 
@Nullable final FlowActionReporter flowActionReporter) {
         final File databaseDirectory = 
properties.getDatabaseRepositoryPath().toFile();
-        return new EntityStoreAuditService(databaseDirectory);
+        final ActionConverter actionConverter = new 
ActionToFlowActionConverter();
+        return new EntityStoreAuditService(databaseDirectory, actionConverter, 
flowActionReporter);
     }
 
     @Bean


Reply via email to