From a97c20cdb23fb124e8559a7392279500e7515530 Mon Sep 17 00:00:00 2001 From: Lehel Date: Tue, 19 Apr 2022 18:37:20 +0200 Subject: [PATCH] NIFI-9936 Added DescribedValue in PropertyDescriptor Builder for AllowableValues This closes #5977 Signed-off-by: David Handermann --- .../nifi/components/AllowableValue.java | 5 +- .../nifi/components/DescribedValue.java | 38 ++++++++++++ .../nifi/components/PropertyDescriptor.java | 16 ++++- .../nifi/components/EnumAllowableValue.java | 49 +++++++++++++++ .../components/TestPropertyDescriptor.java | 59 +++++++++++-------- 5 files changed, 142 insertions(+), 25 deletions(-) create mode 100644 nifi-api/src/main/java/org/apache/nifi/components/DescribedValue.java create mode 100644 nifi-api/src/test/java/org/apache/nifi/components/EnumAllowableValue.java diff --git a/nifi-api/src/main/java/org/apache/nifi/components/AllowableValue.java b/nifi-api/src/main/java/org/apache/nifi/components/AllowableValue.java index a4809616d6..b8303ae122 100644 --- a/nifi-api/src/main/java/org/apache/nifi/components/AllowableValue.java +++ b/nifi-api/src/main/java/org/apache/nifi/components/AllowableValue.java @@ -23,7 +23,7 @@ import java.util.Objects; * Represents a valid value for a {@link PropertyDescriptor} *

*/ -public class AllowableValue { +public class AllowableValue implements DescribedValue { private final String value; private final String displayName; @@ -71,6 +71,7 @@ public class AllowableValue { /** * @return the value of this AllowableValue */ + @Override public String getValue() { return value; } @@ -78,6 +79,7 @@ public class AllowableValue { /** * @return a human-readable name for this AllowableValue */ + @Override public String getDisplayName() { return displayName; } @@ -86,6 +88,7 @@ public class AllowableValue { * @return a description for this value, or null if no * description was provided */ + @Override public String getDescription() { return description; } diff --git a/nifi-api/src/main/java/org/apache/nifi/components/DescribedValue.java b/nifi-api/src/main/java/org/apache/nifi/components/DescribedValue.java new file mode 100644 index 0000000000..e52740714d --- /dev/null +++ b/nifi-api/src/main/java/org/apache/nifi/components/DescribedValue.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.nifi.components; + +/** + * Describes a component property value with display name and description. + */ +public interface DescribedValue { + + /** + * @return the property value as a string + */ + String getValue(); + + /** + * @return the property display name as a string + */ + String getDisplayName(); + + /** + * @return the proeprty description as a string + */ + String getDescription(); +} diff --git a/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java b/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java index e550b83bfb..b36a2a46d8 100644 --- a/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java +++ b/nifi-api/src/main/java/org/apache/nifi/components/PropertyDescriptor.java @@ -396,6 +396,20 @@ public final class PropertyDescriptor implements Comparable return this; } + /** + * Stores allowable values from an enum class. + * @param enumClass an enum class that implements the Allowable interface and contains a set of values + * @param generic parameter for an enum class that implements the Allowable interface + * @return the builder + */ + public & DescribedValue> Builder allowableValues(final Class enumClass) { + this.allowableValues = new ArrayList<>(); + for (E enumValue : enumClass.getEnumConstants()) { + this.allowableValues.add(new AllowableValue(enumValue.getValue(), enumValue.getDisplayName(), enumValue.getDescription())); + } + return this; + } + /** * @param values constrained set of values * @return the builder @@ -569,7 +583,7 @@ public final class PropertyDescriptor implements Comparable public Builder dependsOn(final PropertyDescriptor property, final String firstDependentValue, final String... additionalDependentValues) { final AllowableValue[] dependentValues = new AllowableValue[additionalDependentValues.length + 1]; dependentValues[0] = new AllowableValue(firstDependentValue); - int i=1; + int i = 1; for (final String additionalDependentValue : additionalDependentValues) { dependentValues[i++] = new AllowableValue(additionalDependentValue); } diff --git a/nifi-api/src/test/java/org/apache/nifi/components/EnumAllowableValue.java b/nifi-api/src/test/java/org/apache/nifi/components/EnumAllowableValue.java new file mode 100644 index 0000000000..02aed04ebd --- /dev/null +++ b/nifi-api/src/test/java/org/apache/nifi/components/EnumAllowableValue.java @@ -0,0 +1,49 @@ +/* + * 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.components; + +public enum EnumAllowableValue implements DescribedValue { + + GREEN { + @Override + public String getDisplayName() { + return "GreenDisplayName"; + } + + @Override + public String getDescription() { + return "GreenDescription"; + } + }, + + RED { + @Override + public String getDisplayName() { + return "RedDisplayName"; + } + + @Override + public String getDescription() { + return "RedDescription"; + } + }; + + @Override + public String getValue() { + return name(); + } +} diff --git a/nifi-api/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java b/nifi-api/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java index bab58f7078..8fb9fab584 100644 --- a/nifi-api/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java +++ b/nifi-api/src/test/java/org/apache/nifi/components/TestPropertyDescriptor.java @@ -23,11 +23,13 @@ import org.apache.nifi.expression.ExpressionLanguageScope; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mockito.Mockito; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -43,7 +45,7 @@ public class TestPropertyDescriptor { private static Builder invalidDescriptorBuilder; private static Builder validDescriptorBuilder; - private static String DEFAULT_VALUE = "Default Value"; + private static final String DEFAULT_VALUE = "Default Value"; @BeforeAll public static void setUp() { @@ -52,18 +54,35 @@ public class TestPropertyDescriptor { } @Test - public void testExceptionThrownByDescriptorWithInvalidDefaultValue() { + void testExceptionThrownByDescriptorWithInvalidDefaultValue() { IllegalStateException exception = assertThrows(IllegalStateException.class, () -> invalidDescriptorBuilder.build()); assertTrue(exception.getMessage().contains("[" + DEFAULT_VALUE + "]") ); } @Test - public void testNoExceptionThrownByPropertyDescriptorWithValidDefaultValue() { + void testNoExceptionThrownByPropertyDescriptorWithValidDefaultValue() { assertNotNull(validDescriptorBuilder.build()); } @Test - public void testExternalResourceIgnoredIfELWithAttributesPresent() { + void testPropertyDescriptorWithEnumValue() { + Builder enumDescriptorBuilder = new PropertyDescriptor.Builder() + .name("enumAllowableValueDescriptor") + .allowableValues(EnumAllowableValue.class) + .defaultValue(EnumAllowableValue.GREEN.name()); + + final PropertyDescriptor propertyDescriptor = enumDescriptorBuilder.build(); + assertNotNull(propertyDescriptor); + + assertEquals(EnumAllowableValue.GREEN.name(), propertyDescriptor.getDefaultValue()); + final List expectedAllowableValues = Arrays.stream(EnumAllowableValue.values()) + .map(enumValue -> new AllowableValue(enumValue.name(), enumValue.getDisplayName(), enumValue.getDescription())) + .collect(Collectors.toList()); + assertEquals(expectedAllowableValues, propertyDescriptor.getAllowableValues()); + } + + @Test + void testExternalResourceIgnoredIfELWithAttributesPresent() { final PropertyDescriptor descriptor = new PropertyDescriptor.Builder() .name("dir") .identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE) @@ -74,19 +93,16 @@ public class TestPropertyDescriptor { final ValidationContext validationContext = Mockito.mock(ValidationContext.class); Mockito.when(validationContext.isExpressionLanguagePresent(anyString())).thenReturn(true); Mockito.when(validationContext.isExpressionLanguageSupported(anyString())).thenReturn(true); - Mockito.when(validationContext.newPropertyValue(anyString())).thenAnswer(new Answer() { - @Override - public Object answer(final InvocationOnMock invocation) throws Throwable { - final String inputArg = invocation.getArgument(0); - return inputArg.replace("${TestPropertyDescriptor.Var1}", "__my_var__").replaceAll("\\$\\{.*}", ""); - } + Mockito.when(validationContext.newPropertyValue(anyString())).thenAnswer(invocation -> { + final String inputArg = invocation.getArgument(0); + return inputArg.replace("${TestPropertyDescriptor.Var1}", "__my_var__").replaceAll("\\$\\{.*}", ""); }); assertTrue(descriptor.validate("${TestPropertyDescriptor.Var1}", validationContext).isValid()); } @Test - public void testExternalResourceConsideredIfELVarRegistryPresent() { + void testExternalResourceConsideredIfELVarRegistryPresent() { final PropertyDescriptor descriptor = new PropertyDescriptor.Builder() .name("dir") .identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE, ResourceType.DIRECTORY) @@ -98,17 +114,14 @@ public class TestPropertyDescriptor { final ValidationContext validationContext = Mockito.mock(ValidationContext.class); Mockito.when(validationContext.isExpressionLanguagePresent(anyString())).thenReturn(true); Mockito.when(validationContext.isExpressionLanguageSupported(anyString())).thenReturn(true); - Mockito.when(validationContext.newPropertyValue(anyString())).thenAnswer(new Answer() { - @Override - public Object answer(final InvocationOnMock invocation) { - final String inputArg = invocation.getArgument(0); - final String evaluatedValue = inputArg.replace("${TestPropertyDescriptor.Var1}", variable.get().replaceAll("\\$\\{.*}", "")); + Mockito.when(validationContext.newPropertyValue(anyString())).thenAnswer(invocation -> { + final String inputArg = invocation.getArgument(0); + final String evaluatedValue = inputArg.replace("${TestPropertyDescriptor.Var1}", variable.get().replaceAll("\\$\\{.*}", "")); - final PropertyValue propertyValue = Mockito.mock(PropertyValue.class); - Mockito.when(propertyValue.getValue()).thenReturn(evaluatedValue); - Mockito.when(propertyValue.evaluateAttributeExpressions()).thenReturn(propertyValue); - return propertyValue; - } + final PropertyValue propertyValue = Mockito.mock(PropertyValue.class); + Mockito.when(propertyValue.getValue()).thenReturn(evaluatedValue); + Mockito.when(propertyValue.evaluateAttributeExpressions()).thenReturn(propertyValue); + return propertyValue; }); // Should not be valid because Expression Language scope is VARIABLE_REGISTRY, so the ${TestPropertyDescriptor.Var1} will be replaced with