/**
* Copyright (c) 1997, 2015 by ProSyst Software GmbH and others.
* 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.automation.rest.internal;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
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.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriInfo;
import org.eclipse.smarthome.automation.Action;
import org.eclipse.smarthome.automation.Condition;
import org.eclipse.smarthome.automation.Module;
import org.eclipse.smarthome.automation.Rule;
import org.eclipse.smarthome.automation.RuleRegistry;
import org.eclipse.smarthome.automation.Trigger;
import org.eclipse.smarthome.automation.dto.ActionDTO;
import org.eclipse.smarthome.automation.dto.ActionDTOMapper;
import org.eclipse.smarthome.automation.dto.ConditionDTO;
import org.eclipse.smarthome.automation.dto.ConditionDTOMapper;
import org.eclipse.smarthome.automation.dto.ModuleDTO;
import org.eclipse.smarthome.automation.dto.RuleDTO;
import org.eclipse.smarthome.automation.dto.RuleDTOMapper;
import org.eclipse.smarthome.automation.dto.TriggerDTO;
import org.eclipse.smarthome.automation.dto.TriggerDTOMapper;
import org.eclipse.smarthome.automation.rest.internal.dto.EnrichedRuleDTO;
import org.eclipse.smarthome.automation.rest.internal.dto.EnrichedRuleDTOMapper;
import org.eclipse.smarthome.config.core.ConfigUtil;
import org.eclipse.smarthome.config.core.Configuration;
import org.eclipse.smarthome.io.rest.JSONResponse;
import org.eclipse.smarthome.io.rest.SatisfiableRESTResource;
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;
import io.swagger.annotations.ResponseHeader;
/**
* This class acts as a REST resource for rules and is registered with the Jersey servlet.
*
* @author Kai Kreuzer - Initial contribution
* @author Markus Rathgeb - Use DTOs
*/
@Path("rules")
@Api("rules")
public class RuleResource implements SatisfiableRESTResource {
private final Logger logger = LoggerFactory.getLogger(RuleResource.class);
private RuleRegistry ruleRegistry;
@Context
private UriInfo uriInfo;
protected void setRuleRegistry(RuleRegistry ruleRegistry) {
this.ruleRegistry = ruleRegistry;
}
protected void unsetRuleRegistry(RuleRegistry ruleRegistry) {
this.ruleRegistry = null;
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Get all available rules.", response = EnrichedRuleDTO.class, responseContainer = "Collection")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK", response = EnrichedRuleDTO.class, responseContainer = "Collection") })
public Response getAll() {
Collection<EnrichedRuleDTO> rules = enrich(ruleRegistry.getAll());
return Response.ok(rules).build();
}
private Collection<EnrichedRuleDTO> enrich(Collection<Rule> rules) {
Collection<EnrichedRuleDTO> enrichedRules = new ArrayList<EnrichedRuleDTO>(rules.size());
for (Rule rule : rules) {
enrichedRules.add(EnrichedRuleDTOMapper.map(rule, ruleRegistry));
}
return enrichedRules;
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Creates a rule.")
@ApiResponses(value = {
@ApiResponse(code = 201, message = "Created", responseHeaders = @ResponseHeader(name = "Location", description = "Newly created Rule", response = String.class)),
@ApiResponse(code = 409, message = "Creation of the rule is refused. Rule with the same UID already exists."),
@ApiResponse(code = 400, message = "Creation of the rule is refused. Missing required parameter.") })
public Response create(@ApiParam(value = "rule data", required = true) RuleDTO rule) throws IOException {
try {
final Rule newRule = ruleRegistry.add(RuleDTOMapper.map(rule));
return Response.status(Status.CREATED)
.header("Location", "rules/" + URLEncoder.encode(newRule.getUID(), "UTF-8")).build();
} catch (IllegalArgumentException e) {
String errMessage = "Creation of the rule is refused: " + e.getMessage();
logger.warn(errMessage);
return JSONResponse.createErrorResponse(Status.CONFLICT, errMessage);
} catch (RuntimeException e) {
String errMessage = "Creation of the rule is refused: " + e.getMessage();
logger.warn(errMessage);
return JSONResponse.createErrorResponse(Status.BAD_REQUEST, errMessage);
}
}
@GET
@Path("/{ruleUID}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Gets the rule corresponding to the given UID.", response = EnrichedRuleDTO.class)
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = EnrichedRuleDTO.class),
@ApiResponse(code = 404, message = "Rule not found") })
public Response getByUID(@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID) {
Rule rule = ruleRegistry.get(ruleUID);
if (rule != null) {
return Response.ok(EnrichedRuleDTOMapper.map(rule, ruleRegistry)).build();
} else {
return Response.status(Status.NOT_FOUND).build();
}
}
@DELETE
@Path("/{ruleUID}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Removes an existing rule corresponding to the given UID.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = String.class),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found.") })
public Response remove(@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID) {
Rule removedRule = ruleRegistry.remove(ruleUID);
if (removedRule == null) {
logger.info("Received HTTP DELETE request at '{}' for the unknown rule '{}'.", uriInfo.getPath(), ruleUID);
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok().build();
}
@PUT
@Path("/{ruleUID}")
@Consumes(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Updates an existing rule corresponding to the given UID.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found.") })
public Response update(@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID,
@ApiParam(value = "rule data", required = true) RuleDTO rule) throws IOException {
rule.uid = ruleUID;
final Rule oldRule = ruleRegistry.update(RuleDTOMapper.map(rule));
if (oldRule == null) {
logger.info("Received HTTP PUT request for update at '{}' for the unknown rule '{}'.", uriInfo.getPath(),
ruleUID);
return Response.status(Status.NOT_FOUND).build();
}
return Response.ok().build();
}
@GET
@Path("/{ruleUID}/config")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Gets the rule configuration values.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = String.class),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found.") })
public Response getConfiguration(@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID)
throws IOException {
Rule rule = ruleRegistry.get(ruleUID);
if (rule == null) {
logger.info("Received HTTP GET request for config at '{}' for the unknown rule '{}'.", uriInfo.getPath(),
ruleUID);
return Response.status(Status.NOT_FOUND).build();
} else {
return Response.ok(rule.getConfiguration().getProperties()).build();
}
}
@PUT
@Path("/{ruleUID}/config")
@Consumes(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Sets the rule configuration values.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found.") })
public Response updateConfiguration(
@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID,
@ApiParam(value = "config") Map<String, Object> configurationParameters) throws IOException {
Map<String, Object> config = ConfigUtil.normalizeTypes(configurationParameters);
Rule rule = ruleRegistry.get(ruleUID);
if (rule == null) {
logger.info("Received HTTP PUT request for update config at '{}' for the unknown rule '{}'.",
uriInfo.getPath(), ruleUID);
return Response.status(Status.NOT_FOUND).build();
} else {
rule.setConfiguration(new Configuration(config));
ruleRegistry.update(rule);
return Response.ok().build();
}
}
@POST
@Path("/{ruleUID}/enable")
@Consumes(MediaType.TEXT_PLAIN)
@ApiOperation(value = "Sets the rule enabled status.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found.") })
public Response enableRule(@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID,
@ApiParam(value = "enable", required = true) String enabled) throws IOException {
Rule rule = ruleRegistry.get(ruleUID);
if (rule == null) {
logger.info("Received HTTP PUT request for update config at '{}' for the unknown rule '{}'.",
uriInfo.getPath(), ruleUID);
return Response.status(Status.NOT_FOUND).build();
} else {
ruleRegistry.setEnabled(ruleUID, !"false".equalsIgnoreCase(enabled));
// ruleRegistry.update(rule);
return Response.ok().build();
}
}
@POST
@Path("/{ruleUID}/runnow")
@Consumes(MediaType.TEXT_PLAIN)
@ApiOperation(value = "Executes actions of the rule.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found.") })
public Response runNow(@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID)
throws IOException {
Rule rule = ruleRegistry.get(ruleUID);
if (rule == null) {
logger.info("Received HTTP PUT request for update config at '{}' for the unknown rule '{}'.",
uriInfo.getPath(), ruleUID);
return Response.status(Status.NOT_FOUND).build();
} else {
ruleRegistry.runNow(ruleUID);
return Response.ok().build();
}
}
@GET
@Path("/{ruleUID}/triggers")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Gets the rule triggers.", response = TriggerDTO.class, responseContainer = "List")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK", response = TriggerDTO.class, responseContainer = "List"),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found.") })
public Response getTriggers(@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID) {
Rule rule = ruleRegistry.get(ruleUID);
if (rule != null) {
return Response.ok(TriggerDTOMapper.map(rule.getTriggers())).build();
} else {
return Response.status(Status.NOT_FOUND).build();
}
}
@GET
@Path("/{ruleUID}/conditions")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Gets the rule conditions.", response = ConditionDTO.class, responseContainer = "List")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK", response = ConditionDTO.class, responseContainer = "List"),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found.") })
public Response getConditions(@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID) {
Rule rule = ruleRegistry.get(ruleUID);
if (rule != null) {
return Response.ok(ConditionDTOMapper.map(rule.getConditions())).build();
} else {
return Response.status(Status.NOT_FOUND).build();
}
}
@GET
@Path("/{ruleUID}/actions")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Gets the rule actions.", response = ActionDTO.class, responseContainer = "List")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK", response = ActionDTO.class, responseContainer = "List"),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found.") })
public Response getActions(@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID) {
Rule rule = ruleRegistry.get(ruleUID);
if (rule != null) {
return Response.ok(ActionDTOMapper.map(rule.getActions())).build();
} else {
return Response.status(Status.NOT_FOUND).build();
}
}
@GET
@Path("/{ruleUID}/{moduleCategory}/{id}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Gets the rule's module corresponding to the given Category and ID.", response = ModuleDTO.class)
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = ModuleDTO.class),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found or does not have a module with such Category and ID.") })
public Response getModuleById(@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID,
@PathParam("moduleCategory") @ApiParam(value = "moduleCategory", required = true) String moduleCategory,
@PathParam("id") @ApiParam(value = "id", required = true) String id) {
Rule rule = ruleRegistry.get(ruleUID);
if (rule != null) {
final ModuleDTO dto = getModuleDTO(rule, moduleCategory, id);
if (dto != null) {
return Response.ok(dto).build();
}
}
return Response.status(Status.NOT_FOUND).build();
}
@GET
@Path("/{ruleUID}/{moduleCategory}/{id}/config")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Gets the module's configuration.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = String.class),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found or does not have a module with such Category and ID.") })
public Response getModuleConfig(@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID,
@PathParam("moduleCategory") @ApiParam(value = "moduleCategory", required = true) String moduleCategory,
@PathParam("id") @ApiParam(value = "id", required = true) String id) {
Rule rule = ruleRegistry.get(ruleUID);
if (rule != null) {
Module module = getModule(rule, moduleCategory, id);
if (module != null) {
return Response.ok(module.getConfiguration().getProperties()).build();
}
}
return Response.status(Status.NOT_FOUND).build();
}
@GET
@Path("/{ruleUID}/{moduleCategory}/{id}/config/{param}")
@Produces(MediaType.TEXT_PLAIN)
@ApiOperation(value = "Gets the module's configuration parameter.", response = String.class)
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = String.class),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found or does not have a module with such Category and ID.") })
public Response getModuleConfigParam(
@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID,
@PathParam("moduleCategory") @ApiParam(value = "moduleCategory", required = true) String moduleCategory,
@PathParam("id") @ApiParam(value = "id", required = true) String id,
@PathParam("param") @ApiParam(value = "param", required = true) String param) {
Rule rule = ruleRegistry.get(ruleUID);
if (rule != null) {
Module module = getModule(rule, moduleCategory, id);
if (module != null) {
return Response.ok(module.getConfiguration().getProperties().get(param)).build();
}
}
return Response.status(Status.NOT_FOUND).build();
}
@PUT
@Path("/{ruleUID}/{moduleCategory}/{id}/config/{param}")
@ApiOperation(value = "Sets the module's configuration parameter value.")
@ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
@ApiResponse(code = 404, message = "Rule corresponding to the given UID does not found or does not have a module with such Category and ID.") })
@Consumes(MediaType.TEXT_PLAIN)
public Response setModuleConfigParam(
@PathParam("ruleUID") @ApiParam(value = "ruleUID", required = true) String ruleUID,
@PathParam("moduleCategory") @ApiParam(value = "moduleCategory", required = true) String moduleCategory,
@PathParam("id") @ApiParam(value = "id", required = true) String id,
@PathParam("param") @ApiParam(value = "param", required = true) String param,
@ApiParam(value = "value", required = true) String value) {
Rule rule = ruleRegistry.get(ruleUID);
if (rule != null) {
Module module = getModule(rule, moduleCategory, id);
if (module != null) {
Configuration configuration = module.getConfiguration();
configuration.put(param, ConfigUtil.normalizeType(value));
module.setConfiguration(configuration);
ruleRegistry.update(rule);
return Response.ok().build();
}
}
return Response.status(Status.NOT_FOUND).build();
}
protected <T extends Module> T getModuleById(final Collection<T> coll, final String id) {
if (coll == null) {
return null;
}
for (final T module : coll) {
if (module.getId().equals(id)) {
return module;
}
}
return null;
}
protected Trigger getTrigger(Rule rule, String id) {
return getModuleById(rule.getTriggers(), id);
}
protected Condition getCondition(Rule rule, String id) {
return getModuleById(rule.getConditions(), id);
}
protected Action getAction(Rule rule, String id) {
return getModuleById(rule.getActions(), id);
}
protected Module getModule(Rule rule, String moduleCategory, String id) {
if (moduleCategory.equals("triggers")) {
return getTrigger(rule, id);
} else if (moduleCategory.equals("conditions")) {
return getCondition(rule, id);
} else if (moduleCategory.equals("actions")) {
return getAction(rule, id);
} else {
return null;
}
}
protected ModuleDTO getModuleDTO(Rule rule, String moduleCategory, String id) {
if (moduleCategory.equals("triggers")) {
final Trigger trigger = getTrigger(rule, id);
return trigger == null ? null : TriggerDTOMapper.map(trigger);
} else if (moduleCategory.equals("conditions")) {
final Condition condition = getCondition(rule, id);
return condition == null ? null : ConditionDTOMapper.map(condition);
} else if (moduleCategory.equals("actions")) {
final Action action = getAction(rule, id);
return action == null ? null : ActionDTOMapper.map(action);
} else {
return null;
}
}
@Override
public boolean isSatisfied() {
return ruleRegistry != null;
}
}