Drop old REST server

This commit is contained in:
Michael Zanetti 2019-10-20 01:23:28 +02:00
parent 977247e950
commit 03b8be22d6
40 changed files with 87 additions and 5725 deletions

View File

@ -21,7 +21,6 @@
#include "nymeacore.h"
#include "servers/httprequest.h"
#include "servers/httpreply.h"
#include "servers/rest/restresource.h"
#include "nymeasettings.h"
#include "loggingcategories.h"
#include "debugserverhandler.h"
@ -60,7 +59,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (requestPath == "/debug" || requestPath == "/debug/") {
qCDebug(dcDebugServer()) << "Create debug interface page";
// Fallback default debug page
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createDebugXmlDocument());
return reply;
@ -72,7 +71,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QFile logDatabaseFile(NymeaCore::instance()->configuration()->logDBName());
if (!logDatabaseFile.exists()) {
qCWarning(dcDebugServer()) << "Could not read log database file for debug download" << NymeaCore::instance()->configuration()->logDBName() << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
//: The HTTP error message of the debug interface. The %1 represents the file name.
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(logDatabaseFile.fileName())));
@ -81,7 +80,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (!logDatabaseFile.open(QFile::ReadOnly)) {
qCWarning(dcDebugServer()) << "Could not read log database file for debug download" << NymeaCore::instance()->configuration()->logDBName();
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
//: The HTTP error message of the debug interface. The %1 represents the file name.
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(logDatabaseFile.fileName())));
@ -91,7 +90,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QByteArray logDatabaseRawData = logDatabaseFile.readAll();
logDatabaseFile.close();
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/sql");
reply->setPayload(logDatabaseRawData);
return reply;
@ -105,7 +104,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QFile syslogFile(syslogFileName);
if (!syslogFile.exists()) {
qCWarning(dcDebugServer()) << "Could not read log database file for debug download" << syslogFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(syslogFileName)));
return reply;
@ -113,7 +112,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (!syslogFile.open(QFile::ReadOnly)) {
qCWarning(dcDebugServer()) << "Could not read syslog file for debug download" << syslogFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(syslogFileName)));
return reply;
@ -122,7 +121,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QByteArray syslogFileData = syslogFile.readAll();
syslogFile.close();
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(syslogFileData);
return reply;
@ -136,7 +135,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
return reply;
@ -144,7 +143,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (!settingsFile.open(QFile::ReadOnly)) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
return reply;
@ -153,7 +152,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QByteArray settingsFileData = settingsFile.readAll();
settingsFile.close();
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(settingsFileData);
return reply;
@ -165,7 +164,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
return reply;
@ -173,7 +172,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (!settingsFile.open(QFile::ReadOnly)) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
return reply;
@ -182,7 +181,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QByteArray settingsFileData = settingsFile.readAll();
settingsFile.close();
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(settingsFileData);
return reply;
@ -194,7 +193,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
return reply;
@ -202,7 +201,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (!settingsFile.open(QFile::ReadOnly)) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
return reply;
@ -211,7 +210,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QByteArray settingsFileData = settingsFile.readAll();
settingsFile.close();
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(settingsFileData);
return reply;
@ -223,7 +222,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
return reply;
@ -231,7 +230,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (!settingsFile.open(QFile::ReadOnly)) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
return reply;
@ -240,7 +239,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QByteArray settingsFileData = settingsFile.readAll();
settingsFile.close();
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(settingsFileData);
return reply;
@ -252,7 +251,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
return reply;
@ -260,7 +259,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (!settingsFile.open(QFile::ReadOnly)) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
return reply;
@ -269,7 +268,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QByteArray settingsFileData = settingsFile.readAll();
settingsFile.close();
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(settingsFileData);
return reply;
@ -281,7 +280,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
return reply;
@ -289,7 +288,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (!settingsFile.open(QFile::ReadOnly)) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
return reply;
@ -298,7 +297,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QByteArray settingsFileData = settingsFile.readAll();
settingsFile.close();
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(settingsFileData);
return reply;
@ -311,7 +310,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QFile settingsFile(settingsFileName);
if (!settingsFile.exists()) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName << "file does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not find file \"%1\".").arg(settingsFileName)));
return reply;
@ -319,7 +318,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (!settingsFile.open(QFile::ReadOnly)) {
qCWarning(dcDebugServer()) << "Could not read file for debug download" << settingsFileName;
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::Forbidden);
reply->setHeader(HttpReply::ContentTypeHeader, "text/html");
reply->setPayload(createErrorXmlDocument(HttpReply::NotFound, tr("Could not open file \"%1\".").arg(settingsFileName)));
return reply;
@ -328,7 +327,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
QByteArray settingsFileData = settingsFile.readAll();
settingsFile.close();
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/plain");
reply->setPayload(settingsFileData);
return reply;
@ -337,7 +336,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (requestPath.startsWith("/debug/ping")) {
// Only one ping process should run
if (m_pingProcess || m_pingReply)
return RestResource::createErrorReply(HttpReply::InternalServerError);
return HttpReply::createErrorReply(HttpReply::InternalServerError);
qCDebug(dcDebugServer()) << "Start ping nymea.io process";
m_pingProcess = new QProcess(this);
@ -345,14 +344,14 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
connect(m_pingProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onPingProcessFinished(int,QProcess::ExitStatus)));
m_pingProcess->start("ping", { "-c", "4", "nymea.io" } );
m_pingReply = RestResource::createAsyncReply();
m_pingReply = HttpReply::createAsyncReply();
return m_pingReply;
}
if (requestPath.startsWith("/debug/dig")) {
// Only one dig process should run
if (m_digProcess || m_digReply)
return RestResource::createErrorReply(HttpReply::InternalServerError);
return HttpReply::createErrorReply(HttpReply::InternalServerError);
qCDebug(dcDebugServer()) << "Start dig nymea.io process";
m_digProcess = new QProcess(this);
@ -360,14 +359,14 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
connect(m_digProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onDigProcessFinished(int,QProcess::ExitStatus)));
m_digProcess->start("dig", { "nymea.io" } );
m_digReply = RestResource::createAsyncReply();
m_digReply = HttpReply::createAsyncReply();
return m_digReply;
}
if (requestPath.startsWith("/debug/tracepath")) {
// Only one tracepath process should run
if (m_tracePathProcess || m_tracePathReply)
return RestResource::createErrorReply(HttpReply::InternalServerError);
return HttpReply::createErrorReply(HttpReply::InternalServerError);
qCDebug(dcDebugServer()) << "Start tracepath nymea.io process";
m_tracePathProcess = new QProcess(this);
@ -375,7 +374,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
connect(m_tracePathProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(onTracePathProcessFinished(int,QProcess::ExitStatus)));
m_tracePathProcess->start("tracepath", { "nymea.io" } );
m_tracePathReply = RestResource::createAsyncReply();
m_tracePathReply = HttpReply::createAsyncReply();
return m_tracePathReply;
}
@ -401,7 +400,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
settings.endGroup();
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setPayload(QJsonDocument::fromVariant(dataMap).toJson(QJsonDocument::Indented));
return reply;
} else {
@ -429,7 +428,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
settings.endGroup();
QLoggingCategory::setFilterRules(loggingRules.join('\n'));
return RestResource::createSuccessReply();
return HttpReply::createSuccessReply();
}
}
@ -447,17 +446,17 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
if (!m_debugReportGenerator) {
qCWarning(dcDebugServer()) << "There is currently no debug report generator. The requested file does not exist.";
return RestResource::createErrorReply(HttpReply::NotFound);
return HttpReply::createErrorReply(HttpReply::NotFound);
}
if (m_debugReportGenerator->reportFileName() != fileName) {
qCWarning(dcDebugServer()) << "The requested file is not the file from the current debug report generator" << m_debugReportGenerator->reportFileName() << "!=" << fileName;
return RestResource::createErrorReply(HttpReply::NotFound);
return HttpReply::createErrorReply(HttpReply::NotFound);
}
// Everything looks good, send the requested debug report
HttpReply *downloadReportReply = RestResource::createSuccessReply();
HttpReply *downloadReportReply = HttpReply::createSuccessReply();
downloadReportReply->setPayload(m_debugReportGenerator->reportFileData());
downloadReportReply->setHeader(HttpReply::ContentTypeHeader, "application/tar+gzip;");
return downloadReportReply;
@ -470,13 +469,13 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
connect(m_debugReportGenerator, &DebugReportGenerator::timeout, this, &DebugServerHandler::onDebugReportGeneratorTimeout);
m_debugReportGenerator->generateReport();
// Note: no content will bring the client to poll this report
return RestResource::createErrorReply(HttpReply::NoContent);
return HttpReply::createErrorReply(HttpReply::NoContent);
} else {
// There is a running generator, check if the report is ready
if (!m_debugReportGenerator->isReady()) {
qCDebug(dcDebugServer()) << "Report is not ready yet";
// Note: no content tells the client the report is not ready yet
return RestResource::createErrorReply(HttpReply::NoContent);
return HttpReply::createErrorReply(HttpReply::NoContent);
} else {
if (m_debugReportGenerator->isValid()) {
// Success, the debug report is ready and valid
@ -485,7 +484,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
reportInformation.insert("fileSize", m_debugReportGenerator->reportFileData().size());
reportInformation.insert("md5sum", m_debugReportGenerator->md5Sum());
HttpReply * httpReply = RestResource::createSuccessReply();
HttpReply * httpReply = HttpReply::createSuccessReply();
httpReply->setHttpStatusCode(HttpReply::Ok);
httpReply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
httpReply->setPayload(QJsonDocument::fromVariant(reportInformation).toJson(QJsonDocument::Indented));
@ -494,7 +493,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
qCWarning(dcDebugServer()) << "The debug report generator finished with error.";
m_debugReportGenerator->deleteLater();
m_debugReportGenerator = nullptr;
return RestResource::createErrorReply(HttpReply::InternalServerError);
return HttpReply::createErrorReply(HttpReply::InternalServerError);
}
}
}
@ -508,7 +507,7 @@ HttpReply *DebugServerHandler::processDebugRequest(const QString &requestPath, c
// If nothing matches, redirect to /debug page
qCWarning(dcDebugServer()) << "Resource for debug interface not found. Redirecting to /debug";
HttpReply *reply = RestResource::createErrorReply(HttpReply::PermanentRedirect);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::PermanentRedirect);
reply->setHeader(HttpReply::LocationHeader, "/debug");
return reply;
}
@ -570,7 +569,7 @@ HttpReply *DebugServerHandler::processDebugFileRequest(const QString &requestPat
QByteArray data = loadResourceData(resourceFileName);
// Create reply for resource file
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setPayload(data);
// Check content type

View File

@ -32,14 +32,6 @@ HEADERS += nymeacore.h \
servers/httprequest.h \
servers/httpreply.h \
servers/bluetoothserver.h \
servers/rest/restserver.h \
servers/rest/restresource.h \
servers/rest/devicesresource.h \
servers/rest/deviceclassesresource.h \
servers/rest/vendorsresource.h \
servers/rest/logsresource.h \
servers/rest/pluginsresource.h \
servers/rest/rulesresource.h \
servers/websocketserver.h \
servers/mqttbroker.h \
jsonrpc/jsonrpcserver.h \
@ -119,14 +111,6 @@ SOURCES += nymeacore.cpp \
servers/httpreply.cpp \
servers/websocketserver.cpp \
servers/bluetoothserver.cpp \
servers/rest/restserver.cpp \
servers/rest/restresource.cpp \
servers/rest/devicesresource.cpp \
servers/rest/deviceclassesresource.cpp \
servers/rest/vendorsresource.cpp \
servers/rest/logsresource.cpp \
servers/rest/pluginsresource.cpp \
servers/rest/rulesresource.cpp \
servers/mqttbroker.cpp \
jsonrpc/jsonrpcserver.cpp \
jsonrpc/jsonhandler.cpp \

View File

@ -853,12 +853,6 @@ JsonRPCServer *NymeaCore::jsonRPCServer() const
return m_serverManager->jsonServer();
}
/*! Returns the pointer to the \l{RestServer} of this instance. */
RestServer *NymeaCore::restServer() const
{
return m_serverManager->restServer();
}
void NymeaCore::onDeviceDisappeared(const DeviceId &deviceId)
{
Device *device = m_deviceManager->findConfiguredDevice(deviceId);

View File

@ -82,7 +82,6 @@ public:
NymeaConfiguration *configuration() const;
LogEngine* logEngine() const;
JsonRPCServer *jsonRPCServer() const;
RestServer *restServer() const;
DeviceManager *deviceManager() const;
RuleEngine *ruleEngine() const;
TimeManager *timeManager() const;

View File

@ -43,7 +43,6 @@
#include "jsonrpc/jsonrpcserver.h"
#include "servers/mocktcpserver.h"
#include "servers/tcpserver.h"
#include "servers/rest/restserver.h"
#include "servers/websocketserver.h"
#include "servers/webserver.h"
#include "servers/bluetoothserver.h"
@ -101,8 +100,6 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
// Interfaces
m_jsonServer = new JsonRPCServer(m_sslConfiguration, this);
m_restServer = new RestServer(m_sslConfiguration, this);
// Transports
MockTcpServer *tcpServer = new MockTcpServer(this);
@ -134,7 +131,6 @@ ServerManager::ServerManager(Platform *platform, NymeaConfiguration *configurati
foreach (const WebServerConfiguration &config, configuration->webServerConfigurations()) {
WebServer *webServer = new WebServer(config, m_sslConfiguration, this);
m_restServer->registerWebserver(webServer);
m_webServers.insert(config.id, webServer);
if (webServer->startServer()) {
registerZeroConfService(config, "http", "_http._tcp");
@ -167,12 +163,6 @@ JsonRPCServer *ServerManager::jsonServer() const
return m_jsonServer;
}
/*! Returns the pointer to the created \l{RestServer} in this \l{ServerManager}. */
RestServer *ServerManager::restServer() const
{
return m_restServer;
}
/*! Returns the pointer to the created \l{BluetoothServer} in this \l{ServerManager}. */
BluetoothServer *ServerManager::bluetoothServer() const
{
@ -268,7 +258,6 @@ void ServerManager::webServerConfigurationChanged(const QString &id)
} else {
qDebug(dcServerManager()) << "Received a Web Server config change event but don't have a Web Server instance for it. Creating new WebServer instance on" << config.address.toString() << config.port << "(SSL:" << config.sslEnabled << ")";
server = new WebServer(config, m_sslConfiguration, this);
m_restServer->registerWebserver(server);
m_webServers.insert(config.id, server);
}
if (server->startServer()) {

View File

@ -39,7 +39,6 @@ class TcpServer;
class WebSocketServer;
class WebServer;
class BluetoothServer;
class RestServer;
class MqttBroker;
class MockTcpServer;
@ -52,7 +51,6 @@ public:
// Interfaces
JsonRPCServer *jsonServer() const;
RestServer *restServer() const;
BluetoothServer* bluetoothServer() const;
@ -81,7 +79,6 @@ private:
// Interfaces
JsonRPCServer *m_jsonServer;
RestServer *m_restServer;
BluetoothServer *m_bluetoothServer;
QHash<QString, TcpServer*> m_tcpServers;

View File

@ -188,6 +188,27 @@ HttpReply::HttpReply(const HttpReply::HttpStatusCode &statusCode, const HttpRepl
packReply();
}
HttpReply *HttpReply::createSuccessReply()
{
HttpReply *reply = new HttpReply(HttpReply::Ok, HttpReply::TypeSync);
reply->setPayload("200 Ok");
return reply;
}
HttpReply *HttpReply::createErrorReply(const HttpReply::HttpStatusCode &statusCode)
{
HttpReply *reply = new HttpReply(statusCode, HttpReply::TypeSync);
reply->setPayload(QByteArray::number(reply->httpStatusCode()) + " " + reply->httpReasonPhrase());
return reply;
}
HttpReply *HttpReply::createAsyncReply()
{
HttpReply *reply = new HttpReply(HttpReply::Ok, HttpReply::TypeAsync);
reply->setPayload(QByteArray::number(reply->httpStatusCode()) + " " + reply->httpReasonPhrase());
return reply;
}
void HttpReply::setHttpStatusCode(const HttpReply::HttpStatusCode &statusCode)
{
m_statusCode = statusCode;

View File

@ -74,8 +74,12 @@ public:
TypeAsync
};
HttpReply(QObject *parent = 0);
HttpReply(const HttpStatusCode &statusCode = HttpStatusCode::Ok, const Type &type = TypeSync, QObject *parent = 0);
HttpReply(QObject *parent = nullptr);
HttpReply(const HttpStatusCode &statusCode = HttpStatusCode::Ok, const Type &type = TypeSync, QObject *parent = nullptr);
static HttpReply* createSuccessReply();
static HttpReply* createErrorReply(const HttpReply::HttpStatusCode &statusCode);
static HttpReply* createAsyncReply();
void setHttpStatusCode(const HttpStatusCode &statusCode);
HttpStatusCode httpStatusCode() const;

View File

@ -1,302 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class nymeaserver::DeviceClassesResource
\brief This subclass of \l{RestResource} processes the REST requests for the \tt DeviceClasses namespace.
\ingroup json
\inmodule core
This \l{RestResource} will be created in the \l{RestServer} and used to handle REST requests
for the \tt {DeviceClasses} namespace of the API.
\code
http://localhost:3333/api/v1/deviceclasses
\endcode
\sa DeviceClass, RestResource, RestServer
*/
#include "deviceclassesresource.h"
#include "servers/httprequest.h"
#include "nymeacore.h"
#include "devices/devicediscoveryinfo.h"
#include <QJsonDocument>
namespace nymeaserver {
/*! Constructs a \l DeviceClassesResource with the given \a parent. */
DeviceClassesResource::DeviceClassesResource(QObject *parent) :
RestResource(parent)
{
}
/*! Returns the name of the \l{RestResource}. In this case \b deviceclasses.
\sa RestResource::name()
*/
QString DeviceClassesResource::name() const
{
return "deviceclasses";
}
/*! This method will be used to process the given \a request and the given \a urlTokens. The request
has to be in this namespace. Returns the resulting \l HttpReply.
\sa HttpRequest, HttpReply, RestResource::proccessRequest()
*/
HttpReply *DeviceClassesResource::proccessRequest(const HttpRequest &request, const QStringList &urlTokens)
{
// get the main resource
// /api/v1/deviceclasses/{deviceClassId}/
if (urlTokens.count() >= 4) {
DeviceClassId deviceClassId = DeviceClassId(urlTokens.at(3));
if (deviceClassId.isNull()) {
qCWarning(dcRest) << "Could not parse DeviceClassId:" << urlTokens.at(3);
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorDeviceClassNotFound);
}
m_deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(deviceClassId);
if (!m_deviceClass.isValid()) {
qCWarning(dcRest) << "DeviceClassId" << deviceClassId.toString() << "not found";
return createDeviceErrorReply(HttpReply::NotFound, Device::DeviceErrorDeviceClassNotFound);
}
}
// check method
HttpReply *reply;
switch (request.method()) {
case HttpRequest::Get:
reply = proccessGetRequest(request, urlTokens);
break;
default:
reply = createErrorReply(HttpReply::BadRequest);
break;
}
return reply;
}
HttpReply *DeviceClassesResource::proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
// GET /api/v1/deviceclasses?vendorId="{vendorId}"
if (urlTokens.count() == 3) {
VendorId vendorId;
if (request.url().hasQuery()) {
if (request.urlQuery().hasQueryItem("vendorId")) {
vendorId = VendorId(request.urlQuery().queryItemValue("vendorId"));
if (vendorId.isNull()) {
qCWarning(dcRest) << "Could not parse VendorId:" << request.urlQuery().queryItemValue("vendorId");
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorVendorNotFound);
}
}
}
return getDeviceClasses(vendorId);
}
// GET /api/v1/deviceclasses/{deviceClassId}
if (urlTokens.count() == 4)
return getDeviceClass();
// GET /api/v1/deviceclasses/{deviceClassId}/actiontypes
if (urlTokens.count() == 5 && urlTokens.at(4) == "actiontypes")
return getActionTypes();
// GET /api/v1/deviceclasses/{deviceClassId}/actiontypes/{actionTypeId}
if (urlTokens.count() == 6 && urlTokens.at(4) == "actiontypes") {
ActionTypeId actionTypeId = ActionTypeId(urlTokens.at(5));
if (actionTypeId.isNull()) {
qCWarning(dcRest) << "Could not parse ActionTypeId:" << urlTokens.at(5);
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorActionTypeNotFound);
}
return getActionType(actionTypeId);
}
// GET /api/v1/deviceclasses/{deviceClassId}/statetypes
if (urlTokens.count() == 5 && urlTokens.at(4) == "statetypes")
return getStateTypes();
// GET /api/v1/deviceclasses/{deviceClassId}/statetypes/{stateTypeId}
if (urlTokens.count() == 6 && urlTokens.at(4) == "statetypes") {
StateTypeId stateTypeId = StateTypeId(urlTokens.at(5));
if (stateTypeId.isNull()) {
qCWarning(dcRest) << "Could not parse StateTypeId:" << urlTokens.at(5);
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorStateTypeNotFound);
}
return getStateType(stateTypeId);
}
// GET /api/v1/deviceclasses/{deviceClassId}/eventtypes
if (urlTokens.count() == 5 && urlTokens.at(4) == "eventtypes")
return getEventTypes();
// GET /api/v1/deviceclasses/{deviceClassId}/eventtypes/{eventTypeId}
if (urlTokens.count() == 6 && urlTokens.at(4) == "eventtypes") {
EventTypeId eventTypeId = EventTypeId(urlTokens.at(5));
if (eventTypeId.isNull()) {
qCWarning(dcRest) << "Could not parse EventTypeId:" << urlTokens.at(5);
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorEventTypeNotFound);
}
return getEventType(eventTypeId);
}
// GET /api/v1/deviceclasses/{deviceClassId}/discover?name=paramName&value=paramValue
if (urlTokens.count() == 5 && urlTokens.at(4) == "discover") {
ParamList params;
if (request.urlQuery().hasQueryItem("params")) {
QString paramMapString = request.urlQuery().queryItemValue("params");
QPair<bool, QVariant> verification = verifyPayload(paramMapString.toUtf8());
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
params = JsonTypes::unpackParams(verification.second.toList());
}
return getDiscoverdDevices(params);
}
return createErrorReply(HttpReply::BadRequest);
}
HttpReply *DeviceClassesResource::getDeviceClass()
{
qCDebug(dcRest) << "Get device class with id " << m_deviceClass.id();
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packDeviceClass(m_deviceClass, NymeaCore::instance()->configuration()->locale())).toJson());
return reply;
}
HttpReply *DeviceClassesResource::getActionTypes()
{
qCDebug(dcRest) << "Get action types for device class" << m_deviceClass.id();
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packActionTypes(m_deviceClass, NymeaCore::instance()->configuration()->locale())).toJson());
return reply;
}
HttpReply *DeviceClassesResource::getActionType(const ActionTypeId &actionTypeId)
{
qCDebug(dcRest) << "Get action type with id" << actionTypeId;
foreach (const ActionType &actionType, m_deviceClass.actionTypes()) {
if (actionType.id() == actionTypeId) {
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packActionType(actionType, m_deviceClass.pluginId(), NymeaCore::instance()->configuration()->locale())).toJson());
return reply;
}
}
return createDeviceErrorReply(HttpReply::NotFound, Device::DeviceErrorActionTypeNotFound);
}
HttpReply *DeviceClassesResource::getStateTypes()
{
qCDebug(dcRest) << "Get state types for device class" << m_deviceClass.id();
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packStateTypes(m_deviceClass, NymeaCore::instance()->configuration()->locale())).toJson());
return reply;
}
HttpReply *DeviceClassesResource::getStateType(const StateTypeId &stateTypeId)
{
qCDebug(dcRest) << "Get state type with id" << stateTypeId;
foreach (const StateType &stateType, m_deviceClass.stateTypes()) {
if (stateType.id() == stateTypeId) {
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packStateType(stateType, m_deviceClass.pluginId(), NymeaCore::instance()->configuration()->locale())).toJson());
return reply;
}
}
return createDeviceErrorReply(HttpReply::NotFound, Device::DeviceErrorStateTypeNotFound);
}
HttpReply *DeviceClassesResource::getEventTypes()
{
qCDebug(dcRest) << "Get event types for device class" << m_deviceClass.id();
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packEventTypes(m_deviceClass, NymeaCore::instance()->configuration()->locale())).toJson());
return reply;
}
HttpReply *DeviceClassesResource::getEventType(const EventTypeId &eventTypeId)
{
qCDebug(dcRest) << "Get event type with id" << eventTypeId;
foreach (const EventType &eventType, m_deviceClass.eventTypes()) {
if (eventType.id() == eventTypeId) {
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packEventType(eventType, m_deviceClass.pluginId(), NymeaCore::instance()->configuration()->locale())).toJson());
return reply;
}
}
return createDeviceErrorReply(HttpReply::NotFound, Device::DeviceErrorEventTypeNotFound);
}
HttpReply *DeviceClassesResource::getDiscoverdDevices(const ParamList &discoveryParams)
{
qCDebug(dcRest) << "Discover devices for DeviceClass" << m_deviceClass.id();
qCDebug(dcRest) << discoveryParams;
DeviceDiscoveryInfo *info = NymeaCore::instance()->deviceManager()->discoverDevices(m_deviceClass.id(), discoveryParams);
HttpReply *reply = createAsyncReply();
connect(info, &DeviceDiscoveryInfo::finished, reply, [info, reply](){
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
QVariantMap response;
if (info->status() != Device::DeviceErrorNoError) {
reply->setHttpStatusCode(HttpReply::InternalServerError);
response.insert("error", JsonTypes::deviceErrorToString(info->status()));
reply->setPayload(QJsonDocument::fromVariant(response).toJson());
} else {
reply->setHttpStatusCode(HttpReply::Ok);
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packDeviceDescriptors(info->deviceDescriptors())).toJson());
}
reply->finished();
});
return reply;
}
HttpReply *DeviceClassesResource::getDeviceClasses(const VendorId &vendorId)
{
if (vendorId == VendorId()) {
qCDebug(dcRest) << "Get all device classes.";
} else {
qCDebug(dcRest) << "Get device classes for vendor" << vendorId.toString();
}
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packSupportedDevices(vendorId, NymeaCore::instance()->configuration()->locale())).toJson());
return reply;
}
}

View File

@ -1,74 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DEVICECLASSESRESOURCE_H
#define DEVICECLASSESRESOURCE_H
#include <QObject>
#include <QHash>
#include <QPointer>
#include "jsonrpc/jsontypes.h"
#include "restresource.h"
#include "servers/httpreply.h"
namespace nymeaserver {
class HttpRequest;
class DeviceClassesResource: public RestResource
{
Q_OBJECT
public:
explicit DeviceClassesResource(QObject *parent = nullptr);
QString name() const override;
HttpReply *proccessRequest(const HttpRequest &request, const QStringList &urlTokens) override;
private:
mutable QHash<DeviceClassId, QPointer<HttpReply>> m_discoverRequests;
DeviceClass m_deviceClass;
// Process method
HttpReply *proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens) override;
// Get methods
HttpReply *getDeviceClasses(const VendorId &vendorId);
HttpReply *getDeviceClass();
HttpReply *getActionTypes();
HttpReply *getActionType(const ActionTypeId &actionTypeId);
HttpReply *getStateTypes();
HttpReply *getStateType(const StateTypeId &stateTypeId);
HttpReply *getEventTypes();
HttpReply *getEventType(const EventTypeId &eventTypeId);
HttpReply *getDiscoverdDevices(const ParamList &discoveryParams);
};
}
#endif // DEVICECLASSESRESOURCE_H

View File

@ -1,557 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class nymeaserver::DevicesResource
\brief This subclass of \l{RestResource} processes the REST requests for the \tt Devices namespace.
\ingroup json
\inmodule core
This \l{RestResource} will be created in the \l{RestServer} and used to handle REST requests
for the \tt {Devices} namespace of the API.
\code
http://localhost:3333/api/v1/devices
\endcode
\sa Device, RestResource, RestServer
*/
#include "devicesresource.h"
#include "servers/httpreply.h"
#include "servers/httprequest.h"
#include "jsonrpc/jsontypes.h"
#include "nymeacore.h"
#include "devices/devicesetupinfo.h"
#include "devices/devicepairinginfo.h"
#include "devices/deviceactioninfo.h"
#include <QJsonDocument>
namespace nymeaserver {
/*! Constructs a \l DevicesResource with the given \a parent. */
DevicesResource::DevicesResource(QObject *parent) :
RestResource(parent)
{
}
/*! Returns the name of the \l{RestResource}. In this case \b devices.
\sa RestResource::name()
*/
QString DevicesResource::name() const
{
return "devices";
}
/*! This method will be used to process the given \a request and the given \a urlTokens. The request
has to be in this namespace. Returns the resulting \l HttpReply.
\sa HttpRequest, HttpReply, RestResource::proccessRequest()
*/
HttpReply *DevicesResource::proccessRequest(const HttpRequest &request, const QStringList &urlTokens)
{
m_device = nullptr;
// get the main resource
if (urlTokens.count() >= 4 && urlTokens.at(3) != "pair" && urlTokens.at(3) != "confirmpairing") {
DeviceId deviceId = DeviceId(urlTokens.at(3));
if (deviceId.isNull()) {
qCWarning(dcRest) << "Could not parse DeviceId:" << urlTokens.at(3);
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorDeviceNotFound);
}
m_device = NymeaCore::instance()->deviceManager()->findConfiguredDevice(deviceId);
if (!m_device) {
qCWarning(dcRest) << "Could find any device with DeviceId:" << urlTokens.at(3);
return createDeviceErrorReply(HttpReply::NotFound, Device::DeviceErrorDeviceNotFound);
}
}
// check method
HttpReply *reply;
switch (request.method()) {
case HttpRequest::Get:
reply = proccessGetRequest(request, urlTokens);
break;
case HttpRequest::Post:
reply = proccessPostRequest(request, urlTokens);
break;
case HttpRequest::Put:
reply = proccessPutRequest(request, urlTokens);
break;
case HttpRequest::Delete:
reply = proccessDeleteRequest(request, urlTokens);
break;
case HttpRequest::Options:
reply = proccessOptionsRequest(request, urlTokens);
break;
default:
reply = createErrorReply(HttpReply::BadRequest);
break;
}
return reply;
}
HttpReply *DevicesResource::proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
// GET /api/v1/devices
if (urlTokens.count() == 3)
return getConfiguredDevices();
// GET /api/v1/devices/{deviceId}
if (urlTokens.count() == 4)
return getConfiguredDevice(m_device);
// GET /api/v1/devices/{deviceId}/states
if (urlTokens.count() == 5 && urlTokens.at(4) == "states")
return getDeviceStateValues(m_device);
// /api/v1/devices/{deviceId}/states/{stateTypeId}
if (urlTokens.count() >= 6 && urlTokens.at(4) == "states") {
StateTypeId stateTypeId = StateTypeId(urlTokens.at(5));
if (stateTypeId.isNull()) {
qCWarning(dcRest) << "Could not parse StateTypeId:" << urlTokens.at(5);
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorStateTypeNotFound);
}
if (!m_device->hasState(stateTypeId)){
qCWarning(dcRest) << "This device has no StateTypeId:" << urlTokens.at(5);
return createDeviceErrorReply(HttpReply::NotFound, Device::DeviceErrorStateTypeNotFound);
}
return getDeviceStateValue(m_device, stateTypeId);
}
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *DevicesResource::proccessDeleteRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
// DELETE /api/v1/devices
if (urlTokens.count() == 3)
return createErrorReply(HttpReply::BadRequest);
// DELETE /api/v1/devices/{deviceId}
if (urlTokens.count() == 4) {
QVariantMap params;
if (request.urlQuery().hasQueryItem("params")) {
QString paramMapString = request.urlQuery().queryItemValue("params");
QPair<bool, QVariant> verification = verifyPayload(paramMapString.toUtf8());
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
params = verification.second.toMap();
}
return removeDevice(m_device, params);
}
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *DevicesResource::proccessPutRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
// PUT /api/v1/devices
if (urlTokens.count() == 3)
return createErrorReply(HttpReply::BadRequest);
// PUT /api/v1/devices/{deviceId}
if (urlTokens.count() == 4)
return reconfigureDevice(m_device, request.payload());
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *DevicesResource::proccessPostRequest(const HttpRequest &request, const QStringList &urlTokens)
{
// POST /api/v1/devices
if (urlTokens.count() == 3)
return addConfiguredDevice(request.payload());
// POST /api/v1/devices/pair
if (urlTokens.count() == 4 && urlTokens.at(3) == "pair")
return pairDevice(request.payload());
// POST /api/v1/devices/confirmpairing
if (urlTokens.count() == 4 && urlTokens.at(3) == "confirmpairing")
return confirmPairDevice(request.payload());
// POST /api/v1/devices/{deviceId}
if (urlTokens.count() == 4)
return editDevice(request.payload());
// POST /api/v1/devices/{deviceId}/execute/{actionTypeId}
if (urlTokens.count() >= 6 && urlTokens.at(4) == "execute") {
ActionTypeId actionTypeId = ActionTypeId(urlTokens.at(5));
if (actionTypeId.isNull()) {
qCWarning(dcRest) << "Could not parse ActionTypeId:" << urlTokens.at(5);
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorActionTypeNotFound);
}
bool found = false;
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(m_device->deviceClassId());
foreach (const ActionType actionType, deviceClass.actionTypes()) {
if (actionType.id() == actionTypeId) {
found = true;
break;
}
}
if (!found) {
qCWarning(dcRest) << "Could not find ActionTypeId:" << actionTypeId.toString();
return createDeviceErrorReply(HttpReply::NotFound, Device::DeviceErrorActionTypeNotFound);
}
return executeAction(m_device, actionTypeId, request.payload());
}
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *DevicesResource::proccessOptionsRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
Q_UNUSED(urlTokens)
return RestResource::createCorsSuccessReply();
}
HttpReply *DevicesResource::getConfiguredDevices() const
{
qCDebug(dcRest) << "Get all configured devices";
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packConfiguredDevices()).toJson());
return reply;
}
HttpReply *DevicesResource::getConfiguredDevice(Device *device) const
{
qCDebug(dcRest) << "Get configured device with id:" << device->id().toString();
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packDevice(device)).toJson());
return reply;
}
HttpReply *DevicesResource::getDeviceStateValues(Device *device) const
{
qCDebug(dcRest) << "Get states of device with id:" << device->id().toString();
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packDeviceStates(device)).toJson());
return reply;
}
HttpReply *DevicesResource::getDeviceStateValue(Device *device, const StateTypeId &stateTypeId) const
{
qCDebug(dcRest) << "Get device state value of state with id:" << stateTypeId.toString();
HttpReply *reply = createSuccessReply();
QVariantMap stateValueMap;
stateValueMap.insert("value", device->state(stateTypeId).value());
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(stateValueMap).toJson());
return reply;
}
HttpReply *DevicesResource::removeDevice(Device *device, const QVariantMap &params) const
{
qCDebug(dcRest) << "Remove device with id:" << device->id().toString();
qCDebug(dcRest) << QJsonDocument::fromVariant(params).toJson();
// global removePolicy has priority
if (params.contains("removePolicy")) {
RuleEngine::RemovePolicy removePolicy = params.value("removePolicy").toString() == "RemovePolicyCascade" ? RuleEngine::RemovePolicyCascade : RuleEngine::RemovePolicyUpdate;
Device::DeviceError result = NymeaCore::instance()->removeConfiguredDevice(device->id(), removePolicy);
return createDeviceErrorReply(HttpReply::Ok, result);
}
QHash<RuleId, RuleEngine::RemovePolicy> removePolicyList;
foreach (const QVariant &variant, params.value("removePolicyList").toList()) {
RuleId ruleId = RuleId(variant.toMap().value("ruleId").toString());
RuleEngine::RemovePolicy policy = variant.toMap().value("policy").toString() == "RemovePolicyCascade" ? RuleEngine::RemovePolicyCascade : RuleEngine::RemovePolicyUpdate;
removePolicyList.insert(ruleId, policy);
}
QPair<Device::DeviceError, QList<RuleId> > status = NymeaCore::instance()->removeConfiguredDevice(device->id(), removePolicyList);
// if there are offending rules
if (!status.second.isEmpty()) {
QVariantList ruleIdList;
QVariantMap returns;
returns.insert("error", JsonTypes::deviceErrorToString(status.first));
foreach (const RuleId &ruleId, status.second) {
ruleIdList.append(ruleId.toString());
}
returns.insert("ruleIds", ruleIdList);
HttpReply *reply = createErrorReply(HttpReply::BadRequest);
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(returns).toJson());
return reply;
}
if (status.first == Device::DeviceErrorNoError)
return createDeviceErrorReply(HttpReply::Ok, status.first);
return createDeviceErrorReply(HttpReply::BadRequest, status.first);
}
HttpReply *DevicesResource::executeAction(Device *device, const ActionTypeId &actionTypeId, const QByteArray &payload) const
{
QPair<bool, QVariant> verification = RestResource::verifyPayload(payload);
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
QVariantMap message = verification.second.toMap();
if (!message.contains("params"))
return createErrorReply(HttpReply::BadRequest);
ParamList actionParams = JsonTypes::unpackParams(message.value("params").toList());
qCDebug(dcRest) << "Execute action with" << actionParams;
Action action(actionTypeId, device->id());
action.setParams(actionParams);
HttpReply *httpReply = createAsyncReply();
DeviceActionInfo *info = NymeaCore::instance()->executeAction(action);
connect(info, &DeviceActionInfo::finished, this, [info, httpReply](){
QVariantMap response;
response.insert("error", JsonTypes::deviceErrorToString(info->status()));
httpReply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
if (info->status() == Device::DeviceErrorNoError) {
qCDebug(dcRest) << "Action execution finished successfully";
httpReply->setHttpStatusCode(HttpReply::Ok);
httpReply->setPayload(QJsonDocument::fromVariant(response).toJson());
} else {
qCDebug(dcRest) << "Action execution finished with error" << info->status();
QVariantMap response;
response.insert("error", JsonTypes::deviceErrorToString(info->status()));
httpReply->setHttpStatusCode(HttpReply::InternalServerError);
httpReply->setPayload(QJsonDocument::fromVariant(response).toJson());
}
httpReply->finished();
});
return httpReply;
}
HttpReply *DevicesResource::addConfiguredDevice(const QByteArray &payload) const
{
QPair<bool, QVariant> verification = RestResource::verifyPayload(payload);
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
QVariantMap params = verification.second.toMap();
DeviceClassId deviceClassId(params.value("deviceClassId").toString());
if (deviceClassId.isNull())
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorDeviceClassNotFound);
QString deviceName = params.value("name").toString();
ParamList deviceParams = JsonTypes::unpackParams(params.value("deviceParams").toList());
DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString());
HttpReply *httpReply = createAsyncReply();
DeviceSetupInfo *info;
if (deviceDescriptorId.isNull()) {
qCDebug(dcRest) << "Adding device" << deviceName << "with" << deviceParams;
info = NymeaCore::instance()->deviceManager()->addConfiguredDevice(deviceClassId, deviceParams, deviceName);
} else {
qCDebug(dcRest) << "Adding discovered device" << deviceName << "with DeviceDescriptorId" << deviceDescriptorId.toString();
info = NymeaCore::instance()->deviceManager()->addConfiguredDevice(deviceDescriptorId, deviceParams, deviceName);
}
connect(info, &DeviceSetupInfo::finished, httpReply, [info, httpReply](){
httpReply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
if (info->status() != Device::DeviceErrorNoError) {
qCDebug(dcRest) << "Device setup finished with error" << info->status();
QVariantMap response;
response.insert("error", JsonTypes::deviceErrorToString(info->status()));
httpReply->setHttpStatusCode(HttpReply::InternalServerError);
httpReply->setPayload(QJsonDocument::fromVariant(response).toJson());
} else {
httpReply->setHttpStatusCode(HttpReply::Ok);
QVariant result = JsonTypes::packDevice(info->device());
httpReply->setPayload(QJsonDocument::fromVariant(result).toJson());
}
qCDebug(dcRest) << "Device setup finished successfully";
httpReply->finished();
});
return httpReply;
}
HttpReply *DevicesResource::editDevice(const QByteArray &payload) const
{
QPair<bool, QVariant> verification = RestResource::verifyPayload(payload);
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
QVariantMap params = verification.second.toMap();
QString name = params.value("name").toString();
Device::DeviceError status = NymeaCore::instance()->deviceManager()->editDevice(m_device->id(), name);
if (status != Device::DeviceErrorNoError)
return createDeviceErrorReply(HttpReply::BadRequest, status);
return createDeviceErrorReply(HttpReply::Ok, status);
}
HttpReply *DevicesResource::pairDevice(const QByteArray &payload) const
{
QPair<bool, QVariant> verification = RestResource::verifyPayload(payload);
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
QVariantMap params = verification.second.toMap();
DeviceClassId deviceClassId(params.value("deviceClassId").toString());
DeviceClass deviceClass = NymeaCore::instance()->deviceManager()->findDeviceClass(deviceClassId);
if (deviceClassId.isNull()) {
qCWarning(dcRest) << "Could not find deviceClassId" << params.value("deviceClassId").toString();
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorDeviceClassNotFound);
}
QString deviceName = params.value("name").toString();
ParamList deviceParams = JsonTypes::unpackParams(params.value("deviceParams").toList());
qCDebug(dcRest) << "Pair device" << deviceName << "with deviceClassId" << deviceClassId.toString();
HttpReply *httpReply = createAsyncReply();
DevicePairingInfo *info;
if (params.contains("deviceDescriptorId")) {
DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString());
info = NymeaCore::instance()->deviceManager()->pairDevice(deviceDescriptorId, deviceParams, deviceName);
} else {
info = NymeaCore::instance()->deviceManager()->pairDevice(deviceClassId, deviceParams, deviceName);
}
connect(info, &DevicePairingInfo::finished, httpReply, [info, httpReply](){
httpReply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
if (info->status() != Device::DeviceErrorNoError) {
qCDebug(dcRest) << "Device setup finished with error" << info->status();
QVariantMap response;
response.insert("error", JsonTypes::deviceErrorToString(info->status()));
httpReply->setHttpStatusCode(HttpReply::InternalServerError);
httpReply->setPayload(QJsonDocument::fromVariant(response).toJson());
} else {
httpReply->setHttpStatusCode(HttpReply::Ok);
QVariantMap returns;
returns.insert("error", JsonTypes::deviceErrorToString(info->status()));
returns.insert("pairingTransactionId", info->transactionId().toString());
returns.insert("displayMessage", info->displayMessage());
httpReply->setPayload(QJsonDocument::fromVariant(returns).toJson());
}
qCDebug(dcRest) << "Device pairing initiated successfully";
httpReply->finished();
});
return httpReply;
}
HttpReply *DevicesResource::confirmPairDevice(const QByteArray &payload) const
{
QPair<bool, QVariant> verification = RestResource::verifyPayload(payload);
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
QVariantMap params = verification.second.toMap();
PairingTransactionId pairingTransactionId = PairingTransactionId(params.value("pairingTransactionId").toString());
QString username = params.value("username").toString();
QString secret = params.value("secret").toString();
HttpReply *httpReply = createAsyncReply();
DevicePairingInfo *info = NymeaCore::instance()->deviceManager()->confirmPairing(pairingTransactionId, username, secret);
connect(info, &DevicePairingInfo::finished, httpReply, [info, httpReply](){
qCDebug(dcRest()) << "Confirm pairing finished:" << info->status();
QVariantMap response;
response.insert("error", JsonTypes::deviceErrorToString(info->status()));
if (info->status() != Device::DeviceErrorNoError) {
qCDebug(dcRest) << "Pairing device finished with error.";
httpReply->setHttpStatusCode(HttpReply::InternalServerError);
} else {
httpReply->setHttpStatusCode(HttpReply::Ok);
response.insert("id", info->deviceId().toString());
}
httpReply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
httpReply->setPayload(QJsonDocument::fromVariant(response).toJson());
httpReply->finished();
});
return httpReply;
}
HttpReply *DevicesResource::reconfigureDevice(Device *device, const QByteArray &payload) const
{
qCDebug(dcRest) << "Reconfigure device" << device->id();
QPair<bool, QVariant> verification = RestResource::verifyPayload(payload);
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
QVariantMap params = verification.second.toMap();
ParamList deviceParams = JsonTypes::unpackParams(params.value("deviceParams").toList());
HttpReply *httpReply = createAsyncReply();
DeviceSetupInfo *info;
DeviceDescriptorId deviceDescriptorId(params.value("deviceDescriptorId").toString());
if (deviceDescriptorId.isNull()) {
qCDebug(dcRest) << "Reconfigure device with params:" << deviceParams;
info = NymeaCore::instance()->deviceManager()->reconfigureDevice(device->id(), deviceParams);
} else {
qCDebug(dcRest) << "Reconfigure device using the new discovered device with descriptorId:" << deviceDescriptorId.toString();
info = NymeaCore::instance()->deviceManager()->reconfigureDevice(deviceDescriptorId);
}
connect(info, &DeviceSetupInfo::finished, httpReply, [httpReply, info](){
httpReply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
if (info->status() != Device::DeviceErrorNoError) {
qCDebug(dcRest) << "Device reconfiguration finished with error" << info->status();
QVariantMap response;
response.insert("error", JsonTypes::deviceErrorToString(info->status()));
httpReply->setHttpStatusCode(HttpReply::InternalServerError);
httpReply->setPayload(QJsonDocument::fromVariant(response).toJson());
} else {
qCDebug(dcRest) << "Device reconfiguration finished successfully";
httpReply->setHttpStatusCode(HttpReply::Ok);
QVariant result = JsonTypes::packDevice(info->device());
httpReply->setPayload(QJsonDocument::fromVariant(result).toJson());
}
httpReply->finished();
});
return httpReply;
}
}

View File

@ -1,81 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DEVICESRESOURCE_H
#define DEVICESRESOURCE_H
#include <QObject>
#include <QHash>
#include <QPointer>
#include "jsonrpc/jsontypes.h"
#include "restresource.h"
#include "servers/httpreply.h"
namespace nymeaserver {
class HttpRequest;
class DevicesResource: public RestResource
{
Q_OBJECT
public:
explicit DevicesResource(QObject *parent = nullptr);
QString name() const override;
HttpReply *proccessRequest(const HttpRequest &request, const QStringList &urlTokens) override;
private:
mutable QHash<ActionId, QPointer<HttpReply> > m_asyncActionExecutions;
Device *m_device;
// Process method
HttpReply *proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens) override;
HttpReply *proccessDeleteRequest(const HttpRequest &request, const QStringList &urlTokens) override;
HttpReply *proccessPutRequest(const HttpRequest &request, const QStringList &urlTokens) override;
HttpReply *proccessPostRequest(const HttpRequest &request, const QStringList &urlTokens) override;
HttpReply *proccessOptionsRequest(const HttpRequest &request, const QStringList &urlTokens) override;
// Get methods
HttpReply *getConfiguredDevices() const;
HttpReply *getConfiguredDevice(Device *device) const;
HttpReply *getDeviceStateValues(Device *device) const;
HttpReply *getDeviceStateValue(Device *device, const StateTypeId &stateTypeId) const;
// Delete methods
HttpReply *removeDevice(Device *device, const QVariantMap &params) const;
// Post methods
HttpReply *executeAction(Device *device, const ActionTypeId &actionTypeId, const QByteArray &payload) const;
HttpReply *addConfiguredDevice(const QByteArray &payload) const;
HttpReply *editDevice(const QByteArray &payload) const;
HttpReply *pairDevice(const QByteArray &payload) const;
HttpReply *confirmPairDevice(const QByteArray &payload) const;
// Put methods
HttpReply *reconfigureDevice(Device *device, const QByteArray &payload) const;
};
}
#endif // DEVICESRESOURCE_H

View File

@ -1,121 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class nymeaserver::LogsResource
\brief This subclass of \l{RestResource} processes the REST requests for the \tt Logs namespace.
\ingroup json
\inmodule core
This \l{RestResource} will be created in the \l{RestServer} and used to handle REST requests
for the \tt {Logs} namespace of the API.
\code
http://localhost:3333/api/v1/logs
\endcode
\sa LogEntry, RestResource, RestServer
*/
#include "logsresource.h"
#include "servers/httprequest.h"
#include "loggingcategories.h"
#include "nymeacore.h"
#include "logging/logengine.h"
#include <QJsonDocument>
namespace nymeaserver {
/*! Constructs a \l LogsResource with the given \a parent. */
LogsResource::LogsResource(QObject *parent) :
RestResource(parent)
{
}
/*! Returns the name of the \l{RestResource}. In this case \b logs.
\sa RestResource::name()
*/
QString LogsResource::name() const
{
return "logs";
}
/*! This method will be used to process the given \a request and the given \a urlTokens. The request
has to be in this namespace. Returns the resulting \l HttpReply.
\sa HttpRequest, HttpReply, RestResource::proccessRequest()
*/
HttpReply *LogsResource::proccessRequest(const HttpRequest &request, const QStringList &urlTokens)
{
// check method
HttpReply *reply;
switch (request.method()) {
case HttpRequest::Get:
reply = proccessGetRequest(request, urlTokens);
break;
default:
reply = createErrorReply(HttpReply::BadRequest);
break;
}
return reply;
}
HttpReply *LogsResource::proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens)
{
// GET /api/v1/logs?filter={ogFilter}
if (urlTokens.count() == 3) {
// check filter
QString filterString;
if (request.url().hasQuery()) {
if (request.urlQuery().hasQueryItem("filter")) {
filterString = request.urlQuery().queryItemValue("filter");
}
}
return getLogEntries(filterString);
}
return createErrorReply(HttpReply::NotImplemented);}
HttpReply *LogsResource::getLogEntries(const QString &filterString)
{
qCDebug(dcRest) << "Get log entries";
QPair<bool, QVariant> verification = RestResource::verifyPayload(filterString.toUtf8());
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
QVariantMap filterMap = verification.second.toMap();
LogFilter filter = JsonTypes::unpackLogFilter(filterMap);
QVariantList entries;
foreach (const LogEntry &entry, NymeaCore::instance()->logEngine()->logEntries(filter)) {
entries.append(JsonTypes::packLogEntry(entry));
}
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(entries).toJson());
return reply;
}
}

View File

@ -1,57 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef LOGSRESOURCE_H
#define LOGSRESOURCE_H
#include <QObject>
#include <QHash>
#include "jsonrpc/jsontypes.h"
#include "restresource.h"
#include "servers/httpreply.h"
namespace nymeaserver {
class HttpRequest;
class LogsResource : public RestResource
{
Q_OBJECT
public:
explicit LogsResource(QObject *parent = 0);
QString name() const override;
HttpReply *proccessRequest(const HttpRequest &request, const QStringList &urlTokens) override;
private:
// Process method
HttpReply *proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens) override;
// Get methods
HttpReply *getLogEntries(const QString &filterString);
};
}
#endif // LOGSRESOURCE_H

View File

@ -1,214 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class nymeaserver::PluginsResource
\brief This subclass of \l{RestResource} processes the REST requests for the \tt Plugins namespace.
\ingroup json
\inmodule core
This \l{RestResource} will be created in the \l{RestServer} and used to handle REST requests
for the \tt {Plugins} namespace of the API.
\code
http://localhost:3333/api/v1/plugins
\endcode
\sa DevicePlugin, RestResource, RestServer
*/
#include "pluginsresource.h"
#include "servers/httprequest.h"
#include "loggingcategories.h"
#include "nymeacore.h"
#include <QJsonDocument>
namespace nymeaserver {
/*! Constructs a \l PluginsResource with the given \a parent. */
PluginsResource::PluginsResource(QObject *parent) :
RestResource(parent)
{
}
/*! Returns the name of the \l{RestResource}. In this case \b plugins.
\sa RestResource::name()
*/
QString PluginsResource::name() const
{
return "plugins";
}
/*! This method will be used to process the given \a request and the given \a urlTokens. The request
has to be in this namespace. Returns the resulting \l HttpReply.
\sa HttpRequest, HttpReply, RestResource::proccessRequest()
*/
HttpReply *PluginsResource::proccessRequest(const HttpRequest &request, const QStringList &urlTokens)
{
// get the main resource
// /api/v1/plugins/{pluginId}/
if (urlTokens.count() >= 4) {
m_pluginId = PluginId(urlTokens.at(3));
if (m_pluginId.isNull()) {
qCWarning(dcRest) << "Could not parse PluginId:" << urlTokens.at(3);
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorPluginNotFound);
}
}
// check method
HttpReply *reply;
switch (request.method()) {
case HttpRequest::Get:
reply = proccessGetRequest(request, urlTokens);
break;
case HttpRequest::Put:
reply = proccessPutRequest(request, urlTokens);
break;
case HttpRequest::Options:
reply = proccessOptionsRequest(request, urlTokens);
break;
default:
reply = createErrorReply(HttpReply::BadRequest);
break;
}
return reply;
}
HttpReply *PluginsResource::proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
// GET /api/v1/plugins
if (urlTokens.count() == 3)
return getPlugins();
// GET /api/v1/plugins/{pluginId}
if (urlTokens.count() == 4)
return getPlugin(m_pluginId);
// GET /api/v1/plugins/{pluginId}/configuration
if (urlTokens.count() == 5 && urlTokens.at(4) == "configuration") {
return getPluginConfiguration(m_pluginId);
}
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *PluginsResource::proccessPutRequest(const HttpRequest &request, const QStringList &urlTokens)
{
// PUT /api/v1/plugins/{pluginId}/configuration
if (urlTokens.count() == 5 && urlTokens.at(4) == "configuration") {
return setPluginConfiguration(m_pluginId, request.payload());
}
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *PluginsResource::proccessOptionsRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
Q_UNUSED(urlTokens)
return RestResource::createCorsSuccessReply();
}
HttpReply *PluginsResource::getPlugins() const
{
qCDebug(dcRest) << "Get plugins";
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packPlugins(NymeaCore::instance()->configuration()->locale())).toJson());
return reply;
}
HttpReply *PluginsResource::getPlugin(const PluginId &pluginId) const
{
qCDebug(dcRest) << "Get plugin with id" << pluginId;
HttpReply *reply = createSuccessReply();
foreach (DevicePlugin *plugin, NymeaCore::instance()->deviceManager()->plugins()) {
if (plugin->pluginId() == pluginId) {
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packPlugin(plugin, NymeaCore::instance()->configuration()->locale())).toJson());
return reply;
}
}
return createDeviceErrorReply(HttpReply::NotFound, Device::DeviceErrorPluginNotFound);
}
HttpReply *PluginsResource::getPluginConfiguration(const PluginId &pluginId) const
{
qCDebug(dcRest) << "Get configuration of plugin with id" << pluginId.toString();
DevicePlugin *plugin = 0;
plugin = findPlugin(pluginId);
if (!plugin)
return createDeviceErrorReply(HttpReply::NotFound, Device::DeviceErrorPluginNotFound);
QVariantList configurationParamsList;
foreach (const Param &param, plugin->configuration()) {
configurationParamsList.append(JsonTypes::packParam(param));
}
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(configurationParamsList).toJson());
return reply;
}
HttpReply *PluginsResource::setPluginConfiguration(const PluginId &pluginId, const QByteArray &payload) const
{
DevicePlugin *plugin = 0;
plugin = findPlugin(pluginId);
if (!plugin)
return createDeviceErrorReply(HttpReply::NotFound, Device::DeviceErrorPluginNotFound);
qCDebug(dcRest) << "Set configuration of plugin with id" << pluginId.toString();
QPair<bool, QVariant> verification = RestResource::verifyPayload(payload);
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
QVariantList configuration = verification.second.toList();
ParamList pluginParams = JsonTypes::unpackParams(configuration);
qCDebug(dcRest) << pluginParams;
Device::DeviceError result = NymeaCore::instance()->deviceManager()->setPluginConfig(pluginId, pluginParams);
if (result != Device::DeviceErrorNoError)
return createDeviceErrorReply(HttpReply::BadRequest, result);
return createDeviceErrorReply(HttpReply::Ok, result);
}
DevicePlugin *PluginsResource::findPlugin(const PluginId &pluginId) const
{
foreach (DevicePlugin *plugin, NymeaCore::instance()->deviceManager()->plugins()) {
if (plugin->pluginId() == pluginId) {
return plugin;
}
}
return 0;
}
}

View File

@ -1,67 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef PLUGINSRESOURCE_H
#define PLUGINSRESOURCE_H
#include <QObject>
#include <QHash>
#include "jsonrpc/jsontypes.h"
#include "restresource.h"
#include "servers/httpreply.h"
namespace nymeaserver {
class HttpRequest;
class PluginsResource : public RestResource
{
Q_OBJECT
public:
explicit PluginsResource(QObject *parent = 0);
QString name() const override;
HttpReply *proccessRequest(const HttpRequest &request, const QStringList &urlTokens) override;
private:
PluginId m_pluginId;
// Process method
HttpReply *proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens) override;
HttpReply *proccessPutRequest(const HttpRequest &request, const QStringList &urlTokens) override;
HttpReply *proccessOptionsRequest(const HttpRequest &request, const QStringList &urlTokens) override;
// Get methods
HttpReply *getPlugins() const;
HttpReply *getPlugin(const PluginId &pluginId) const;
HttpReply *getPluginConfiguration(const PluginId &pluginId) const;
HttpReply *setPluginConfiguration(const PluginId &pluginId, const QByteArray &payload) const;
// Put methods
DevicePlugin *findPlugin(const PluginId &pluginId) const;
};
}
#endif // PLUGINSRESOURCE_H

View File

@ -1,209 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class nymeaserver::RestResource
\brief This class provides an interface for REST API resources.
\ingroup api
\inmodule core
The \l{RestResource} class provides an interface for subclassing a new resource for
the REST API. A resource represents an Object in the core like \l{Device}{Devices}
or \l{DevicePlugin}{Plugins}. A \l{RestResource} will be identified by it's
\l{RestResource::name()}{name}.
Each subclass of \l{RestResource} has to implement the method \l{RestResource::proccessRequest()}
where a valid \l{HttpRequest} will be processed.
This name of the resource will define the endpoint of the an API call:
\code
http://localhost:3333/api/v1/<resourceName>
\endcode
\sa RestServer
*/
/*! \fn QString nymeaserver::RestResource::name() const;
This method must be implemented in a subclass of \l{RestResource}. It returns
the endpoint name of this \l{RestResource} i.e. if this method returns \tt "example"
the API resource will be accessable with the URL:
\code
http://localhost:3333/api/v1/example
\endcode
*/
/*! \fn HttpReply *nymeaserver::RestResource::proccessRequest(const HttpRequest &request, const QStringList &urlTokens)
This method will be called from the \l{RestServer} once a \l{HttpRequest} \a request was identified to belong
to this \l{RestResource}. The given \a urlTokens contain the full list of URL tokens from this request.
*/
#include "restresource.h"
#include "servers/httprequest.h"
#include "loggingcategories.h"
#include "devices/devicemanager.h"
#include <QJsonDocument>
#include <QVariant>
namespace nymeaserver {
/*! Constructs a \l{RestResource} with the given \a parent. */
RestResource::RestResource(QObject *parent) :
QObject(parent)
{
}
/*! Pure virtual destructor for this \l{RestResource}. */
RestResource::~RestResource()
{
}
/*! Returns the pointer to a new created \l{HttpReply} initialized with \l{HttpReply::Ok} and \l{HttpReply::TypeSync}. */
HttpReply *RestResource::createSuccessReply()
{
HttpReply *reply = new HttpReply(HttpReply::Ok, HttpReply::TypeSync);
reply->setPayload("200 Ok");
return reply;
}
/*! Returns the pointer to a new created \l{HttpReply} which represents a response to a CORS request. */
HttpReply *RestResource::createCorsSuccessReply()
{
HttpReply *reply = RestResource::createSuccessReply();
reply->setRawHeader("Accept","application/json");
reply->setRawHeader("Allow", "PUT, POST, GET, DELETE, OPTIONS");
reply->setRawHeader("Access-Control-Allow-Methods", "PUT, POST, GET, DELETE, OPTIONS");
reply->setRawHeader("Access-Control-Allow-Headers", "Origin, Content-Type, Accept");
reply->setRawHeader("Access-Control-Max-Age", "1728000");
return reply;
}
/*! Returns the pointer to a new created error \l{HttpReply} initialized with the given \a statusCode and \l{HttpReply::TypeSync}. */
HttpReply *RestResource::createErrorReply(const HttpReply::HttpStatusCode &statusCode)
{
HttpReply *reply = new HttpReply(statusCode, HttpReply::TypeSync);
reply->setPayload(QByteArray::number(reply->httpStatusCode()) + " " + reply->httpReasonPhrase());
return reply;
}
/*! Returns the pointer to a new created error \l{HttpReply} initialized with the given \a statusCode, \l{HttpReply::TypeSync} and the \a deviceError. */
HttpReply *RestResource::createDeviceErrorReply(const HttpReply::HttpStatusCode &statusCode, const Device::DeviceError &deviceError)
{
HttpReply *reply = new HttpReply(statusCode, HttpReply::TypeSync);
QVariantMap response;
response.insert("error", JsonTypes::deviceErrorToString(deviceError));
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(response).toJson());
return reply;
}
/*! Returns the pointer to a new created \l{HttpReply} with the given \a statusCode and \a ruleError. This method will
* be used to create an error response for the \l{nymeaserver::RulesResource}{RulesResource}. The \a ruleError will be written in the payload of this reply.*/
HttpReply *RestResource::createRuleErrorReply(const HttpReply::HttpStatusCode &statusCode, const RuleEngine::RuleError &ruleError)
{
HttpReply *reply = new HttpReply(statusCode, HttpReply::TypeSync);
QVariantMap response;
response.insert("error", JsonTypes::ruleErrorToString(ruleError));
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(response).toJson());
return reply;
}
/*! Returns the pointer to a new created \l{HttpReply} with the given \a statusCode and \a loggingError. This method will
* be used to create an error response for the \l{nymeaserver::LogsResource}{LogsResource}. The \a loggingError will be written in the payload of this reply.*/
HttpReply *RestResource::createLoggingErrorReply(const HttpReply::HttpStatusCode &statusCode, const Logging::LoggingError &loggingError)
{
HttpReply *reply = new HttpReply(statusCode, HttpReply::TypeSync);
QVariantMap response;
response.insert("error", JsonTypes::loggingErrorToString(loggingError));
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(response).toJson());
return reply;
}
/*! Returns the pointer to a new created \l{HttpReply} initialized with \l{HttpReply::Ok} and \l{HttpReply::TypeAsync}. */
HttpReply *RestResource::createAsyncReply()
{
HttpReply *reply = new HttpReply(HttpReply::Ok, HttpReply::TypeAsync);
reply->setPayload(QByteArray::number(reply->httpStatusCode()) + " " + reply->httpReasonPhrase());
return reply;
}
/*! This method can be used from every \l{RestResource} in order to verify if the \a payload of a
\l{HttpRequest} is a valid JSON document. Returns \tt true and the valid \e QVariant if there
was no error while parsing JSON. Returns \tt false and an invalid \e QVariant if the \a payload
could not be parsed.
*/
QPair<bool, QVariant> RestResource::verifyPayload(const QByteArray &payload)
{
QVariant data;
if (!payload.isEmpty()) {
QJsonParseError error;
QJsonDocument jsonDoc = QJsonDocument::fromJson(payload, &error);
if(error.error != QJsonParseError::NoError) {
qCWarning(dcRest) << "Failed to parse JSON payload" << payload << ":" << error.errorString();
return QPair<bool, QVariant>(false, QVariant());
}
data = jsonDoc.toVariant();
}
return QPair<bool, QVariant>(true, data);
}
HttpReply *RestResource::proccessPostRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
Q_UNUSED(urlTokens)
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *RestResource::proccessOptionsRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
Q_UNUSED(urlTokens)
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *RestResource::proccessPutRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
Q_UNUSED(urlTokens)
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *RestResource::proccessDeleteRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
Q_UNUSED(urlTokens)
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *RestResource::proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
Q_UNUSED(urlTokens)
return createErrorReply(HttpReply::NotImplemented);
}
}

View File

@ -1,70 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef RESTRESOURCE_H
#define RESTRESOURCE_H
#include <QObject>
#include <QPair>
#include "servers/httpreply.h"
#include "servers/httprequest.h"
#include "jsonrpc/jsontypes.h"
class QVariant;
namespace nymeaserver {
class RestResource : public QObject
{
Q_OBJECT
public:
explicit RestResource(QObject *parent = 0);
virtual ~RestResource() = 0;
virtual QString name() const = 0;
virtual HttpReply *proccessRequest(const HttpRequest &request, const QStringList &urlTokens) = 0;
static HttpReply *createSuccessReply();
static HttpReply *createCorsSuccessReply();
static HttpReply *createErrorReply(const HttpReply::HttpStatusCode &statusCode);
static HttpReply *createDeviceErrorReply(const HttpReply::HttpStatusCode &statusCode, const Device::DeviceError &deviceError);
static HttpReply *createRuleErrorReply(const HttpReply::HttpStatusCode &statusCode, const RuleEngine::RuleError &ruleError);
static HttpReply *createLoggingErrorReply(const HttpReply::HttpStatusCode &statusCode, const Logging::LoggingError &loggingError);
static HttpReply *createAsyncReply();
static QPair<bool, QVariant> verifyPayload(const QByteArray &payload);
private:
virtual HttpReply *proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens);
virtual HttpReply *proccessDeleteRequest(const HttpRequest &request, const QStringList &urlTokens);
virtual HttpReply *proccessPutRequest(const HttpRequest &request, const QStringList &urlTokens);
virtual HttpReply *proccessPostRequest(const HttpRequest &request, const QStringList &urlTokens);
virtual HttpReply *proccessOptionsRequest(const HttpRequest &request, const QStringList &urlTokens);
QHash<QPair<HttpRequest::RequestMethod, QString>, QString> m_descriptions;
QHash<QString, QVariantMap> m_params;
QHash<QString, QVariantMap> m_returns;
};
}
#endif // RESTRESOURCE_H

View File

@ -1,180 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class nymeaserver::RestServer
\brief This class provides the REST API interface to the \l{WebServer}{WebServers}.
\ingroup server
\inmodule core
The \l{RestServer} class provides the server interface for a REST API call. The \l{RestServer}
will create a \l{WebServer} object. The \l{WebServer} will parse the \l{HttpRequest} and emits
the signal \l{WebServer::httpRequestReady()}. This signal will be handled by this \l{RestServer}
and processed by the corresponding \l{RestResource}. Once the \l{HttpRequest} is finished, the
\l{RestServer} will send a \l{HttpReply} back to the client using \l{WebServer::sendHttpReply()}.
\sa ServerManager, WebServer, HttpRequest, HttpReply
*/
#include "restserver.h"
#include "loggingcategories.h"
#include "servers/httprequest.h"
#include "servers/httpreply.h"
#include <QJsonDocument>
#include <QSslConfiguration>
namespace nymeaserver {
/*! Constructs a \l{RestServer} with the given \a sslConfiguration and \a parent. */
RestServer::RestServer(const QSslConfiguration &sslConfiguration, QObject *parent) :
QObject(parent)
{
Q_UNUSED(sslConfiguration)
QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection);
}
/*! Register the given \a webServer in this \l{RestServer}.
*
* \sa WebServer
*
*/
void RestServer::registerWebserver(WebServer *webServer)
{
connect(webServer, &WebServer::clientConnected, this, &RestServer::clientConnected);
connect(webServer, &WebServer::clientDisconnected, this, &RestServer::clientDisconnected);
connect(webServer, &WebServer::httpRequestReady, this, &RestServer::processHttpRequest);
}
void RestServer::setup()
{
// Create resources
m_deviceResource = new DevicesResource(this);
m_deviceClassesResource = new DeviceClassesResource(this);
m_vendorsResource = new VendorsResource(this);
m_pluginsResource = new PluginsResource(this);
m_rulesResource = new RulesResource(this);
m_logsResource = new LogsResource(this);
m_resources.insert(m_deviceResource->name(), m_deviceResource);
m_resources.insert(m_deviceClassesResource->name(), m_deviceClassesResource);
m_resources.insert(m_vendorsResource->name(), m_vendorsResource);
m_resources.insert(m_pluginsResource->name(), m_pluginsResource);
m_resources.insert(m_rulesResource->name(), m_rulesResource);
m_resources.insert(m_logsResource->name(), m_logsResource);
}
void RestServer::clientConnected(const QUuid &clientId)
{
WebServer *webserver = dynamic_cast<WebServer*>(sender());
m_clientList.insert(clientId, webserver);
}
void RestServer::clientDisconnected(const QUuid &clientId)
{
m_clientList.take(clientId);
}
void RestServer::processHttpRequest(const QUuid &clientId, const HttpRequest &request)
{
QStringList urlTokens = request.url().path().split("/");
urlTokens.removeAll(QString());
WebServer *webserver = dynamic_cast<WebServer*>(sender());
Q_ASSERT(webserver);
// check token count
if (urlTokens.count() < 3) {
HttpReply *reply = RestResource::createErrorReply(HttpReply::BadRequest);
reply->setClientId(clientId);
webserver->sendHttpReply(reply);
reply->deleteLater();
return;
}
// check resource
QString resourceName = urlTokens.at(2);
if (!m_resources.contains(resourceName)) {
HttpReply *reply = RestResource::createErrorReply(HttpReply::BadRequest);
reply->setClientId(clientId);
webserver->sendHttpReply(reply);
reply->deleteLater();
return;
}
// check CORS call for main resource
if (request.method() == HttpRequest::Options && urlTokens.count() == 3) {
HttpReply *reply = RestResource::createCorsSuccessReply();
reply->setClientId(clientId);
webserver->sendHttpReply(reply);
reply->deleteLater();
return;
}
// process request in corresponding resource
RestResource *resource = m_resources.value(resourceName);
HttpReply *reply = resource->proccessRequest(request, urlTokens);
reply->setClientId(clientId);
if (reply->type() == HttpReply::TypeAsync) {
connect(reply, &HttpReply::finished, this, &RestServer::asyncReplyFinished);
m_asyncReplies.insert(clientId, reply);
reply->startWait();
return;
}
webserver->sendHttpReply(reply);
reply->deleteLater();
}
void RestServer::asyncReplyFinished()
{
HttpReply *reply = qobject_cast<HttpReply*>(sender());
if (!m_asyncReplies.values().contains(reply)) {
qCWarning(dcWebServer()) << "Reply for async request does no longer exist";
reply->deleteLater();
return;
}
QUuid clientId = m_asyncReplies.key(reply);
m_asyncReplies.remove(clientId);
qCDebug(dcWebServer()) << "Async reply finished";
// check if the reply timeouted
if (reply->timedOut()) {
reply->clear();
reply->setHttpStatusCode(HttpReply::GatewayTimeout);
}
// check if client is still connected
if (!m_clientList.contains(clientId)) {
qCWarning(dcWebServer()) << "Client for async reply not longer connected.";
} else {
reply->setClientId(clientId);
WebServer *webserver = m_clientList.value(clientId);
webserver->sendHttpReply(reply);
}
reply->deleteLater();
}
}

View File

@ -1,75 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef RESTSERVER_H
#define RESTSERVER_H
#include <QObject>
#include "servers/webserver.h"
#include "jsonrpc/jsonhandler.h"
#include "devicesresource.h"
#include "deviceclassesresource.h"
#include "vendorsresource.h"
#include "pluginsresource.h"
#include "rulesresource.h"
#include "logsresource.h"
class QSslConfiguration;
namespace nymeaserver {
class HttpRequest;
class HttpReply;
class RestServer : public QObject
{
Q_OBJECT
public:
explicit RestServer(const QSslConfiguration &sslConfiguration = QSslConfiguration(), QObject *parent = 0);
void registerWebserver(WebServer *webServer);
private:
QHash<QUuid, WebServer*> m_clientList;
QHash<QString, RestResource *> m_resources;
QHash<QUuid, HttpReply *> m_asyncReplies;
DevicesResource *m_deviceResource;
DeviceClassesResource *m_deviceClassesResource;
VendorsResource *m_vendorsResource;
PluginsResource *m_pluginsResource;
RulesResource *m_rulesResource;
LogsResource *m_logsResource;
private slots:
void setup();
void clientConnected(const QUuid &clientId);
void clientDisconnected(const QUuid &clientId);
void processHttpRequest(const QUuid &clientId, const HttpRequest &request);
void asyncReplyFinished();
};
}
#endif // RESTSERVER_H

View File

@ -1,329 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class nymeaserver::RulesResource
\brief This subclass of \l{RestResource} processes the REST requests for the \tt Rules namespace.
\ingroup json
\inmodule core
This \l{RestResource} will be created in the \l{RestServer} and used to handle REST requests
for the \tt {Rules} namespace of the API.
\code
http://localhost:3333/api/v1/rules
\endcode
\sa Rule, RestResource, RestServer
*/
#include "rulesresource.h"
#include "servers/httprequest.h"
#include "typeutils.h"
#include "loggingcategories.h"
#include "nymeacore.h"
#include <QJsonDocument>
namespace nymeaserver {
/*! Constructs a \l RulesResource with the given \a parent. */
RulesResource::RulesResource(QObject *parent) :
RestResource(parent)
{
}
/*! Returns the name of the \l{RestResource}. In this case \b rules.
\sa RestResource::name()
*/
QString RulesResource::name() const
{
return "rules";
}
/*! This method will be used to process the given \a request and the given \a urlTokens. The request
has to be in this namespace. Returns the resulting \l HttpReply.
\sa HttpRequest, HttpReply, RestResource::proccessRequest()
*/
HttpReply *RulesResource::proccessRequest(const HttpRequest &request, const QStringList &urlTokens)
{
// /api/v1/rules/{ruleId}/
if (urlTokens.count() >= 4) {
m_ruleId = RuleId(urlTokens.at(3));
if (m_ruleId.isNull()) {
qCWarning(dcRest) << "Could not parse RuleId:" << urlTokens.at(3);
return createRuleErrorReply(HttpReply::BadRequest, RuleEngine::RuleErrorRuleNotFound);
}
if (!NymeaCore::instance()->ruleEngine()->findRule(m_ruleId).isValid()) {
qCWarning(dcRest) << "Could not find rule with id" << m_ruleId.toString();
return createRuleErrorReply(HttpReply::NotFound, RuleEngine::RuleErrorRuleNotFound);
}
}
// check method
HttpReply *reply;
switch (request.method()) {
case HttpRequest::Get:
reply = proccessGetRequest(request, urlTokens);
break;
case HttpRequest::Put:
reply = proccessPutRequest(request, urlTokens);
break;
case HttpRequest::Post:
reply = proccessPostRequest(request, urlTokens);
break;
case HttpRequest::Delete:
reply = proccessDeleteRequest(request, urlTokens);
break;
case HttpRequest::Options:
reply = proccessOptionsRequest(request, urlTokens);
break;
default:
reply = createErrorReply(HttpReply::BadRequest);
break;
}
return reply;
}
HttpReply *RulesResource::proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
// GET /api/v1/rules
if (urlTokens.count() == 3) {
// check if we should filter for rules containing a certain device
DeviceId deviceId;
if (request.url().hasQuery() && request.urlQuery().hasQueryItem("deviceId")) {
deviceId = DeviceId(request.urlQuery().queryItemValue("deviceId"));
if (deviceId.isNull())
return createRuleErrorReply(HttpReply::NotFound, RuleEngine::RuleErrorDeviceNotFound);
}
return getRules(deviceId);
}
// GET /api/v1/rules/{ruleId}
if (urlTokens.count() == 4)
return getRuleDetails(m_ruleId);
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *RulesResource::proccessDeleteRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
// DELETE /api/v1/rules
if (urlTokens.count() == 3)
return createErrorReply(HttpReply::BadRequest);
// DELETE /api/v1/rules/{ruleId}
if (urlTokens.count() == 4)
return removeRule(m_ruleId);
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *RulesResource::proccessPutRequest(const HttpRequest &request, const QStringList &urlTokens)
{
// PUT /api/v1/rules
if (urlTokens.count() == 3)
return createErrorReply(HttpReply::BadRequest);
// PUT /api/v1/rules/{ruleId}
if (urlTokens.count() == 4)
return editRule(m_ruleId, request.payload());
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *RulesResource::proccessPostRequest(const HttpRequest &request, const QStringList &urlTokens)
{
// POST /api/v1/rules
if (urlTokens.count() == 3)
return addRule(request.payload());
// POST /api/v1/rules/{ruleId}/enable
if (urlTokens.count() == 5 && urlTokens.at(4) == "enable")
return enableRule(m_ruleId);
// POST /api/v1/rules/{ruleId}/disable
if (urlTokens.count() == 5 && urlTokens.at(4) == "disable")
return disableRule(m_ruleId);
// POST /api/v1/rules/{ruleId}/executeactions
if (urlTokens.count() == 5 && urlTokens.at(4) == "executeactions")
return executeActions(m_ruleId);
// POST /api/v1/rules/{ruleId}/executeexitactions
if (urlTokens.count() == 5 && urlTokens.at(4) == "executeexitactions")
return executeExitActions(m_ruleId);
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *RulesResource::proccessOptionsRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
Q_UNUSED(urlTokens)
return RestResource::createCorsSuccessReply();
}
HttpReply *RulesResource::getRules(const DeviceId &deviceId) const
{
HttpReply *reply = createSuccessReply();
if (deviceId.isNull()) {
qCDebug(dcRest) << "Get rule descriptions";
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packRuleDescriptions()).toJson());
} else {
qCDebug(dcRest) << "Get rule descriptions which contain the device with id" << deviceId.toString();
QList<RuleId> ruleIdsList = NymeaCore::instance()->ruleEngine()->findRules(deviceId);
QList<Rule> ruleList;
foreach (const RuleId &ruleId, ruleIdsList) {
Rule rule = NymeaCore::instance()->ruleEngine()->findRule(ruleId);
if (rule.isValid())
ruleList.append(rule);
}
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packRuleDescriptions(ruleList)).toJson());
}
return reply;
}
HttpReply *RulesResource::getRuleDetails(const RuleId &ruleId) const
{
qCDebug(dcRest) << "Get rule details";
// Note: rule existence already checked
Rule rule = NymeaCore::instance()->ruleEngine()->findRule(ruleId);
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packRule(rule)).toJson());
return reply;
}
HttpReply *RulesResource::removeRule(const RuleId &ruleId) const
{
qCDebug(dcRest) << "Remove rule with id" << ruleId.toString();
RuleEngine::RuleError status = NymeaCore::instance()->removeRule(ruleId);
if (status != RuleEngine::RuleErrorNoError)
return createRuleErrorReply(HttpReply::InternalServerError, status);
return createRuleErrorReply(HttpReply::Ok, status);
}
HttpReply *RulesResource::addRule(const QByteArray &payload) const
{
qCDebug(dcRest) << "Add new rule";
QPair<bool, QVariant> verification = RestResource::verifyPayload(payload);
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
QVariantMap params = verification.second.toMap();
Rule rule = JsonTypes::unpackRule(params);
rule.setId(RuleId::createRuleId());
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->addRule(rule);
if (status == RuleEngine::RuleErrorNoError) {
QVariant returns = JsonTypes::packRule(NymeaCore::instance()->ruleEngine()->findRule(rule.id()));
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(returns).toJson());
return reply;
}
return createRuleErrorReply(HttpReply::BadRequest, status);
}
HttpReply *RulesResource::enableRule(const RuleId &ruleId) const
{
qCDebug(dcRest) << "Enable rule with id" << ruleId.toString();
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->enableRule(ruleId);
if (status != RuleEngine::RuleErrorNoError)
return createRuleErrorReply(HttpReply::InternalServerError, status);
return createRuleErrorReply(HttpReply::Ok, status);
}
HttpReply *RulesResource::disableRule(const RuleId &ruleId) const
{
qCDebug(dcRest) << "Disable rule with id" << ruleId.toString();
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->disableRule(ruleId);
if (status != RuleEngine::RuleErrorNoError)
return createRuleErrorReply(HttpReply::InternalServerError, status);
return createRuleErrorReply(HttpReply::Ok, status);
}
HttpReply *RulesResource::executeActions(const RuleId &ruleId) const
{
qCDebug(dcRest) << "Execute actions of rule with id" << ruleId.toString();
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->executeActions(ruleId);
if (status != RuleEngine::RuleErrorNoError)
return createRuleErrorReply(HttpReply::InternalServerError, status);
return createRuleErrorReply(HttpReply::Ok, status);
}
HttpReply *RulesResource::executeExitActions(const RuleId &ruleId) const
{
qCDebug(dcRest) << "Execute exit actions of rule with id" << ruleId.toString();
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->executeExitActions(ruleId);
if (status != RuleEngine::RuleErrorNoError)
return createRuleErrorReply(HttpReply::InternalServerError, status);
return createRuleErrorReply(HttpReply::Ok, status);
}
HttpReply *RulesResource::editRule(const RuleId &ruleId, const QByteArray &payload) const
{
qCDebug(dcRest) << "Edit rule with id" << ruleId.toString();
QPair<bool, QVariant> verification = RestResource::verifyPayload(payload);
if (!verification.first)
return createErrorReply(HttpReply::BadRequest);
QVariantMap params = verification.second.toMap();
Rule rule = JsonTypes::unpackRule(params);
RuleEngine::RuleError status = NymeaCore::instance()->ruleEngine()->editRule(rule);
if (status == RuleEngine::RuleErrorNoError) {
qCDebug(dcRest) << "Edit rule successfully finished";
return createRuleErrorReply(HttpReply::Ok, status);
}
qCWarning(dcRest) << "Edit rule finished with error" << JsonTypes::ruleErrorToString(status);
return createRuleErrorReply(HttpReply::BadRequest, status);
}
}

View File

@ -1,78 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef RULESRESOURCE_H
#define RULESRESOURCE_H
#include <QObject>
#include <QHash>
#include "jsonrpc/jsontypes.h"
#include "restresource.h"
#include "servers/httpreply.h"
namespace nymeaserver {
class HttpRequest;
class RulesResource : public RestResource
{
Q_OBJECT
public:
explicit RulesResource(QObject *parent = 0);
QString name() const override;
HttpReply *proccessRequest(const HttpRequest &request, const QStringList &urlTokens) override;
private:
RuleId m_ruleId;
// Process method
HttpReply *proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens) override;
HttpReply *proccessDeleteRequest(const HttpRequest &request, const QStringList &urlTokens) override;
HttpReply *proccessPutRequest(const HttpRequest &request, const QStringList &urlTokens) override;
HttpReply *proccessPostRequest(const HttpRequest &request, const QStringList &urlTokens) override;
HttpReply *proccessOptionsRequest(const HttpRequest &request, const QStringList &urlTokens) override;
// Get methods
HttpReply *getRules(const DeviceId &deviceId) const;
HttpReply *getRuleDetails(const RuleId &ruleId) const;
// Delete methods
HttpReply *removeRule(const RuleId &ruleId) const;
// Post methods
HttpReply *addRule(const QByteArray &payload) const;
HttpReply *enableRule(const RuleId &ruleId) const;
HttpReply *disableRule(const RuleId &ruleId) const;
HttpReply *executeActions(const RuleId &ruleId) const;
HttpReply *executeExitActions(const RuleId &ruleId) const;
// Put methods
HttpReply *editRule(const RuleId &ruleId, const QByteArray &payload) const;
};
}
#endif // RULESRESOURCE_H

View File

@ -1,136 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*!
\class nymeaserver::VendorsResource
\brief This subclass of \l{RestResource} processes the REST requests for the \tt Vendors namespace.
\ingroup json
\inmodule core
This \l{RestResource} will be created in the \l{RestServer} and used to handle REST requests
for the \tt {Vendors} namespace of the API.
\code
http://localhost:3333/api/v1/vendors
\endcode
\sa Vendor, RestResource, RestServer
*/
#include "vendorsresource.h"
#include "servers/httprequest.h"
#include "loggingcategories.h"
#include "nymeacore.h"
#include <QJsonDocument>
namespace nymeaserver {
/*! Constructs a \l VendorsResource with the given \a parent. */
VendorsResource::VendorsResource(QObject *parent) :
RestResource(parent)
{
}
/*! Returns the name of the \l{RestResource}. In this case \b vendors.
\sa RestResource::name()
*/
QString VendorsResource::name() const
{
return "vendors";
}
/*! This method will be used to process the given \a request and the given \a urlTokens. The request
has to be in this namespace. Returns the resulting \l HttpReply.
\sa HttpRequest, HttpReply, RestResource::proccessRequest()
*/
HttpReply *VendorsResource::proccessRequest(const HttpRequest &request, const QStringList &urlTokens)
{
// /api/v1/vendors/{vendorId}/
if (urlTokens.count() >= 4) {
m_vendorId = VendorId(urlTokens.at(3));
if (m_vendorId.isNull()) {
qCWarning(dcRest) << "Could not parse VendorId:" << urlTokens.at(3);
return createDeviceErrorReply(HttpReply::BadRequest, Device::DeviceErrorVendorNotFound);
}
}
// check method
HttpReply *reply;
switch (request.method()) {
case HttpRequest::Get:
reply = proccessGetRequest(request, urlTokens);
break;
default:
reply = createErrorReply(HttpReply::BadRequest);
break;
}
return reply;
}
HttpReply *VendorsResource::proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens)
{
Q_UNUSED(request)
// GET /api/v1/vendors
if (urlTokens.count() == 3)
return getVendors();
// GET /api/v1/vendors/{vendorId}
if (urlTokens.count() == 4)
return getVendor(m_vendorId);
return createErrorReply(HttpReply::NotImplemented);
}
HttpReply *VendorsResource::getVendors() const
{
qCDebug(dcRest) << "Get vendors";
HttpReply *reply = createSuccessReply();
QVariantList vendorsList;
foreach (const Vendor &vendor, NymeaCore::instance()->deviceManager()->supportedVendors()) {
vendorsList.append(JsonTypes::packVendor(vendor, NymeaCore::instance()->configuration()->locale()));
}
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(vendorsList).toJson());
return reply;
}
HttpReply *VendorsResource::getVendor(const VendorId &vendorId) const
{
qCDebug(dcRest) << "Get vendor with id" << vendorId;
foreach (const Vendor &vendor, NymeaCore::instance()->deviceManager()->supportedVendors()) {
if (vendor.id() == vendorId) {
HttpReply *reply = createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "application/json; charset=\"utf-8\";");
reply->setPayload(QJsonDocument::fromVariant(JsonTypes::packVendor(vendor, NymeaCore::instance()->configuration()->locale())).toJson());
return reply;
}
}
return createDeviceErrorReply(HttpReply::NotFound, Device::DeviceErrorVendorNotFound);
}
}

View File

@ -1,59 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
* nymea 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef VENDORSRESOURCE_H
#define VENDORSRESOURCE_H
#include <QObject>
#include <QHash>
#include "jsonrpc/jsontypes.h"
#include "restresource.h"
#include "servers/httpreply.h"
namespace nymeaserver {
class HttpRequest;
class VendorsResource : public RestResource
{
Q_OBJECT
public:
explicit VendorsResource(QObject *parent = 0);
QString name() const override;
HttpReply *proccessRequest(const HttpRequest &request, const QStringList &urlTokens) override;
private:
VendorId m_vendorId;
// Process method
HttpReply *proccessGetRequest(const HttpRequest &request, const QStringList &urlTokens) override;
// Get methods
HttpReply *getVendors() const;
HttpReply *getVendor(const VendorId &vendorId) const;
};
}
#endif // VENDORSRESOURCE_H

View File

@ -76,7 +76,6 @@
#include "nymeacore.h"
#include "httpreply.h"
#include "httprequest.h"
#include "rest/restresource.h"
#include "debugserverhandler.h"
#include <QJsonDocument>
@ -150,7 +149,7 @@ bool WebServer::verifyFile(QSslSocket *socket, const QString &fileName)
// make sure the file exists
if (!file.exists()) {
qCWarning(dcWebServer()) << "requested file" << file.filePath() << "does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setClientId(m_clientList.key(socket));
sendHttpReply(reply);
reply->deleteLater();
@ -160,7 +159,7 @@ bool WebServer::verifyFile(QSslSocket *socket, const QString &fileName)
// make sure the file is in the public directory
if (!file.canonicalFilePath().startsWith(QDir(m_configuration.publicFolder).canonicalPath())) {
qCWarning(dcWebServer()) << "Requested file" << file.fileName() << "is outside the public folder.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::Forbidden);
reply->setClientId(m_clientList.key(socket));
sendHttpReply(reply);
reply->deleteLater();
@ -170,7 +169,7 @@ bool WebServer::verifyFile(QSslSocket *socket, const QString &fileName)
// make sure we can read the file
if (!file.isReadable()) {
qCWarning(dcWebServer()) << "Requested file" << file.fileName() << "is not readable.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::Forbidden);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::Forbidden);
reply->setClientId(m_clientList.key(socket));
reply->setPayload("403 Forbidden. File not readable");
sendHttpReply(reply);
@ -195,7 +194,7 @@ QString WebServer::fileName(const QString &query)
HttpReply *WebServer::processIconRequest(const QString &fileName)
{
if (!fileName.endsWith(".png"))
return RestResource::createErrorReply(HttpReply::NotFound);
return HttpReply::createErrorReply(HttpReply::NotFound);
QByteArray imageData;
@ -205,13 +204,13 @@ HttpReply *WebServer::processIconRequest(const QString &fileName)
image.save(&buffer, "png");
if (!imageData.isEmpty()) {
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "image/png");
reply->setPayload(imageData);
return reply;
}
return RestResource::createErrorReply(HttpReply::NotFound);
return HttpReply::createErrorReply(HttpReply::NotFound);
}
void WebServer::incomingConnection(qintptr socketDescriptor)
@ -309,7 +308,7 @@ void WebServer::readClient()
// Check if the request is valid
if (!request.isValid()) {
qCWarning(dcWebServer()) << "Got invalid request:" << request.url().path();
HttpReply *reply = RestResource::createErrorReply(HttpReply::BadRequest);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::BadRequest);
reply->setClientId(clientId);
sendHttpReply(reply);
reply->deleteLater();
@ -319,7 +318,7 @@ void WebServer::readClient()
// Check HTTP version
if (request.httpVersion() != "HTTP/1.1" && request.httpVersion() != "HTTP/1.0") {
qCWarning(dcWebServer()) << "HTTP version is not supported." << request.httpVersion();
HttpReply *reply = RestResource::createErrorReply(HttpReply::HttpVersionNotSupported);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::HttpVersionNotSupported);
reply->setClientId(clientId);
sendHttpReply(reply);
reply->deleteLater();
@ -338,7 +337,7 @@ void WebServer::readClient()
// Verify method
if (request.method() == HttpRequest::Unhandled) {
HttpReply *reply = RestResource::createErrorReply(HttpReply::MethodNotAllowed);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::MethodNotAllowed);
reply->setClientId(clientId);
reply->setHeader(HttpReply::AllowHeader, "GET, PUT, POST, DELETE, OPTIONS");
sendHttpReply(reply);
@ -346,21 +345,6 @@ void WebServer::readClient()
return;
}
// Verify API query
if (request.url().path().startsWith("/api/v1")) {
if (m_configuration.restServerEnabled) {
emit httpRequestReady(clientId, request);
return;
} else {
qCWarning(dcWebServer()) << "The REST server is disabled. You can enable it by adding \'restServerEnabled=true\' in the WebServer section of the nymead.conf file.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
reply->setClientId(clientId);
sendHttpReply(reply);
reply->deleteLater();
return;
}
}
// Check icon call
if (request.url().path().startsWith("/icons/") && request.method() == HttpRequest::Get) {
HttpReply *reply = processIconRequest(request.url().path());
@ -376,7 +360,7 @@ void WebServer::readClient()
if (NymeaCore::instance()->configuration()->debugServerEnabled()) {
// Verify methods
if (request.method() != HttpRequest::Get && request.method() != HttpRequest::Options) {
HttpReply *reply = RestResource::createErrorReply(HttpReply::MethodNotAllowed);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::MethodNotAllowed);
reply->setClientId(clientId);
reply->setHeader(HttpReply::AllowHeader, "GET, OPTIONS");
sendHttpReply(reply);
@ -399,7 +383,7 @@ void WebServer::readClient()
return;
} else {
qCWarning(dcWebServer()) << "The debug server handler is disabled. You can enable it by adding \'debugServerEnabled=true\' in the \'nymead\' section of the nymead.conf file.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setClientId(clientId);
sendHttpReply(reply);
reply->deleteLater();
@ -410,7 +394,7 @@ void WebServer::readClient()
// Check server.xml call
if (request.url().path() == "/server.xml" && request.method() == HttpRequest::Get) {
qCDebug(dcWebServer()) << "Server XML request call";
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
reply->setHeader(HttpReply::ContentTypeHeader, "text/xml");
reply->setPayload(createServerXmlDocument(socket->localAddress()));
reply->setClientId(clientId);
@ -426,7 +410,7 @@ void WebServer::readClient()
// FIXME: return a default webpage containing server information
if (!QDir(m_configuration.publicFolder).exists()) {
qCWarning(dcWebServer()) << "Webinterface folder" << m_configuration.publicFolder << "does not exist.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotFound);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotFound);
reply->setClientId(clientId);
sendHttpReply(reply);
reply->deleteLater();
@ -440,7 +424,7 @@ void WebServer::readClient()
QFile file(path);
if (file.open(QFile::ReadOnly | QFile::Truncate)) {
qCDebug(dcWebServer()) << "Load file" << file.fileName();
HttpReply *reply = RestResource::createSuccessReply();
HttpReply *reply = HttpReply::createSuccessReply();
// Check content type
if (file.fileName().endsWith(".html")) {
@ -477,7 +461,7 @@ void WebServer::readClient()
// Reject everything else...
qCWarning(dcWebServer()) << "Unknown message received.";
HttpReply *reply = RestResource::createErrorReply(HttpReply::NotImplemented);
HttpReply *reply = HttpReply::createErrorReply(HttpReply::NotImplemented);
reply->setClientId(clientId);
sendHttpReply(reply);
reply->deleteLater();

View File

@ -9,16 +9,10 @@ SUBDIRS = versioning \
rules \
plugins \
webserver \
restdevices \
restdeviceclasses \
restplugins \
restvendors \
restrules \
websocketserver \
logging \
loggingdirect \
loggingloading \
restlogging \
#coap \ # temporary removed until fixed
configurations \
timemanager \

View File

@ -1,5 +0,0 @@
include(../../../nymea.pri)
include(../autotests.pri)
TARGET = restdeviceclasses
SOURCES += testrestdeviceclasses.cpp

View File

@ -1,338 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
**
* 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "nymeatestbase.h"
#include "nymeacore.h"
using namespace nymeaserver;
class TestRestDeviceClasses: public NymeaTestBase
{
Q_OBJECT
private slots:
void initTestCase();
void getSupportedDevices();
void invalidMethod();
void getActionTypes_data();
void getActionTypes();
void getStateTypes_data();
void getStateTypes();
void getEventTypes_data();
void getEventTypes();
void discoverDevices_data();
void discoverDevices();
};
#include "testrestdeviceclasses.moc"
void TestRestDeviceClasses::initTestCase()
{
NymeaTestBase::initTestCase();
QLoggingCategory::setFilterRules("*.debug=false\n"
"Tests.debug=true\n"
"Rest.debug=true\n"
"WebServer.debug=true\n"
"MockDevice.debug=true");
foreach (const WebServerConfiguration &config, NymeaCore::instance()->configuration()->webServerConfigurations()) {
if (config.port == 3333 && (config.address == QHostAddress("127.0.0.1") || config.address == QHostAddress("0.0.0.0"))) {
qDebug() << "Already have a webserver listening on 127.0.0.1:3333";
return;
}
}
qDebug() << "Creating new webserver instance on 127.0.0.1:3333";
WebServerConfiguration config;
config.address = QHostAddress("127.0.0.1");
config.port = 3333;
config.sslEnabled = true;
config.restServerEnabled = true;
NymeaCore::instance()->configuration()->setWebServerConfiguration(config);
qApp->processEvents();
}
void TestRestDeviceClasses::getSupportedDevices()
{
// Get all deviceclasses
QUrl url("https://localhost:3333/api/v1/deviceclasses");
QVariant response = getAndWait(QNetworkRequest(url));
QVariantList deviceClassesList = response.toList();
QVERIFY2(deviceClassesList.count() > 0, "Not enough deviceclasses.");
// Get each of thouse devices individualy
foreach (const QVariant &deviceClass, deviceClassesList) {
QVariantMap deviceClassMap = deviceClass.toMap();
QNetworkRequest request;
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1").arg(deviceClassMap.value("id").toString())));
response = getAndWait(request);
QVERIFY2(!response.isNull(), "Could not get device");
}
// get with vendor filter
QUrlQuery query;
query.addQueryItem("vendorId", guhVendorId.toString());
url.setQuery(query);
response = getAndWait(QNetworkRequest(url));
deviceClassesList = response.toList();
QVERIFY2(deviceClassesList.count() > 0, "Not enough deviceclasses.");
// get with invalid vendor filter
query.clear();
query.addQueryItem("vendorId", "uuid");
url.setQuery(query);
response = getAndWait(QNetworkRequest(url), 400);
QCOMPARE(JsonTypes::deviceErrorToString(Device::DeviceErrorVendorNotFound), response.toMap().value("error").toString());
}
void TestRestDeviceClasses::invalidMethod()
{
QNetworkAccessManager nam;
connect(&nam, &QNetworkAccessManager::sslErrors, [this, &nam](QNetworkReply *reply, const QList<QSslError> &) {
reply->ignoreSslErrors();
});
QSignalSpy clientSpy(&nam, SIGNAL(finished(QNetworkReply*)));
QNetworkRequest request;
request.setUrl(QUrl("https://localhost:3333/api/v1/deviceclasses/"));
QNetworkReply *reply = nam.post(request, QByteArray());
clientSpy.wait();
QVERIFY2(clientSpy.count() != 0, "expected at least 1 response from webserver");
bool ok = false;
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
QVERIFY2(ok, "Could not convert statuscode from response to int");
QCOMPARE(statusCode, 400);
reply->deleteLater();
}
void TestRestDeviceClasses::getActionTypes_data()
{
QTest::addColumn<QString>("deviceClassId");
QTest::addColumn<QString>("actionTypeId");
QTest::addColumn<int>("expectedStatusCode");
QTest::addColumn<Device::DeviceError>("error");
QTest::newRow("all ActionTypes") << mockDeviceClassId.toString() << QString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("ActionType async") << mockDeviceClassId.toString() << mockAsyncActionTypeId.toString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("ActionType no params") << mockDeviceClassId.toString() << mockWithoutParamsActionTypeId.toString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("ActionType failing") << mockDeviceClassId.toString() << mockFailingActionTypeId.toString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("ActionType with params") << mockDeviceClassId.toString() << mockWithParamsActionTypeId.toString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("invalid DeviceClassId") << DeviceClassId::createDeviceClassId().toString() << mockWithoutParamsActionTypeId.toString() << 404 << Device::DeviceErrorDeviceClassNotFound;
QTest::newRow("invalid ActionTypeId") << mockDeviceClassId.toString() << ActionTypeId::createActionTypeId().toString() << 404 << Device::DeviceErrorActionTypeNotFound;
QTest::newRow("invalid ActionTypeId format") << mockDeviceClassId.toString() << "uuid" << 400 << Device::DeviceErrorActionTypeNotFound;
QTest::newRow("invalid DeviceClassId format") << "uuid" << "uuid" << 400 << Device::DeviceErrorDeviceClassNotFound;
}
void TestRestDeviceClasses::getActionTypes()
{
QFETCH(QString, deviceClassId);
QFETCH(QString, actionTypeId);
QFETCH(int, expectedStatusCode);
QFETCH(Device::DeviceError, error);
QNetworkRequest request;
if (!actionTypeId.isEmpty()) {
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/actiontypes/%2").arg(deviceClassId).arg(actionTypeId)));
} else {
// Get all actiontypes
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/actiontypes").arg(deviceClassId)));
}
QVariant response = getAndWait(request, expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not read get action type response");
if (expectedStatusCode != 200)
QCOMPARE(JsonTypes::deviceErrorToString(error), response.toMap().value("error").toString());
}
void TestRestDeviceClasses::getStateTypes_data()
{
QTest::addColumn<QString>("deviceClassId");
QTest::addColumn<QString>("stateTypeId");
QTest::addColumn<int>("expectedStatusCode");
QTest::addColumn<Device::DeviceError>("error");
QTest::newRow("all ActionTypes") << mockDeviceClassId.toString() << QString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("StateType bool") << mockDeviceClassId.toString() << mockBoolStateTypeId.toString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("StateType int") << mockDeviceClassId.toString() << mockIntStateTypeId.toString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("invalid DeviceClassId") << DeviceClassId::createDeviceClassId().toString() << mockBoolStateTypeId.toString() << 404 << Device::DeviceErrorDeviceClassNotFound;
QTest::newRow("invalid StateTypeId") << mockDeviceClassId.toString() << StateTypeId::createStateTypeId().toString() << 404 << Device::DeviceErrorStateTypeNotFound;
QTest::newRow("invalid StateTypeId format") << mockDeviceClassId.toString() << "uuid" << 400 << Device::DeviceErrorStateTypeNotFound;
QTest::newRow("invalid DeviceClassId format") << "uuid" << "uuid" << 400 << Device::DeviceErrorDeviceClassNotFound;
}
void TestRestDeviceClasses::getStateTypes()
{
QFETCH(QString, deviceClassId);
QFETCH(QString, stateTypeId);
QFETCH(int, expectedStatusCode);
QFETCH(Device::DeviceError, error);
QNetworkRequest request;
if (!stateTypeId.isEmpty()) {
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/statetypes/%2").arg(deviceClassId).arg(stateTypeId)));
} else {
// Get all actiontypes
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/statetypes").arg(deviceClassId)));
}
QVariant response = getAndWait(request, expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not read get action type response");
if (expectedStatusCode != 200)
QCOMPARE(JsonTypes::deviceErrorToString(error), response.toMap().value("error").toString());
}
void TestRestDeviceClasses::getEventTypes_data()
{
QTest::addColumn<QString>("deviceClassId");
QTest::addColumn<QString>("eventTypeId");
QTest::addColumn<int>("expectedStatusCode");
QTest::addColumn<Device::DeviceError>("error");
QTest::newRow("all ActionTypes") << mockDeviceClassId.toString() << QString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("EventType 1") << mockDeviceClassId.toString() << mockEvent1EventTypeId.toString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("EventType 2") << mockDeviceClassId.toString() << mockEvent2EventTypeId.toString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("invalid DeviceClassId") << DeviceClassId::createDeviceClassId().toString() << mockEvent2EventTypeId.toString() << 404 << Device::DeviceErrorDeviceClassNotFound;
QTest::newRow("invalid EventTypeId") << mockDeviceClassId.toString() << EventTypeId::createEventTypeId().toString() << 404 << Device::DeviceErrorEventTypeNotFound;
QTest::newRow("invalid EventTypeId format") << mockDeviceClassId.toString() << "uuid" << 400 << Device::DeviceErrorEventTypeNotFound;
QTest::newRow("invalid DeviceClassId format") << "uuid" << "uuid" << 400 << Device::DeviceErrorDeviceClassNotFound;
}
void TestRestDeviceClasses::getEventTypes()
{
QFETCH(QString, deviceClassId);
QFETCH(QString, eventTypeId);
QFETCH(int, expectedStatusCode);
QFETCH(Device::DeviceError, error);
QNetworkRequest request;
if (!eventTypeId.isNull()) {
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/eventtypes/%2").arg(deviceClassId).arg(eventTypeId)));
} else {
// Get all actiontypes
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/eventtypes").arg(deviceClassId)));
}
QVariant response = getAndWait(request, expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not read get action type response");
if (expectedStatusCode != 200)
QCOMPARE(JsonTypes::deviceErrorToString(error), response.toMap().value("error").toString());
}
void TestRestDeviceClasses::discoverDevices_data()
{
QTest::addColumn<DeviceClassId>("deviceClassId");
QTest::addColumn<int>("resultCount");
QTest::addColumn<QVariantList>("discoveryParams");
QTest::addColumn<int>("expectedStatusCode");
QTest::addColumn<Device::DeviceError>("error");
QVariantMap resultCountParam;
resultCountParam.insert("paramTypeId", mockDiscoveryResultCountParamTypeId);
resultCountParam.insert("value", 1);
QVariantMap invalidResultCountParam;
invalidResultCountParam.insert("paramTypeId", mockDiscoveryResultCountParamTypeId);
invalidResultCountParam.insert("value", 10);
QVariantList discoveryParams;
discoveryParams.append(resultCountParam);
QVariantList invalidDiscoveryParams;
invalidDiscoveryParams.append(invalidResultCountParam);
QTest::newRow("valid deviceClassId without params") << mockDeviceClassId << 2 << QVariantList() << 200 << Device::DeviceErrorNoError;
QTest::newRow("valid deviceClassId with params") << mockDeviceClassId << 1 << discoveryParams << 200 << Device::DeviceErrorNoError;
QTest::newRow("invalid deviceClassId") << DeviceClassId::createDeviceClassId() << 0 << QVariantList() << 404 << Device::DeviceErrorDeviceClassNotFound;
QTest::newRow("valid deviceClassId with invalid params") << mockDeviceClassId << 10 << invalidDiscoveryParams << 500 << Device::DeviceErrorInvalidParameter;
}
void TestRestDeviceClasses::discoverDevices()
{
QFETCH(DeviceClassId, deviceClassId);
QFETCH(int, resultCount);
QFETCH(QVariantList, discoveryParams);
QFETCH(int, expectedStatusCode);
QFETCH(Device::DeviceError, error);
QVariantMap params;
params.insert("deviceClassId", deviceClassId);
params.insert("discoveryParams", discoveryParams);
// DISCOVER
QUrl url(QString("https://localhost:3333/api/v1/deviceclasses/%1/discover").arg(deviceClassId.toString()));
if (!discoveryParams.isEmpty()) {
QUrlQuery query;
query.addQueryItem("params", QJsonDocument::fromVariant(discoveryParams).toJson(QJsonDocument::Compact));
url.setQuery(query);
}
QVariant response = getAndWait(QNetworkRequest(url), expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not read response");
if (expectedStatusCode != 200) {
QCOMPARE(JsonTypes::deviceErrorToString(error), response.toMap().value("error").toString());
return;
}
// check response
QVariantList foundDevices = response.toList();
QCOMPARE(foundDevices.count(), resultCount);
// ADD the discovered device
QNetworkRequest request(QUrl("https://localhost:3333/api/v1/devices"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
DeviceDescriptorId descriptorId = DeviceDescriptorId(foundDevices.first().toMap().value("id").toString());
params.clear();
params.insert("deviceClassId", deviceClassId);
params.insert("deviceDescriptorId", descriptorId.toString());
response = postAndWait(request, params, expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not read response");
DeviceId deviceId = DeviceId(response.toMap().value("id").toString());
QVERIFY2(!deviceId.isNull(), "got invalid device id");
// REMOVE added device
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
response = deleteAndWait(request);
QVERIFY2(!response.isNull(), "Could not delete device");
}
QTEST_MAIN(TestRestDeviceClasses)

View File

@ -1,5 +0,0 @@
include(../../../nymea.pri)
include(../autotests.pri)
TARGET = restdevices
SOURCES += testrestdevices.cpp

View File

@ -1,805 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
**
* 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "nymeatestbase.h"
#include "nymeacore.h"
using namespace nymeaserver;
class TestRestDevices: public NymeaTestBase
{
Q_OBJECT
private slots:
void initTestCase();
void getConfiguredDevices();
void addConfiguredDevice_data();
void addConfiguredDevice();
void addPushButtonDevices_data();
void addPushButtonDevices();
void addDisplayPinDevices_data();
void addDisplayPinDevices();
void parentChildDevices();
void executeAction_data();
void executeAction();
void getStateValue_data();
void getStateValue();
void editDevices_data();
void editDevices();
void reconfigureDevices_data();
void reconfigureDevices();
void reconfigureByDiscovery_data();
void reconfigureByDiscovery();
};
void TestRestDevices::initTestCase()
{
NymeaTestBase::initTestCase();
QLoggingCategory::setFilterRules("*.debug=false\nTests.debug=true\nMockDevice.debug=true\nRest.debug=true");
foreach (const WebServerConfiguration &config, NymeaCore::instance()->configuration()->webServerConfigurations()) {
if (config.port == 3333 && (config.address == QHostAddress("127.0.0.1") || config.address == QHostAddress("0.0.0.0"))) {
qCWarning(dcTests()) << "Already have a webserver listening on 127.0.0.1:3333";
return;
}
}
qCDebug(dcTests()) << "Creating new webserver instance on 127.0.0.1:3333";
WebServerConfiguration config;
config.address = QHostAddress("127.0.0.1");
config.port = 3333;
config.sslEnabled = true;
config.restServerEnabled = true;
NymeaCore::instance()->configuration()->setWebServerConfiguration(config);
qApp->processEvents();
}
void TestRestDevices::getConfiguredDevices()
{
// Get all devices
QVariant response = getAndWait(QNetworkRequest(QUrl("https://localhost:3333/api/v1/devices")));
QVERIFY2(!response.isNull(), "Could not get device");
QVariantList deviceList = response.toList();
QVERIFY2(deviceList.count() >= 2, "not enough devices.");
// Get each of those devices individualy
foreach (const QVariant &device, deviceList) {
QVariantMap deviceMap = device.toMap();
QNetworkRequest request(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceMap.value("id").toString())));
response = getAndWait(request);
QVERIFY2(!response.isNull(), "Could not get device");
}
}
void TestRestDevices::addConfiguredDevice_data()
{
QTest::addColumn<DeviceClassId>("deviceClassId");
QTest::addColumn<QVariantList>("deviceParams");
QTest::addColumn<int>("expectedStatusCode");
QVariantMap httpportParam;
httpportParam.insert("paramTypeId", mockDeviceHttpportParamTypeId);
httpportParam.insert("value", m_mockDevice1Port - 1);
QVariantMap asyncParam;
asyncParam.insert("paramTypeId", mockDeviceAsyncParamTypeId);
asyncParam.insert("value", true);
QVariantMap notAsyncParam;
notAsyncParam.insert("paramTypeId", mockDeviceAsyncParamTypeId);
notAsyncParam.insert("value", false);
QVariantMap notBrokenParam;
notBrokenParam.insert("paramTypeId", mockDeviceBrokenParamTypeId);
notBrokenParam.insert("value", false);
QVariantMap brokenParam;
brokenParam.insert("paramTypeId", mockDeviceBrokenParamTypeId);
brokenParam.insert("value", true);
QVariantList deviceParams;
deviceParams.clear(); deviceParams << httpportParam << notAsyncParam << notBrokenParam;
QTest::newRow("User, JustAdd") << mockDeviceClassId << deviceParams << 200;
deviceParams.clear(); deviceParams << httpportParam << asyncParam << notBrokenParam;
QTest::newRow("User, JustAdd, Async") << mockDeviceClassId << deviceParams << 200;
QTest::newRow("Invalid DeviceClassId") << DeviceClassId::createDeviceClassId() << deviceParams << 500;
deviceParams.clear(); deviceParams << httpportParam << brokenParam;
QTest::newRow("Setup failure") << mockDeviceClassId << deviceParams << 500;
deviceParams.clear(); deviceParams << httpportParam << asyncParam << brokenParam;
QTest::newRow("Setup failure, Async") << mockDeviceClassId << deviceParams << 500;
QVariantList invalidDeviceParams;
QTest::newRow("User, JustAdd, missing params") << mockDeviceClassId << invalidDeviceParams << 500;
QVariantMap fakeparam;
fakeparam.insert("paramTypeId", ParamTypeId::createParamTypeId());
invalidDeviceParams.append(fakeparam);
QTest::newRow("User, JustAdd, invalid param") << mockDeviceClassId << invalidDeviceParams << 500;
fakeparam.insert("value", "buhuu");
invalidDeviceParams.clear();
invalidDeviceParams.append(fakeparam);
QTest::newRow("User, JustAdd, wrong param") << mockDeviceClassId << invalidDeviceParams << 500;
}
void TestRestDevices::addConfiguredDevice()
{
QFETCH(DeviceClassId, deviceClassId);
QFETCH(QVariantList, deviceParams);
QFETCH(int, expectedStatusCode);
QVariantMap params;
params.insert("deviceClassId", deviceClassId);
params.insert("name", "Mock device");
params.insert("deviceParams", deviceParams);
QNetworkRequest request(QUrl("https://localhost:3333/api/v1/devices"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QVariant response = postAndWait(request, params, expectedStatusCode);
qDebug() << QJsonDocument::fromVariant(response).toJson();
QVERIFY2(!response.isNull(), "Could not add device");
if (expectedStatusCode == 200) {
// remove added device
DeviceId deviceId = DeviceId(response.toMap().value("id").toString());
QVERIFY2(!deviceId.isNull(), "invalid device id for removing");
QNetworkRequest deleteRequest(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
response = deleteAndWait(deleteRequest);
QVERIFY2(!response.isNull(), "Could not delete device");
}
}
void TestRestDevices::addPushButtonDevices_data()
{
QTest::addColumn<DeviceClassId>("deviceClassId");
QTest::addColumn<int>("expectedStatusCode");
QTest::addColumn<bool>("waitForButtonPressed");
QTest::newRow("Valid: Add PushButton device") << mockPushButtonDeviceClassId << 200 << true;
QTest::newRow("Invalid: Add PushButton device (press to early)") << mockPushButtonDeviceClassId << 500 << false;
}
void TestRestDevices::addPushButtonDevices()
{
QFETCH(DeviceClassId, deviceClassId);
QFETCH(int, expectedStatusCode);
QFETCH(bool, waitForButtonPressed);
// Discover device
QVariantList discoveryParams;
QVariantMap resultCountParam;
resultCountParam.insert("paramTypeId", mockPushButtonDiscoveryResultCountParamTypeId);
resultCountParam.insert("value", 1);
discoveryParams.append(resultCountParam);
// Discover
QVariantMap params;
params.insert("deviceClassId", deviceClassId);
params.insert("discoveryParams", discoveryParams);
// create URL
QUrl url(QString("https://localhost:3333/api/v1/deviceclasses/%1/discover").arg(deviceClassId.toString()));
QUrlQuery query;
query.addQueryItem("params", QJsonDocument::fromVariant(discoveryParams).toJson(QJsonDocument::Compact));
url.setQuery(query);
QVariant response = getAndWait(QNetworkRequest(url));
QVariantList foundDevices = response.toList();
QCOMPARE(foundDevices.count(), 1);
DeviceDescriptorId deviceDescriptoId(foundDevices.first().toMap().value("id").toString());
// Pair
params.clear();
params.insert("deviceClassId", deviceClassId.toString());
params.insert("name", "Push button mock device");
params.insert("deviceDescriptorId", deviceDescriptoId);
QNetworkRequest pairRequest(QUrl("https://localhost:3333/api/v1/devices/pair"));
pairRequest.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
response = postAndWait(pairRequest, params);
QVERIFY2(!response.isNull(), "Could not pair device");
PairingTransactionId pairingTransactionId(response.toMap().value("pairingTransactionId").toString());
QString displayMessage = response.toMap().value("displayMessage").toString();
qDebug() << "displayMessage" << displayMessage;
if (waitForButtonPressed)
QTest::qWait(3500);
// Confirm pairing
params.clear();
params.insert("pairingTransactionId", pairingTransactionId.toString());
QNetworkRequest confirmPairingRequest(QUrl("https://localhost:3333/api/v1/devices/confirmpairing"));
confirmPairingRequest.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
response = postAndWait(confirmPairingRequest, params, expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not confirm pairing device");
if (expectedStatusCode == 200) {
// remove added device
DeviceId deviceId = DeviceId(response.toMap().value("id").toString());
QVERIFY2(!deviceId.isNull(), "invalid device id for removing");
QNetworkRequest deleteRequest(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
response = deleteAndWait(deleteRequest);
QVERIFY2(!response.isNull(), "Could not delete device");
}
}
void TestRestDevices::addDisplayPinDevices_data()
{
QTest::addColumn<DeviceClassId>("deviceClassId");
QTest::addColumn<int>("expectedStatusCode");
QTest::addColumn<QString>("secret");
QTest::newRow("Valid: Add DisplayPin device") << mockDisplayPinDeviceClassId << 200 << "243681";
QTest::newRow("Invalid: Add DisplayPin device (wrong pin)") << mockDisplayPinDeviceClassId << 500 << "243682";
}
void TestRestDevices::addDisplayPinDevices()
{
QFETCH(DeviceClassId, deviceClassId);
QFETCH(int, expectedStatusCode);
QFETCH(QString, secret);
// Discover device
QVariantList discoveryParams;
QVariantMap resultCountParam;
resultCountParam.insert("paramTypeId", mockDisplayPinDiscoveryResultCountParamTypeId);
resultCountParam.insert("value", 1);
discoveryParams.append(resultCountParam);
// Discover
QVariantMap params;
params.insert("deviceClassId", deviceClassId);
params.insert("discoveryParams", discoveryParams);
// create URL
QUrl url(QString("https://localhost:3333/api/v1/deviceclasses/%1/discover").arg(deviceClassId.toString()));
QUrlQuery query;
query.addQueryItem("params", QJsonDocument::fromVariant(discoveryParams).toJson(QJsonDocument::Compact));
url.setQuery(query);
QVariant response = getAndWait(QNetworkRequest(url));
QVariantList foundDevices = response.toList();
QCOMPARE(foundDevices.count(), 1);
DeviceDescriptorId deviceDescriptoId(foundDevices.first().toMap().value("id").toString());
// Pair
params.clear();
params.insert("deviceClassId", deviceClassId.toString());
params.insert("name", "Display pin mock device");
params.insert("deviceDescriptorId", deviceDescriptoId);
QNetworkRequest pairRequest(QUrl("https://localhost:3333/api/v1/devices/pair"));
pairRequest.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
response = postAndWait(pairRequest, params);
QVERIFY2(!response.isNull(), "Could not pair device");
PairingTransactionId pairingTransactionId(response.toMap().value("pairingTransactionId").toString());
QString displayMessage = response.toMap().value("displayMessage").toString();
qDebug() << "displayMessage" << displayMessage;
// Confirm pairing
params.clear();
params.insert("pairingTransactionId", pairingTransactionId.toString());
params.insert("secret", secret);
QNetworkRequest confirmPairingRequest(QUrl("https://localhost:3333/api/v1/devices/confirmpairing"));
confirmPairingRequest.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
response = postAndWait(confirmPairingRequest, params, expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not confirm pairing device");
if (expectedStatusCode == 200) {
// remove added device
DeviceId deviceId = DeviceId(response.toMap().value("id").toString());
QVERIFY2(!deviceId.isNull(), "invalid device id for removing");
QNetworkRequest deleteRequest(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
response = deleteAndWait(deleteRequest);
QVERIFY2(!response.isNull(), "Could not delete device");
}
}
void TestRestDevices::parentChildDevices()
{
// Add parent device
QVariantMap params;
params.insert("deviceClassId", mockParentDeviceClassId);
QNetworkRequest request(QUrl(QString("https://localhost:3333/api/v1/devices")));
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
QVariant response = postAndWait(request, params);
QVERIFY2(!response.isNull(), "Could not read add device response");
DeviceId parentDeviceId = DeviceId(response.toMap().value("id").toString());
QVERIFY2(parentDeviceId != DeviceId(), "DeviceId not returned");
// find child device
response = getAndWait(QNetworkRequest(QUrl("https://localhost:3333/api/v1/devices")));
QVariantList deviceList = response.toList();
DeviceId childDeviceId;
foreach (const QVariant deviceVariant, deviceList) {
QVariantMap deviceMap = deviceVariant.toMap();
if (deviceMap.value("deviceClassId").toString() == mockChildDeviceClassId.toString()) {
if (deviceMap.value("parentId") == parentDeviceId.toString()) {
childDeviceId = DeviceId(deviceMap.value("id").toString());
}
}
}
QVERIFY2(!childDeviceId.isNull(), "Could not find child device");
// try to remove child device
QNetworkRequest deleteRequest(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(childDeviceId.toString())));
response = deleteAndWait(deleteRequest, 400);
//QVERIFY2(!response.isNull(), "Could not delete device");
QCOMPARE(JsonTypes::deviceErrorToString(Device::DeviceErrorDeviceIsChild), response.toMap().value("error").toString());
// check if the child device is still there
response = getAndWait(QNetworkRequest(QUrl("https://localhost:3333/api/v1/devices")));
deviceList = response.toList();
bool found = false;
foreach (const QVariant deviceVariant, deviceList) {
QVariantMap deviceMap = deviceVariant.toMap();
if (deviceMap.value("deviceClassId").toString() == mockChildDeviceClassId.toString()) {
if (deviceMap.value("id") == childDeviceId.toString()) {
found = true;
break;
}
}
}
QVERIFY2(found, "Could not find child device.");
// remove the parent device
deleteRequest.setUrl(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(parentDeviceId.toString())));
response = deleteAndWait(deleteRequest);
QVERIFY2(!response.isNull(), "Could not delete device");
// check if the child device is still there
response = getAndWait(QNetworkRequest(QUrl("https://localhost:3333/api/v1/devices")));
deviceList = response.toList();
found = false;
foreach (const QVariant deviceVariant, deviceList) {
QVariantMap deviceMap = deviceVariant.toMap();
if (deviceMap.value("deviceClassId").toString() == mockChildDeviceClassId.toString()) {
if (deviceMap.value("id") == childDeviceId.toString()) {
found = true;
break;
}
}
}
QVERIFY2(!found, "Could not find child device.");
}
void TestRestDevices::executeAction_data()
{
QTest::addColumn<DeviceId>("deviceId");
QTest::addColumn<ActionTypeId>("actionTypeId");
QTest::addColumn<QVariantList>("actionParams");
QTest::addColumn<int>("expectedStatusCode");
QTest::addColumn<Device::DeviceError>("error");
QVariantList params;
QVariantMap param1;
param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
param1.insert("value", 5);
params.append(param1);
QVariantMap param2;
param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId);
param2.insert("value", true);
params.append(param2);
QTest::newRow("valid action") << m_mockDeviceId << mockWithParamsActionTypeId << params << 200 << Device::DeviceErrorNoError;
QTest::newRow("invalid deviceId") << DeviceId::createDeviceId() << mockWithParamsActionTypeId << params << 404 << Device::DeviceErrorDeviceNotFound;
QTest::newRow("invalid actionTypeId") << m_mockDeviceId << ActionTypeId::createActionTypeId() << params << 404 << Device::DeviceErrorActionTypeNotFound;
QTest::newRow("missing params") << m_mockDeviceId << mockWithParamsActionTypeId << QVariantList() << 500 << Device::DeviceErrorMissingParameter;
QTest::newRow("async action") << m_mockDeviceId << mockAsyncActionTypeId << QVariantList() << 200 << Device::DeviceErrorNoError;
QTest::newRow("broken action") << m_mockDeviceId << mockFailingActionTypeId << QVariantList() << 500 << Device::DeviceErrorSetupFailed;
QTest::newRow("async broken action") << m_mockDeviceId << mockAsyncFailingActionTypeId << QVariantList() << 500 << Device::DeviceErrorSetupFailed;
}
void TestRestDevices::executeAction()
{
QFETCH(DeviceId, deviceId);
QFETCH(ActionTypeId, actionTypeId);
QFETCH(QVariantList, actionParams);
QFETCH(int, expectedStatusCode);
QFETCH(Device::DeviceError, error);
// execute action
QVariantMap params;
params.insert("params", actionParams);
QNetworkRequest request(QUrl(QString("https://localhost:3333/api/v1/devices/%1/execute/%2").arg(deviceId.toString()).arg(actionTypeId.toString())));
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
QVariant response = postAndWait(request, params, expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not read execute action response");
QCOMPARE(JsonTypes::deviceErrorToString(error), response.toMap().value("error").toString());
// Fetch action execution history from mock device
QNetworkAccessManager nam;
QSignalSpy spy(&nam, SIGNAL(finished(QNetworkReply*)));
request.setUrl(QUrl(QString("http://localhost:%1/actionhistory").arg(m_mockDevice1Port)));
QNetworkReply *reply = nam.get(request);
spy.wait();
QCOMPARE(spy.count(), 1);
reply->deleteLater();
QByteArray data = reply->readAll();
if (error == Device::DeviceErrorNoError) {
QVERIFY2(actionTypeId == ActionTypeId(data), QString("ActionTypeId mismatch. Got %1, Expected: %2")
.arg(ActionTypeId(data).toString()).arg(actionTypeId.toString()).toLatin1().data());
} else {
QVERIFY2(data.length() == 0, QString("Data is %1, should be empty.").arg(QString(data)).toLatin1().data());
}
// cleanup for the next run
spy.clear();
request.setUrl(QUrl(QString("http://localhost:%1/clearactionhistory").arg(m_mockDevice1Port)));
reply = nam.get(request);
spy.wait();
QCOMPARE(spy.count(), 1);
reply->deleteLater();
spy.clear();
request.setUrl(QUrl(QString("http://localhost:%1/actionhistory").arg(m_mockDevice1Port)));
reply = nam.get(request);
spy.wait();
QCOMPARE(spy.count(), 1);
reply->deleteLater();
data = reply->readAll();
qDebug() << "cleared data:" << data;
}
void TestRestDevices::getStateValue_data()
{
QList<Device*> devices = NymeaCore::instance()->deviceManager()->findConfiguredDevices(mockDeviceClassId);
QVERIFY2(devices.count() > 0, "There needs to be at least one configured Mock Device for this test");
Device *device = devices.first();
QTest::addColumn<QString>("deviceId");
QTest::addColumn<QString>("stateTypeId");
QTest::addColumn<int>("expectedStatusCode");
QTest::addColumn<Device::DeviceError>("error");
QTest::newRow("existing state") << device->id().toString() << mockIntStateTypeId.toString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("all states") << device->id().toString() << QString() << 200 << Device::DeviceErrorNoError;
QTest::newRow("invalid device") << DeviceId::createDeviceId().toString() << mockIntStateTypeId.toString() << 404 << Device::DeviceErrorDeviceNotFound;
QTest::newRow("invalid device id format") << "uuid" << StateTypeId::createStateTypeId().toString() << 400 << Device::DeviceErrorDeviceNotFound;
QTest::newRow("invalid statetype") << device->id().toString() << StateTypeId::createStateTypeId().toString() << 404 << Device::DeviceErrorStateTypeNotFound;
QTest::newRow("invalid statetype format") << device->id().toString() << "uuid" << 400 << Device::DeviceErrorStateTypeNotFound;
}
void TestRestDevices::getStateValue()
{
QFETCH(QString, deviceId);
QFETCH(QString, stateTypeId);
QFETCH(int, expectedStatusCode);
QFETCH(Device::DeviceError, error);
QNetworkRequest request;
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
if (!stateTypeId.isNull()) {
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/devices/%1/states/%2").arg(deviceId).arg(stateTypeId)));
} else {
// Get all states
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/devices/%1/states").arg(deviceId)));
}
QVariant response = getAndWait(request, expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not read get state value response");
if (expectedStatusCode != 200)
QCOMPARE(JsonTypes::deviceErrorToString(error), response.toMap().value("error").toString());
}
void TestRestDevices::editDevices_data()
{
QTest::addColumn<QString>("name");
QTest::newRow("change name") << "New device name";
QTest::newRow("change name") << "Foo device";
QTest::newRow("change name") << "Bar device";
}
void TestRestDevices::editDevices()
{
QFETCH(QString, name);
QString originalName = "Test device";
QVariantList deviceParams;
QVariantMap httpportParam;
httpportParam.insert("paramTypeId", mockDeviceHttpportParamTypeId);
httpportParam.insert("value", m_mockDevice1Port - 2);
deviceParams.append(httpportParam);
QVariantMap params;
params.insert("deviceClassId", mockDeviceClassId.toString());
params.insert("name", originalName);
params.insert("deviceParams", deviceParams);
QNetworkRequest addRequest(QUrl("https://localhost:3333/api/v1/devices"));
addRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QVariant response = postAndWait(addRequest, params);
DeviceId deviceId = DeviceId(response.toMap().value("id").toString());
QVERIFY2(!deviceId.isNull(), "invalid device id");
// edit device
params.clear();
params.insert("name", name);
QNetworkRequest deviceRequest(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
deviceRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
response = postAndWait(deviceRequest, params);
QVERIFY2(response.toMap().value("error").toString() == JsonTypes::deviceErrorToString(Device::DeviceErrorNoError), "Could not edit device name");
// check device name
response = getAndWait(deviceRequest);
QCOMPARE(response.toMap().value("name").toString(), name);
// Remove the device
response = deleteAndWait(deviceRequest);
QVERIFY2(response.toMap().value("error").toString() == JsonTypes::deviceErrorToString(Device::DeviceErrorNoError), "Could not remove device");
}
void TestRestDevices::reconfigureDevices_data()
{
QVariantList asyncChangeDeviceParams;
QVariantMap asyncParamDifferent;
asyncParamDifferent.insert("paramTypeId", mockDeviceAsyncParamTypeId);
asyncParamDifferent.insert("value", true);
asyncChangeDeviceParams.append(asyncParamDifferent);
QVariantList httpportChangeDeviceParams;
QVariantMap httpportParamDifferent;
httpportParamDifferent.insert("paramTypeId", mockDeviceHttpportParamTypeId);
httpportParamDifferent.insert("value", 8895); // if change -> change also newPort in reconfigureDevices()
httpportChangeDeviceParams.append(httpportParamDifferent);
QVariantList brokenChangedDeviceParams;
QVariantMap brokenParamDifferent;
brokenParamDifferent.insert("paramTypeId", mockDeviceBrokenParamTypeId);
brokenParamDifferent.insert("value", true);
brokenChangedDeviceParams.append(brokenParamDifferent);
QVariantList asyncAndPortChangeDeviceParams;
asyncAndPortChangeDeviceParams.append(asyncParamDifferent);
asyncAndPortChangeDeviceParams.append(httpportParamDifferent);
QVariantList changeAllWritableDeviceParams;
changeAllWritableDeviceParams.append(asyncParamDifferent);
changeAllWritableDeviceParams.append(httpportParamDifferent);
QTest::addColumn<bool>("broken");
QTest::addColumn<QVariantList>("newDeviceParams");
QTest::addColumn<int>("expectedStatusCode");
QTest::addColumn<Device::DeviceError>("error");
QTest::newRow("invalid - change async param") << false << asyncChangeDeviceParams << 500 << Device::DeviceErrorParameterNotWritable;
QTest::newRow("valid - change httpport param") << false << httpportChangeDeviceParams << 200 << Device::DeviceErrorNoError;
QTest::newRow("valid - change httpport and async param") << false << asyncAndPortChangeDeviceParams << 500 << Device::DeviceErrorParameterNotWritable;
QTest::newRow("invalid - change all params (except broken)") << false << changeAllWritableDeviceParams << 500 << Device::DeviceErrorParameterNotWritable;
}
void TestRestDevices::reconfigureDevices()
{
QFETCH(bool, broken);
QFETCH(QVariantList, newDeviceParams);
QFETCH(int, expectedStatusCode);
QFETCH(Device::DeviceError, error);
// add device
QVariantMap params;
params.insert("deviceClassId", mockDeviceClassId);
params.insert("name", "Edit mock device");
QVariantList deviceParams;
QVariantMap asyncParam;
asyncParam.insert("paramTypeId", mockDeviceAsyncParamTypeId);
asyncParam.insert("value", false);
deviceParams.append(asyncParam);
QVariantMap brokenParam;
brokenParam.insert("paramTypeId", mockDeviceBrokenParamTypeId);
brokenParam.insert("value", broken);
deviceParams.append(brokenParam);
QVariantMap httpportParam;
httpportParam.insert("paramTypeId", mockDeviceHttpportParamTypeId);
httpportParam.insert("value", 8896);
deviceParams.append(httpportParam);
params.insert("deviceParams", deviceParams);
// ADD a mockdevice
QNetworkRequest request(QUrl(QString("https://localhost:3333/api/v1/devices")));
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
QVariant response = postAndWait(request, params);
QVERIFY2(!response.isNull(), "Could not read add device response");
DeviceId deviceId = DeviceId(response.toMap().value("id").toString());
QVERIFY2(deviceId != DeviceId(), "DeviceId not returned");
// now RECONFIGURE the added device
QVariantMap editParams;
editParams.insert("deviceId", deviceId);
editParams.insert("deviceParams", newDeviceParams);
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
response = putAndWait(request, editParams, expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not read edit device response");
// if the reconfigure should have been successful
if (expectedStatusCode == 200) {
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
response = getAndWait(request);
QVariantMap deviceMap = response.toMap();
verifyParams(newDeviceParams, deviceMap.value("params").toList(), false);
} else {
QCOMPARE(JsonTypes::deviceErrorToString(error), response.toMap().value("error").toString());
}
// delete it
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
response = deleteAndWait(request);
QVERIFY2(!response.isNull(), "Could not delete device");
}
void TestRestDevices::reconfigureByDiscovery_data()
{
QTest::addColumn<DeviceClassId>("deviceClassId");
QTest::addColumn<int>("resultCount");
QTest::addColumn<QVariantList>("discoveryParams");
QTest::addColumn<int>("expectedStatusCode");
QVariantList discoveryParams;
QVariantMap resultCountParam;
resultCountParam.insert("paramTypeId", mockDiscoveryResultCountParamTypeId);
resultCountParam.insert("value", 2);
discoveryParams.append(resultCountParam);
QTest::newRow("discover 2 devices with params") << mockDeviceClassId << 2 << discoveryParams << 200;
}
void TestRestDevices::reconfigureByDiscovery()
{
QFETCH(DeviceClassId, deviceClassId);
QFETCH(int, resultCount);
QFETCH(QVariantList, discoveryParams);
QFETCH(int, expectedStatusCode);
QVariantMap params;
params.insert("deviceClassId", deviceClassId);
params.insert("discoveryParams", discoveryParams);
QUrl url(QString("https://localhost:3333/api/v1/deviceclasses/%1/discover").arg(deviceClassId.toString()));
if (!discoveryParams.isEmpty()) {
QUrlQuery query;
query.addQueryItem("params", QJsonDocument::fromVariant(discoveryParams).toJson(QJsonDocument::Compact));
url.setQuery(query);
}
QNetworkRequest request(url);
QVariantList foundDevices = getAndWait(request).toList();
QCOMPARE(foundDevices.count(), resultCount);
// add Discovered Device 1 port 55555
request.setUrl(QUrl("https://localhost:3333/api/v1/devices"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
DeviceDescriptorId descriptorId;
foreach (const QVariant &descriptor, foundDevices) {
// find the device with port 55555
if (descriptor.toMap().value("description").toString() == "55555") {
descriptorId = DeviceDescriptorId(descriptor.toMap().value("id").toString());
qDebug() << descriptorId.toString();
break;
}
}
params.clear();
params.insert("deviceClassId", deviceClassId);
params.insert("name", "Discovered mock device");
params.insert("deviceDescriptorId", descriptorId.toString());
QVariant response = postAndWait(request, params, expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not delete device");
DeviceId deviceId = DeviceId(response.toMap().value("id").toString());
QVERIFY(!deviceId.isNull());
// and now rediscover, and edit the first device with the second
params.clear();
params.insert("deviceClassId", deviceClassId);
params.insert("discoveryParams", discoveryParams);
url = QUrl(QString("https://localhost:3333/api/v1/deviceclasses/%1/discover").arg(deviceClassId.toString()));
QUrlQuery query2;
query2.addQueryItem("params", QJsonDocument::fromVariant(discoveryParams).toJson(QJsonDocument::Compact));
url.setQuery(query2);
response = getAndWait(QNetworkRequest(url), expectedStatusCode);
foundDevices = response.toList();
QCOMPARE(foundDevices.count(), resultCount);
// find the already added device
descriptorId = DeviceDescriptorId(); // reset it first
foreach (const QVariant &descriptor, foundDevices) {
if (descriptor.toMap().value("deviceId").toUuid().toString() == deviceId.toString()) {
descriptorId = DeviceDescriptorId(descriptor.toMap().value("id").toString());
break;
}
}
QVERIFY(!descriptorId.isNull());
qDebug() << "edit device 1 (55555) with descriptor 2 (55556) " << descriptorId;
// RECONFIGURE
response.clear();
params.clear();
params.insert("deviceDescriptorId", descriptorId);
// override port param
QVariantMap portParam;
portParam.insert("paramTypeId", mockDeviceHttpportParamTypeId);
portParam.insert("value", "55556");
params.insert("deviceParams", QVariantList() << portParam);
request = QNetworkRequest(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
request.setHeader(QNetworkRequest::ContentTypeHeader, "text/json");
response = putAndWait(request, params, expectedStatusCode);
QVERIFY2(!response.isNull(), "Could not delete device");
// remove added device
request.setUrl(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(deviceId.toString())));
response = deleteAndWait(request);
QVERIFY2(!response.isNull(), "Could not delete device");
}
#include "testrestdevices.moc"
QTEST_MAIN(TestRestDevices)

View File

@ -1,5 +0,0 @@
include(../../../nymea.pri)
include(../autotests.pri)
TARGET = testrestlogging
SOURCES += testrestlogging.cpp

View File

@ -1,379 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
**
* 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "nymeatestbase.h"
#include "nymeacore.h"
#include "servers/mocktcpserver.h"
using namespace nymeaserver;
class TestRestLogging : public NymeaTestBase
{
Q_OBJECT
private:
private slots:
void initTestCase();
void initLogs();
void invalidFilter_data();
void invalidFilter();
void invalidFilterJson();
void eventLogs();
void actionLog();
// this has to be the last test
void removeDevice();
};
void TestRestLogging::initTestCase()
{
NymeaTestBase::initTestCase();
foreach (const WebServerConfiguration &config, NymeaCore::instance()->configuration()->webServerConfigurations()) {
if (config.port == 3333 && (config.address == QHostAddress("127.0.0.1") || config.address == QHostAddress("0.0.0.0"))) {
qDebug() << "Already have a webserver listening on 127.0.0.1:3333";
return;
}
}
qDebug() << "Creating new webserver instance on 127.0.0.1:3333";
WebServerConfiguration config;
config.address = QHostAddress("127.0.0.1");
config.port = 3333;
config.sslEnabled = true;
config.restServerEnabled = true;
NymeaCore::instance()->configuration()->setWebServerConfiguration(config);
qApp->processEvents();
}
void TestRestLogging::initLogs()
{
QNetworkRequest request(QUrl("https://localhost:3333/api/v1/logs"));
QVariant response = getAndWait(request);
QVariantList logEntries = response.toList();
qDebug() << "Got" << logEntries.count() << "logs";
QVERIFY(logEntries.count() > 0);
clearLoggingDatabase();
response = getAndWait(request);
logEntries = response.toList();
qDebug() << "Got" << logEntries.count() << "logs";
QVERIFY(logEntries.count() == 0);
}
void TestRestLogging::invalidFilter_data()
{
QVariantMap invalidSourcesFilter;
invalidSourcesFilter.insert("loggingSources", QVariantList() << "bla");
QVariantMap invalidFilterValue;
invalidFilterValue.insert("loggingSource", QVariantList() << "bla");
QVariantMap invalidTypeIds;
invalidTypeIds.insert("typeId", QVariantList() << "bla" << "blub");
QVariantMap invalidEventTypes;
invalidEventTypes.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger) << "blub");
QTest::addColumn<QVariantMap>("filter");
QTest::newRow("Invalid source") << invalidSourcesFilter;
QTest::newRow("Invalid filter value") << invalidFilterValue;
QTest::newRow("Invalid typeIds") << invalidTypeIds;
QTest::newRow("Invalid eventTypes") << invalidEventTypes;
}
void TestRestLogging::invalidFilter()
{
QFETCH(QVariantMap, filter);
QUrl url("https://localhost:3333/api/v1/logs");
QUrlQuery query;
query.addQueryItem("filter", QJsonDocument::fromVariant(filter).toJson(QJsonDocument::Compact));
url.setQuery(query);
// there should be 2 logs, one for shutdown, one for startup (from server restart)
QVariant response = getAndWait(QNetworkRequest(url));
QVERIFY(!response.isNull());
// TODO: validate filter for REST api
}
void TestRestLogging::invalidFilterJson()
{
QUrl url("https://localhost:3333/api/v1/logs");
QUrlQuery query;
query.addQueryItem("filter", "blabla:!!");
url.setQuery(query);
getAndWait(QNetworkRequest(url), 400);
}
void TestRestLogging::eventLogs()
{
QList<Device*> devices = NymeaCore::instance()->deviceManager()->findConfiguredDevices(mockDeviceClassId);
QVERIFY2(devices.count() > 0, "There needs to be at least one configured Mock Device for this test");
Device *device = devices.first();
enableNotifications({"Logging"});
// Setup connection to mock client
QNetworkAccessManager nam;
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
// trigger event in mock device
int port = device->paramValue(mockDeviceHttpportParamTypeId).toInt();
QNetworkRequest request(QUrl(QString("http://localhost:%1/generateevent?eventtypeid=%2").arg(port).arg(mockEvent1EventTypeId.toString())));
QNetworkReply *reply = nam.get(request);
clientSpy.wait();
// Lets wait for the notification
QVariantList loggEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded");
QVERIFY2(!loggEntryAddedVariants.isEmpty(), "Did not get Logging.LogEntryAdded notification.");
qDebug() << "got" << loggEntryAddedVariants.count() << "Logging.LogEntryAdded notifications";
reply->deleteLater();
bool found = false;
qDebug() << "got" << loggEntryAddedVariants.count() << "Logging.LogEntryAdded";
foreach (const QVariant &loggEntryAddedVariant, loggEntryAddedVariants) {
QVariantMap logEntry = loggEntryAddedVariant.toMap().value("params").toMap().value("logEntry").toMap();
if (logEntry.value("deviceId").toString() == device->id().toString()) {
found = true;
// Make sure the notification contains all the stuff we expect
QCOMPARE(logEntry.value("typeId").toString(), mockEvent1EventTypeId.toString());
QCOMPARE(logEntry.value("eventType").toString(), JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
QCOMPARE(logEntry.value("source").toString(), JsonTypes::loggingSourceToString(Logging::LoggingSourceEvents));
QCOMPARE(logEntry.value("loggingLevel").toString(), JsonTypes::loggingLevelToString(Logging::LoggingLevelInfo));
break;
}
}
if (!found)
qDebug() << QJsonDocument::fromVariant(loggEntryAddedVariants).toJson();
QVERIFY2(found, "Could not find the corresponding Logging.LogEntryAdded notification");
// get this logentry with filter
QVariantMap params;
params.insert("deviceIds", QVariantList() << device->id());
params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceEvents));
params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
params.insert("typeIds", QVariantList() << mockEvent1EventTypeId);
QVariant response = injectAndWait("Logging.GetLogEntries", params);
verifyLoggingError(response);
QVariantList logEntries = response.toMap().value("params").toMap().value("logEntries").toList();
QVERIFY(logEntries.count() == 1);
// disable notifications
QCOMPARE(disableNotifications(), true);
}
void TestRestLogging::actionLog()
{
QVariantList actionParams;
QVariantMap param1;
param1.insert("paramTypeId", mockWithParamsActionParam1ParamTypeId);
param1.insert("value", 7);
actionParams.append(param1);
QVariantMap param2;
param2.insert("paramTypeId", mockWithParamsActionParam2ParamTypeId);
param2.insert("value", true);
actionParams.append(param2);
QVariantMap params;
params.insert("actionTypeId", mockWithParamsActionTypeId);
params.insert("deviceId", m_mockDeviceId);
params.insert("params", actionParams);
// enable notifications
enableNotifications({"Logging"});
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
// EXECUTE with params
QVariant response = injectAndWait("Actions.ExecuteAction", params);
verifyDeviceError(response);
// Lets wait 3for the notification
clientSpy.wait(200);
QVariant notification = checkNotification(clientSpy, "Logging.LogEntryAdded");
QVERIFY(!notification.isNull());
QVariantMap logEntry = notification.toMap().value("params").toMap().value("logEntry").toMap();
// Make sure the notification contains all the stuff we expect
QCOMPARE(logEntry.value("typeId").toString(), mockWithParamsActionTypeId.toString());
QCOMPARE(logEntry.value("deviceId").toString(), m_mockDeviceId.toString());
QCOMPARE(logEntry.value("eventType").toString(), JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
QCOMPARE(logEntry.value("source").toString(), JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
QCOMPARE(logEntry.value("loggingLevel").toString(), JsonTypes::loggingLevelToString(Logging::LoggingLevelInfo));
// EXECUTE without params
params.clear(); clientSpy.clear();
params.insert("actionTypeId", mockWithoutParamsActionTypeId);
params.insert("deviceId", m_mockDeviceId);
response = injectAndWait("Actions.ExecuteAction", params);
verifyDeviceError(response);
clientSpy.wait(200);
notification = checkNotification(clientSpy, "Logging.LogEntryAdded");
QVERIFY(!notification.isNull());
// get this logentry with filter
params.clear();
params.insert("deviceIds", QVariantList() << m_mockDeviceId);
params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
// FIXME: filter for values currently disabled
//params.insert("values", QVariantList() << "7, true");
QUrl url("https://localhost:3333/api/v1/logs");
QUrlQuery query;
query.addQueryItem("filter", QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact));
url.setQuery(query);
response = getAndWait(QNetworkRequest(url));
QVariantList logEntries = response.toList();
QVERIFY(!logEntries.isEmpty());
// EXECUTE broken action
params.clear(); clientSpy.clear();
params.insert("actionTypeId", mockFailingActionTypeId);
params.insert("deviceId", m_mockDeviceId);
response = injectAndWait("Actions.ExecuteAction", params);
verifyDeviceError(response, Device::DeviceErrorSetupFailed);
clientSpy.wait(200);
notification = checkNotification(clientSpy, "Logging.LogEntryAdded");
QVERIFY(!notification.isNull());
logEntry = notification.toMap().value("params").toMap().value("logEntry").toMap();
// Make sure the notification contains all the stuff we expect
QCOMPARE(logEntry.value("typeId").toString(), mockFailingActionTypeId.toString());
QCOMPARE(logEntry.value("deviceId").toString(), m_mockDeviceId.toString());
QCOMPARE(logEntry.value("eventType").toString(), JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
QCOMPARE(logEntry.value("source").toString(), JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
QCOMPARE(logEntry.value("loggingLevel").toString(), JsonTypes::loggingLevelToString(Logging::LoggingLevelAlert));
QCOMPARE(logEntry.value("errorCode").toString(), JsonTypes::deviceErrorToString(Device::DeviceErrorSetupFailed));
// get this logentry with filter
params.clear();
params.insert("deviceIds", QVariantList() << m_mockDeviceId);
params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
// FIXME: filter for values currently disabled
//params.insert("values", QVariantList() << "7, true");
query.clear();
query.addQueryItem("filter", QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact));
url.setQuery(query);
response = getAndWait(QNetworkRequest(url));
logEntries = response.toList();
QVERIFY(!logEntries.isEmpty());
// check different filters
params.clear();
params.insert("deviceIds", QVariantList() << m_mockDeviceId);
params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
params.insert("typeIds", QVariantList() << mockWithoutParamsActionTypeId);
query.clear();
query.addQueryItem("filter", QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact));
url.setQuery(query);
response = getAndWait(QNetworkRequest(url));
logEntries = response.toList();
QVERIFY(!logEntries.isEmpty());
params.clear();
params.insert("deviceIds", QVariantList() << m_mockDeviceId);
params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
params.insert("typeIds", QVariantList() << mockWithoutParamsActionTypeId << mockWithParamsActionTypeId << mockFailingActionTypeId);
query.clear();
query.addQueryItem("filter", QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact));
url.setQuery(query);
response = getAndWait(QNetworkRequest(url));
logEntries = response.toList();
QVERIFY(!logEntries.isEmpty());
// disable notifications
QCOMPARE(disableNotifications(), true);
}
void TestRestLogging::removeDevice()
{
// enable notifications
enableNotifications({"Logging"});
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
QNetworkRequest deleteRequest(QUrl(QString("https://localhost:3333/api/v1/devices/%1").arg(m_mockDeviceId.toString())));
QVariant response = deleteAndWait(deleteRequest);
QVERIFY2(!response.isNull(), "Could not delete device");
clientSpy.wait(200);
QVariant notification = checkNotification(clientSpy, "Logging.LogDatabaseUpdated");
QVERIFY(!notification.isNull());
// get this logentry with filter
QUrl url("https://localhost:3333/api/v1/logs");
response = getAndWait(QNetworkRequest(url));
QVariantList logEntries = response.toList();
QVERIFY2(!logEntries.count() != 0, "No log entries left");
// verify that the logs from this device where removed from the db
QVariantMap params;
params.insert("deviceIds", QVariantList() << m_mockDeviceId);
QUrlQuery query;
query.addQueryItem("filter", QJsonDocument::fromVariant(params).toJson(QJsonDocument::Compact));
url.setQuery(query);
response = getAndWait(QNetworkRequest(url));
logEntries = response.toList();
QCOMPARE(logEntries.count(), 0);
// disable notifications
QCOMPARE(disableNotifications(), true);
}
#include "testrestlogging.moc"
QTEST_MAIN(TestRestLogging)

View File

@ -1,5 +0,0 @@
include(../../../nymea.pri)
include(../autotests.pri)
TARGET = restplugins
SOURCES += testrestplugins.cpp

View File

@ -1,280 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
**
* 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "nymeatestbase.h"
#include "nymeacore.h"
using namespace nymeaserver;
class TestRestPlugins: public NymeaTestBase
{
Q_OBJECT
private slots:
void initTestCase();
void getPlugins();
void invalidMethod();
void invalidPath();
void invalidPlugin_data();
void invalidPlugin();
void getPluginConfiguration();
// void setPluginConfiguration_data();
// void setPluginConfiguration();
};
void TestRestPlugins::initTestCase()
{
NymeaTestBase::initTestCase();
foreach (const WebServerConfiguration &config, NymeaCore::instance()->configuration()->webServerConfigurations()) {
if (config.port == 3333 && (config.address == QHostAddress("127.0.0.1") || config.address == QHostAddress("0.0.0.0"))) {
qDebug() << "Already have a webserver listening on 127.0.0.1:3333";
return;
}
}
qDebug() << "Creating new webserver instance on 127.0.0.1:3333";
WebServerConfiguration config;
config.address = QHostAddress("127.0.0.1");
config.port = 3333;
config.sslEnabled = true;
config.restServerEnabled = true;
NymeaCore::instance()->configuration()->setWebServerConfiguration(config);
qApp->processEvents();
}
void TestRestPlugins::getPlugins()
{
// Get all plugins
QVariant response = getAndWait(QNetworkRequest(QUrl("https://localhost:3333/api/v1/plugins")));
QVariantList pluginList = response.toList();
QVERIFY2(pluginList.count() > 0, "Not enough plugins.");
// Get each of thouse plugins individualy
foreach (const QVariant &plugin, pluginList) {
QVariantMap pluginMap = plugin.toMap();
if (!VendorId(pluginMap.value("id").toString()).isNull()) {
QNetworkRequest request(QUrl(QString("https://localhost:3333/api/v1/plugins/%1").arg(pluginMap.value("id").toString())));
response = getAndWait(request);
QVERIFY2(!response.isNull(), "Could not get plugin");
}
}
}
void TestRestPlugins::invalidMethod()
{
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
connect(nam, &QNetworkAccessManager::sslErrors, [this, nam](QNetworkReply *reply, const QList<QSslError> &) {
reply->ignoreSslErrors();
});
QSignalSpy clientSpy(nam, SIGNAL(finished(QNetworkReply*)));
QNetworkRequest request;
request.setUrl(QUrl("https://localhost:3333/api/v1/plugins"));
QNetworkReply *reply = nam->deleteResource(request);
clientSpy.wait();
QVERIFY2(clientSpy.count() != 0, "expected at least 1 response from webserver");
bool ok = false;
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
QVERIFY2(ok, "Could not convert statuscode from response to int");
QCOMPARE(statusCode, 400);
reply->deleteLater();
nam->deleteLater();
}
void TestRestPlugins::invalidPath()
{
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
connect(nam, &QNetworkAccessManager::sslErrors, [this, nam](QNetworkReply *reply, const QList<QSslError> &) {
reply->ignoreSslErrors();
});
QSignalSpy clientSpy(nam, SIGNAL(finished(QNetworkReply*)));
QNetworkRequest request;
request.setUrl(QUrl("https://localhost:3333/api/v1/plugins/" + QUuid::createUuid().toString() + "/" + QUuid::createUuid().toString()));
QNetworkReply *reply = nam->get(request);
clientSpy.wait();
QVERIFY2(clientSpy.count() != 0, "expected at least 1 response from webserver");
bool ok = false;
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
QVERIFY2(ok, "Could not convert statuscode from response to int");
QCOMPARE(statusCode, 501);
reply->deleteLater();
nam->deleteLater();
}
void TestRestPlugins::invalidPlugin_data()
{
QTest::addColumn<QString>("path");
QTest::addColumn<int>("expectedStatusCode");
QTest::newRow("invalid PluginId") << QUuid::createUuid().toString() << 404;
QTest::newRow("invalid PluginId format") << "uuid" << 400;
}
void TestRestPlugins::invalidPlugin()
{
QFETCH(QString, path);
QFETCH(int, expectedStatusCode);
QNetworkRequest request(QUrl("https://localhost:3333/api/v1/vendors/" + path));
QVariant response = getAndWait(request, expectedStatusCode);
QCOMPARE(JsonTypes::deviceErrorToString(Device::DeviceErrorVendorNotFound), response.toMap().value("error").toString());
}
void TestRestPlugins::getPluginConfiguration()
{
// Get plugin config
QNetworkRequest request(QUrl(QString("https://localhost:3333/api/v1/plugins/%1/configuration").arg(mockPluginId.toString())));
QVariant response = getAndWait(request);
// QVariantList configurations = response.toList();
// QVERIFY2(configurations.count() == 2, "there should be 2 configurations");
}
//void TestRestPlugins::setPluginConfiguration_data()
//{
// QTest::addColumn<PluginId>("pluginId");
// QTest::addColumn<QVariantList>("newConfigurations");
// QTest::addColumn<int>("expectedStatusCode");
// QVariantMap validIntParam;
// validIntParam.insert("name","configParamInt");
// validIntParam.insert("value", 5);
// QVariantMap validBoolParam;
// validBoolParam.insert("name","configParamBool");
// validBoolParam.insert("value", false);
// QVariantMap invalidIntParam;
// invalidIntParam.insert("name","configParamInt");
// invalidIntParam.insert("value", 69);
// QVariantMap invalidIntParam2;
// invalidIntParam2.insert("name","configParamInt");
// invalidIntParam2.insert("value", -1);
// QVariantList validConfigurations;
// validConfigurations.append(validIntParam);
// validConfigurations.append(validBoolParam);
// QVariantList invalidConfigurations;
// invalidConfigurations.append(invalidIntParam);
// QVariantList invalidConfigurations2;
// invalidConfigurations2.append(invalidIntParam2);
//// QTest::newRow("valid plugin configuration") << mockPluginId << validConfigurations << 200;
//// QTest::newRow("invalid plugin id") << PluginId::createPluginId() << validConfigurations << 404;
//// QTest::newRow("invalid plugin configuration") << mockPluginId << invalidConfigurations << 400;
//// QTest::newRow("invalid plugin configuration 2") << mockPluginId << invalidConfigurations2 << 400;
//}
//void TestRestPlugins::setPluginConfiguration()
//{
// QFETCH(PluginId, pluginId);
// QFETCH(QVariantList, newConfigurations);
// QFETCH(int, expectedStatusCode);
// QNetworkAccessManager *nam = new QNetworkAccessManager(this);
// QSignalSpy clientSpy(nam, SIGNAL(finished(QNetworkReply*)));
// // Get plugin configuration
// QNetworkRequest request;
// request.setUrl(QUrl(QString("https://localhost:3333/api/v1/plugins/%1/configuration").arg(pluginId.toString())));
// QNetworkReply *reply = nam->get(request);
// clientSpy.wait();
// QCOMPARE(clientSpy.count(), 1);
// int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// reply->deleteLater();
// if (expectedStatusCode == 404)
// return;
// QByteArray data = reply->readAll();
// reply->deleteLater();
// QJsonParseError error;
// QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &error);
// QCOMPARE(error.error, QJsonParseError::NoError);
// QVariantList originalConfigurations = jsonDoc.toVariant().toList();
// QVERIFY2(originalConfigurations.count() == 2, "there should be 2 configurations");
// // Set new configuration
// clientSpy.clear();
// request.setUrl(QUrl(QString("https://localhost:3333/api/v1/plugins/%1/configuration").arg(pluginId.toString())));
// reply = nam->put(request, QJsonDocument::fromVariant(newConfigurations).toJson(QJsonDocument::Compact));
// clientSpy.wait();
// QCOMPARE(clientSpy.count(), 1);
// statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// QCOMPARE(statusCode, expectedStatusCode);
// reply->deleteLater();
// if (expectedStatusCode != 200)
// return;
// // check new configurations
// clientSpy.clear();
// request.setUrl(QUrl(QString("https://localhost:3333/api/v1/plugins/%1/configuration").arg(pluginId.toString())));
// reply = nam->get(request);
// clientSpy.wait();
// QCOMPARE(clientSpy.count(), 1);
// statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// QCOMPARE(statusCode, expectedStatusCode);
// data = reply->readAll();
// reply->deleteLater();
// jsonDoc = QJsonDocument::fromJson(data, &error);
// QCOMPARE(error.error, QJsonParseError::NoError);
// QVariantList checkConfigurations = jsonDoc.toVariant().toList();
// //QVERIFY2(checkConfigurations.count() == 2, "there should be 2 configurations");
// // verify new configurations
// verifyParams(newConfigurations, checkConfigurations);
// // check new configurations after restart
// clientSpy.clear();
// request.setUrl(QUrl(QString("https://localhost:3333/api/v1/plugins/%1/configuration").arg(pluginId.toString())));
// reply = nam->get(request);
// clientSpy.wait();
// QCOMPARE(clientSpy.count(), 1);
// statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
// QCOMPARE(statusCode, 200);
// data = reply->readAll();
// reply->deleteLater();
// jsonDoc = QJsonDocument::fromJson(data, &error);
// QCOMPARE(error.error, QJsonParseError::NoError);
// checkConfigurations = jsonDoc.toVariant().toList();
// //QVERIFY2(checkConfigurations.count() == 2, "there should be 2 configurations");
// // verify new configurations
// verifyParams(newConfigurations, checkConfigurations);
// nam->deleteLater();
//}
#include "testrestplugins.moc"
QTEST_MAIN(TestRestPlugins)

View File

@ -1,5 +0,0 @@
include(../../../nymea.pri)
include(../autotests.pri)
TARGET = restrules
SOURCES += testrestrules.cpp

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
include(../../../nymea.pri)
include(../autotests.pri)
TARGET = restvendors
SOURCES += testrestvendors.cpp

View File

@ -1,146 +0,0 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of nymea. *
* *
* nymea 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. *
* *
**
* 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 nymea. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "nymeatestbase.h"
#include "nymeacore.h"
using namespace nymeaserver;
class TestRestVendors: public NymeaTestBase
{
Q_OBJECT
private slots:
void initTestCase();
void getVendors();
void invalidMethod();
void invalidPath();
void invalidVendor_data();
void invalidVendor();
};
void TestRestVendors::initTestCase()
{
NymeaTestBase::initTestCase();
foreach (const WebServerConfiguration &config, NymeaCore::instance()->configuration()->webServerConfigurations()) {
if (config.port == 3333 && (config.address == QHostAddress("127.0.0.1") || config.address == QHostAddress("0.0.0.0"))) {
qDebug() << "Already have a webserver listening on 127.0.0.1:3333";
return;
}
}
qDebug() << "Creating new webserver instance on 127.0.0.1:3333";
WebServerConfiguration config;
config.address = QHostAddress("127.0.0.1");
config.port = 3333;
config.sslEnabled = true;
config.restServerEnabled = true;
NymeaCore::instance()->configuration()->setWebServerConfiguration(config);
qApp->processEvents();
}
void TestRestVendors::getVendors()
{
// Get all vendors
QVariant response = getAndWait(QNetworkRequest(QUrl("https://localhost:3333/api/v1/vendors")));
QVariantList vendorList = response.toList();
QVERIFY2(vendorList.count() > 0, "Not enough vendors.");
// Get each of thouse vendors individualy
foreach (const QVariant &vendor, vendorList) {
QVariantMap vendorMap = vendor.toMap();
if (!VendorId(vendorMap.value("id").toString()).isNull()) {
QNetworkRequest request(QUrl(QString("https://localhost:3333/api/v1/vendors/%1").arg(vendorMap.value("id").toString())));
response = getAndWait(request);
QVERIFY2(!response.isNull(), "Could not get vendor");
}
}
}
void TestRestVendors::invalidMethod()
{
QNetworkAccessManager nam;
connect(&nam, &QNetworkAccessManager::sslErrors, [this, &nam](QNetworkReply *reply, const QList<QSslError> &) {
reply->ignoreSslErrors();
});
QSignalSpy clientSpy(&nam, SIGNAL(finished(QNetworkReply*)));
QNetworkRequest request;
request.setUrl(QUrl("https://localhost:3333/api/v1/vendors"));
QNetworkReply *reply = nam.post(request, QByteArray());
clientSpy.wait();
QVERIFY2(clientSpy.count() != 0, "expected at least 1 response from webserver");
bool ok = false;
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
QVERIFY2(ok, "Could not convert statuscode from response to int");
QCOMPARE(statusCode, 400);
reply->deleteLater();
}
void TestRestVendors::invalidPath()
{
QNetworkAccessManager nam;
connect(&nam, &QNetworkAccessManager::sslErrors, [this, &nam](QNetworkReply *reply, const QList<QSslError> &) {
reply->ignoreSslErrors();
});
QSignalSpy clientSpy(&nam, SIGNAL(finished(QNetworkReply*)));
QNetworkRequest request;
request.setUrl(QUrl("https://localhost:3333/api/v1/vendors/" + QUuid::createUuid().toString() + "/" + QUuid::createUuid().toString()));
QNetworkReply *reply = nam.get(request);
clientSpy.wait();
QVERIFY2(clientSpy.count() != 0, "expected at least 1 response from webserver");
bool ok = false;
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(&ok);
QVERIFY2(ok, "Could not convert statuscode from response to int");
QCOMPARE(statusCode, 501);
reply->deleteLater();
}
void TestRestVendors::invalidVendor_data()
{
QTest::addColumn<QString>("path");
QTest::addColumn<int>("expectedStatusCode");
QTest::newRow("invalid VendorId") << QUuid::createUuid().toString() << 404;
QTest::newRow("invalid VendorId format") << "uuid" << 400;
}
void TestRestVendors::invalidVendor()
{
QFETCH(QString, path);
QFETCH(int, expectedStatusCode);
QNetworkRequest request(QUrl("https://localhost:3333/api/v1/vendors/" + path));
QVariant response = getAndWait(request, expectedStatusCode);
QCOMPARE(JsonTypes::deviceErrorToString(Device::DeviceErrorVendorNotFound), response.toMap().value("error").toString());
}
#include "testrestvendors.moc"
QTEST_MAIN(TestRestVendors)