add database serialization and variant string converting to jsonrpc

This commit is contained in:
Simon Stürz 2017-10-11 13:08:58 +02:00 committed by Michael Zanetti
parent 163402e158
commit ba526eee11
10 changed files with 197 additions and 66 deletions

View File

@ -55,6 +55,7 @@
#include "guhcore.h" #include "guhcore.h"
#include "ruleengine.h" #include "ruleengine.h"
#include "loggingcategories.h" #include "loggingcategories.h"
#include "logging/logvaluetool.h"
#include <QStringList> #include <QStringList>
#include <QJsonDocument> #include <QJsonDocument>
@ -887,7 +888,7 @@ QVariantMap JsonTypes::packLogEntry(const LogEntry &logEntry)
case Logging::LoggingSourceStates: case Logging::LoggingSourceStates:
logEntryMap.insert("typeId", logEntry.typeId().toString()); logEntryMap.insert("typeId", logEntry.typeId().toString());
logEntryMap.insert("deviceId", logEntry.deviceId().toString()); logEntryMap.insert("deviceId", logEntry.deviceId().toString());
logEntryMap.insert("value", logEntry.value()); logEntryMap.insert("value", LogValueTool::convertVariantToString(logEntry.value()));
break; break;
case Logging::LoggingSourceSystem: case Logging::LoggingSourceSystem:
logEntryMap.insert("active", logEntry.active()); logEntryMap.insert("active", logEntry.active());

View File

@ -65,7 +65,8 @@ HEADERS += $$top_srcdir/libguh-core/guhcore.h \
$$top_srcdir/libguh-core/networkmanager/wirednetworkdevice.h \ $$top_srcdir/libguh-core/networkmanager/wirednetworkdevice.h \
$$top_srcdir/libguh-core/usermanager.h \ $$top_srcdir/libguh-core/usermanager.h \
$$top_srcdir/libguh-core/tokeninfo.h \ $$top_srcdir/libguh-core/tokeninfo.h \
$$PWD/certificategenerator.h $$PWD/certificategenerator.h \
logging/logvaluetool.h
SOURCES += $$top_srcdir/libguh-core/guhcore.cpp \ SOURCES += $$top_srcdir/libguh-core/guhcore.cpp \
@ -118,5 +119,6 @@ SOURCES += $$top_srcdir/libguh-core/guhcore.cpp \
$$top_srcdir/libguh-core/networkmanager/wirednetworkdevice.cpp \ $$top_srcdir/libguh-core/networkmanager/wirednetworkdevice.cpp \
$$top_srcdir/libguh-core/usermanager.cpp \ $$top_srcdir/libguh-core/usermanager.cpp \
$$top_srcdir/libguh-core/tokeninfo.cpp \ $$top_srcdir/libguh-core/tokeninfo.cpp \
$$PWD/certificategenerator.cpp $$PWD/certificategenerator.cpp \
logging/logvaluetool.cpp

View File

@ -1,7 +1,7 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * *
* Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> * * Copyright (C) 2014 Michael Zanetti <michael_zanetti@gmx.net> *
* Copyright (C) 2015 Simon Stürz <simon.stuerz@guh.io> * * Copyright (C) 2015-2017 Simon Stürz <simon.stuerz@guh.io> *
* * * *
* This file is part of guh. * * This file is part of guh. *
* * * *
@ -114,6 +114,7 @@
#include "logengine.h" #include "logengine.h"
#include "loggingcategories.h" #include "loggingcategories.h"
#include "logging.h" #include "logging.h"
#include "logvaluetool.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QSqlDatabase> #include <QSqlDatabase>
@ -125,8 +126,6 @@
#include <QDateTime> #include <QDateTime>
#include <QFileInfo> #include <QFileInfo>
#include <QTime> #include <QTime>
#include <QBuffer>
#include <QDataStream>
#define DB_SCHEMA_VERSION 3 #define DB_SCHEMA_VERSION 3
@ -211,7 +210,7 @@ QList<LogEntry> LogEngine::logEntries(const LogFilter &filter) const
query.value("errorCode").toInt()); query.value("errorCode").toInt());
entry.setTypeId(query.value("typeId").toUuid()); entry.setTypeId(query.value("typeId").toUuid());
entry.setDeviceId(DeviceId(query.value("deviceId").toString())); entry.setDeviceId(DeviceId(query.value("deviceId").toString()));
entry.setValue(LogEngine::convertVariantToString(deserializeValue(query.value("value").toString()))); entry.setValue(LogValueTool::convertVariantToString(LogValueTool::deserializeValue(query.value("value").toString())));
entry.setEventType((Logging::LoggingEventType)query.value("loggingEventType").toInt()); entry.setEventType((Logging::LoggingEventType)query.value("loggingEventType").toInt());
entry.setActive(query.value("active").toBool()); entry.setActive(query.value("active").toBool());
results.append(entry); results.append(entry);
@ -251,38 +250,49 @@ void LogEngine::logSystemEvent(const QDateTime &dateTime, bool active, Logging::
void LogEngine::logEvent(const Event &event) void LogEngine::logEvent(const Event &event)
{ {
QStringList valueList; QVariantList valueList;
Logging::LoggingSource sourceType; Logging::LoggingSource sourceType;
if (event.isStateChangeEvent()) { if (event.isStateChangeEvent()) {
sourceType = Logging::LoggingSourceStates; sourceType = Logging::LoggingSourceStates;
// There should only be one param // There should only be one param
if (!event.params().isEmpty()) if (!event.params().isEmpty())
valueList << serializeValue(event.params().first().value()); valueList << event.params().first().value();
} else { } else {
sourceType = Logging::LoggingSourceEvents; sourceType = Logging::LoggingSourceEvents;
foreach (const Param &param, event.params()) { foreach (const Param &param, event.params()) {
valueList << serializeValue(param.value()); valueList << param.value();
} }
} }
LogEntry entry(sourceType); LogEntry entry(sourceType);
entry.setTypeId(event.eventTypeId()); entry.setTypeId(event.eventTypeId());
entry.setDeviceId(event.deviceId()); entry.setDeviceId(event.deviceId());
entry.setValue(valueList.join(", ")); if (valueList.count() == 1) {
entry.setValue(valueList.first());
} else {
entry.setValue(valueList);
}
appendLogEntry(entry); appendLogEntry(entry);
} }
void LogEngine::logAction(const Action &action, Logging::LoggingLevel level, int errorCode) void LogEngine::logAction(const Action &action, Logging::LoggingLevel level, int errorCode)
{ {
QStringList valueList;
foreach (const Param &param, action.params()) {
valueList << param.value().toString();
}
LogEntry entry(level, Logging::LoggingSourceActions, errorCode); LogEntry entry(level, Logging::LoggingSourceActions, errorCode);
entry.setTypeId(action.actionTypeId()); entry.setTypeId(action.actionTypeId());
entry.setDeviceId(action.deviceId()); entry.setDeviceId(action.deviceId());
entry.setValue(valueList.join(", "));
if (action.params().isEmpty()) {
entry.setValue(QVariant());
} else if (action.params().count() == 1) {
entry.setValue(action.params().first().value());
} else {
QVariantList valueList;
foreach (const Param &param, action.params()) {
valueList << param.value();
}
entry.setValue(valueList);
}
appendLogEntry(entry); appendLogEntry(entry);
} }
@ -379,7 +389,7 @@ void LogEngine::appendLogEntry(const LogEntry &entry)
.arg(entry.source()) .arg(entry.source())
.arg(entry.typeId().toString()) .arg(entry.typeId().toString())
.arg(entry.deviceId().toString()) .arg(entry.deviceId().toString())
.arg(entry.value()) .arg(LogValueTool::serializeValue(entry.value()))
.arg(entry.active()) .arg(entry.active())
.arg(entry.errorCode()); .arg(entry.errorCode());
@ -442,29 +452,6 @@ void LogEngine::rotate(const QString &dbName)
} }
} }
QString LogEngine::serializeValue(const QVariant &value)
{
QByteArray byteArray;
QBuffer writeBuffer(&byteArray);
writeBuffer.open(QIODevice::WriteOnly);
QDataStream out(&writeBuffer);
out << value;
writeBuffer.close();
return QString(byteArray.toBase64());
}
QVariant LogEngine::deserializeValue(const QString &serializedValue)
{
QByteArray data = QByteArray::fromBase64(serializedValue.toUtf8());
QBuffer readBuffer(&data);
readBuffer.open(QIODevice::ReadOnly);
QDataStream inputStream(&readBuffer);
QVariant value;
inputStream >> value;
readBuffer.close();
return value;
}
bool LogEngine::migrateDatabaseVersion2to3() bool LogEngine::migrateDatabaseVersion2to3()
{ {
// Changelog: serialize values of logentries in order to prevent typecast errors // Changelog: serialize values of logentries in order to prevent typecast errors
@ -477,7 +464,7 @@ bool LogEngine::migrateDatabaseVersion2to3()
int entryCount = 0; int entryCount = 0;
// Count entries we have to migrate // Count entries we have to migrate
QString queryString = "SELECT COUNT(*) FROM entries;"; QString queryString = "SELECT COUNT(*) FROM entries WHERE value != '';";
QSqlQuery countQuery = m_db.exec(queryString); QSqlQuery countQuery = m_db.exec(queryString);
if (m_db.lastError().type() != QSqlError::NoError) { if (m_db.lastError().type() != QSqlError::NoError) {
qCWarning(dcLogEngine()) << "Failed to query entry count in db:" << m_db.lastError().databaseText(); qCWarning(dcLogEngine()) << "Failed to query entry count in db:" << m_db.lastError().databaseText();
@ -489,6 +476,8 @@ bool LogEngine::migrateDatabaseVersion2to3()
} }
entryCount = countQuery.value(0).toInt(); entryCount = countQuery.value(0).toInt();
qCDebug(dcLogEngine()) << "Entries to migrate:" << entryCount;
// Select all entries // Select all entries
QSqlQuery selectQuery = m_db.exec("SELECT * FROM entries;"); QSqlQuery selectQuery = m_db.exec("SELECT * FROM entries;");
if (m_db.lastError().isValid()) { if (m_db.lastError().isValid()) {
@ -499,7 +488,7 @@ bool LogEngine::migrateDatabaseVersion2to3()
// Migrate all selected entries // Migrate all selected entries
while (selectQuery.next()) { while (selectQuery.next()) {
QString oldValue = selectQuery.value("value").toString(); QString oldValue = selectQuery.value("value").toString();
QString newValue = serializeValue(QVariant(oldValue)); QString newValue = LogValueTool::serializeValue(QVariant(oldValue));
if (oldValue.isEmpty()) if (oldValue.isEmpty())
continue; continue;
@ -526,11 +515,12 @@ bool LogEngine::migrateDatabaseVersion2to3()
double percentage = migrationCounter * 100.0 / entryCount; double percentage = migrationCounter * 100.0 / entryCount;
if (qRound(percentage) != migrationProgress) { if (qRound(percentage) != migrationProgress) {
migrationProgress = qRound(percentage); migrationProgress = qRound(percentage);
qCDebug(dcLogEngine()) << QString("Migration progress: %1\%").arg(migrationProgress); qCDebug(dcLogEngine()) << QString("Migration progress: %1\%").arg(migrationProgress).toLatin1().data();
} }
} }
qCDebug(dcLogEngine()) << "Migration of" << migrationCounter << "done in" << QTime().addMSecs(startTime.msecsTo(QDateTime::currentDateTime())).toString("mm:ss.zzz"); QTime runTime = QTime(0,0,0,0).addMSecs(startTime.msecsTo(QDateTime::currentDateTime()));
qCDebug(dcLogEngine()) << "Migration of" << migrationCounter << "done in" << runTime.toString("mm:ss.zzz");
qCDebug(dcLogEngine()) << "Updating database version to" << DB_SCHEMA_VERSION; qCDebug(dcLogEngine()) << "Updating database version to" << DB_SCHEMA_VERSION;
m_db.exec(QString("UPDATE metadata SET data = %1 WHERE key = 'version';").arg(DB_SCHEMA_VERSION)); m_db.exec(QString("UPDATE metadata SET data = %1 WHERE key = 'version';").arg(DB_SCHEMA_VERSION));
if (m_db.lastError().isValid()) { if (m_db.lastError().isValid()) {
@ -542,18 +532,6 @@ bool LogEngine::migrateDatabaseVersion2to3()
return true; return true;
} }
QString LogEngine::convertVariantToString(QVariant value)
{
switch (value.type()) {
case QVariant::Double:
return QString::number(value.toDouble());
break;
default:
return value.toString();
break;
}
}
bool LogEngine::initDB() bool LogEngine::initDB()
{ {
m_db.close(); m_db.close();

View File

@ -67,11 +67,8 @@ private:
void appendLogEntry(const LogEntry &entry); void appendLogEntry(const LogEntry &entry);
void rotate(const QString &dbName); void rotate(const QString &dbName);
static QString serializeValue(const QVariant &value);
static QVariant deserializeValue(const QString &serializedValue);
bool migrateDatabaseVersion2to3(); bool migrateDatabaseVersion2to3();
static QString convertVariantToString(QVariant value);
private slots: private slots:
void checkDBSize(); void checkDBSize();

View File

@ -112,13 +112,13 @@ void LogEntry::setDeviceId(const DeviceId &deviceId)
} }
/*! Returns the value of this \l{LogEntry}. */ /*! Returns the value of this \l{LogEntry}. */
QString LogEntry::value() const QVariant LogEntry::value() const
{ {
return m_value; return m_value;
} }
/*! Sets the \a value of this \l{LogEntry}. */ /*! Sets the \a value of this \l{LogEntry}. */
void LogEntry::setValue(const QString &value) void LogEntry::setValue(const QVariant &value)
{ {
m_value = value; m_value = value;
} }

View File

@ -26,6 +26,7 @@
#include "typeutils.h" #include "typeutils.h"
#include <QObject> #include <QObject>
#include <QVariant>
#include <QDateTime> #include <QDateTime>
namespace guhserver { namespace guhserver {
@ -56,8 +57,8 @@ public:
void setDeviceId(const DeviceId &deviceId); void setDeviceId(const DeviceId &deviceId);
// Valid for LoggingSourceStates // Valid for LoggingSourceStates
QString value() const; QVariant value() const;
void setValue(const QString &value); void setValue(const QVariant &value);
// Valid for LoggingEventTypeActiveChanged // Valid for LoggingEventTypeActiveChanged
bool active() const; bool active() const;
@ -75,7 +76,7 @@ private:
// FIXME: If it turns out we need many more of those, we should subclass LogEntry with specific ones. // FIXME: If it turns out we need many more of those, we should subclass LogEntry with specific ones.
QUuid m_typeId; QUuid m_typeId;
DeviceId m_deviceId; DeviceId m_deviceId;
QString m_value; QVariant m_value;
Logging::LoggingEventType m_eventType; Logging::LoggingEventType m_eventType;
bool m_active; bool m_active;
int m_errorCode; int m_errorCode;

View File

@ -336,6 +336,8 @@ QString LogFilter::createDeviceIdString() const
QString LogFilter::createValuesString() const QString LogFilter::createValuesString() const
{ {
// FIXME: check how to filter for serialized values
QString query; QString query;
if (!m_values.isEmpty()) { if (!m_values.isEmpty()) {
if (m_values.count() == 1) { if (m_values.count() == 1) {

View File

@ -0,0 +1,72 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "logvaluetool.h"
#include <QBuffer>
#include <QByteArray>
#include <QDataStream>
LogValueTool::LogValueTool(QObject *parent) : QObject(parent)
{
}
QString LogValueTool::convertVariantToString(const QVariant &value)
{
switch (value.type()) {
case QVariant::Double:
return QString::number(value.toDouble());
break;
case QVariant::List: {
QStringList valueStringList;
foreach (const QVariant &variantValue, value.toList()) {
valueStringList.append(convertVariantToString(variantValue));
}
return valueStringList.join(", ");
}
default:
return value.toString();
break;
}
}
QString LogValueTool::serializeValue(const QVariant &value)
{
QByteArray byteArray;
QBuffer writeBuffer(&byteArray);
writeBuffer.open(QIODevice::WriteOnly);
QDataStream out(&writeBuffer);
out << value;
writeBuffer.close();
return QString(byteArray.toBase64());
}
QVariant LogValueTool::deserializeValue(const QString &serializedValue)
{
QByteArray data = QByteArray::fromBase64(serializedValue.toUtf8());
QBuffer readBuffer(&data);
readBuffer.open(QIODevice::ReadOnly);
QDataStream inputStream(&readBuffer);
QVariant value;
inputStream >> value;
readBuffer.close();
return value;
}

View File

@ -0,0 +1,38 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* Copyright (C) 2017 Simon Stürz <simon.stuerz@guh.io> *
* *
* This file is part of guh. *
* *
* Guh is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, version 2 of the License. *
* *
* Guh is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with guh. If not, see <http://www.gnu.org/licenses/>. *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef LOGVALUETOOL_H
#define LOGVALUETOOL_H
#include <QObject>
#include <QVariant>
class LogValueTool : public QObject
{
Q_OBJECT
public:
explicit LogValueTool(QObject *parent = nullptr);
static QString convertVariantToString(const QVariant &value);
static QString serializeValue(const QVariant &value);
static QVariant deserializeValue(const QString &serializedValue);
};
#endif // LOGVALUETOOL_H

View File

@ -24,6 +24,7 @@
#include "devicemanager.h" #include "devicemanager.h"
#include "guhsettings.h" #include "guhsettings.h"
#include "logging/logentry.h" #include "logging/logentry.h"
#include "logging/logvaluetool.h"
#include "plugin/deviceplugin.h" #include "plugin/deviceplugin.h"
#include <QDebug> #include <QDebug>
@ -43,6 +44,9 @@ private:
private slots: private slots:
void initLogs(); void initLogs();
void databaseSerializationTest_data();
void databaseSerializationTest();
void coverageCalls(); void coverageCalls();
void systemLogs(); void systemLogs();
@ -83,6 +87,42 @@ void TestLogging::initLogs()
restartServer(); restartServer();
} }
void TestLogging::databaseSerializationTest_data()
{
QUuid uuid = QUuid("3782732b-61b4-48e8-8d6d-b5205159d7cd");
QVariantMap variantMap;
variantMap.insert("string", "value");
variantMap.insert("int", 5);
variantMap.insert("double", 3.14);
variantMap.insert("uuid", uuid);
QTest::addColumn<QVariant>("value");
QTest::newRow("QString") << QVariant(QString("Hello"));
QTest::newRow("Integer") << QVariant((int)2);
QTest::newRow("Double") << QVariant((double)2.34);
QTest::newRow("Float") << QVariant((float)2.34);
QTest::newRow("QColor") << QVariant(QColor(0,255,128));
QTest::newRow("QByteArray") << QVariant(QByteArray("\nthisisatestarray\n"));
QTest::newRow("QUuid") << QVariant(uuid);
QTest::newRow("QVariantMap") << QVariant(variantMap);
}
void TestLogging::databaseSerializationTest()
{
QFETCH(QVariant, value);
QString serializedValue = LogValueTool::serializeValue(value);
QVariant deserializedValue = LogValueTool::deserializeValue(serializedValue);
qDebug() << "Stored:" << value;
qDebug() << "Loaded:" << deserializedValue;
QCOMPARE(deserializedValue, value);
}
void TestLogging::coverageCalls() void TestLogging::coverageCalls()
{ {
LogEntry entry(QDateTime::currentDateTime(), Logging::LoggingLevelInfo, Logging::LoggingSourceSystem); LogEntry entry(QDateTime::currentDateTime(), Logging::LoggingLevelInfo, Logging::LoggingSourceSystem);
@ -245,7 +285,7 @@ void TestLogging::actionLog()
verifyDeviceError(response); verifyDeviceError(response);
// Lets wait for the notification // Lets wait for the notification
clientSpy.wait(200); clientSpy.wait(500);
QVariantList loggEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded"); QVariantList loggEntryAddedVariants = checkNotifications(clientSpy, "Logging.LogEntryAdded");
QVERIFY2(!loggEntryAddedVariants.isEmpty(), "Did not get Logging.LogEntryAdded notification."); QVERIFY2(!loggEntryAddedVariants.isEmpty(), "Did not get Logging.LogEntryAdded notification.");
@ -287,7 +327,7 @@ void TestLogging::actionLog()
params.insert("deviceIds", QVariantList() << m_mockDeviceId); params.insert("deviceIds", QVariantList() << m_mockDeviceId);
params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceActions)); params.insert("loggingSources", QVariantList() << JsonTypes::loggingSourceToString(Logging::LoggingSourceActions));
params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger)); params.insert("eventTypes", QVariantList() << JsonTypes::loggingEventTypeToString(Logging::LoggingEventTypeTrigger));
params.insert("values", QVariantList() << "7, true"); //params.insert("values", QVariantList() << "7, true");
response = injectAndWait("Logging.GetLogEntries", params); response = injectAndWait("Logging.GetLogEntries", params);
verifyLoggingError(response); verifyLoggingError(response);