Skip to content

Commit

Permalink
[config] Add config validator for parameter options (#2691)
Browse files Browse the repository at this point in the history
Signed-off-by: Christoph Weitkamp <github@christophweitkamp.de>
  • Loading branch information
cweitkamp committed Feb 9, 2022
1 parent 9b438d7 commit 9cda2c8
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,13 @@ public static ConfigDescriptionParameterValidator createMinMaxValidator() {
public static ConfigDescriptionParameterValidator createPatternValidator() {
return new PatternValidator();
}

/**
* Returns a new validator for the parameter options of a config description parameter.
*
* @return a new validator for the parameter options of a config description parameter
*/
public static ConfigDescriptionParameterValidator createOptionsValidator() {
return new OptionsValidator();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ public final class ConfigDescriptionValidatorImpl implements ConfigDescriptionVa
ConfigDescriptionParameterValidatorFactory.createRequiredValidator(), //
ConfigDescriptionParameterValidatorFactory.createTypeValidator(), //
ConfigDescriptionParameterValidatorFactory.createMinMaxValidator(), //
ConfigDescriptionParameterValidatorFactory.createPatternValidator() //
ConfigDescriptionParameterValidatorFactory.createPatternValidator(), //
ConfigDescriptionParameterValidatorFactory.createOptionsValidator() //
);

private final Logger logger = LoggerFactory.getLogger(ConfigDescriptionValidatorImpl.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ final class MessageKey {
static final MessageKey PATTERN_VIOLATED = new MessageKey("pattern_violated",
"The value {0} does not match the pattern {1}.");

static final MessageKey OPTIONS_VIOLATED = new MessageKey("options_violated",
"The value {0} does not match allowed parameter options. Allowed options are: {1}");

static final MessageKey MULTIPLE_LIMIT_VIOLATED = new MessageKey("multiple_limit_violated",
"Only {0} elements are allowed but {1} are provided.");
/** The key to be used for internationalization. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.core.ConfigDescriptionParameter;
import org.openhab.core.config.core.ConfigDescriptionParameter.Type;
import org.openhab.core.config.core.ParameterOption;
import org.openhab.core.config.core.internal.validation.TypeIntrospections.TypeIntrospection;
import org.openhab.core.config.core.validation.ConfigValidationMessage;

Expand All @@ -39,23 +38,19 @@ final class MinMaxValidator implements ConfigDescriptionParameterValidator {
}

// Allow specified options to be outside of the min/max value
for (ParameterOption option : parameter.getOptions()) {
// Option values are a string, so we can do a simple compare
if (option.getValue().equals(value.toString())) {
return null;
}
// Option values are a string, so we can do a simple compare
if (parameter.getOptions().stream().map(o -> o.getValue()).anyMatch(v -> v.equals(value.toString()))) {
return null;
}

TypeIntrospection typeIntrospection = TypeIntrospections.get(parameter.getType());

if (parameter.getMinimum() != null) {
BigDecimal min = parameter.getMinimum();
if (typeIntrospection.isMinViolated(value, min)) {
return createMinMaxViolationMessage(parameter.getName(), typeIntrospection.getMinViolationMessageKey(),
min);
}
}

if (parameter.getMaximum() != null) {
BigDecimal max = parameter.getMaximum();
if (typeIntrospection.isMaxViolated(value, max)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright (c) 2010-2022 Contributors to the openHAB project
*
* See the NOTICE file(s) distributed with this work for additional
* information.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0
*
* SPDX-License-Identifier: EPL-2.0
*/
package org.openhab.core.config.core.internal.validation;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.openhab.core.config.core.ConfigDescriptionParameter;
import org.openhab.core.config.core.validation.ConfigValidationMessage;

/**
* The {@link ConfigDescriptionParameterValidator} for the options of a {@link ConfigDescriptionParameter}.
*
* @author Christoph Weitkamp - Initial contribution
*/
@NonNullByDefault
final class OptionsValidator implements ConfigDescriptionParameterValidator {

@Override
public @Nullable ConfigValidationMessage validate(ConfigDescriptionParameter param, @Nullable Object value) {
if (value == null || !param.getLimitToOptions() || param.getOptions().isEmpty()) {
return null;
}

// Option values are a string, so we can do a simple compare
if (param.getOptions().stream().map(o -> o.getValue()).noneMatch(v -> v.equals(value.toString()))) {
MessageKey messageKey = MessageKey.OPTIONS_VIOLATED;
return new ConfigValidationMessage(param.getName(), messageKey.defaultMessage, messageKey.key,
param.getOptions());
}
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ min_value_txt_violated=The value must not consist of less than {0} characters.
min_value_numeric_violated=The value must not be less than {0}.

pattern_violated=The value {0} does not match the pattern {1}.
options_violated=The value {0} does not match allowed parameter options. Allowed options are: {1}
multiple_limit_violated=Only {0} elements are allowed but {1} are provided.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
Expand All @@ -35,6 +37,7 @@
import org.openhab.core.config.core.ConfigDescriptionParameter.Type;
import org.openhab.core.config.core.ConfigDescriptionParameterBuilder;
import org.openhab.core.config.core.ConfigDescriptionRegistry;
import org.openhab.core.config.core.ParameterOption;
import org.openhab.core.config.core.validation.ConfigDescriptionValidator;
import org.openhab.core.config.core.validation.ConfigValidationException;
import org.openhab.core.config.core.validation.ConfigValidationMessage;
Expand All @@ -48,12 +51,13 @@
* @author Thomas Höfer - Initial contribution
* @author Wouter Born - Migrate tests from Groovy to Java
*/
@NonNullByDefault
public class ConfigDescriptionValidatorTest {

private static final int MIN_VIOLATED = 1;
private static final int MAX_VIOLATED = 1234;

private static final BigDecimal DECIMAL_MIN_VIOLATED = new BigDecimal("1");
private static final BigDecimal DECIMAL_MIN_VIOLATED = BigDecimal.ONE;
private static final BigDecimal DECIMAL_MAX_VIOLATED = new BigDecimal("3.5");

private static final BigDecimal MIN = BigDecimal.valueOf(2);
Expand All @@ -76,6 +80,8 @@ public class ConfigDescriptionValidatorTest {
private static final String TXT_MAX_PARAM_NAME = "txt-max-name";
private static final String TXT_PATTERN_PARAM_NAME = "txt-pattern-name";
private static final String TXT_MAX_PATTERN_PARAM_NAME = "txt-max-pattern-name";
private static final String TXT_PARAM_WITH_LIMITED_OPTIONS_NAME = "txt-param-with-limited-options-name";
private static final String TXT_PARAM_WITH_UNLIMITED_OPTIONS_NAME = "txt-param-with-unlimited-options-name";
private static final String TXT_MULTIPLE_LIMIT_PARAM_NAME = "txt-multiple-limit-name";

private static final String INT_PARAM_NAME = "int-param";
Expand All @@ -88,6 +94,11 @@ public class ConfigDescriptionValidatorTest {
private static final String DECIMAL_MIN_PARAM_NAME = "decimal-min-name";
private static final String DECIMAL_MAX_PARAM_NAME = "decimal-max-name";

private static final List<ParameterOption> PARAMETER_OPTIONS = List.of( //
new ParameterOption("http", "HTTP"), //
new ParameterOption("https", "HTTPS") //
);

private static final ConfigDescriptionParameter BOOL_PARAM = ConfigDescriptionParameterBuilder
.create(BOOL_PARAM_NAME, ConfigDescriptionParameter.Type.BOOLEAN).build();
private static final ConfigDescriptionParameter BOOL_REQUIRED_PARAM = ConfigDescriptionParameterBuilder
Expand All @@ -106,6 +117,12 @@ public class ConfigDescriptionValidatorTest {
private static final ConfigDescriptionParameter TXT_MAX_PATTERN_PARAM = ConfigDescriptionParameterBuilder
.create(TXT_MAX_PATTERN_PARAM_NAME, ConfigDescriptionParameter.Type.TEXT).withMaximum(MAX)
.withPattern(PATTERN).build();
private static final ConfigDescriptionParameter TXT_PARAM_WITH_LIMITED_OPTIONS = ConfigDescriptionParameterBuilder
.create(TXT_PARAM_WITH_LIMITED_OPTIONS_NAME, ConfigDescriptionParameter.Type.TEXT)
.withOptions(PARAMETER_OPTIONS).build();
private static final ConfigDescriptionParameter TXT_PARAM_WITH_UNLIMITED_OPTIONS = ConfigDescriptionParameterBuilder
.create(TXT_PARAM_WITH_UNLIMITED_OPTIONS_NAME, ConfigDescriptionParameter.Type.TEXT)
.withOptions(PARAMETER_OPTIONS).withLimitToOptions(false).build();
private static final ConfigDescriptionParameter TXT_MULTIPLE_LIMIT_PARAM = ConfigDescriptionParameterBuilder
.create(TXT_MULTIPLE_LIMIT_PARAM_NAME, Type.TEXT).withMultiple(true).withMultipleLimit(2).build();

Expand Down Expand Up @@ -139,13 +156,14 @@ private static final URI createURI(String s) {

private static final ConfigDescription CONFIG_DESCRIPTION = ConfigDescriptionBuilder.create(CONFIG_DESCRIPTION_URI)
.withParameters(List.of(BOOL_PARAM, BOOL_REQUIRED_PARAM, TXT_PARAM, TXT_REQUIRED_PARAM, TXT_MIN_PARAM,
TXT_MAX_PARAM, TXT_PATTERN_PARAM, TXT_MAX_PATTERN_PARAM, TXT_MULTIPLE_LIMIT_PARAM, INT_PARAM,
INT_REQUIRED_PARAM, INT_MIN_PARAM, INT_MAX_PARAM, DECIMAL_PARAM, DECIMAL_REQUIRED_PARAM,
DECIMAL_MIN_PARAM, DECIMAL_MAX_PARAM))
TXT_MAX_PARAM, TXT_PATTERN_PARAM, TXT_MAX_PATTERN_PARAM, TXT_PARAM_WITH_LIMITED_OPTIONS,
TXT_PARAM_WITH_UNLIMITED_OPTIONS, TXT_MULTIPLE_LIMIT_PARAM, INT_PARAM, INT_REQUIRED_PARAM,
INT_MIN_PARAM, INT_MAX_PARAM, DECIMAL_PARAM, DECIMAL_REQUIRED_PARAM, DECIMAL_MIN_PARAM,
DECIMAL_MAX_PARAM))
.build();

private Map<String, Object> params;
private ConfigDescriptionValidatorImpl configDescriptionValidator;
private @NonNullByDefault({}) Map<String, Object> params;
private @NonNullByDefault({}) ConfigDescriptionValidatorImpl configDescriptionValidator;

@BeforeEach
public void setUp() {
Expand All @@ -171,6 +189,7 @@ public void setUp() {
params.put(TXT_MAX_PARAM_NAME, String.valueOf(MIN_VIOLATED));
params.put(TXT_PATTERN_PARAM_NAME, "abbbc");
params.put(TXT_MAX_PATTERN_PARAM_NAME, "abc");
params.put(TXT_PARAM_WITH_LIMITED_OPTIONS_NAME, "http");
params.put(TXT_MULTIPLE_LIMIT_PARAM_NAME, List.of("1", "2"));
params.put(INT_PARAM_NAME, null);
params.put(INT_REQUIRED_PARAM_NAME, 0);
Expand Down Expand Up @@ -408,6 +427,27 @@ public void assertValidationThrowsExceptionContainingMessagesForInvalidPatternFo
assertThat(getConfigValidationMessages(exception), is(expected));
}

// ===========================================================================
// PARAMETER OPTION VALIDATIONS
// ===========================================================================

@Test
public void assertValidationThrowsExceptionForNotAllowedLimitedParameterOption() {
List<ConfigValidationMessage> expected = List.of(new ConfigValidationMessage(
TXT_PARAM_WITH_LIMITED_OPTIONS_NAME, MessageKey.OPTIONS_VIOLATED.defaultMessage,
MessageKey.OPTIONS_VIOLATED.key, PARAMETER_OPTIONS));
params.put(TXT_PARAM_WITH_LIMITED_OPTIONS_NAME, "ftp");
ConfigValidationException exception = Assertions.assertThrows(ConfigValidationException.class,
() -> configDescriptionValidator.validate(params, CONFIG_DESCRIPTION_URI));
assertThat(getConfigValidationMessages(exception), is(expected));
}

@Test
public void assertValidationThrowsNoExceptionForAllowedUnlimitedParameterOption() {
params.put(TXT_PARAM_WITH_UNLIMITED_OPTIONS_NAME, "ftp");
Assertions.assertDoesNotThrow(() -> configDescriptionValidator.validate(params, CONFIG_DESCRIPTION_URI));
}

// ===========================================================================
// MISC VALIDATIONS
// ===========================================================================
Expand Down Expand Up @@ -484,7 +524,7 @@ public void assertValidateCanHandleUnknownURIs() throws Exception {
}

@SuppressWarnings("unchecked")
private static List<ConfigValidationMessage> getConfigValidationMessages(ConfigValidationException cve) {
private static @Nullable List<ConfigValidationMessage> getConfigValidationMessages(ConfigValidationException cve) {
try {
Field field = cve.getClass().getDeclaredField("configValidationMessages");
field.setAccessible(true);
Expand Down

0 comments on commit 9cda2c8

Please sign in to comment.