diff --git a/doc/jsonrpc.qdoc b/doc/jsonrpc.qdoc index 8f33b868..1cecb20e 100644 --- a/doc/jsonrpc.qdoc +++ b/doc/jsonrpc.qdoc @@ -31,6 +31,8 @@ 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 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 diff --git a/libguh/devicemanager.cpp b/libguh/devicemanager.cpp index bb7b7d21..09292fe0 100644 --- a/libguh/devicemanager.cpp +++ b/libguh/devicemanager.cpp @@ -121,10 +121,20 @@ QList DeviceManager::supportedVendors() const 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 DeviceManager::supportedDevices(const VendorId &vendorId) const { - return m_supportedDevices.values(); + qDebug() << "returning devices" << vendorId; + QList 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. diff --git a/libguh/typeutils.h b/libguh/typeutils.h index 8e89e788..0f30c628 100644 --- a/libguh/typeutils.h +++ b/libguh/typeutils.h @@ -1,6 +1,7 @@ #ifndef TYPEUTILS_H #define TYPEUTILS_H +#include #include #define DECLARE_TYPE_ID(type) class type##Id: public QUuid \ @@ -10,12 +11,13 @@ public: \ type##Id(): QUuid() {} \ static type##Id create##type##Id() { return type##Id(QUuid::createUuid().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(DeviceClass) +DECLARE_TYPE_ID(Device) DECLARE_TYPE_ID(EventType) DECLARE_TYPE_ID(StateType) diff --git a/server/jsonrpc/devicehandler.cpp b/server/jsonrpc/devicehandler.cpp index c54339a0..86176264 100644 --- a/server/jsonrpc/devicehandler.cpp +++ b/server/jsonrpc/devicehandler.cpp @@ -40,7 +40,8 @@ DeviceHandler::DeviceHandler(QObject *parent) : setReturns("GetSupportedVendors", returns); 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); QVariantList deviceClasses; deviceClasses.append(JsonTypes::deviceClassRef()); @@ -73,7 +74,7 @@ DeviceHandler::DeviceHandler(QObject *parent) : setParams("AddConfiguredDevice", params); returns.insert("success", "bool"); returns.insert("errorMessage", "string"); - returns.insert("deviceId", "uuid"); + returns.insert("deviceId", "o:uuid"); setReturns("AddConfiguredDevice", returns); params.clear(); returns.clear(); @@ -126,7 +127,7 @@ DeviceHandler::DeviceHandler(QObject *parent) : setParams("GetStateValue", params); returns.insert("success", "bool"); returns.insert("errorMessage", "string"); - returns.insert("value", "variant"); + returns.insert("value", "o:variant"); setReturns("GetStateValue", returns); // Notifications @@ -158,10 +159,15 @@ QVariantMap DeviceHandler::GetSupportedVendors(const QVariantMap ¶ms) const QVariantMap DeviceHandler::GetSupportedDevices(const QVariantMap ¶ms) const { - Q_UNUSED(params) QVariantMap returns; QVariantList supportedDeviceList; - foreach (const DeviceClass &deviceClass, GuhCore::instance()->deviceManager()->supportedDevices()) { + QList 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)); } returns.insert("deviceClasses", supportedDeviceList); @@ -202,6 +208,7 @@ QVariantMap DeviceHandler::AddConfiguredDevice(const QVariantMap ¶ms) switch(status) { case DeviceManager::DeviceErrorNoError: returns.insert("success", true); + returns.insert("errorMessage", ""); returns.insert("deviceId", newDeviceId); break; case DeviceManager::DeviceErrorDeviceClassNotFound: @@ -310,6 +317,7 @@ QVariantMap DeviceHandler::GetStateValue(const QVariantMap ¶ms) const QVariant stateValue = device->stateValue(params.value("stateTypeId").toUuid()); returns.insert("success", true); + returns.insert("errorMessage", ""); returns.insert("value", stateValue); return returns; } diff --git a/server/jsonrpc/jsonrpcserver.cpp b/server/jsonrpc/jsonrpcserver.cpp index c0b606e8..5a443253 100644 --- a/server/jsonrpc/jsonrpcserver.cpp +++ b/server/jsonrpc/jsonrpcserver.cpp @@ -192,7 +192,7 @@ void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &jsonDat QVariantMap returns; 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); } diff --git a/server/jsonrpc/jsontypes.cpp b/server/jsonrpc/jsontypes.cpp index fa54ff6d..69403c7a 100644 --- a/server/jsonrpc/jsontypes.cpp +++ b/server/jsonrpc/jsontypes.cpp @@ -263,7 +263,10 @@ QPair JsonTypes::validateMap(const QVariantMap &templateMap, cons { s_lastError.clear(); 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; QJsonDocument jsonDoc = QJsonDocument::fromVariant(map); return report(false, QString("Missing key \"%1\" in %2").arg(key).arg(QString(jsonDoc.toJson()))); @@ -279,20 +282,23 @@ QPair JsonTypes::validateMap(const QVariantMap &templateMap, cons QPair 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()); 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()); 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()); return report(value.canConvert(QVariant::Bool), errorString); } - qWarning() << 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(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(strippedTemplateValue); return report(false, errorString); } @@ -339,6 +345,11 @@ QPair JsonTypes::validateVariant(const QVariant &templateVariant, qDebug() << "device not valid"; return result; } + } else if (refName == vendorRef()) { + QPair result = validateMap(vendorDescription(), variant.toMap()); + if (!result.first) { + qDebug() << "value not allowed in" << vendorRef(); + } } else if (refName == deviceClassRef()) { QPair result = validateMap(deviceClassDescription(), variant.toMap()); if (!result.first) { diff --git a/tests/auto/testjsonrpc.cpp b/tests/auto/testjsonrpc.cpp index 70c4f396..98b41af4 100644 --- a/tests/auto/testjsonrpc.cpp +++ b/tests/auto/testjsonrpc.cpp @@ -46,6 +46,8 @@ private slots: void introspect(); void getSupportedVendors(); + + void getSupportedDevices_data(); void getSupportedDevices(); void enableDisableNotifications_data(); @@ -217,14 +219,33 @@ void TestJSONRPC::getSupportedVendors() QCOMPARE(vendorId, guhVendorId); } +void TestJSONRPC::getSupportedDevices_data() +{ + QTest::addColumn("vendorId"); + QTest::addColumn("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() { - 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 - QCOMPARE(supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().count(), 1); - QString deviceName = supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().first().toMap().value("name").toString(); - QCOMPARE(deviceName, QString("Mock Device")); + QCOMPARE(supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().count(), resultCount); + if (resultCount > 0) { + QString deviceName = supportedDevices.toMap().value("params").toMap().value("deviceClasses").toList().first().toMap().value("name").toString(); + QCOMPARE(deviceName, QString("Mock Device")); + } } void TestJSONRPC::enableDisableNotifications_data()