Drop old REST server
This commit is contained in:
parent
977247e950
commit
03b8be22d6
@ -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
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
@ -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 ¶ms) 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;
|
||||
}
|
||||
}
|
||||
@ -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 ¶ms) 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
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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 ¶m, 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;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
@ -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();
|
||||
|
||||
@ -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 \
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
include(../../../nymea.pri)
|
||||
include(../autotests.pri)
|
||||
|
||||
TARGET = restdeviceclasses
|
||||
SOURCES += testrestdeviceclasses.cpp
|
||||
@ -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)
|
||||
@ -1,5 +0,0 @@
|
||||
include(../../../nymea.pri)
|
||||
include(../autotests.pri)
|
||||
|
||||
TARGET = restdevices
|
||||
SOURCES += testrestdevices.cpp
|
||||
@ -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)
|
||||
@ -1,5 +0,0 @@
|
||||
include(../../../nymea.pri)
|
||||
include(../autotests.pri)
|
||||
|
||||
TARGET = testrestlogging
|
||||
SOURCES += testrestlogging.cpp
|
||||
@ -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)
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
include(../../../nymea.pri)
|
||||
include(../autotests.pri)
|
||||
|
||||
TARGET = restplugins
|
||||
SOURCES += testrestplugins.cpp
|
||||
@ -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)
|
||||
@ -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
@ -1,5 +0,0 @@
|
||||
include(../../../nymea.pri)
|
||||
include(../autotests.pri)
|
||||
|
||||
TARGET = restvendors
|
||||
SOURCES += testrestvendors.cpp
|
||||
@ -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)
|
||||
Reference in New Issue
Block a user