NIFI-9936 Added DescribedValue in PropertyDescriptor Builder for AllowableValues

This closes #5977

Signed-off-by: David Handermann <exceptionfactory@apache.org>
This commit is contained in:
Lehel 2022-04-19 18:37:20 +02:00 committed by exceptionfactory
parent fbfdcdca9f
commit a97c20cdb2
No known key found for this signature in database
GPG Key ID: 29B6A52D2AAE8DBA
5 changed files with 142 additions and 25 deletions

View File

@ -23,7 +23,7 @@ import java.util.Objects;
* Represents a valid value for a {@link PropertyDescriptor}
* </p>
*/
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 <code>null</code> if no
* description was provided
*/
@Override
public String getDescription() {
return description;
}

View File

@ -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();
}

View File

@ -396,6 +396,20 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
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 <E> generic parameter for an enum class that implements the Allowable interface
* @return the builder
*/
public <E extends Enum<E> & DescribedValue> Builder allowableValues(final Class<E> 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<PropertyDescriptor>
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);
}

View File

@ -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();
}
}

View File

@ -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<AllowableValue> 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<Object>() {
@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<Object>() {
@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