Also imoprove validation errros

This commit is contained in:
Michael Zanetti 2019-09-25 12:56:19 +02:00
parent abd9fd8089
commit 9aa98c4fa4
4 changed files with 85 additions and 66 deletions

View File

@ -951,7 +951,9 @@ void DeviceManagerImplementation::loadPlugins()
PluginMetadata metaData(loader.metaData().value("MetaData").toObject()); PluginMetadata metaData(loader.metaData().value("MetaData").toObject());
if (!metaData.isValid()) { if (!metaData.isValid()) {
qCWarning(dcDeviceManager()) << "Plugin metadata not valid for" << entry; foreach (const QString &error, metaData.validationErrors()) {
qCWarning(dcDeviceManager()) << error;
}
loader.unload(); loader.unload();
continue; continue;
} }

View File

@ -48,6 +48,11 @@ bool PluginMetadata::isValid() const
return m_isValid; return m_isValid;
} }
QStringList PluginMetadata::validationErrors() const
{
return m_validationErrors;
}
PluginId PluginMetadata::pluginId() const PluginId PluginMetadata::pluginId() const
{ {
return m_pluginId; return m_pluginId;
@ -92,21 +97,23 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
QStringList pluginJsonProperties = QStringList() << "id" << "name" << "displayName" << "vendors" << "paramTypes" << "builtIn"; QStringList pluginJsonProperties = QStringList() << "id" << "name" << "displayName" << "vendors" << "paramTypes" << "builtIn";
QPair<QStringList, QStringList> verificationResult = verifyFields(pluginJsonProperties, pluginMandatoryJsonProperties, jsonObject); QPair<QStringList, QStringList> verificationResult = verifyFields(pluginJsonProperties, pluginMandatoryJsonProperties, jsonObject);
if (!verificationResult.first.isEmpty()) { if (!verificationResult.first.isEmpty()) {
qCWarning(dcPluginMetadata()) << "Plugin has missing fields:" << verificationResult.first.join(", ") << endl << jsonObject; m_validationErrors.append("Plugin metadata has missing fields: " + verificationResult.first.join(", "));
hasError = true; hasError = true;
// Not gonna continue parsing as we rely on mandatory fields being available // Not gonna continue parsing as we rely on mandatory fields being available
return; return;
} }
if (!verificationResult.second.isEmpty()) {
qCWarning(dcPluginMetadata()) << "Plugin has unknown fields:" << verificationResult.second.join(", ") << endl << qUtf8Printable(QJsonDocument::fromVariant(jsonObject.toVariantMap()).toJson(QJsonDocument::Indented));
hasError = true;
}
m_pluginId = jsonObject.value("id").toString(); m_pluginId = jsonObject.value("id").toString();
m_pluginName = jsonObject.value("name").toString(); m_pluginName = jsonObject.value("name").toString();
m_pluginDisplayName = jsonObject.value("displayName").toString(); m_pluginDisplayName = jsonObject.value("displayName").toString();
if (!verificationResult.second.isEmpty()) {
m_validationErrors.append("Plugin \"" + m_pluginName + "\" has unknown fields: \"" + verificationResult.second.join("\", \"") + "\"");
hasError = true;
}
if (!verifyDuplicateUuid(m_pluginId)) { if (!verifyDuplicateUuid(m_pluginId)) {
qCWarning(dcPluginMetadata()) << "Plugin" << m_pluginName << "has duplicate UUID:" << m_pluginId.toString(); m_validationErrors.append("Plugin \"" + m_pluginName + "\" has duplicate UUID: " + m_pluginId.toString());
hasError = true; hasError = true;
} }
@ -131,22 +138,23 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
// Check mandatory fields // Check mandatory fields
if (!verificationResult.first.isEmpty()) { if (!verificationResult.first.isEmpty()) {
qCWarning(dcPluginMetadata()) << "Vendor has missing fields:" << verificationResult.first.join(", ") << endl << vendorObject; m_validationErrors.append("Vendor metadata has missing fields: " + verificationResult.first.join(", ") + "\n" + qUtf8Printable(QJsonDocument::fromVariant(vendorObject.toVariantMap()).toJson(QJsonDocument::Indented)));
hasError = true; hasError = true;
// Not continuing parsing vendor as we rely on mandatory fields being around. // Not continuing parsing vendor as we rely on mandatory fields being around.
break; break;
} }
VendorId vendorId = VendorId(vendorObject.value("id").toString());
QString vendorName = vendorObject.value("name").toString();
// Check if there are any unknown fields // Check if there are any unknown fields
if (!verificationResult.second.isEmpty()) { if (!verificationResult.second.isEmpty()) {
qCWarning(dcPluginMetadata()) << pluginName() << "Vendor has unknown fields:" << verificationResult.second.join(", ") << endl << qUtf8Printable(QJsonDocument::fromVariant(vendorObject.toVariantMap()).toJson(QJsonDocument::Indented)); m_validationErrors.append("Vendor \"" + vendorName + "\" has unknown fields: \"" + verificationResult.second.join("\", \"") + "\"\n" + qUtf8Printable(QJsonDocument::fromVariant(vendorObject.toVariantMap()).toJson(QJsonDocument::Indented)));
hasError = true; hasError = true;
} }
VendorId vendorId = VendorId(vendorObject.value("id").toString());
QString vendorName = vendorObject.value("name").toString();
if (!verifyDuplicateUuid(vendorId)) { if (!verifyDuplicateUuid(vendorId)) {
qCWarning(dcPluginMetadata()) << "Vendor" << vendorName << "has duplicate UUID:" << vendorId.toString(); m_validationErrors.append("Vendor \"" + vendorName + "\" has duplicate UUID: " + vendorId.toString());
hasError = true; hasError = true;
} }
Vendor vendor(vendorId, vendorName); Vendor vendor(vendorId, vendorName);
@ -170,22 +178,23 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
// Check mandatory fields // Check mandatory fields
if (!verificationResult.first.isEmpty()) { if (!verificationResult.first.isEmpty()) {
qCWarning(dcPluginMetadata()) << "Device class has missing fields:" << verificationResult.first.join(", ") << endl << deviceClassObject; m_validationErrors.append("Device class has missing fields: \"" + verificationResult.first.join("\", \"") + "\"\n" + qUtf8Printable(QJsonDocument::fromVariant(deviceClassObject.toVariantMap()).toJson(QJsonDocument::Indented)));
hasError = true; hasError = true;
// Stop parsing this deviceClass as we rely on mandatory fields being around. // Stop parsing this deviceClass as we rely on mandatory fields being around.
continue; continue;
} }
DeviceClassId deviceClassId = deviceClassObject.value("id").toString();
QString deviceClassName = deviceClassObject.value("name").toString();
// Check if there are any unknown fields // Check if there are any unknown fields
if (!verificationResult.second.isEmpty()) { if (!verificationResult.second.isEmpty()) {
qCWarning(dcPluginMetadata()) << "Device class has unknown fields:" << verificationResult.second.join(", ") << endl << qUtf8Printable(QJsonDocument::fromVariant(deviceClassObject.toVariantMap()).toJson(QJsonDocument::Indented)); m_validationErrors.append("Device class \"" + deviceClassName + "\" has unknown fields: \"" + verificationResult.second.join("\", \"") + "\"\n" + qUtf8Printable(QJsonDocument::fromVariant(deviceClassObject.toVariantMap()).toJson(QJsonDocument::Indented)));
hasError = true; hasError = true;
} }
DeviceClassId deviceClassId = deviceClassObject.value("id").toString();
QString deviceClassName = deviceClassObject.value("name").toString();
if (!verifyDuplicateUuid(deviceClassId)) { if (!verifyDuplicateUuid(deviceClassId)) {
qCWarning(dcPluginMetadata()) << "Device class" << deviceClassName << "has duplicate UUID:" << deviceClassName; m_validationErrors.append("Device class \"" + deviceClassName + "\" has duplicate UUID: " + deviceClassName);
hasError = true; hasError = true;
} }
@ -208,7 +217,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
} else if (createMethodValue.toString().toLower() == "user") { } else if (createMethodValue.toString().toLower() == "user") {
createMethods |= DeviceClass::CreateMethodUser; createMethods |= DeviceClass::CreateMethodUser;
} else { } else {
qCWarning(dcPluginMetadata()) << "Unknown createMehtod" << createMethodValue.toString() << "in deviceClass " << deviceClass.name() << "."; m_validationErrors.append("Unknown createMehtod \"" + createMethodValue.toString() + "\" in deviceClass \"" + deviceClass.name() + "\".");
hasError = true; hasError = true;
} }
} }
@ -252,7 +261,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
} else if (setupMethodString.toLower() == "justadd") { } else if (setupMethodString.toLower() == "justadd") {
setupMethod = DeviceClass::SetupMethodJustAdd; setupMethod = DeviceClass::SetupMethodJustAdd;
} else { } else {
qCWarning(dcPluginMetadata()) << "Unknown setupMethod" << setupMethod << "in deviceClass" << deviceClass.name() << "."; m_validationErrors.append("Unknown setupMethod \"" + setupMethodString + "\" in deviceClass \"" + deviceClass.name() + "\".");
hasError = true; hasError = true;
} }
} }
@ -276,15 +285,18 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
// Check mandatory fields // Check mandatory fields
if (!verificationResult.first.isEmpty()) { if (!verificationResult.first.isEmpty()) {
qCWarning(dcPluginMetadata()) << "Device class stateType" << deviceClass.name() << "has missing properties" << verificationResult.first.join(", ") << "in stateType" << st; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" has missing properties \"" + verificationResult.first.join("\", \"") + "\" in stateType definition\n" + qUtf8Printable(QJsonDocument::fromVariant(st.toVariantMap()).toJson(QJsonDocument::Indented)));
hasError = true; hasError = true;
// Not processing further as mandatory fields are expected to be here // Not processing further as mandatory fields are expected to be here
continue; continue;
} }
StateTypeId stateTypeId = st.value("id").toString();
QString stateTypeName = st.value("name").toString();
// Check if there are any unknown fields // Check if there are any unknown fields
if (!verificationResult.second.isEmpty()) { if (!verificationResult.second.isEmpty()) {
qCWarning(dcPluginMetadata()) << "Device class stateType" << deviceClass.name() << "has unknown properties" << verificationResult.second.join(", ") << "in stateType" << st; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" state type \"" + stateTypeName + "\" has unknown properties \"" + verificationResult.second.join("\", \"") + "\"");
hasError = true; hasError = true;
} }
@ -292,21 +304,19 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
if (st.contains("writable") && st.value("writable").toBool()) { if (st.contains("writable") && st.value("writable").toBool()) {
writableState = true; writableState = true;
if (!st.contains("displayNameAction")) { if (!st.contains("displayNameAction")) {
qCWarning(dcPluginMetadata()) << "Device class" << deviceClass.name() << " has writable state but does not define the displayNameAction property" << st; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" state type \"" + stateTypeName + "\" has writable state but does not define the displayNameAction property");
hasError = true; hasError = true;
} }
} }
QVariant::Type t = QVariant::nameToType(st.value("type").toString().toLatin1().data()); QVariant::Type t = QVariant::nameToType(st.value("type").toString().toLatin1().data());
if (t == QVariant::Invalid) { if (t == QVariant::Invalid) {
qCWarning(dcPluginMetadata()) << "Invalid StateType type:" << st.value("type").toString(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" state type \"" + stateTypeName + "\" has invalid type: \"" + st.value("type").toString() + "\"");
hasError = true; hasError = true;
} }
StateTypeId stateTypeId = st.value("id").toString();
QString stateTypeName = st.value("name").toString();
if (!verifyDuplicateUuid(stateTypeId)) { if (!verifyDuplicateUuid(stateTypeId)) {
qCWarning(dcPluginMetadata()) << "StateType" << stateTypeName << "has duplicate UUID" << stateTypeId.toString(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" state type \"" + stateTypeName + "\" has duplicate UUID: " + stateTypeId.toString());
hasError = true; hasError = true;
} }
@ -337,8 +347,7 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
stateType.setPossibleValues(possibleValues); stateType.setPossibleValues(possibleValues);
if (!stateType.possibleValues().contains(stateType.defaultValue())) { if (!stateType.possibleValues().contains(stateType.defaultValue())) {
qCWarning(dcPluginMetadata()) << QString("\"%1\" plugin:").arg(pluginName()).toLatin1().data() << QString("The given default value \"%1\" is not in the possible values of the stateType \"%2\".") m_validationErrors.append("Device class \"" + deviceClass.name() + "\" state type \"" + stateTypeName + "\" has invalid default value \"" + stateType.defaultValue().toString() + "\" which is not in the list of possible values.");
.arg(stateType.defaultValue().toString()).arg(stateType.name()).toLatin1().data();
hasError = true; hasError = true;
break; break;
} }
@ -384,21 +393,22 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
// Check mandatory fields // Check mandatory fields
if (!verificationResult.first.isEmpty()) { if (!verificationResult.first.isEmpty()) {
qCWarning(dcPluginMetadata()) << "Device class" << deviceClass.name() << " has missing fields" << verificationResult.first.join(", ") << "in action type:" << endl << at; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" has missing fields \"" + verificationResult.first.join("\", \"") + "\" in action type definition.");
hasError = true; hasError = true;
continue; continue;
} }
ActionTypeId actionTypeId = ActionTypeId(at.value("id").toString());
QString actionTypeName = at.value("name").toString();
// Check if there are any unknown fields // Check if there are any unknown fields
if (!verificationResult.second.isEmpty()) { if (!verificationResult.second.isEmpty()) {
qCWarning(dcPluginMetadata()) << pluginName() << "Device class" << deviceClass.name() << "has unknown fields:" << verificationResult.second.join(", ") << "in action type:" << endl << at; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" action type \"" + actionTypeName + "\" has unknown fields \"" + verificationResult.second.join("\", \"") + "\"");
hasError = true; hasError = true;
} }
ActionTypeId actionTypeId = ActionTypeId(at.value("id").toString());
QString actionTypeName = at.value("name").toString();
if (!verifyDuplicateUuid(actionTypeId)) { if (!verifyDuplicateUuid(actionTypeId)) {
qCWarning(dcPluginMetadata()) << "Action Type" << actionTypeName << "has duplicate UUID:" << actionTypeId.toString(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" action type \"" + actionTypeName + "\" has duplicate UUID: " + actionTypeId.toString());
hasError = true; hasError = true;
} }
ActionType actionType(actionTypeId); ActionType actionType(actionTypeId);
@ -427,21 +437,22 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
// Check mandatory fields // Check mandatory fields
if (!verificationResult.first.isEmpty()) { if (!verificationResult.first.isEmpty()) {
qCWarning(dcPluginMetadata()) << "Device class" << deviceClass.name() << "has missing fields" << verificationResult.first.join(", ") << "in event type:" << endl << et; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" has missing fields \"" + verificationResult.first.join("\", \"") + "\" in event type defintion");
hasError = true; hasError = true;
continue; continue;
} }
EventTypeId eventTypeId = EventTypeId(et.value("id").toString());
QString eventTypeName = et.value("name").toString();
// Check if there are any unknown fields // Check if there are any unknown fields
if (!verificationResult.second.isEmpty()) { if (!verificationResult.second.isEmpty()) {
qCWarning(dcPluginMetadata()) << "Device class" << deviceClass.name() << "has unknown fields:" << verificationResult.second.join(", ") << "in event type:" << endl << et; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" event type \"" + eventTypeName + "\" has unknown fields \"" + verificationResult.second.join("\", \"") + "\"");
hasError = true; hasError = true;
} }
EventTypeId eventTypeId = EventTypeId(et.value("id").toString());
QString eventTypeName = et.value("name").toString();
if (!verifyDuplicateUuid(eventTypeId)) { if (!verifyDuplicateUuid(eventTypeId)) {
qCWarning(dcPluginMetadata()) << "Event type" << eventTypeName << "has duplicate UUID:" << eventTypeId.toString(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" event type \"" + eventTypeName + "\" has duplicate UUID: " + eventTypeId.toString());
hasError = true; hasError = true;
} }
EventType eventType(eventTypeId); EventType eventType(eventTypeId);
@ -467,21 +478,22 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
// Check mandatory fields // Check mandatory fields
if (!verificationResult.first.isEmpty()) { if (!verificationResult.first.isEmpty()) {
qCWarning(dcPluginMetadata()) << "Device class" << deviceClass.name() << " has missing fields" << verificationResult.first.join(", ") << "in browser item action type:" << endl << at; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" has missing fields \"" + verificationResult.first.join("\", \"") + "\" in browser item action type definition");
hasError = true; hasError = true;
continue; continue;
} }
ActionTypeId actionTypeId = ActionTypeId(at.value("id").toString());
QString actionTypeName = at.value("name").toString();
// Check if there are any unknown fields // Check if there are any unknown fields
if (!verificationResult.second.isEmpty()) { if (!verificationResult.second.isEmpty()) {
qCWarning(dcPluginMetadata()) << pluginName() << "Device class" << deviceClass.name() << "has unknown fields:" << verificationResult.second.join(", ") << "in browser item action type:" << endl << at; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" browser action type \"" + actionTypeName + "\" has unknown fields \"" + verificationResult.first.join("\", \"") + "\"");
hasError = true; hasError = true;
} }
ActionTypeId actionTypeId = ActionTypeId(at.value("id").toString());
QString actionTypeName = at.value("name").toString();
if (!verifyDuplicateUuid(actionTypeId)) { if (!verifyDuplicateUuid(actionTypeId)) {
qCWarning(dcPluginMetadata()) << "Browser Action Type" << actionTypeName << "has duplicate UUID:" << actionTypeId.toString(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" browser action type \"" + actionTypeName + "\" has duplicate UUID: " + actionTypeId.toString());
hasError = true; hasError = true;
} }
ActionType actionType(actionTypeId); ActionType actionType(actionTypeId);
@ -513,24 +525,24 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
foreach (const StateType &ifaceStateType, iface.stateTypes()) { foreach (const StateType &ifaceStateType, iface.stateTypes()) {
StateType stateType = stateTypes.findByName(ifaceStateType.name()); StateType stateType = stateTypes.findByName(ifaceStateType.name());
if (stateType.id().isNull()) { if (stateType.id().isNull()) {
qCWarning(dcPluginMetadata()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement state" << ifaceStateType.name(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement state \"" + ifaceStateType.name() + "\"");
hasError = true; hasError = true;
continue; continue;
} }
if (ifaceStateType.type() != stateType.type()) { if (ifaceStateType.type() != stateType.type()) {
qCWarning(dcPluginMetadata()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has not matching type" << stateType.type() << "!=" << ifaceStateType.type(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching type: \"" + QVariant::typeToName(stateType.type()) + "\" != \"" + QVariant::typeToName(ifaceStateType.type()) + "\"");
hasError = true; hasError = true;
continue; continue;
} }
if (ifaceStateType.minValue().isValid() && !ifaceStateType.minValue().isNull()) { if (ifaceStateType.minValue().isValid() && !ifaceStateType.minValue().isNull()) {
if (ifaceStateType.minValue().toString() == "any") { if (ifaceStateType.minValue().toString() == "any") {
if (stateType.minValue().isNull()) { if (stateType.minValue().isNull()) {
qCWarning(dcPluginMetadata()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has no minimum value defined."; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has no minimum value defined.");
hasError = true; hasError = true;
continue; continue;
} }
} else if (ifaceStateType.minValue() != stateType.minValue()) { } else if (ifaceStateType.minValue() != stateType.minValue()) {
qCWarning(dcPluginMetadata()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has not matching minimum value:" << ifaceStateType.minValue() << "!=" << stateType.minValue(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching minimum value: \"" + ifaceStateType.minValue().toString() + "\" != \"" + stateType.minValue().toString() + "\"");
hasError = true; hasError = true;
continue; continue;
} }
@ -538,18 +550,18 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
if (ifaceStateType.maxValue().isValid() && !ifaceStateType.maxValue().isNull()) { if (ifaceStateType.maxValue().isValid() && !ifaceStateType.maxValue().isNull()) {
if (ifaceStateType.maxValue().toString() == "any") { if (ifaceStateType.maxValue().toString() == "any") {
if (stateType.maxValue().isNull()) { if (stateType.maxValue().isNull()) {
qCWarning(dcPluginMetadata()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has no maximum value defined."; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has no maximum value defined.");
hasError = true; hasError = true;
continue; continue;
} }
} else if (ifaceStateType.maxValue() != stateType.maxValue()) { } else if (ifaceStateType.maxValue() != stateType.maxValue()) {
qCWarning(dcPluginMetadata()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has not matching maximum value:" << ifaceStateType.maxValue() << "!=" << stateType.minValue(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching maximum value: \"" + ifaceStateType.maxValue().toString() + "\" != \"" + stateType.minValue().toString() + "\"");
hasError = true; hasError = true;
continue; continue;
} }
} }
if (!ifaceStateType.possibleValues().isEmpty() && ifaceStateType.possibleValues() != stateType.possibleValues()) { if (!ifaceStateType.possibleValues().isEmpty() && ifaceStateType.possibleValues() != stateType.possibleValues()) {
qCWarning(dcPluginMetadata()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but state" << stateType.name() << "has not matching allowed values" << ifaceStateType.possibleValues() << "!=" << stateType.possibleValues(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but state \"" + stateType.name() + "\" has not matching allowed values.");
hasError = true; hasError = true;
continue; continue;
} }
@ -558,22 +570,22 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
foreach (const ActionType &ifaceActionType, iface.actionTypes()) { foreach (const ActionType &ifaceActionType, iface.actionTypes()) {
ActionType actionType = actionTypes.findByName(ifaceActionType.name()); ActionType actionType = actionTypes.findByName(ifaceActionType.name());
if (actionType.id().isNull()) { if (actionType.id().isNull()) {
qCWarning(dcPluginMetadata) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement action" << ifaceActionType.name(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement action \"" + ifaceActionType.name() + "\"");
hasError = true; hasError = true;
} }
foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) { foreach (const ParamType &ifaceActionParamType, ifaceActionType.paramTypes()) {
ParamType paramType = actionType.paramTypes().findByName(ifaceActionParamType.name()); ParamType paramType = actionType.paramTypes().findByName(ifaceActionParamType.name());
if (!paramType.isValid()) { if (!paramType.isValid()) {
qCWarning(dcPluginMetadata) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement action param" << ifaceActionType.name() << ":" << ifaceActionParamType.name(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but action \"" + actionType.name() + "\" doesn't implement action param \"" + ifaceActionParamType.name() + "\"");
hasError = true; hasError = true;
} else { } else {
if (paramType.type() != ifaceActionParamType.type()) { if (paramType.type() != ifaceActionParamType.type()) {
qCWarning(dcPluginMetadata()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but param" << paramType.name() << "is of wrong type:" << QVariant::typeToName(paramType.type()) << "expected:" << QVariant::typeToName(ifaceActionParamType.type()); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but action \"" + actionType.name() + "\" param \"" + paramType.name() + "\" is of wrong type: \"" + QVariant::typeToName(paramType.type()) + "\" expected: \"" + QVariant::typeToName(ifaceActionParamType.type()) + "\"");
hasError = true; hasError = true;
} }
foreach (const QVariant &allowedValue, ifaceActionParamType.allowedValues()) { foreach (const QVariant &allowedValue, ifaceActionParamType.allowedValues()) {
if (!paramType.allowedValues().contains(allowedValue)) { if (!paramType.allowedValues().contains(allowedValue)) {
qCWarning(dcPluginMetadata()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but param" << paramType.name() << "is missing allowed value" << allowedValue; m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but action \"" + actionType.name() + "\" param \"" + paramType.name() + "\" is missing allowed value \"" + allowedValue.toString() + "\"");
hasError = true; hasError = true;
} }
} }
@ -584,17 +596,17 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
foreach (const EventType &ifaceEventType, iface.eventTypes()) { foreach (const EventType &ifaceEventType, iface.eventTypes()) {
EventType eventType = eventTypes.findByName(ifaceEventType.name()); EventType eventType = eventTypes.findByName(ifaceEventType.name());
if (!eventType.isValid()) { if (!eventType.isValid()) {
qCWarning(dcPluginMetadata) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement event" << ifaceEventType.name(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but doesn't implement event \"" + ifaceEventType.name() + "\"");
hasError = true; hasError = true;
} }
foreach (const ParamType &ifaceEventParamType, ifaceEventType.paramTypes()) { foreach (const ParamType &ifaceEventParamType, ifaceEventType.paramTypes()) {
ParamType paramType = eventType.paramTypes().findByName(ifaceEventParamType.name()); ParamType paramType = eventType.paramTypes().findByName(ifaceEventParamType.name());
if (!paramType.isValid()) { if (!paramType.isValid()) {
qCWarning(dcPluginMetadata) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but doesn't implement event param" << ifaceEventType.name() << ":" << ifaceEventParamType.name(); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but event \"" + eventType.name() + "\" doesn't implement event param \"" + ifaceEventParamType.name() + "\"");
hasError = true; hasError = true;
} else { } else {
if (paramType.type() != ifaceEventParamType.type()) { if (paramType.type() != ifaceEventParamType.type()) {
qCWarning(dcPluginMetadata()) << "DeviceClass" << deviceClass.name() << "claims to implement interface" << value.toString() << "but param" << paramType.name() << "is of wrong type:" << QVariant::typeToName(paramType.type()) << "expected:" << QVariant::typeToName(ifaceEventParamType.type()); m_validationErrors.append("Device class \"" + deviceClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but event \"" + eventType.name() + "\" param \"" + paramType.name() + "\" is of wrong type: \"" + QVariant::typeToName(paramType.type()) + "\" expected: \"" + QVariant::typeToName(ifaceEventParamType.type()) + "\"");
hasError = true; hasError = true;
} }
} }
@ -676,30 +688,29 @@ QPair<bool, ParamTypes> PluginMetadata::parseParamTypes(const QJsonArray &array)
// Check mandatory fields // Check mandatory fields
if (!verificationResult.first.isEmpty()) { if (!verificationResult.first.isEmpty()) {
qCWarning(dcPluginMetadata()) << pluginName() << "Error parsing ParamType: missing fields:" << verificationResult.first.join(", ") << endl << pt; m_validationErrors.append("Error parsing ParamType. Missing fields: \"" + verificationResult.first.join("\", \"") + "\"");
hasErrors = true; hasErrors = true;
continue; continue;
} }
ParamTypeId paramTypeId = ParamTypeId(pt.value("id").toString());
QString paramName = pt.value("name").toString();
// Check if there are any unknown fields // Check if there are any unknown fields
if (!verificationResult.second.isEmpty()) { if (!verificationResult.second.isEmpty()) {
qCWarning(dcPluginMetadata()) << pluginName() << "Error parsing ParamType: unknown fields:" << verificationResult.second.join(", ") << endl << pt; m_validationErrors.append("Param type \"" + paramName + "\" has unknown fields: \"" + verificationResult.second.join("\", \"") + "\"");
hasErrors = true; hasErrors = true;
} }
// Check type // Check type
QVariant::Type t = QVariant::nameToType(pt.value("type").toString().toLatin1().data()); QVariant::Type t = QVariant::nameToType(pt.value("type").toString().toLatin1().data());
if (t == QVariant::Invalid) { if (t == QVariant::Invalid) {
qCWarning(dcPluginMetadata()) << pluginName() << QString("Invalid type %1 for param %2 in json file.") m_validationErrors.append("Param type \"" + paramName + "\" has unknown invalid type \"" + pt.value("type").toString() + "\"");
.arg(pt.value("type").toString())
.arg(pt.value("name").toString()).toLatin1().data();
hasErrors = true; hasErrors = true;
} }
ParamTypeId paramTypeId = ParamTypeId(pt.value("id").toString());
QString paramName = pt.value("name").toString();
if (!verifyDuplicateUuid(paramTypeId)) { if (!verifyDuplicateUuid(paramTypeId)) {
qCWarning(dcPluginMetadata()) << "Param" << paramName << "has duplicate UUID:" << paramTypeId.toString(); m_validationErrors.append("Param type \"" + paramName + "\" has duplicate UUID: " + paramTypeId.toString());
hasErrors = true; hasErrors = true;
} }
ParamType paramType(paramTypeId, paramName, t, pt.value("defaultValue").toVariant()); ParamType paramType(paramTypeId, paramName, t, pt.value("defaultValue").toVariant());

View File

@ -33,6 +33,7 @@ public:
PluginMetadata(const QJsonObject &jsonObject, bool isBuiltIn = false); PluginMetadata(const QJsonObject &jsonObject, bool isBuiltIn = false);
bool isValid() const; bool isValid() const;
QStringList validationErrors() const;
PluginId pluginId() const; PluginId pluginId() const;
QString pluginName() const; QString pluginName() const;
@ -71,6 +72,8 @@ private:
// As strict as possible without breaking them, but this should be removed ASAP // As strict as possible without breaking them, but this should be removed ASAP
// and only m_allUuids should be used to check for dupes // and only m_allUuids should be used to check for dupes
QList<QUuid> m_currentScopUuids; QList<QUuid> m_currentScopUuids;
QStringList m_validationErrors;
}; };
#endif // PLUGINMETADATA_H #endif // PLUGINMETADATA_H

View File

@ -74,7 +74,10 @@ int PluginInfoCompiler::compile(const QString &inputFile, const QString &outputF
PluginMetadata metadata(jsonObject); PluginMetadata metadata(jsonObject);
if (!metadata.isValid()) { if (!metadata.isValid()) {
qWarning().nospace().noquote() << inputFile << ": error: Plugin JSON failed validation. Aborting."; foreach (const QString &error, metadata.validationErrors()) {
QDebug dbg = qWarning().noquote().nospace();
dbg << inputFile << ": error: Plugin JSON failed validation: " << error;
}
return 2; return 2;
} }