This is an automated email from the ASF dual-hosted git repository.
kdoran pushed a commit to branch NIFI-15258
in repository https://gitbox.apache.org/repos/asf/nifi-api.git
The following commit(s) were added to refs/heads/NIFI-15258 by this push:
new 8b20ea8 NIFI-15315 Add support for assets in Connectors (#31)
8b20ea8 is described below
commit 8b20ea8105f46ec5a86e247218170132ee0714a5
Author: Bryan Bende <[email protected]>
AuthorDate: Wed Dec 17 16:28:41 2025 -0500
NIFI-15315 Add support for assets in Connectors (#31)
- Add ownerIdentifier in Asset and deprecate parameterContextIdentifier
- Add ASSET and ASSET_LIST types for Connector properties and update
validation
- Support multiple asset references
Signed-off-by: Kevin Doran <[email protected]>
---
src/main/java/org/apache/nifi/asset/Asset.java | 9 ++
.../nifi/components/connector/AssetReference.java | 25 +++--
.../connector/ConnectorPropertyDescriptor.java | 53 ++++++++--
.../nifi/components/connector/PropertyType.java | 4 +-
.../flow/VersionedConnectorValueReference.java | 25 ++---
.../connector/TestConnectorPropertyDescriptor.java | 114 ++++++++++++++++++++-
.../org/apache/nifi/parameter/TestParameter.java | 5 +
7 files changed, 203 insertions(+), 32 deletions(-)
diff --git a/src/main/java/org/apache/nifi/asset/Asset.java
b/src/main/java/org/apache/nifi/asset/Asset.java
index 3e8c026..2ea52b2 100644
--- a/src/main/java/org/apache/nifi/asset/Asset.java
+++ b/src/main/java/org/apache/nifi/asset/Asset.java
@@ -37,9 +37,18 @@ public interface Asset {
* Returns the identifier of the parameter context the Asset belongs to
*
* @return Parameter Context Identifier
+ * @deprecated Use {@link #getOwnerIdentifier()} instead
*/
+ @Deprecated
String getParameterContextIdentifier();
+ /**
+ * Returns the identifier of the resource the Asset belongs to
+ *
+ * @return Owner Identifier
+ */
+ String getOwnerIdentifier();
+
/**
* Returns the name of the Asset
*
diff --git
a/src/main/java/org/apache/nifi/components/connector/AssetReference.java
b/src/main/java/org/apache/nifi/components/connector/AssetReference.java
index 13abf73..1115745 100644
--- a/src/main/java/org/apache/nifi/components/connector/AssetReference.java
+++ b/src/main/java/org/apache/nifi/components/connector/AssetReference.java
@@ -17,26 +17,29 @@
package org.apache.nifi.components.connector;
+import java.util.Collections;
+import java.util.HashSet;
import java.util.Objects;
+import java.util.Set;
/**
- * A ConnectorValueReference implementation representing a reference to an
asset.
+ * A ConnectorValueReference implementation representing a reference to one or
more assets.
*/
public final class AssetReference implements ConnectorValueReference {
- private final String assetIdentifier;
+ private final Set<String> assetIdentifiers;
- public AssetReference(final String assetIdentifier) {
- this.assetIdentifier = assetIdentifier;
+ public AssetReference(final Set<String> assetIdentifiers) {
+ this.assetIdentifiers = assetIdentifiers == null ?
Collections.emptySet() : new HashSet<>(assetIdentifiers);
}
/**
- * Returns the asset identifier.
+ * Returns the asset identifiers.
*
- * @return the asset identifier
+ * @return the asset identifiers
*/
- public String getAssetIdentifier() {
- return assetIdentifier;
+ public Set<String> getAssetIdentifiers() {
+ return assetIdentifiers;
}
@Override
@@ -53,16 +56,16 @@ public final class AssetReference implements
ConnectorValueReference {
return false;
}
final AssetReference that = (AssetReference) object;
- return Objects.equals(assetIdentifier, that.assetIdentifier);
+ return Objects.equals(assetIdentifiers, that.assetIdentifiers);
}
@Override
public int hashCode() {
- return Objects.hashCode(assetIdentifier);
+ return Objects.hashCode(assetIdentifiers);
}
@Override
public String toString() {
- return "AssetReference[assetId=" + assetIdentifier + "]";
+ return "AssetReference[assetIds=" + assetIdentifiers + "]";
}
}
diff --git
a/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDescriptor.java
b/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDescriptor.java
index 209039b..8debeb4 100644
---
a/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDescriptor.java
+++
b/src/main/java/org/apache/nifi/components/connector/ConnectorPropertyDescriptor.java
@@ -22,6 +22,7 @@ import org.apache.nifi.components.DescribedValue;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.components.Validator;
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -113,19 +114,23 @@ public final class ConnectorPropertyDescriptor {
fetchedAllowableValues = null;
}
- if (type != PropertyType.STRING_LIST) {
- return validateIndividual(stepName, groupName, value,
validationContext, fetchedAllowableValues);
- }
-
if (required && value == null) {
return new ValidationResult.Builder()
.subject(name)
- .input(value)
+ .input(null)
.valid(false)
.explanation("Property is required but no value was specified")
.build();
}
+ if (type == PropertyType.ASSET || type == PropertyType.ASSET_LIST) {
+ return validateAssets(value);
+ }
+
+ if (type != PropertyType.STRING_LIST) {
+ return validateIndividual(stepName, groupName, value,
validationContext, fetchedAllowableValues);
+ }
+
final String[] values = value.split(",");
for (final String individualValue : values) {
final ValidationResult result = validateIndividual(stepName,
groupName, individualValue.trim(), validationContext, fetchedAllowableValues);
@@ -190,10 +195,9 @@ public final class ConnectorPropertyDescriptor {
return false;
}
-
private ValidationResult validateType(final String value) {
final String explanation = switch (type) {
- case SECRET, STRING, STRING_LIST -> null;
+ case SECRET, STRING, STRING_LIST, ASSET, ASSET_LIST -> null;
case BOOLEAN -> BOOLEAN_PATTERN.matcher(value).matches() ? null :
"Value must be true or false";
case INTEGER -> INTEGER_PATTERN.matcher(value).matches() ? null :
"Value must be an integer";
case DOUBLE, FLOAT -> DOUBLE_PATTERN.matcher(value).matches() ?
null : "Value must be a floating point number";
@@ -211,6 +215,41 @@ public final class ConnectorPropertyDescriptor {
.build();
}
+ private ValidationResult validateAssets(final String value) {
+ final String[] values = value.split(",");
+ if (type == PropertyType.ASSET && values.length > 1) {
+ return new ValidationResult.Builder()
+ .subject(name)
+ .input(value)
+ .valid(false)
+ .explanation("Property only supports a single asset, but " +
values.length + " assets were specified")
+ .build();
+ }
+
+ final List<String> nonExistentAssets = new ArrayList<>();
+ for (final String assetValue : values) {
+ final File assetFile = new File(assetValue);
+ if (!assetFile.exists() || !assetFile.canRead()) {
+ nonExistentAssets.add(assetValue);
+ }
+ }
+
+ if (!nonExistentAssets.isEmpty()) {
+ return new ValidationResult.Builder()
+ .subject(name)
+ .input(value)
+ .valid(false)
+ .explanation("The specified resource(s) do not exist or could
not be accessed: " + nonExistentAssets)
+ .build();
+ }
+
+ return new ValidationResult.Builder()
+ .subject(name)
+ .input(value)
+ .valid(true)
+ .build();
+ }
+
@Override
public boolean equals(final Object o) {
if (o == null || getClass() != o.getClass()) {
diff --git
a/src/main/java/org/apache/nifi/components/connector/PropertyType.java
b/src/main/java/org/apache/nifi/components/connector/PropertyType.java
index f01a91e..945b492 100644
--- a/src/main/java/org/apache/nifi/components/connector/PropertyType.java
+++ b/src/main/java/org/apache/nifi/components/connector/PropertyType.java
@@ -24,5 +24,7 @@ public enum PropertyType {
FLOAT,
DOUBLE,
STRING_LIST,
- SECRET
+ SECRET,
+ ASSET,
+ ASSET_LIST
}
diff --git
a/src/main/java/org/apache/nifi/flow/VersionedConnectorValueReference.java
b/src/main/java/org/apache/nifi/flow/VersionedConnectorValueReference.java
index e9e881c..7bdc3b5 100644
--- a/src/main/java/org/apache/nifi/flow/VersionedConnectorValueReference.java
+++ b/src/main/java/org/apache/nifi/flow/VersionedConnectorValueReference.java
@@ -18,6 +18,7 @@
package org.apache.nifi.flow;
import java.util.Objects;
+import java.util.Set;
/**
* Represents a property value reference for a Connector in a versioned flow.
@@ -27,11 +28,11 @@ import java.util.Objects;
public class VersionedConnectorValueReference {
private String valueType;
private String value;
- private String assetId;
private String providerId;
private String providerName;
private String secretName;
private String fullyQualifiedSecretName;
+ private Set<String> assetIds;
public String getValueType() {
return valueType;
@@ -49,14 +50,6 @@ public class VersionedConnectorValueReference {
this.value = value;
}
- public String getAssetId() {
- return assetId;
- }
-
- public void setAssetId(final String assetId) {
- this.assetId = assetId;
- }
-
public String getProviderId() {
return providerId;
}
@@ -89,6 +82,14 @@ public class VersionedConnectorValueReference {
this.fullyQualifiedSecretName = fullyQualifiedSecretName;
}
+ public Set<String> getAssetIds() {
+ return assetIds;
+ }
+
+ public void setAssetIds(final Set<String> assetIds) {
+ this.assetIds = assetIds;
+ }
+
@Override
public boolean equals(final Object obj) {
if (this == obj) {
@@ -99,7 +100,7 @@ public class VersionedConnectorValueReference {
}
return Objects.equals(valueType, other.valueType)
&& Objects.equals(value, other.value)
- && Objects.equals(assetId, other.assetId)
+ && Objects.equals(assetIds, other.assetIds)
&& Objects.equals(providerId, other.providerId)
&& Objects.equals(secretName, other.secretName)
&& Objects.equals(fullyQualifiedSecretName,
other.fullyQualifiedSecretName);
@@ -107,12 +108,12 @@ public class VersionedConnectorValueReference {
@Override
public int hashCode() {
- return Objects.hash(valueType, value, assetId, providerId, secretName,
fullyQualifiedSecretName);
+ return Objects.hash(valueType, value, assetIds, providerId,
secretName, fullyQualifiedSecretName);
}
@Override
public String toString() {
return "VersionedConnectorValueReference[valueType=" + valueType + ",
value=" + value
- + ", assetId=" + assetId + ", providerId=" + providerId + ",
fullyQualifiedSecretName=" + fullyQualifiedSecretName + "]";
+ + ", assetIds=" + assetIds + ", providerId=" + providerId + ",
fullyQualifiedSecretName=" + fullyQualifiedSecretName + "]";
}
}
diff --git
a/src/test/java/org/apache/nifi/components/connector/TestConnectorPropertyDescriptor.java
b/src/test/java/org/apache/nifi/components/connector/TestConnectorPropertyDescriptor.java
index 823c3ef..ec6da58 100644
---
a/src/test/java/org/apache/nifi/components/connector/TestConnectorPropertyDescriptor.java
+++
b/src/test/java/org/apache/nifi/components/connector/TestConnectorPropertyDescriptor.java
@@ -21,7 +21,14 @@ import org.apache.nifi.components.DescribedValue;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.junit.jupiter.api.Test;
-
+import org.junit.jupiter.api.io.TempDir;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
@@ -34,6 +41,18 @@ public class TestConnectorPropertyDescriptor {
private static final String TEST_STEP_NAME = "test-step";
private static final String TEST_GROUP_NAME = "test-group";
+ @Test
+ void testRequiredProperty() {
+ final ConnectorPropertyDescriptor descriptor = new
ConnectorPropertyDescriptor.Builder()
+ .name("Required Property")
+ .type(PropertyType.STRING)
+ .required(true)
+ .build();
+
+ final ConnectorValidationContext context = new
TestConnectorValidationContext();
+ assertFalse(descriptor.validate(TEST_STEP_NAME, TEST_GROUP_NAME, null,
context).isValid());
+ }
+
@Test
void testValidateStringType() {
final ConnectorPropertyDescriptor descriptor = new
ConnectorPropertyDescriptor.Builder()
@@ -210,6 +229,99 @@ public class TestConnectorPropertyDescriptor {
assertFalse(descriptor.validate(TEST_STEP_NAME, TEST_GROUP_NAME,
".123", context).isValid());
}
+ @Test
+ void testValidateAssetTypeWithExistingAsset(@TempDir final Path tempDir)
throws IOException {
+ final File assetFile = createAssetFile(tempDir, "asset1.txt");
+
+ final ConnectorPropertyDescriptor descriptor = new
ConnectorPropertyDescriptor.Builder()
+ .name("Asset Property")
+ .type(PropertyType.ASSET)
+ .build();
+
+ final ConnectorValidationContext context = new
TestConnectorValidationContext();
+ assertTrue(descriptor.validate(TEST_STEP_NAME, TEST_GROUP_NAME,
assetFile.getAbsolutePath(), context).isValid());
+ }
+
+ @Test
+ void testValidateAssetTypeWithMissingAsset(@TempDir final Path tempDir)
throws IOException {
+ final File assetFile = new File(tempDir.toFile(), "asset1.txt");
+
+ final ConnectorPropertyDescriptor descriptor = new
ConnectorPropertyDescriptor.Builder()
+ .name("Asset Property")
+ .type(PropertyType.ASSET)
+ .build();
+
+ final ConnectorValidationContext context = new
TestConnectorValidationContext();
+ assertFalse(descriptor.validate(TEST_STEP_NAME, TEST_GROUP_NAME,
assetFile.getAbsolutePath(), context).isValid());
+ }
+
+ @Test
+ void testValidateAssetTypeWithMultipleAssets(@TempDir final Path tempDir)
throws IOException {
+ final File assetFile1 = new File(tempDir.toFile(), "asset1.txt");
+ final File assetFile2 = new File(tempDir.toFile(), "asset2.txt");
+ final String multipleAssetValue = assetFile1.getAbsolutePath() + "," +
assetFile2.getAbsolutePath();
+
+ final ConnectorPropertyDescriptor descriptor = new
ConnectorPropertyDescriptor.Builder()
+ .name("Asset Property")
+ .type(PropertyType.ASSET)
+ .build();
+
+ final ConnectorValidationContext context = new
TestConnectorValidationContext();
+ assertFalse(descriptor.validate(TEST_STEP_NAME, TEST_GROUP_NAME,
multipleAssetValue, context).isValid());
+ }
+
+ @Test
+ void testValidateAssetListTypeWithSingleExistingAsset(@TempDir final Path
tempDir) throws IOException {
+ final File assetFile1 = createAssetFile(tempDir, "asset1.txt");
+
+ final ConnectorPropertyDescriptor descriptor = new
ConnectorPropertyDescriptor.Builder()
+ .name("Asset List Property")
+ .type(PropertyType.ASSET_LIST)
+ .build();
+
+ final ConnectorValidationContext context = new
TestConnectorValidationContext();
+ assertTrue(descriptor.validate(TEST_STEP_NAME, TEST_GROUP_NAME,
assetFile1.getAbsolutePath(), context).isValid());
+ }
+
+ @Test
+ void testValidateAssetListTypeWithMultipleExistingAssets(@TempDir final
Path tempDir) throws IOException {
+ final File assetFile1 = createAssetFile(tempDir, "asset1.txt");
+ final File assetFile2 = createAssetFile(tempDir, "asset2.txt");
+ final String multipleAssetValue = assetFile1.getAbsolutePath() + "," +
assetFile2.getAbsolutePath();
+
+ final ConnectorPropertyDescriptor descriptor = new
ConnectorPropertyDescriptor.Builder()
+ .name("Asset List Property")
+ .type(PropertyType.ASSET_LIST)
+ .build();
+
+ final ConnectorValidationContext context = new
TestConnectorValidationContext();
+ assertTrue(descriptor.validate(TEST_STEP_NAME, TEST_GROUP_NAME,
multipleAssetValue, context).isValid());
+ }
+
+ @Test
+ void testValidateAssetListTypeWithSomeMissingAssets(@TempDir final Path
tempDir) throws IOException {
+ final File assetFile1 = createAssetFile(tempDir, "asset1.txt");
+ final File assetFile2 = new File(tempDir.toFile(), "asset2.txt"); //
Never created
+ final String multipleAssetValue = assetFile1.getAbsolutePath() + "," +
assetFile2.getAbsolutePath();
+
+ final ConnectorPropertyDescriptor descriptor = new
ConnectorPropertyDescriptor.Builder()
+ .name("Asset List Property")
+ .type(PropertyType.ASSET_LIST)
+ .build();
+
+ final ConnectorValidationContext context = new
TestConnectorValidationContext();
+ assertFalse(descriptor.validate(TEST_STEP_NAME, TEST_GROUP_NAME,
multipleAssetValue, context).isValid());
+ }
+
+ private File createAssetFile(final Path parentDir, final String name)
throws IOException {
+ final File assetFile = new File(parentDir.toFile(), name);
+ try (final OutputStream outputStream = new
FileOutputStream(assetFile)) {
+ outputStream.write(name.getBytes(StandardCharsets.UTF_8));
+ outputStream.flush();
+ }
+ return assetFile;
+ }
+
/**
* Simple test implementation of ConnectorValidationContext for unit
testing.
*/
diff --git a/src/test/java/org/apache/nifi/parameter/TestParameter.java
b/src/test/java/org/apache/nifi/parameter/TestParameter.java
index cc366ff..6a536eb 100644
--- a/src/test/java/org/apache/nifi/parameter/TestParameter.java
+++ b/src/test/java/org/apache/nifi/parameter/TestParameter.java
@@ -200,6 +200,11 @@ public class TestParameter {
return parameterContextIdentifier;
}
+ @Override
+ public String getOwnerIdentifier() {
+ return parameterContextIdentifier;
+ }
+
@Override
public String getName() {
return name;