reenable strict argument checking, now that we can mark parameters as optional

This commit is contained in:
Michael Zanetti 2014-04-06 01:32:46 +02:00
parent 913d544386
commit c027bb1481
7 changed files with 75 additions and 21 deletions

View File

@ -31,6 +31,8 @@
The JSONRPC API is self documenting and can be introspected by calling \c"JSONRPC.Introspect". The JSONRPC API is self documenting and can be introspected by calling \c"JSONRPC.Introspect".
Parameters are optional if the type is the type is prefixed with "o:" for optional.
\section1 Communicating with the server \section1 Communicating with the server
The server listens on TCP port 1234 for incoming TCP connections. It will respond to incoming The server listens on TCP port 1234 for incoming TCP connections. It will respond to incoming
connections with a some information about the server. Telnet can be used to issue commands for connections with a some information about the server. Telnet can be used to issue commands for

View File

@ -121,10 +121,20 @@ QList<Vendor> DeviceManager::supportedVendors() const
return m_supportedVendors.values(); return m_supportedVendors.values();
} }
/*! Returns all the supported \l{DeviceClass}{DeviceClasses} by all \l{DevicePlugin}{DevicePlugins} loaded in the system. */ /*! Returns all the supported \l{DeviceClass}{DeviceClasses} by all \l{DevicePlugin}{DevicePlugins} loaded in the system.
Optionally filtered by vendorId. */
QList<DeviceClass> DeviceManager::supportedDevices(const VendorId &vendorId) const QList<DeviceClass> DeviceManager::supportedDevices(const VendorId &vendorId) const
{ {
return m_supportedDevices.values(); qDebug() << "returning devices" << vendorId;
QList<DeviceClass> ret;
if (vendorId.isNull()) {
ret = m_supportedDevices.values();
} else {
foreach (const DeviceClassId &deviceClassId, m_vendorDeviceMap.value(vendorId)) {
ret.append(m_supportedDevices.value(deviceClassId));
}
}
return ret;
} }
/*! Add a new configured device for the given \l{DeviceClass} and the given parameters. /*! Add a new configured device for the given \l{DeviceClass} and the given parameters.

View File

@ -1,6 +1,7 @@
#ifndef TYPEUTILS_H #ifndef TYPEUTILS_H
#define TYPEUTILS_H #define TYPEUTILS_H
#include <QMetaType>
#include <QUuid> #include <QUuid>
#define DECLARE_TYPE_ID(type) class type##Id: public QUuid \ #define DECLARE_TYPE_ID(type) class type##Id: public QUuid \
@ -10,12 +11,13 @@ public: \
type##Id(): QUuid() {} \ type##Id(): QUuid() {} \
static type##Id create##type##Id() { return type##Id(QUuid::createUuid().toString()); } \ static type##Id create##type##Id() { return type##Id(QUuid::createUuid().toString()); } \
static type##Id fromUuid(const QUuid &uuid) { return type##Id(uuid.toString()); } \ static type##Id fromUuid(const QUuid &uuid) { return type##Id(uuid.toString()); } \
}; }; \
Q_DECLARE_METATYPE(type##Id);
DECLARE_TYPE_ID(Device)
DECLARE_TYPE_ID(DeviceClass)
DECLARE_TYPE_ID(Vendor) DECLARE_TYPE_ID(Vendor)
DECLARE_TYPE_ID(DeviceClass)
DECLARE_TYPE_ID(Device)
DECLARE_TYPE_ID(EventType) DECLARE_TYPE_ID(EventType)
DECLARE_TYPE_ID(StateType) DECLARE_TYPE_ID(StateType)

View File

@ -40,7 +40,8 @@ DeviceHandler::DeviceHandler(QObject *parent) :
setReturns("GetSupportedVendors", returns); setReturns("GetSupportedVendors", returns);
params.clear(); returns.clear(); params.clear(); returns.clear();
setDescription("GetSupportedDevices", "Returns a list of supported Device classes."); setDescription("GetSupportedDevices", "Returns a list of supported Device classes, optionally filtered by vendorId.");
params.insert("vendorId", "o:uuid");
setParams("GetSupportedDevices", params); setParams("GetSupportedDevices", params);
QVariantList deviceClasses; QVariantList deviceClasses;
deviceClasses.append(JsonTypes::deviceClassRef()); deviceClasses.append(JsonTypes::deviceClassRef());
@ -73,7 +74,7 @@ DeviceHandler::DeviceHandler(QObject *parent) :
setParams("AddConfiguredDevice", params); setParams("AddConfiguredDevice", params);
returns.insert("success", "bool"); returns.insert("success", "bool");
returns.insert("errorMessage", "string"); returns.insert("errorMessage", "string");
returns.insert("deviceId", "uuid"); returns.insert("deviceId", "o:uuid");
setReturns("AddConfiguredDevice", returns); setReturns("AddConfiguredDevice", returns);
params.clear(); returns.clear(); params.clear(); returns.clear();
@ -126,7 +127,7 @@ DeviceHandler::DeviceHandler(QObject *parent) :
setParams("GetStateValue", params); setParams("GetStateValue", params);
returns.insert("success", "bool"); returns.insert("success", "bool");
returns.insert("errorMessage", "string"); returns.insert("errorMessage", "string");
returns.insert("value", "variant"); returns.insert("value", "o:variant");
setReturns("GetStateValue", returns); setReturns("GetStateValue", returns);
// Notifications // Notifications
@ -158,10 +159,15 @@ QVariantMap DeviceHandler::GetSupportedVendors(const QVariantMap &params) const
QVariantMap DeviceHandler::GetSupportedDevices(const QVariantMap &params) const QVariantMap DeviceHandler::GetSupportedDevices(const QVariantMap &params) const
{ {
Q_UNUSED(params)
QVariantMap returns; QVariantMap returns;
QVariantList supportedDeviceList; QVariantList supportedDeviceList;
foreach (const DeviceClass &deviceClass, GuhCore::instance()->deviceManager()->supportedDevices()) { QList<DeviceClass> supportedDevices;
if (params.contains("vendorId")) {
supportedDevices = GuhCore::instance()->deviceManager()->supportedDevices(VendorId(params.value("vendorId").toString()));
} else {
supportedDevices = GuhCore::instance()->deviceManager()->supportedDevices();
}
foreach (const DeviceClass &deviceClass, supportedDevices) {
supportedDeviceList.append(JsonTypes::packDeviceClass(deviceClass)); supportedDeviceList.append(JsonTypes::packDeviceClass(deviceClass));
} }
returns.insert("deviceClasses", supportedDeviceList); returns.insert("deviceClasses", supportedDeviceList);
@ -202,6 +208,7 @@ QVariantMap DeviceHandler::AddConfiguredDevice(const QVariantMap &params)
switch(status) { switch(status) {
case DeviceManager::DeviceErrorNoError: case DeviceManager::DeviceErrorNoError:
returns.insert("success", true); returns.insert("success", true);
returns.insert("errorMessage", "");
returns.insert("deviceId", newDeviceId); returns.insert("deviceId", newDeviceId);
break; break;
case DeviceManager::DeviceErrorDeviceClassNotFound: case DeviceManager::DeviceErrorDeviceClassNotFound:
@ -310,6 +317,7 @@ QVariantMap DeviceHandler::GetStateValue(const QVariantMap &params) const
QVariant stateValue = device->stateValue(params.value("stateTypeId").toUuid()); QVariant stateValue = device->stateValue(params.value("stateTypeId").toUuid());
returns.insert("success", true); returns.insert("success", true);
returns.insert("errorMessage", "");
returns.insert("value", stateValue); returns.insert("value", stateValue);
return returns; return returns;
} }

View File

@ -192,7 +192,7 @@ void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &jsonDat
QVariantMap returns; QVariantMap returns;
QMetaObject::invokeMethod(handler, method.toLatin1().data(), Q_RETURN_ARG(QVariantMap, returns), Q_ARG(QVariantMap, params)); QMetaObject::invokeMethod(handler, method.toLatin1().data(), Q_RETURN_ARG(QVariantMap, returns), Q_ARG(QVariantMap, params));
// Q_ASSERT((targetNamespace == "JSONRPC" && method == "Introspect") || handler->validateReturns(method, returns)); Q_ASSERT((targetNamespace == "JSONRPC" && method == "Introspect") || handler->validateReturns(method, returns).first);
sendResponse(clientId, commandId, returns); sendResponse(clientId, commandId, returns);
} }

View File

@ -263,7 +263,10 @@ QPair<bool, QString> JsonTypes::validateMap(const QVariantMap &templateMap, cons
{ {
s_lastError.clear(); s_lastError.clear();
foreach (const QString &key, templateMap.keys()) { foreach (const QString &key, templateMap.keys()) {
if (key != "params" && !map.contains(key)) { if (templateMap.value(key).toString().startsWith("o:")) {
continue;
}
if (!map.contains(key)) {
qDebug() << "missing key" << key << templateMap << map; qDebug() << "missing key" << key << templateMap << map;
QJsonDocument jsonDoc = QJsonDocument::fromVariant(map); QJsonDocument jsonDoc = QJsonDocument::fromVariant(map);
return report(false, QString("Missing key \"%1\" in %2").arg(key).arg(QString(jsonDoc.toJson()))); return report(false, QString("Missing key \"%1\" in %2").arg(key).arg(QString(jsonDoc.toJson())));
@ -279,20 +282,23 @@ QPair<bool, QString> JsonTypes::validateMap(const QVariantMap &templateMap, cons
QPair<bool, QString> JsonTypes::validateProperty(const QVariant &templateValue, const QVariant &value) QPair<bool, QString> JsonTypes::validateProperty(const QVariant &templateValue, const QVariant &value)
{ {
if (templateValue == "uuid") { QString strippedTemplateValue = templateValue.toString();
strippedTemplateValue.remove(QRegExp("^o:"));
if (strippedTemplateValue == "uuid") {
QString errorString = QString("Param %1 is not a uuid.").arg(value.toString()); QString errorString = QString("Param %1 is not a uuid.").arg(value.toString());
return report(value.canConvert(QVariant::Uuid), errorString); return report(value.canConvert(QVariant::Uuid), errorString);
} }
if (templateValue == "string") { if (strippedTemplateValue == "string") {
QString errorString = QString("Param %1 is not a string.").arg(value.toString()); QString errorString = QString("Param %1 is not a string.").arg(value.toString());
return report(value.canConvert(QVariant::String), errorString); return report(value.canConvert(QVariant::String), errorString);
} }
if (templateValue == "bool") { if (strippedTemplateValue == "bool") {
QString errorString = QString("Param %1 is not a bool.").arg(value.toString()); QString errorString = QString("Param %1 is not a bool.").arg(value.toString());
return report(value.canConvert(QVariant::Bool), errorString); return report(value.canConvert(QVariant::Bool), errorString);
} }
qWarning() << QString("Unhandled property type: %1 (expected: %2)").arg(value.toString()).arg(templateValue.toString()); qWarning() << QString("Unhandled property type: %1 (expected: %2)").arg(value.toString()).arg(strippedTemplateValue);
QString errorString = QString("Unhandled property type: %1 (expected: %2)").arg(value.toString()).arg(templateValue.toString()); QString errorString = QString("Unhandled property type: %1 (expected: %2)").arg(value.toString()).arg(strippedTemplateValue);
return report(false, errorString); return report(false, errorString);
} }
@ -339,6 +345,11 @@ QPair<bool, QString> JsonTypes::validateVariant(const QVariant &templateVariant,
qDebug() << "device not valid"; qDebug() << "device not valid";
return result; return result;
} }
} else if (refName == vendorRef()) {
QPair<bool, QString> result = validateMap(vendorDescription(), variant.toMap());
if (!result.first) {
qDebug() << "value not allowed in" << vendorRef();
}
} else if (refName == deviceClassRef()) { } else if (refName == deviceClassRef()) {
QPair<bool, QString> result = validateMap(deviceClassDescription(), variant.toMap()); QPair<bool, QString> result = validateMap(deviceClassDescription(), variant.toMap());
if (!result.first) { if (!result.first) {

View File

@ -46,6 +46,8 @@ private slots:
void introspect(); void introspect();
void getSupportedVendors(); void getSupportedVendors();
void getSupportedDevices_data();
void getSupportedDevices(); void getSupportedDevices();
void enableDisableNotifications_data(); void enableDisableNotifications_data();
@ -217,14 +219,33 @@ void TestJSONRPC::getSupportedVendors()
QCOMPARE(vendorId, guhVendorId); QCOMPARE(vendorId, guhVendorId);
} }
void TestJSONRPC::getSupportedDevices_data()
{
QTest::addColumn<VendorId>("vendorId");
QTest::addColumn<int>("resultCount");
QTest::newRow("vendor guh") << guhVendorId << 1;
QTest::newRow("no filter") << VendorId() << 1;
QTest::newRow("invalid vendor") << VendorId("93e7d361-8025-4354-b17e-b68406c800bc") << 0;
}
void TestJSONRPC::getSupportedDevices() void TestJSONRPC::getSupportedDevices()
{ {
QVariant supportedDevices = injectAndWait("Devices.GetSupportedDevices"); QFETCH(VendorId, vendorId);
QFETCH(int, resultCount);
QVariantMap params;
if (!vendorId.isNull()) {
params.insert("vendorId", vendorId);
}
QVariant supportedDevices = injectAndWait("Devices.GetSupportedDevices", params);
// Make sure there is exactly 1 supported device class with the name Mock Wifi Device // Make sure there is exactly 1 supported device class with the name Mock Wifi Device
QCOMPARE(supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().count(), 1); QCOMPARE(supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().count(), resultCount);
QString deviceName = supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().first().toMap().value("name").toString(); if (resultCount > 0) {
QCOMPARE(deviceName, QString("Mock Device")); QString deviceName = supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().first().toMap().value("name").toString();
QCOMPARE(deviceName, QString("Mock Device"));
}
} }
void TestJSONRPC::enableDisableNotifications_data() void TestJSONRPC::enableDisableNotifications_data()