Merge PR #336: Add caching information to client API

pull/352/head
Jenkins nymea 2020-10-28 19:07:35 +01:00
commit 82c347c3a3
9 changed files with 119 additions and 4 deletions

View File

@ -45,6 +45,7 @@
#include "integrations/browseritemresult.h"
#include <QDebug>
#include <QJsonDocument>
namespace nymeaserver {
@ -400,6 +401,37 @@ DeviceHandler::DeviceHandler(QObject *parent) :
connect(NymeaCore::instance(), &NymeaCore::thingAdded, this, &DeviceHandler::deviceAddedNotification);
connect(NymeaCore::instance(), &NymeaCore::thingChanged, this, &DeviceHandler::deviceChangedNotification);
connect(NymeaCore::instance(), &NymeaCore::thingSettingChanged, this, &DeviceHandler::deviceSettingChangedNotification);
connect(NymeaCore::instance(), &NymeaCore::initialized, this, [=](){
// Generating cache hashes.
// NOTE: We need to sort the lists to get a stable result
QHash<ThingClassId, ThingClass> thingClassesMap;
foreach (const ThingClass &tc, NymeaCore::instance()->thingManager()->supportedThings()) {
thingClassesMap.insert(tc.id(), tc);
}
QList<ThingClassId> thingClassIds = thingClassesMap.keys();
std::sort(thingClassIds.begin(), thingClassIds.end());
DeviceClasses thingClasses;
foreach (const ThingClassId &id, thingClassIds) {
thingClasses.append(thingClassesMap.value(id));
}
QByteArray hash = QCryptographicHash::hash(QJsonDocument::fromVariant(pack(thingClasses)).toJson(), QCryptographicHash::Md5).toHex();
m_cacheHashes.insert("GetSupportedDevices", hash);
QHash<VendorId, Vendor> vendorsMap;
foreach (const Vendor &v, NymeaCore::instance()->thingManager()->supportedVendors()) {
vendorsMap.insert(v.id(), v);
}
QList<VendorId> vendorIds = vendorsMap.keys();
std::sort(vendorIds.begin(), vendorIds.end());
Vendors vendors;
foreach (const VendorId &id, vendorIds) {
vendors.append(vendorsMap.value(id));
}
hash = QCryptographicHash::hash(QJsonDocument::fromVariant(pack(vendors)).toJson(), QCryptographicHash::Md5).toHex();
m_cacheHashes.insert("GetSupportedVendors", hash);
});
}
/*! Returns the name of the \l{DeviceHandler}. In this case \b Devices.*/
@ -408,6 +440,11 @@ QString DeviceHandler::name() const
return "Devices";
}
QHash<QString, QString> DeviceHandler::cacheHashes() const
{
return m_cacheHashes;
}
JsonReply* DeviceHandler::GetSupportedVendors(const QVariantMap &params, const JsonContext &context) const
{
Q_UNUSED(params)

View File

@ -139,6 +139,8 @@ public:
explicit DeviceHandler(QObject *parent = nullptr);
QString name() const override;
QHash<QString, QString> cacheHashes() const override;
QVariantMap translateNotification(const QString &notification, const QVariantMap &params, const QLocale &locale) override;
Q_INVOKABLE JsonReply *GetSupportedVendors(const QVariantMap &params, const JsonContext &context) const;
@ -196,6 +198,8 @@ private slots:
private:
QVariantMap statusToReply(Device::ThingError status) const;
QHash<QString, QString> m_cacheHashes;
};
}

View File

@ -45,6 +45,7 @@
#include "integrations/browseritemresult.h"
#include <QDebug>
#include <QJsonDocument>
namespace nymeaserver {
@ -445,6 +446,36 @@ IntegrationsHandler::IntegrationsHandler(ThingManager *thingManager, QObject *pa
connect(NymeaCore::instance(), &NymeaCore::thingAdded, this, &IntegrationsHandler::thingAddedNotification);
connect(NymeaCore::instance(), &NymeaCore::thingChanged, this, &IntegrationsHandler::thingChangedNotification);
connect(NymeaCore::instance(), &NymeaCore::thingSettingChanged, this, &IntegrationsHandler::thingSettingChangedNotification);
connect(NymeaCore::instance(), &NymeaCore::initialized, this, [=](){
// Generating cache hashes.
// NOTE: We need to sort the lists to get a stable result
QHash<ThingClassId, ThingClass> thingClassesMap;
foreach (const ThingClass &tc, m_thingManager->supportedThings()) {
thingClassesMap.insert(tc.id(), tc);
}
QList<ThingClassId> thingClassIds = thingClassesMap.keys();
std::sort(thingClassIds.begin(), thingClassIds.end());
ThingClasses thingClasses;
foreach (const ThingClassId &id, thingClassIds) {
thingClasses.append(thingClassesMap.value(id));
}
QByteArray hash = QCryptographicHash::hash(QJsonDocument::fromVariant(pack(thingClasses)).toJson(), QCryptographicHash::Md5).toHex();
m_cacheHashes.insert("GetThingClasses", hash);
QHash<VendorId, Vendor> vendorsMap;
foreach (const Vendor &v, m_thingManager->supportedVendors()) {
vendorsMap.insert(v.id(), v);
}
QList<VendorId> vendorIds = vendorsMap.keys();
std::sort(vendorIds.begin(), vendorIds.end());
Vendors vendors;
foreach (const VendorId &id, vendorIds) {
vendors.append(vendorsMap.value(id));
}
hash = QCryptographicHash::hash(QJsonDocument::fromVariant(pack(vendors)).toJson(), QCryptographicHash::Md5).toHex();
m_cacheHashes.insert("GetVendors", hash);
});
}
QString IntegrationsHandler::name() const
@ -452,6 +483,11 @@ QString IntegrationsHandler::name() const
return "Integrations";
}
QHash<QString, QString> IntegrationsHandler::cacheHashes() const
{
return m_cacheHashes;
}
JsonReply* IntegrationsHandler::GetVendors(const QVariantMap &params, const JsonContext &context) const
{
Q_UNUSED(params)

View File

@ -43,6 +43,7 @@ public:
explicit IntegrationsHandler(ThingManager *thingManager, QObject *parent = nullptr);
QString name() const override;
QHash<QString, QString> cacheHashes() const override;
Q_INVOKABLE JsonReply *GetVendors(const QVariantMap &params, const JsonContext &context) const;
Q_INVOKABLE JsonReply *GetThingClasses(const QVariantMap &params, const JsonContext &context) const;
@ -105,6 +106,8 @@ private slots:
private:
ThingManager *m_thingManager = nullptr;
QVariantMap statusToReply(Thing::ThingError status) const;
QHash<QString, QString> m_cacheHashes;
};
}

View File

@ -101,6 +101,11 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
experiece.insert("version", enumValueName(String));
registerObject("Experience", experiece);
QVariantMap cacheHash;
cacheHash.insert("method", enumValueName(String));
cacheHash.insert("hash", enumValueName(String));
registerObject("CacheHash", cacheHash);
// Methods
QString description; QVariantMap returns; QVariantMap params;
description = "Initiates a connection. Use this method to perform an initial handshake of the "
@ -111,7 +116,10 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
"about this core instance such as version information, uuid and its name. The locale value"
"indicates the locale used for this connection. Note: This method can be called multiple "
"times. The locale used in the last call for this connection will be used. Other values, "
"like initialSetupRequired might change if the setup has been performed in the meantime.";
"like initialSetupRequired might change if the setup has been performed in the meantime.\n "
"The field cacheHashes may contain a map of methods and MD5 hashes. As long as the hash for "
"a method does not change, a client may use a previously cached copy of the call instead of "
"fetching the content again.";
params.insert("o:locale", enumValueName(String));
returns.insert("server", enumValueName(String));
returns.insert("name", enumValueName(String));
@ -124,6 +132,7 @@ JsonRPCServerImplementation::JsonRPCServerImplementation(const QSslConfiguration
returns.insert("authenticationRequired", enumValueName(Bool));
returns.insert("pushButtonAuthAvailable", enumValueName(Bool));
returns.insert("o:experiences", QVariantList() << objectRef("Experience"));
returns.insert("o:cacheHashes", QVariantList() << objectRef("CacheHash"));
registerMethod("Hello", description, params, returns);
params.clear(); returns.clear();
@ -555,6 +564,19 @@ QVariantMap JsonRPCServerImplementation::createWelcomeMessage(TransportInterface
}
handshake.insert("experiences", experiences);
}
QVariantList cacheHashes;
foreach (const QString &handlerName, m_handlers.keys()) {
QHash<QString, QString> hashes = m_handlers.value(handlerName)->cacheHashes();
foreach (const QString &hashName, hashes.keys()) {
QVariantMap cacheHash;
cacheHash.insert("method", handlerName + "." + hashName);
cacheHash.insert("hash", hashes.value(hashName));
cacheHashes.append(cacheHash);
}
}
if (!cacheHashes.isEmpty()) {
handshake.insert("cacheHashes", cacheHashes);
}
return handshake;
}

View File

@ -41,6 +41,11 @@ JsonHandler::JsonHandler(QObject *parent) : QObject(parent)
registerEnum<BasicType>();
}
QHash<QString, QString> JsonHandler::cacheHashes() const
{
return QHash<QString, QString>();
}
QVariantMap JsonHandler::translateNotification(const QString &notification, const QVariantMap &params, const QLocale &locale)
{
Q_UNUSED(notification)

View File

@ -64,6 +64,7 @@ public:
virtual ~JsonHandler() = default;
virtual QString name() const = 0;
virtual QHash<QString, QString> cacheHashes() const;
virtual QVariantMap translateNotification(const QString &notification, const QVariantMap &params, const QLocale &locale);

View File

@ -5,7 +5,7 @@ NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"
# define protocol versions
JSON_PROTOCOL_VERSION_MAJOR=5
JSON_PROTOCOL_VERSION_MINOR=1
JSON_PROTOCOL_VERSION_MINOR=2
JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}"
LIBNYMEA_API_VERSION_MAJOR=7
LIBNYMEA_API_VERSION_MINOR=0

View File

@ -1,4 +1,4 @@
5.1
5.2
{
"enums": {
"BasicType": [
@ -1240,7 +1240,7 @@
}
},
"JSONRPC.Hello": {
"description": "Initiates a connection. Use this method to perform an initial handshake of the connection. Optionally, a parameter \"locale\" is can be passed to set up the used locale for this connection. Strings such as ThingClass displayNames etc will be localized to this locale. If this parameter is omitted, the default system locale (depending on the configuration) is used. The reply of this method contains information about this core instance such as version information, uuid and its name. The locale valueindicates the locale used for this connection. Note: This method can be called multiple times. The locale used in the last call for this connection will be used. Other values, like initialSetupRequired might change if the setup has been performed in the meantime.",
"description": "Initiates a connection. Use this method to perform an initial handshake of the connection. Optionally, a parameter \"locale\" is can be passed to set up the used locale for this connection. Strings such as ThingClass displayNames etc will be localized to this locale. If this parameter is omitted, the default system locale (depending on the configuration) is used. The reply of this method contains information about this core instance such as version information, uuid and its name. The locale valueindicates the locale used for this connection. Note: This method can be called multiple times. The locale used in the last call for this connection will be used. Other values, like initialSetupRequired might change if the setup has been performed in the meantime.\n The field cacheHashes may contain a map of methods and MD5 hashes. As long as the hash for a method does not change, a client may use a previously cached copy of the call instead of fetching the content again.",
"params": {
"o:locale": "String"
},
@ -1250,6 +1250,9 @@
"language": "String",
"locale": "String",
"name": "String",
"o:cacheHashes": [
"$ref:CacheHash"
],
"o:experiences": [
"$ref:Experience"
],
@ -2380,6 +2383,10 @@
"o:mediaIcon": "$ref:MediaBrowserIcon",
"thumbnail": "String"
},
"CacheHash": {
"hash": "String",
"method": "String"
},
"CalendarItem": {
"duration": "Uint",
"o:datetime": "Uint",