mirror of https://github.com/nymea/nymea.git
added deviceresources and first tests
parent
5a3c7a6cfb
commit
b86a062a87
|
|
@ -28,5 +28,6 @@ Q_LOGGING_CATEGORY(dcConnection, "Connection")
|
|||
Q_LOGGING_CATEGORY(dcTcpServer, "TcpServer")
|
||||
Q_LOGGING_CATEGORY(dcWebServer, "WebServer")
|
||||
Q_LOGGING_CATEGORY(dcJsonRpc, "JsonRpc")
|
||||
Q_LOGGING_CATEGORY(dcRest, "Rest")
|
||||
Q_LOGGING_CATEGORY(dcLogEngine, "LogEngine")
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcRuleEngine)
|
|||
Q_DECLARE_LOGGING_CATEGORY(dcHardware)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcConnection)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcJsonRpc)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcRest)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcLogEngine)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcTcpServer)
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcWebServer)
|
||||
|
|
|
|||
|
|
@ -109,9 +109,9 @@ HttpReply::HttpReply(const HttpStatusCode &statusCode) :
|
|||
{
|
||||
// set known headers
|
||||
setHeader(HttpHeaderType::ServerHeader, "guh/" + QByteArray(GUH_VERSION_STRING));
|
||||
setHeader(HttpHeaderType::UserAgentHeader, "guh/" + QByteArray(REST_API_VERSION));
|
||||
setHeader(HttpHeaderType::DateHeader, QDateTime::currentDateTime().toString("ddd, dd MMM yyyy hh:mm:ss").toUtf8() + " GMT");
|
||||
setHeader(HttpHeaderType::CacheControlHeader, "no-cache");
|
||||
packReply();
|
||||
}
|
||||
|
||||
/*! Set the \a statusCode for this \l{HttpReply}.*/
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ public:
|
|||
ServerHeader
|
||||
};
|
||||
|
||||
explicit HttpReply(const HttpStatusCode &statusCode);
|
||||
explicit HttpReply(const HttpStatusCode &statusCode = HttpStatusCode::Ok);
|
||||
|
||||
void setHttpStatusCode(const HttpStatusCode &statusCode);
|
||||
HttpStatusCode httpStatusCode() const;
|
||||
|
|
|
|||
|
|
@ -50,15 +50,17 @@ HttpRequest::HttpRequest(QByteArray rawData) :
|
|||
return;
|
||||
}
|
||||
|
||||
m_method = statusLineTokens.at(0).toUtf8().simplified();
|
||||
m_urlQuery = QUrlQuery(statusLineTokens.at(1).simplified());
|
||||
// verify http version
|
||||
m_httpVersion = statusLineTokens.at(2).toUtf8().simplified();
|
||||
|
||||
if (!m_httpVersion.contains("HTTP")) {
|
||||
qCWarning(dcWebServer) << "Unknown HTTP version:" << m_httpVersion;
|
||||
return;
|
||||
}
|
||||
m_method = getRequestMethodType(statusLineTokens.at(0).simplified());
|
||||
|
||||
m_urlQuery = QUrlQuery(statusLineTokens.at(1).simplified());
|
||||
|
||||
// verify headers
|
||||
foreach (const QString &line, headerLines) {
|
||||
if (!line.contains(":")) {
|
||||
qCWarning(dcWebServer) << "Invalid HTTP header:" << line;
|
||||
|
|
@ -70,6 +72,8 @@ HttpRequest::HttpRequest(QByteArray rawData) :
|
|||
m_rawHeaderList.insert(key, value);
|
||||
}
|
||||
|
||||
// TODO: check content size
|
||||
|
||||
m_valid = true;
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +87,7 @@ QHash<QByteArray, QByteArray> HttpRequest::rawHeaderList() const
|
|||
return m_rawHeaderList;
|
||||
}
|
||||
|
||||
QByteArray HttpRequest::method() const
|
||||
HttpRequest::RequestMethod HttpRequest::method() const
|
||||
{
|
||||
return m_method;
|
||||
}
|
||||
|
|
@ -113,6 +117,21 @@ bool HttpRequest::hasPayload() const
|
|||
return !m_payload.isEmpty();
|
||||
}
|
||||
|
||||
HttpRequest::RequestMethod HttpRequest::getRequestMethodType(const QString &methodString)
|
||||
{
|
||||
if (methodString == "GET") {
|
||||
return RequestMethod::Get;
|
||||
} else if (methodString == "POST") {
|
||||
return RequestMethod::Post;
|
||||
} else if (methodString == "PUT") {
|
||||
return RequestMethod::Put;
|
||||
} else if (methodString == "DELETE") {
|
||||
return RequestMethod::Delete;
|
||||
}
|
||||
qCWarning(dcWebServer) << "Method" << methodString << "will not be handled.";
|
||||
return RequestMethod::Unhandled;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug debug, const HttpRequest &httpRequest)
|
||||
{
|
||||
debug << "===================================" << "\n";
|
||||
|
|
|
|||
|
|
@ -29,12 +29,20 @@
|
|||
class HttpRequest
|
||||
{
|
||||
public:
|
||||
enum RequestMethod {
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Unhandled
|
||||
};
|
||||
|
||||
explicit HttpRequest(QByteArray rawData);
|
||||
|
||||
QByteArray rawHeader() const;
|
||||
QHash<QByteArray, QByteArray> rawHeaderList() const;
|
||||
|
||||
QByteArray method() const;
|
||||
RequestMethod method() const;
|
||||
QByteArray httpVersion() const;
|
||||
QUrlQuery urlQuery() const;
|
||||
|
||||
|
|
@ -48,13 +56,15 @@ private:
|
|||
QByteArray m_rawHeader;
|
||||
QHash<QByteArray, QByteArray> m_rawHeaderList;
|
||||
|
||||
QByteArray m_method;
|
||||
RequestMethod m_method;
|
||||
QByteArray m_httpVersion;
|
||||
QUrlQuery m_urlQuery;
|
||||
|
||||
QByteArray m_payload;
|
||||
|
||||
bool m_valid;
|
||||
|
||||
RequestMethod getRequestMethodType(const QString &methodString);
|
||||
};
|
||||
|
||||
QDebug operator<< (QDebug debug, const HttpRequest &httpRequest);
|
||||
|
|
|
|||
|
|
@ -262,29 +262,16 @@ QString DeviceHandler::name() const
|
|||
JsonReply* DeviceHandler::GetSupportedVendors(const QVariantMap ¶ms) const
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
|
||||
QVariantMap returns;
|
||||
QVariantList supportedVendors;
|
||||
foreach (const Vendor &vendor, GuhCore::instance()->supportedVendors()) {
|
||||
supportedVendors.append(JsonTypes::packVendor(vendor));
|
||||
}
|
||||
returns.insert("vendors", supportedVendors);
|
||||
returns.insert("vendors", JsonTypes::packSupportedVendors());
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
JsonReply* DeviceHandler::GetSupportedDevices(const QVariantMap ¶ms) const
|
||||
{
|
||||
QVariantMap returns;
|
||||
QVariantList supportedDeviceList;
|
||||
QList<DeviceClass> supportedDevices;
|
||||
if (params.contains("vendorId")) {
|
||||
supportedDevices = GuhCore::instance()->supportedDevices(VendorId(params.value("vendorId").toString()));
|
||||
} else {
|
||||
supportedDevices = GuhCore::instance()->supportedDevices();
|
||||
}
|
||||
foreach (const DeviceClass &deviceClass, supportedDevices) {
|
||||
supportedDeviceList.append(JsonTypes::packDeviceClass(deviceClass));
|
||||
}
|
||||
returns.insert("deviceClasses", supportedDeviceList);
|
||||
returns.insert("deviceClasses", JsonTypes::packSupportedDevices(VendorId(params.value("vendorId").toString())));
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
|
|
@ -309,20 +296,9 @@ JsonReply *DeviceHandler::GetDiscoveredDevices(const QVariantMap ¶ms) const
|
|||
JsonReply* DeviceHandler::GetPlugins(const QVariantMap ¶ms) const
|
||||
{
|
||||
Q_UNUSED(params)
|
||||
|
||||
QVariantMap returns;
|
||||
QVariantList plugins;
|
||||
foreach (DevicePlugin *plugin, GuhCore::instance()->plugins()) {
|
||||
QVariantMap pluginMap;
|
||||
pluginMap.insert("id", plugin->pluginId());
|
||||
pluginMap.insert("name", plugin->pluginName());
|
||||
QVariantList params;
|
||||
foreach (const ParamType ¶m, plugin->configurationDescription()) {
|
||||
params.append(JsonTypes::packParamType(param));
|
||||
}
|
||||
pluginMap.insert("params", params);
|
||||
plugins.append(pluginMap);
|
||||
}
|
||||
returns.insert("plugins", plugins);
|
||||
returns.insert("plugins", JsonTypes::packPlugins());
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
|
|
@ -531,14 +507,14 @@ JsonReply* DeviceHandler::GetStateValue(const QVariantMap ¶ms) const
|
|||
returns.insert("deviceError", JsonTypes::deviceErrorToString(DeviceManager::DeviceErrorDeviceNotFound));
|
||||
return createReply(returns);
|
||||
}
|
||||
if (!device->hasState(StateTypeId(params.value("stateTypeId").toString()))) {
|
||||
StateTypeId stateTypeId = StateTypeId(params.value("stateTypeId").toString());
|
||||
if (!device->hasState(stateTypeId)) {
|
||||
returns.insert("deviceError", JsonTypes::deviceErrorToString(DeviceManager::DeviceErrorStateTypeNotFound));
|
||||
return createReply(returns);
|
||||
}
|
||||
QVariant stateValue = device->stateValue(StateTypeId(params.value("stateTypeId").toString()));
|
||||
|
||||
returns.insert("deviceError", JsonTypes::deviceErrorToString(DeviceManager::DeviceErrorNoError));
|
||||
returns.insert("value", stateValue);
|
||||
returns.insert("value", device->state(stateTypeId).value());
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
|
|
@ -552,20 +528,8 @@ JsonReply *DeviceHandler::GetStateValues(const QVariantMap ¶ms) const
|
|||
return createReply(returns);
|
||||
}
|
||||
|
||||
DeviceClass deviceClass = GuhCore::instance()->findDeviceClass(device->deviceClassId());
|
||||
if (!deviceClass.isValid()) {
|
||||
returns.insert("deviceError", JsonTypes::deviceErrorToString(DeviceManager::DeviceErrorDeviceClassNotFound));
|
||||
return createReply(returns);
|
||||
}
|
||||
QVariantList values;
|
||||
foreach (const StateType &stateType, deviceClass.stateTypes()) {
|
||||
QVariantMap stateValue;
|
||||
stateValue.insert("stateTypeId", stateType.id().toString());
|
||||
stateValue.insert("value", device->stateValue(stateType.id()));
|
||||
values.append(stateValue);
|
||||
}
|
||||
returns.insert("deviceError", JsonTypes::deviceErrorToString(DeviceManager::DeviceErrorNoError));
|
||||
returns.insert("values", values);
|
||||
returns.insert("values", JsonTypes::packDeviceStates(device));
|
||||
return createReply(returns);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "plugin/device.h"
|
||||
#include "devicemanager.h"
|
||||
#include "guhcore.h"
|
||||
#include "ruleengine.h"
|
||||
#include "loggingcategories.h"
|
||||
|
||||
|
|
@ -387,6 +388,14 @@ QVariantMap JsonTypes::packRuleActionParam(const RuleActionParam &ruleActionPara
|
|||
return variantMap;
|
||||
}
|
||||
|
||||
QVariantMap JsonTypes::packState(const State &state)
|
||||
{
|
||||
QVariantMap stateMap;
|
||||
stateMap.insert("stateTypeId", state.stateTypeId().toString());
|
||||
stateMap.insert("value", state.value());
|
||||
return stateMap;
|
||||
}
|
||||
|
||||
QVariantMap JsonTypes::packStateType(const StateType &stateType)
|
||||
{
|
||||
QVariantMap variantMap;
|
||||
|
|
@ -526,9 +535,17 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass)
|
|||
|
||||
QVariantMap JsonTypes::packPlugin(DevicePlugin *plugin)
|
||||
{
|
||||
Q_UNUSED(plugin)
|
||||
qCWarning(dcDeviceManager) << "packPlugin not implemented yet!";
|
||||
return QVariantMap();
|
||||
QVariantMap pluginMap;
|
||||
pluginMap.insert("id", plugin->pluginId());
|
||||
pluginMap.insert("name", plugin->pluginName());
|
||||
|
||||
QVariantList params;
|
||||
foreach (const ParamType ¶m, plugin->configurationDescription()) {
|
||||
params.append(packParamType(param));
|
||||
}
|
||||
pluginMap.insert("params", params);
|
||||
|
||||
return pluginMap;
|
||||
}
|
||||
|
||||
QVariantMap JsonTypes::packDevice(Device *device)
|
||||
|
|
@ -656,6 +673,56 @@ QVariantList JsonTypes::packCreateMethods(DeviceClass::CreateMethods createMetho
|
|||
return ret;
|
||||
}
|
||||
|
||||
QVariantList JsonTypes::packSupportedVendors()
|
||||
{
|
||||
QVariantList supportedVendors;
|
||||
foreach (const Vendor &vendor, GuhCore::instance()->supportedVendors()) {
|
||||
supportedVendors.append(packVendor(vendor));
|
||||
}
|
||||
return supportedVendors;
|
||||
}
|
||||
|
||||
QVariantList JsonTypes::packSupportedDevices(const VendorId &vendorId)
|
||||
{
|
||||
QVariantList supportedDeviceList;
|
||||
foreach (const DeviceClass &deviceClass, GuhCore::instance()->supportedDevices(vendorId)) {
|
||||
supportedDeviceList.append(packDeviceClass(deviceClass));
|
||||
}
|
||||
return supportedDeviceList;
|
||||
}
|
||||
|
||||
QVariantList JsonTypes::packConfiguredDevices()
|
||||
{
|
||||
QVariantList configuredDeviceList;
|
||||
foreach (Device *device, GuhCore::instance()->configuredDevices()) {
|
||||
configuredDeviceList.append(packDevice(device));
|
||||
}
|
||||
return configuredDeviceList;
|
||||
}
|
||||
|
||||
QVariantList JsonTypes::packDeviceStates(Device *device)
|
||||
{
|
||||
DeviceClass deviceClass = GuhCore::instance()->findDeviceClass(device->deviceClassId());
|
||||
QVariantList stateValues;
|
||||
foreach (const StateType &stateType, deviceClass.stateTypes()) {
|
||||
QVariantMap stateValue;
|
||||
stateValue.insert("stateTypeId", stateType.id().toString());
|
||||
stateValue.insert("value", device->stateValue(stateType.id()));
|
||||
stateValues.append(stateValue);
|
||||
}
|
||||
return stateValues;
|
||||
}
|
||||
|
||||
QVariantList JsonTypes::packPlugins()
|
||||
{
|
||||
QVariantList pluginsList;
|
||||
foreach (DevicePlugin *plugin, GuhCore::instance()->plugins()) {
|
||||
QVariantMap pluginMap = packPlugin(plugin);
|
||||
pluginsList.append(pluginMap);
|
||||
}
|
||||
return pluginsList;
|
||||
}
|
||||
|
||||
Param JsonTypes::unpackParam(const QVariantMap ¶mMap)
|
||||
{
|
||||
if (paramMap.keys().count() == 0) {
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@ public:
|
|||
DECLARE_OBJECT(ruleDescription, "RuleDescription")
|
||||
DECLARE_OBJECT(logEntry, "LogEntry")
|
||||
|
||||
// pack types
|
||||
static QVariantMap packEventType(const EventType &eventType);
|
||||
static QVariantMap packEvent(const Event &event);
|
||||
static QVariantMap packEventDescriptor(const EventDescriptor &event);
|
||||
|
|
@ -142,6 +143,7 @@ public:
|
|||
static QVariantMap packAction(const Action &action);
|
||||
static QVariantMap packRuleAction(const RuleAction &ruleAction);
|
||||
static QVariantMap packRuleActionParam(const RuleActionParam &ruleActionParam);
|
||||
static QVariantMap packState(const State &state);
|
||||
static QVariantMap packStateType(const StateType &stateType);
|
||||
static QVariantMap packStateDescriptor(const StateDescriptor &stateDescriptor);
|
||||
static QVariantMap packStateEvaluator(const StateEvaluator &stateEvaluator);
|
||||
|
|
@ -158,6 +160,16 @@ public:
|
|||
static QVariantMap packLogEntry(const LogEntry &logEntry);
|
||||
static QVariantList packCreateMethods(DeviceClass::CreateMethods createMethods);
|
||||
|
||||
// pack resources
|
||||
static QVariantList packSupportedVendors();
|
||||
static QVariantList packSupportedDevices(const VendorId &vendorId);
|
||||
static QVariantList packConfiguredDevices();
|
||||
static QVariantList packDeviceStates(Device *device);
|
||||
|
||||
static QVariantList packPlugins();
|
||||
|
||||
|
||||
// unpack Types
|
||||
static Param unpackParam(const QVariantMap ¶mMap);
|
||||
static ParamList unpackParams(const QVariantList ¶mList);
|
||||
static RuleActionParam unpackRuleActionParam(const QVariantMap &ruleActionParamMap);
|
||||
|
|
@ -169,6 +181,7 @@ public:
|
|||
static StateDescriptor unpackStateDescriptor(const QVariantMap &stateDescriptorMap);
|
||||
static LogFilter unpackLogFilter(const QVariantMap &logFilterMap);
|
||||
|
||||
// validate
|
||||
static QPair<bool, QString> validateMap(const QVariantMap &templateMap, const QVariantMap &map);
|
||||
static QPair<bool, QString> validateProperty(const QVariant &templateValue, const QVariant &value);
|
||||
static QPair<bool, QString> validateList(const QVariantList &templateList, const QVariantList &list);
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ int main(int argc, char *argv[])
|
|||
s_loggingFilters.insert("TcpServer", true);
|
||||
s_loggingFilters.insert("WebServer", true);
|
||||
s_loggingFilters.insert("JsonRpc", false);
|
||||
s_loggingFilters.insert("Rest", true);
|
||||
s_loggingFilters.insert("Hardware", false);
|
||||
s_loggingFilters.insert("LogEngine", false);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,153 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "devicesresource.h"
|
||||
#include "jsontypes.h"
|
||||
#include "guhcore.h"
|
||||
#include "network/httpreply.h"
|
||||
#include "network/httprequest.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
DevicesResource::DevicesResource(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
HttpReply DevicesResource::proccessDeviceRequest(const HttpRequest &request, const QStringList &urlTokens)
|
||||
{
|
||||
DeviceId deviceId;
|
||||
StateTypeId stateTypeId;
|
||||
|
||||
Device *device = 0;
|
||||
|
||||
// first parse device, stateTypeId
|
||||
if (urlTokens.count() >= 4) {
|
||||
deviceId = DeviceId(urlTokens.at(3));
|
||||
if (deviceId.isNull()) {
|
||||
qCWarning(dcRest) << "Could not parse DeviceId:" << urlTokens.at(3);
|
||||
return HttpReply(HttpReply::BadRequest);
|
||||
}
|
||||
device = GuhCore::instance()->findConfiguredDevice(deviceId);
|
||||
if (!device) {
|
||||
qCWarning(dcRest) << "Could find any device with DeviceId:" << urlTokens.at(3);
|
||||
return HttpReply(HttpReply::NotFound);
|
||||
}
|
||||
|
||||
// /api/v1/devices/{deviceId}/states/{stateTypeId}
|
||||
if (urlTokens.count() >= 6 && urlTokens.at(4) == "states") {
|
||||
stateTypeId = StateTypeId(urlTokens.at(5));
|
||||
if (stateTypeId.isNull()) {
|
||||
qCWarning(dcRest) << "Could not parse StateTypeId:" << urlTokens.at(5);
|
||||
return HttpReply(HttpReply::BadRequest);
|
||||
}
|
||||
|
||||
if (!device->hasState(stateTypeId)){
|
||||
qCWarning(dcRest) << "This device has no StateTypeId:" << urlTokens.at(5);
|
||||
return HttpReply(HttpReply::NotFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check methods
|
||||
if (request.method() == HttpRequest::Get) {
|
||||
|
||||
// /api/v1/devices
|
||||
if (urlTokens.count() == 3)
|
||||
return getConfiguredDevices();
|
||||
|
||||
// /api/v1/devices/{deviceId}
|
||||
if (urlTokens.count() == 4)
|
||||
return getConfiguredDevice(device);
|
||||
|
||||
// /api/v1/devices/{deviceId}/states
|
||||
if (urlTokens.count() == 5 && urlTokens.at(4) == "states")
|
||||
return getDeviceStateValues(device);
|
||||
|
||||
// /api/v1/devices/{deviceId}/states/{stateTypeId}
|
||||
if (urlTokens.count() == 6 && urlTokens.at(4) == "states")
|
||||
return getDeviceStateValue(device, stateTypeId);
|
||||
} else if (request.method() == HttpRequest::Delete) {
|
||||
|
||||
// /api/v1/devices
|
||||
if (urlTokens.count() == 3)
|
||||
return HttpReply(HttpReply::BadRequest);
|
||||
|
||||
if (urlTokens.count() == 4)
|
||||
return removeDevice(device);
|
||||
|
||||
}
|
||||
|
||||
return HttpReply(HttpReply::BadRequest);
|
||||
}
|
||||
|
||||
HttpReply DevicesResource::getConfiguredDevices()
|
||||
{
|
||||
HttpReply httpReply(HttpReply::Ok);
|
||||
httpReply.setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
|
||||
httpReply.setPayload(QJsonDocument::fromVariant(JsonTypes::packConfiguredDevices()).toJson());
|
||||
httpReply.packReply();
|
||||
return httpReply;
|
||||
}
|
||||
|
||||
HttpReply DevicesResource::getConfiguredDevice(Device *device)
|
||||
{
|
||||
HttpReply httpReply(HttpReply::Ok);
|
||||
httpReply.setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
|
||||
httpReply.setPayload(QJsonDocument::fromVariant(JsonTypes::packDevice(device)).toJson());
|
||||
httpReply.packReply();
|
||||
return httpReply;
|
||||
}
|
||||
|
||||
HttpReply DevicesResource::getDeviceStateValues(Device *device)
|
||||
{
|
||||
HttpReply httpReply(HttpReply::Ok);
|
||||
httpReply.setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
|
||||
httpReply.setPayload(QJsonDocument::fromVariant(JsonTypes::packDeviceStates(device)).toJson());
|
||||
httpReply.packReply();
|
||||
return httpReply;
|
||||
}
|
||||
|
||||
HttpReply DevicesResource::getDeviceStateValue(Device *device, const StateTypeId &stateTypeId)
|
||||
{
|
||||
HttpReply httpReply(HttpReply::Ok);
|
||||
httpReply.setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
|
||||
QVariantMap stateValue;
|
||||
stateValue.insert("value", device->state(stateTypeId).value());
|
||||
httpReply.setPayload(QJsonDocument::fromVariant(stateValue).toJson());
|
||||
httpReply.packReply();
|
||||
return httpReply;
|
||||
}
|
||||
|
||||
HttpReply DevicesResource::removeDevice(Device *device)
|
||||
{
|
||||
DeviceManager::DeviceError result = GuhCore::instance()->removeConfiguredDevice(device->id(), QHash<RuleId, RuleEngine::RemovePolicy>());
|
||||
|
||||
// TODO: parse removepolicy query params
|
||||
|
||||
if (result == DeviceManager::DeviceErrorNoError)
|
||||
return HttpReply(HttpReply::Ok);
|
||||
|
||||
return HttpReply(HttpReply::Forbidden);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef DEVICESRESOURCE_H
|
||||
#define DEVICESRESOURCE_H
|
||||
|
||||
#include <QObject>
|
||||
#include "jsontypes.h"
|
||||
|
||||
class HttpReply;
|
||||
class HttpRequest;
|
||||
|
||||
namespace guhserver {
|
||||
|
||||
class DevicesResource : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit DevicesResource(QObject *parent = 0);
|
||||
|
||||
HttpReply proccessDeviceRequest(const HttpRequest &request, const QStringList &urlTokens);
|
||||
|
||||
private:
|
||||
HttpReply getConfiguredDevices();
|
||||
HttpReply getConfiguredDevice(Device *device);
|
||||
HttpReply getDeviceStateValues(Device *device);
|
||||
HttpReply getDeviceStateValue(Device *device, const StateTypeId &stateTypeId);
|
||||
|
||||
HttpReply removeDevice(Device *device);
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // DEVICESRESOURCE_H
|
||||
|
|
@ -24,13 +24,6 @@
|
|||
#include "network/httpreply.h"
|
||||
#include "guhcore.h"
|
||||
|
||||
#include "devicehandler.h"
|
||||
#include "actionhandler.h"
|
||||
#include "ruleshandler.h"
|
||||
#include "eventhandler.h"
|
||||
#include "logginghandler.h"
|
||||
#include "statehandler.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
|
||||
namespace guhserver {
|
||||
|
|
@ -43,6 +36,10 @@ RestServer::RestServer(QObject *parent) :
|
|||
connect(m_webserver, &WebServer::clientDisconnected, this, &RestServer::clientDisconnected);
|
||||
connect(m_webserver, &WebServer::httpRequestReady, this, &RestServer::processHttpRequest);
|
||||
|
||||
// Resources
|
||||
m_deviceResource = new DevicesResource(this);
|
||||
|
||||
|
||||
m_webserver->startServer();
|
||||
}
|
||||
|
||||
|
|
@ -58,57 +55,87 @@ void RestServer::clientDisconnected(const QUuid &clientId)
|
|||
|
||||
void RestServer::processHttpRequest(const QUuid &clientId, const HttpRequest &request)
|
||||
{
|
||||
qCDebug(dcWebServer) << "process http request" << clientId << request.method() << request.urlQuery().query();
|
||||
qCDebug(dcRest) << "Process HTTP request" << clientId << request.method() << request.urlQuery().query();
|
||||
|
||||
QString targetNamespace;
|
||||
QString method;
|
||||
QVariantMap params;
|
||||
QStringList urlTokens = request.urlQuery().query(QUrl::FullyDecoded).split("/");
|
||||
urlTokens.removeAll(QString());
|
||||
|
||||
if (request.urlQuery().hasQueryItem("devices")) {
|
||||
qCDebug(dcWebServer) << "devices resource";
|
||||
qCDebug(dcRest) << urlTokens;
|
||||
|
||||
if (urlTokens.count() < 3) {
|
||||
m_webserver->sendHttpReply(clientId, HttpReply(HttpReply::BadRequest));
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.method() == "GET" && request.urlQuery().query() == "/api/v1/devices.json") {
|
||||
targetNamespace = "Devices";
|
||||
method = "GetConfiguredDevices";
|
||||
} else if (request.method() == "GET" && request.urlQuery().query() == "/api/v1/devices.json") {
|
||||
targetNamespace = "Devices";
|
||||
method = "GetConfiguredDevices";
|
||||
} else {
|
||||
HttpReply httpReply(HttpReply::BadRequest);
|
||||
httpReply.setPayload("400 Bad Request.");
|
||||
httpReply.packReply();
|
||||
if (urlTokens.at(2) == "devices") {
|
||||
HttpReply httpReply = m_deviceResource->proccessDeviceRequest(request, urlTokens);
|
||||
qCDebug(dcRest) << "sending header" << httpReply.rawHeader();
|
||||
m_webserver->sendHttpReply(clientId, httpReply);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonHandler *handler = GuhCore::instance()->jsonRPCServer()->handlers().value(targetNamespace);
|
||||
|
||||
QPair<bool, QString> validationResult = handler->validateParams(method, params);
|
||||
if (!validationResult.first) {
|
||||
qCWarning(dcWebServer) << "Invalid params: " << validationResult.second;
|
||||
return;
|
||||
}
|
||||
// QString targetNamespace;
|
||||
// QString method;
|
||||
// QVariantMap params;
|
||||
|
||||
JsonReply *jsonReply;
|
||||
QMetaObject::invokeMethod(handler, method.toLatin1().data(), Q_RETURN_ARG(JsonReply*, jsonReply), Q_ARG(QVariantMap, params));
|
||||
if (jsonReply->type() == JsonReply::TypeAsync) {
|
||||
jsonReply->setClientId(clientId);
|
||||
connect(jsonReply, &JsonReply::finished, this, &RestServer::asyncReplyFinished);
|
||||
jsonReply->startWait();
|
||||
m_asyncReplies.insert(clientId, jsonReply);
|
||||
return;
|
||||
}
|
||||
// // check filter
|
||||
// QVariantList deviceList;
|
||||
// if (!request.urlQuery().hasQueryItem("id")) {
|
||||
// HttpReply httpReply = m_deviceResource->proccessDeviceRequest(request);
|
||||
// m_webserver->sendHttpReply(clientId, httpReply);
|
||||
// return;
|
||||
// } else {
|
||||
// foreach (const QString& idString, request.urlQuery().allQueryItemValues("id")) {
|
||||
// Device *device = GuhCore::instance()->deviceManager()->findConfiguredDevice(DeviceId(idString));
|
||||
// if (device == Device()) {
|
||||
|
||||
HttpReply httpReply(HttpReply::Ok);
|
||||
httpReply.setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
|
||||
httpReply.setPayload(QJsonDocument::fromVariant(jsonReply->data()).toJson());
|
||||
httpReply.packReply();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
m_webserver->sendHttpReply(clientId, httpReply);
|
||||
|
||||
jsonReply->deleteLater();
|
||||
// if (request.method() == HttpRequest::Get && request.urlQuery().query() == "/api/v1/devices.json") {
|
||||
// targetNamespace = "Devices";
|
||||
// method = "GetConfiguredDevices";
|
||||
// } else if (request.method() == HttpRequest::Get && request.urlQuery().query() == "/api/v1/devices.json") {
|
||||
// targetNamespace = "Devices";
|
||||
// method = "GetConfiguredDevices";
|
||||
// } else {
|
||||
// HttpReply httpReply(HttpReply::BadRequest);
|
||||
// httpReply.setPayload("400 Bad Request.");
|
||||
// httpReply.packReply();
|
||||
// m_webserver->sendHttpReply(clientId, httpReply);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// JsonHandler *handler = GuhCore::instance()->jsonRPCServer()->handlers().value(targetNamespace);
|
||||
|
||||
// QPair<bool, QString> validationResult = handler->validateParams(method, params);
|
||||
// if (!validationResult.first) {
|
||||
// qCWarning(dcWebServer) << "Invalid params: " << validationResult.second;
|
||||
// return;
|
||||
// }
|
||||
|
||||
// JsonReply *jsonReply;
|
||||
// QMetaObject::invokeMethod(handler, method.toLatin1().data(), Q_RETURN_ARG(JsonReply*, jsonReply), Q_ARG(QVariantMap, params));
|
||||
// if (jsonReply->type() == JsonReply::TypeAsync) {
|
||||
// jsonReply->setClientId(clientId);
|
||||
// connect(jsonReply, &JsonReply::finished, this, &RestServer::asyncReplyFinished);
|
||||
// jsonReply->startWait();
|
||||
// m_asyncReplies.insert(clientId, jsonReply);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// HttpReply httpReply(HttpReply::Ok);
|
||||
// httpReply.setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
|
||||
// httpReply.setPayload(QJsonDocument::fromVariant(jsonReply->data()).toJson());
|
||||
// httpReply.packReply();
|
||||
|
||||
// m_webserver->sendHttpReply(clientId, httpReply);
|
||||
|
||||
// jsonReply->deleteLater();
|
||||
}
|
||||
|
||||
void RestServer::asyncReplyFinished()
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
#include "webserver.h"
|
||||
#include "jsonhandler.h"
|
||||
#include "devicesresource.h"
|
||||
|
||||
class HttpRequest;
|
||||
class HttpReply;
|
||||
|
|
@ -42,6 +43,8 @@ private:
|
|||
QList<QUuid> m_clientList;
|
||||
QHash<QUuid, JsonReply *> m_asyncReplies;
|
||||
|
||||
DevicesResource *m_deviceResource;
|
||||
|
||||
signals:
|
||||
void httpReplyReady(const HttpReply &httpReply);
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ SOURCES += $$top_srcdir/server/guhcore.cpp \
|
|||
$$top_srcdir/server/transportinterface.cpp \
|
||||
$$top_srcdir/server/servermanager.cpp \
|
||||
$$top_srcdir/server/websocketserver.cpp \
|
||||
$$top_srcdir/server/rest/restserver.cpp
|
||||
$$top_srcdir/server/rest/restserver.cpp \
|
||||
$$top_srcdir/server/rest/devicesresource.cpp \
|
||||
|
||||
|
||||
HEADERS += $$top_srcdir/server/guhcore.h \
|
||||
|
|
@ -51,5 +52,6 @@ HEADERS += $$top_srcdir/server/guhcore.h \
|
|||
$$top_srcdir/server/servermanager.h \
|
||||
$$top_srcdir/server/websocketserver.h \
|
||||
$$top_srcdir/server/rest/restserver.h \
|
||||
$$top_srcdir/server/rest/devicesresource.h \
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ WebServer::WebServer(QObject *parent) :
|
|||
{
|
||||
// load webserver settings
|
||||
GuhSettings settings(GuhSettings::SettingsRoleGlobal);
|
||||
qCDebug(dcTcpServer) << "Loading Webserver settings from:" << settings.fileName();
|
||||
qCDebug(dcTcpServer) << "Loading webserver settings from:" << settings.fileName();
|
||||
|
||||
settings.beginGroup("Webserver");
|
||||
m_port = settings.value("port", 3000).toInt();
|
||||
|
|
@ -140,21 +140,6 @@ QString WebServer::fileName(const QString &query)
|
|||
return QFileInfo(m_webinterfaceDir.path() + fileName).canonicalFilePath();
|
||||
}
|
||||
|
||||
|
||||
WebServer::RequestMethod WebServer::getRequestMethodType(const QString &methodString)
|
||||
{
|
||||
if (methodString == "GET") {
|
||||
return RequestMethod::Get;
|
||||
} else if (methodString == "POST") {
|
||||
return RequestMethod::Post;
|
||||
} else if (methodString == "PUT") {
|
||||
return RequestMethod::Put;
|
||||
} else if (methodString == "DELETE") {
|
||||
return RequestMethod::Delete;
|
||||
}
|
||||
return RequestMethod::Unhandled;
|
||||
}
|
||||
|
||||
void WebServer::writeData(QTcpSocket *socket, const QByteArray &data)
|
||||
{
|
||||
QTextStream os(socket);
|
||||
|
|
@ -195,10 +180,10 @@ void WebServer::readClient()
|
|||
return;
|
||||
}
|
||||
|
||||
// read http request
|
||||
// read HTTP request
|
||||
HttpRequest request = HttpRequest(socket->readAll());
|
||||
if (!request.isValid()) {
|
||||
qCWarning(dcWebServer) << "Invalid request.";
|
||||
qCWarning(dcWebServer) << "Got invalid request.";
|
||||
HttpReply reply(HttpReply::BadRequest);
|
||||
reply.setPayload("400 Bad Request.");
|
||||
reply.packReply();
|
||||
|
|
@ -220,9 +205,7 @@ void WebServer::readClient()
|
|||
qCDebug(dcWebServer) << request;
|
||||
|
||||
// verify method
|
||||
RequestMethod requestMethod = getRequestMethodType(request.method());
|
||||
if (requestMethod == RequestMethod::Unhandled) {
|
||||
qCWarning(dcWebServer) << "method" << request.method() << "not allowed";
|
||||
if (request.method() == HttpRequest::Unhandled) {
|
||||
HttpReply reply(HttpReply::MethodNotAllowed);
|
||||
reply.setHeader(HttpReply::AllowHeader, "GET, PUT, POST, DELETE");
|
||||
reply.setPayload("405 Method not allowed.");
|
||||
|
|
@ -238,7 +221,7 @@ void WebServer::readClient()
|
|||
}
|
||||
|
||||
// request for a file...
|
||||
if (requestMethod == RequestMethod::Get) {
|
||||
if (request.method() == HttpRequest::Get) {
|
||||
if (!verifyFile(socket, fileName(request.urlQuery().query())))
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -43,13 +43,7 @@ class WebServer : public TransportInterface
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum RequestMethod {
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Unhandled
|
||||
};
|
||||
|
||||
|
||||
explicit WebServer(QObject *parent = 0);
|
||||
~WebServer();
|
||||
|
|
@ -70,7 +64,6 @@ private:
|
|||
bool verifyFile(QTcpSocket *socket, const QString &fileName);
|
||||
|
||||
QString fileName(const QString &query);
|
||||
RequestMethod getRequestMethodType(const QString &methodString);
|
||||
|
||||
void writeData(QTcpSocket *socket, const QByteArray &data);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
TEMPLATE=subdirs
|
||||
SUBDIRS=versioning devices jsonrpc events states actions rules plugins webserver
|
||||
SUBDIRS=versioning devices jsonrpc events states actions rules plugins webserver restdevices
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
include(../../../guh.pri)
|
||||
include(../autotests.pri)
|
||||
|
||||
TARGET = restdevices
|
||||
SOURCES += testrestdevices.cpp
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* *
|
||||
* Copyright (C) 2015 Simon Stuerz <simon.stuerz@guh.guru> *
|
||||
* *
|
||||
* 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/>. *
|
||||
* *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "guhtestbase.h"
|
||||
#include "guhcore.h"
|
||||
#include "devicemanager.h"
|
||||
#include "mocktcpserver.h"
|
||||
#include "webserver.h"
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <QCoreApplication>
|
||||
#include <QTcpSocket>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkRequest>
|
||||
#include <QNetworkReply>
|
||||
#include <QCoreApplication>
|
||||
#include <QJsonDocument>
|
||||
#include <QMetaType>
|
||||
|
||||
using namespace guhserver;
|
||||
|
||||
class TestRestDevices: public GuhTestBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void getConfiguredDevices();
|
||||
|
||||
|
||||
private:
|
||||
// for debugging
|
||||
void printResponse(QNetworkReply *reply);
|
||||
|
||||
};
|
||||
|
||||
void TestRestDevices::getConfiguredDevices()
|
||||
{
|
||||
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
|
||||
QSignalSpy clientSpy(nam, SIGNAL(finished(QNetworkReply*)));
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setUrl(QUrl("http://localhost:3000/api/v1/devices"));
|
||||
QNetworkReply *reply;
|
||||
|
||||
reply = nam->get(request);
|
||||
clientSpy.wait(200);
|
||||
QVERIFY2(clientSpy.count() == 1, "expected exactly 1 response from webserver");
|
||||
|
||||
QJsonParseError error;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &error);
|
||||
QCOMPARE(error.error, QJsonParseError::NoError);
|
||||
QVariantList deviceList = jsonDoc.toVariant().toList();
|
||||
QCOMPARE(deviceList.count(), 3);
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void TestRestDevices::printResponse(QNetworkReply *reply)
|
||||
{
|
||||
qDebug() << "-------------------------------";
|
||||
qDebug() << reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
|
||||
foreach (const QNetworkReply::RawHeaderPair &headerPair, reply->rawHeaderPairs()) {
|
||||
qDebug() << headerPair.first << ":" << headerPair.second;
|
||||
}
|
||||
qDebug() << "-------------------------------";
|
||||
qDebug() << reply->readAll();
|
||||
qDebug() << "-------------------------------";
|
||||
}
|
||||
|
||||
#include "testrestdevices.moc"
|
||||
QTEST_MAIN(TestRestDevices)
|
||||
|
|
@ -40,7 +40,6 @@ class TestWebserver: public GuhTestBase
|
|||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void pingServer();
|
||||
void httpVersion();
|
||||
|
||||
void checkAllowedMethodCall_data();
|
||||
|
|
@ -55,11 +54,6 @@ private:
|
|||
|
||||
};
|
||||
|
||||
void TestWebserver::pingServer()
|
||||
{
|
||||
// TODO: when QWebsocket will be used
|
||||
}
|
||||
|
||||
void TestWebserver::httpVersion()
|
||||
{
|
||||
QTcpSocket *socket = new QTcpSocket(this);
|
||||
|
|
@ -156,8 +150,6 @@ void TestWebserver::getFiles_data()
|
|||
QTest::newRow("get /etc/guh/guhd.conf") << "/etc/guh/guhd.conf" << 404;
|
||||
QTest::newRow("get /etc/sudoers") << "/etc/sudoers" << 404;
|
||||
QTest::newRow("get /root/.ssh/id_rsa.pub") << "/root/.ssh/id_rsa.pub" << 404;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void TestWebserver::getFiles()
|
||||
|
|
|
|||
Loading…
Reference in New Issue