mirror of https://github.com/apache/nifi.git
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:
parent
fbfdcdca9f
commit
a97c20cdb2
|
@ -23,7 +23,7 @@ import java.util.Objects;
|
||||||
* Represents a valid value for a {@link PropertyDescriptor}
|
* Represents a valid value for a {@link PropertyDescriptor}
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class AllowableValue {
|
public class AllowableValue implements DescribedValue {
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
|
@ -71,6 +71,7 @@ public class AllowableValue {
|
||||||
/**
|
/**
|
||||||
* @return the value of this AllowableValue
|
* @return the value of this AllowableValue
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -78,6 +79,7 @@ public class AllowableValue {
|
||||||
/**
|
/**
|
||||||
* @return a human-readable name for this AllowableValue
|
* @return a human-readable name for this AllowableValue
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
return displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
@ -86,6 +88,7 @@ public class AllowableValue {
|
||||||
* @return a description for this value, or <code>null</code> if no
|
* @return a description for this value, or <code>null</code> if no
|
||||||
* description was provided
|
* description was provided
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -396,6 +396,20 @@ public final class PropertyDescriptor implements Comparable<PropertyDescriptor>
|
||||||
return this;
|
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
|
* @param values constrained set of values
|
||||||
* @return the builder
|
* @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) {
|
public Builder dependsOn(final PropertyDescriptor property, final String firstDependentValue, final String... additionalDependentValues) {
|
||||||
final AllowableValue[] dependentValues = new AllowableValue[additionalDependentValues.length + 1];
|
final AllowableValue[] dependentValues = new AllowableValue[additionalDependentValues.length + 1];
|
||||||
dependentValues[0] = new AllowableValue(firstDependentValue);
|
dependentValues[0] = new AllowableValue(firstDependentValue);
|
||||||
int i=1;
|
int i = 1;
|
||||||
for (final String additionalDependentValue : additionalDependentValues) {
|
for (final String additionalDependentValue : additionalDependentValues) {
|
||||||
dependentValues[i++] = new AllowableValue(additionalDependentValue);
|
dependentValues[i++] = new AllowableValue(additionalDependentValue);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,11 +23,13 @@ import org.apache.nifi.expression.ExpressionLanguageScope;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.Mockito;
|
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.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.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
@ -43,7 +45,7 @@ public class TestPropertyDescriptor {
|
||||||
|
|
||||||
private static Builder invalidDescriptorBuilder;
|
private static Builder invalidDescriptorBuilder;
|
||||||
private static Builder validDescriptorBuilder;
|
private static Builder validDescriptorBuilder;
|
||||||
private static String DEFAULT_VALUE = "Default Value";
|
private static final String DEFAULT_VALUE = "Default Value";
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void setUp() {
|
public static void setUp() {
|
||||||
|
@ -52,18 +54,35 @@ public class TestPropertyDescriptor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExceptionThrownByDescriptorWithInvalidDefaultValue() {
|
void testExceptionThrownByDescriptorWithInvalidDefaultValue() {
|
||||||
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> invalidDescriptorBuilder.build());
|
IllegalStateException exception = assertThrows(IllegalStateException.class, () -> invalidDescriptorBuilder.build());
|
||||||
assertTrue(exception.getMessage().contains("[" + DEFAULT_VALUE + "]") );
|
assertTrue(exception.getMessage().contains("[" + DEFAULT_VALUE + "]") );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoExceptionThrownByPropertyDescriptorWithValidDefaultValue() {
|
void testNoExceptionThrownByPropertyDescriptorWithValidDefaultValue() {
|
||||||
assertNotNull(validDescriptorBuilder.build());
|
assertNotNull(validDescriptorBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@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()
|
final PropertyDescriptor descriptor = new PropertyDescriptor.Builder()
|
||||||
.name("dir")
|
.name("dir")
|
||||||
.identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE)
|
.identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE)
|
||||||
|
@ -74,19 +93,16 @@ public class TestPropertyDescriptor {
|
||||||
final ValidationContext validationContext = Mockito.mock(ValidationContext.class);
|
final ValidationContext validationContext = Mockito.mock(ValidationContext.class);
|
||||||
Mockito.when(validationContext.isExpressionLanguagePresent(anyString())).thenReturn(true);
|
Mockito.when(validationContext.isExpressionLanguagePresent(anyString())).thenReturn(true);
|
||||||
Mockito.when(validationContext.isExpressionLanguageSupported(anyString())).thenReturn(true);
|
Mockito.when(validationContext.isExpressionLanguageSupported(anyString())).thenReturn(true);
|
||||||
Mockito.when(validationContext.newPropertyValue(anyString())).thenAnswer(new Answer<Object>() {
|
Mockito.when(validationContext.newPropertyValue(anyString())).thenAnswer(invocation -> {
|
||||||
@Override
|
final String inputArg = invocation.getArgument(0);
|
||||||
public Object answer(final InvocationOnMock invocation) throws Throwable {
|
return inputArg.replace("${TestPropertyDescriptor.Var1}", "__my_var__").replaceAll("\\$\\{.*}", "");
|
||||||
final String inputArg = invocation.getArgument(0);
|
|
||||||
return inputArg.replace("${TestPropertyDescriptor.Var1}", "__my_var__").replaceAll("\\$\\{.*}", "");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
assertTrue(descriptor.validate("${TestPropertyDescriptor.Var1}", validationContext).isValid());
|
assertTrue(descriptor.validate("${TestPropertyDescriptor.Var1}", validationContext).isValid());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testExternalResourceConsideredIfELVarRegistryPresent() {
|
void testExternalResourceConsideredIfELVarRegistryPresent() {
|
||||||
final PropertyDescriptor descriptor = new PropertyDescriptor.Builder()
|
final PropertyDescriptor descriptor = new PropertyDescriptor.Builder()
|
||||||
.name("dir")
|
.name("dir")
|
||||||
.identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE, ResourceType.DIRECTORY)
|
.identifiesExternalResource(ResourceCardinality.SINGLE, ResourceType.FILE, ResourceType.DIRECTORY)
|
||||||
|
@ -98,17 +114,14 @@ public class TestPropertyDescriptor {
|
||||||
final ValidationContext validationContext = Mockito.mock(ValidationContext.class);
|
final ValidationContext validationContext = Mockito.mock(ValidationContext.class);
|
||||||
Mockito.when(validationContext.isExpressionLanguagePresent(anyString())).thenReturn(true);
|
Mockito.when(validationContext.isExpressionLanguagePresent(anyString())).thenReturn(true);
|
||||||
Mockito.when(validationContext.isExpressionLanguageSupported(anyString())).thenReturn(true);
|
Mockito.when(validationContext.isExpressionLanguageSupported(anyString())).thenReturn(true);
|
||||||
Mockito.when(validationContext.newPropertyValue(anyString())).thenAnswer(new Answer<Object>() {
|
Mockito.when(validationContext.newPropertyValue(anyString())).thenAnswer(invocation -> {
|
||||||
@Override
|
final String inputArg = invocation.getArgument(0);
|
||||||
public Object answer(final InvocationOnMock invocation) {
|
final String evaluatedValue = inputArg.replace("${TestPropertyDescriptor.Var1}", variable.get().replaceAll("\\$\\{.*}", ""));
|
||||||
final String inputArg = invocation.getArgument(0);
|
|
||||||
final String evaluatedValue = inputArg.replace("${TestPropertyDescriptor.Var1}", variable.get().replaceAll("\\$\\{.*}", ""));
|
|
||||||
|
|
||||||
final PropertyValue propertyValue = Mockito.mock(PropertyValue.class);
|
final PropertyValue propertyValue = Mockito.mock(PropertyValue.class);
|
||||||
Mockito.when(propertyValue.getValue()).thenReturn(evaluatedValue);
|
Mockito.when(propertyValue.getValue()).thenReturn(evaluatedValue);
|
||||||
Mockito.when(propertyValue.evaluateAttributeExpressions()).thenReturn(propertyValue);
|
Mockito.when(propertyValue.evaluateAttributeExpressions()).thenReturn(propertyValue);
|
||||||
return propertyValue;
|
return propertyValue;
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Should not be valid because Expression Language scope is VARIABLE_REGISTRY, so the ${TestPropertyDescriptor.Var1} will be replaced with
|
// Should not be valid because Expression Language scope is VARIABLE_REGISTRY, so the ${TestPropertyDescriptor.Var1} will be replaced with
|
||||||
|
|
Loading…
Reference in New Issue