Merge PR #410: Add support for storing application data on the core

This commit is contained in:
Jenkins nymea 2021-04-19 11:38:30 +02:00
commit c9af1280aa
7 changed files with 142 additions and 1 deletions

View File

@ -0,0 +1,82 @@
#include "appdatahandler.h"
#include "jsonrpc/jsonrpcserver.h"
#include "nymeasettings.h"
#include <QSettings>
#include <QDir>
AppDataHandler::AppDataHandler(QObject *parent) : JsonHandler(parent)
{
// Methods
QString description; QVariantMap params; QVariantMap returns;
description = "Store an app data entry to the server. App data can be used by the client application "
"to store configuration values. The app data storage is a key-value pair storage. Each "
"entry value is identified by an appId, a key and optionally a group. The value data is "
"a bytearray and can contain arbitrary data, such as a JSON map or image data, however, "
"be aware of the maximum packet size for the used transport.\n"
"This might be useful to a client application to sync settings across multiple instances of "
"the same application.\n"
"The group parameter might be used to create groups for this application.\n"
"IMPORTANT: Currently no verification of the appId is done. The appid is merely a mechanism "
"to prevent different different client apps from colliding by using the same key for data "
"entries. This implies that the app data storage may not be suited for sensitive data given "
"that anyone with a valid server token can read it.\n ";
params.insert("appId", enumValueName(String));
params.insert("o:group", enumValueName(String));
params.insert("key", enumValueName(String));
params.insert("value", enumValueName(String));
registerMethod("Store", description, params, returns);
description.clear(); params.clear(); returns.clear();
description = "Retrieve an app data storage value that has previously been set with Store(). If no value "
"had been set for this appId/key combination before, an empty value will be returned.";
params.insert("appId", enumValueName(String));
params.insert("o:group", enumValueName(String));
params.insert("key", enumValueName(String));
returns.insert("value", enumValueName(String));
registerMethod("Load", description, params, returns);
// Notifications
description.clear(); params.clear();
description = "Emitted whenever the app data is changed on the server.";
params.insert("appId", enumValueName(String));
params.insert("o:group", enumValueName(String));
params.insert("key", enumValueName(String));
params.insert("value", enumValueName(String));
registerNotification("Changed", description, params);
}
QString AppDataHandler::name() const
{
return "AppData";
}
JsonReply* AppDataHandler::Store(const QVariantMap &params)
{
QString appId = params.value("appId").toString();
QString group = params.value("group").toString();
QString key = params.value("key").toString();
QVariant value = params.value("value");
// Note: we're using a different file for each group as QSettings tends to get slow with loads of keys.
// Might be replaced with a DB at some point if needed. However, current estimate is this won't be
// used for excessive amounts of data as it is mostly meant as a config file syncing mechanism.
QSettings settings(NymeaSettings::storagePath() + "/appdata/" + appId + '/' + group + ".conf", QSettings::IniFormat);
settings.setValue(key, value);
return createReply(QVariantMap());
}
JsonReply* AppDataHandler::Load(const QVariantMap &params)
{
QString appId = params.value("appId").toString();
QString group = params.value("group").toString();
QString key = params.value("key").toString();
QSettings settings(NymeaSettings::storagePath() + "/appdata/" + appId + '/' + group + ".conf", QSettings::IniFormat);
QVariantMap returns;
returns.insert("value", settings.value(key).toString());
return createReply(returns);
}

View File

@ -0,0 +1,22 @@
#ifndef APPDATAHANDLER_H
#define APPDATAHANDLER_H
#include <QObject>
#include "jsonrpc/jsonhandler.h"
class AppDataHandler : public JsonHandler
{
Q_OBJECT
public:
explicit AppDataHandler(QObject *parent = nullptr);
QString name() const override;
Q_INVOKABLE JsonReply *Store(const QVariantMap &params);
Q_INVOKABLE JsonReply *Load(const QVariantMap &params);
signals:
};
#endif // APPDATAHANDLER_H

View File

@ -71,6 +71,7 @@
#include "configurationhandler.h"
#include "networkmanagerhandler.h"
#include "tagshandler.h"
#include "appdatahandler.h"
#include "systemhandler.h"
#include "usershandler.h"
#include "zigbeehandler.h"
@ -594,6 +595,7 @@ void JsonRPCServerImplementation::setup()
registerHandler(new ConfigurationHandler(this));
registerHandler(new NetworkManagerHandler(NymeaCore::instance()->networkManager(), this));
registerHandler(new TagsHandler(this));
registerHandler(new AppDataHandler(this));
registerHandler(new SystemHandler(NymeaCore::instance()->platform(), this));
registerHandler(new UsersHandler(NymeaCore::instance()->userManager(), this));
registerHandler(new ZigbeeHandler(NymeaCore::instance()->zigbeeManager(), this));

View File

@ -92,6 +92,7 @@ HEADERS += nymeacore.h \
jsonrpc/configurationhandler.h \
jsonrpc/networkmanagerhandler.h \
jsonrpc/tagshandler.h \
jsonrpc/appdatahandler.h \
jsonrpc/systemhandler.h \
jsonrpc/scriptshandler.h \
jsonrpc/usershandler.h \
@ -179,6 +180,7 @@ SOURCES += nymeacore.cpp \
jsonrpc/configurationhandler.cpp \
jsonrpc/networkmanagerhandler.cpp \
jsonrpc/tagshandler.cpp \
jsonrpc/appdatahandler.cpp \
jsonrpc/systemhandler.cpp \
jsonrpc/scriptshandler.cpp \
jsonrpc/usershandler.cpp \

View File

@ -83,6 +83,8 @@ translations.files = $$[QT_SOURCE_TREE]/translations/*.qm
# Redefine target to make output file suite the plugin filename schema
TARGET = $$qtLibraryTarget(nymea_integrationplugin"$$TARGET")
target.depends += $${JSONFILE}
# Install plugin
target.path = $$[QT_INSTALL_LIBS]/nymea/plugins/
INSTALLS += target translations

View File

@ -441,6 +441,28 @@
"o:actionType": "$ref:ActionType"
}
},
"AppData.Load": {
"description": "Retrieve an app data storage value that has previously been set with Store(). If no value had been set for this appId/key combination before, an empty value will be returned.",
"params": {
"appId": "String",
"key": "String",
"o:group": "String"
},
"returns": {
"value": "String"
}
},
"AppData.Store": {
"description": "Store an app data entry to the server. App data can be used by the client application to store configuration values. The app data storage is a key-value pair storage. Each entry value is identified by an appId, a key and optionally a group. The value data is a bytearray and can contain arbitrary data, such as a JSON map or image data, however, be aware of the maximum packet size for the used transport.\nThis might be useful to a client application to sync settings across multiple instances of the same application.\nThe group parameter might be used to create groups for this application.\nIMPORTANT: Currently no verification of the appId is done. The appid is merely a mechanism to prevent different different client apps from colliding by using the same key for data entries. This implies that the app data storage may not be suited for sensitive data given that anyone with a valid server token can read it.\n ",
"params": {
"appId": "String",
"key": "String",
"o:group": "String",
"value": "String"
},
"returns": {
}
},
"Configuration.DeleteMqttPolicy": {
"description": "Delete a MQTT policy from the broker.",
"params": {
@ -2058,6 +2080,15 @@
}
},
"notifications": {
"AppData.Changed": {
"description": "Emitted whenever the app data is changed on the server.",
"params": {
"appId": "String",
"key": "String",
"o:group": "String",
"value": "String"
}
},
"Configuration.BasicConfigurationChanged": {
"description": "Emitted whenever the basic configuration of this server changes.",
"params": {

View File

@ -674,7 +674,7 @@ void TestJSONRPC::enableDisableNotifications_legacy()
QStringList expectedNamespaces;
if (enabled == "true") {
expectedNamespaces << "Actions" << "NetworkManager" << "Devices" << "Integrations" << "System" << "Rules" << "States" << "Logging" << "Tags" << "JSONRPC" << "Configuration" << "Events" << "Scripts" << "Users" << "Zigbee";
expectedNamespaces << "Actions" << "NetworkManager" << "Devices" << "Integrations" << "System" << "Rules" << "States" << "Logging" << "Tags" << "AppData" << "JSONRPC" << "Configuration" << "Events" << "Scripts" << "Users" << "Zigbee";
}
std::sort(expectedNamespaces.begin(), expectedNamespaces.end());