/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2015 Simon Stuerz * * Copyright (C) 2014 Michael Zanetti * * * * This file is part of guh. * * * * Guh is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, version 2 of the License. * * * * Guh is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with guh. If not, see . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "ruleshandler.h" #include "guhcore.h" #include "ruleengine.h" #include "loggingcategories.h" #include namespace guhserver { RulesHandler::RulesHandler(QObject *parent) : JsonHandler(parent) { QVariantMap params; QVariantMap returns; params.clear(); returns.clear(); setDescription("GetRules", "Get the descriptions of all configured rules. If you need more information about a specific rule use the " "method Rules.GetRuleDetails."); setParams("GetRules", params); returns.insert("ruleDescriptions", QVariantList() << JsonTypes::ruleDescriptionRef()); setReturns("GetRules", returns); params.clear(); returns.clear(); setDescription("GetRuleDetails", "Get details for the rule identified by ruleId"); params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); setParams("GetRuleDetails", params); returns.insert("o:rule", JsonTypes::ruleRef()); returns.insert("ruleError", JsonTypes::ruleErrorRef()); setReturns("GetRuleDetails", returns); params.clear(); returns.clear(); setDescription("AddRule", "Add a rule. You can describe rules by one or many EventDesciptors and a StateEvaluator. Note that only " "one of either eventDescriptor or eventDescriptorList may be passed at a time. A rule can be created but left disabled, " "meaning it won't actually be executed until set to enabled. If not given, enabled defaults to true."); params.insert("o:eventDescriptor", JsonTypes::eventDescriptorRef()); params.insert("o:eventDescriptorList", QVariantList() << JsonTypes::eventDescriptorRef()); params.insert("o:stateEvaluator", JsonTypes::stateEvaluatorRef()); params.insert("o:exitActions", QVariantList() << JsonTypes::ruleActionRef()); params.insert("o:enabled", JsonTypes::basicTypeToString(JsonTypes::Bool)); params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String)); QVariantList actions; actions.append(JsonTypes::ruleActionRef()); params.insert("actions", actions); setParams("AddRule", params); returns.insert("ruleError", JsonTypes::ruleErrorRef()); returns.insert("o:ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); setReturns("AddRule", returns); params.clear(); returns.clear(); actions.clear(); setDescription("EditRule", "Edit the parameters of a rule. The configuration of the rule with the given ruleId " "will be replaced with the new given configuration. In ordert to enable or disable a Rule, please use the " "methods \"Rules.EnableRule\" and \"Rules.DisableRule\". If successfull, the notification \"Rule.RuleConfigurationChanged\" " "will be emitted."); params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); params.insert("name", JsonTypes::basicTypeToString(JsonTypes::String)); params.insert("o:eventDescriptor", JsonTypes::eventDescriptorRef()); params.insert("o:eventDescriptorList", QVariantList() << JsonTypes::eventDescriptorRef()); params.insert("o:stateEvaluator", JsonTypes::stateEvaluatorRef()); params.insert("o:exitActions", QVariantList() << JsonTypes::ruleActionRef()); params.insert("o:enabled", JsonTypes::basicTypeToString(JsonTypes::Bool)); actions.append(JsonTypes::ruleActionRef()); params.insert("actions", actions); setParams("EditRule", params); returns.insert("ruleError", JsonTypes::ruleErrorRef()); returns.insert("o:rule", JsonTypes::ruleRef()); setReturns("EditRule", returns); params.clear(); returns.clear(); setDescription("RemoveRule", "Remove a rule"); params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); setParams("RemoveRule", params); returns.insert("ruleError", JsonTypes::ruleErrorRef()); setReturns("RemoveRule", returns); params.clear(); returns.clear(); setDescription("FindRules", "Find a list of rules containing any of the given parameters."); params.insert("deviceId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); setParams("FindRules", params); returns.insert("ruleIds", QVariantList() << JsonTypes::basicTypeToString(JsonTypes::Uuid)); setReturns("FindRules", returns); params.clear(); returns.clear(); setDescription("EnableRule", "Enabled a rule that has previously been disabled." "If successfull, the notification \"Rule.RuleConfigurationChanged\" will be emitted."); params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); setParams("EnableRule", params); returns.insert("ruleError", JsonTypes::ruleErrorRef()); setReturns("EnableRule", returns); params.clear(); returns.clear(); setDescription("DisableRule", "Disable a rule. The rule won't be triggered by it's events or state changes while it is disabled. " "If successfull, the notification \"Rule.RuleConfigurationChanged\" will be emitted."); params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); setParams("DisableRule", params); returns.insert("ruleError", JsonTypes::ruleErrorRef()); setReturns("DisableRule", returns); // Notifications params.clear(); returns.clear(); setDescription("RuleRemoved", "Emitted whenever a Rule was removed."); params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); setParams("RuleRemoved", params); params.clear(); returns.clear(); setDescription("RuleAdded", "Emitted whenever a Rule was added."); params.insert("rule", JsonTypes::ruleRef()); setParams("RuleAdded", params); params.clear(); returns.clear(); setDescription("RuleActiveChanged", "Emitted whenever the active state of a Rule changed."); params.insert("ruleId", JsonTypes::basicTypeToString(JsonTypes::Uuid)); params.insert("active", JsonTypes::basicTypeToString(JsonTypes::Bool)); setParams("RuleActiveChanged", params); params.clear(); returns.clear(); setDescription("RuleConfigurationChanged", "Emitted whenever the configuration of a Rule changed."); params.insert("rule", JsonTypes::ruleRef()); setParams("RuleConfigurationChanged", params); connect(GuhCore::instance(), &GuhCore::ruleAdded, this, &RulesHandler::ruleAddedNotification); connect(GuhCore::instance(), &GuhCore::ruleRemoved, this, &RulesHandler::ruleRemovedNotification); connect(GuhCore::instance(), &GuhCore::ruleActiveChanged, this, &RulesHandler::ruleActiveChangedNotification); connect(GuhCore::instance(), &GuhCore::ruleConfigurationChanged, this, &RulesHandler::ruleConfigurationChangedNotification); } QString RulesHandler::name() const { return "Rules"; } JsonReply* RulesHandler::GetRules(const QVariantMap ¶ms) { Q_UNUSED(params) QVariantList rulesList; foreach (const Rule &rule, GuhCore::instance()->rules()) { rulesList.append(JsonTypes::packRuleDescription(rule)); } QVariantMap returns; returns.insert("ruleDescriptions", rulesList); return createReply(returns); } JsonReply *RulesHandler::GetRuleDetails(const QVariantMap ¶ms) { RuleId ruleId = RuleId(params.value("ruleId").toString()); Rule rule = GuhCore::instance()->findRule(ruleId); if (rule.id().isNull()) { return createReply(statusToReply(RuleEngine::RuleErrorRuleNotFound)); } QVariantMap returns = statusToReply(RuleEngine::RuleErrorNoError); returns.insert("rule", JsonTypes::packRule(rule)); return createReply(returns); } JsonReply* RulesHandler::AddRule(const QVariantMap ¶ms) { // check rule consistency RuleEngine::RuleError ruleConsistencyError = verifyRuleConsistency(params); if (ruleConsistencyError != RuleEngine::RuleErrorNoError) { QVariantMap returns; returns.insert("ruleError", JsonTypes::ruleErrorToString(ruleConsistencyError)); return createReply(returns); } // Check and upack eventDescriptorList QPair, RuleEngine::RuleError> eventDescriptorVerification = verifyEventDescriptors(params); QList eventDescriptorList = eventDescriptorVerification.first; if (eventDescriptorVerification.second != RuleEngine::RuleErrorNoError) { QVariantMap returns; returns.insert("ruleError", JsonTypes::ruleErrorToString(eventDescriptorVerification.second)); return createReply(returns); } // Check and unpack stateEvaluator qCDebug(dcJsonRpc) << "unpacking stateEvaluator:" << params.value("stateEvaluator").toMap(); StateEvaluator stateEvaluator = JsonTypes::unpackStateEvaluator(params.value("stateEvaluator").toMap()); // Check and unpack actions QPair, RuleEngine::RuleError> actionsVerification = verifyActions(params, eventDescriptorList); QList actions = actionsVerification.first; if (actionsVerification.second != RuleEngine::RuleErrorNoError) { QVariantMap returns; returns.insert("ruleError", JsonTypes::ruleErrorToString(actionsVerification.second)); return createReply(returns); } // Check and unpack exitActions QPair, RuleEngine::RuleError> exitActionsVerification = verifyExitActions(params); QList exitActions = exitActionsVerification.first; if (exitActionsVerification.second != RuleEngine::RuleErrorNoError) { QVariantMap returns; returns.insert("ruleError", JsonTypes::ruleErrorToString(exitActionsVerification.second)); return createReply(returns); } QString name = params.value("name", QString()).toString(); bool enabled = params.value("enabled", true).toBool(); RuleId newRuleId = RuleId::createRuleId(); RuleEngine::RuleError status = GuhCore::instance()->addRule(newRuleId, name, eventDescriptorList, stateEvaluator, actions, exitActions, enabled); QVariantMap returns; if (status == RuleEngine::RuleErrorNoError) { returns.insert("ruleId", newRuleId.toString()); } returns.insert("ruleError", JsonTypes::ruleErrorToString(status)); return createReply(returns); } JsonReply *RulesHandler::EditRule(const QVariantMap ¶ms) { // check rule consistency RuleEngine::RuleError ruleConsistencyError = verifyRuleConsistency(params); if (ruleConsistencyError != RuleEngine::RuleErrorNoError) { QVariantMap returns; returns.insert("ruleError", JsonTypes::ruleErrorToString(ruleConsistencyError)); return createReply(returns); } // Check and upack eventDescriptorList QPair, RuleEngine::RuleError> eventDescriptorVerification = verifyEventDescriptors(params); QList eventDescriptorList = eventDescriptorVerification.first; if (eventDescriptorVerification.second != RuleEngine::RuleErrorNoError) { QVariantMap returns; returns.insert("ruleError", JsonTypes::ruleErrorToString(eventDescriptorVerification.second)); return createReply(returns); } // Check and unpack stateEvaluator qCDebug(dcJsonRpc) << "unpacking stateEvaluator:" << params.value("stateEvaluator").toMap(); StateEvaluator stateEvaluator = JsonTypes::unpackStateEvaluator(params.value("stateEvaluator").toMap()); // Check and unpack actions QPair, RuleEngine::RuleError> actionsVerification = verifyActions(params, eventDescriptorList); QList actions = actionsVerification.first; if (actionsVerification.second != RuleEngine::RuleErrorNoError) { QVariantMap returns; returns.insert("ruleError", JsonTypes::ruleErrorToString(actionsVerification.second)); return createReply(returns); } // Check and unpack exitActions QPair, RuleEngine::RuleError> exitActionsVerification = verifyExitActions(params); QList exitActions = exitActionsVerification.first; if (exitActionsVerification.second != RuleEngine::RuleErrorNoError) { QVariantMap returns; returns.insert("ruleError", JsonTypes::ruleErrorToString(exitActionsVerification.second)); return createReply(returns); } QString name = params.value("name", QString()).toString(); bool enabled = params.value("enabled", true).toBool(); RuleId ruleId = RuleId(params.value("ruleId").toString()); RuleEngine::RuleError status = GuhCore::instance()->editRule(ruleId, name, eventDescriptorList, stateEvaluator, actions, exitActions, enabled); QVariantMap returns; if (status == RuleEngine::RuleErrorNoError) { returns.insert("rule", JsonTypes::packRule(GuhCore::instance()->findRule(ruleId))); } returns.insert("ruleError", JsonTypes::ruleErrorToString(status)); return createReply(returns); } JsonReply* RulesHandler::RemoveRule(const QVariantMap ¶ms) { QVariantMap returns; RuleId ruleId(params.value("ruleId").toString()); RuleEngine::RuleError status = GuhCore::instance()->removeRule(ruleId); returns.insert("ruleError", JsonTypes::ruleErrorToString(status)); return createReply(returns); } JsonReply *RulesHandler::FindRules(const QVariantMap ¶ms) { DeviceId deviceId = DeviceId(params.value("deviceId").toString()); QList rules = GuhCore::instance()->findRules(deviceId); QVariantList rulesList; foreach (const RuleId &ruleId, rules) { rulesList.append(ruleId); } QVariantMap returns; returns.insert("ruleIds", rulesList); return createReply(returns); } JsonReply *RulesHandler::EnableRule(const QVariantMap ¶ms) { return createReply(statusToReply(GuhCore::instance()->enableRule(RuleId(params.value("ruleId").toString())))); } JsonReply *RulesHandler::DisableRule(const QVariantMap ¶ms) { return createReply(statusToReply(GuhCore::instance()->disableRule(RuleId(params.value("ruleId").toString())))); } QVariant::Type RulesHandler::getActionParamType(const ActionTypeId &actionTypeId, const QString ¶mName) { foreach (const DeviceClass &deviceClass, GuhCore::instance()->supportedDevices()) { foreach (const ActionType &actionType, deviceClass.actionTypes()) { if (actionType.id() == actionTypeId) { foreach (const ParamType ¶mType, actionType.paramTypes()) { if (paramType.name() == paramName) { return paramType.type(); } } } } } return QVariant::Invalid; } QVariant::Type RulesHandler::getEventParamType(const EventTypeId &eventTypeId, const QString ¶mName) { foreach (const DeviceClass &deviceClass, GuhCore::instance()->supportedDevices()) { foreach (const EventType &eventType, deviceClass.eventTypes()) { if (eventType.id() == eventTypeId) { foreach (const ParamType ¶mType, eventType.paramTypes()) { // get ParamType of Event if (paramType.name() == paramName) { return paramType.type(); } } } } } return QVariant::Invalid; } bool RulesHandler::checkEventDescriptors(const QList eventDescriptors, const EventTypeId &eventTypeId) { foreach (const EventDescriptor eventDescriptor, eventDescriptors) { if (eventDescriptor.eventTypeId() == eventTypeId) { return true; } } return false; } RuleEngine::RuleError RulesHandler::verifyRuleConsistency(const QVariantMap ¶ms) { // check if there are an eventDescriptor and an eventDescriptorList if (params.contains("eventDescriptor") && params.contains("eventDescriptorList")) { qCWarning(dcJsonRpc) << "Only one of eventDesciptor or eventDescriptorList may be used."; return RuleEngine::RuleErrorInvalidParameter; } // check if this rules is based on any event and contains exit actions if (params.contains("eventDescriptor") || params.contains("eventDescriptorList")) { if (params.contains("exitActions")) { qCWarning(dcJsonRpc) << "The exitActions will never be executed if the rule contains an eventDescriptor."; return RuleEngine::RuleErrorInvalidRuleFormat; } } // check if there are any actions if (params.value("actions").toList().isEmpty()) { qCWarning(dcJsonRpc) << "Rule actions missing. A rule without actions has no effect."; return RuleEngine::RuleErrorMissingParameter; } // TODO: check if events and stateEvaluators are missing return RuleEngine::RuleErrorNoError; } QPair, RuleEngine::RuleError> RulesHandler::verifyEventDescriptors(const QVariantMap ¶ms) { // Check and unpack eventDescriptors QList eventDescriptorList = QList(); if (params.contains("eventDescriptor")) { QVariantMap eventMap = params.value("eventDescriptor").toMap(); qCDebug(dcJsonRpc) << "unpacking eventDescriptor" << eventMap; eventDescriptorList.append(JsonTypes::unpackEventDescriptor(eventMap)); } else if (params.contains("eventDescriptorList")) { QVariantList eventDescriptors = params.value("eventDescriptorList").toList(); qCDebug(dcJsonRpc) << "unpacking eventDescriptorList:" << eventDescriptors; foreach (const QVariant &eventVariant, eventDescriptors) { QVariantMap eventMap = eventVariant.toMap(); eventDescriptorList.append(JsonTypes::unpackEventDescriptor(eventMap)); } } return QPair, RuleEngine::RuleError>(eventDescriptorList, RuleEngine::RuleErrorNoError); } QPair, RuleEngine::RuleError> RulesHandler::verifyActions(const QVariantMap ¶ms, const QList &eventDescriptorList) { QList actions; QVariantList actionList = params.value("actions").toList(); qCDebug(dcJsonRpc) << "unpacking actions:" << actionList; foreach (const QVariant &actionVariant, actionList) { QVariantMap actionMap = actionVariant.toMap(); RuleAction action(ActionTypeId(actionMap.value("actionTypeId").toString()), DeviceId(actionMap.value("deviceId").toString())); RuleActionParamList actionParamList = JsonTypes::unpackRuleActionParams(actionMap.value("ruleActionParams").toList()); foreach (const RuleActionParam &ruleActionParam, actionParamList) { if (!ruleActionParam.isValid()) { qCWarning(dcJsonRpc) << "got an actionParam with value AND eventTypeId!"; return QPair, RuleEngine::RuleError>(actions, RuleEngine::RuleErrorInvalidRuleActionParameter); } } qCDebug(dcJsonRpc) << "params in exitAction" << action.ruleActionParams(); action.setRuleActionParams(actionParamList); actions.append(action); } // check possible eventTypeIds in params foreach (const RuleAction &ruleAction, actions) { if (ruleAction.isEventBased()) { foreach (const RuleActionParam &ruleActionParam, ruleAction.ruleActionParams()) { if (ruleActionParam.eventTypeId() != EventTypeId()) { // We have an eventTypeId if (eventDescriptorList.isEmpty()) { qCWarning(dcJsonRpc) << "RuleAction" << ruleAction.actionTypeId() << "contains an eventTypeId, but there are no eventDescriptors."; return QPair, RuleEngine::RuleError>(actions, RuleEngine::RuleErrorInvalidRuleActionParameter); } // now check if this eventType is in the eventDescriptorList of this rule if (!checkEventDescriptors(eventDescriptorList, ruleActionParam.eventTypeId())) { qCWarning(dcJsonRpc) << "eventTypeId from RuleAction" << ruleAction.actionTypeId() << "missing in eventDescriptors."; return QPair, RuleEngine::RuleError>(actions, RuleEngine::RuleErrorInvalidRuleActionParameter); } // check if the param type of the event and the action match QVariant::Type eventParamType = getEventParamType(ruleActionParam.eventTypeId(), ruleActionParam.eventParamName()); QVariant::Type actionParamType = getActionParamType(ruleAction.actionTypeId(), ruleActionParam.name()); if (eventParamType != actionParamType) { qCWarning(dcJsonRpc) << "RuleActionParam" << ruleActionParam.name() << " and given event param " << ruleActionParam.eventParamName() << "have not the same type:"; qCWarning(dcJsonRpc) << " -> actionParamType:" << actionParamType; qCWarning(dcJsonRpc) << " -> eventParamType:" << eventParamType; return QPair, RuleEngine::RuleError>(actions, RuleEngine::RuleErrorTypesNotMatching); } } } } } return QPair, RuleEngine::RuleError>(actions, RuleEngine::RuleErrorNoError); } QPair, RuleEngine::RuleError> RulesHandler::verifyExitActions(const QVariantMap ¶ms) { QList exitActions; if (params.contains("exitActions")) { QVariantList exitActionList = params.value("exitActions").toList(); qCDebug(dcJsonRpc) << "unpacking exitActions:" << exitActionList; foreach (const QVariant &actionVariant, exitActionList) { QVariantMap actionMap = actionVariant.toMap(); RuleAction action(ActionTypeId(actionMap.value("actionTypeId").toString()), DeviceId(actionMap.value("deviceId").toString())); if (action.isEventBased()) { qCWarning(dcJsonRpc) << "got exitAction with a param value containing an eventTypeId!"; return QPair, RuleEngine::RuleError>(exitActions, RuleEngine::RuleErrorInvalidRuleActionParameter); } qCDebug(dcJsonRpc) << "params in exitAction" << action.ruleActionParams(); action.setRuleActionParams(JsonTypes::unpackRuleActionParams(actionMap.value("ruleActionParams").toList())); exitActions.append(action); } } return QPair, RuleEngine::RuleError>(exitActions, RuleEngine::RuleErrorNoError); } void RulesHandler::ruleRemovedNotification(const RuleId &ruleId) { QVariantMap params; params.insert("ruleId", ruleId); emit RuleRemoved(params); } void RulesHandler::ruleAddedNotification(const Rule &rule) { QVariantMap params; params.insert("rule", JsonTypes::packRule(rule)); emit RuleAdded(params); } void RulesHandler::ruleActiveChangedNotification(const Rule &rule) { QVariantMap params; params.insert("ruleId", rule.id()); params.insert("active", rule.active()); emit RuleActiveChanged(params); } void RulesHandler::ruleConfigurationChangedNotification(const Rule &rule) { QVariantMap params; params.insert("rule", JsonTypes::packRule(rule)); emit RuleConfigurationChanged(params); } }