From d8a0225b480ac4d8fd74e5a91cb28b59774bbe72 Mon Sep 17 00:00:00 2001 From: Christoph Weitkamp Date: Mon, 14 Oct 2019 12:51:22 +0200 Subject: [PATCH] [rest] Added support for default values containing a list of configuration options (#1031) * Added support for default values containing a list of configuration options Signed-off-by: Christoph Weitkamp --- .../core/dto/ConfigDescriptionDTOMapper.java | 6 +- .../EnrichedConfigDescriptionDTOMapper.java | 77 +++++++++++++++++++ ...EnrichedConfigDescriptionParameterDTO.java | 61 +++++++++++++++ .../config/ConfigDescriptionResource.java | 22 +++--- ...nrichedConfigDescriptionDTOMapperTest.java | 76 ++++++++++++++++++ 5 files changed, 230 insertions(+), 12 deletions(-) create mode 100644 bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionDTOMapper.java create mode 100644 bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionParameterDTO.java create mode 100644 bundles/org.openhab.core.io.rest.core/src/test/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionDTOMapperTest.java diff --git a/bundles/org.openhab.core.config.core/src/main/java/org/eclipse/smarthome/config/core/dto/ConfigDescriptionDTOMapper.java b/bundles/org.openhab.core.config.core/src/main/java/org/eclipse/smarthome/config/core/dto/ConfigDescriptionDTOMapper.java index a2e9a711c..382016b81 100644 --- a/bundles/org.openhab.core.config.core/src/main/java/org/eclipse/smarthome/config/core/dto/ConfigDescriptionDTOMapper.java +++ b/bundles/org.openhab.core.config.core/src/main/java/org/eclipse/smarthome/config/core/dto/ConfigDescriptionDTOMapper.java @@ -46,7 +46,7 @@ public class ConfigDescriptionDTOMapper { return new ConfigDescriptionDTO(toDecodedString(configDescription.getUID()), parameters, parameterGroups); } - private static String toDecodedString(URI uri) { + protected static String toDecodedString(URI uri) { // combine these partials because URI.toString() does not decode return uri.getScheme() + ":" + uri.getSchemeSpecificPart(); } @@ -139,7 +139,7 @@ public class ConfigDescriptionDTOMapper { return parameterGroupBeans; } - private static List mapFilterCriteria(List filterCriteria) { + protected static List mapFilterCriteria(List filterCriteria) { if (filterCriteria == null) { return null; } @@ -150,7 +150,7 @@ public class ConfigDescriptionDTOMapper { return result; } - private static List mapOptions(List options) { + protected static List mapOptions(List options) { if (options == null) { return null; } diff --git a/bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionDTOMapper.java b/bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionDTOMapper.java new file mode 100644 index 000000000..63558b556 --- /dev/null +++ b/bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionDTOMapper.java @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2010-2019 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.eclipse.smarthome.io.rest.core.config; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.smarthome.config.core.ConfigDescription; +import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; +import org.eclipse.smarthome.config.core.dto.ConfigDescriptionDTO; +import org.eclipse.smarthome.config.core.dto.ConfigDescriptionDTOMapper; +import org.eclipse.smarthome.config.core.dto.ConfigDescriptionParameterDTO; +import org.eclipse.smarthome.config.core.dto.ConfigDescriptionParameterGroupDTO; + +/** + * The {@link EnrichedConfigDescriptionDTOMapper} is a utility class to map {@link ConfigDescription}s to config + * descriptions data transform objects {@link ConfigDescriptionDTO} containing + * {@link EnrichedConfigDescriptionParameterDTO}. + * + * @author Christoph Weitkamp - Initial contribution + */ +@NonNullByDefault +public class EnrichedConfigDescriptionDTOMapper extends ConfigDescriptionDTOMapper { + + /** + * Maps configuration description into configuration description DTO object. + * + * @param configDescription the configuration description (not null) + * @return enriched configuration description DTO object + */ + public static ConfigDescriptionDTO map(ConfigDescription configDescription) { + List parameterGroups = mapParameterGroups( + configDescription.getParameterGroups()); + List parameters = mapEnrichedParameters(configDescription.getParameters()); + return new ConfigDescriptionDTO(toDecodedString(configDescription.getUID()), parameters, parameterGroups); + } + + /** + * Maps configuration description parameters into enriched DTO objects. + * + * @param parameters the configuration description parameters (not null) + * @return the parameter enriched DTO objects + */ + public static List mapEnrichedParameters( + List parameters) { + List configDescriptionParameterBeans = new ArrayList<>(parameters.size()); + for (ConfigDescriptionParameter configDescriptionParameter : parameters) { + ConfigDescriptionParameterDTO configDescriptionParameterBean = new EnrichedConfigDescriptionParameterDTO( + configDescriptionParameter.getName(), configDescriptionParameter.getType(), + configDescriptionParameter.getMinimum(), configDescriptionParameter.getMaximum(), + configDescriptionParameter.getStepSize(), configDescriptionParameter.getPattern(), + configDescriptionParameter.isRequired(), configDescriptionParameter.isReadOnly(), + configDescriptionParameter.isMultiple(), configDescriptionParameter.getContext(), + configDescriptionParameter.getDefault(), configDescriptionParameter.getLabel(), + configDescriptionParameter.getDescription(), mapOptions(configDescriptionParameter.getOptions()), + mapFilterCriteria(configDescriptionParameter.getFilterCriteria()), + configDescriptionParameter.getGroupName(), configDescriptionParameter.isAdvanced(), + configDescriptionParameter.getLimitToOptions(), configDescriptionParameter.getMultipleLimit(), + configDescriptionParameter.getUnit(), configDescriptionParameter.getUnitLabel(), + configDescriptionParameter.isVerifyable()); + configDescriptionParameterBeans.add(configDescriptionParameterBean); + } + return configDescriptionParameterBeans; + } + +} diff --git a/bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionParameterDTO.java b/bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionParameterDTO.java new file mode 100644 index 000000000..358b12208 --- /dev/null +++ b/bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionParameterDTO.java @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2010-2019 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.eclipse.smarthome.io.rest.core.config; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.smarthome.config.core.ConfigDescriptionParameter.Type; +import org.eclipse.smarthome.config.core.dto.ConfigDescriptionParameterDTO; +import org.eclipse.smarthome.config.core.dto.FilterCriteriaDTO; +import org.eclipse.smarthome.config.core.dto.ParameterOptionDTO; + +/** + * This is an enriched data transfer object that is used to serialize config descriptions parameters with a list of + * default values if a configuration description defines multiple="true". + * + * @author Christoph Weitkamp - Initial contribution + */ +@NonNullByDefault +public class EnrichedConfigDescriptionParameterDTO extends ConfigDescriptionParameterDTO { + + private static final String DEFAULT_LIST_DELIMITER = ","; + + public @Nullable Collection defaultValues; + + public EnrichedConfigDescriptionParameterDTO(String name, Type type, BigDecimal minimum, BigDecimal maximum, + BigDecimal stepsize, String pattern, Boolean required, Boolean readOnly, Boolean multiple, String context, + String defaultValue, String label, String description, List options, + List filterCriteria, String groupName, Boolean advanced, Boolean limitToOptions, + Integer multipleLimit, String unit, String unitLabel, Boolean verify) { + super(name, type, minimum, maximum, stepsize, pattern, required, readOnly, multiple, context, defaultValue, + label, description, options, filterCriteria, groupName, advanced, limitToOptions, multipleLimit, unit, + unitLabel, verify); + + if (multiple) { + if (defaultValue.contains(DEFAULT_LIST_DELIMITER)) { + defaultValues = Arrays.asList(defaultValue.split(DEFAULT_LIST_DELIMITER)).stream().map(v -> v.trim()) + .filter(v -> !v.isEmpty()).collect(Collectors.toList()); + } else { + defaultValues = Collections.singleton(defaultValue); + } + } + } + +} diff --git a/bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/internal/config/ConfigDescriptionResource.java b/bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/internal/config/ConfigDescriptionResource.java index f0e80da23..990d2d9f7 100644 --- a/bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/internal/config/ConfigDescriptionResource.java +++ b/bundles/org.openhab.core.io.rest.core/src/main/java/org/eclipse/smarthome/io/rest/core/internal/config/ConfigDescriptionResource.java @@ -28,16 +28,17 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriBuilder; +import org.eclipse.jdt.annotation.NonNullByDefault; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.smarthome.config.core.ConfigDescription; import org.eclipse.smarthome.config.core.ConfigDescriptionRegistry; import org.eclipse.smarthome.config.core.dto.ConfigDescriptionDTO; -import org.eclipse.smarthome.config.core.dto.ConfigDescriptionDTOMapper; import org.eclipse.smarthome.core.auth.Role; import org.eclipse.smarthome.io.rest.JSONResponse; import org.eclipse.smarthome.io.rest.LocaleService; import org.eclipse.smarthome.io.rest.RESTResource; import org.eclipse.smarthome.io.rest.Stream2JSONInputStream; +import org.eclipse.smarthome.io.rest.core.config.EnrichedConfigDescriptionDTOMapper; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; @@ -60,14 +61,15 @@ import io.swagger.annotations.ApiResponses; @RolesAllowed({ Role.ADMIN }) @Api(value = ConfigDescriptionResource.PATH_CONFIG_DESCRIPTIONS) @Component +@NonNullByDefault public class ConfigDescriptionResource implements RESTResource { /** The URI path to this resource */ public static final String PATH_CONFIG_DESCRIPTIONS = "config-descriptions"; - private ConfigDescriptionRegistry configDescriptionRegistry; + private @NonNullByDefault({}) ConfigDescriptionRegistry configDescriptionRegistry; - private LocaleService localeService; + private @NonNullByDefault({}) LocaleService localeService; @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC) protected void setLocaleService(LocaleService localeService) { @@ -82,14 +84,14 @@ public class ConfigDescriptionResource implements RESTResource { @Produces(MediaType.APPLICATION_JSON) @ApiOperation(value = "Gets all available config descriptions.", response = ConfigDescriptionDTO.class, responseContainer = "List") @ApiResponses(value = @ApiResponse(code = 200, message = "OK", response = ConfigDescriptionDTO.class, responseContainer = "List")) - public Response getAll(@HeaderParam("Accept-Language") @ApiParam(value = "Accept-Language") String language, // + public Response getAll( + @HeaderParam("Accept-Language") @ApiParam(value = "Accept-Language") @Nullable String language, // @QueryParam("scheme") @ApiParam(value = "scheme filter", required = false) @Nullable String scheme) { Locale locale = localeService.getLocale(language); Collection configDescriptions = configDescriptionRegistry.getConfigDescriptions(locale); - return Response.ok(new Stream2JSONInputStream(configDescriptions.stream().filter(configDescription -> { return scheme == null || scheme.equals(configDescription.getUID().getScheme()); - }).map(ConfigDescriptionDTOMapper::map))).build(); + }).map(EnrichedConfigDescriptionDTOMapper::map))).build(); } @GET @@ -98,12 +100,14 @@ public class ConfigDescriptionResource implements RESTResource { @ApiOperation(value = "Gets a config description by URI.", response = ConfigDescriptionDTO.class) @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = ConfigDescriptionDTO.class), @ApiResponse(code = 400, message = "Invalid URI syntax"), @ApiResponse(code = 404, message = "Not found") }) - public Response getByURI(@HeaderParam("Accept-Language") @ApiParam(value = "Accept-Language") String language, + public Response getByURI( + @HeaderParam("Accept-Language") @ApiParam(value = "Accept-Language") @Nullable String language, @PathParam("uri") @ApiParam(value = "uri") String uri) { Locale locale = localeService.getLocale(language); URI uriObject = UriBuilder.fromPath(uri).build(); - ConfigDescription configDescription = this.configDescriptionRegistry.getConfigDescription(uriObject, locale); - return configDescription != null ? Response.ok(ConfigDescriptionDTOMapper.map(configDescription)).build() + ConfigDescription configDescription = configDescriptionRegistry.getConfigDescription(uriObject, locale); + return configDescription != null + ? Response.ok(EnrichedConfigDescriptionDTOMapper.map(configDescription)).build() : JSONResponse.createErrorResponse(Status.NOT_FOUND, "Configuration not found: " + uri); } diff --git a/bundles/org.openhab.core.io.rest.core/src/test/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionDTOMapperTest.java b/bundles/org.openhab.core.io.rest.core/src/test/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionDTOMapperTest.java new file mode 100644 index 000000000..5d63645ee --- /dev/null +++ b/bundles/org.openhab.core.io.rest.core/src/test/java/org/eclipse/smarthome/io/rest/core/config/EnrichedConfigDescriptionDTOMapperTest.java @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2010-2019 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.eclipse.smarthome.io.rest.core.config; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; + +import java.net.URI; +import java.util.Arrays; + +import org.eclipse.smarthome.config.core.ConfigDescription; +import org.eclipse.smarthome.config.core.ConfigDescriptionParameter; +import org.eclipse.smarthome.config.core.ConfigDescriptionParameter.Type; +import org.eclipse.smarthome.config.core.ConfigDescriptionParameterBuilder; +import org.eclipse.smarthome.config.core.dto.ConfigDescriptionDTO; +import org.eclipse.smarthome.config.core.dto.ConfigDescriptionParameterDTO; +import org.junit.Test; + +/** + * @author Christoph Weitkamp - Initial contribution + */ +public class EnrichedConfigDescriptionDTOMapperTest { + + private static final URI CONFIG_URI = URI.create("system:ephemeris"); + private static final String CONFIG_PARAMETER_NAME = "test"; + private static final String CONFIG_PARAMETER_DEFAULT_VALUE = "first value,second value,third value"; + + @Test + public void testThatDefaultValueIsNotAList() { + ConfigDescriptionParameter configDescriptionParameter = ConfigDescriptionParameterBuilder + .create(CONFIG_PARAMETER_NAME, Type.TEXT).withDefault(CONFIG_PARAMETER_DEFAULT_VALUE).build(); + ConfigDescription configDescription = new ConfigDescription(CONFIG_URI, + Arrays.asList(configDescriptionParameter)); + + ConfigDescriptionDTO cddto = EnrichedConfigDescriptionDTOMapper.map(configDescription); + assertThat(cddto.parameters.size(), is(1)); + + ConfigDescriptionParameterDTO cdpdto = cddto.parameters.get(0); + assertThat(cdpdto, instanceOf(EnrichedConfigDescriptionParameterDTO.class)); + assertThat(cdpdto.defaultValue, is(CONFIG_PARAMETER_DEFAULT_VALUE)); + EnrichedConfigDescriptionParameterDTO ecdpdto = (EnrichedConfigDescriptionParameterDTO) cdpdto; + assertThat(ecdpdto.defaultValues, is(nullValue())); + } + + @SuppressWarnings("null") + @Test + public void testThatDefaultValuesAreAList() { + ConfigDescriptionParameter configDescriptionParameter = ConfigDescriptionParameterBuilder + .create(CONFIG_PARAMETER_NAME, Type.TEXT).withDefault(CONFIG_PARAMETER_DEFAULT_VALUE).withMultiple(true) + .build(); + ConfigDescription configDescription = new ConfigDescription(CONFIG_URI, + Arrays.asList(configDescriptionParameter)); + + ConfigDescriptionDTO cddto = EnrichedConfigDescriptionDTOMapper.map(configDescription); + assertThat(cddto.parameters.size(), is(1)); + + ConfigDescriptionParameterDTO cdpdto = cddto.parameters.get(0); + assertThat(cdpdto, instanceOf(EnrichedConfigDescriptionParameterDTO.class)); + assertThat(cdpdto.defaultValue, is(CONFIG_PARAMETER_DEFAULT_VALUE)); + EnrichedConfigDescriptionParameterDTO ecdpdto = (EnrichedConfigDescriptionParameterDTO) cdpdto; + assertThat(ecdpdto.defaultValues, is(notNullValue())); + assertThat(ecdpdto.defaultValues.size(), is(3)); + assertThat(ecdpdto.defaultValues, is(equalTo(Arrays.asList("first value", "second value", "third value")))); + } + +}