diff --git a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionParameterValidatorFactory.java b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionParameterValidatorFactory.java index bd2e7e2e4..dc3a9bbe6 100644 --- a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionParameterValidatorFactory.java +++ b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionParameterValidatorFactory.java @@ -62,4 +62,13 @@ public final class ConfigDescriptionParameterValidatorFactory { 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(); + } } diff --git a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionValidatorImpl.java b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionValidatorImpl.java index 39ddeb08c..8fecbc456 100644 --- a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionValidatorImpl.java +++ b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionValidatorImpl.java @@ -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); diff --git a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/MessageKey.java b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/MessageKey.java index d75b99e2f..4a0a03b9a 100644 --- a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/MessageKey.java +++ b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/MessageKey.java @@ -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. */ diff --git a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/MinMaxValidator.java b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/MinMaxValidator.java index 68e1a0645..d595cc974 100644 --- a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/MinMaxValidator.java +++ b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/MinMaxValidator.java @@ -18,7 +18,6 @@ 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.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; @@ -39,15 +38,12 @@ 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)) { @@ -55,7 +51,6 @@ final class MinMaxValidator implements ConfigDescriptionParameterValidator { min); } } - if (parameter.getMaximum() != null) { BigDecimal max = parameter.getMaximum(); if (typeIntrospection.isMaxViolated(value, max)) { diff --git a/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/OptionsValidator.java b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/OptionsValidator.java new file mode 100644 index 000000000..427e27f0f --- /dev/null +++ b/bundles/org.openhab.core.config.core/src/main/java/org/openhab/core/config/core/internal/validation/OptionsValidator.java @@ -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; + } +} diff --git a/bundles/org.openhab.core.config.core/src/main/resources/OH-INF/i18n/validation.properties b/bundles/org.openhab.core.config.core/src/main/resources/OH-INF/i18n/validation.properties index baa0ec327..2cd7c54f2 100644 --- a/bundles/org.openhab.core.config.core/src/main/resources/OH-INF/i18n/validation.properties +++ b/bundles/org.openhab.core.config.core/src/main/resources/OH-INF/i18n/validation.properties @@ -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. diff --git a/bundles/org.openhab.core.config.core/src/test/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionValidatorTest.java b/bundles/org.openhab.core.config.core/src/test/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionValidatorTest.java index b1d4f96fa..f11ce56ca 100644 --- a/bundles/org.openhab.core.config.core/src/test/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionValidatorTest.java +++ b/bundles/org.openhab.core.config.core/src/test/java/org/openhab/core/config/core/internal/validation/ConfigDescriptionValidatorTest.java @@ -25,6 +25,8 @@ import java.util.LinkedHashMap; 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; @@ -35,6 +37,7 @@ import org.openhab.core.config.core.ConfigDescriptionParameter; 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; @@ -48,12 +51,13 @@ import org.osgi.framework.BundleContext; * @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); @@ -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"; @@ -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 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 @@ -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(); @@ -139,13 +156,14 @@ public class ConfigDescriptionValidatorTest { 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 params; - private ConfigDescriptionValidatorImpl configDescriptionValidator; + private @NonNullByDefault({}) Map params; + private @NonNullByDefault({}) ConfigDescriptionValidatorImpl configDescriptionValidator; @BeforeEach public void setUp() { @@ -171,6 +189,7 @@ public class ConfigDescriptionValidatorTest { 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); @@ -408,6 +427,27 @@ public class ConfigDescriptionValidatorTest { assertThat(getConfigValidationMessages(exception), is(expected)); } + // =========================================================================== + // PARAMETER OPTION VALIDATIONS + // =========================================================================== + + @Test + public void assertValidationThrowsExceptionForNotAllowedLimitedParameterOption() { + List 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 // =========================================================================== @@ -484,7 +524,7 @@ public class ConfigDescriptionValidatorTest { } @SuppressWarnings("unchecked") - private static List getConfigValidationMessages(ConfigValidationException cve) { + private static @Nullable List getConfigValidationMessages(ConfigValidationException cve) { try { Field field = cve.getClass().getDeclaredField("configValidationMessages"); field.setAccessible(true);