diff --git a/minifi/minifi-commons/minifi-commons-api/src/main/java/org/apache/nifi/minifi/commons/api/MiNiFiProperties.java b/minifi/minifi-commons/minifi-commons-api/src/main/java/org/apache/nifi/minifi/commons/api/MiNiFiProperties.java
index 07e2c16a501c..b346eed20be8 100644
--- a/minifi/minifi-commons/minifi-commons-api/src/main/java/org/apache/nifi/minifi/commons/api/MiNiFiProperties.java
+++ b/minifi/minifi-commons/minifi-commons-api/src/main/java/org/apache/nifi/minifi/commons/api/MiNiFiProperties.java
@@ -81,8 +81,8 @@ public enum MiNiFiProperties {
C2_KEEP_ALIVE_DURATION("c2.rest.keepAliveDuration", "5 min", false, true, TIME_PERIOD_VALIDATOR),
C2_REST_HTTP_HEADERS("c2.rest.http.headers", "Accept:application/json", false, true, VALID),
C2_CONFIG_DIRECTORY("c2.config.directory", "./conf", false, true, VALID),
- C2_RUNTIME_MANIFEST_IDENTIFIER("c2.runtime.manifest.identifier", "", false, true, VALID),
- C2_RUNTIME_TYPE("c2.runtime.type", "", false, true, VALID),
+ C2_RUNTIME_MANIFEST_IDENTIFIER("c2.runtime.manifest.identifier", "minifi", false, true, VALID),
+ C2_RUNTIME_TYPE("c2.runtime.type", "minifi-java", false, true, VALID),
C2_ASSET_DIRECTORY("c2.asset.directory", "./asset", false, true, VALID),
C2_SECURITY_TRUSTSTORE_LOCATION("c2.security.truststore.location", "", false, false, VALID),
C2_SECURITY_TRUSTSTORE_PASSWORD("c2.security.truststore.password", "", true, false, VALID),
diff --git a/nifi-commons/nifi-metrics/pom.xml b/nifi-commons/nifi-metrics/pom.xml
index 8025ad7809c3..5743be5c7097 100644
--- a/nifi-commons/nifi-metrics/pom.xml
+++ b/nifi-commons/nifi-metrics/pom.xml
@@ -31,12 +31,12 @@
io.dropwizard.metrics
metrics-jvm
- 4.2.25
+ 4.2.26
io.dropwizard.metrics
metrics-core
- 4.2.25
+ 4.2.26
diff --git a/nifi-commons/nifi-property-protection-azure/pom.xml b/nifi-commons/nifi-property-protection-azure/pom.xml
index 5e7ba04adc8a..96f939853c89 100644
--- a/nifi-commons/nifi-property-protection-azure/pom.xml
+++ b/nifi-commons/nifi-property-protection-azure/pom.xml
@@ -98,7 +98,7 @@
com.microsoft.azure
msal4j
- 1.15.0
+ 1.15.1
diff --git a/nifi-commons/nifi-record-path/pom.xml b/nifi-commons/nifi-record-path/pom.xml
index 7aa53b9b4e8c..3548b176041b 100644
--- a/nifi-commons/nifi-record-path/pom.xml
+++ b/nifi-commons/nifi-record-path/pom.xml
@@ -125,5 +125,12 @@
com.fasterxml.jackson.core
jackson-core
+
+
+ org.apache.nifi
+ nifi-json-record-utils
+ 2.0.0-SNAPSHOT
+ test
+
diff --git a/nifi-commons/nifi-record-path/src/test/java/org/apache/nifi/record/path/TestRecordPath.java b/nifi-commons/nifi-record-path/src/test/java/org/apache/nifi/record/path/TestRecordPath.java
index a548ccd812f5..2513b92ee125 100644
--- a/nifi-commons/nifi-record-path/src/test/java/org/apache/nifi/record/path/TestRecordPath.java
+++ b/nifi-commons/nifi-record-path/src/test/java/org/apache/nifi/record/path/TestRecordPath.java
@@ -17,7 +17,13 @@
package org.apache.nifi.record.path;
+import org.apache.nifi.json.JsonRecordSource;
+import org.apache.nifi.json.JsonSchemaInference;
+import org.apache.nifi.json.JsonTreeRowRecordReader;
+import org.apache.nifi.logging.ComponentLog;
import org.apache.nifi.record.path.exception.RecordPathException;
+import org.apache.nifi.schema.inference.TimeValueInference;
+import org.apache.nifi.serialization.MalformedRecordException;
import org.apache.nifi.serialization.SimpleRecordSchema;
import org.apache.nifi.serialization.record.DataType;
import org.apache.nifi.serialization.record.MapRecord;
@@ -31,7 +37,10 @@
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
@@ -241,11 +250,67 @@ public void testDescendantField() {
}
@Test
- public void testParent() {
+ public void testDescendantFieldWithArrayOfRecords() throws IOException, MalformedRecordException {
+ final String recordJson = """
+ {
+ "container" : {
+ "id" : "0",
+ "metadata" : {
+ "filename" : "file1.pdf",
+ "page.count" : "165"
+ },
+ "textElement" : null,
+ "containers" : [ {
+ "id" : "1",
+ "title" : null,
+ "metadata" : {
+ "end.page" : 1,
+ "start.page" : 1
+ },
+ "textElement" : {
+ "text" : "Table of Contents",
+ "metadata" : { }
+ },
+ "containers" : [ ]
+ } ]
+ }
+ }
+ """;
+
+ final JsonSchemaInference schemaInference = new JsonSchemaInference(new TimeValueInference("MM/dd/yyyy", "HH:mm:ss", "MM/dd/yyyy HH:mm:ss"));
+ final JsonRecordSource jsonRecordSource = new JsonRecordSource(new ByteArrayInputStream(recordJson.getBytes(StandardCharsets.UTF_8)));
+ final RecordSchema schema = schemaInference.inferSchema(jsonRecordSource);
+
+ final JsonTreeRowRecordReader reader = new JsonTreeRowRecordReader(new ByteArrayInputStream(recordJson.getBytes(StandardCharsets.UTF_8)), Mockito.mock(ComponentLog.class),
+ schema, "MM/dd/yyyy", "HH:mm:ss", "MM/dd/yyyy HH:mm:ss");
+ final Record record = reader.nextRecord();
+
+ final List fieldValues = RecordPath.compile("//textElement[./text = 'Table of Contents']/metadata/insertion").evaluate(record).getSelectedFields().toList();
+ assertEquals(1, fieldValues.size());
+ fieldValues.getFirst().updateValue("Hello");
+ record.incorporateInactiveFields();
+
+ final Record container = (Record) record.getValue("container");
+ final Object[] containers = (Object[]) container.getValue("containers");
+ final Record textElement = (Record) (((Record) containers[0]).getValue("textElement"));
+ final Record metadata = (Record) textElement.getValue("metadata");
+ assertEquals("Hello", metadata.getValue("insertion"));
+
+ final List metadataFields = metadata.getSchema().getFields();
+ assertEquals(1, metadataFields.size());
+ assertEquals("insertion", metadataFields.getFirst().getFieldName());
+ }
+
+ private Record createAccountRecord(final int id, final double balance) {
final Map accountValues = new HashMap<>();
- accountValues.put("id", 1);
- accountValues.put("balance", 123.45D);
- final Record accountRecord = new MapRecord(getAccountSchema(), accountValues);
+ accountValues.put("id", id);
+ accountValues.put("balance", balance);
+ return new MapRecord(getAccountSchema(), accountValues);
+ }
+
+ @Test
+ public void testParent() {
+ final Record accountRecord = createAccountRecord(1, 123.45D);
final RecordSchema schema = new SimpleRecordSchema(getDefaultFields());
final Map values = new HashMap<>();
@@ -2234,9 +2299,24 @@ private List getDefaultFields() {
final DataType accountsType = RecordFieldType.ARRAY.getArrayDataType(accountDataType);
final RecordField accountsField = new RecordField("accounts", accountsType);
fields.add(accountsField);
+
+ final DataType bankType = RecordFieldType.CHOICE.getChoiceDataType(
+ RecordFieldType.STRING.getDataType(),
+ RecordFieldType.RECORD.getRecordDataType(getBankSchema())
+ );
+ final RecordField banksField = new RecordField("banks", RecordFieldType.ARRAY.getArrayDataType(bankType));
+ fields.add(banksField);
+
return fields;
}
+ private RecordSchema getBankSchema() {
+ final DataType accountDataType = RecordFieldType.RECORD.getRecordDataType(getAccountSchema());
+ final DataType accountsType = RecordFieldType.ARRAY.getArrayDataType(accountDataType);
+ final RecordSchema bankSchema = new SimpleRecordSchema(List.of(new RecordField("accounts", accountsType)));
+ return bankSchema;
+ }
+
private RecordSchema getAccountSchema() {
final List accountFields = new ArrayList<>();
accountFields.add(new RecordField("id", RecordFieldType.INT.getDataType()));
diff --git a/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/util/DataTypeUtils.java b/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/util/DataTypeUtils.java
index 90c6a53c0b10..aaf459a001a4 100644
--- a/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/util/DataTypeUtils.java
+++ b/nifi-commons/nifi-record/src/main/java/org/apache/nifi/serialization/record/util/DataTypeUtils.java
@@ -1884,9 +1884,43 @@ private static Optional getWiderRecordType(final RecordDataType thisRe
return Optional.of(otherRecordDataType);
}
+ // Check if all fields in 'thisSchema' are equal to or wider than all fields in 'otherSchema'
+ if (isRecordWider(thisSchema, otherSchema)) {
+ return Optional.of(thisRecordDataType);
+ }
+ if (isRecordWider(otherSchema, thisSchema)) {
+ return Optional.of(otherRecordDataType);
+ }
+
return Optional.empty();
}
+ public static boolean isRecordWider(final RecordSchema potentiallyWider, final RecordSchema potentiallyNarrower) {
+ final List narrowerFields = potentiallyNarrower.getFields();
+
+ for (final RecordField narrowerField : narrowerFields) {
+ final Optional widerField = potentiallyWider.getField(narrowerField.getFieldName());
+ if (widerField.isEmpty()) {
+ return false;
+ }
+
+ if (widerField.get().getDataType().equals(narrowerField.getDataType())) {
+ continue;
+ }
+
+ final Optional widerType = getWiderType(widerField.get().getDataType(), narrowerField.getDataType());
+ if (widerType.isEmpty()) {
+ return false;
+ }
+
+ if (!widerType.get().equals(widerField.get().getDataType())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
private static boolean isDecimalType(final RecordFieldType fieldType) {
return switch (fieldType) {
case FLOAT, DOUBLE, DECIMAL -> true;
diff --git a/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/TestDataTypeUtils.java b/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/TestDataTypeUtils.java
index bd84b449386b..d5faca787eca 100644
--- a/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/TestDataTypeUtils.java
+++ b/nifi-commons/nifi-record/src/test/java/org/apache/nifi/serialization/record/TestDataTypeUtils.java
@@ -111,6 +111,67 @@ public void testWiderRecordWhenAllFieldsContainedWithin() {
assertEquals(((RecordDataType) widerType.get()).getChildSchema(), widerRecord.getSchema());
}
+ @Test
+ public void testWiderRecordWhenChildRecordHasAllFieldsContainedWithin() {
+ final Record jane = DataTypeUtils.toRecord(Map.of(
+ "name", "Jane"
+ ), "");
+
+ final Record smallRecord = DataTypeUtils.toRecord(Map.of(
+ "firstName", "John",
+ "lastName", "Doe",
+ "child", jane,
+ "age", 30), "");
+
+ final Record janeWithAge = DataTypeUtils.toRecord(Map.of(
+ "name", "Jane",
+ "age", 2
+ ), "");
+
+ final Record widerRecord = DataTypeUtils.toRecord(Map.of(
+ "firstName", "John",
+ "lastName", "Doe",
+ "fullName", "John Doe",
+ "child", janeWithAge,
+ "age", 30), "");
+
+ final Optional widerType = DataTypeUtils.getWiderType(RecordFieldType.RECORD.getRecordDataType(smallRecord.getSchema()),
+ RecordFieldType.RECORD.getRecordDataType(widerRecord.getSchema()));
+ assertTrue(widerType.isPresent());
+ assertEquals(((RecordDataType) widerType.get()).getChildSchema(), widerRecord.getSchema());
+ }
+
+ @Test
+ public void testIsRecordWiderWithExtraField() {
+ final Record jane = DataTypeUtils.toRecord(Map.of(
+ ), "");
+
+ final Record smallRecord = DataTypeUtils.toRecord(Map.of(
+ "firstName", "John",
+ "lastName", "Doe",
+ "child", jane,
+ "age", 30), "");
+
+ final Record janeWithAge = DataTypeUtils.toRecord(Map.of(
+ "name", "Jane",
+ "age", 2
+ ), "");
+
+ final Record widerRecord = DataTypeUtils.toRecord(Map.of(
+ "firstName", "John",
+ "lastName", "Doe",
+ "fullName", "John Doe",
+ "child", janeWithAge,
+ "age", 30), "");
+
+ assertFalse(DataTypeUtils.isRecordWider(smallRecord.getSchema(), widerRecord.getSchema()));
+ assertTrue(DataTypeUtils.isRecordWider(widerRecord.getSchema(), smallRecord.getSchema()));
+
+ assertFalse(DataTypeUtils.isRecordWider(jane.getSchema(), janeWithAge.getSchema()));
+ assertTrue(DataTypeUtils.isRecordWider(janeWithAge.getSchema(), jane.getSchema()));
+ }
+
+
@Test
public void testWiderRecordDifferingFields() {
final Record firstRecord = DataTypeUtils.toRecord(Map.of(
diff --git a/nifi-docs/src/main/asciidoc/toolkit-guide.adoc b/nifi-docs/src/main/asciidoc/toolkit-guide.adoc
index b6ef3ec56563..70f0ea8286fa 100644
--- a/nifi-docs/src/main/asciidoc/toolkit-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/toolkit-guide.adoc
@@ -77,7 +77,6 @@ The following are available commands:
nifi pg-start
nifi pg-stop
nifi pg-create
- nifi pg-export
nifi pg-get-version
nifi pg-stop-version-control
nifi pg-change-version
@@ -92,6 +91,7 @@ The following are available commands:
nifi pg-get-param-context
nifi pg-set-param-context
nifi pg-replace
+ nifi pg-export
nifi get-services
nifi get-service
nifi create-service
@@ -106,6 +106,12 @@ The following are available commands:
nifi export-reporting-tasks
nifi export-reporting-task
nifi import-reporting-tasks
+ nifi create-flow-analysis-rule
+ nifi get-flow-analysis-rules
+ nifi get-flow-analysis-rule
+ nifi enable-flow-analysis-rules
+ nifi disable-flow-analysis-rules
+ nifi delete-flow-analysis-rule
nifi list-users
nifi create-user
nifi list-user-groups
@@ -296,21 +302,18 @@ For example, typing tab at an empty prompt should display possible commands for
Typing "nifi " and then a tab will show the sub-commands for NiFi:
#> nifi
- change-version-processor delete-reporting-task get-policy merge-param-context pg-replace update-user-group
- cluster-summary disable-services get-reg-client-id offload-node pg-set-param-context
- connect-node disconnect-node get-reporting-task pg-change-version pg-start
- create-param-context enable-services get-reporting-tasks pg-connect pg-status
- create-param-provider export-param-context get-root-id pg-create pg-stop
- create-reg-client export-reporting-task get-service pg-create-service pg-stop-version-control
- create-reporting-task export-reporting-tasks get-services pg-disable-services remove-inherited-param-contexts
- create-service fetch-params import-param-context pg-enable-services set-inherited-param-contexts
- create-user get-access-token import-reporting-tasks pg-export set-param
- create-user-group get-access-token-spnego list-param-contexts pg-get-all-versions set-param-provider-property
- current-user get-controller-configuration list-param-providers pg-get-param-context start-reporting-tasks
- delete-node get-node list-reg-clients pg-get-services stop-reporting-tasks
- delete-param get-nodes list-user-groups pg-get-version update-controller-configuration
- delete-param-context get-param-context list-users pg-import update-policy
- delete-param-provider get-param-provider logout-access-token pg-list update-reg-client
+ change-version-processor delete-flow-analysis-rule export-reporting-task get-policy list-user-groups pg-export pg-stop-version-control
+ cluster-summary delete-node export-reporting-tasks get-reg-client-id list-users pg-get-all-versions remove-inherited-param-contexts
+ connect-node delete-param fetch-params get-reporting-task logout-access-token pg-get-param-context set-inherited-param-contexts
+ create-flow-analysis-rule delete-param-context get-access-token get-reporting-tasks merge-param-context pg-get-services set-param
+ create-param-context delete-param-provider get-access-token-spnego get-root-id offload-node pg-get-version set-param-provider-property
+ create-param-provider delete-reporting-task get-controller-configuration get-service pg-change-all-versions pg-import start-reporting-tasks
+ create-reg-client disable-flow-analysis-rules get-flow-analysis-rule get-services pg-change-version pg-list stop-reporting-tasks
+ create-reporting-task disable-services get-flow-analysis-rules import-param-context pg-connect pg-replace update-controller-configuration
+ create-service disconnect-node get-node import-reporting-tasks pg-create pg-set-param-context update-policy
+ create-user enable-flow-analysis-rules get-nodes list-param-contexts pg-create-service pg-start update-reg-client
+ create-user-group enable-services get-param-context list-param-providers pg-disable-services pg-status update-user-group
+ current-user export-param-context get-param-provider list-reg-clients pg-enable-services pg-stop
Arguments that represent a path to a file, such as `-p` or when setting a properties file in the session, will auto-complete the path being typed:
diff --git a/nifi-extension-bom/pom.xml b/nifi-extension-bom/pom.xml
index 6cf9f75b4a38..57f806867fd9 100644
--- a/nifi-extension-bom/pom.xml
+++ b/nifi-extension-bom/pom.xml
@@ -238,7 +238,7 @@
org.eclipse.jdt
ecj
- 3.37.0
+ 3.38.0
provided
diff --git a/nifi-extension-bundles/nifi-azure-bundle/nifi-azure-processors/pom.xml b/nifi-extension-bundles/nifi-azure-bundle/nifi-azure-processors/pom.xml
index 0563412de0ac..798bdea31d01 100644
--- a/nifi-extension-bundles/nifi-azure-bundle/nifi-azure-processors/pom.xml
+++ b/nifi-extension-bundles/nifi-azure-bundle/nifi-azure-processors/pom.xml
@@ -111,7 +111,7 @@
io.projectreactor
reactor-core
- 3.6.6
+ 3.6.7
com.azure
@@ -191,7 +191,7 @@
io.projectreactor
reactor-test
- 3.6.6
+ 3.6.7
test
diff --git a/nifi-extension-bundles/nifi-azure-bundle/pom.xml b/nifi-extension-bundles/nifi-azure-bundle/pom.xml
index 9592a0179499..7fba44a572b0 100644
--- a/nifi-extension-bundles/nifi-azure-bundle/pom.xml
+++ b/nifi-extension-bundles/nifi-azure-bundle/pom.xml
@@ -29,7 +29,7 @@
1.2.24
- 1.15.0
+ 1.15.1
0.34.1
diff --git a/nifi-extension-bundles/nifi-box-bundle/pom.xml b/nifi-extension-bundles/nifi-box-bundle/pom.xml
index 4a473d90eedf..ad3d2e7671a0 100644
--- a/nifi-extension-bundles/nifi-box-bundle/pom.xml
+++ b/nifi-extension-bundles/nifi-box-bundle/pom.xml
@@ -39,7 +39,7 @@
com.box
box-java-sdk
- 4.9.1
+ 4.10.0
diff --git a/nifi-extension-bundles/nifi-elasticsearch-bundle/pom.xml b/nifi-extension-bundles/nifi-elasticsearch-bundle/pom.xml
index 6b9506f9b784..b2239d1bd438 100644
--- a/nifi-extension-bundles/nifi-elasticsearch-bundle/pom.xml
+++ b/nifi-extension-bundles/nifi-elasticsearch-bundle/pom.xml
@@ -33,7 +33,7 @@ language governing permissions and limitations under the License. -->
- 8.13.4
+ 8.14.1
@@ -88,7 +88,7 @@ language governing permissions and limitations under the License. -->
- 8.13.4
+ 8.14.1
s3cret
diff --git a/nifi-extension-bundles/nifi-email-bundle/nifi-email-processors/pom.xml b/nifi-extension-bundles/nifi-email-bundle/nifi-email-processors/pom.xml
index e67afa41cf29..4f390c21fe90 100644
--- a/nifi-extension-bundles/nifi-email-bundle/nifi-email-processors/pom.xml
+++ b/nifi-extension-bundles/nifi-email-bundle/nifi-email-processors/pom.xml
@@ -25,7 +25,7 @@
nifi-email-processors
jar
- 6.2.5
+ 6.3.0
5.2.5
diff --git a/nifi-extension-bundles/nifi-extension-utils/nifi-file-transfer/src/main/java/org/apache/nifi/processor/util/file/transfer/FileTransfer.java b/nifi-extension-bundles/nifi-extension-utils/nifi-file-transfer/src/main/java/org/apache/nifi/processor/util/file/transfer/FileTransfer.java
index 56f6f1bfa72a..969f51791bd7 100644
--- a/nifi-extension-bundles/nifi-extension-utils/nifi-file-transfer/src/main/java/org/apache/nifi/processor/util/file/transfer/FileTransfer.java
+++ b/nifi-extension-bundles/nifi-extension-utils/nifi-file-transfer/src/main/java/org/apache/nifi/processor/util/file/transfer/FileTransfer.java
@@ -22,6 +22,7 @@
import java.io.InputStream;
import java.util.List;
+import org.apache.nifi.components.AllowableValue;
import org.apache.nifi.components.PropertyDescriptor;
import org.apache.nifi.components.Validator;
import org.apache.nifi.expression.ExpressionLanguageScope;
@@ -207,17 +208,49 @@ default String getAbsolutePath(FlowFile flowFile, String remotePath) throws IOEx
public static final String FILE_MODIFY_DATE_ATTR_FORMAT = "yyyy-MM-dd'T'HH:mm:ssZ";
public static final String CONFLICT_RESOLUTION_REPLACE = "REPLACE";
+ AllowableValue CONFLICT_RESOLUTION_REPLACE_ALLOWABLE =
+ new AllowableValue(CONFLICT_RESOLUTION_REPLACE, CONFLICT_RESOLUTION_REPLACE,
+ "Remote file is replaced with new file, FlowFile goes to success");
+
public static final String CONFLICT_RESOLUTION_RENAME = "RENAME";
+ AllowableValue CONFLICT_RESOLUTION_RENAME_ALLOWABLE =
+ new AllowableValue(CONFLICT_RESOLUTION_RENAME, CONFLICT_RESOLUTION_RENAME,
+ "New file is renamed with a one-up number at the beginning, FlowFile goes to success");
+
public static final String CONFLICT_RESOLUTION_IGNORE = "IGNORE";
+ AllowableValue CONFLICT_RESOLUTION_IGNORE_ALLOWABLE =
+ new AllowableValue(CONFLICT_RESOLUTION_IGNORE, CONFLICT_RESOLUTION_IGNORE,
+ "File is not transferred, FlowFile goes to success");
+
public static final String CONFLICT_RESOLUTION_REJECT = "REJECT";
+ AllowableValue CONFLICT_RESOLUTION_REJECT_ALLOWABLE =
+ new AllowableValue(CONFLICT_RESOLUTION_REJECT, CONFLICT_RESOLUTION_REJECT,
+ "File is not transferred, FlowFile goes to reject");
+
public static final String CONFLICT_RESOLUTION_FAIL = "FAIL";
+ AllowableValue CONFLICT_RESOLUTION_FAIL_ALLOWABLE =
+ new AllowableValue(CONFLICT_RESOLUTION_FAIL, CONFLICT_RESOLUTION_FAIL,
+ "File is not transferred, FlowFile goes to failure");
+
public static final String CONFLICT_RESOLUTION_NONE = "NONE";
+ AllowableValue CONFLICT_RESOLUTION_NONE_ALLOWABLE =
+ new AllowableValue(CONFLICT_RESOLUTION_NONE, CONFLICT_RESOLUTION_NONE,
+ "Do not check for conflict before transfer, FlowFile goes to success or failure");
+
+ AllowableValue[] CONFLICT_RESOLUTION_ALLOWABLE_VALUES = new AllowableValue[] {
+ CONFLICT_RESOLUTION_REPLACE_ALLOWABLE,
+ CONFLICT_RESOLUTION_IGNORE_ALLOWABLE,
+ CONFLICT_RESOLUTION_RENAME_ALLOWABLE,
+ CONFLICT_RESOLUTION_REJECT_ALLOWABLE,
+ CONFLICT_RESOLUTION_FAIL_ALLOWABLE,
+ CONFLICT_RESOLUTION_NONE_ALLOWABLE};
+
public static final PropertyDescriptor CONFLICT_RESOLUTION = new PropertyDescriptor.Builder()
.name("Conflict Resolution")
.description("Determines how to handle the problem of filename collisions")
.required(true)
- .allowableValues(CONFLICT_RESOLUTION_REPLACE, CONFLICT_RESOLUTION_IGNORE, CONFLICT_RESOLUTION_RENAME, CONFLICT_RESOLUTION_REJECT, CONFLICT_RESOLUTION_FAIL, CONFLICT_RESOLUTION_NONE)
- .defaultValue(CONFLICT_RESOLUTION_NONE)
+ .allowableValues(CONFLICT_RESOLUTION_ALLOWABLE_VALUES)
+ .defaultValue(CONFLICT_RESOLUTION_NONE_ALLOWABLE)
.build();
public static final PropertyDescriptor REJECT_ZERO_BYTE = new PropertyDescriptor.Builder()
.name("Reject Zero-Byte Files")
diff --git a/nifi-extension-bundles/nifi-provenance-repository-bundle/pom.xml b/nifi-extension-bundles/nifi-provenance-repository-bundle/pom.xml
index db5104a49cde..2d87e5217945 100644
--- a/nifi-extension-bundles/nifi-provenance-repository-bundle/pom.xml
+++ b/nifi-extension-bundles/nifi-provenance-repository-bundle/pom.xml
@@ -27,7 +27,7 @@
nifi-provenance-repository-nar
- 9.10.0
+ 9.11.0
diff --git a/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/python/processor/FlowFileTransformProxy.java b/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/python/processor/FlowFileTransformProxy.java
index 073158f59be7..4d654d7c4eda 100644
--- a/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/python/processor/FlowFileTransformProxy.java
+++ b/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/python/processor/FlowFileTransformProxy.java
@@ -62,13 +62,18 @@ public void onTrigger(final ProcessContext context, final ProcessSession session
try {
final String relationshipName = result.getRelationship();
final Relationship relationship = new Relationship.Builder().name(relationshipName).build();
+ final Map attributes = result.getAttributes();
+
if (REL_FAILURE.getName().equals(relationshipName)) {
session.remove(transformed);
+ if (attributes != null) {
+ original = session.putAllAttributes(original, attributes);
+ }
+
session.transfer(original, REL_FAILURE);
return;
}
- final Map attributes = result.getAttributes();
if (attributes != null) {
transformed = session.putAllAttributes(transformed, attributes);
}
diff --git a/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/python/processor/RecordTransformProxy.java b/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/python/processor/RecordTransformProxy.java
index 00389f5b6129..91d774e130b2 100644
--- a/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/python/processor/RecordTransformProxy.java
+++ b/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-bridge/src/main/java/org/apache/nifi/python/processor/RecordTransformProxy.java
@@ -17,6 +17,7 @@
package org.apache.nifi.python.processor;
+import org.apache.commons.io.IOUtils;
import org.apache.nifi.NullSuppression;
import org.apache.nifi.annotation.behavior.InputRequirement;
import org.apache.nifi.annotation.behavior.InputRequirement.Requirement;
@@ -264,13 +265,16 @@ private void writeResult(final RecordTransformResult result, final Map originalAttributes = originalFlowFile.getAttributes();
final RecordSchema writeSchema = writerFactory.getSchema(originalAttributes, transformed.getSchema());
writer = writerFactory.createWriter(getLogger(), writeSchema, out, originalAttributes);
writer.beginRecordSet();
} catch (final Exception e) {
+ // If we failed to create the RecordSetWriter, ensure that we close the Output Stream
+ IOUtils.closeQuietly(out);
session.remove(destinationFlowFile);
throw e;
}
diff --git a/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-integration-tests/src/test/java/org.apache.nifi.py4j/PythonControllerInteractionIT.java b/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-integration-tests/src/test/java/org.apache.nifi.py4j/PythonControllerInteractionIT.java
index 1303b98ac9ac..f84ad13b2089 100644
--- a/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-integration-tests/src/test/java/org.apache.nifi.py4j/PythonControllerInteractionIT.java
+++ b/nifi-extension-bundles/nifi-py4j-bundle/nifi-py4j-integration-tests/src/test/java/org.apache.nifi.py4j/PythonControllerInteractionIT.java
@@ -30,10 +30,6 @@
import org.apache.nifi.python.PythonProcessConfig;
import org.apache.nifi.python.PythonProcessorDetails;
import org.apache.nifi.reporting.InitializationException;
-import org.apache.nifi.serialization.SimpleRecordSchema;
-import org.apache.nifi.serialization.record.RecordField;
-import org.apache.nifi.serialization.record.RecordFieldType;
-import org.apache.nifi.serialization.record.RecordSchema;
import org.apache.nifi.util.MockFlowFile;
import org.apache.nifi.util.TestRunner;
import org.apache.nifi.util.TestRunners;
@@ -52,7 +48,6 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Duration;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -559,16 +554,18 @@ public void testProcessRestarted() {
runner.assertTransferCount("success", 7);
}
- private RecordSchema createSimpleRecordSchema(final List fieldNames) {
- final List recordFields = new ArrayList<>();
- for (final String fieldName : fieldNames) {
- recordFields.add(new RecordField(fieldName, RecordFieldType.STRING.getDataType(), true));
- }
- final RecordSchema schema = new SimpleRecordSchema(recordFields);
- return schema;
- }
+ @Test
+ public void testRouteToFailureWithAttributes() {
+ final TestRunner runner = createFlowFileTransform("FailWithAttributes");
+ runner.enqueue("Hello, World");
+ runner.run();
+ runner.assertAllFlowFilesTransferred("failure", 1);
+ final MockFlowFile out = runner.getFlowFilesForRelationship("failure").getFirst();
+ out.assertAttributeEquals("number", "1");
+ out.assertAttributeEquals("failureReason", "Intentional failure of unit test");
+ }
public interface StringLookupService extends ControllerService {
Optional lookup(Map coordinates);
diff --git a/nifi-extension-bundles/nifi-py4j-bundle/nifi-python-test-extensions/src/main/resources/extensions/FailWithAttributes.py b/nifi-extension-bundles/nifi-py4j-bundle/nifi-python-test-extensions/src/main/resources/extensions/FailWithAttributes.py
new file mode 100644
index 000000000000..3ea1afab5bbf
--- /dev/null
+++ b/nifi-extension-bundles/nifi-py4j-bundle/nifi-python-test-extensions/src/main/resources/extensions/FailWithAttributes.py
@@ -0,0 +1,29 @@
+# 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.
+
+from nifiapi.flowfiletransform import FlowFileTransform, FlowFileTransformResult
+
+class FailWithAttributes(FlowFileTransform):
+ class Java:
+ implements = ['org.apache.nifi.python.processor.FlowFileTransform']
+ class ProcessorDetails:
+ version = '0.0.1-SNAPSHOT'
+ description = 'Routes a FlowFile to failure and adds attributes to it.'
+
+ def __init__(self, **kwargs):
+ pass
+
+ def transform(self, context, flowFile):
+ return FlowFileTransformResult(relationship="failure", attributes={"number": "1", "failureReason": "Intentional failure of unit test"})
diff --git a/nifi-extension-bundles/nifi-slack-bundle/nifi-slack-processors/pom.xml b/nifi-extension-bundles/nifi-slack-bundle/nifi-slack-processors/pom.xml
index a577a77b433f..cee35376df49 100644
--- a/nifi-extension-bundles/nifi-slack-bundle/nifi-slack-processors/pom.xml
+++ b/nifi-extension-bundles/nifi-slack-bundle/nifi-slack-processors/pom.xml
@@ -30,7 +30,7 @@
com.slack.api
bolt-socket-mode
- 1.39.3
+ 1.40.0
diff --git a/nifi-extension-bundles/nifi-standard-shared-bundle/nifi-standard-shared-bom/pom.xml b/nifi-extension-bundles/nifi-standard-shared-bundle/nifi-standard-shared-bom/pom.xml
index 8c00d25cc50a..4503d2e3a7cb 100644
--- a/nifi-extension-bundles/nifi-standard-shared-bundle/nifi-standard-shared-bom/pom.xml
+++ b/nifi-extension-bundles/nifi-standard-shared-bundle/nifi-standard-shared-bom/pom.xml
@@ -121,7 +121,7 @@
org.checkerframework
checker-qual
- 3.43.0
+ 3.44.0
provided
diff --git a/nifi-framework-bundle/pom.xml b/nifi-framework-bundle/pom.xml
index b58bdc616589..b6b6de056cd6 100644
--- a/nifi-framework-bundle/pom.xml
+++ b/nifi-framework-bundle/pom.xml
@@ -317,7 +317,7 @@
io.dropwizard.metrics
metrics-core
- 4.2.25
+ 4.2.26
org.apache.curator
diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/interceptors/auth.interceptor.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/interceptors/auth.interceptor.ts
index 7b003d886d0f..d7b64a43c9ae 100644
--- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/interceptors/auth.interceptor.ts
+++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/interceptors/auth.interceptor.ts
@@ -17,7 +17,7 @@
import { inject } from '@angular/core';
import { HttpErrorResponse, HttpHandlerFn, HttpInterceptorFn, HttpRequest } from '@angular/common/http';
-import { catchError, map, take, combineLatest, tap } from 'rxjs';
+import { catchError, take, combineLatest, tap, NEVER, switchMap } from 'rxjs';
import { Store } from '@ngrx/store';
import { NiFiState } from '../../state';
import { fullScreenError, setRoutedToFullScreenError } from '../../state/error/error.actions';
@@ -43,7 +43,7 @@ export const authInterceptor: HttpInterceptorFn = (request: HttpRequest
tap(() => store.dispatch(setRoutedToFullScreenError({ routedToFullScreenError: true })))
)
]).pipe(
- map(([currentUserState, loginConfiguration, routedToFullScreenError]) => {
+ switchMap(([currentUserState, loginConfiguration, routedToFullScreenError]) => {
if (
currentUserState.status === 'pending' &&
loginConfiguration?.loginSupported &&
@@ -61,7 +61,7 @@ export const authInterceptor: HttpInterceptorFn = (request: HttpRequest
);
}
- throw errorResponse;
+ return NEVER;
})
);
} else {
diff --git a/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/interceptors/polling.interceptor.ts b/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/interceptors/polling.interceptor.ts
index c444c2caa0f1..9862a3bc119f 100644
--- a/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/interceptors/polling.interceptor.ts
+++ b/nifi-frontend/src/main/frontend/apps/nifi/src/app/service/interceptors/polling.interceptor.ts
@@ -17,24 +17,36 @@
import { inject } from '@angular/core';
import { HttpErrorResponse, HttpHandlerFn, HttpInterceptorFn, HttpRequest } from '@angular/common/http';
-import { tap } from 'rxjs';
+import { catchError, NEVER } from 'rxjs';
import { NiFiState } from '../../state';
import { Store } from '@ngrx/store';
import { stopCurrentUserPolling } from '../../state/current-user/current-user.actions';
import { stopProcessGroupPolling } from '../../pages/flow-designer/state/flow/flow.actions';
import { stopClusterSummaryPolling } from '../../state/cluster-summary/cluster-summary.actions';
+import { fullScreenError } from '../../state/error/error.actions';
export const pollingInterceptor: HttpInterceptorFn = (request: HttpRequest, next: HttpHandlerFn) => {
const store: Store = inject(Store);
return next(request).pipe(
- tap({
- error: (error) => {
- if (error instanceof HttpErrorResponse && error.status === 0) {
- store.dispatch(stopCurrentUserPolling());
- store.dispatch(stopProcessGroupPolling());
- store.dispatch(stopClusterSummaryPolling());
- }
+ catchError((errorResponse) => {
+ if (errorResponse instanceof HttpErrorResponse && errorResponse.status === 0) {
+ store.dispatch(stopCurrentUserPolling());
+ store.dispatch(stopProcessGroupPolling());
+ store.dispatch(stopClusterSummaryPolling());
+
+ store.dispatch(
+ fullScreenError({
+ errorDetail: {
+ title: 'Unable to communicate with NiFi',
+ message: 'Please ensure the application is running and check the logs for any errors.'
+ }
+ })
+ );
+
+ return NEVER;
+ } else {
+ throw errorResponse;
}
})
);
diff --git a/nifi-registry/pom.xml b/nifi-registry/pom.xml
index e51e3ffe5667..45389dd8416a 100644
--- a/nifi-registry/pom.xml
+++ b/nifi-registry/pom.xml
@@ -36,10 +36,10 @@
3.3.0
- 10.13.0
+ 10.15.0
10.0.0
3.12.0
- 6.9.0.202403050737-r
+ 6.10.0.202406032230-r
2.12.1
diff --git a/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/flowanalysis/SensitiveDynamicPropertiesFlowAnalysisRule.java b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/flowanalysis/SensitiveDynamicPropertiesFlowAnalysisRule.java
index 7c6f73f2a7e9..b58e760cbd33 100644
--- a/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/flowanalysis/SensitiveDynamicPropertiesFlowAnalysisRule.java
+++ b/nifi-system-tests/nifi-system-test-extensions-bundle/nifi-system-test-extensions/src/main/java/org/apache/nifi/flowanalysis/SensitiveDynamicPropertiesFlowAnalysisRule.java
@@ -28,7 +28,6 @@ protected PropertyDescriptor getSupportedDynamicPropertyDescriptor(final String
return new PropertyDescriptor.Builder().name(propertyName)
.addValidator(Validator.VALID)
.dynamic(true)
- .sensitive(true)
.build();
}
}
diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiClientUtil.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiClientUtil.java
index cf2e190fee42..8e62196f6e0b 100644
--- a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiClientUtil.java
+++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiClientUtil.java
@@ -39,6 +39,7 @@
import org.apache.nifi.web.api.dto.CounterDTO;
import org.apache.nifi.web.api.dto.CountersSnapshotDTO;
import org.apache.nifi.web.api.dto.DifferenceDTO;
+import org.apache.nifi.web.api.dto.FlowAnalysisRuleDTO;
import org.apache.nifi.web.api.dto.FlowFileSummaryDTO;
import org.apache.nifi.web.api.dto.FlowRegistryClientDTO;
import org.apache.nifi.web.api.dto.FlowSnippetDTO;
@@ -76,6 +77,9 @@
import org.apache.nifi.web.api.entity.CopySnippetRequestEntity;
import org.apache.nifi.web.api.entity.CountersEntity;
import org.apache.nifi.web.api.entity.DropRequestEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleRunStatusEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRulesEntity;
import org.apache.nifi.web.api.entity.FlowComparisonEntity;
import org.apache.nifi.web.api.entity.FlowEntity;
import org.apache.nifi.web.api.entity.FlowFileEntity;
@@ -395,6 +399,141 @@ public void deleteReportingTasks() throws NiFiClientException, IOException {
}
}
+ public FlowAnalysisRuleEntity createFlowAnalysisRule(final String type) throws NiFiClientException, IOException {
+ return createFlowAnalysisRule(NiFiSystemIT.TEST_FLOW_ANALYSIS_RULE_PACKAGE + "." + type, getTestBundle());
+ }
+
+ public FlowAnalysisRuleEntity createFlowAnalysisRule(final String type, final BundleDTO bundle) throws NiFiClientException, IOException {
+ final FlowAnalysisRuleDTO dto = new FlowAnalysisRuleDTO();
+ dto.setBundle(bundle);
+ dto.setType(type);
+
+ final FlowAnalysisRuleEntity entity = new FlowAnalysisRuleEntity();
+ entity.setComponent(dto);
+ entity.setRevision(createNewRevision());
+ entity.setDisconnectedNodeAcknowledged(true);
+
+ final FlowAnalysisRuleEntity flowAnalysisRule = nifiClient.getControllerClient().createFlowAnalysisRule(entity);
+ logger.info("Created Flow Analysis Rule [type={}, id={}] for Test [{}]", simpleName(type), flowAnalysisRule.getId(), testName);
+
+ return flowAnalysisRule;
+ }
+
+ public FlowAnalysisRuleEntity updateFlowAnalysisRuleProperties(final FlowAnalysisRuleEntity currentEntity, final Map properties) throws NiFiClientException, IOException {
+ final FlowAnalysisRuleDTO ruleDto = new FlowAnalysisRuleDTO();
+ ruleDto.setProperties(properties);
+ ruleDto.setId(currentEntity.getId());
+
+ final FlowAnalysisRuleEntity updatedEntity = new FlowAnalysisRuleEntity();
+ updatedEntity.setRevision(currentEntity.getRevision());
+ updatedEntity.setComponent(ruleDto);
+ updatedEntity.setId(currentEntity.getId());
+ updatedEntity.setDisconnectedNodeAcknowledged(true);
+
+ return nifiClient.getControllerClient().updateFlowAnalysisRule(updatedEntity);
+
+ }
+
+ public FlowAnalysisRuleEntity enableFlowAnalysisRule(final FlowAnalysisRuleEntity entity) throws NiFiClientException, IOException {
+ final FlowAnalysisRuleRunStatusEntity runStatusEntity = new FlowAnalysisRuleRunStatusEntity();
+ runStatusEntity.setState("ENABLED");
+ runStatusEntity.setRevision(entity.getRevision());
+ runStatusEntity.setDisconnectedNodeAcknowledged(true);
+
+ return nifiClient.getControllerClient().activateFlowAnalysisRule(entity.getId(), runStatusEntity);
+ }
+
+ public FlowAnalysisRuleEntity disableFlowAnalysisRule(final FlowAnalysisRuleEntity entity) throws NiFiClientException, IOException {
+ final FlowAnalysisRuleRunStatusEntity runStatusEntity = new FlowAnalysisRuleRunStatusEntity();
+ runStatusEntity.setState("DISABLED");
+ runStatusEntity.setRevision(entity.getRevision());
+ runStatusEntity.setDisconnectedNodeAcknowledged(true);
+
+ return nifiClient.getControllerClient().activateFlowAnalysisRule(entity.getId(), runStatusEntity);
+ }
+
+ public void disableFlowAnalysisRules() throws NiFiClientException, IOException {
+ final FlowAnalysisRulesEntity rules = nifiClient.getControllerClient().getFlowAnalysisRules();
+
+ Collection toBeDisabledRuleIds = new ArrayList<>();
+ for (final FlowAnalysisRuleEntity rule : rules.getFlowAnalysisRules()) {
+ disableFlowAnalysisRule(rule);
+ toBeDisabledRuleIds.add(rule.getId());
+ }
+
+ waitForFlowAnalysisRuleState("DISABLED", toBeDisabledRuleIds);
+ }
+
+ public void deleteFlowAnalysisRules() throws NiFiClientException, IOException {
+ final FlowAnalysisRulesEntity rulesEntity = nifiClient.getControllerClient().getFlowAnalysisRules();
+ for (final FlowAnalysisRuleEntity taskEntity : rulesEntity.getFlowAnalysisRules()) {
+ taskEntity.setDisconnectedNodeAcknowledged(true);
+ nifiClient.getControllerClient().deleteFlowAnalysisRule(taskEntity);
+ }
+ }
+
+ public void waitForFlowAnalysisRuleState(final String desiredState, final Collection ruleIdsOfInterest) throws NiFiClientException, IOException {
+ final long maxTimestamp = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(2L);
+
+ while (System.currentTimeMillis() < maxTimestamp) {
+ final List flowAnalysisRulesNotInState = getFlowAnalysisRulesNotInState(desiredState, ruleIdsOfInterest);
+ if (flowAnalysisRulesNotInState.isEmpty()) {
+ logger.info("Flow Analysis Rules have desired state [{}]", desiredState);
+ return;
+ }
+
+ final FlowAnalysisRuleEntity entity = flowAnalysisRulesNotInState.get(0);
+ logger.info(
+ "Flow Analysis Rule ID [{}] Type [{}] State [{}] waiting for State [{}]: sleeping for 500 ms before retrying",
+ entity.getId(), entity.getComponent().getType(), entity.getComponent().getState(), desiredState
+ );
+
+ try {
+ Thread.sleep(500L);
+ } catch (final Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ public List getFlowAnalysisRulesNotInState(final String desiredState, final Collection ruleIds) throws NiFiClientException, IOException {
+ final FlowAnalysisRulesEntity rulesEntity = nifiClient.getControllerClient().getFlowAnalysisRules();
+
+ return rulesEntity.getFlowAnalysisRules().stream()
+ .filter(rule -> ruleIds == null || ruleIds.isEmpty() || ruleIds.contains(rule.getId()))
+ .filter(rule -> !desiredState.equalsIgnoreCase(rule.getComponent().getState()))
+ .collect(Collectors.toList());
+ }
+
+ public void waitForFlowAnalysisRuleValid(final String reportingTaskId) throws NiFiClientException, IOException {
+ waitForFlowAnalysisRuleValidationStatus(reportingTaskId, "Valid");
+ }
+
+ public void waitForFlowAnalysisRuleValidationStatus(final String flowAnalysisRuleId, final String validationStatus) throws NiFiClientException, IOException {
+ final long maxTimestamp = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(2L);
+
+ while (System.currentTimeMillis() < maxTimestamp) {
+ final FlowAnalysisRuleEntity flowAnalysisRuleEntity = nifiClient.getControllerClient().getFlowAnalysisRule(flowAnalysisRuleId);
+ final String currentValidationStatus = flowAnalysisRuleEntity.getStatus().getValidationStatus();
+ if (validationStatus.equalsIgnoreCase(currentValidationStatus)) {
+ logger.info("Flow Analysis Rule ID [{}] Type [{}] Validation Status [{}] matched",
+ flowAnalysisRuleId, flowAnalysisRuleEntity.getComponent().getType(), validationStatus
+ );
+ return;
+ }
+
+ logger.info("Flow Analysis Rule ID [{}] Type [{}] Validation Status [{}] waiting for [{}]: sleeping for 500 ms before retrying",
+ flowAnalysisRuleEntity, flowAnalysisRuleEntity.getComponent().getType(), currentValidationStatus, validationStatus
+ );
+
+ try {
+ Thread.sleep(500L);
+ } catch (final Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
public ParameterEntity createParameterEntity(final String name, final String description, final boolean sensitive, final String value) {
final ParameterDTO dto = new ParameterDTO();
dto.setName(name);
@@ -1678,6 +1817,27 @@ public List verifyReportingTaskConfig(final String
return results.getRequest().getResults();
}
+ public List verifyFlowAnalysisRuleConfig(final String ruleId, final Map properties)
+ throws InterruptedException, IOException, NiFiClientException {
+
+ final VerifyConfigRequestDTO requestDto = new VerifyConfigRequestDTO();
+ requestDto.setComponentId(ruleId);
+ requestDto.setProperties(properties);
+
+ final VerifyConfigRequestEntity verificationRequest = new VerifyConfigRequestEntity();
+ verificationRequest.setRequest(requestDto);
+
+ VerifyConfigRequestEntity results = nifiClient.getControllerClient().submitFlowAnalysisRuleConfigVerificationRequest(verificationRequest);
+ while ((!results.getRequest().isComplete()) || (results.getRequest().getResults() == null)) {
+ Thread.sleep(50L);
+ results = nifiClient.getControllerClient().getFlowAnalysisRuleConfigVerificationRequest(ruleId, results.getRequest().getRequestId());
+ }
+
+ nifiClient.getControllerClient().deleteFlowAnalysisRuleConfigVerificationRequest(ruleId, results.getRequest().getRequestId());
+
+ return results.getRequest().getResults();
+ }
+
public ReportingTaskEntity createReportingTask(final String type, final String bundleGroupId, final String artifactId, final String version)
throws NiFiClientException, IOException {
diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemIT.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemIT.java
index e816c2d0000f..57e2dbe0c607 100644
--- a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemIT.java
+++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/NiFiSystemIT.java
@@ -88,6 +88,7 @@ public abstract class NiFiSystemIT implements NiFiInstanceProvider {
public static final String TEST_PROCESSORS_PACKAGE = "org.apache.nifi.processors.tests.system";
public static final String TEST_CS_PACKAGE = "org.apache.nifi.cs.tests.system";
public static final String TEST_REPORTING_TASK_PACKAGE = "org.apache.nifi.reporting";
+ public static final String TEST_FLOW_ANALYSIS_RULE_PACKAGE = "org.apache.nifi.flowanalysis";
private static final Pattern FRAMEWORK_NAR_PATTERN = Pattern.compile("nifi-framework-nar-(.*?)\\.nar");
private static final File LIB_DIR = new File("target/nifi-lib-assembly/lib");
@@ -242,10 +243,12 @@ protected void destroyFlow() throws NiFiClientException, IOException, Interrupte
getClientUtil().disableControllerServices("root", true);
getClientUtil().stopReportingTasks();
getClientUtil().disableControllerLevelServices();
+ getClientUtil().disableFlowAnalysisRules();
getClientUtil().stopTransmitting("root");
getClientUtil().deleteAll("root");
getClientUtil().deleteControllerLevelServices();
getClientUtil().deleteReportingTasks();
+ getClientUtil().deleteFlowAnalysisRules();
logger.info("Finished destroyFlow");
}
diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/flowanalysisrule/FlowAnalysisRuleIT.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/flowanalysisrule/FlowAnalysisRuleIT.java
new file mode 100644
index 000000000000..21b7a80a16f9
--- /dev/null
+++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/flowanalysisrule/FlowAnalysisRuleIT.java
@@ -0,0 +1,108 @@
+/*
+ * 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.tests.system.flowanalysisrule;
+
+import org.apache.nifi.tests.system.NiFiSystemIT;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.web.api.dto.FlowAnalysisRuleDTO;
+import org.apache.nifi.web.api.dto.PropertyDescriptorDTO;
+import org.apache.nifi.web.api.entity.PropertyDescriptorEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class FlowAnalysisRuleIT extends NiFiSystemIT {
+
+
+ public static final String SENSITIVE_PROPERTY_NAME = "SensitiveProperty";
+
+ private static final String SENSITIVE_PROPERTY_VALUE = "SensitiveValue";
+
+ private static final Set SENSITIVE_DYNAMIC_PROPERTY_NAMES = Collections.singleton(SENSITIVE_PROPERTY_NAME);
+
+ @Test
+ public void testGetPropertyDescriptor() throws NiFiClientException, IOException {
+ final FlowAnalysisRuleEntity flowAnalysisRuleEntity = getClientUtil().createFlowAnalysisRule("SensitiveDynamicPropertiesFlowAnalysisRule");
+
+ final PropertyDescriptorEntity propertyDescriptorEntity = getNifiClient().getControllerClient().getFlowAnalysisRulePropertyDescriptor(
+ flowAnalysisRuleEntity.getId(),
+ SENSITIVE_PROPERTY_NAME,
+ null
+ );
+ final PropertyDescriptorDTO propertyDescriptor = propertyDescriptorEntity.getPropertyDescriptor();
+ assertFalse(propertyDescriptor.isSensitive());
+ assertTrue(propertyDescriptor.isDynamic());
+
+ final PropertyDescriptorEntity sensitivePropertyDescriptorEntity = getNifiClient().getControllerClient().getFlowAnalysisRulePropertyDescriptor(
+ flowAnalysisRuleEntity.getId(),
+ SENSITIVE_PROPERTY_NAME,
+ true
+ );
+ final PropertyDescriptorDTO sensitivePropertyDescriptor = sensitivePropertyDescriptorEntity.getPropertyDescriptor();
+ assertTrue(sensitivePropertyDescriptor.isSensitive());
+ assertTrue(sensitivePropertyDescriptor.isDynamic());
+ }
+
+ @Test
+ public void testSensitiveDynamicPropertiesNotSupported() throws NiFiClientException, IOException {
+ final FlowAnalysisRuleEntity flowAnalysisRuleEntity = getClientUtil().createFlowAnalysisRule("ControllerServiceReferencingFlowAnalysisRule");
+ final FlowAnalysisRuleDTO component = flowAnalysisRuleEntity.getComponent();
+ assertFalse(component.getSupportsSensitiveDynamicProperties());
+
+ component.setSensitiveDynamicPropertyNames(SENSITIVE_DYNAMIC_PROPERTY_NAMES);
+
+ getNifiClient().getControllerClient().updateFlowAnalysisRule(flowAnalysisRuleEntity);
+
+ getClientUtil().waitForFlowAnalysisRuleValidationStatus(flowAnalysisRuleEntity.getId(), FlowAnalysisRuleDTO.INVALID);
+ }
+
+ @Test
+ public void testSensitiveDynamicPropertiesSupportedConfigured() throws NiFiClientException, IOException {
+ final FlowAnalysisRuleEntity flowAnalysisRuleEntity = getClientUtil().createFlowAnalysisRule("SensitiveDynamicPropertiesFlowAnalysisRule");
+ final FlowAnalysisRuleDTO component = flowAnalysisRuleEntity.getComponent();
+ assertTrue(component.getSupportsSensitiveDynamicProperties());
+
+ component.setSensitiveDynamicPropertyNames(SENSITIVE_DYNAMIC_PROPERTY_NAMES);
+ component.setProperties(Collections.singletonMap(SENSITIVE_PROPERTY_NAME, SENSITIVE_PROPERTY_VALUE));
+
+ getNifiClient().getControllerClient().updateFlowAnalysisRule(flowAnalysisRuleEntity);
+
+ final FlowAnalysisRuleEntity updatedFlowAnalysisRuleEntity = getNifiClient().getControllerClient().getFlowAnalysisRule(flowAnalysisRuleEntity.getId());
+ final FlowAnalysisRuleDTO updatedComponent = updatedFlowAnalysisRuleEntity.getComponent();
+
+ final Map properties = updatedComponent.getProperties();
+ assertNotSame(SENSITIVE_PROPERTY_VALUE, properties.get(SENSITIVE_PROPERTY_NAME));
+
+ final Map descriptors = updatedComponent.getDescriptors();
+ final PropertyDescriptorDTO descriptor = descriptors.get(SENSITIVE_PROPERTY_NAME);
+ assertNotNull(descriptor);
+ assertTrue(descriptor.isSensitive());
+ assertTrue(descriptor.isDynamic());
+
+ getClientUtil().waitForFlowAnalysisRuleValid(flowAnalysisRuleEntity.getId());
+ }
+}
diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/verification/ClusteredVerifiableFlowAnalysisRuleSystemIT.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/verification/ClusteredVerifiableFlowAnalysisRuleSystemIT.java
new file mode 100644
index 000000000000..b79b58526b60
--- /dev/null
+++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/verification/ClusteredVerifiableFlowAnalysisRuleSystemIT.java
@@ -0,0 +1,59 @@
+/*
+ * 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.tests.system.verification;
+
+import org.apache.nifi.components.ConfigVerificationResult.Outcome;
+import org.apache.nifi.tests.system.NiFiInstanceFactory;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.web.api.dto.ConfigVerificationResultDTO;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class ClusteredVerifiableFlowAnalysisRuleSystemIT extends VerifiableFlowAnalysisRuleSystemIT {
+ @Override
+ public NiFiInstanceFactory getInstanceFactory() {
+ return createTwoNodeInstanceFactory();
+ }
+
+ @Test
+ public void testDifferentResultsFromDifferentNodes() throws InterruptedException, IOException, NiFiClientException {
+ final FlowAnalysisRuleEntity rule = getClientUtil().createFlowAnalysisRule("EnsureFlowAnalysisRuleConfigurationCorrect");
+
+ final Map properties = new HashMap<>();
+ properties.put("Successful Verification", "true");
+ properties.put("Failure Node Number", "2");
+ getClientUtil().updateFlowAnalysisRuleProperties(rule, properties);
+
+ final List resultList = getClientUtil().verifyFlowAnalysisRuleConfig(rule.getId(), properties);
+ assertEquals(3, resultList.size());
+
+ // First verification result will be component validation.
+ assertEquals(Outcome.SUCCESSFUL.name(), resultList.get(0).getOutcome());
+ // Second verification result will be verification results
+ assertEquals(Outcome.SUCCESSFUL.name(), resultList.get(1).getOutcome());
+ // Third verification result is for Fail On Primary Node
+ // assertEquals(Outcome.FAILED.name(), resultList.get(2).getOutcome()); // NIFI-9717
+ }
+}
diff --git a/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/verification/VerifiableFlowAnalysisRuleSystemIT.java b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/verification/VerifiableFlowAnalysisRuleSystemIT.java
new file mode 100644
index 000000000000..74f08f84fbbc
--- /dev/null
+++ b/nifi-system-tests/nifi-system-test-suite/src/test/java/org/apache/nifi/tests/system/verification/VerifiableFlowAnalysisRuleSystemIT.java
@@ -0,0 +1,159 @@
+/*
+ * 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.tests.system.verification;
+
+import org.apache.nifi.components.ConfigVerificationResult.Outcome;
+import org.apache.nifi.tests.system.NiFiSystemIT;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.web.api.dto.ConfigVerificationResultDTO;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class VerifiableFlowAnalysisRuleSystemIT extends NiFiSystemIT {
+
+ @Test
+ public void testVerificationWithValidConfigWhenComponentValid() throws NiFiClientException, IOException, InterruptedException {
+ final FlowAnalysisRuleEntity rule = getClientUtil().createFlowAnalysisRule("EnsureFlowAnalysisRuleConfigurationCorrect");
+
+ final Map properties = Collections.singletonMap("Successful Verification", "true");
+ getClientUtil().updateFlowAnalysisRuleProperties(rule, properties);
+
+ final List resultList = getClientUtil().verifyFlowAnalysisRuleConfig(rule.getId(), properties);
+ assertEquals(3, resultList.size());
+
+ // First verification result will be component validation.
+ assertEquals(Outcome.SUCCESSFUL.name(), resultList.get(0).getOutcome());
+ // Second verification result will be verification results
+ assertEquals(Outcome.SUCCESSFUL.name(), resultList.get(1).getOutcome());
+ // Third verification result is for Fail On Primary Node
+ assertEquals(Outcome.SKIPPED.name(), resultList.get(2).getOutcome());
+ }
+
+
+ @Test
+ public void testVerifyWithInvalidConfigWhenComponentValid() throws NiFiClientException, IOException, InterruptedException {
+ final FlowAnalysisRuleEntity rule = getClientUtil().createFlowAnalysisRule("EnsureFlowAnalysisRuleConfigurationCorrect");
+
+ final Map properties = Collections.singletonMap("Successful Verification", "true");
+ getClientUtil().updateFlowAnalysisRuleProperties(rule, properties);
+
+ // Verify with properties that will give us failed verification
+ final Map invalidProperties = Collections.singletonMap("Successful Verification", "false");
+ final List resultList = getClientUtil().verifyFlowAnalysisRuleConfig(rule.getId(), invalidProperties);
+ assertEquals(3, resultList.size());
+
+ // First verification result will be component validation.
+ assertEquals(Outcome.SUCCESSFUL.name(), resultList.get(0).getOutcome());
+ // Second verification result will be FAILED because the 'Successful Verification' property is set to false
+ assertEquals(Outcome.FAILED.name(), resultList.get(1).getOutcome());
+ // Third verification result is for Fail On Primary Node
+ assertEquals(Outcome.SKIPPED.name(), resultList.get(2).getOutcome());
+ }
+
+ @Test
+ public void testVerificationWithValidConfigWhenComponentInvalid() throws NiFiClientException, IOException, InterruptedException {
+ final FlowAnalysisRuleEntity rule = getClientUtil().createFlowAnalysisRule("EnsureFlowAnalysisRuleConfigurationCorrect");
+
+ final Map invalidProperties = Collections.singletonMap("Successful Verification", "foo");
+ getClientUtil().updateFlowAnalysisRuleProperties(rule, invalidProperties);
+
+ final Map validProperties = Collections.singletonMap("Successful Verification", "true");
+ final List resultList = getClientUtil().verifyFlowAnalysisRuleConfig(rule.getId(), validProperties);
+ assertEquals(3, resultList.size());
+
+ assertEquals(Outcome.SUCCESSFUL.name(), resultList.get(0).getOutcome());
+ assertEquals(Outcome.SUCCESSFUL.name(), resultList.get(1).getOutcome());
+ assertEquals(Outcome.SKIPPED.name(), resultList.get(2).getOutcome());
+ }
+
+ @Test
+ public void testVerifyWithInvalidConfigWhenComponentInvalid() throws InterruptedException, IOException, NiFiClientException {
+ final FlowAnalysisRuleEntity rule = getClientUtil().createFlowAnalysisRule("EnsureFlowAnalysisRuleConfigurationCorrect");
+
+ final Map invalidProperties = Collections.singletonMap("Successful Verification", "foo");
+ getClientUtil().updateFlowAnalysisRuleProperties(rule, invalidProperties);
+
+ final Map otherInvalidProperties = Collections.singletonMap("Successful Verification", "bar");
+ final List resultList = getClientUtil().verifyFlowAnalysisRuleConfig(rule.getId(), otherInvalidProperties);
+ assertEquals(1, resultList.size());
+
+ for (final ConfigVerificationResultDTO resultDto : resultList) {
+ assertEquals(Outcome.FAILED.name(), resultDto.getOutcome());
+ }
+ }
+
+ @Test
+ public void testVerificationWithValidConfigWhenComponentRunning() throws IOException, NiFiClientException {
+ final FlowAnalysisRuleEntity rule = getClientUtil().createFlowAnalysisRule("EnsureFlowAnalysisRuleConfigurationCorrect");
+
+ final Map properties = Collections.singletonMap("Successful Verification", "true");
+ getClientUtil().updateFlowAnalysisRuleProperties(rule, properties);
+
+ getClientUtil().enableFlowAnalysisRule(rule);
+
+ assertThrows(NiFiClientException.class, () -> getClientUtil().verifyFlowAnalysisRuleConfig(rule.getId(), properties));
+ }
+
+
+ @Test
+ public void testVerifyWhenExceptionThrown() throws InterruptedException, IOException, NiFiClientException {
+ final FlowAnalysisRuleEntity rule = getClientUtil().createFlowAnalysisRule("EnsureFlowAnalysisRuleConfigurationCorrect");
+
+ final Map properties = new HashMap<>();
+ properties.put("Successful Verification", "true");
+ properties.put("Exception on Verification", "true");
+ getClientUtil().updateFlowAnalysisRuleProperties(rule, properties);
+
+ final List resultList = getClientUtil().verifyFlowAnalysisRuleConfig(rule.getId(), properties);
+ assertEquals(2, resultList.size());
+
+ // Results should show that validation is successful but that there was a failure in performing verification
+ assertEquals(Outcome.SUCCESSFUL.name(), resultList.get(0).getOutcome());
+ assertEquals(Outcome.FAILED.name(), resultList.get(1).getOutcome());
+ }
+
+ @Test
+ public void testValidProcessorWithoutVerifiableFlowAnalysisRuleAnnotation() throws NiFiClientException, IOException, InterruptedException {
+ final FlowAnalysisRuleEntity rule = getClientUtil().createFlowAnalysisRule("SensitiveDynamicPropertiesFlowAnalysisRule");
+
+ // Even though rule does not implement VerifiableFlowAnalysisRule, validation should still be run
+ final List resultList = getClientUtil().verifyFlowAnalysisRuleConfig(rule.getId(), Collections.emptyMap());
+ assertEquals(1, resultList.size());
+
+ assertEquals(Outcome.SUCCESSFUL.name(), resultList.get(0).getOutcome());
+ }
+
+ @Test
+ public void testInvalidConfigForRuleWithoutVerifiableFlowAnalysisRuleAnnotation() throws NiFiClientException, IOException, InterruptedException {
+ final FlowAnalysisRuleEntity rule = getClientUtil().createFlowAnalysisRule("ControllerServiceReferencingFlowAnalysisRule");
+
+ final List resultList = getClientUtil().verifyFlowAnalysisRuleConfig(rule.getId(), Collections.emptyMap());
+ assertEquals(1, resultList.size());
+
+ assertEquals(Outcome.FAILED.name(), resultList.get(0).getOutcome());
+ }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ControllerClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ControllerClient.java
index 34b3cdce29ac..2d002abad48d 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ControllerClient.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/ControllerClient.java
@@ -19,11 +19,16 @@
import org.apache.nifi.web.api.entity.ClusterEntity;
import org.apache.nifi.web.api.entity.ControllerConfigurationEntity;
import org.apache.nifi.web.api.entity.ControllerServiceEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleRunStatusEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRulesEntity;
import org.apache.nifi.web.api.entity.FlowRegistryClientEntity;
import org.apache.nifi.web.api.entity.FlowRegistryClientsEntity;
import org.apache.nifi.web.api.entity.NodeEntity;
import org.apache.nifi.web.api.entity.ParameterProviderEntity;
+import org.apache.nifi.web.api.entity.PropertyDescriptorEntity;
import org.apache.nifi.web.api.entity.ReportingTaskEntity;
+import org.apache.nifi.web.api.entity.VerifyConfigRequestEntity;
import org.apache.nifi.web.api.entity.VersionedReportingTaskImportRequestEntity;
import org.apache.nifi.web.api.entity.VersionedReportingTaskImportResponseEntity;
@@ -61,6 +66,26 @@ public interface ControllerClient {
VersionedReportingTaskImportResponseEntity importReportingTasks(VersionedReportingTaskImportRequestEntity importRequestEntity)
throws NiFiClientException, IOException;
+ FlowAnalysisRulesEntity getFlowAnalysisRules() throws NiFiClientException, IOException;
+
+ FlowAnalysisRuleEntity getFlowAnalysisRule(final String id) throws NiFiClientException, IOException;
+
+ PropertyDescriptorEntity getFlowAnalysisRulePropertyDescriptor(final String componentId, final String propertyName, final Boolean sensitive) throws NiFiClientException, IOException;
+
+ FlowAnalysisRuleEntity createFlowAnalysisRule(FlowAnalysisRuleEntity reportingTask) throws NiFiClientException, IOException;
+
+ FlowAnalysisRuleEntity updateFlowAnalysisRule(final FlowAnalysisRuleEntity flowAnalysisRuleEntity) throws NiFiClientException, IOException;
+
+ FlowAnalysisRuleEntity activateFlowAnalysisRule(final String id, final FlowAnalysisRuleRunStatusEntity runStatusEntity) throws NiFiClientException, IOException;
+
+ FlowAnalysisRuleEntity deleteFlowAnalysisRule(final FlowAnalysisRuleEntity flowAnalysisRule) throws NiFiClientException, IOException;
+
+ VerifyConfigRequestEntity submitFlowAnalysisRuleConfigVerificationRequest(final VerifyConfigRequestEntity configRequestEntity) throws NiFiClientException, IOException;
+
+ VerifyConfigRequestEntity getFlowAnalysisRuleConfigVerificationRequest(final String taskId, final String verificationRequestId) throws NiFiClientException, IOException;
+
+ VerifyConfigRequestEntity deleteFlowAnalysisRuleConfigVerificationRequest(final String taskId, final String verificationRequestId) throws NiFiClientException, IOException;
+
ParameterProviderEntity createParamProvider(ParameterProviderEntity paramProvider) throws NiFiClientException, IOException;
ControllerConfigurationEntity getControllerConfiguration() throws NiFiClientException, IOException;
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyControllerClient.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyControllerClient.java
index f621f4e6ed83..e3e4b086aba8 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyControllerClient.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/client/nifi/impl/JerseyControllerClient.java
@@ -20,21 +20,29 @@
import org.apache.nifi.toolkit.cli.impl.client.nifi.ControllerClient;
import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
import org.apache.nifi.toolkit.cli.impl.client.nifi.RequestConfig;
+import org.apache.nifi.web.api.dto.RevisionDTO;
+
+import jakarta.ws.rs.client.Entity;
+import jakarta.ws.rs.client.WebTarget;
+import jakarta.ws.rs.core.MediaType;
import org.apache.nifi.web.api.entity.ClusterEntity;
import org.apache.nifi.web.api.entity.ControllerConfigurationEntity;
import org.apache.nifi.web.api.entity.ControllerServiceEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleRunStatusEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRulesEntity;
import org.apache.nifi.web.api.entity.FlowRegistryClientEntity;
import org.apache.nifi.web.api.entity.FlowRegistryClientsEntity;
import org.apache.nifi.web.api.entity.NodeEntity;
import org.apache.nifi.web.api.entity.ParameterProviderEntity;
+import org.apache.nifi.web.api.entity.PropertyDescriptorEntity;
import org.apache.nifi.web.api.entity.ReportingTaskEntity;
+import org.apache.nifi.web.api.entity.VerifyConfigRequestEntity;
import org.apache.nifi.web.api.entity.VersionedReportingTaskImportRequestEntity;
import org.apache.nifi.web.api.entity.VersionedReportingTaskImportResponseEntity;
-import jakarta.ws.rs.client.Entity;
-import jakarta.ws.rs.client.WebTarget;
-import jakarta.ws.rs.core.MediaType;
import java.io.IOException;
+import java.util.Objects;
/**
* Jersey implementation of ControllerClient.
@@ -246,6 +254,184 @@ public VersionedReportingTaskImportResponseEntity importReportingTasks(Versioned
});
}
+ @Override
+ public FlowAnalysisRulesEntity getFlowAnalysisRules() throws NiFiClientException, IOException {
+ return executeAction("Error retrieving flow analysis rules", () -> {
+ final WebTarget target = controllerTarget.path("flow-analysis-rules");
+ return getRequestBuilder(target).get(FlowAnalysisRulesEntity.class);
+ });
+ }
+
+ @Override
+ public FlowAnalysisRuleEntity getFlowAnalysisRule(final String id) throws NiFiClientException, IOException {
+ if (StringUtils.isBlank(id)) {
+ throw new IllegalArgumentException("Flow analysis rule id cannot be null");
+ }
+
+ return executeAction("Error retrieving status of flow analysis rule", () -> {
+ final WebTarget target = controllerTarget.path("flow-analysis-rules/{id}").resolveTemplate("id", id);
+ return getRequestBuilder(target).get(FlowAnalysisRuleEntity.class);
+ });
+ }
+
+ @Override
+ public PropertyDescriptorEntity getFlowAnalysisRulePropertyDescriptor(final String componentId, final String propertyName, final Boolean sensitive) throws NiFiClientException, IOException {
+ Objects.requireNonNull(componentId, "Component ID required");
+ Objects.requireNonNull(propertyName, "Property Name required");
+
+ return executeAction("Error retrieving Flow Analysis Rule Property Descriptor", () -> {
+ final WebTarget target = controllerTarget
+ .path("flow-analysis-rules/{id}/descriptors").resolveTemplate("id", componentId)
+ .queryParam("propertyName", propertyName)
+ .queryParam("sensitive", sensitive);
+
+ return getRequestBuilder(target).get(PropertyDescriptorEntity.class);
+ });
+ }
+
+ @Override
+ public FlowAnalysisRuleEntity createFlowAnalysisRule(FlowAnalysisRuleEntity flowAnalysisRule) throws NiFiClientException, IOException {
+ if (flowAnalysisRule == null) {
+ throw new IllegalArgumentException("Flow analysis rule entity cannot be null");
+ }
+
+ return executeAction("Error creating flow analysis rule", () -> {
+ final WebTarget target = controllerTarget.path("flow-analysis-rules");
+
+ return getRequestBuilder(target).post(
+ Entity.entity(flowAnalysisRule, MediaType.APPLICATION_JSON),
+ FlowAnalysisRuleEntity.class
+ );
+ });
+ }
+
+ @Override
+ public FlowAnalysisRuleEntity updateFlowAnalysisRule(final FlowAnalysisRuleEntity flowAnalysisRuleEntity) throws NiFiClientException, IOException {
+ if (flowAnalysisRuleEntity == null) {
+ throw new IllegalArgumentException("Flow Analysis Rule cannot be null");
+ }
+ if (flowAnalysisRuleEntity.getComponent() == null) {
+ throw new IllegalArgumentException("Component cannot be null");
+ }
+
+ return executeAction("Error updating Flow Analysis Rule", () -> {
+ final WebTarget target = controllerTarget.path("flow-analysis-rules/{id}").resolveTemplate("id", flowAnalysisRuleEntity.getId());
+ return getRequestBuilder(target).put(
+ Entity.entity(flowAnalysisRuleEntity, MediaType.APPLICATION_JSON_TYPE),
+ FlowAnalysisRuleEntity.class);
+ });
+ }
+
+ @Override
+ public FlowAnalysisRuleEntity activateFlowAnalysisRule(
+ final String id,
+ final FlowAnalysisRuleRunStatusEntity runStatusEntity
+ ) throws NiFiClientException, IOException {
+ if (StringUtils.isBlank(id)) {
+ throw new IllegalArgumentException("Flow analysis rule id cannot be null");
+ }
+
+ if (runStatusEntity == null) {
+ throw new IllegalArgumentException("Entity cannot be null");
+ }
+
+ return executeAction("Error enabling or disabling flow analysis rule", () -> {
+ final WebTarget target = controllerTarget
+ .path("flow-analysis-rules/{id}/run-status").resolveTemplate("id", id);
+ return getRequestBuilder(target).put(
+ Entity.entity(runStatusEntity, MediaType.APPLICATION_JSON_TYPE),
+ FlowAnalysisRuleEntity.class
+ );
+ });
+ }
+
+ @Override
+ public FlowAnalysisRuleEntity deleteFlowAnalysisRule(final FlowAnalysisRuleEntity flowAnalysisRule) throws NiFiClientException, IOException {
+ if (flowAnalysisRule == null) {
+ throw new IllegalArgumentException("Flow Analysis Rule Entity cannot be null");
+ }
+ if (flowAnalysisRule.getId() == null) {
+ throw new IllegalArgumentException("Flow Analysis Rule ID cannot be null");
+ }
+
+ final RevisionDTO revision = flowAnalysisRule.getRevision();
+ if (revision == null) {
+ throw new IllegalArgumentException("Revision cannot be null");
+ }
+
+ return executeAction("Error deleting Flow Analysis Rule", () -> {
+ WebTarget target = controllerTarget
+ .path("flow-analysis-rules/{id}").resolveTemplate("id", flowAnalysisRule.getId())
+ .queryParam("version", revision.getVersion())
+ .queryParam("clientId", revision.getClientId());
+
+ if (flowAnalysisRule.isDisconnectedNodeAcknowledged() == Boolean.TRUE) {
+ target = target.queryParam("disconnectedNodeAcknowledged", "true");
+ }
+
+ return getRequestBuilder(target).delete(FlowAnalysisRuleEntity.class);
+ });
+ }
+
+ @Override
+ public VerifyConfigRequestEntity submitFlowAnalysisRuleConfigVerificationRequest(final VerifyConfigRequestEntity configRequestEntity) throws NiFiClientException, IOException {
+ if (configRequestEntity == null) {
+ throw new IllegalArgumentException("Config Request Entity cannot be null");
+ }
+ if (configRequestEntity.getRequest() == null) {
+ throw new IllegalArgumentException("Config Request DTO cannot be null");
+ }
+ if (configRequestEntity.getRequest().getComponentId() == null) {
+ throw new IllegalArgumentException("Flow Analysis Rule ID cannot be null");
+ }
+ if (configRequestEntity.getRequest().getProperties() == null) {
+ throw new IllegalArgumentException("Flow Analysis Rule properties cannot be null");
+ }
+
+ return executeAction("Error submitting Flow Analysis Rule Config Verification Request", () -> {
+ final WebTarget target = controllerTarget
+ .path("flow-analysis-rules/{id}/config/verification-requests")
+ .resolveTemplate("id", configRequestEntity.getRequest().getComponentId());
+
+ return getRequestBuilder(target).post(
+ Entity.entity(configRequestEntity, MediaType.APPLICATION_JSON_TYPE),
+ VerifyConfigRequestEntity.class
+ );
+ });
+ }
+
+ @Override
+ public VerifyConfigRequestEntity getFlowAnalysisRuleConfigVerificationRequest(final String taskId, final String verificationRequestId) throws NiFiClientException, IOException {
+ if (verificationRequestId == null) {
+ throw new IllegalArgumentException("Verification Request ID cannot be null");
+ }
+
+ return executeAction("Error retrieving Flow Analysis Rule Config Verification Request", () -> {
+ final WebTarget target = controllerTarget
+ .path("flow-analysis-rules/{id}/config/verification-requests/{requestId}")
+ .resolveTemplate("id", taskId)
+ .resolveTemplate("requestId", verificationRequestId);
+
+ return getRequestBuilder(target).get(VerifyConfigRequestEntity.class);
+ });
+ }
+
+ @Override
+ public VerifyConfigRequestEntity deleteFlowAnalysisRuleConfigVerificationRequest(final String taskId, final String verificationRequestId) throws NiFiClientException, IOException {
+ if (verificationRequestId == null) {
+ throw new IllegalArgumentException("Verification Request ID cannot be null");
+ }
+
+ return executeAction("Error deleting Flow Analysis Rule Config Verification Request", () -> {
+ final WebTarget target = controllerTarget
+ .path("flow-analysis-rules/{id}/config/verification-requests/{requestId}")
+ .resolveTemplate("id", taskId)
+ .resolveTemplate("requestId", verificationRequestId);
+
+ return getRequestBuilder(target).delete(VerifyConfigRequestEntity.class);
+ });
+ }
+
@Override
public ParameterProviderEntity createParamProvider(final ParameterProviderEntity paramProvider) throws NiFiClientException, IOException {
if (paramProvider == null) {
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
index 900ed791cde7..21fbe51b850e 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/CommandOption.java
@@ -105,6 +105,9 @@ public enum CommandOption {
// NiFi - Reporting Tasks
RT_ID("rt", "reportingTaskId", "The id of a reporting task", true),
+ // NiFi - Flow Analysis Rules
+ FAR_ID("far", "flowAnalysisRuleId", "The id of a flow analysis rule", true),
+
// NiFi - User/Group
USER_NAME("un", "userName", "The name of a user", true),
USER_ID("ui", "userIdentifier", "The identifier of a user", true),
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
index 21fc6779ac6c..a5af68399595 100644
--- a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/NiFiCommandGroup.java
@@ -27,11 +27,17 @@
import org.apache.nifi.toolkit.cli.impl.command.nifi.cs.GetControllerService;
import org.apache.nifi.toolkit.cli.impl.command.nifi.cs.GetControllerServices;
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.ClusterSummary;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.CreateFlowAnalysisRule;
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.CreateReportingTask;
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.CurrentUser;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.DeleteFlowAnalysisRule;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.DisableFlowAnalysisRules;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.EnableFlowAnalysisRules;
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.ExportReportingTask;
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.ExportReportingTasks;
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.GetControllerConfiguration;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.GetFlowAnalysisRule;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.GetFlowAnalysisRules;
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.GetReportingTask;
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.GetReportingTasks;
import org.apache.nifi.toolkit.cli.impl.command.nifi.flow.GetRootId;
@@ -160,6 +166,12 @@ protected List createCommands() {
commands.add(new ExportReportingTasks());
commands.add(new ExportReportingTask());
commands.add(new ImportReportingTasks());
+ commands.add(new CreateFlowAnalysisRule());
+ commands.add(new GetFlowAnalysisRules());
+ commands.add(new GetFlowAnalysisRule());
+ commands.add(new EnableFlowAnalysisRules());
+ commands.add(new DisableFlowAnalysisRules());
+ commands.add(new DeleteFlowAnalysisRule());
commands.add(new ListUsers());
commands.add(new CreateUser());
commands.add(new ListUserGroups());
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/CreateFlowAnalysisRule.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/CreateFlowAnalysisRule.java
new file mode 100644
index 000000000000..e87ae017c83d
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/CreateFlowAnalysisRule.java
@@ -0,0 +1,79 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.flow;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.io.IOUtils;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ControllerClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.StringResult;
+import org.apache.nifi.toolkit.cli.impl.util.JacksonUtils;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Paths;
+import java.util.Properties;
+
+/**
+ * Command for creating a flow analysis rule.
+ */
+public class CreateFlowAnalysisRule extends AbstractNiFiCommand {
+
+ public CreateFlowAnalysisRule() {
+ super("create-flow-analysis-rule", StringResult.class);
+ }
+
+ @Override
+ public String getDescription() {
+ return "Creates a flow analysis rule from a local file.";
+ }
+
+ @Override
+ public void doInitialize(final Context context) {
+ addOption(CommandOption.INPUT_SOURCE.createOption());
+ }
+
+ @Override
+ public StringResult doExecute(final NiFiClient client, final Properties properties)
+ throws NiFiClientException, IOException, MissingOptionException, CommandException {
+ final String inputFile = getRequiredArg(properties, CommandOption.INPUT_SOURCE);
+ final URI uri = Paths.get(inputFile).toAbsolutePath().toUri();
+ final String contents = IOUtils.toString(uri, StandardCharsets.UTF_8);
+
+ final ObjectMapper objectMapper = JacksonUtils.getObjectMapper();
+ final FlowAnalysisRuleEntity deserializedTask = objectMapper.readValue(contents, FlowAnalysisRuleEntity.class);
+ if (deserializedTask == null) {
+ throw new IOException("Unable to deserialize flow analysis rule from " + inputFile);
+ }
+
+ deserializedTask.setRevision(getInitialRevisionDTO());
+
+ final ControllerClient controllerClient = client.getControllerClient();
+ final FlowAnalysisRuleEntity createdEntity = controllerClient.createFlowAnalysisRule(deserializedTask);
+
+ return new StringResult(String.valueOf(createdEntity.getId()), getContext().isInteractive());
+ }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/DeleteFlowAnalysisRule.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/DeleteFlowAnalysisRule.java
new file mode 100644
index 000000000000..20ca24c00021
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/DeleteFlowAnalysisRule.java
@@ -0,0 +1,65 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.flow;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ControllerClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.nifi.FlowAnalysisRuleResult;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Command for deleting a flow analysis rule.
+ */
+public class DeleteFlowAnalysisRule extends AbstractNiFiCommand {
+
+ public DeleteFlowAnalysisRule() {
+ super("delete-flow-analysis-rule", FlowAnalysisRuleResult.class);
+ }
+
+ @Override
+ public String getDescription() {
+ return "Delete a flow analysis rule.";
+ }
+
+ @Override
+ public void doInitialize(final Context context) {
+ addOption(CommandOption.FAR_ID.createOption());
+ }
+
+ @Override
+ public FlowAnalysisRuleResult doExecute(final NiFiClient client, final Properties properties)
+ throws NiFiClientException, IOException, MissingOptionException, CommandException {
+
+ final String flowAnalysisRuleId = getRequiredArg(properties, CommandOption.FAR_ID);
+
+ final ControllerClient controllerClient = client.getControllerClient();
+ FlowAnalysisRuleEntity flowAnalysisRule = controllerClient.getFlowAnalysisRule(flowAnalysisRuleId);
+ final FlowAnalysisRuleEntity deletedFlowAnalysisRuleEntity = controllerClient.deleteFlowAnalysisRule(flowAnalysisRule);
+
+ return new FlowAnalysisRuleResult(getResultType(properties), deletedFlowAnalysisRuleEntity);
+ }
+
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/DisableFlowAnalysisRules.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/DisableFlowAnalysisRules.java
new file mode 100644
index 000000000000..ab152abb650b
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/DisableFlowAnalysisRules.java
@@ -0,0 +1,98 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.flow;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiActivateCommand;
+import org.apache.nifi.toolkit.cli.impl.result.VoidResult;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleRunStatusEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRulesEntity;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * Command for disabling flow analysis rule.
+ */
+public class DisableFlowAnalysisRules extends AbstractNiFiActivateCommand {
+
+ public DisableFlowAnalysisRules() {
+ super("disable-flow-analysis-rules");
+ }
+
+ @Override
+ public String getDescription() {
+ return "Attempts to disable one or all flow analysis rule(s). In stand-alone mode this command " +
+ "will not produce all of the output seen in interactive mode unless the --verbose argument is specified.";
+ }
+
+ @Override
+ protected void doInitialize(final Context context) {
+ addOption(CommandOption.FAR_ID.createOption());
+ }
+
+ @Override
+ public VoidResult doExecute(
+ final NiFiClient client,
+ final Properties properties
+ ) throws NiFiClientException, IOException, MissingOptionException, CommandException {
+ final String ruleId = getArg(properties, CommandOption.FAR_ID);
+ final Set ruleEntities = new HashSet<>();
+
+ if (StringUtils.isBlank(ruleId)) {
+ final FlowAnalysisRulesEntity rulesEntity = client.getControllerClient().getFlowAnalysisRules();
+ ruleEntities.addAll(rulesEntity.getFlowAnalysisRules());
+ } else {
+ ruleEntities.add(client.getControllerClient().getFlowAnalysisRule(ruleId));
+ }
+
+ activate(client, properties, ruleEntities, "DISABLED");
+
+ return VoidResult.getInstance();
+ }
+
+ @Override
+ public FlowAnalysisRuleRunStatusEntity getRunStatusEntity() {
+ return new FlowAnalysisRuleRunStatusEntity();
+ }
+
+ @Override
+ public FlowAnalysisRuleEntity activateComponent(
+ final NiFiClient client,
+ final FlowAnalysisRuleEntity ruleEntity,
+ final FlowAnalysisRuleRunStatusEntity runStatusEntity
+ ) throws NiFiClientException, IOException {
+ return client.getControllerClient().activateFlowAnalysisRule(ruleEntity.getId(), runStatusEntity);
+ }
+
+ @Override
+ public String getDispName(final FlowAnalysisRuleEntity ruleEntity) {
+ return "Flow analysis rule \"" + ruleEntity.getComponent().getName() + "\" " +
+ "(id: " + ruleEntity.getId() + ")";
+ }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/EnableFlowAnalysisRules.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/EnableFlowAnalysisRules.java
new file mode 100644
index 000000000000..7ee2d8ae04a6
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/EnableFlowAnalysisRules.java
@@ -0,0 +1,98 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.flow;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiActivateCommand;
+import org.apache.nifi.toolkit.cli.impl.result.VoidResult;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleRunStatusEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRulesEntity;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+/**
+ * Command for enabling flow analysis rule.
+ */
+public class EnableFlowAnalysisRules extends AbstractNiFiActivateCommand {
+
+ public EnableFlowAnalysisRules() {
+ super("enable-flow-analysis-rules");
+ }
+
+ @Override
+ public String getDescription() {
+ return "Attempts to enable one or all flow analysis rule(s). In stand-alone mode this command " +
+ "will not produce all of the output seen in interactive mode unless the --verbose argument is specified.";
+ }
+
+ @Override
+ protected void doInitialize(final Context context) {
+ addOption(CommandOption.FAR_ID.createOption());
+ }
+
+ @Override
+ public VoidResult doExecute(
+ final NiFiClient client,
+ final Properties properties
+ ) throws NiFiClientException, IOException, MissingOptionException, CommandException {
+ final String ruleId = getArg(properties, CommandOption.FAR_ID);
+ final Set ruleEntities = new HashSet<>();
+
+ if (StringUtils.isBlank(ruleId)) {
+ final FlowAnalysisRulesEntity rulesEntity = client.getControllerClient().getFlowAnalysisRules();
+ ruleEntities.addAll(rulesEntity.getFlowAnalysisRules());
+ } else {
+ ruleEntities.add(client.getControllerClient().getFlowAnalysisRule(ruleId));
+ }
+
+ activate(client, properties, ruleEntities, "ENABLED");
+
+ return VoidResult.getInstance();
+ }
+
+ @Override
+ public FlowAnalysisRuleRunStatusEntity getRunStatusEntity() {
+ return new FlowAnalysisRuleRunStatusEntity();
+ }
+
+ @Override
+ public FlowAnalysisRuleEntity activateComponent(
+ final NiFiClient client,
+ final FlowAnalysisRuleEntity ruleEntity,
+ final FlowAnalysisRuleRunStatusEntity runStatusEntity
+ ) throws NiFiClientException, IOException {
+ return client.getControllerClient().activateFlowAnalysisRule(ruleEntity.getId(), runStatusEntity);
+ }
+
+ @Override
+ public String getDispName(final FlowAnalysisRuleEntity ruleEntity) {
+ return "Flow analysis rule \"" + ruleEntity.getComponent().getName() + "\" " +
+ "(id: " + ruleEntity.getId() + ")";
+ }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetFlowAnalysisRule.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetFlowAnalysisRule.java
new file mode 100644
index 000000000000..8279789e9b59
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetFlowAnalysisRule.java
@@ -0,0 +1,62 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.flow;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.api.Context;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ControllerClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.command.CommandOption;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.nifi.FlowAnalysisRuleResult;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Command for retrieving a flow analysis rule.
+ */
+public class GetFlowAnalysisRule extends AbstractNiFiCommand {
+
+ public GetFlowAnalysisRule() {
+ super("get-flow-analysis-rule", FlowAnalysisRuleResult.class);
+ }
+
+ @Override
+ public String getDescription() {
+ return "Retrieves a flow analysis rule.";
+ }
+
+ @Override
+ protected void doInitialize(final Context context) {
+ addOption(CommandOption.FAR_ID.createOption());
+ }
+
+ @Override
+ public FlowAnalysisRuleResult doExecute(final NiFiClient client, final Properties properties)
+ throws NiFiClientException, IOException, MissingOptionException, CommandException {
+ final String ruleId = getRequiredArg(properties, CommandOption.FAR_ID);
+ final ControllerClient controllerClient = client.getControllerClient();
+
+ final FlowAnalysisRuleEntity ruleEntity = controllerClient.getFlowAnalysisRule(ruleId);
+ return new FlowAnalysisRuleResult(getResultType(properties), ruleEntity);
+ }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetFlowAnalysisRules.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetFlowAnalysisRules.java
new file mode 100644
index 000000000000..aa9e2734d657
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/command/nifi/flow/GetFlowAnalysisRules.java
@@ -0,0 +1,52 @@
+/*
+ * 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.toolkit.cli.impl.command.nifi.flow;
+
+import org.apache.commons.cli.MissingOptionException;
+import org.apache.nifi.toolkit.cli.api.CommandException;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.ControllerClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClient;
+import org.apache.nifi.toolkit.cli.impl.client.nifi.NiFiClientException;
+import org.apache.nifi.toolkit.cli.impl.command.nifi.AbstractNiFiCommand;
+import org.apache.nifi.toolkit.cli.impl.result.nifi.FlowAnalysisRulesResult;
+import org.apache.nifi.web.api.entity.FlowAnalysisRulesEntity;
+
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * Command to get the list of flow analysis rules.
+ */
+public class GetFlowAnalysisRules extends AbstractNiFiCommand {
+
+ public GetFlowAnalysisRules() {
+ super("get-flow-analysis-rules", FlowAnalysisRulesResult.class);
+ }
+
+ @Override
+ public String getDescription() {
+ return "Retrieves the list of flow analysis rules.";
+ }
+
+ @Override
+ public FlowAnalysisRulesResult doExecute(NiFiClient client, Properties properties)
+ throws NiFiClientException, IOException, MissingOptionException, CommandException {
+ final ControllerClient controllerClient = client.getControllerClient();
+ final FlowAnalysisRulesEntity tasksEntity = controllerClient.getFlowAnalysisRules();
+ return new FlowAnalysisRulesResult(getResultType(properties), tasksEntity);
+ }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/FlowAnalysisRuleResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/FlowAnalysisRuleResult.java
new file mode 100644
index 000000000000..f97b41f415f4
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/FlowAnalysisRuleResult.java
@@ -0,0 +1,53 @@
+/*
+ * 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.toolkit.cli.impl.result.nifi;
+
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.result.AbstractWritableResult;
+import org.apache.nifi.web.api.dto.BundleDTO;
+import org.apache.nifi.web.api.dto.FlowAnalysisRuleDTO;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Objects;
+
+public class FlowAnalysisRuleResult extends AbstractWritableResult {
+
+ private final FlowAnalysisRuleEntity flowAnalysisRuleEntity;
+
+ public FlowAnalysisRuleResult(final ResultType resultType, final FlowAnalysisRuleEntity flowAnalysisRuleEntity) {
+ super(resultType);
+ this.flowAnalysisRuleEntity = Objects.requireNonNull(flowAnalysisRuleEntity);
+ }
+
+ @Override
+ public FlowAnalysisRuleEntity getResult() {
+ return flowAnalysisRuleEntity;
+ }
+
+ @Override
+ protected void writeSimpleResult(final PrintStream output) throws IOException {
+ final FlowAnalysisRuleDTO flowAnalysisRuleDTO = flowAnalysisRuleEntity.getComponent();
+
+ final BundleDTO bundle = flowAnalysisRuleDTO.getBundle();
+ output.printf("Name : %s\nID : %s\nType : %s\nBundle: %s - %s %s\nState : %s\n",
+ flowAnalysisRuleDTO.getName(), flowAnalysisRuleDTO.getId(), flowAnalysisRuleDTO.getType(),
+ bundle.getGroup(), bundle.getArtifact(), bundle.getVersion(), flowAnalysisRuleDTO.getState());
+ }
+}
diff --git a/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/FlowAnalysisRulesResult.java b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/FlowAnalysisRulesResult.java
new file mode 100644
index 000000000000..02e16d97fdd9
--- /dev/null
+++ b/nifi-toolkit/nifi-toolkit-cli/src/main/java/org/apache/nifi/toolkit/cli/impl/result/nifi/FlowAnalysisRulesResult.java
@@ -0,0 +1,88 @@
+/*
+ * 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.toolkit.cli.impl.result.nifi;
+
+import org.apache.nifi.toolkit.cli.api.ResultType;
+import org.apache.nifi.toolkit.cli.impl.result.AbstractWritableResult;
+import org.apache.nifi.toolkit.cli.impl.result.writer.DynamicTableWriter;
+import org.apache.nifi.toolkit.cli.impl.result.writer.Table;
+import org.apache.nifi.toolkit.cli.impl.result.writer.TableWriter;
+import org.apache.nifi.web.api.dto.FlowAnalysisRuleDTO;
+import org.apache.nifi.web.api.entity.FlowAnalysisRuleEntity;
+import org.apache.nifi.web.api.entity.FlowAnalysisRulesEntity;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Result for FlowAnalysisRulesEntity.
+ */
+public class FlowAnalysisRulesResult extends AbstractWritableResult {
+
+ private final FlowAnalysisRulesEntity flowAnalysisRulesEntity;
+
+ public FlowAnalysisRulesResult(final ResultType resultType, final FlowAnalysisRulesEntity flowAnalysisRulesEntity) {
+ super(resultType);
+ this.flowAnalysisRulesEntity = Objects.requireNonNull(flowAnalysisRulesEntity);
+ }
+
+ @Override
+ protected void writeSimpleResult(final PrintStream output) throws IOException {
+ final Set ruleEntities = flowAnalysisRulesEntity.getFlowAnalysisRules();
+ if (ruleEntities == null) {
+ return;
+ }
+
+ final List ruleDTOS = ruleEntities.stream()
+ .map(FlowAnalysisRuleEntity::getComponent)
+ .sorted(Comparator.comparing(FlowAnalysisRuleDTO::getName))
+ .collect(Collectors.toList());
+
+ final Table table = new Table.Builder()
+ .column("#", 3, 3, false)
+ .column("Name", 5, 40, true)
+ .column("ID", 36, 36, false)
+ .column("Type", 5, 40, true)
+ .column("State", 10, 20, false)
+ .build();
+
+ for (int i = 0; i < ruleDTOS.size(); i++) {
+ final FlowAnalysisRuleDTO ruleDTO = ruleDTOS.get(i);
+ final String[] typeSplit = ruleDTO.getType().split("\\.", -1);
+ table.addRow(
+ String.valueOf(i + 1),
+ ruleDTO.getName(),
+ ruleDTO.getId(),
+ typeSplit[typeSplit.length - 1],
+ ruleDTO.getState()
+ );
+ }
+
+ final TableWriter tableWriter = new DynamicTableWriter();
+ tableWriter.write(table, output);
+ }
+
+ @Override
+ public FlowAnalysisRulesEntity getResult() {
+ return flowAnalysisRulesEntity;
+ }
+}
diff --git a/pom.xml b/pom.xml
index e87b8f7a8709..641e6fe9b644 100644
--- a/pom.xml
+++ b/pom.xml
@@ -110,8 +110,8 @@
UTF-8
UTF-8
2014
- 1.12.733
- 2.25.63
+ 1.12.742
+ 2.26.1
2.10.1
6.13.0
1.9.24
@@ -121,9 +121,9 @@
1.17.0
1.26.2
1.5.6-3
- 2.10.1
+ 2.11.0
3.14.0
- 3.11.0
+ 3.11.1
2.16.1
1.12.0
4.5.14
@@ -133,7 +133,7 @@
2.0.13
2.9.0
10.17.1.0
- 12.0.9
+ 12.0.10
2.17.1
1.11.3
4.0.5
@@ -153,9 +153,9 @@
5.12.0
3.10.6.Final
2.2
- 4.1.110.Final
+ 4.1.111.Final
6.0.0
- 6.1.8
+ 6.1.9
6.3.0
2.2.22
2.2.224