nymea/libnymea-core/jsonrpc/jsonhandler.cpp

347 lines
11 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> *
* *
* 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 <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class nymeaserver::JsonHandler
\brief This class represents an interface for developing a handler for the JSON-RPC API.
\ingroup json
\inmodule core
\sa JsonRPCServer, JsonReply
*/
/*! \fn QString nymeaserver::JsonHandler::name() const;
Pure virtual method for a JSON RPC handler. Returns the namespace of the handler.
*/
/*! \fn void nymeaserver::JsonHandler::asyncReply(int id, const QVariantMap &params);
This signal will be emitted when a reply with the given \a id and \a params is finished.
*/
#include "jsonhandler.h"
#include "loggingcategories.h"
#include <QMetaMethod>
#include <QDebug>
#include <QRegExp>
namespace nymeaserver {
/*! Constructs a new \l JsonHandler with the given \a parent. */
JsonHandler::JsonHandler(QObject *parent) :
QObject(parent)
{
}
/*! Returns a map with all supported methods, notifications and types for the given meta \a type. */
QVariantMap JsonHandler::introspect(QMetaMethod::MethodType type)
{
QVariantMap data;
for (int i = 0; i < metaObject()->methodCount(); ++i) {
QMetaMethod method = metaObject()->method(i);
if (method.methodType() != type) {
continue;
}
switch (method.methodType()) {
case QMetaMethod::Method: {
if (!m_descriptions.contains(method.name()) || !m_params.contains(method.name()) || !m_returns.contains(method.name())) {
continue;
}
qCDebug(dcJsonRpc) << "got method" << method.name();
QVariantMap methodData;
methodData.insert("description", m_descriptions.value(method.name()));
methodData.insert("params", m_params.value(method.name()));
methodData.insert("returns", m_returns.value(method.name()));
data.insert(name() + "." + method.name(), methodData);
break;
}
case QMetaMethod::Signal: {
if (!m_descriptions.contains(method.name()) || !m_params.contains(method.name())) {
continue;
}
if (QString(method.name()).contains(QRegExp("^[A-Z]"))) {
qCDebug(dcJsonRpc) << "got signal" << method.name();
QVariantMap methodData;
methodData.insert("description", m_descriptions.value(method.name()));
methodData.insert("params", m_params.value(method.name()));
data.insert(name() + "." + method.name(), methodData);
}
break;
default:
;;// Nothing to do for slots
}
}
}
return data;
}
/*! Returns true if this \l JsonHandler has a method with the given \a methodName.*/
bool JsonHandler::hasMethod(const QString &methodName)
{
return m_descriptions.contains(methodName) && m_params.contains(methodName) && m_returns.contains(methodName);
}
/*! Validates the given \a params for the given \a methodName. Returns the error string and false if
the params are not valid. */
QPair<bool, QString> JsonHandler::validateParams(const QString &methodName, const QVariantMap &params)
{
QVariantMap paramTemplate = m_params.value(methodName);
return JsonTypes::validateMap(paramTemplate, params);
}
/*! Validates the given \a returns for the given \a methodName. Returns the error string and false if
the params are not valid. */
QPair<bool, QString> JsonHandler::validateReturns(const QString &methodName, const QVariantMap &returns)
{
QVariantMap returnsTemplate = m_returns.value(methodName);
return JsonTypes::validateMap(returnsTemplate, returns);
}
/*! Sets the \a description of the method with the given \a methodName. */
void JsonHandler::setDescription(const QString &methodName, const QString &description)
{
for(int i = 0; i < metaObject()->methodCount(); ++i) {
QMetaMethod method = metaObject()->method(i);
if (method.name() == methodName) {
m_descriptions.insert(methodName, description);
return;
}
}
qCWarning(dcJsonRpc) << "Cannot set description. No such method:" << methodName;
}
/*! Sets the \a params of the method with the given \a methodName. */
void JsonHandler::setParams(const QString &methodName, const QVariantMap &params)
{
for(int i = 0; i < metaObject()->methodCount(); ++i) {
QMetaMethod method = metaObject()->method(i);
if (method.name() == methodName) {
m_params.insert(methodName, params);
return;
}
}
qCWarning(dcJsonRpc) << "Cannot set params. No such method:" << methodName;
}
/*! Sets the \a returns of the method with the given \a methodName. */
void JsonHandler::setReturns(const QString &methodName, const QVariantMap &returns)
{
for(int i = 0; i < metaObject()->methodCount(); ++i) {
QMetaMethod method = metaObject()->method(i);
if (method.name() == methodName) {
m_returns.insert(methodName, returns);
return;
}
}
qCWarning(dcJsonRpc) << "Cannot set returns. No such method:" << methodName;
}
/*! Returns the pointer to a new \l{JsonReply} with the given \a data. */
JsonReply *JsonHandler::createReply(const QVariantMap &data) const
{
return JsonReply::createReply(const_cast<JsonHandler*>(this), data);
}
/*! Returns the pointer to an asynchronous new \l{JsonReply} with the given \a method. */
JsonReply* JsonHandler::createAsyncReply(const QString &method) const
{
return JsonReply::createAsyncReply(const_cast<JsonHandler*>(this), method);
}
/*! Returns the formated error map for the given \a status.
*
* \sa DeviceManager::DeviceError
*/
QVariantMap JsonHandler::statusToReply(DeviceManager::DeviceError status) const
{
QVariantMap returns;
returns.insert("deviceError", JsonTypes::deviceErrorToString(status));
return returns;
}
/*! Returns the formated error map for the given \a status.
*
* \sa RuleEngine::RuleError
*/
QVariantMap JsonHandler::statusToReply(RuleEngine::RuleError status) const
{
QVariantMap returns;
returns.insert("ruleError", JsonTypes::ruleErrorToString(status));
return returns;
}
/*! Returns the formated error map for the given \a status.
*
* \sa Logging::LoggingError
*/
QVariantMap JsonHandler::statusToReply(Logging::LoggingError status) const
{
QVariantMap returns;
returns.insert("loggingError", JsonTypes::loggingErrorToString(status));
return returns;
}
/*! Returns the formated error map for the given \a status. */
QVariantMap JsonHandler::statusToReply(NymeaConfiguration::ConfigurationError status) const
{
QVariantMap returns;
returns.insert("configurationError", JsonTypes::configurationErrorToString(status));
return returns;
}
/*! Returns the formated error map for the given \a status. */
QVariantMap JsonHandler::statusToReply(NetworkManager::NetworkManagerError status) const
{
QVariantMap returns;
returns.insert("networkManagerError", JsonTypes::networkManagerErrorToString(status));
return returns;
}
/*!
\class nymeaserver::JsonReply
\brief This class represents a reply for the JSON-RPC API request.
\ingroup json
\inmodule core
\sa JsonHandler, JsonRPCServer
*/
/*! \enum nymeaserver::JsonReply::Type
This enum type specifies the type of a JsonReply.
\value TypeSync
The response is synchronous.
\value TypeAsync
The response is asynchronous.
*/
/*! \fn void nymeaserver::JsonReply::finished();
This signal will be emitted when a JsonReply is finished. A JsonReply is finished when
the response is ready or then the reply timed out.
*/
/*! Constructs a new \l JsonReply with the given \a type, \a handler, \a method and \a data. */
JsonReply::JsonReply(Type type, JsonHandler *handler, const QString &method, const QVariantMap &data):
m_type(type),
m_data(data),
m_handler(handler),
m_method(method),
m_timedOut(false)
{
connect(&m_timeout, &QTimer::timeout, this, &JsonReply::timeout);
}
/*! Returns the pointer to a new \l{JsonReply} for the given \a handler and \a data. */
JsonReply *JsonReply::createReply(JsonHandler *handler, const QVariantMap &data)
{
return new JsonReply(TypeSync, handler, QString(), data);
}
/*! Returns the pointer to a new asynchronous \l{JsonReply} for the given \a handler and \a method. */
JsonReply *JsonReply::createAsyncReply(JsonHandler *handler, const QString &method)
{
return new JsonReply(TypeAsync, handler, method);
}
/*! Returns the type of this \l{JsonReply}.*/
JsonReply::Type JsonReply::type() const
{
return m_type;
}
/*! Returns the data of this \l{JsonReply}.*/
QVariantMap JsonReply::data() const
{
return m_data;
}
/*! Sets the \a data of this \l{JsonReply}.*/
void JsonReply::setData(const QVariantMap &data)
{
m_data = data;
}
/*! Returns the handler of this \l{JsonReply}.*/
JsonHandler *JsonReply::handler() const
{
return m_handler;
}
/*! Returns the method of this \l{JsonReply}.*/
QString JsonReply::method() const
{
return m_method;
}
/*! Returns the client ID of this \l{JsonReply}.*/
QUuid JsonReply::clientId() const
{
return m_clientId;
}
/*! Sets the \a clientId of this \l{JsonReply}.*/
void JsonReply::setClientId(const QUuid &clientId)
{
m_clientId = clientId;
}
/*! Returns the command ID of this \l{JsonReply}.*/
int JsonReply::commandId() const
{
return m_commandId;
}
/*! Returns the \a commandId of this \l{JsonReply}.*/
void JsonReply::setCommandId(int commandId)
{
m_commandId = commandId;
}
/*! Start the timeout timer for this \l{JsonReply}. The default timeout is 15 seconds. */
void JsonReply::startWait()
{
m_timeout.start(30000);
}
void JsonReply::timeout()
{
m_timedOut = true;
emit finished();
}
/*! Returns true if this \l{JsonReply} timed out.*/
bool JsonReply::timedOut() const
{
return m_timedOut;
}
}