Merge PR #140: Drop deprecated api

This commit is contained in:
Jenkins 2019-04-12 13:42:30 +02:00
commit 2d91a6243c
26 changed files with 258 additions and 931 deletions

4
debian/changelog vendored
View File

@ -1,3 +1,7 @@
nymea (0.12.0) UNRELEASED; urgency=medium
-- Michael Zanetti <michael.zanetti@guh.io> Fri, 22 Mar 2019 00:49:04 +0100
nymea (0.11.1) xenial; urgency=medium
[ Simon Stürz ]

View File

@ -30,29 +30,6 @@ See also: \l{Param}
}
\endcode
See also: \l{ParamType}
\section2 BasicTag
\code
[
"BasicTagService",
"BasicTagDevice",
"BasicTagSensor",
"BasicTagActuator",
"BasicTagLighting",
"BasicTagEnergy",
"BasicTagMultimedia",
"BasicTagWeather",
"BasicTagGateway",
"BasicTagHeating",
"BasicTagCooling",
"BasicTagNotification",
"BasicTagSecurity",
"BasicTagTime",
"BasicTagShading",
"BasicTagAppliance",
"BasicTagCamera",
"BasicTagLock"
]
\endcode
\section2 BasicType
\code
[
@ -134,13 +111,9 @@ See also: \l{Param}
"actionTypes": [
"$ref:ActionType"
],
"basicTags": [
"$ref:BasicTag"
],
"createMethods": [
"$ref:CreateMethod"
],
"deviceIcon": "$ref:DeviceIcon",
"discoveryParamTypes": [
"$ref:ParamType"
],
@ -153,9 +126,6 @@ See also: \l{Param}
"String"
],
"name": "String",
"o:criticalStateTypeId": "Uuid",
"o:primaryActionTypeId": "Uuid",
"o:primaryStateTypeId": "Uuid",
"paramTypes": [
"$ref:ParamType"
],
@ -167,7 +137,7 @@ See also: \l{Param}
"vendorId": "Uuid"
}
\endcode
See also: \l{ActionType}, \l{CreateMethod}, \l{DeviceIcon}, \l{BasicTag}, \l{ParamType}, \l{SetupMethod}, \l{StateType}, \l{EventType}, \l{ParamType}
See also: \l{ActionType}, \l{CreateMethod}, \l{ParamType}, \l{SetupMethod}, \l{StateType}, \l{EventType}, \l{ParamType}
\section2 DeviceDescriptor
\code
{
@ -206,47 +176,6 @@ See also: \l{ActionType}, \l{CreateMethod}, \l{DeviceIcon}, \l{BasicTag}, \l{Par
"DeviceErrorParameterNotWritable"
]
\endcode
\section2 DeviceIcon
\code
[
"DeviceIconNone",
"DeviceIconBed",
"DeviceIconBlinds",
"DeviceIconCeilingLamp",
"DeviceIconCouch",
"DeviceIconDeskLamp",
"DeviceIconDesk",
"DeviceIconHifi",
"DeviceIconPower",
"DeviceIconEnergy",
"DeviceIconRadio",
"DeviceIconSmartPhone",
"DeviceIconSocket",
"DeviceIconStandardLamp",
"DeviceIconSun",
"DeviceIconTablet",
"DeviceIconThermometer",
"DeviceIconTune",
"DeviceIconTv",
"DeviceIconBattery",
"DeviceIconDishwasher",
"DeviceIconWashingMachine",
"DeviceIconLaundryDryer",
"DeviceIconIrHeater",
"DeviceIconRadiator",
"DeviceIconSwitch",
"DeviceIconMotionDetectors",
"DeviceIconWeather",
"DeviceIconTime",
"DeviceIconLightBulb",
"DeviceIconGateway",
"DeviceIconMail",
"DeviceIconNetwork",
"DeviceIconCloud",
"DeviceIconGarage",
"DeviceIconRollerShutter"
]
\endcode
\section2 Event
\code
{
@ -278,8 +207,6 @@ See also: \l{ParamDescriptor}
"id": "Uuid",
"index": "Int",
"name": "String",
"o:graphRelevant": "Bool",
"o:ruleRelevant": "Bool",
"paramTypes": [
"$ref:ParamType"
]
@ -628,13 +555,11 @@ See also: \l{StateEvaluator}, \l{StateDescriptor}, \l{StateOperator}
"id": "Uuid",
"index": "Int",
"name": "String",
"o:graphRelevant": "Bool",
"o:maxValue": "Variant",
"o:minValue": "Variant",
"o:possibleValues": [
"Variant"
],
"o:ruleRelevant": "Bool",
"o:unit": "$ref:Unit",
"type": "$ref:BasicType"
}
@ -3403,26 +3328,6 @@ See also: \l{Tag}
"$ref:ParamType"
]
},
"BasicTag": [
"BasicTagService",
"BasicTagDevice",
"BasicTagSensor",
"BasicTagActuator",
"BasicTagLighting",
"BasicTagEnergy",
"BasicTagMultimedia",
"BasicTagWeather",
"BasicTagGateway",
"BasicTagHeating",
"BasicTagCooling",
"BasicTagNotification",
"BasicTagSecurity",
"BasicTagTime",
"BasicTagShading",
"BasicTagAppliance",
"BasicTagCamera",
"BasicTagLock"
],
"BasicType": [
"Uuid",
"String",
@ -3482,13 +3387,9 @@ See also: \l{Tag}
"actionTypes": [
"$ref:ActionType"
],
"basicTags": [
"$ref:BasicTag"
],
"createMethods": [
"$ref:CreateMethod"
],
"deviceIcon": "$ref:DeviceIcon",
"discoveryParamTypes": [
"$ref:ParamType"
],
@ -3501,9 +3402,6 @@ See also: \l{Tag}
"String"
],
"name": "String",
"o:criticalStateTypeId": "Uuid",
"o:primaryActionTypeId": "Uuid",
"o:primaryStateTypeId": "Uuid",
"paramTypes": [
"$ref:ParamType"
],
@ -3545,44 +3443,6 @@ See also: \l{Tag}
"DeviceErrorPairingTransactionIdNotFound",
"DeviceErrorParameterNotWritable"
],
"DeviceIcon": [
"DeviceIconNone",
"DeviceIconBed",
"DeviceIconBlinds",
"DeviceIconCeilingLamp",
"DeviceIconCouch",
"DeviceIconDeskLamp",
"DeviceIconDesk",
"DeviceIconHifi",
"DeviceIconPower",
"DeviceIconEnergy",
"DeviceIconRadio",
"DeviceIconSmartPhone",
"DeviceIconSocket",
"DeviceIconStandardLamp",
"DeviceIconSun",
"DeviceIconTablet",
"DeviceIconThermometer",
"DeviceIconTune",
"DeviceIconTv",
"DeviceIconBattery",
"DeviceIconDishwasher",
"DeviceIconWashingMachine",
"DeviceIconLaundryDryer",
"DeviceIconIrHeater",
"DeviceIconRadiator",
"DeviceIconSwitch",
"DeviceIconMotionDetectors",
"DeviceIconWeather",
"DeviceIconTime",
"DeviceIconLightBulb",
"DeviceIconGateway",
"DeviceIconMail",
"DeviceIconNetwork",
"DeviceIconCloud",
"DeviceIconGarage",
"DeviceIconRollerShutter"
],
"Event": {
"deviceId": "Uuid",
"eventTypeId": "Uuid",
@ -3604,8 +3464,6 @@ See also: \l{Tag}
"id": "Uuid",
"index": "Int",
"name": "String",
"o:graphRelevant": "Bool",
"o:ruleRelevant": "Bool",
"paramTypes": [
"$ref:ParamType"
]
@ -3855,13 +3713,11 @@ See also: \l{Tag}
"id": "Uuid",
"index": "Int",
"name": "String",
"o:graphRelevant": "Bool",
"o:maxValue": "Variant",
"o:minValue": "Variant",
"o:possibleValues": [
"Variant"
],
"o:ruleRelevant": "Bool",
"o:unit": "$ref:Unit",
"type": "$ref:BasicType"
},

View File

@ -192,13 +192,8 @@
"displayName": "The name of the device class (translatable)",
"o:createMethods": [ ],
"o:setupMethod": "SetupMethod",
"o:deviceIcon": "Icon",
"o:interfaces": [ "interfacename" ],
"o:basicTags": [ ],
"o:pairingInfo": "Information how to pair the device. (translatable)",
"o:criticalStateTypeId": "uuid",
"o:primaryStateTypeId": "uuid",
"o:primaryActionTypeId": "uuid",
"o:discoveryParamTypes": [ ],
"o:paramTypes": [ ],
"o:stateTypes": [ ],
@ -233,34 +228,9 @@
\li \tt interfaces
\li \b O
\li array
\li A string list of \l{Interfaces for DeviceClasses}{interfaces} this plugin implements. Interfaces show you how types
of this DeviceClass \underline{must} look like.
\li A string list of \l{Interfaces for DeviceClasses}{interfaces} this plugin implements. Interfaces define states, events and actions to
provide more defined ways of creating device class. A plugin developer should always try to follow interface definitions if possible.
\row
\li \tt basicTags
\li \b O
\li array
\li A string list of \l{DeviceClass::BasicTag}{BasicTags} for this device \unicode{0x2192} \l{DeviceClass::basicTags()}. A \l{DeviceClass} can have
multiple \l{DeviceClass::BasicTag}{BasicTags} which describe the basic category of the DeviceClass.
A \l{DeviceClass} should be eighter a Service or a Device, never both. See enum \l{DeviceClass::BasicTag} for more information.
The expected value for the \e basicTags parameters matches the enum name like this:
\tt {DeviceClass::BasicTagService} \unicode{0x2192} \tt {"basicTags": [ "Service" ]}
\tt {DeviceClass::BasicTagLighting} \unicode{0x2192} \tt {"basicTags": [ "Lighting" ]}
\tt ...
\row
\li \tt deviceIcon
\li \b O
\li string
\li Defines the icon for this \l{DeviceClass}. See enum \l{DeviceClass::DeviceIcon} for more information. The expected value for the \tt deviceIcon
parameters matches the enum name like this:
\tt {DeviceClass::DeviceIconBed} \unicode{0x2192} \tt {"deviceIcon": "Bed"}
\tt {DeviceClass::DeviceIconPower} \unicode{0x2192} \tt {"deviceIcon": "Power"}
\tt ...
\row
\li \tt createMethods
\li \b O
@ -295,21 +265,6 @@
\li The \l{DeviceClass::pairingInfo()}{pairingInfo} will inform the user how to pair the device \unicode{0x2192} \l{DeviceClass::setupMethod()}.
This parameter will only be used for \l{DeviceClass::SetupMethodDisplayPin}{DisplayPin} and \l{DeviceClass::SetupMethodEnterPin}{EnterPin}
and \l{DeviceClass::SetupMethodPushButton}{PushButton}. Example: "Please press the button on the device before continue."
\row
\li \tt criticalStateTypeId
\li \b O
\li string
\li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead.
\row
\li \tt primaryStateTypeId
\li \b O
\li string
\li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead.
\row
\li \tt primaryActionTypeId
\li \b O
\li string
\li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead.
\row
\li \tt discoveryParamTypes
\li \b O
@ -476,9 +431,6 @@ A \l{StateType} has following parameters:
"type": "DataType",
"defaultValue": "The state will be initialized with this value."
"o:cached": "bool",
"o:ruleRelevant": "bool",
"o:eventRuleRelevant": "bool",
"o:graphRelevant": "bool",
"o:unit": "The unit of the state value.",
"o:minValue": "Numeric minimum value for this state.",
"o:maxValue": "Numeric maximum value for this state.",
@ -537,23 +489,6 @@ A \l{StateType} has following parameters:
\li Indicates if a state value should be cached over reboot of the server. The value will be initialized with the last known value.
By default all states get chached. If you want to disable that behaviour you can set this property to \tt{false}. In that case the
value will be initialized with the default value of the State.
\row
\li \tt ruleRelevant
\li \b O
\li bool
\li Since not all \l{State}{States} make sense for the user in a rule, with this flag can be specified if this state should be visible
in the rule engine for the user or not. This flag has no effect to the ruleengine mechanism and is only ment to filter out not
interesting \l{State}{States}. By default, every state is rule relevant.
\row
\li \tt eventRuleRelevant
\li \b O
\li bool
\li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead.
\row
\li \tt graphRelevant
\li \b O
\li bool
\li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead.
\row
\li \tt unit
\li \b O
@ -710,8 +645,6 @@ A \l{StateType} has following parameters:
"id": "uuid",
"name": "eventName",
"displayName": "Name of the event (translatable)",
"o:ruleRelevant": "bool",
"o:graphRelevant": "bool",
"o:paramTypes": [
...
]
@ -745,18 +678,6 @@ A \l{StateType} has following parameters:
\li A list of \l{ParamType}{ParamTypes} which define the parameters of this event \unicode{0x2192} \l{EventType::paramTypes()}.
\b{See also:} \l{The ParamType definition}"
\row
\li \tt ruleRelevant
\li \b O
\li bool
\li Since not all \l{Event}{Events} make sense for the user in a rule, with this flag can be specidied if this event should be visible in the rule engine
for the user or not. This flag has no effect to the ruleengine mechanism and is only ment to filter out not interesting \l{Event}{Events}. By default,
every event is rule relevant.
\row
\li \tt graphRelevant
\li \b O
\li bool
\li Deprecated: please use \l{Interfaces for DeviceClasses}{interfaces} instead.
\endtable
*/

View File

@ -298,6 +298,12 @@ DeviceHandler::DeviceHandler(QObject *parent) :
params.insert("device", JsonTypes::deviceRef());
setParams("DeviceChanged", params);
params.clear(); returns.clear();
setDescription("PluginConfigurationChanged", "Emitted whenever a plugin's configuration is changed.");
params.insert("pluginId", JsonTypes::basicTypeToString(JsonTypes::Uuid));
params.insert("configuration", QVariantList() << JsonTypes::paramRef());
setParams("PluginConfigurationChanged", params);
connect(NymeaCore::instance(), &NymeaCore::pluginConfigChanged, this, &DeviceHandler::pluginConfigChanged);
connect(NymeaCore::instance(), &NymeaCore::deviceStateChanged, this, &DeviceHandler::deviceStateChanged);
connect(NymeaCore::instance(), &NymeaCore::deviceRemoved, this, &DeviceHandler::deviceRemovedNotification);
@ -637,11 +643,11 @@ void DeviceHandler::pluginConfigChanged(const PluginId &id, const ParamList &con
{
QVariantMap params;
params.insert("pluginId", id);
QVariantMap configMap;
QVariantList configList;
foreach (const Param &param, config) {
configMap.insert(param.paramTypeId().toString(), param.value());
configList << JsonTypes::packParam(param);
}
params.insert("configuration", configMap);
params.insert("configuration", configList);
emit PluginConfigurationChanged(params);
}

View File

@ -248,6 +248,11 @@ JsonReply *JsonRPCServer::Hello(const QVariantMap &params)
qCDebug(dcJsonRpc()) << "Client" << clientId << "initiated handshake." << m_clientLocales.value(clientId);
// If we waited for the handshake, here it is. Remove the timer...
if (m_newConnectionWaitTimers.contains(clientId)) {
delete m_newConnectionWaitTimers.take(clientId);
}
return createReply(createWelcomeMessage(interface, clientId));
}
@ -285,6 +290,7 @@ JsonReply* JsonRPCServer::Version(const QVariantMap &params) const
JsonReply* JsonRPCServer::SetNotificationStatus(const QVariantMap &params)
{
QUuid clientId = this->property("clientId").toUuid();
Q_ASSERT_X(m_clientNotifications.contains(clientId), "JsonRPCServer", "SetNotificationStatus for an unknown client called");
m_clientNotifications[clientId] = params.value("enabled").toBool();
QVariantMap returns;
returns.insert("enabled", m_clientNotifications[clientId]);
@ -585,12 +591,16 @@ void JsonRPCServer::processJsonPacket(TransportInterface *interface, const QUuid
if (NymeaCore::instance()->userManager()->initRequired()) {
if (!(targetNamespace == "JSONRPC" && authExemptMethodsNoUser.contains(method)) && (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token))) {
sendUnauthorizedResponse(interface, clientId, commandId, "Initial setup required. Call CreateUser first.");
qCWarning(dcJsonRpc()) << "Initial setup required but client does not call the setup. Dropping connection.";
interface->terminateClientConnection(clientId);
return;
}
} else {
// ok, we have a user. if there isn't a valid token, let's fail unless this is a Authenticate, Introspect Hello call
if (!(targetNamespace == "JSONRPC" && authExemptMethodsWithUser.contains(method)) && (token.isEmpty() || !NymeaCore::instance()->userManager()->verifyToken(token))) {
sendUnauthorizedResponse(interface, clientId, commandId, "Forbidden: Invalid token.");
qCWarning(dcJsonRpc()) << "Client did not not present a valid token. Dropping connection.";
interface->terminateClientConnection(clientId);
return;
}
}
@ -622,7 +632,16 @@ void JsonRPCServer::processJsonPacket(TransportInterface *interface, const QUuid
qCDebug(dcJsonRpc()) << "Invoking method" << targetNamespace << method.toLatin1().data();
if (targetNamespace != "JSONRPC" || method != "Hello") {
if (!(targetNamespace == "JSONRPC" && method == "Hello")) {
// This is not the handshake message. If we've waited for it, consider this a protocol violation and drop connection
if (m_newConnectionWaitTimers.contains(clientId)) {
sendErrorResponse(interface, clientId, commandId, "Handshake required. Call JSONRPC.Hello first.");
qCWarning(dcJsonRpc()) << "Connection requires a handshake but client did not initiate handshake. Dropping connection";
interface->terminateClientConnection(clientId);
return;
}
// Unless this is the Hello message, which allows setting the locale explicity, attach the locale
// for this connection
// If the client did request a locale in the Hello message, use that locale
@ -767,7 +786,17 @@ void JsonRPCServer::clientConnected(const QUuid &clientId)
// Initialize the connection locale to the settings default
m_clientLocales.insert(clientId, NymeaCore::instance()->configuration()->locale());
interface->sendData(clientId, QJsonDocument::fromVariant(createWelcomeMessage(interface, clientId)).toJson(QJsonDocument::Compact));
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, [this, timer, clientId, interface](){
// Client did not initiate handshake within timeout. Drop connection...
m_clientTransports.value(clientId)->disconnect();
timer->deleteLater();
m_newConnectionWaitTimers.remove(clientId);
qCDebug(dcJsonRpc()) << "Client" << clientId << "did not initiate the handshake within the required timeout. Dropping connection.";
interface->terminateClientConnection(clientId);
});
m_newConnectionWaitTimers.insert(clientId, timer);
timer->start(10000);
}
void JsonRPCServer::clientDisconnected(const QUuid &clientId)
@ -780,6 +809,9 @@ void JsonRPCServer::clientDisconnected(const QUuid &clientId)
if (m_pushButtonTransactions.values().contains(clientId)) {
NymeaCore::instance()->userManager()->cancelPushButtonAuth(m_pushButtonTransactions.key(clientId));
}
if (m_newConnectionWaitTimers.contains(clientId)) {
delete m_newConnectionWaitTimers.take(clientId);
}
}
}

View File

@ -107,6 +107,7 @@ private:
QHash<QUuid, bool> m_clientNotifications;
QHash<QUuid, QLocale> m_clientLocales;
QHash<int, QUuid> m_pushButtonTransactions;
QHash<QUuid, QTimer*> m_newConnectionWaitTimers;
QHash<QString, JsonReply*> m_pairingRequests;

View File

@ -70,8 +70,6 @@ bool JsonTypes::s_initialized = false;
QString JsonTypes::s_lastError;
QVariantList JsonTypes::s_basicType;
QVariantList JsonTypes::s_basicTag;
QVariantList JsonTypes::s_deviceIcon;
QVariantList JsonTypes::s_stateOperator;
QVariantList JsonTypes::s_valueOperator;
QVariantList JsonTypes::s_inputType;
@ -139,8 +137,6 @@ void JsonTypes::init()
s_unit = enumToStrings(Types::staticMetaObject, "Unit");
s_createMethod = enumToStrings(DeviceClass::staticMetaObject, "CreateMethod");
s_setupMethod = enumToStrings(DeviceClass::staticMetaObject, "SetupMethod");
s_basicTag = enumToStrings(DeviceClass::staticMetaObject, "BasicTag");
s_deviceIcon = enumToStrings(DeviceClass::staticMetaObject, "DeviceIcon");
s_removePolicy = enumToStrings(RuleEngine::staticMetaObject, "RemovePolicy");
s_deviceError = enumToStrings(DeviceManager::staticMetaObject, "DeviceError");
s_ruleError = enumToStrings(RuleEngine::staticMetaObject, "RuleError");
@ -203,8 +199,6 @@ void JsonTypes::init()
s_stateType.insert("index", basicTypeToString(Int));
s_stateType.insert("defaultValue", basicTypeToString(Variant));
s_stateType.insert("o:unit", unitRef());
s_stateType.insert("o:ruleRelevant", basicTypeToString(Bool));
s_stateType.insert("o:graphRelevant", basicTypeToString(Bool));
s_stateType.insert("o:minValue", basicTypeToString(Variant));
s_stateType.insert("o:maxValue", basicTypeToString(Variant));
s_stateType.insert("o:possibleValues", QVariantList() << basicTypeToString(Variant));
@ -233,8 +227,6 @@ void JsonTypes::init()
s_eventType.insert("displayName", basicTypeToString(String));
s_eventType.insert("index", basicTypeToString(Int));
s_eventType.insert("paramTypes", QVariantList() << paramTypeRef());
s_eventType.insert("o:ruleRelevant", basicTypeToString(Bool));
s_eventType.insert("o:graphRelevant", basicTypeToString(Bool));
// Event
s_event.insert("eventTypeId", basicTypeToString(Uuid));
@ -277,14 +269,9 @@ void JsonTypes::init()
s_deviceClass.insert("pluginId", basicTypeToString(Uuid));
s_deviceClass.insert("name", basicTypeToString(String));
s_deviceClass.insert("displayName", basicTypeToString(String));
s_deviceClass.insert("deviceIcon", deviceIconRef());
s_deviceClass.insert("interfaces", QVariantList() << basicTypeToString(String));
s_deviceClass.insert("basicTags", QVariantList() << basicTagRef());
s_deviceClass.insert("setupMethod", setupMethodRef());
s_deviceClass.insert("createMethods", QVariantList() << createMethodRef());
s_deviceClass.insert("o:criticalStateTypeId", basicTypeToString(Uuid));
s_deviceClass.insert("o:primaryStateTypeId", basicTypeToString(Uuid));
s_deviceClass.insert("o:primaryActionTypeId", basicTypeToString(Uuid));
s_deviceClass.insert("stateTypes", QVariantList() << stateTypeRef());
s_deviceClass.insert("eventTypes", QVariantList() << eventTypeRef());
s_deviceClass.insert("actionTypes", QVariantList() << actionTypeRef());
@ -437,13 +424,11 @@ QVariantMap JsonTypes::allTypes()
{
QVariantMap allTypes;
allTypes.insert("BasicType", basicType());
allTypes.insert("BasicTag", basicTag());
allTypes.insert("ParamType", paramTypeDescription());
allTypes.insert("InputType", inputType());
allTypes.insert("Unit", unit());
allTypes.insert("CreateMethod", createMethod());
allTypes.insert("SetupMethod", setupMethod());
allTypes.insert("DeviceIcon", deviceIcon());
allTypes.insert("ValueOperator", valueOperator());
allTypes.insert("StateOperator", stateOperator());
allTypes.insert("RemovePolicy", removePolicy());
@ -507,11 +492,6 @@ QVariantMap JsonTypes::packEventType(const EventType &eventType, const PluginId
variant.insert("name", eventType.name());
variant.insert("displayName", NymeaCore::instance()->deviceManager()->translator()->translate(pluginId, eventType.displayName(), locale));
variant.insert("index", eventType.index());
if (!eventType.ruleRelevant())
variant.insert("ruleRelevant", false);
if (eventType.graphRelevant())
variant.insert("graphRelevant", true);
QVariantList paramTypes;
foreach (const ParamType &paramType, eventType.paramTypes())
@ -642,12 +622,6 @@ QVariantMap JsonTypes::packStateType(const StateType &stateType, const PluginId
variantMap.insert("type", basicTypeToString(stateType.type()));
variantMap.insert("defaultValue", stateType.defaultValue());
if (!stateType.ruleRelevant())
variantMap.insert("ruleRelevant", false);
if (stateType.graphRelevant())
variantMap.insert("graphRelevant", true);
if (stateType.maxValue().isValid())
variantMap.insert("maxValue", stateType.maxValue());
@ -782,13 +756,8 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass, const QLo
variant.insert("displayName", NymeaCore::instance()->deviceManager()->translator()->translate(deviceClass.pluginId(), deviceClass.displayName(), locale));
variant.insert("vendorId", deviceClass.vendorId().toString());
variant.insert("pluginId", deviceClass.pluginId().toString());
variant.insert("deviceIcon", s_deviceIcon.at(deviceClass.deviceIcon()));
variant.insert("interfaces", deviceClass.interfaces());
QVariantList basicTags;
foreach (const DeviceClass::BasicTag &basicTag, deviceClass.basicTags())
basicTags.append(s_basicTag.at(basicTag));
QVariantList stateTypes;
foreach (const StateType &stateType, deviceClass.stateTypes())
stateTypes.append(packStateType(stateType, deviceClass.pluginId(), locale));
@ -809,16 +778,6 @@ QVariantMap JsonTypes::packDeviceClass(const DeviceClass &deviceClass, const QLo
foreach (const ParamType &paramType, deviceClass.discoveryParamTypes())
discoveryParamTypes.append(packParamType(paramType, deviceClass.pluginId(), locale));
if (!deviceClass.criticalStateTypeId().isNull())
variant.insert("criticalStateTypeId", deviceClass.criticalStateTypeId().toString());
if (!deviceClass.primaryStateTypeId().isNull())
variant.insert("primaryStateTypeId", deviceClass.primaryStateTypeId().toString());
if (!deviceClass.primaryActionTypeId().isNull())
variant.insert("primaryActionTypeId", deviceClass.primaryActionTypeId().toString());
variant.insert("basicTags", basicTags);
variant.insert("paramTypes", paramTypes);
variant.insert("discoveryParamTypes", discoveryParamTypes);
variant.insert("stateTypes", stateTypes);
@ -1372,7 +1331,6 @@ Rule JsonTypes::unpackRule(const QVariantMap &ruleMap)
QList<EventDescriptor> eventDescriptors;
if (ruleMap.contains("eventDescriptors")) {
QVariantList eventDescriptorVariantList = ruleMap.value("eventDescriptors").toList();
qCDebug(dcJsonRpc) << "unpacking eventDescriptors:" << eventDescriptorVariantList;
foreach (const QVariant &eventDescriptorVariant, eventDescriptorVariantList) {
eventDescriptors.append(JsonTypes::unpackEventDescriptor(eventDescriptorVariant.toMap()));
}
@ -2122,18 +2080,6 @@ QPair<bool, QString> JsonTypes::validateVariant(const QVariant &templateVariant,
qCWarning(dcJsonRpc) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(unitRef());
return result;
}
} else if (refName == basicTagRef()) {
QPair<bool, QString> result = validateEnum(s_basicTag, variant);
if (!result.first) {
qCWarning(dcJsonRpc) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(basicTagRef());
return result;
}
} else if (refName == deviceIconRef()) {
QPair<bool, QString> result = validateEnum(s_deviceIcon, variant);
if (!result.first) {
qCWarning(dcJsonRpc) << QString("Value %1 not allowed in %2").arg(variant.toString()).arg(deviceIconRef());
return result;
}
} else if (refName == repeatingModeRef()) {
QPair<bool, QString> result = validateEnum(s_repeatingMode, variant);
if (!result.first) {

View File

@ -120,14 +120,12 @@ public:
static QVariantMap allTypes();
DECLARE_TYPE(basicType, "BasicType", JsonTypes, BasicType)
DECLARE_TYPE(basicTag, "BasicTag", DeviceClass, BasicTag)
DECLARE_TYPE(stateOperator, "StateOperator", Types, StateOperator)
DECLARE_TYPE(valueOperator, "ValueOperator", Types, ValueOperator)
DECLARE_TYPE(inputType, "InputType", Types, InputType)
DECLARE_TYPE(unit, "Unit", Types, Unit)
DECLARE_TYPE(createMethod, "CreateMethod", DeviceClass, CreateMethod)
DECLARE_TYPE(setupMethod, "SetupMethod", DeviceClass, SetupMethod)
DECLARE_TYPE(deviceIcon, "DeviceIcon", DeviceClass, DeviceIcon)
DECLARE_TYPE(deviceError, "DeviceError", DeviceManager, DeviceError)
DECLARE_TYPE(removePolicy, "RemovePolicy", RuleEngine, RemovePolicy)
DECLARE_TYPE(ruleError, "RuleError", RuleEngine, RuleError)

View File

@ -33,6 +33,14 @@ MockTcpServer::MockTcpServer(QObject *parent):
TransportInterface(ServerConfiguration(), parent)
{
s_allServers.append(this);
connect(this, &TransportInterface::clientConnected, this, [this](const QUuid &clientId){
m_connectedClients.append(clientId);
});
connect(this, &TransportInterface::clientDisconnected, this, [this](const QUuid &clientId){
m_connectedClients.removeAll(clientId);
});
}
MockTcpServer::~MockTcpServer()
@ -65,6 +73,7 @@ QList<MockTcpServer *> MockTcpServer::servers()
void MockTcpServer::injectData(const QUuid &clientId, const QByteArray &data)
{
Q_ASSERT_X(m_connectedClients.contains(clientId), "MockTcpServer", "Cannot inject data. Client is not connected");
emit dataAvailable(clientId, data);
}

View File

@ -57,6 +57,8 @@ public slots:
private:
static QList<MockTcpServer*> s_allServers;
QList<QUuid> m_connectedClients;
};
#endif // TCPSERVER_H

View File

@ -594,15 +594,6 @@ void DevicePlugin::loadMetaData()
}
deviceClass.setCreateMethods(createMethods);
// Read device icon
QPair<bool, DeviceClass::DeviceIcon> deviceIconVerification = loadAndVerifyDeviceIcon(deviceClassObject.value("deviceIcon").toString());
if (!deviceIconVerification.first) {
broken = true;
break;
} else {
deviceClass.setDeviceIcon(deviceIconVerification.second);
}
// Read params
QPair<bool, QList<ParamType> > paramTypesVerification = parseParamTypes(deviceClassObject.value("paramTypes").toArray());
if (!paramTypesVerification.first) {
@ -644,19 +635,6 @@ void DevicePlugin::loadMetaData()
// Read pairing info
deviceClass.setPairingInfo(deviceClassObject.value("pairingInfo").toString());
// Read basic tags
QList<DeviceClass::BasicTag> basicTags;
foreach (const QJsonValue &basicTagJson, deviceClassObject.value("basicTags").toArray()) {
QPair<bool, DeviceClass::BasicTag> basicTagVerification = loadAndVerifyBasicTag(basicTagJson.toString());
if (!basicTagVerification.first) {
broken = true;
break;
} else {
basicTags.append(basicTagVerification.second);
}
}
deviceClass.setBasicTags(basicTags);
QList<ActionType> actionTypes;
QList<StateType> stateTypes;
QList<EventType> eventTypes;
@ -720,12 +698,6 @@ void DevicePlugin::loadMetaData()
if (st.contains("maxValue"))
stateType.setMaxValue(st.value("maxValue").toVariant());
if (st.contains("ruleRelevant"))
stateType.setRuleRelevant(st.value("ruleRelevant").toBool());
if (st.contains("graphRelevant"))
stateType.setGraphRelevant(st.value("graphRelevant").toBool());
if (st.contains("possibleValues")) {
QVariantList possibleValues;
foreach (const QJsonValue &possibleValueJson, st.value("possibleValues").toArray()) {
@ -748,9 +720,6 @@ void DevicePlugin::loadMetaData()
// Events for state changed
EventType eventType(EventTypeId(stateType.id().toString()));
if (st.contains("eventRuleRelevant"))
eventType.setRuleRelevant(st.value("eventRuleRelevant").toBool());
eventType.setName(st.value("name").toString());
eventType.setDisplayName(st.value("displayNameEvent").toString());
ParamType paramType(ParamTypeId(stateType.id().toString()), st.value("name").toString(), stateType.type());
@ -838,11 +807,6 @@ void DevicePlugin::loadMetaData()
eventType.setName(et.value("name").toString());
eventType.setDisplayName(et.value("displayName").toString());
eventType.setIndex(index++);
if (et.contains("ruleRelevant"))
eventType.setRuleRelevant(et.value("ruleRelevant").toBool());
if (et.contains("graphRelevant"))
eventType.setGraphRelevant(et.value("graphRelevant").toBool());
QPair<bool, QList<ParamType> > paramVerification = parseParamTypes(et.value("paramTypes").toArray());
if (!paramVerification.first) {
@ -855,41 +819,6 @@ void DevicePlugin::loadMetaData()
}
deviceClass.setEventTypes(eventTypes);
// Note: keep this after the actionType / stateType / eventType parsing
if (deviceClassObject.contains("criticalStateTypeId")) {
StateTypeId criticalStateTypeId = StateTypeId(deviceClassObject.value("criticalStateTypeId").toString());
if (!deviceClass.hasStateType(criticalStateTypeId)) {
qCWarning(dcDeviceManager()) << "Skipping device class" << deviceClass.name() << ": the definend critical stateTypeId" << criticalStateTypeId.toString() << "does not match any StateType of this DeviceClass.";
broken = true;
} else if (deviceClass.getStateType(criticalStateTypeId).type() != QVariant::Bool) {
// Make sure the critical stateType is a bool state
qCWarning(dcDeviceManager()) << "Skipping device class" << deviceClass.name() << ": the definend critical stateTypeId" << criticalStateTypeId.toString() << "is not a bool StateType.";
broken = true;
} else {
deviceClass.setCriticalStateTypeId(criticalStateTypeId);
}
}
if (deviceClassObject.contains("primaryStateTypeId")) {
StateTypeId primaryStateTypeId = StateTypeId(deviceClassObject.value("primaryStateTypeId").toString());
if (!deviceClass.hasStateType(primaryStateTypeId)) {
qCWarning(dcDeviceManager()) << "Skipping device class" << deviceClass.name() << ": the definend primary stateTypeId" << primaryStateTypeId.toString() << "does not match any StateType of this DeviceClass.";
broken = true;
} else {
deviceClass.setPrimaryStateTypeId(primaryStateTypeId);
}
}
if (deviceClassObject.contains("primaryActionTypeId")) {
ActionTypeId primaryActionTypeId = ActionTypeId(deviceClassObject.value("primaryActionTypeId").toString());
if (!deviceClass.hasActionType(primaryActionTypeId)) {
qCWarning(dcDeviceManager()) << "Skipping device class" << deviceClass.name() << ": the definend primary actionTypeId" << primaryActionTypeId.toString() << "does not match any ActionType of this DeviceClass.";
broken = true;
} else {
deviceClass.setPrimaryActionTypeId(primaryActionTypeId);
}
}
// Read interfaces
QStringList interfaces;
foreach (const QJsonValue &value, deviceClassObject.value("interfaces").toArray()) {
@ -1053,58 +982,6 @@ QPair<bool, Types::InputType> DevicePlugin::loadAndVerifyInputType(const QString
return QPair<bool, Types::InputType>(true, (Types::InputType)enumValue);
}
QPair<bool, DeviceClass::BasicTag> DevicePlugin::loadAndVerifyBasicTag(const QString &basicTag) const
{
if (basicTag.isEmpty())
return QPair<bool, DeviceClass::BasicTag>(true, DeviceClass::BasicTagDevice);
QMetaObject metaObject = DeviceClass::staticMetaObject;
int enumIndex = metaObject.indexOfEnumerator(QString("BasicTag").toLatin1().data());
QMetaEnum metaEnum = metaObject.enumerator(enumIndex);
int enumValue = -1;
for (int i = 0; i < metaEnum.keyCount(); i++) {
if (QString(metaEnum.valueToKey(metaEnum.value(i))) == QString("BasicTag" + basicTag)) {
enumValue = metaEnum.value(i);
break;
}
}
// inform the plugin developer about the error in the plugin json file
if (enumValue == -1) {
qCWarning(dcDeviceManager()) << QString("\"%1\" plugin:").arg(pluginName()).toLatin1().data() << QString("Invalid basicTag \"%1\" in json file.").arg(basicTag).toLatin1().data();
return QPair<bool, DeviceClass::BasicTag>(false, DeviceClass::BasicTagDevice);
}
return QPair<bool, DeviceClass::BasicTag>(true, (DeviceClass::BasicTag)enumValue);
}
QPair<bool, DeviceClass::DeviceIcon> DevicePlugin::loadAndVerifyDeviceIcon(const QString &deviceIcon) const
{
if (deviceIcon.isEmpty())
return QPair<bool, DeviceClass::DeviceIcon>(true, DeviceClass::DeviceIconNone);
QMetaObject metaObject = DeviceClass::staticMetaObject;
int enumIndex = metaObject.indexOfEnumerator(QString("DeviceIcon").toLatin1().data());
QMetaEnum metaEnum = metaObject.enumerator(enumIndex);
int enumValue = -1;
for (int i = 0; i < metaEnum.keyCount(); i++) {
if (QString(metaEnum.valueToKey(metaEnum.value(i))) == QString("DeviceIcon" + deviceIcon)) {
enumValue = metaEnum.value(i);
break;
}
}
// inform the plugin developer about the error in the plugin json file
if (enumValue == -1) {
qCWarning(dcDeviceManager()) << QString("\"%1\" plugin:").arg(pluginName()).toLatin1().data() << QString("Invalid deviceIcon \"%1\" in json file.").arg(deviceIcon).toLatin1().data();
return QPair<bool, DeviceClass::DeviceIcon>(false, DeviceClass::DeviceIconNone);
}
return QPair<bool, DeviceClass::DeviceIcon>(true, (DeviceClass::DeviceIcon)enumValue);
}
Interfaces DevicePlugin::allInterfaces()
{
Interfaces ret;

View File

@ -113,8 +113,6 @@ private:
// load and verify enum values
QPair<bool, Types::Unit> loadAndVerifyUnit(const QString &unitString) const;
QPair<bool, Types::InputType> loadAndVerifyInputType(const QString &inputType) const;
QPair<bool, DeviceClass::BasicTag> loadAndVerifyBasicTag(const QString &basicTag) const;
QPair<bool, DeviceClass::DeviceIcon> loadAndVerifyDeviceIcon(const QString &deviceIcon) const;
// FIXME: This is expensive because it will open all the files.
// Once DeviceManager is in libnymea-core this should probably be there too.

View File

@ -62,103 +62,6 @@
During the setup, a button has to be pushed in order to pair the \l{Device}.
*/
/*! \enum DeviceClass::BasicTag
This enum type specifies the basic tags which describe the categories of the DeviceClass.
A DeviceClass can have multiple tags.
\value BasicTagService
The \l{DeviceClass} describes a service.
\value BasicTagDevice
The \l{DeviceClass} describes a real device.
\value BasicTagSensor
The \l{DeviceClass} describes a sensor. Any device which can measure or detect something is a sensor.
\value BasicTagActuator
The \l{DeviceClass} describes an actuator. Any device which can do something is an actuator.
\value BasicTagLighting
The \l{DeviceClass} describes a lighting device.
\value BasicTagEnergy
The \l{DeviceClass} describes an energy device.
\value BasicTagMultimedia
The \l{DeviceClass} describes a multimedia device/service.
\value BasicTagWeather
The \l{DeviceClass} describes a weather device/service.
\value BasicTagGateway
The \l{DeviceClass} describes a gateway device.
\value BasicTagHeating
The \l{DeviceClass} describes a heating device.
\value BasicTagCooling
The \l{DeviceClass} describes a cooling device.
\value BasicTagNotification
The \l{DeviceClass} describes a notification service.
\value BasicTagSecurity
The \l{DeviceClass} describes a security device/service.
\value BasicTagTime
The \l{DeviceClass} describes a time device/service.
\value BasicTagShading
The \l{DeviceClass} describes a shading device.
\value BasicTagAppliance
The \l{DeviceClass} describes an appliance.
\value BasicTagCamera
The \l{DeviceClass} describes a camera device.
\value BasicTagLock
The \l{DeviceClass} describes a lock device.
*/
/*! \enum DeviceClass::DeviceIcon
This enum type specifies the default device icon of the DeviceClass.
Each client should have an icon set containing this list of icon types.
If a client want's to offer special icons for a special DeviceClass or device type,
he can bring it's own icon for a special DeviceClassId. If there is no special icon
defined, he can fallback to the default icon.
\value DeviceIconNone
\value DeviceIconBed
\value DeviceIconBlinds
\value DeviceIconCeilingLamp
\value DeviceIconCouch
\value DeviceIconDeskLamp
\value DeviceIconDesk
\value DeviceIconHifi
\value DeviceIconPower
\value DeviceIconEnergy
\value DeviceIconRadio
\value DeviceIconSmartPhone
\value DeviceIconSocket
\value DeviceIconStandardLamp
\value DeviceIconSun
\value DeviceIconTablet
\value DeviceIconThermometer
\value DeviceIconTune
\value DeviceIconTv
\value DeviceIconBattery
\value DeviceIconDishwasher
\value DeviceIconWashingMachine
\value DeviceIconLaundryDryer
\value DeviceIconIrHeater
\value DeviceIconRadiator
\value DeviceIconSwitch
\value DeviceIconMotionDetectors
\value DeviceIconWeather
\value DeviceIconTime
\value DeviceIconLightBulb
\value DeviceIconGateway
\value DeviceIconMail
\value DeviceIconNetwork
\value DeviceIconCloud
\value DeviceIconGarage,
\value DeviceIconRollerShutter
*/
/*! \fn void DeviceClass::setBasicTags(const QList<BasicTag> &basicTags);
Set the list of \a basicTags of this DeviceClass.
*/
/*! \fn void DeviceClass::setDeviceIcon(const DeviceIcon &deviceIcon);
Set the \a deviceIcon of this DeviceClass.
*/
#include "deviceclass.h"
@ -170,10 +73,6 @@ DeviceClass::DeviceClass(const PluginId &pluginId, const VendorId &vendorId, con
m_id(id),
m_vendorId(vendorId),
m_pluginId(pluginId),
m_criticalStateTypeId(StateTypeId()),
m_primaryStateTypeId(StateTypeId()),
m_primaryActionTypeId(ActionTypeId()),
m_deviceIcon(DeviceIconPower),
m_createMethods(CreateMethodUser),
m_setupMethod(SetupMethodJustAdd)
{
@ -228,76 +127,6 @@ void DeviceClass::setDisplayName(const QString &displayName)
m_displayName = displayName;
}
/*! Returns the critical \l{StateTypeId} of this \l{DeviceClass}.
* A critical \l{State} describes the state which disables the whole device (i.e. connected, available or reachable). */
StateTypeId DeviceClass::criticalStateTypeId() const
{
return m_criticalStateTypeId;
}
/*! Set the \a criticalStateTypeId of this \l{DeviceClass}. */
void DeviceClass::setCriticalStateTypeId(const StateTypeId &criticalStateTypeId)
{
m_criticalStateTypeId = criticalStateTypeId;
}
/*! Returns the primary \l{StateTypeId} of this \l{DeviceClass}. */
StateTypeId DeviceClass::primaryStateTypeId() const
{
return m_primaryStateTypeId;
}
/*! Set the \a primaryStateTypeId of this \l{DeviceClass}. */
void DeviceClass::setPrimaryStateTypeId(const StateTypeId &primaryStateTypeId)
{
m_primaryStateTypeId = primaryStateTypeId;
}
/*! Returns the primary \l{ActionTypeId} of this \l{DeviceClass}. */
ActionTypeId DeviceClass::primaryActionTypeId() const
{
return m_primaryActionTypeId;
}
/*! Set the \a primaryActionTypeId of this \l{DeviceClass}. */
void DeviceClass::setPrimaryActionTypeId(const ActionTypeId &primaryActionTypeId)
{
m_primaryActionTypeId = primaryActionTypeId;
}
/*! Returns the default \l{DeviceIcon} of this \l{DeviceClass}. */
DeviceClass::DeviceIcon DeviceClass::deviceIcon() const
{
return m_deviceIcon;
}
/*! Set the \a deviceIcon of this \l{DeviceClass}.
\sa DeviceClass::DeviceIcon
*/
void DeviceClass::setDeviceIcon(const DeviceClass::DeviceIcon &deviceIcon)
{
m_deviceIcon = deviceIcon;
}
/*! Returns the list of basicTags of this DeviceClass.
\sa DeviceClass::BasicTag
*/
QList<DeviceClass::BasicTag> DeviceClass::basicTags() const
{
return m_basicTags;
}
/*! Set the list of \a basicTags of this DeviceClass.
\sa DeviceClass::BasicTag
*/
void DeviceClass::setBasicTags(const QList<DeviceClass::BasicTag> &basicTags)
{
m_basicTags = basicTags;
}
/*! Returns the statesTypes of this DeviceClass. \{Device}{Devices} created
from this \l{DeviceClass} must have their states matching to this template. */
StateTypes DeviceClass::stateTypes() const
@ -474,10 +303,9 @@ bool DeviceClass::operator==(const DeviceClass &deviceClass) const
/*! Returns a list of all valid JSON properties a DeviceClass JSON definition can have. */
QStringList DeviceClass::typeProperties()
{
return QStringList() << "id" << "name" << "displayName" << "createMethods" << "setupMethod" << "deviceIcon"
<< "interfaces" << "basicTags" << "pairingInfo" << "criticalStateTypeId"
<< "primaryStateTypeId" << "primaryActionTypeId" << "discoveryParamTypes"
<< "discoveryParamTypes" << "paramTypes" << "stateTypes" << "actionTypes" << "eventTypes";
return QStringList() << "id" << "name" << "displayName" << "createMethods" << "setupMethod"
<< "interfaces" << "pairingInfo" << "discoveryParamTypes" << "discoveryParamTypes"
<< "paramTypes" << "stateTypes" << "actionTypes" << "eventTypes";
}
/*! Returns a list of mandatory JSON properties a DeviceClass JSON definition must have. */

View File

@ -41,8 +41,7 @@ class LIBNYMEA_EXPORT DeviceClass
Q_ENUMS(CreateMethod)
Q_ENUMS(SetupMethod)
Q_ENUMS(BasicTag)
Q_ENUMS(DeviceIcon)
Q_FLAGS(CreateMethods)
Q_ENUMS(CreateMethods)
public:
enum CreateMethod {
@ -59,66 +58,6 @@ public:
SetupMethodPushButton
};
enum BasicTag {
BasicTagService,
BasicTagDevice,
BasicTagSensor,
BasicTagActuator,
BasicTagLighting,
BasicTagEnergy,
BasicTagMultimedia,
BasicTagWeather,
BasicTagGateway,
BasicTagHeating,
BasicTagCooling,
BasicTagNotification,
BasicTagSecurity,
BasicTagTime,
BasicTagShading,
BasicTagAppliance,
BasicTagCamera,
BasicTagLock
};
enum DeviceIcon {
DeviceIconNone,
DeviceIconBed,
DeviceIconBlinds,
DeviceIconCeilingLamp,
DeviceIconCouch,
DeviceIconDeskLamp,
DeviceIconDesk,
DeviceIconHifi,
DeviceIconPower,
DeviceIconEnergy,
DeviceIconRadio,
DeviceIconSmartPhone,
DeviceIconSocket,
DeviceIconStandardLamp,
DeviceIconSun,
DeviceIconTablet,
DeviceIconThermometer,
DeviceIconTune,
DeviceIconTv,
DeviceIconBattery,
DeviceIconDishwasher,
DeviceIconWashingMachine,
DeviceIconLaundryDryer,
DeviceIconIrHeater,
DeviceIconRadiator,
DeviceIconSwitch,
DeviceIconMotionDetectors,
DeviceIconWeather,
DeviceIconTime,
DeviceIconLightBulb,
DeviceIconGateway,
DeviceIconMail,
DeviceIconNetwork,
DeviceIconCloud,
DeviceIconGarage,
DeviceIconRollerShutter
};
DeviceClass(const PluginId &pluginId = PluginId(), const VendorId &vendorId = VendorId(), const DeviceClassId &id = DeviceClassId());
DeviceClassId id() const;
@ -132,21 +71,6 @@ public:
QString displayName() const;
void setDisplayName(const QString &displayName);
StateTypeId criticalStateTypeId() const;
void setCriticalStateTypeId(const StateTypeId &criticalStateTypeId);
StateTypeId primaryStateTypeId() const;
void setPrimaryStateTypeId(const StateTypeId &primaryStateTypeId);
ActionTypeId primaryActionTypeId() const;
void setPrimaryActionTypeId(const ActionTypeId &primaryActionTypeId);
DeviceIcon deviceIcon() const;
void setDeviceIcon(const DeviceIcon &deviceIcon);
QList<BasicTag> basicTags() const;
void setBasicTags(const QList<BasicTag> &basicTags);
StateTypes stateTypes() const;
StateType getStateType(const StateTypeId &stateTypeId);
void setStateTypes(const QList<StateType> &stateTypes);
@ -189,11 +113,6 @@ private:
PluginId m_pluginId;
QString m_name;
QString m_displayName;
StateTypeId m_criticalStateTypeId;
StateTypeId m_primaryStateTypeId;
ActionTypeId m_primaryActionTypeId;
DeviceIcon m_deviceIcon;
QList<BasicTag> m_basicTags;
QList<StateType> m_stateTypes;
QList<EventType> m_eventTypes;
QList<ActionType> m_actionTypes;

View File

@ -36,9 +36,7 @@
/*! Constructs a EventType object with the given \a id. */
EventType::EventType(const EventTypeId &id):
m_id(id),
m_index(0),
m_ruleRelevant(true),
m_graphRelevant(false)
m_index(0)
{
}
@ -100,30 +98,6 @@ void EventType::setParamTypes(const ParamTypes &paramTypes)
m_paramTypes = paramTypes;
}
/*! Returns true if this EventType is relevant for the rule from a user perspective. */
bool EventType::ruleRelevant() const
{
return m_ruleRelevant;
}
/*! Sets this EventType relevant for the rule from a user perspective to \a ruleRelevant. */
void EventType::setRuleRelevant(const bool &ruleRelevant)
{
m_ruleRelevant = ruleRelevant;
}
/*! Returns true if this EventType is interesting to visualize the logs in a graph/chart from a user perspective. */
bool EventType::graphRelevant() const
{
return m_graphRelevant;
}
/*! Sets this EventType \a graphRelevant to inform the client application if this \l{EventType} is interesting to visualize the logs in a graph/chart. */
void EventType::setGraphRelevant(const bool &graphRelevant)
{
m_graphRelevant = graphRelevant;
}
/*! Returns true if this EventType has a valid id and name */
bool EventType::isValid() const
{
@ -133,7 +107,7 @@ bool EventType::isValid() const
/*! Returns a list of all valid JSON properties a EventType JSON definition can have. */
QStringList EventType::typeProperties()
{
return QStringList() << "id" << "name" << "displayName" << "paramTypes" << "ruleRelevant" << "graphRelevant";
return QStringList() << "id" << "name" << "displayName" << "paramTypes";
}
/*! Returns a list of mandatory JSON properties a EventType JSON definition must have. */

View File

@ -49,12 +49,6 @@ public:
ParamTypes paramTypes() const;
void setParamTypes(const ParamTypes &paramTypes);
bool ruleRelevant() const;
void setRuleRelevant(const bool &ruleRelevant);
bool graphRelevant() const;
void setGraphRelevant(const bool &graphRelevant);
bool isValid() const;
static QStringList typeProperties();
@ -66,8 +60,6 @@ private:
QString m_displayName;
int m_index;
QList<ParamType> m_paramTypes;
bool m_ruleRelevant;
bool m_graphRelevant;
};
class EventTypes: public QList<EventType>

View File

@ -160,30 +160,6 @@ void StateType::setUnit(const Types::Unit &unit)
m_unit = unit;
}
/*! Returns true if this StateType is relevant for the rule from a user perspective. */
bool StateType::ruleRelevant() const
{
return m_ruleRelevant;
}
/*! Sets this StateType relevant for the rule from a user perspective to \a ruleRelevant. */
void StateType::setRuleRelevant(const bool &ruleRelevant)
{
m_ruleRelevant = ruleRelevant;
}
/*! Returns true if this StateType is interesting to visualize the logs in a graph/chart from a user perspective. */
bool StateType::graphRelevant() const
{
return m_graphRelevant;
}
/*! Sets this StateType \a graphRelevant to inform the client application if this \l{StateType} is interesting to visualize the logs in a graph/chart. */
void StateType::setGraphRelevant(const bool &graphRelevant)
{
m_graphRelevant = graphRelevant;
}
/*! Returns true if this StateType is to be cached. This means, the last state value will be stored to disk upon shutdown and restored on reboot. If this is false, states will be initialized with the default value on each boot. By default all states are cached by the system. */
bool StateType::cached() const
{
@ -200,8 +176,8 @@ void StateType::setCached(bool cached)
QStringList StateType::typeProperties()
{
return QStringList() << "id" << "name" << "displayName" << "displayNameEvent" << "type" << "defaultValue"
<< "cached" << "ruleRelevant" << "eventRuleRelevant" << "graphRelevant" << "unit"
<< "minValue" << "maxValue" << "possibleValues" << "writable" << "displayNameAction";
<< "cached" << "unit" << "minValue" << "maxValue" << "possibleValues" << "writable"
<< "displayNameAction";
}
/*! Returns a list of mandatory properties a DeviceClass definition must have. */

View File

@ -64,12 +64,6 @@ public:
Types::Unit unit() const;
void setUnit(const Types::Unit &unit);
bool ruleRelevant() const;
void setRuleRelevant(const bool &ruleRelevant);
bool graphRelevant() const;
void setGraphRelevant(const bool &graphRelevant);
bool cached() const;
void setCached(bool cached);
@ -87,8 +81,6 @@ private:
QVariant m_maxValue;
QVariantList m_possibleValues;
Types::Unit m_unit = Types::UnitNone;
bool m_ruleRelevant = true;
bool m_graphRelevant = false;
bool m_cached = true;
};

View File

@ -5,8 +5,8 @@ NYMEA_VERSION_STRING=$$system('dpkg-parsechangelog | sed -n -e "s/^Version: //p"
NYMEA_PLUGINS_PATH=/usr/lib/$$system('dpkg-architecture -q DEB_HOST_MULTIARCH')/nymea/plugins/
# define protocol versions
JSON_PROTOCOL_VERSION_MAJOR=1
JSON_PROTOCOL_VERSION_MINOR=14
JSON_PROTOCOL_VERSION_MAJOR=2
JSON_PROTOCOL_VERSION_MINOR=0
REST_API_VERSION=1
COPYRIGHT_YEAR_FROM=2013

View File

@ -15,11 +15,10 @@ doc.commands += cd $$top_srcdir/doc; ./generate-interfaces-qdoc.py;
doc.commands += cd $$top_srcdir/doc; ./generate-api-qdoc.py;
doc.commands += cd $$top_srcdir/doc; qdoc --highlighting config.qdocconf; cp -r images/* html/images/; \
cp -r favicons/* html/; cp -r $$top_srcdir/doc/html $$top_builddir/
QMAKE_EXTRA_TARGETS += doc
licensecheck.commands = $$top_srcdir/tests/auto/checklicenseheaders.sh $$top_srcdir
test.depends = licensecheck
test.commands = LD_LIBRARY_PATH=$$top_builddir/libnymea-core:$$top_builddir/libnymea make check
QMAKE_EXTRA_TARGETS += licensecheck
# Translations:
# make lupdate to update .ts files
@ -42,7 +41,11 @@ translations.path = /usr/share/nymea/translations
translations.depends = lrelease
INSTALLS += translations
QMAKE_EXTRA_TARGETS += licensecheck doc test lupdate lrelease
QMAKE_EXTRA_TARGETS += lupdate lrelease
test.depends = licensecheck lrelease
test.commands = LD_LIBRARY_PATH=$$top_builddir/libnymea-core:$$top_builddir/libnymea make check
QMAKE_EXTRA_TARGETS += test
# Show doc files in project tree
OTHER_FILES += doc/*.qdoc* \

View File

@ -30,16 +30,8 @@
"id": "753f0d32-0468-4d08-82ed-1964aab03298",
"name": "mock",
"displayName": "Mock Device",
"deviceIcon": "Tune",
"interfaces": ["system", "light", "gateway", "battery"],
"basicTags": [
"Device",
"Actuator",
"Gateway"
],
"createMethods": ["user", "discovery"],
"primaryActionTypeId": "defd3ed6-1a0d-400b-8879-a0202cf39935",
"primaryStateTypeId": "80baec19-54de-4948-ac46-31eabfaceb83",
"discoveryParamTypes": [
{
"id": "d222adb4-2f9c-4c3f-8655-76400d0fb6ce",
@ -80,7 +72,6 @@
"displayName": "Dummy int state",
"displayNameEvent": "Dummy int state changed",
"defaultValue": 10,
"graphRelevant": true,
"type": "int"
},
{
@ -136,8 +127,7 @@
{
"id": "45bf3752-0fc6-46b9-89fd-ffd878b5b22b",
"name": "mockEvent1",
"displayName": "Mock Event 1",
"graphRelevant": true
"displayName": "Mock Event 1"
},
{
"id": "863d5920-b1cf-4eb9-88bd-8f7b8583b1cf",
@ -201,15 +191,7 @@
"name": "mockDeviceAuto",
"displayName": "Mock Device (Auto created)",
"interfaces": ["system"],
"basicTags": [
"Device",
"Actuator",
"Gateway"
],
"createMethods": ["auto"],
"primaryActionTypeId": "defd3ed6-1a0d-400b-8879-a0202cf39935",
"primaryStateTypeId": "80baec19-54de-4948-ac46-31eabfaceb83",
"deviceIcon": "Tune",
"paramTypes": [
{
"id": "d4f06047-125e-4479-9810-b54c189917f5",
@ -240,7 +222,6 @@
"displayName": "Dummy int state",
"displayNameEvent": "Dummy int state changed",
"defaultValue": 10,
"graphRelevant": true,
"type": "int"
},
{
@ -321,14 +302,8 @@
"name": "mockPushButton",
"displayName": "Mock Device (Push Button)",
"interfaces": ["system"],
"basicTags": [
"Device",
"Actuator",
"Gateway"
],
"createMethods": ["discovery"],
"setupMethod": "pushButton",
"deviceIcon": "Tune",
"pairingInfo": "Wait 3 second before you continue, the push button will be pressed automatically.",
"paramTypes": [ ],
"discoveryParamTypes": [
@ -350,8 +325,6 @@
"displayNameAction": "Set color",
"type": "QColor",
"defaultValue": "#000000",
"ruleRelevant": false,
"eventRuleRelevant": false,
"writable": true
},
{
@ -363,7 +336,6 @@
"type": "int",
"unit": "Percentage",
"defaultValue": 0,
"ruleRelevant": false,
"minValue": 0,
"maxValue": 100,
"writable": true
@ -419,13 +391,7 @@
"id": "296f1fd4-e893-46b2-8a42-50d1bceb8730",
"name": "mockDisplayPin",
"displayName": "Mock Device (Display Pin)",
"deviceIcon": "Tune",
"interfaces": ["system"],
"basicTags": [
"Device",
"Actuator",
"Gateway"
],
"createMethods": ["discovery"],
"setupMethod": "displayPin",
"pairingInfo": "Please enter the secret which normaly will be displayed on the device. For the mockdevice the pin is 243681.",
@ -459,8 +425,6 @@
"displayNameAction": "Set color",
"type": "QColor",
"defaultValue": "#000000",
"ruleRelevant": false,
"eventRuleRelevant": false,
"writable": true
},
{
@ -472,7 +436,6 @@
"type": "int",
"unit": "Percentage",
"defaultValue": 0,
"ruleRelevant": false,
"minValue": 0,
"maxValue": 100,
"writable": true
@ -528,13 +491,7 @@
"id": "a71fbde9-9a38-4bf8-beab-c8aade2608ba",
"name": "mockParent",
"displayName": "Mock Device (Parent)",
"deviceIcon": "Tune",
"interfaces": ["system"],
"basicTags": [
"Device",
"Actuator",
"Gateway"
],
"createMethods": ["user"],
"paramTypes": [ ],
"stateTypes": [
@ -556,10 +513,6 @@
"displayName": "Mock Device (Child)",
"createMethods": ["auto"],
"paramTypes": [],
"basicTags": [
"Device",
"Actuator"
],
"stateTypes": [
{
"id": "d24ede5f-4064-4898-bb84-cfb533b1fbc0",
@ -577,10 +530,6 @@
"id": "515ffdf1-55e5-498d-9abc-4e2fe768f3a9",
"name": "mockInputType",
"displayName": "Mock Device (InputTypes)",
"deviceIcon": "Tune",
"basicTags": [
"Device"
],
"createMethods": ["user"],
"paramTypes": [
{

View File

@ -1,4 +1,4 @@
1.14
2.0
{
"methods": {
"Actions.ExecuteAction": {
@ -1008,6 +1008,15 @@
"deviceId": "Uuid"
}
},
"Devices.PluginConfigurationChanged": {
"description": "Emitted whenever a plugin's configuration is changed.",
"params": {
"configuration": [
"$ref:Param"
],
"pluginId": "Uuid"
}
},
"Devices.StateChanged": {
"description": "Emitted whenever a State of a device changes.",
"params": {
@ -1155,26 +1164,6 @@
"$ref:ParamType"
]
},
"BasicTag": [
"BasicTagService",
"BasicTagDevice",
"BasicTagSensor",
"BasicTagActuator",
"BasicTagLighting",
"BasicTagEnergy",
"BasicTagMultimedia",
"BasicTagWeather",
"BasicTagGateway",
"BasicTagHeating",
"BasicTagCooling",
"BasicTagNotification",
"BasicTagSecurity",
"BasicTagTime",
"BasicTagShading",
"BasicTagAppliance",
"BasicTagCamera",
"BasicTagLock"
],
"BasicType": [
"Uuid",
"String",
@ -1235,13 +1224,9 @@
"actionTypes": [
"$ref:ActionType"
],
"basicTags": [
"$ref:BasicTag"
],
"createMethods": [
"$ref:CreateMethod"
],
"deviceIcon": "$ref:DeviceIcon",
"discoveryParamTypes": [
"$ref:ParamType"
],
@ -1254,9 +1239,6 @@
"String"
],
"name": "String",
"o:criticalStateTypeId": "Uuid",
"o:primaryActionTypeId": "Uuid",
"o:primaryStateTypeId": "Uuid",
"paramTypes": [
"$ref:ParamType"
],
@ -1302,44 +1284,6 @@
"DeviceErrorPairingTransactionIdNotFound",
"DeviceErrorParameterNotWritable"
],
"DeviceIcon": [
"DeviceIconNone",
"DeviceIconBed",
"DeviceIconBlinds",
"DeviceIconCeilingLamp",
"DeviceIconCouch",
"DeviceIconDeskLamp",
"DeviceIconDesk",
"DeviceIconHifi",
"DeviceIconPower",
"DeviceIconEnergy",
"DeviceIconRadio",
"DeviceIconSmartPhone",
"DeviceIconSocket",
"DeviceIconStandardLamp",
"DeviceIconSun",
"DeviceIconTablet",
"DeviceIconThermometer",
"DeviceIconTune",
"DeviceIconTv",
"DeviceIconBattery",
"DeviceIconDishwasher",
"DeviceIconWashingMachine",
"DeviceIconLaundryDryer",
"DeviceIconIrHeater",
"DeviceIconRadiator",
"DeviceIconSwitch",
"DeviceIconMotionDetectors",
"DeviceIconWeather",
"DeviceIconTime",
"DeviceIconLightBulb",
"DeviceIconGateway",
"DeviceIconMail",
"DeviceIconNetwork",
"DeviceIconCloud",
"DeviceIconGarage",
"DeviceIconRollerShutter"
],
"Event": {
"deviceId": "Uuid",
"eventTypeId": "Uuid",
@ -1361,8 +1305,6 @@
"id": "Uuid",
"index": "Int",
"name": "String",
"o:graphRelevant": "Bool",
"o:ruleRelevant": "Bool",
"paramTypes": [
"$ref:ParamType"
]
@ -1619,13 +1561,11 @@
"id": "Uuid",
"index": "Int",
"name": "String",
"o:graphRelevant": "Bool",
"o:maxValue": "Variant",
"o:minValue": "Variant",
"o:possibleValues": [
"Variant"
],
"o:ruleRelevant": "Bool",
"o:unit": "$ref:Unit",
"type": "$ref:BasicType"
},

View File

@ -133,25 +133,22 @@ void TestJSONRPC::initTestCase()
{
NymeaTestBase::initTestCase();
QLoggingCategory::setFilterRules("*.debug=false\n"
// "JsonRpc*.debug=true\n"
// "JsonRpcTraffic.debug=true\n"
"JsonRpc.debug=true\n"
"Translations.debug=true\n"
"Tests.debug=true");
}
void TestJSONRPC::testHandshake()
{
// first test if the handshake message is auto-sent upon connecting
QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
QUuid newClientId = QUuid::createUuid();
m_mockTcpServer->clientConnected(newClientId);
QVERIFY2(spy.count() > 0, "Did not get the handshake message upon connect.");
QVERIFY2(spy.first().first() == newClientId, "Handshake message addressed at the wrong client.");
qApp->processEvents();
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
QVariantMap handShake = jsonDoc.toVariant().toMap();
// Check the Hello reply
QVariantMap handShake = injectAndWait("JSONRPC.Hello", QVariantMap(), newClientId).toMap();
QString nymeaVersionString(NYMEA_VERSION_STRING);
QVERIFY2(handShake.value("version").toString() == nymeaVersionString, "Handshake version doesn't match nymea version.");
QVERIFY2(handShake.value("params").toMap().value("version").toString() == nymeaVersionString, "Handshake version doesn't match nymea version.");
// Check whether pushButtonAuth is disabled
QCOMPARE(handShake.value("pushButtonAuthAvailable").toBool(), false);
@ -164,11 +161,12 @@ void TestJSONRPC::testHandshake()
handShake = injectAndWait("JSONRPC.Hello").toMap();
QCOMPARE(handShake.value("params").toMap().value("version").toString(), nymeaVersionString);
m_mockTcpServer->clientDisconnected(newClientId);
// Check whether pushButtonAuth is now
// Check whether pushButtonAuth is now enabled
QCOMPARE(handShake.value("params").toMap().value("pushButtonAuthAvailable").toBool(), true);
emit m_mockTcpServer->clientDisconnected(newClientId);
// And now check if it is sent again when calling JSONRPC.Hello
handShake = injectAndWait("JSONRPC.Hello").toMap();
QCOMPARE(handShake.value("params").toMap().value("version").toString(), nymeaVersionString);
@ -222,8 +220,11 @@ void TestJSONRPC::testInitialSetup()
QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
QVERIFY(spy.isValid());
QSignalSpy connectedSpy(m_mockTcpServer, &MockTcpServer::clientConnected);
QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected);
// Introspect call should work in any case
qCDebug(dcTests()) << "Calling Introspect, expecting success";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Introspect\"}\n");
if (spy.count() == 0) {
spy.wait();
@ -231,12 +232,13 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
QVariantMap response = jsonDoc.toVariant().toMap();
qWarning() << "Calling introspect on uninitialized instance:" << response.value("status").toString() << response.value("error").toString();
qCDebug(dcTests()) << "Result:" << response.value("status").toString() << response.value("error").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
// Hello call should work in any case too
spy.clear();
qCDebug(dcTests()) << "Calling Hello, expecting success";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n");
if (spy.count() == 0) {
spy.wait();
@ -244,12 +246,14 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Hello on uninitialized instance:" << response.value("status").toString() << response.value("error").toString();
qCDebug(dcTests()) << "Result:" << response.value("status").toString() << response.value("error").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
QCOMPARE(response.value("params").toMap().value("initialSetupRequired").toBool(), true);
// Any other call should fail with "unauthorized" even if we use a previously valid token
spy.clear();
disconnectedSpy.clear();
qCDebug(dcTests()) << "Calling Version, expecting failure (unauthenticated)";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Version\"}\n");
if (spy.count() == 0) {
spy.wait();
@ -257,13 +261,31 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Version on uninitialized instance:" << response.value("status").toString() << response.value("error").toString();
qCDebug(dcTests()) << "Result:" << response.value("status").toString() << response.value("error").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized"));
// Connection should terminate
if (disconnectedSpy.count() == 0) disconnectedSpy.wait();
QCOMPARE(disconnectedSpy.count(), 1);
qCDebug(dcTests()) << "Mock client disconnected";
connectedSpy.clear();
emit m_mockTcpServer->clientConnected(m_clientId);
if (connectedSpy.count() == 0) connectedSpy.wait();
QCOMPARE(connectedSpy.count(), 1);
qCDebug(dcTests()) << "Mock client connected";
spy.clear();
m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
if (spy.count() == 0) {
spy.wait();
}
QVERIFY(spy.count() == 1);
// Except CreateUser
// But it should still fail when giving a an invalid username
spy.clear();
qCDebug(dcTests()) << "Calling CreateUser, expecting failure (bad username)";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"dummy\", \"password\": \"DummyPW1!\"}}\n");
if (spy.count() == 0) {
spy.wait();
@ -271,12 +293,13 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling CreateUser on uninitialized instance with invalid user:" << response.value("status").toString() << response.value("params").toMap().value("error").toString();
qCDebug(dcTests()) << "Calling CreateUser on uninitialized instance with invalid user:" << response.value("status").toString() << response.value("params").toMap().value("error").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 0);
// or when giving a bad password
spy.clear();
qCDebug(dcTests()) << "Calling CreateUser, expecting failure (bad password)";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"weak\"}}\n");
if (spy.count() == 0) {
spy.wait();
@ -284,12 +307,13 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling CreateUser on uninitialized instance with weak password:" << response.value("status").toString() << response.value("params").toMap().value("error").toString();
qCDebug(dcTests()) << "Calling CreateUser on uninitialized instance with weak password:" << response.value("status").toString() << response.value("params").toMap().value("error").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 0);
// Now lets play by the rules (with an uppercase email)
spy.clear();
qCDebug(dcTests()) << "Calling CreateUser, expecting success";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"DummyPW1!\"}}\n");
if (spy.count() == 0) {
spy.wait();
@ -297,12 +321,13 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling CreateUser on uninitialized instance:" << response.value("status").toString() << response.value("error").toString();
qCDebug(dcTests) << "Calling CreateUser on uninitialized instance:" << response.value("status").toString() << response.value("error").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 1);
// Now that we have a user, initialSetup should be false in the Hello call
spy.clear();
qCDebug(dcTests()) << "Calling Hello, expecting success";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n");
if (spy.count() == 0) {
spy.wait();
@ -310,12 +335,14 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Hello on initialized instance:" << response.value("status").toString() << response.value("error").toString();
qCDebug(dcTests) << "Calling Hello on initialized instance:" << response.value("status").toString() << response.value("error").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
QCOMPARE(response.value("params").toMap().value("initialSetupRequired").toBool(), false);
// Calls should still fail, given we didn't get a new token yet
spy.clear();
disconnectedSpy.clear();
qCDebug(dcTests()) << "Calling Version, expecting failure (bad token)";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Version\"}\n");
if (spy.count() == 0) {
spy.wait();
@ -323,11 +350,30 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Version with old token:" << response.value("status").toString() << response.value("error").toString();
qCDebug(dcTests) << "Calling Version with old token:" << response.value("status").toString() << response.value("error").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized"));
// Connection should terminate
if (disconnectedSpy.count() == 0) disconnectedSpy.wait();
QCOMPARE(disconnectedSpy.count(), 1);
qCDebug(dcTests()) << "Mock client disconnected";
connectedSpy.clear();
emit m_mockTcpServer->clientConnected(m_clientId);
if (connectedSpy.count() == 0) connectedSpy.wait();
QCOMPARE(connectedSpy.count(), 1);
qCDebug(dcTests()) << "Mock client connected";
spy.clear();
m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
if (spy.count() == 0) {
spy.wait();
}
QVERIFY(spy.count() == 1);
// Now lets authenticate with a wrong user
spy.clear();
qCDebug(dcTests()) << "Calling Authenticate, expecting failure (bad user)";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy@wrong.domain\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n");
if (spy.count() == 0) {
spy.wait();
@ -335,7 +381,7 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Authenticate with wrong user:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString();
qCDebug(dcTests()) << "Calling Authenticate with wrong user:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
QCOMPARE(response.value("params").toMap().value("success").toBool(), false);
QVERIFY(response.value("params").toMap().value("token").toByteArray().isEmpty());
@ -343,6 +389,7 @@ void TestJSONRPC::testInitialSetup()
// Now lets authenticate with a wrong password
spy.clear();
qCDebug(dcTests()) << "Calling Authenticate, expecting failure (bad password)";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"wrongpw\", \"deviceName\": \"testcase\"}}\n");
if (spy.count() == 0) {
spy.wait();
@ -350,7 +397,7 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Authenticate with wrong password:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString();
qCDebug(dcTests()) << "Calling Authenticate with wrong password:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
QCOMPARE(response.value("params").toMap().value("success").toBool(), false);
QVERIFY(response.value("params").toMap().value("token").toByteArray().isEmpty());
@ -358,6 +405,7 @@ void TestJSONRPC::testInitialSetup()
// Now lets authenticate for real (but intentionally use a lowercase email here, should still work)
spy.clear();
qCDebug(dcTests()) << "Calling Authenticate, expecting success";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Authenticate\", \"params\": {\"username\": \"dummy@guh.io\", \"password\": \"DummyPW1!\", \"deviceName\": \"testcase\"}}\n");
if (spy.count() == 0) {
spy.wait();
@ -365,7 +413,7 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString();
qCDebug(dcTests()) << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
QCOMPARE(response.value("params").toMap().value("success").toBool(), true);
m_apiToken = response.value("params").toMap().value("token").toByteArray();
@ -373,6 +421,7 @@ void TestJSONRPC::testInitialSetup()
// Now do a Version call with the valid token and it should work
spy.clear();
qCDebug(dcTests()) << "Calling Version, expecting success";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + m_apiToken + "\", \"method\": \"JSONRPC.Version\"}\n");
if (spy.count() == 0) {
spy.wait();
@ -380,15 +429,19 @@ void TestJSONRPC::testInitialSetup()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString();
qCDebug(dcTests()) << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
}
void TestJSONRPC::testRevokeToken()
{
QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
QSignalSpy spy(m_mockTcpServer, &MockTcpServer::outgoingData);
QVERIFY(spy.isValid());
QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected);
QVERIFY(disconnectedSpy.isValid());
QSignalSpy connectedSpy(m_mockTcpServer, &MockTcpServer::clientConnected);
QVERIFY(connectedSpy.isValid());
// Now get all the tokens
spy.clear();
@ -399,7 +452,7 @@ void TestJSONRPC::testRevokeToken()
QVERIFY(spy.count() == 1);
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
QVariantMap response = jsonDoc.toVariant().toMap();
qWarning() << "Getting existing Tokens" << response.value("status").toString() << response;
qCDebug(dcTests()) << "Getting existing Tokens" << response.value("status").toString() << response;
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
QVariantList tokenList = response.value("params").toMap().value("tokenInfoList").toList();
QCOMPARE(tokenList.count(), 1);
@ -414,7 +467,7 @@ void TestJSONRPC::testRevokeToken()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString();
qCDebug(dcTests()) << "Calling Authenticate with valid credentials:" << response.value("params").toMap().value("success").toString() << response.value("params").toMap().value("token").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
QCOMPARE(response.value("params").toMap().value("success").toBool(), true);
QByteArray newToken = response.value("params").toMap().value("token").toByteArray();
@ -429,7 +482,7 @@ void TestJSONRPC::testRevokeToken()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString();
qCDebug(dcTests()) << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
// Now get all the tokens using the old token
@ -441,7 +494,7 @@ void TestJSONRPC::testRevokeToken()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Tokens" << response.value("status").toString();
qCDebug(dcTests()) << "Calling Tokens" << response.value("status").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
tokenList = response.value("params").toMap().value("tokenInfoList").toList();
QCOMPARE(tokenList.count(), 2);
@ -464,11 +517,12 @@ void TestJSONRPC::testRevokeToken()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling RemoveToken" << response.value("status").toString() << response;
qCDebug(dcTests()) << "Calling RemoveToken" << response.value("status").toString() << response;
QCOMPARE(response.value("status").toString(), QStringLiteral("success"));
// Do a call with the now removed token, it should be forbidden
spy.clear();
disconnectedSpy.clear();
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"token\": \"" + newToken + "\", \"method\": \"JSONRPC.Version\"}\n");
if (spy.count() == 0) {
spy.wait();
@ -476,8 +530,19 @@ void TestJSONRPC::testRevokeToken()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant().toMap();
qWarning() << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString();
qCDebug(dcTests()) << "Calling Version with valid token:" << response.value("status").toString() << response.value("error").toString();
QCOMPARE(response.value("status").toString(), QStringLiteral("unauthorized"));
// And connection should drop
if (disconnectedSpy.count() == 0) disconnectedSpy.wait();
QCOMPARE(disconnectedSpy.count(), 1);
// Connect again to not impact subsequent tests...
connectedSpy.clear();
emit m_mockTcpServer->clientConnected(m_clientId);
if (connectedSpy.count() == 0) connectedSpy.wait();
QCOMPARE(connectedSpy.count(), 1);
injectAndWait("JSONRPC.Hello");
}
void TestJSONRPC::testBasicCall_data()
@ -492,9 +557,9 @@ void TestJSONRPC::testBasicCall_data()
QTest::newRow("missing id") << QByteArray("{\"method\":\"JSONRPC.Introspect\"}\n") << false << false;
QTest::newRow("missing method") << QByteArray("{\"id\":42}\n") << true << false;
QTest::newRow("borked") << QByteArray("{\"id\":42, \"method\":\"JSO}\n") << false << false;
QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Foobar\"}\n") << true << false;
QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"FOO.Introspect\"}\n") << true << false;
QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"JSONRPCIntrospect\"}\n") << true << false;
QTest::newRow("invalid function") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Foobar\", \"token\": \"" + m_apiToken + "\"}\n") << true << false;
QTest::newRow("invalid namespace") << QByteArray("{\"id\":42, \"method\":\"FOO.Introspect\", \"token\": \"" + m_apiToken + "\"}\n") << true << false;
QTest::newRow("missing dot") << QByteArray("{\"id\":42, \"method\":\"JSONRPCIntrospect\", \"token\": \"" + m_apiToken + "\"}\n") << true << false;
QTest::newRow("invalid params") << QByteArray("{\"id\":42, \"method\":\"JSONRPC.Introspect\", \"params\":{\"törööö\":\"chooo-chooo\"}}\n") << true << false;
}
@ -594,7 +659,7 @@ void TestJSONRPC::deviceAddedRemovedNotifications()
params.insert("name", "Mock device");
params.insert("deviceParams", deviceParams);
QVariant response = injectAndWait("Devices.AddConfiguredDevice", params);
clientSpy.wait(2000);
if (clientSpy.count() == 0) clientSpy.wait();
verifyDeviceError(response);
QVariantMap notificationDeviceMap = checkNotification(clientSpy, "Devices.DeviceAdded").toMap().value("params").toMap().value("device").toMap();
@ -614,7 +679,7 @@ void TestJSONRPC::deviceAddedRemovedNotifications()
params.clear(); response.clear(); clientSpy.clear();
params.insert("deviceId", deviceId);
response = injectAndWait("Devices.RemoveConfiguredDevice", params);
clientSpy.wait(2000);
if (clientSpy.count() == 0) clientSpy.wait();
verifyDeviceError(response);
checkNotification(clientSpy, "Devices.DeviceRemoved");
@ -659,7 +724,7 @@ void TestJSONRPC::ruleAddedRemovedNotifications()
params.insert("stateEvaluator", stateEvaluator);
QVariant response = injectAndWait("Rules.AddRule", params);
clientSpy.wait(2000);
if (clientSpy.count() == 0) clientSpy.wait();
QVariantMap notificationRuleMap = checkNotification(clientSpy, "Rules.RuleAdded").toMap().value("params").toMap().value("rule").toMap();
verifyRuleError(response);
@ -678,7 +743,7 @@ void TestJSONRPC::ruleAddedRemovedNotifications()
params.clear(); response.clear(); clientSpy.clear();
params.insert("ruleId", ruleId);
response = injectAndWait("Rules.RemoveRule", params);
clientSpy.wait(2000);
if (clientSpy.count() == 0) clientSpy.wait();
checkNotification(clientSpy, "Devices.DeviceRemoved");
verifyRuleError(response);
@ -719,7 +784,7 @@ void TestJSONRPC::ruleActiveChangedNotifications()
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
response = injectAndWait("Rules.AddRule", params);
clientSpy.wait();
if (clientSpy.count() == 0) clientSpy.wait();
QVariant notificationVariant = checkNotification(clientSpy, "Rules.RuleAdded");
verifyRuleError(response);
@ -743,7 +808,7 @@ void TestJSONRPC::ruleActiveChangedNotifications()
QNetworkReply *reply = nam.get(request);
connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater()));
spy.wait();
if (spy.count() == 0) spy.wait();
notificationVariant = checkNotification(clientSpy, "Rules.RuleActiveChanged");
verifyRuleError(response);
@ -756,12 +821,12 @@ void TestJSONRPC::ruleActiveChangedNotifications()
qDebug() << "setting mock int state to 42";
QNetworkRequest request2(QUrl(QString("http://localhost:%1/setstate?%2=%3").arg(m_mockDevice1Port).arg(mockIntStateId.toString()).arg(42)));
QNetworkReply *reply2 = nam.get(request2);
spy.wait();
if (spy.count() == 0) spy.wait();
QCOMPARE(spy.count(), 1);
connect(reply2, SIGNAL(finished()), reply2, SLOT(deleteLater()));
clientSpy.wait();
if (clientSpy.count() == 0) clientSpy.wait();
notificationVariant = checkNotification(clientSpy, "Rules.RuleActiveChanged");
verifyRuleError(response);
@ -773,7 +838,7 @@ void TestJSONRPC::ruleActiveChangedNotifications()
params.insert("ruleId", ruleId);
response = injectAndWait("Rules.RemoveRule", params);
clientSpy.wait();
if (clientSpy.count() == 0) clientSpy.wait();
notificationVariant = checkNotification(clientSpy, "Rules.RuleRemoved");
checkNotification(clientSpy, "Logging.LogDatabaseUpdated");
verifyRuleError(response);
@ -807,7 +872,7 @@ void TestJSONRPC::deviceChangedNotifications()
response = injectAndWait("Devices.AddConfiguredDevice", params);
DeviceId deviceId = DeviceId(response.toMap().value("params").toMap().value("deviceId").toString());
QVERIFY(!deviceId.isNull());
clientSpy.wait();
if (clientSpy.count() == 0) clientSpy.wait();
verifyDeviceError(response);
QVariantMap notificationDeviceMap = checkNotification(clientSpy, "Devices.DeviceAdded").toMap().value("params").toMap().value("device").toMap();
@ -831,7 +896,7 @@ void TestJSONRPC::deviceChangedNotifications()
params.insert("deviceId", deviceId);
params.insert("deviceParams", newDeviceParams);
response = injectAndWait("Devices.ReconfigureDevice", params);
clientSpy.wait(2000);
if (clientSpy.count() == 0) clientSpy.wait();
verifyDeviceError(response);
QVariantMap reconfigureDeviceNotificationMap = checkNotification(clientSpy, "Devices.DeviceChanged").toMap().value("params").toMap().value("device").toMap();
QCOMPARE(reconfigureDeviceNotificationMap.value("deviceClassId").toString(), mockDeviceClassId.toString());
@ -848,7 +913,7 @@ void TestJSONRPC::deviceChangedNotifications()
params.insert("deviceId", deviceId);
params.insert("name", deviceName);
response = injectAndWait("Devices.EditDevice", params);
clientSpy.wait(2000);
if (clientSpy.count() == 0) clientSpy.wait();
verifyDeviceError(response);
QVariantMap editDeviceNotificationMap = checkNotification(clientSpy, "Devices.DeviceChanged").toMap().value("params").toMap().value("device").toMap();
QCOMPARE(editDeviceNotificationMap.value("deviceClassId").toString(), mockDeviceClassId.toString());
@ -860,7 +925,7 @@ void TestJSONRPC::deviceChangedNotifications()
params.clear(); response.clear(); clientSpy.clear();
params.insert("deviceId", deviceId);
response = injectAndWait("Devices.RemoveConfiguredDevice", params);
clientSpy.wait();
if (clientSpy.count() == 0) clientSpy.wait();
verifyDeviceError(response);
checkNotification(clientSpy, "Devices.DeviceRemoved");
checkNotification(clientSpy, "Logging.LogDatabaseUpdated");
@ -882,7 +947,7 @@ void TestJSONRPC::stateChangeEmitsNotifications()
QNetworkReply *reply = nam.get(request);
connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater()));
QSignalSpy replySpy(reply, SIGNAL(finished()));
replySpy.wait();
if (replySpy.count() == 0) replySpy.wait();
// Make sure the notification contains all the stuff we expect
QVariantList stateChangedVariants = checkNotifications(clientSpy, "Devices.StateChanged");
@ -1001,9 +1066,7 @@ void TestJSONRPC::testPushButtonAuth()
pushButtonAgent.sendButtonPressed();
if (clientSpy.count() == 0) {
clientSpy.wait();
}
if (clientSpy.count() == 0) clientSpy.wait();
QVariantMap rsp = checkNotification(clientSpy, "JSONRPC.PushButtonAuthFinished").toMap();
QCOMPARE(rsp.value("params").toMap().value("transactionId").toInt(), transactionId);
@ -1023,6 +1086,9 @@ void TestJSONRPC::testPushButtonAuthInterrupt()
// Create a new clientId for mallory and connect it to the server
QUuid malloryId = QUuid::createUuid();
m_mockTcpServer->clientConnected(malloryId);
QSignalSpy responseSpy(m_mockTcpServer, &MockTcpServer::outgoingData);
m_mockTcpServer->injectData(malloryId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
if (responseSpy.count() == 0) responseSpy.wait();
// Snoop in on everything the TCP server sends to its clients.
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
@ -1120,11 +1186,13 @@ void TestJSONRPC::testPushButtonAuthConnectionDrop()
pushButtonAgent.init();
// Snoop in on everything the TCP server sends to its clients.
QSignalSpy clientSpy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
QSignalSpy clientSpy(m_mockTcpServer, &MockTcpServer::outgoingData);
// Create a new clientId for alice and connect it to the server
QUuid aliceId = QUuid::createUuid();
m_mockTcpServer->clientConnected(aliceId);
m_mockTcpServer->injectData(aliceId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
if (clientSpy.count() == 0) clientSpy.wait();
// request push button auth for client 1 (alice) and check for OK reply
QVariantMap params;
@ -1139,6 +1207,9 @@ void TestJSONRPC::testPushButtonAuthConnectionDrop()
// Create a new clientId for bob and connect it to the server
QUuid bobId = QUuid::createUuid();
m_mockTcpServer->clientConnected(bobId);
clientSpy.clear();
m_mockTcpServer->injectData(bobId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
if (clientSpy.count() == 0) clientSpy.wait();
// request push button auth for client 2 (bob) and check for OK reply
params.clear();
@ -1175,7 +1246,7 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth()
NymeaCore::instance()->userManager()->removeUser("");
QVERIFY(NymeaCore::instance()->userManager()->initRequired());
QSignalSpy spy(m_mockTcpServer, SIGNAL(outgoingData(QUuid,QByteArray)));
QSignalSpy spy(m_mockTcpServer, &MockTcpServer::outgoingData);
QVERIFY(spy.isValid());
PushButtonAgent pushButtonAgent;
@ -1183,6 +1254,7 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth()
// Hello call should work in any case, telling us initial setup is required
spy.clear();
qCDebug(dcTests()) << "Calling Hello on uninitialized instance";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n");
if (spy.count() == 0) {
spy.wait();
@ -1190,13 +1262,16 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth()
QVERIFY(spy.count() == 1);
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
QVariant response = jsonDoc.toVariant();
qWarning() << "Calling Hello on uninitialized instance:" << response.toMap().value("status").toString() << response.toMap().value("error").toString();
qCDebug(dcTests()) << "Result:" << response.toMap().value("status").toString() << response.toMap().value("error").toString();
QCOMPARE(response.toMap().value("status").toString(), QStringLiteral("success"));
QCOMPARE(response.toMap().value("params").toMap().value("initialSetupRequired").toBool(), true);
// request push button auth for alice and check for OK reply
QUuid aliceId = QUuid::createUuid();
m_mockTcpServer->clientConnected(aliceId);
spy.clear();
m_mockTcpServer->injectData(aliceId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
if (spy.count() == 0) spy.wait();
QVariantMap params;
params.insert("deviceName", "alice");
@ -1223,6 +1298,7 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth()
// initialSetupRequired should be false in Hello call now
spy.clear();
qCDebug(dcTests()) << "Calling Hello on uninitialized instance";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.Hello\"}\n");
if (spy.count() == 0) {
spy.wait();
@ -1230,13 +1306,15 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant();
qWarning() << "Calling Hello on uninitialized instance:" << response.toMap().value("status").toString() << response.toMap().value("error").toString();
qCDebug(dcTests()) << "Result:" << response.toMap().value("status").toString() << response.toMap().value("error").toString();
QCOMPARE(response.toMap().value("status").toString(), QStringLiteral("success"));
QCOMPARE(response.toMap().value("params").toMap().value("initialSetupRequired").toBool(), false);
// CreateUser without a token should fail now even though there are 0 users in the DB
spy.clear();
QSignalSpy disconnectedSpy(m_mockTcpServer, &MockTcpServer::clientDisconnected);
qCDebug(dcTests()) << "Calling CreateUser on uninitialized instance";
m_mockTcpServer->injectData(m_clientId, "{\"id\": 555, \"method\": \"JSONRPC.CreateUser\", \"params\": {\"username\": \"Dummy@guh.io\", \"password\": \"DummyPW1!\"}}\n");
if (spy.count() == 0) {
spy.wait();
@ -1244,10 +1322,19 @@ void TestJSONRPC::testInitialSetupWithPushButtonAuth()
QVERIFY(spy.count() == 1);
jsonDoc = QJsonDocument::fromJson(spy.first().at(1).toByteArray());
response = jsonDoc.toVariant();
qWarning() << "Calling CreateUser on uninitialized instance:" << response.toMap().value("status").toString() << response.toMap().value("error").toString();
qCDebug(dcTests()) << "Result:" << response.toMap().value("status").toString() << response.toMap().value("error").toString();
QCOMPARE(response.toMap().value("status").toString(), QStringLiteral("unauthorized"));
QCOMPARE(NymeaCore::instance()->userManager()->users().count(), 0);
// Connection should drop
if (disconnectedSpy.isEmpty()) disconnectedSpy.wait();
QCOMPARE(disconnectedSpy.count(), 1);
// Reconnect to not impact subsequent tests
m_mockTcpServer->clientConnected(m_clientId);
spy.clear();
m_mockTcpServer->injectData(m_clientId, "{\"id\": 0, \"method\": \"JSONRPC.Hello\"}");
if (spy.isEmpty()) spy.wait();
}
void TestJSONRPC::testDataFragmentation_data()
@ -1302,12 +1389,10 @@ void TestJSONRPC::testGarbageData()
for (int i = 0; i < 1024; i++) {
data.append("a");
}
for (int i = 0; i < 11; i ++) {
for (int i = 0; i < 11 && spy.count() == 0; i ++) {
m_mockTcpServer->injectData(m_clientId, data);
}
QCOMPARE(spy.count(), 1);
}
#include "testjsonrpc.moc"

View File

@ -143,9 +143,11 @@ void NymeaTestBase::initTestCase()
m_clientId = QUuid::createUuid();
m_mockTcpServer->clientConnected(m_clientId);
QVariant response = injectAndWait("JSONRPC.Hello");
createMockDevice();
QVariant response = injectAndWait("Devices.GetConfiguredDevices", {});
response = injectAndWait("Devices.GetConfiguredDevices", {});
foreach (const QVariant &device, response.toMap().value("params").toMap().value("devices").toList()) {
if (device.toMap().value("deviceClassId").toUuid() == mockDeviceAutoClassId) {
m_mockDeviceAutoId = DeviceId(device.toMap().value("id").toString());
@ -442,6 +444,8 @@ void NymeaTestBase::restartServer()
coreSpy.wait();
m_mockTcpServer = MockTcpServer::servers().first();
m_mockTcpServer->clientConnected(m_clientId);
injectAndWait("JSONRPC.Hello");
}
void NymeaTestBase::clearLoggingDatabase()

View File

@ -91,7 +91,7 @@ void TestVersioning::apiChangeBumpsVersion()
p.waitForFinished();
QByteArray apiDiff = p.readAll();
qDebug() << "API Differences:" << endl << qUtf8Printable(apiDiff);
qCDebug(dcTests()) << "API Differences:" << endl << qUtf8Printable(apiDiff);
if (oldVersion == newVersionStripped && oldApi != newApi) {
QVERIFY2(false, "JSONRPC API has changed but version is still the same. You need to bump the API version.");

View File

@ -81,8 +81,14 @@ void TestWebSocketServer::testHandshake()
{
QWebSocket *socket = new QWebSocket("nymea tests", QWebSocketProtocol::Version13);
connect(socket, &QWebSocket::sslErrors, this, &TestWebSocketServer::sslErrors);
QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString)));
QSignalSpy connectedSpy(socket, &QWebSocket::connected);
socket->open(QUrl(QStringLiteral("wss://localhost:4444")));
connectedSpy.wait();
QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString)));
socket->sendTextMessage("{\"id\":0, \"method\": \"JSONRPC.Hello\"}");
spy.wait();
QVERIFY2(spy.count() > 0, "Did not get the handshake message upon connect.");
QJsonDocument jsonDoc = QJsonDocument::fromJson(spy.first().first().toByteArray());
@ -90,8 +96,8 @@ void TestWebSocketServer::testHandshake()
QString nymeaVersionString(NYMEA_VERSION_STRING);
QString jsonProtocolVersionString(JSON_PROTOCOL_VERSION);
QCOMPARE(handShake.value("version").toString(), nymeaVersionString);
QCOMPARE(handShake.value("protocol version").toString(), jsonProtocolVersionString);
QCOMPARE(handShake.value("params").toMap().value("version").toString(), nymeaVersionString);
QCOMPARE(handShake.value("params").toMap().value("protocol version").toString(), jsonProtocolVersionString);
socket->close();
socket->deleteLater();
@ -173,6 +179,10 @@ QVariant TestWebSocketServer::injectSocketAndWait(const QString &method, const Q
}
QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString)));
socket->sendTextMessage("{\"id\":0, \"method\": \"JSONRPC.Hello\"}");
spy.wait();
spy.clear();
socket->sendTextMessage(QString(jsonDoc.toJson(QJsonDocument::Compact)));
spy.wait();
@ -214,6 +224,11 @@ QVariant TestWebSocketServer::injectSocketData(const QByteArray &data)
}
QSignalSpy spy(socket, SIGNAL(textMessageReceived(QString)));
socket->sendTextMessage("{\"id\":0, \"method\": \"JSONRPC.Hello\"}");
spy.wait();
spy.clear();
socket->sendTextMessage(QString(data));
spy.wait();