diff --git a/libnymea-core/jsonrpc/jsonvalidator.cpp b/libnymea-core/jsonrpc/jsonvalidator.cpp index b3691312..6cfe24a3 100644 --- a/libnymea-core/jsonrpc/jsonvalidator.cpp +++ b/libnymea-core/jsonrpc/jsonvalidator.cpp @@ -75,7 +75,7 @@ bool JsonValidator::checkRefs(const QVariantMap &map, const QVariantMap &api) JsonValidator::Result JsonValidator::validateParams(const QVariantMap ¶ms, const QString &method, const QVariantMap &api) { QVariantMap paramDefinition = api.value("methods").toMap().value(method).toMap().value("params").toMap(); - m_result = validateMap(params, paramDefinition, api); + m_result = validateMap(params, paramDefinition, api, QIODevice::WriteOnly); m_result.setWhere(method + ", param " + m_result.where()); return m_result; } @@ -83,7 +83,7 @@ JsonValidator::Result JsonValidator::validateParams(const QVariantMap ¶ms, c JsonValidator::Result JsonValidator::validateReturns(const QVariantMap &returns, const QString &method, const QVariantMap &api) { QVariantMap returnsDefinition = api.value("methods").toMap().value(method).toMap().value("returns").toMap(); - m_result = validateMap(returns, returnsDefinition, api); + m_result = validateMap(returns, returnsDefinition, api, QIODevice::ReadOnly); m_result.setWhere(method + ", returns " + m_result.where()); return m_result; } @@ -91,7 +91,7 @@ JsonValidator::Result JsonValidator::validateReturns(const QVariantMap &returns, JsonValidator::Result JsonValidator::validateNotificationParams(const QVariantMap ¶ms, const QString ¬ification, const QVariantMap &api) { QVariantMap paramDefinition = api.value("notifications").toMap().value(notification).toMap().value("params").toMap(); - m_result = validateMap(params, paramDefinition, api); + m_result = validateMap(params, paramDefinition, api, QIODevice::ReadOnly); m_result.setWhere(notification + ", param " + m_result.where()); return m_result; } @@ -101,14 +101,21 @@ JsonValidator::Result JsonValidator::result() const return m_result; } -JsonValidator::Result JsonValidator::validateMap(const QVariantMap &map, const QVariantMap &definition, const QVariantMap &api) +JsonValidator::Result JsonValidator::validateMap(const QVariantMap &map, const QVariantMap &definition, const QVariantMap &api, QIODevice::OpenMode openMode) { // Make sure all required values are available foreach (const QString &key, definition.keys()) { - if (key.startsWith("o:")) { + QRegExp isOptional = QRegExp("^([a-z]:)*o:.*"); + if (isOptional.exactMatch(key)) { continue; } - if (!map.contains(key)) { + QRegExp isReadOnly = QRegExp("^([a-z]:)*r:.*"); + if (isReadOnly.exactMatch(key) && openMode.testFlag(QIODevice::WriteOnly)) { + continue; + } + QString trimmedKey = key; + trimmedKey.remove(QRegExp("^(o:|r:)")); + if (!map.contains(trimmedKey)) { return Result(false, "Missing required key: " + key, key); } } @@ -117,6 +124,15 @@ JsonValidator::Result JsonValidator::validateMap(const QVariantMap &map, const Q foreach (const QString &key, map.keys()) { // Is the key allowed in here? QVariant expectedValue = definition.value(key); + foreach (const QString &definitionKey, definition.keys()) { + QRegExp regExp = QRegExp("(o:|r:)*" + key); + if (regExp.exactMatch(definitionKey)) { + expectedValue = definition.value(definitionKey); + } + } + if (!expectedValue.isValid()) { + expectedValue = definition.value("o:" + key); + } if (!expectedValue.isValid()) { expectedValue = definition.value("o:" + key); } @@ -127,7 +143,7 @@ JsonValidator::Result JsonValidator::validateMap(const QVariantMap &map, const Q // Validate content QVariant value = map.value(key); - Result result = validateEntry(value, expectedValue, api); + Result result = validateEntry(value, expectedValue, api, openMode); if (!result.success()) { result.setWhere(key + '.' + result.where()); result.setErrorString(result.errorString()); @@ -139,7 +155,7 @@ JsonValidator::Result JsonValidator::validateMap(const QVariantMap &map, const Q return Result(true); } -JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const QVariant &definition, const QVariantMap &api) +JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const QVariant &definition, const QVariantMap &api, QIODevice::OpenMode openMode) { if (definition.type() == QVariant::String) { QString expectedTypeName = definition.toString(); @@ -168,7 +184,7 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const } QString flagEnum = refDefinition.toList().first().toString(); foreach (const QVariant &flagsEntry, value.toList()) { - Result result = validateEntry(flagsEntry, flagEnum, api); + Result result = validateEntry(flagsEntry, flagEnum, api, openMode); if (!result.success()) { return result; } @@ -178,7 +194,7 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const QVariantMap types = api.value("types").toMap(); QVariant refDefinition = types.value(refName); - return validateEntry(value, refDefinition, api); + return validateEntry(value, refDefinition, api, openMode); } JsonHandler::BasicType expectedBasicType = JsonHandler::enumNameToValue(expectedTypeName); @@ -240,7 +256,7 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const if (value.type() != QVariant::Map) { return Result(false, "Invalid value. Expected a map bug received: " + value.toString()); } - return validateMap(value.toMap(), definition.toMap(), api); + return validateMap(value.toMap(), definition.toMap(), api, openMode); } if (definition.type() == QVariant::List) { @@ -250,7 +266,7 @@ JsonValidator::Result JsonValidator::validateEntry(const QVariant &value, const return Result(false, "Expected list of " + entryDefinition.toString() + " but got value of type " + value.typeName() + "\n" + QJsonDocument::fromVariant(value).toJson()); } foreach (const QVariant &entry, value.toList()) { - Result result = validateEntry(entry, entryDefinition, api); + Result result = validateEntry(entry, entryDefinition, api, openMode); if (!result.success()) { return result; } diff --git a/libnymea-core/jsonrpc/jsonvalidator.h b/libnymea-core/jsonrpc/jsonvalidator.h index 7b168fec..e59f41b4 100644 --- a/libnymea-core/jsonrpc/jsonvalidator.h +++ b/libnymea-core/jsonrpc/jsonvalidator.h @@ -23,6 +23,7 @@ #include #include +#include namespace nymeaserver { @@ -58,8 +59,8 @@ public: Result result() const; private: - Result validateMap(const QVariantMap &map, const QVariantMap &definition, const QVariantMap &api); - Result validateEntry(const QVariant &value, const QVariant &definition, const QVariantMap &api); + Result validateMap(const QVariantMap &map, const QVariantMap &definition, const QVariantMap &api, QIODevice::OpenMode openMode); + Result validateEntry(const QVariant &value, const QVariant &definition, const QVariantMap &api, QIODevice::OpenMode openMode); Result m_result; }; diff --git a/libnymea/jsonrpc/jsonhandler.cpp b/libnymea/jsonrpc/jsonhandler.cpp index 42e6ee86..3ea88515 100644 --- a/libnymea/jsonrpc/jsonhandler.cpp +++ b/libnymea/jsonrpc/jsonhandler.cpp @@ -173,6 +173,9 @@ void JsonHandler::registerObject(const QMetaObject &metaObject) if (metaProperty.isUser()) { name.prepend("o:"); } + if (!metaProperty.isWritable()) { + name.prepend("r:"); + } QVariant typeName; if (metaProperty.type() == QVariant::UserType) { if (metaProperty.typeName() == QStringLiteral("QVariant::Type")) { @@ -305,12 +308,22 @@ QVariant JsonHandler::pack(const QMetaObject &metaObject, const void *value) con continue; } - // Manually converting QList... Only QVariantList is known to the meta system - if (propertyTypeName.startsWith("QList")) { + // Manually converting QList... Only QVariantList is known to the meta system + if (propertyTypeName.startsWith("QList<")) { QVariantList list; - foreach (int entry, propertyValue.value>()) { - list << entry; + if (propertyTypeName == "QList") { + foreach (int entry, propertyValue.value>()) { + list << entry; + } + } else if (propertyTypeName == "QList") { + foreach (const QUuid &entry, propertyValue.value>()) { + list << entry; + } + } else { + Q_ASSERT_X(false, this->metaObject()->className(), QString("Unhandled list type: %1").arg(propertyTypeName).toUtf8()); + qCWarning(dcJsonRpc()) << "Cannot pack property of unhandled list type" << propertyTypeName; } + if (!list.isEmpty() || !metaProperty.isUser()) { ret.insert(metaProperty.name(), list); } @@ -410,12 +423,20 @@ QVariant JsonHandler::unpack(const QMetaObject &metaObject, const QVariant &valu continue; } - if (metaProperty.typeName() == QStringLiteral("QList")) { - QList intList; - foreach (const QVariant &val, variant.toList()) { - intList.append(val.toInt()); + if (QString(metaProperty.typeName()).startsWith("QList<")) { + if (metaProperty.typeName() == QStringLiteral("QList")) { + QList intList; + foreach (const QVariant &val, variant.toList()) { + intList.append(val.toInt()); + } + metaProperty.writeOnGadget(ptr, QVariant::fromValue(intList)); + } else if (metaProperty.typeName() == QStringLiteral("QList")) { + QList uuidList; + foreach (const QVariant &val, variant.toList()) { + uuidList.append(val.toUuid()); + } + metaProperty.writeOnGadget(ptr, QVariant::fromValue(uuidList)); } - metaProperty.writeOnGadget(ptr, QVariant::fromValue(intList)); continue; } diff --git a/libnymea/time/repeatingoption.cpp b/libnymea/time/repeatingoption.cpp index 159be676..6dea237f 100644 --- a/libnymea/time/repeatingoption.cpp +++ b/libnymea/time/repeatingoption.cpp @@ -210,43 +210,3 @@ QDebug operator<<(QDebug dbg, const RepeatingOption &repeatingOption) dbg.nospace() << "RepeatingOption(Mode:" << repeatingOption.mode() << ", Monthdays:" << repeatingOption.monthDays() << "Weekdays:" << repeatingOption.weekDays() << ")"; return dbg; } - -WeekDays::WeekDays() -{ - -} - -WeekDays::WeekDays(const QList &other): QList(other) -{ - -} - -QVariant WeekDays::get(int index) const -{ - return at(index); -} - -void WeekDays::put(const QVariant &value) -{ - append(value.toInt()); -} - -MonthDays::MonthDays() -{ - -} - -MonthDays::MonthDays(const QList &other): QList(other) -{ - -} - -QVariant MonthDays::get(int index) const -{ - return at(index); -} - -void MonthDays::put(const QVariant &value) -{ - append(value.toInt()); -} diff --git a/libnymea/time/repeatingoption.h b/libnymea/time/repeatingoption.h index 4015b5a7..071d1fcb 100644 --- a/libnymea/time/repeatingoption.h +++ b/libnymea/time/repeatingoption.h @@ -27,30 +27,6 @@ class QDateTime; -class WeekDays: public QList -{ - Q_GADGET - Q_PROPERTY(int count READ count) -public: - WeekDays(); - WeekDays(const QList &other); - Q_INVOKABLE QVariant get(int index) const; - Q_INVOKABLE void put(const QVariant &value); -}; -Q_DECLARE_METATYPE(WeekDays) - -class MonthDays: public QList -{ - Q_GADGET - Q_PROPERTY(int count READ count) -public: - MonthDays(); - MonthDays(const QList &other); - Q_INVOKABLE QVariant get(int index) const; - Q_INVOKABLE void put(const QVariant &value); -}; -Q_DECLARE_METATYPE(MonthDays) - class RepeatingOption { Q_GADGET diff --git a/tests/auto/api.json b/tests/auto/api.json index 95483d3c..f78a8b5a 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -1683,10 +1683,10 @@ }, "ActionType": { "displayName": "String", - "id": "Uuid", "index": "Int", "name": "String", - "paramTypes": "$ref:ParamTypes" + "paramTypes": "$ref:ParamTypes", + "r:id": "Uuid" }, "ActionTypes": [ "$ref:ActionType" @@ -1715,51 +1715,51 @@ "$ref:CalendarItem" ], "Device": { - "deviceClassId": "Uuid", - "id": "Uuid", "name": "String", "o:parentId": "Uuid", "params": "$ref:ParamList", + "r:deviceClassId": "Uuid", + "r:id": "Uuid", "settings": "$ref:ParamList", "setupComplete": "Bool", "states": "$ref:States" }, "DeviceClass": { - "actionTypes": "$ref:ActionTypes", - "browsable": "Bool", - "browserItemActionTypes": "$ref:ActionTypes", - "createMethods": "$ref:CreateMethods", - "discoveryParamTypes": "$ref:ParamTypes", - "displayName": "String", - "eventTypes": "$ref:EventTypes", - "id": "Uuid", - "interfaces": "StringList", - "name": "String", - "paramTypes": "$ref:ParamTypes", - "pluginId": "Uuid", - "settingsTypes": "$ref:ParamTypes", - "setupMethod": "$ref:SetupMethod", - "stateTypes": "$ref:StateTypes", - "vendorId": "Uuid" + "r:actionTypes": "$ref:ActionTypes", + "r:browsable": "Bool", + "r:browserItemActionTypes": "$ref:ActionTypes", + "r:createMethods": "$ref:CreateMethods", + "r:discoveryParamTypes": "$ref:ParamTypes", + "r:displayName": "String", + "r:eventTypes": "$ref:EventTypes", + "r:id": "Uuid", + "r:interfaces": "StringList", + "r:name": "String", + "r:paramTypes": "$ref:ParamTypes", + "r:pluginId": "Uuid", + "r:settingsTypes": "$ref:ParamTypes", + "r:setupMethod": "$ref:SetupMethod", + "r:stateTypes": "$ref:StateTypes", + "r:vendorId": "Uuid" }, "DeviceClasses": [ "$ref:DeviceClass" ], "DeviceDescriptor": { - "description": "String", - "deviceParams": "$ref:ParamList", - "id": "Uuid", - "o:deviceId": "Uuid", - "title": "String" + "r:description": "String", + "r:deviceParams": "$ref:ParamList", + "r:id": "Uuid", + "r:o:deviceId": "Uuid", + "r:title": "String" }, "DeviceDescriptors": [ "$ref:DeviceDescriptor" ], "DevicePlugin": { - "displayName": "String", - "id": "Uuid", - "name": "String", - "paramTypes": "$ref:ParamTypes" + "r:displayName": "String", + "r:id": "Uuid", + "r:name": "String", + "r:paramTypes": "$ref:ParamTypes" }, "DevicePlugins": [ "$ref:DevicePlugin" @@ -1768,9 +1768,9 @@ "$ref:Device" ], "Event": { - "deviceId": "Uuid", - "eventTypeId": "Uuid", - "params": "$ref:ParamList" + "r:deviceId": "Uuid", + "r:eventTypeId": "Uuid", + "r:params": "$ref:ParamList" }, "EventDescriptor": { "o:deviceId": "Uuid", @@ -1784,10 +1784,10 @@ ], "EventType": { "displayName": "String", - "id": "Uuid", - "index": "Int", "name": "String", - "paramTypes": "$ref:ParamTypes" + "paramTypes": "$ref:ParamTypes", + "r:id": "Uuid", + "r:index": "Int" }, "EventTypes": [ "$ref:EventType" @@ -1800,15 +1800,15 @@ "$ref:LogEntry" ], "LogEntry": { - "loggingLevel": "$ref:LoggingLevel", - "o:active": "Bool", - "o:deviceId": "Uuid", - "o:errorCode": "String", - "o:eventType": "$ref:LoggingEventType", - "o:typeId": "Uuid", - "o:value": "Variant", - "source": "$ref:LoggingSource", - "timestamp": "Uint" + "r:loggingLevel": "$ref:LoggingLevel", + "r:o:active": "Bool", + "r:o:deviceId": "Uuid", + "r:o:errorCode": "String", + "r:o:eventType": "$ref:LoggingEventType", + "r:o:typeId": "Uuid", + "r:o:value": "Variant", + "r:source": "$ref:LoggingSource", + "r:timestamp": "Uint" }, "MqttPolicy": { "allowedPublishTopicFilters": "StringList", @@ -1818,15 +1818,15 @@ "username": "String" }, "Package": { - "canRemove": "Bool", - "candidateVersion": "String", - "changelog": "String", - "displayName": "String", - "id": "String", - "installedVersion": "String", - "rollbackAvailable": "Bool", - "summary": "String", - "updateAvailable": "Bool" + "r:canRemove": "Bool", + "r:candidateVersion": "String", + "r:changelog": "String", + "r:displayName": "String", + "r:id": "String", + "r:installedVersion": "String", + "r:rollbackAvailable": "Bool", + "r:summary": "String", + "r:updateAvailable": "Bool" }, "Packages": [ "$ref:Package" @@ -1849,7 +1849,6 @@ ], "ParamType": { "displayName": "String", - "id": "Uuid", "index": "Int", "name": "String", "o:allowedValues": [ @@ -1861,6 +1860,7 @@ "o:minValue": "Variant", "o:readOnly": "Bool", "o:unit": "$ref:Unit", + "r:id": "Uuid", "type": "$ref:BasicType" }, "ParamTypes": [ @@ -1879,21 +1879,21 @@ "$ref:Repository" ], "Repository": { - "displayName": "String", "enabled": "Bool", - "id": "String" + "r:displayName": "String", + "r:id": "String" }, "Rule": { "actions": "$ref:RuleActions", "name": "String", - "o:active": "Bool", "o:enabled": "Bool", "o:eventDescriptors": "$ref:EventDescriptors", "o:executable": "Bool", "o:exitActions": "$ref:RuleActions", "o:id": "Uuid", "o:stateEvaluator": "$ref:StateEvaluator", - "o:timeDescriptor": "$ref:TimeDescriptor" + "o:timeDescriptor": "$ref:TimeDescriptor", + "r:o:active": "Bool" }, "RuleAction": { "o:actionTypeId": "Uuid", @@ -1936,8 +1936,8 @@ "sslEnabled": "Bool" }, "State": { - "stateTypeId": "Uuid", - "value": "Variant" + "r:stateTypeId": "Uuid", + "r:value": "Variant" }, "StateDescriptor": { "o:deviceId": "Uuid", @@ -1958,7 +1958,6 @@ "StateType": { "defaultValue": "Variant", "displayName": "String", - "id": "Uuid", "index": "Int", "name": "String", "o:maxValue": "Variant", @@ -1967,6 +1966,7 @@ "Variant" ], "o:unit": "$ref:Unit", + "r:id": "Uuid", "type": "$ref:BasicType" }, "StateTypes": [ @@ -1998,10 +1998,10 @@ "$ref:TimeEventItem" ], "TokenInfo": { - "creationTime": "Uint", - "deviveName": "String", - "id": "Uuid", - "username": "String" + "r:creationTime": "Uint", + "r:deviveName": "String", + "r:id": "Uuid", + "r:username": "String" }, "Vendor": { "displayName": "String", @@ -2020,18 +2020,18 @@ "sslEnabled": "Bool" }, "WiredNetworkDevice": { - "bitRate": "String", - "interface": "String", - "macAddress": "String", - "pluggedIn": "Bool", - "state": "$ref:NetworkDeviceState" + "r:bitRate": "String", + "r:interface": "String", + "r:macAddress": "String", + "r:pluggedIn": "Bool", + "r:state": "$ref:NetworkDeviceState" }, "WirelessAccessPoint": { - "frequency": "Double", - "macAddress": "String", - "protected": "Bool", - "signalStrength": "Int", - "ssid": "String" + "r:frequency": "Double", + "r:macAddress": "String", + "r:protected": "Bool", + "r:signalStrength": "Int", + "r:ssid": "String" }, "WirelessNetworkDevice": { "bitRate": "String",