/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.smarthome.io.rest.core.binding;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import org.eclipse.smarthome.config.core.ConfigDescription;
import org.eclipse.smarthome.config.core.ConfigDescriptionRegistry;
import org.eclipse.smarthome.config.core.ConfigUtil;
import org.eclipse.smarthome.config.core.Configuration;
import org.eclipse.smarthome.core.auth.Role;
import org.eclipse.smarthome.core.binding.BindingInfo;
import org.eclipse.smarthome.core.binding.BindingInfoRegistry;
import org.eclipse.smarthome.core.binding.dto.BindingInfoDTO;
import org.eclipse.smarthome.io.rest.LocaleUtil;
import org.eclipse.smarthome.io.rest.SatisfiableRESTResource;
import org.eclipse.smarthome.io.rest.core.config.ConfigurationService;
import org.eclipse.smarthome.io.rest.core.service.ConfigurableServiceResource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
/**
* This class acts as a REST resource for bindings and is registered with the
* Jersey servlet.
*
* @author Dennis Nobel - Initial contribution
* @author Kai Kreuzer - refactored for using the OSGi JAX-RS connector
* @author Yordan Zhelev - Added Swagger annotations
* @author Franck Dechavanne - Added DTOs to ApiResponses
*/
@Path(BindingResource.PATH_BINDINGS)
@RolesAllowed({ Role.ADMIN })
@Api(value = BindingResource.PATH_BINDINGS)
public class BindingResource implements SatisfiableRESTResource {
/** The URI path to this resource */
public static final String PATH_BINDINGS = "bindings";
private final Logger logger = LoggerFactory.getLogger(ConfigurableServiceResource.class);
private ConfigurationService configurationService;
private ConfigDescriptionRegistry configDescRegistry;
private BindingInfoRegistry bindingInfoRegistry;
protected void setBindingInfoRegistry(BindingInfoRegistry bindingInfoRegistry) {
this.bindingInfoRegistry = bindingInfoRegistry;
}
protected void unsetBindingInfoRegistry(BindingInfoRegistry bindingInfoRegistry) {
this.bindingInfoRegistry = null;
}
@Context
UriInfo uriInfo;
@GET
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Get all bindings.", response = BindingInfoDTO.class, responseContainer = "Set")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK", response = BindingInfoDTO.class, responseContainer = "Set") })
public Response getAll(@HeaderParam(HttpHeaders.ACCEPT_LANGUAGE) @ApiParam(value = "language") String language) {
final Locale locale = LocaleUtil.getLocale(language);
Set<BindingInfo> bindingInfos = bindingInfoRegistry.getBindingInfos(locale);
Set<BindingInfoDTO> bindingInfoBeans = map(bindingInfos, locale);
return Response.ok(bindingInfoBeans).build();
}
@GET
@Path("/{bindingId}/config")
@Produces({ MediaType.APPLICATION_JSON })
@ApiOperation(value = "Get binding configuration for given binding ID.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = String.class),
@ApiResponse(code = 404, message = "Binding does not exist"),
@ApiResponse(code = 500, message = "Configuration can not be read due to internal error") })
public Response getConfiguration(
@PathParam("bindingId") @ApiParam(value = "service ID", required = true) String bindingId) {
try {
String configId = getConfigId(bindingId);
if (configId == null) {
logger.warn("Cannot get config id for binding id '{}', probably because binding does not exist.",
bindingId);
return Response.status(404).build();
}
Configuration configuration = configurationService.get(configId);
return configuration != null ? Response.ok(configuration.getProperties()).build()
: Response.ok(Collections.emptyMap()).build();
} catch (IOException ex) {
logger.error("Cannot get configuration for service {}: " + ex.getMessage(), bindingId, ex);
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
}
@PUT
@Path("/{bindingId}/config")
@Consumes(MediaType.APPLICATION_JSON)
@Produces({ MediaType.APPLICATION_JSON })
@ApiOperation(value = "Updates a binding configuration for given binding ID and returns the old configuration.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = String.class),
@ApiResponse(code = 204, message = "No old configuration"),
@ApiResponse(code = 404, message = "Binding does not exist"),
@ApiResponse(code = 500, message = "Configuration can not be updated due to internal error") })
public Response updateConfiguration(
@PathParam("bindingId") @ApiParam(value = "service ID", required = true) String bindingId,
Map<String, Object> configuration) {
try {
String configId = getConfigId(bindingId);
if (configId == null) {
logger.warn("Cannot get config id for binding id '{}', probably because binding does not exist.",
bindingId);
return Response.status(404).build();
}
Configuration oldConfiguration = configurationService.get(configId);
configurationService.update(configId, new Configuration(normalizeConfiguration(configuration, bindingId)));
return oldConfiguration != null ? Response.ok(oldConfiguration.getProperties()).build()
: Response.noContent().build();
} catch (IOException ex) {
logger.error("Cannot update configuration for service {}: " + ex.getMessage(), bindingId, ex);
return Response.status(Status.INTERNAL_SERVER_ERROR).build();
}
}
private Map<String, Object> normalizeConfiguration(Map<String, Object> properties, String bindingId) {
if (properties == null || properties.isEmpty()) {
return properties;
}
BindingInfo bindingInfo = this.bindingInfoRegistry.getBindingInfo(bindingId);
if (bindingInfo == null || bindingInfo.getConfigDescriptionURI() == null) {
return properties;
}
ConfigDescription configDesc = configDescRegistry.getConfigDescription(bindingInfo.getConfigDescriptionURI());
if (configDesc == null) {
return properties;
}
return ConfigUtil.normalizeTypes(properties, Collections.singletonList(configDesc));
}
private String getConfigId(String bindingId) {
BindingInfo bindingInfo = this.bindingInfoRegistry.getBindingInfo(bindingId);
if (bindingInfo != null) {
return bindingInfo.getServiceId();
} else {
return null;
}
}
private BindingInfoDTO map(BindingInfo bindingInfo, Locale locale) {
URI configDescriptionURI = bindingInfo.getConfigDescriptionURI();
return new BindingInfoDTO(bindingInfo.getId(), bindingInfo.getName(), bindingInfo.getAuthor(),
bindingInfo.getDescription(), configDescriptionURI != null ? configDescriptionURI.toString() : null);
}
private Set<BindingInfoDTO> map(Set<BindingInfo> bindingInfos, Locale locale) {
Set<BindingInfoDTO> bindingInfoBeans = new LinkedHashSet<>();
for (BindingInfo bindingInfo : bindingInfos) {
bindingInfoBeans.add(map(bindingInfo, locale));
}
return bindingInfoBeans;
}
protected void setConfigurationService(ConfigurationService configurationService) {
this.configurationService = configurationService;
}
protected void unsetConfigurationService(ConfigurationService configurationService) {
this.configurationService = null;
}
protected void setConfigDescriptionRegistry(ConfigDescriptionRegistry configDescriptionRegistry) {
this.configDescRegistry = configDescriptionRegistry;
}
protected void unsetConfigDescriptionRegistry(ConfigDescriptionRegistry configDescriptionRegistry) {
this.configDescRegistry = null;
}
@Override
public boolean isSatisfied() {
return configurationService != null && configDescRegistry != null && bindingInfoRegistry != null;
}
}