JSON Auto correction + hyperion schema split for better readability (#452)

* revoke schema split

* add "getAutoCorrectedConfig" function

* revoke schema split

* revoke schema split

* revoke schema split

* Prevent compiler error if none grabber is available

* revoke schema split

* add "getAutoCorrectedConfig" function

* revoke schema split

* remove "configMigrator"

* remove "configMigrator"

* Change TestConfigFile to show how the autocorrection works

* revoke schema split

* revoke schema split

* remove "ConfigMigrator"

* remove "ConfigMigrator"

* remove "ConfigMigratorBase"

* remove "ConfigMigratorBase"

* Add QJsonUtils.h

* added ability "ignore-required"

It has been added the ability to correct the configuration without having to pay attention to the keyword "required" in the hyperion schema

* Allow Comments in Hyperion Schema

* add ability to ignore the "required" keyword in hyperion schema

* add ability to ignore the "required" keyword in hyperion schema

* add ability to ignore the "required" keyword in hyperion schema

* //Allow Comments in Hyperion Schema

* Update jsonschema.py to version 0.8.0 to support ...

references in json schema

* add RefResolver from jsonschema.py to resolve

references in hyperion schema

* remove dupe code

* split the hyperion schema in separatly files

For better readability

* add function "resolveReferences" to resolve

references in hyperion schema.

* remove CURRENT_CONFIG_VERSION

* remove CURRENT_CONFIG_VERSION

* split the hyperion schema in separatly files

For better readability

* Create schema-backgroundEffect.json

* Add the rest of the Hyperion schema via upload

* Remove Comments in config file

* Add return variable to function writeJson().

fix function resolveReferences().
edit function load() to handle QPair result from schemaChecker.

* edit function validate() to return QPair variable

* fit function loadEffectDefinition()

* fit function checkJson()

*  Expand error check by dividing

"_error" variable in "_error" and "_schemaError".
Replace variable "bool" in validate() in QPair

* Extend function "cmd_cfg_set" to handle auto correction

* Extend function "loadConfig" to handle auto correction

* fix function loadConfig()
This commit is contained in:
Paulchen Panther 2017-07-30 13:32:10 +02:00 committed by brindosch
parent 622a171808
commit 5bd020a570
45 changed files with 2356 additions and 2254 deletions

View File

@ -14,7 +14,6 @@ SET ( HYPERION_VERSION_STABLE OFF )
SET ( HYPERION_VERSION_MAJOR 2 )
SET ( HYPERION_VERSION_MINOR 0 )
SET ( HYPERION_VERSION_PATCH 0 )
SET ( CURRENT_CONFIG_VERSION 2 )
SET ( DEFAULT_AMLOGIC OFF )
SET ( DEFAULT_DISPMANX OFF )
@ -160,7 +159,7 @@ IF ( ${CHECK_EFFECTS_FAILED} )
ENDIF ()
EXECUTE_PROCESS (
COMMAND python test/jsonchecks/checkschema.py config/hyperion.config.json.default libsrc/hyperion/schemas/hyperion.schema-${CURRENT_CONFIG_VERSION}.json
COMMAND python test/jsonchecks/checkschema.py config/hyperion.config.json.default libsrc/hyperion/hyperion.schema.json
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
RESULT_VARIABLE CHECK_CONFIG_FAILED
)

View File

@ -30,9 +30,6 @@
// Define to enable profiler for development purpose
#cmakedefine ENABLE_PROFILER
// Define version id of current config
#define CURRENT_CONFIG_VERSION ${CURRENT_CONFIG_VERSION}
// the hyperion build id string
#define HYPERION_BUILD_ID "${HYPERION_BUILD_ID}"

View File

@ -40,7 +40,7 @@ $(document).ready( function() {
var r = new FileReader();
r.onload = function(e)
{
var content = e.target.result;
var content = e.target.result.replace(/[^:]?\/\/.*/g, ''); //remove Comments
//check file is json
var check = isJsonString(content);
@ -56,7 +56,7 @@ $(document).ready( function() {
if(typeof content.leds === 'undefined' || typeof content.general === 'undefined')
{
showInfoDialog('error', "", $.i18n('infoDialog_import_hyperror_text', f.name));
dis_imp_btn(true);
dis_imp_btn(true);
}
else
{

View File

@ -25,17 +25,14 @@ public:
// create the validator
QJsonSchemaChecker schemaChecker;
schemaChecker.setSchema(schemaTree);
bool valid = schemaChecker.validate(configTree);
QStringList messages = schemaChecker.getMessages();
for (int i = 0; i < messages.size(); ++i)
{
std::cout << messages[i].toStdString() << std::endl;
}
if (!valid)
if (!schemaChecker.validate(configTree).first)
{
for (int i = 0; i < messages.size(); ++i)
std::cout << messages[i].toStdString() << std::endl;
std::cerr << "Validation failed for configuration file: " << config.toStdString() << std::endl;
return -3;
}
@ -54,6 +51,7 @@ public:
throw std::runtime_error(QString("Configuration file not found: '" + path + "' (" + file.errorString() + ")").toStdString());
}
//Allow Comments in Config
QString config = QString(file.readAll());
config.remove(QRegularExpression("([^:]?\\/\\/.*)"));
@ -112,23 +110,62 @@ public:
}
}
throw std::runtime_error(QString("ERROR: Json schema wrong: " + error.errorString() + " at Line: " + QString::number(errorLine)
+ ", Column: " + QString::number(errorColumn)).toStdString()
);
throw std::runtime_error(QString("ERROR: Json schema wrong: " + error.errorString() +
" at Line: " + QString::number(errorLine) +
", Column: " + QString::number(errorColumn)).toStdString());
}
return doc.object();
return resolveReferences(doc.object());
}
static void writeJson(const QString& filename, QJsonObject& jsonTree)
static QJsonObject resolveReferences(const QJsonObject& schema)
{
QJsonObject result;
for (QJsonObject::const_iterator i = schema.begin(); i != schema.end(); ++i)
{
QString attribute = i.key();
const QJsonValue & attributeValue = *i;
if (attribute == "$ref" && attributeValue.isString())
{
try
{
result = readSchema(":/" + attributeValue.toString());
}
catch (std::runtime_error& error)
{
throw std::runtime_error(error.what());
}
}
else if (attributeValue.isObject())
result.insert(attribute, resolveReferences(attributeValue.toObject()));
else
result.insert(attribute, attributeValue);
}
return result;
}
static bool writeJson(const QString& filename, QJsonObject& jsonTree)
{
QJsonDocument doc;
doc.setObject(jsonTree);
QByteArray configData = doc.toJson(QJsonDocument::Indented);
QFile configFile(filename);
configFile.open(QIODevice::WriteOnly | QIODevice::Truncate);
if (!configFile.open(QFile::WriteOnly | QFile::Truncate))
return false;
configFile.write(configData);
QFile::FileError error = configFile.error();
if (error != QFile::NoError)
return false;
configFile.close();
return true;
}
};

View File

@ -5,6 +5,7 @@
#include <QJsonValue>
#include <QJsonArray>
#include <QStringList>
#include <QPair>
/// JsonSchemaChecker is a very basic implementation of json schema.
/// The json schema definition draft can be found at
@ -33,13 +34,23 @@ public:
/// @return true upon succes
///
bool setSchema(const QJsonObject & schema);
///
/// @brief Validate a JSON structure
/// @param value The JSON value to check
/// @return true when the arguments is valid according to the schema
/// @param ignoreRequired Ignore the "required" keyword in hyperion schema. Default is false
/// @return The first boolean is true when the arguments is valid according to the schema. The second is true when the schema contains no errors
/// @return TODO: Check the Schema in SetSchema() function and remove the QPair result
///
bool validate(const QJsonObject & value, bool ignoreRequired = false);
QPair<bool, bool> validate(const QJsonObject & value, bool ignoreRequired = false);
///
/// @brief Auto correct a JSON structure
/// @param value The JSON value to correct
/// @param ignoreRequired Ignore the "required" keyword in hyperion schema. Default is false
/// @return The corrected JSON structure
///
QJsonObject getAutoCorrectedConfig(const QJsonObject & value, bool ignoreRequired = false);
///
/// @return A list of error messages
@ -72,7 +83,7 @@ private:
/// @param[in] value The given value
/// @param[in] schema The specified type (as json-value)
///
void checkType(const QJsonValue & value, const QJsonValue & schema);
void checkType(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
///
/// Checks is required properties of an json-object exist and if all properties are of the
@ -101,7 +112,7 @@ private:
/// @param[in] value The given value
/// @param[in] schema The minimum value (as json-value)
///
void checkMinimum(const QJsonValue & value, const QJsonValue & schema);
void checkMinimum(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
///
/// Checks if the given value is smaller or equal to the specified value. If this is not the
@ -110,7 +121,7 @@ private:
/// @param[in] value The given value
/// @param[in] schema The maximum value (as json-value)
///
void checkMaximum(const QJsonValue & value, const QJsonValue & schema);
void checkMaximum(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
///
/// Checks if the given value is hugher than the specified value. If this is the
@ -119,7 +130,7 @@ private:
/// @param value The given value
/// @param schema The minimum size specification (as json-value)
///
void checkMinLength(const QJsonValue & value, const QJsonValue & schema);
void checkMinLength(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
///
/// Checks if the given value is smaller than the specified value. If this is the
@ -128,7 +139,7 @@ private:
/// @param value The given value
/// @param schema The maximum size specification (as json-value)
///
void checkMaxLength(const QJsonValue & value, const QJsonValue & schema);
void checkMaxLength(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
///
/// Validates all the items of an array.
@ -145,7 +156,7 @@ private:
/// @param value The json-array
/// @param schema The minimum size specification (as json-value)
///
void checkMinItems(const QJsonValue & value, const QJsonValue & schema);
void checkMinItems(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
///
/// Checks if a given array has at most a maximum number of items. If this is not the case
@ -154,7 +165,7 @@ private:
/// @param value The json-array
/// @param schema The maximum size specification (as json-value)
///
void checkMaxItems(const QJsonValue & value, const QJsonValue & schema);
void checkMaxItems(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
///
/// Checks if a given array contains only unique items. If this is not the case
@ -172,17 +183,23 @@ private:
/// @param value The enum value
/// @param schema The enum schema definition
///
void checkEnum(const QJsonValue & value, const QJsonValue & schema);
void checkEnum(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue);
private:
/// The schema of the entire json-configuration
QJsonObject _qSchema;
/// ignore the required value in json schema
bool _ignoreRequired;
/// Auto correction variable
QString _correct;
/// The auto corrected json-configuration
QJsonObject _autoCorrected;
/// The current location into a json-configuration structure being checked
QStringList _currentPath;
/// The result messages collected during the schema verification
QStringList _messages;
/// Flag indicating an error occured during validation
bool _error;
/// Flag indicating an schema error occured during validation
bool _schemaError;
};

View File

@ -0,0 +1,273 @@
#pragma once
// QT include
#include <QString>
#include <QStringList>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
class QJsonUtils
{
public:
static void modify(QJsonObject& value, QStringList path, const QJsonValue& newValue = QJsonValue::Null, QString propertyName = "")
{
QJsonObject result;
if (!path.isEmpty())
{
if (path.first() == "[root]")
path.removeFirst();
for (QStringList::iterator it = path.begin(); it != path.end(); ++it)
{
QString current = *it;
if (current.left(1) == ".")
*it = current.mid(1, current.size()-1);
}
if (!value.isEmpty())
modifyValue(value, result, path, newValue, propertyName);
else if (newValue != QJsonValue::Null && !propertyName.isEmpty())
result[propertyName] = newValue;
}
value = result;
}
static QJsonValue create(QJsonValue schema, bool ignoreRequired = false)
{
return createValue(schema, ignoreRequired);
}
private:
static QJsonValue createValue(QJsonValue schema, bool ignoreRequired)
{
QJsonObject result;
QJsonObject obj = schema.toObject();
if (obj.find("type") != obj.end() && obj.find("type").value().isString())
{
QJsonValue ret = QJsonValue::Null;
if (obj.find("type").value().toString() == "object" && ( obj.find("required").value().toBool() || ignoreRequired ) )
ret = createValue(obj["properties"], ignoreRequired);
else if (obj.find("type").value().toString() == "array" && ( obj.find("required").value().toBool() || ignoreRequired ) )
{
QJsonArray array;
if (obj.find("default") != obj.end())
ret = obj.find("default").value();
else
{
ret = createValue(obj["items"], ignoreRequired);
if (!ret.toObject().isEmpty())
array.append(ret);
ret = array;
}
}
else if ( obj.find("required").value().toBool() || ignoreRequired )
if (obj.find("default") != obj.end())
ret = obj.find("default").value();
return ret;
}
else
{
for (QJsonObject::const_iterator i = obj.begin(); i != obj.end(); ++i)
{
QString attribute = i.key();
const QJsonValue & attributeValue = *i;
QJsonValue subValue = obj[attribute];
if (attributeValue.toObject().find("type") != attributeValue.toObject().end())
{
if (attributeValue.toObject().find("type").value().toString() == "object" && ( attributeValue.toObject().find("required").value().toBool() || ignoreRequired ) )
{
if (obj.contains("properties"))
result[attribute] = createValue(obj["properties"], ignoreRequired);
else
result[attribute] = createValue(subValue, ignoreRequired);
}
else if (attributeValue.toObject().find("type").value().toString() == "array" && ( attributeValue.toObject().find("required").value().toBool() || ignoreRequired ) )
{
QJsonArray array;
if (attributeValue.toObject().find("default") != attributeValue.toObject().end())
result[attribute] = attributeValue.toObject().find("default").value();
else
{
QJsonValue retEmpty;
retEmpty = createValue(attributeValue.toObject()["items"], ignoreRequired);
if (!retEmpty.toObject().isEmpty())
array.append(retEmpty);
result[attribute] = array;
}
}
else if ( attributeValue.toObject().find("required").value().toBool() || ignoreRequired )
{
if (attributeValue.toObject().find("default") != attributeValue.toObject().end())
result[attribute] = attributeValue.toObject().find("default").value();
else
result[attribute] = QJsonValue::Null;
}
}
}
}
return result;
}
static void modifyValue(QJsonValue source, QJsonObject& target, QStringList path, const QJsonValue& newValue, QString& property)
{
QJsonObject obj = source.toObject();
if (!obj.isEmpty())
{
for (QJsonObject::iterator i = obj.begin(); i != obj.end(); ++i)
{
QString propertyName = i.key();
QJsonValue subValue = obj[propertyName];
if (subValue.isObject())
{
if (!path.isEmpty())
{
if (propertyName == path.first())
{
path.removeFirst();
if (!path.isEmpty())
{
QJsonObject obj;
modifyValue(subValue, obj, path, newValue, property);
subValue = obj;
}
else if (newValue != QJsonValue::Null)
subValue = newValue;
else
continue;
if (!subValue.toObject().isEmpty())
target[propertyName] = subValue;
}
else
{
if (path.first() == property && newValue != QJsonValue::Null)
{
target[property] = newValue;
property = QString();
}
target[propertyName] = subValue;
}
}
else
if (!subValue.toObject().isEmpty())
target[propertyName] = subValue;
}
else if (subValue.isArray())
{
if (!path.isEmpty())
{
if (propertyName == path.first())
{
path.removeFirst();
int arrayLevel = -1;
if (!path.isEmpty())
{
if ((path.first().left(1) == "[") && (path.first().right(1) == "]"))
{
arrayLevel = path.first().mid(1, path.first().size()-2).toInt();
path.removeFirst();
}
}
QJsonArray array = subValue.toArray();
QJsonArray json_array;
for (QJsonArray::iterator i = array.begin(); i != array.end(); ++i)
{
if (!path.isEmpty())
{
QJsonObject arr;
modifyValue(*i, arr, path, newValue, property);
subValue = arr;
}
else if (newValue != QJsonValue::Null)
subValue = newValue;
else
continue;
if (!subValue.toObject().isEmpty())
json_array.append(subValue);
else if (newValue != QJsonValue::Null && arrayLevel != -1)
json_array.append( (i - array.begin() == arrayLevel) ? subValue : *i );
}
if (!json_array.isEmpty())
target[propertyName] = json_array;
else if (newValue != QJsonValue::Null && arrayLevel == -1)
target[propertyName] = newValue;
}
else
{
if (path.first() == property && newValue != QJsonValue::Null)
{
target[property] = newValue;
property = QString();
}
target[propertyName] = subValue;
}
}
else
if (!subValue.toArray().isEmpty())
target[propertyName] = subValue;
}
else
{
if (!path.isEmpty())
{
if (propertyName == path.first())
{
path.removeFirst();
if (path.isEmpty())
{
if (newValue != QJsonValue::Null && property.isEmpty())
subValue = newValue;
else
continue;
}
target[propertyName] = subValue;
}
else
{
if (path.first() == property && newValue != QJsonValue::Null)
{
target[property] = newValue;
property = QString();
}
target[propertyName] = subValue;
}
}
else
target[propertyName] = subValue;
}
}
}
else if (newValue != QJsonValue::Null && !property.isEmpty())
{
target[property] = newValue;
property = QString();
}
}
};

View File

@ -150,7 +150,7 @@ bool EffectEngine::loadEffectDefinition(const QString &path, const QString &effe
QJsonSchemaChecker schemaChecker;
schemaChecker.setSchema(configSchema.object());
if (!schemaChecker.validate(configEffect.object()))
if (!schemaChecker.validate(configEffect.object()).first)
{
const QStringList & errors = schemaChecker.getMessages();
foreach (auto & error, errors)

View File

@ -5,9 +5,84 @@
{
"general" :
{
"type" : "object",
"required" : true
"$ref": "schema-general.json"
},
"logger" :
{
"$ref": "schema-logger.json"
},
"device" :
{
"$ref": "schema-device.json"
},
"color" :
{
"$ref": "schema-color.json"
},
"smoothing":
{
"$ref": "schema-smoothing.json"
},
"grabberV4L2" :
{
"$ref": "schema-grabberV4L2.json"
},
"framegrabber" :
{
"$ref": "schema-framegrabber.json"
},
"blackborderdetector" :
{
"$ref": "schema-blackborderdetector.json"
},
"kodiVideoChecker" :
{
"$ref": "schema-kodiVideoChecker.json"
},
"foregroundEffect" :
{
"$ref": "schema-foregroundEffect.json"
},
"backgroundEffect" :
{
"$ref": "schema-backgroundEffect.json"
},
"forwarder" :
{
"$ref": "schema-forwarder.json"
},
"jsonServer" :
{
"$ref": "schema-jsonServer.json"
},
"protoServer" :
{
"$ref": "schema-protoServer.json"
},
"boblightServer" :
{
"$ref": "schema-boblightServer.json"
},
"udpListener" :
{
"$ref": "schema-udpListener.json"
},
"webConfig" :
{
"$ref": "schema-webConfig.json"
},
"effects" :
{
"$ref": "schema-effects.json"
},
"ledConfig":
{
"$ref": "schema-ledConfig.json"
},
"leds":
{
"$ref": "schema-leds.json"
}
},
"additionalProperties" : true
"additionalProperties" : false
}

View File

@ -1,8 +1,26 @@
<RCC>
<qresource prefix="/">
<file alias="hyperion-schema">hyperion.schema.json</file>
<file alias="hyperion-schema-1">schemas/hyperion.schema-1.json</file>
<file alias="hyperion-schema-2">schemas/hyperion.schema-2.json</file>
<file alias="hyperion_default.config">../../config/hyperion.config.json.default</file>
</qresource>
<qresource prefix="/">
<file alias="hyperion-schema">hyperion.schema.json</file>
<file alias="hyperion_default.config">../../config/hyperion.config.json.default</file>
<file alias="schema-general.json">schema/schema-general.json</file>
<file alias="schema-logger.json">schema/schema-logger.json</file>
<file alias="schema-device.json">schema/schema-device.json</file>
<file alias="schema-color.json">schema/schema-color.json</file>
<file alias="schema-smoothing.json">schema/schema-smoothing.json</file>
<file alias="schema-grabberV4L2.json">schema/schema-grabberV4L2.json</file>
<file alias="schema-framegrabber.json">schema/schema-framegrabber.json</file>
<file alias="schema-blackborderdetector.json">schema/schema-blackborderdetector.json</file>
<file alias="schema-kodiVideoChecker.json">schema/schema-kodiVideoChecker.json</file>
<file alias="schema-foregroundEffect.json">schema/schema-foregroundEffect.json</file>
<file alias="schema-backgroundEffect.json">schema/schema-backgroundEffect.json</file>
<file alias="schema-forwarder.json">schema/schema-forwarder.json</file>
<file alias="schema-jsonServer.json">schema/schema-jsonServer.json</file>
<file alias="schema-protoServer.json">schema/schema-protoServer.json</file>
<file alias="schema-boblightServer.json">schema/schema-boblightServer.json</file>
<file alias="schema-udpListener.json">schema/schema-udpListener.json</file>
<file alias="schema-webConfig.json">schema/schema-webConfig.json</file>
<file alias="schema-effects.json">schema/schema-effects.json</file>
<file alias="schema-ledConfig.json">schema/schema-ledConfig.json</file>
<file alias="schema-leds.json">schema/schema-leds.json</file>
</qresource>
</RCC>

View File

@ -0,0 +1,48 @@
{
"type" : "object",
"title" : "edt_conf_bge_heading_title",
"properties" :
{
"enable" :
{
"type" : "boolean",
"title" : "edt_conf_general_enable_title",
"default" : true,
"propertyOrder" : 1
},
"type" :
{
"type" : "string",
"title" : "edt_conf_fge_type_title",
"enum" : ["color", "effect"],
"default" : "effect",
"options" : {
"enum_titles" : ["edt_conf_enum_color", "edt_conf_enum_effect"]
},
"propertyOrder" : 2
},
"color" :
{
"type" : "array",
"format" : "colorpicker",
"title" : "edt_conf_fge_color_title",
"default" : [255,138,0],
"items" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 255
},
"minItems" : 3,
"maxItems" : 3,
"propertyOrder" : 3
},
"effect" :
{
"type" : "string",
"format" : "select",
"title" : "edt_conf_fge_effect_title",
"propertyOrder" : 4
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,72 @@
{
"type" : "object",
"title" : "edt_conf_bb_heading_title",
"properties" :
{
"enable" :
{
"type" : "boolean",
"title" : "edt_conf_general_enable_title",
"default" : true,
"propertyOrder" : 1
},
"threshold" :
{
"type" : "integer",
"title" : "edt_conf_bb_threshold_title",
"minimum" : 0,
"maximum" : 100,
"default" : 5,
"append" : "edt_append_percent",
"propertyOrder" : 2
},
"unknownFrameCnt" :
{
"type" : "integer",
"title" : "edt_conf_bb_unknownFrameCnt_title",
"minimum" : 0,
"default" : 600,
"access" : "expert",
"propertyOrder" : 3
},
"borderFrameCnt" :
{
"type" : "integer",
"title" : "edt_conf_bb_borderFrameCnt_title",
"minimum" : 0,
"default" : 50,
"access" : "expert",
"propertyOrder" : 4
},
"maxInconsistentCnt" :
{
"type" : "integer",
"title" : "edt_conf_bb_maxInconsistentCnt_title",
"minimum" : 0,
"default" : 10,
"access" : "expert",
"propertyOrder" : 5
},
"blurRemoveCnt" :
{
"type" : "integer",
"title" : "edt_conf_bb_blurRemoveCnt_title",
"minimum" : 0,
"default" : 1,
"access" : "expert",
"propertyOrder" : 6
},
"mode" :
{
"type" : "string",
"title": "edt_conf_bb_mode_title",
"enum" : ["default", "classic", "osd"],
"default" : "default",
"options" : {
"enum_titles" : ["edt_conf_enum_bbdefault", "edt_conf_enum_bbclassic", "edt_conf_enum_bbosd"]
},
"propertyOrder" : 7
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,33 @@
{
"type" : "object",
"title" : "edt_conf_bobls_heading_title",
"properties" :
{
"enable" :
{
"type" : "boolean",
"title" : "edt_conf_general_enable_title",
"default" : false,
"propertyOrder" : 1
},
"port" :
{
"type" : "integer",
"required" : true,
"title" : "edt_conf_general_port_title",
"minimum" : 0,
"maximum" : 65535,
"propertyOrder" : 2
},
"priority" :
{
"type" : "integer",
"title" : "edt_conf_general_priority_title",
"minimum" : 100,
"maximum" : 254,
"default" : 201,
"propertyOrder" : 3
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,257 @@
{
"type":"object",
"title" : "edt_conf_color_heading_title",
"required" : true,
"properties":
{
"imageToLedMappingType" :
{
"type" : "string",
"required" : true,
"title" : "edt_conf_color_imageToLedMappingType_title",
"enum" : ["multicolor_mean", "unicolor_mean"],
"default" : "multicolor_mean",
"options" : {
"enum_titles" : ["edt_conf_enum_multicolor_mean", "edt_conf_enum_unicolor_mean"]
},
"propertyOrder" : 1
},
"channelAdjustment" :
{
"type" : "array",
"title" : "edt_conf_color_channelAdjustment_header_title",
"required" : true,
"propertyOrder" : 3,
"items" :
{
"type" : "object",
"required" : true,
"title" : "edt_conf_color_channelAdjustment_header_itemtitle",
"properties" :
{
"id" :
{
"type" : "string",
"title" : "edt_conf_color_id_title",
"required" : true,
"default" : "A userdefined name",
"propertyOrder" : 1
},
"leds" :
{
"type" : "string",
"title" : "edt_conf_color_leds_title",
"required" : true,
"default" : "*",
"propertyOrder" : 2
},
"black" :
{
"type" : "array",
"title" : "edt_conf_color_black_title",
"format" : "colorpicker",
"required" : true,
"default": [0,0,0],
"items" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 255
},
"minItems" : 3,
"maxItems" : 3,
"propertyOrder" : 3
},
"white" :
{
"type" : "array",
"title" : "edt_conf_color_white_title",
"format" : "colorpicker",
"required" : true,
"default": [255,255,255],
"items" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 255
},
"minItems" : 3,
"maxItems" : 3,
"propertyOrder" : 4
},
"red" :
{
"type" : "array",
"title" : "edt_conf_color_red_title",
"format" : "colorpicker",
"required" : true,
"default": [255,0,0],
"items" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 255
},
"minItems" : 3,
"maxItems" : 3,
"propertyOrder" : 5
},
"green" :
{
"type" : "array",
"title" : "edt_conf_color_green_title",
"format" : "colorpicker",
"required" : true,
"default": [0,255,0],
"items" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 255
},
"minItems" : 3,
"maxItems" : 3,
"propertyOrder" : 6
},
"blue" :
{
"type" : "array",
"title" : "edt_conf_color_blue_title",
"format" : "colorpicker",
"required" : true,
"default": [0,0,255],
"items" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 255
},
"minItems" : 3,
"maxItems" : 3,
"propertyOrder" : 7
},
"cyan" :
{
"type" : "array",
"title" : "edt_conf_color_cyan_title",
"format" : "colorpicker",
"required" : true,
"default": [0,255,255],
"items" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 255
},
"minItems" : 3,
"maxItems" : 3,
"propertyOrder" : 8
},
"magenta" :
{
"type" : "array",
"title" : "edt_conf_color_magenta_title",
"format" : "colorpicker",
"required" : true,
"default": [255,0,255],
"items" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 255
},
"minItems" : 3,
"maxItems" : 3,
"propertyOrder" : 9
},
"yellow" :
{
"type" : "array",
"title" : "edt_conf_color_yellow_title",
"format" : "colorpicker",
"required" : true,
"default": [255,255,0],
"items" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 255
},
"minItems" : 3,
"maxItems" : 3,
"propertyOrder" : 10
},
"backlightThreshold" :
{
"type" : "integer",
"title" : "edt_conf_color_backlightThreshold_title",
"required" : true,
"minimum" : 0,
"maximum": 100,
"default" : 0,
"append" : "edt_append_percent",
"propertyOrder" : 11
},
"backlightColored" :
{
"type" : "boolean",
"title" : "edt_conf_color_backlightColored_title",
"required" : true,
"default" : false,
"propertyOrder" : 12
},
"brightness" :
{
"type" : "integer",
"title" : "edt_conf_color_brightness_title",
"required" : true,
"minimum" : 0,
"maximum": 100,
"default" : 100,
"append" : "edt_append_percent",
"propertyOrder" : 13
},
"brightnessCompensation" :
{
"type" : "integer",
"title" : "edt_conf_color_brightnessComp_title",
"required" : true,
"minimum" : 0,
"maximum": 100,
"default" : 90,
"append" : "edt_append_percent",
"access" : "advanced",
"propertyOrder" : 14
},
"gammaRed" :
{
"type" : "number",
"title" : "edt_conf_color_gammaRed_title",
"required" : true,
"minimum" : 0.1,
"maximum": 100.0,
"default" : 1.5,
"step" : 0.1,
"propertyOrder" : 15
},
"gammaGreen" :
{
"type" : "number",
"title" : "edt_conf_color_gammaGreen_title",
"required" : true,
"minimum" : 0.1,
"maximum": 100.0,
"default" : 1.5,
"step" : 0.1,
"propertyOrder" : 16
},
"gammaBlue" :
{
"type" : "number",
"title" : "edt_conf_color_gammaBlue_title",
"required" : true,
"minimum" : 0.1,
"maximum": 100.0,
"default" : 1.5,
"step" : 0.1,
"propertyOrder" : 17
}
},
"additionalProperties" : false
}
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,41 @@
{
"type" : "object",
"title" : "edt_dev_general_heading_title",
"required" : true,
"defaultProperties": ["ledCount","colorOrder","rewriteTime","minimumWriteTime"],
"properties" :
{
"type" :
{
"type" : "string"
},
"ledCount" :
{
"type" : "integer",
"minimum" : 0,
"title" : "edt_dev_general_ledCount_title",
"propertyOrder" : 2
},
"colorOrder" :
{
"type" : "string",
"title" : "edt_dev_general_colorOrder_title",
"enum" : ["rgb", "bgr", "rbg", "brg", "gbr", "grb"],
"default" : "rgb",
"options" : {
"enum_titles" : ["edt_conf_enum_rgb", "edt_conf_enum_bgr", "edt_conf_enum_rbg", "edt_conf_enum_brg", "edt_conf_enum_gbr", "edt_conf_enum_grb"]
},
"propertyOrder" : 3
},
"rewriteTime": {
"type": "integer",
"title":"edt_dev_general_rewriteTime_title",
"default": 1000,
"append" : "edt_append_ms",
"minimum": 0,
"access" : "expert",
"propertyOrder" : 4
}
},
"additionalProperties" : true
}

View File

@ -0,0 +1,29 @@
{
"type" : "object",
"title" : "edt_conf_effp_heading_title",
"properties" :
{
"paths" :
{
"type" : "array",
"title" : "edt_conf_effp_paths_title",
"default" : ["../custom-effects"],
"items" : {
"type": "string",
"title" : "edt_conf_effp_paths_itemtitle"
},
"propertyOrder" : 1
},
"disable" :
{
"type" : "array",
"title" : "edt_conf_effp_disable_title",
"items" : {
"type": "string",
"title" : "edt_conf_effp_disable_itemtitle"
},
"propertyOrder" : 2
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,57 @@
{
"type" : "object",
"title" : "edt_conf_fge_heading_title",
"properties" :
{
"enable" :
{
"type" : "boolean",
"title" : "edt_conf_general_enable_title",
"default" : true,
"propertyOrder" : 1
},
"type" :
{
"type" : "string",
"title" : "edt_conf_fge_type_title",
"enum" : ["color", "effect"],
"default" : "effect",
"options" : {
"enum_titles" : ["edt_conf_enum_color", "edt_conf_enum_effect"]
},
"propertyOrder" : 2
},
"color" :
{
"type" : "array",
"format" : "colorpicker",
"title" : "edt_conf_fge_color_title",
"default" : [255,0,0],
"items" : {
"type" : "integer",
"minimum" : 0,
"maximum" : 255
},
"minItems" : 3,
"maxItems" : 3,
"propertyOrder" : 3
},
"effect" :
{
"type" : "string",
"format" : "select",
"title" : "edt_conf_fge_effect_title",
"propertyOrder" : 4
},
"duration_ms" :
{
"type" : "integer",
"title" : "edt_conf_fge_duration_ms_title",
"default" : 3000,
"minimum" : 100,
"append" : "edt_append_ms",
"propertyOrder" : 5
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,36 @@
{
"type" : "object",
"title" : "edt_conf_fw_heading_title",
"required" : true,
"properties" :
{
"enable" :
{
"type" : "boolean",
"title" : "edt_conf_general_enable_title",
"propertyOrder" : 1
},
"json" :
{
"type" : "array",
"title" : "edt_conf_fw_json_title",
"required" : true,
"items" : {
"type": "string",
"title" : "edt_conf_fw_json_itemtitle"
},
"propertyOrder" : 2
},
"proto" :
{
"type" : "array",
"title" : "edt_conf_fw_proto_title",
"items" : {
"type": "string",
"title" : "edt_conf_fw_proto_itemtitle"
},
"propertyOrder" : 3
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,132 @@
{
"type" : "object",
"title" : "edt_conf_fg_heading_title",
"properties" :
{
"enable" :
{
"type" : "boolean",
"title" : "edt_conf_general_enable_title",
"default" : true,
"propertyOrder" : 1
},
"type" :
{
"type" : "string",
"title" : "edt_conf_fg_type_title",
"enum" : ["auto","dispmanx","amlogic","x11","framebuffer"],
"default" : "auto",
"propertyOrder" : 2
},
"width" :
{
"type" : "integer",
"title" : "edt_conf_fg_width_title",
"minimum" : 10,
"default" : 80,
"append" : "edt_append_pixel",
"propertyOrder" : 3
},
"height" :
{
"type" : "integer",
"title" : "edt_conf_fg_height_title",
"minimum" : 10,
"default" : 45,
"append" : "edt_append_pixel",
"propertyOrder" : 3
},
"frequency_Hz" :
{
"type" : "integer",
"title" : "edt_conf_fg_frequency_Hz_title",
"minimum" : 1,
"default" : 10,
"append" : "edt_append_hz",
"propertyOrder" : 4
},
"priority" :
{
"type" : "integer",
"title" : "edt_conf_general_priority_title",
"minimum" : 100,
"maximum" : 254,
"default" : 250,
"propertyOrder" : 5
},
"cropLeft" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_cropLeft_title",
"minimum" : 0,
"default" : 0,
"append" : "edt_append_pixel",
"propertyOrder" : 6
},
"cropRight" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_cropRight_title",
"minimum" : 0,
"default" : 0,
"append" : "edt_append_pixel",
"propertyOrder" : 7
},
"cropTop" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_cropTop_title",
"minimum" : 0,
"default" : 0,
"append" : "edt_append_pixel",
"propertyOrder" : 8
},
"cropBottom" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_cropBottom_title",
"minimum" : 0,
"default" : 0,
"append" : "edt_append_pixel",
"propertyOrder" : 9
},
"useXGetImage" :
{
"type" : "boolean",
"title" : "edt_conf_fg_useXGetImage_title",
"default" : false,
"propertyOrder" : 10
},
"horizontalPixelDecimation" :
{
"type" : "integer",
"title" : "edt_conf_fg_horizontalPixelDecimation_title",
"minimum" : 0,
"default" : 8,
"propertyOrder" : 11
},
"verticalPixelDecimation" :
{
"type" : "integer",
"title" : "edt_conf_fg_verticalPixelDecimation_title",
"minimum" : 0,
"default" : 8,
"propertyOrder" : 12
},
"device" :
{
"type" : "string",
"title" : "edt_conf_fg_device_title",
"default" : "/dev/fb0",
"propertyOrder" : 13
},
"display" :
{
"type" : "integer",
"title" : "edt_conf_fg_display_title",
"minimum" : 0,
"propertyOrder" : 14
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,34 @@
{
"type" : "object",
"title" : "edt_conf_gen_heading_title",
"required" : true,
"properties" :
{
"name" :
{
"type" : "string",
"title" : "edt_conf_gen_name_title",
"default" : "My Hyperion Config",
"minLength" : 4,
"maxLength" : 20,
"required" : true,
"propertyOrder" : 1
},
"showOptHelp" :
{
"type" : "boolean",
"title" : "edt_conf_gen_showOptHelp_title",
"default" : true,
"required" : true,
"propertyOrder" : 2
},
"configVersion" :
{
"type" : "integer",
"default" : 2,
"minimum" : 1,
"access" : "system",
"required" : true
}
}
}

View File

@ -0,0 +1,226 @@
{
"type":"array",
"title" : "edt_conf_v4l2_heading_title",
"minItems": 1,
"maxItems": 2,
"items":
{
"type" : "object",
"title" : "edt_conf_v4l2_heading_title",
"properties" :
{
"enable" :
{
"type" : "boolean",
"title" : "edt_conf_general_enable_title",
"default" : false,
"propertyOrder" : 1
},
"device" :
{
"type" : "string",
"title" : "edt_conf_v4l2_device_title",
"default" : "auto",
"propertyOrder" : 2
},
"input" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_input_title",
"minimum" : 0,
"default" : 0,
"propertyOrder" : 3
},
"standard" :
{
"type" : "string",
"title" : "edt_conf_v4l2_standard_title",
"enum" : ["PAL","NTSC"],
"default" : "PAL",
"options" : {
"enum_titles" : ["edt_conf_enum_PAL", "edt_conf_enum_NTSC"]
},
"propertyOrder" : 4
},
"width" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_width_title",
"minimum" : -1,
"default" : -1,
"append" : "edt_append_pixel",
"propertyOrder" : 5
},
"height" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_height_title",
"minimum" : -1,
"default" : -1,
"append" : "edt_append_pixel",
"propertyOrder" : 6
},
"frameDecimation" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_frameDecimation_title",
"minimum" : 0,
"default" : 2,
"propertyOrder" : 7
},
"sizeDecimation" :
{
"type" : "integer",
"title" : "Size decimation",
"minimum" : 0,
"default" : 6,
"propertyOrder" : 8
},
"priority" :
{
"type" : "integer",
"minimum" : 100,
"maximum" : 253,
"title" : "edt_conf_general_priority_title",
"default" : 240,
"propertyOrder" : 9
},
"mode" :
{
"type" : "string",
"title" : "edt_conf_v4l2_mode_title",
"enum" : ["2D","3DSBS","3DTAB"],
"default" : "2D",
"propertyOrder" : 10
},
"useKodiChecker" :
{
"type" : "boolean",
"title" : "edt_conf_v4l2_useKodiChecker_title",
"default" : false,
"propertyOrder" : 11
},
"cropLeft" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_cropLeft_title",
"minimum" : 0,
"default" : 0,
"append" : "edt_append_pixel",
"propertyOrder" : 12
},
"cropRight" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_cropRight_title",
"minimum" : 0,
"default" : 0,
"append" : "edt_append_pixel",
"propertyOrder" : 13
},
"cropTop" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_cropTop_title",
"minimum" : 0,
"default" : 0,
"append" : "edt_append_pixel",
"propertyOrder" : 14
},
"cropBottom" :
{
"type" : "integer",
"title" : "edt_conf_v4l2_cropBottom_title",
"minimum" : 0,
"default" : 0,
"append" : "edt_append_pixel",
"propertyOrder" : 15
},
"signalDetection" :
{
"type" : "boolean",
"title" : "edt_conf_v4l2_signalDetection_title",
"default" : true,
"propertyOrder" : 16
},
"redSignalThreshold" :
{
"type" : "number",
"title" : "edt_conf_v4l2_redSignalThreshold_title",
"minimum" : 0.0,
"maximum" : 1.0,
"default" : 0.1,
"step" : 0.005,
"append" : "edt_append_percent",
"propertyOrder" : 17
},
"greenSignalThreshold" :
{
"type" : "number",
"title" : "edt_conf_v4l2_greenSignalThreshold_title",
"minimum" : 0.0,
"maximum" : 1.0,
"default" : 0.1,
"step" : 0.025,
"append" : "edt_append_percent",
"propertyOrder" : 18
},
"blueSignalThreshold" :
{
"type" : "number",
"title" : "edt_conf_v4l2_blueSignalThreshold_title",
"minimum" : 0.0,
"maximum" : 1.0,
"default" : 0.1,
"step" : 0.005,
"append" : "edt_append_percent",
"propertyOrder" : 19
},
"signalDetectionVerticalOffsetMin" :
{
"type" : "number",
"title" : "edt_conf_v4l2_signalDetectionVerticalOffsetMin_title",
"minimum" : 0.0,
"maximum" : 1.0,
"default" : 0.1,
"step" : 0.005,
"append" : "edt_append_percent",
"propertyOrder" : 20
},
"signalDetectionVerticalOffsetMax" :
{
"type" : "number",
"title" : "edt_conf_v4l2_signalDetectionVerticalOffsetMax_title",
"minimum" : 0.0,
"maximum" : 1.0,
"default" : 0.1,
"step" : 0.005,
"append" : "edt_append_percent",
"propertyOrder" : 21
},
"signalDetectionHorizontalOffsetMin" :
{
"type" : "number",
"title" : "edt_conf_v4l2_signalDetectionHorizontalOffsetMin_title",
"minimum" : 0.0,
"maximum" : 1.0,
"default" : 0.1,
"step" : 0.005,
"append" : "edt_append_percent",
"propertyOrder" : 22
},
"signalDetectionHorizontalOffsetMax" :
{
"type" : "number",
"title" : "edt_conf_v4l2_signalDetectionHorizontalOffsetMax_title",
"minimum" : 0.0,
"maximum" : 1.0,
"default" : 0.1,
"step" : 0.005,
"append" : "edt_append_percent",
"propertyOrder" : 23
}
},
"additionalProperties" : false
}
}

View File

@ -0,0 +1,18 @@
{
"type" : "object",
"required" : true,
"title" : "edt_conf_js_heading_title",
"properties" :
{
"port" :
{
"type" : "integer",
"required" : true,
"title" : "edt_conf_general_port_title",
"minimum" : 0,
"maximum" : 65535,
"default" : 19444
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,80 @@
{
"type" : "object",
"title" : "edt_conf_kodic_heading_title",
"properties" :
{
"enable" :
{
"type" : "boolean",
"title" : "edt_conf_general_enable_title",
"default" : false,
"propertyOrder" : 1
},
"kodiAddress" :
{
"type" : "string",
"title" : "edt_conf_kodic_kodiAddress_title",
"default" : "127.0.0.1",
"propertyOrder" : 2
},
"kodiTcpPort" :
{
"type" : "integer",
"title" : "edt_conf_kodic_kodiTcpPort_title",
"minimum" : 0,
"maximum" : 65535,
"default" : 9090,
"propertyOrder" : 3
},
"grabVideo" :
{
"type" : "boolean",
"title" : "edt_conf_kodic_grabVideo_title",
"default" : true,
"propertyOrder" : 4
},
"grabPictures" :
{
"type" : "boolean",
"title" : "edt_conf_kodic_grabPictures_title",
"default" : true,
"propertyOrder" : 5
},
"grabAudio" :
{
"type" : "boolean",
"title" : "edt_conf_kodic_grabAudio_title",
"default" : true,
"propertyOrder" : 6
},
"grabMenu" :
{
"type" : "boolean",
"title" : "edt_conf_kodic_grabMenu_title",
"default" : false,
"propertyOrder" : 7
},
"grabPause" :
{
"type" : "boolean",
"title" : "edt_conf_kodic_grabPause_title",
"default" : false,
"propertyOrder" : 8
},
"grabScreensaver" :
{
"type" : "boolean",
"title" : "edt_conf_kodic_grabScreensaver_title",
"default" : false,
"propertyOrder" : 9
},
"enable3DDetection" :
{
"type" : "boolean",
"title" : "edt_conf_kodic_enable3DDetection_title",
"default" : false,
"propertyOrder" : 10
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,80 @@
{
"type" : "object",
"properties" :
{
"top" :
{
"type" : "integer",
"minimum" : 0,
"default" : 8
},
"bottom" :
{
"type" : "integer",
"minimum" : 0,
"default" : 8
},
"left" :
{
"type" : "integer",
"minimum" : 0,
"default" : 5
},
"right" :
{
"type" : "integer",
"minimum" : 0,
"default" : 5
},
"glength" :
{
"type" : "integer",
"minimum" : 0,
"default" : 0
},
"gpos" :
{
"type" : "integer",
"minimum" : 0,
"default" : 0
},
"position" :
{
"type" : "integer",
"default" : 0
},
"reverse" :
{
"type" : "boolean",
"default" : false
},
"hdepth" :
{
"type" : "integer",
"minimum" : 1,
"maximum" : 100,
"default" : 8
},
"vdepth" :
{
"type" : "integer",
"minimum" : 1,
"maximum" : 100,
"default" : 5
},
"overlap" :
{
"type" : "integer",
"minimum" : 0,
"default" : 0
},
"edgegap" :
{
"type" : "integer",
"minimum" : 0,
"maximum" : 50,
"default" : 0
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,70 @@
{
"type":"array",
"required":true,
"items":
{
"type":"object",
"properties":
{
"index":
{
"type":"integer",
"required":true
},
"clone":
{
"type":"integer"
},
"hscan":
{
"type":"object",
"properties":
{
"minimum":
{
"type":"number",
"minimum" : 0,
"maximum" : 1,
"required":true
},
"maximum":
{
"type":"number",
"minimum" : 0,
"maximum" : 1,
"required":true
}
},
"additionalProperties" : false
},
"vscan":
{
"type":"object",
"properties":
{
"minimum":
{
"type":"number",
"minimum" : 0,
"maximum" : 1,
"required":true
},
"maximum":
{
"type":"number",
"minimum" : 0,
"maximum" : 1,
"required":true
}
},
"additionalProperties" : false
},
"colorOrder":
{
"type": "string",
"enum" : ["rgb", "bgr", "rbg", "brg", "gbr", "grb"]
}
},
"additionalProperties" : false
}
}

View File

@ -0,0 +1,18 @@
{
"type" : "object",
"title" : "edt_conf_log_heading_title",
"properties" :
{
"level" :
{
"type" : "string",
"enum" : ["silent", "warn", "verbose", "debug"],
"title" : "edt_conf_log_level_title",
"options" : {
"enum_titles" : ["edt_conf_enum_logsilent", "edt_conf_enum_logwarn", "edt_conf_enum_logverbose", "edt_conf_enum_logdebug"]
},
"default" : "warn"
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,18 @@
{
"type" : "object",
"required" : true,
"title" : "edt_conf_ps_heading_title",
"properties" :
{
"port" :
{
"type" : "integer",
"required" : true,
"title" : "edt_conf_general_port_title",
"minimum" : 0,
"maximum" : 65535,
"default" : 19445
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,63 @@
{
"type" : "object",
"title" : "edt_conf_smooth_heading_title",
"properties" :
{
"enable" :
{
"type" : "boolean",
"title" : "edt_conf_general_enable_title",
"default" : true,
"propertyOrder" : 1
},
"type" :
{
"type" : "string",
"title" : "edt_conf_smooth_type_title",
"enum" : ["linear"],
"default" : "linear",
"options" : {
"enum_titles" : ["edt_conf_enum_linear"]
},
"propertyOrder" : 2
},
"time_ms" :
{
"type" : "integer",
"title" : "edt_conf_smooth_time_ms_title",
"minimum" : 25,
"maximum": 600,
"default" : 200,
"append" : "edt_append_ms",
"propertyOrder" : 3
},
"updateFrequency" :
{
"type" : "number",
"title" : "edt_conf_smooth_updateFrequency_title",
"minimum" : 1.0,
"maximum" : 100.0,
"default" : 25.0,
"append" : "edt_append_hz",
"propertyOrder" : 4
},
"updateDelay" :
{
"type" : "integer",
"title" : "edt_conf_smooth_updateDelay_title",
"minimum" : 0,
"maximum": 2048,
"default" : 0,
"append" : "edt_append_ms",
"propertyOrder" : 5
},
"continuousOutput" :
{
"type" : "boolean",
"title" : "edt_conf_smooth_continuousOutput_title",
"default" : true,
"propertyOrder" : 6
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,56 @@
{
"type" : "object",
"title" : "edt_conf_udpl_heading_title",
"properties" :
{
"enable" :
{
"type" : "boolean",
"title" : "edt_conf_general_enable_title",
"default" : false,
"propertyOrder" : 1
},
"address" :
{
"type" : "string",
"title" : "edt_conf_udpl_address_title",
"default" : "239.255.28.01",
"propertyOrder" : 2
},
"port" :
{
"type" : "integer",
"title" : "edt_conf_general_port_title",
"minimum" : 0,
"maximum" : 65535,
"default" : 2801,
"propertyOrder" : 3
},
"priority" :
{
"type" : "integer",
"title" : "edt_conf_general_priority_title",
"minimum" : 100,
"maximum" : 254,
"default" : 200,
"propertyOrder" : 4
},
"timeout" :
{
"type" : "integer",
"title" : "edt_conf_udpl_timeout_title",
"minimum" : 1000,
"default" : 10000,
"append" : "edt_append_ms",
"propertyOrder" : 5
},
"shared" :
{
"type" : "boolean",
"title" : "edt_conf_udpl_shared_title",
"default" : false,
"propertyOrder" : 6
}
},
"additionalProperties" : false
}

View File

@ -0,0 +1,33 @@
{
"type" : "object",
"title" : "edt_conf_webc_heading_title",
"properties" :
{
"enable" :
{
"type" : "boolean",
"title" : "edt_conf_general_enable_title",
"default" : true,
"access" : "expert",
"propertyOrder" : 1
},
"document_root" :
{
"type" : "string",
"title" : "edt_conf_webc_docroot_title",
"access" : "expert",
"propertyOrder" : 2
},
"port" :
{
"type" : "integer",
"title" : "edt_conf_general_port_title",
"minimum" : 0,
"maximum" : 65535,
"default" : 8099,
"access" : "expert",
"propertyOrder" : 3
}
},
"additionalProperties" : false
}

View File

@ -1,386 +0,0 @@
{
"type" : "object",
"required" : true,
"properties" : {
"device" : {
"type" : "object",
"required" : true,
"properties" : {
"name" : {
"type" : "string",
"required" : true
},
"type" : {
"type" : "string",
"required" : true
},
"output" : {
"type" : "string",
"required" : true
},
"rate" : {
"type" : "integer",
"required" : true,
"minimum" : 0
},
"colorOrder" : {
"type" : "string",
"required" : false
},
"bgr-output" : {
"type" : "boolean",
"required" : false
}
},
"additionalProperties" : false
},
"color": {
"type":"object",
"required":false,
"properties": {
"hsv" : {
"type" : "object",
"required" : false,
"properties" : {
"saturationGain" : {
"type" : "number",
"required" : false,
"minimum" : 0.0
},
"valueGain" : {
"type" : "number",
"required" : false,
"minimum" : 0.0
}
},
"additionalProperties" : false
},
"hsl" : {
"type" : "object",
"required" : false,
"properties" : {
"saturationGain" : {
"type" : "number",
"required" : false,
"minimum" : 0.0
},
"luminanceGain" : {
"type" : "number",
"required" : false,
"minimum" : 0.0
},
"luminanceMinimum" : {
"type" : "number",
"required" : false,
"minimum" : 0.0
}
},
"additionalProperties" : false
},
"red": {
"type":"object",
"required":false,
"properties":{
"gamma": {
"type":"number",
"required":false
},
"blacklevel": {
"type":"number",
"required":false
},
"whitelevel": {
"type":"number",
"required":false
},
"threshold": {
"type":"number",
"required":false,
"minimum" : 0.0,
"maximum" : 1.0
}
},
"additionalProperties" : false
},
"green": {
"type":"object",
"required":false,
"properties":{
"gamma": {
"type":"number",
"required":false
},
"blacklevel": {
"type":"number",
"required":false
},
"whitelevel": {
"type":"number",
"required":false
},
"threshold": {
"type":"number",
"required":false,
"minimum" : 0.0,
"maximum" : 1.0
}
},
"additionalProperties" : false
},
"blue": {
"type":"object",
"required":false,
"properties":{
"gamma": {
"type":"number",
"required":false
},
"whitelevel": {
"type":"number",
"required":false
},
"blacklevel": {
"type":"number",
"required":false
},
"threshold": {
"type":"number",
"required":false,
"minimum" : 0.0,
"maximum" : 1.0
}
},
"additionalProperties" : false
},
"smoothing" : {
"type" : "object",
"required" : false,
"properties" : {
"type" : {
"type" : "enum",
"required" : true,
"values" : ["none", "linear"]
},
"time_ms" : {
"type" : "integer",
"required" : false,
"minimum" : 10
},
"updateFrequency" : {
"type" : "number",
"required" : false,
"minimum" : 0.001
}
},
"additionalProperties" : false
}
},
"additionalProperties" : false
},
"leds": {
"type":"array",
"required":true,
"items": {
"type":"object",
"properties": {
"index": {
"type":"integer",
"required":true
},
"hscan": {
"type":"object",
"required":true,
"properties": {
"minimum": {
"type":"number",
"required":true
},
"maximum": {
"type":"number",
"required":true
}
},
"additionalProperties" : false
},
"vscan": {
"type":"object",
"required":true,
"properties": {
"minimum": {
"type":"number",
"required":true
},
"maximum": {
"type":"number",
"required":true
}
},
"additionalProperties" : false
},
"colorOrder" : {
"type" : "string",
"required" : false
}
},
"additionalProperties" : false
}
},
"effects" :
{
"type" : "object",
"required" : false,
"properties" : {
"paths" : {
"type" : "array",
"required" : false,
"items" : {
"type" : "string"
}
}
},
"additionalProperties" : false
},
"blackborderdetector" :
{
"type" : "object",
"required" : false,
"properties" : {
"enable" : {
"type" : "boolean",
"required" : true
},
"threshold" : {
"type" : "number",
"required" : false,
"minimum" : 0.0,
"maximum" : 1.0
}
},
"additionalProperties" : false
},
"xbmcVideoChecker" :
{
"type" : "object",
"required" : false,
"properties" : {
"xbmcAddress" : {
"type" : "string",
"required" : true
},
"xbmcTcpPort" : {
"type" : "integer",
"required" : true
},
"grabVideo" : {
"type" : "boolean",
"required" : true
},
"grabPictures" : {
"type" : "boolean",
"required" : true
},
"grabAudio" : {
"type" : "boolean",
"required" : true
},
"grabMenu" : {
"type" : "boolean",
"required" : true
},
"grabPause" : {
"type" : "boolean",
"required" : false
},
"grabScreensaver" : {
"type" : "boolean",
"required" : false
},
"enable3DDetection" : {
"type" : "boolean",
"required" : false
}
},
"additionalProperties" : false
},
"bootsequence" :
{
"type" : "object",
"required" : false,
"properties" : {
"path" : {
"type" : "string",
"required" : true
},
"effect" : {
"type" : "string",
"required" : true
}
},
"additionalProperties" : false
},
"framegrabber" :
{
"type" : "object",
"required" : false,
"properties" : {
"width" : {
"type" : "integer",
"required" : true
},
"height" : {
"type" : "integer",
"required" : true
},
"frequency_Hz" : {
"type" : "integer",
"required" : true
}
},
"additionalProperties" : false
},
"jsonServer" :
{
"type" : "object",
"required" : false,
"properties" : {
"port" : {
"type" : "integer",
"required" : true,
"minimum" : 0,
"maximum" : 65535
}
},
"additionalProperties" : false
},
"protoServer" :
{
"type" : "object",
"required" : false,
"properties" : {
"port" : {
"type" : "integer",
"required" : true,
"minimum" : 0,
"maximum" : 65535
}
},
"additionalProperties" : false
},
"boblightServer" :
{
"type" : "object",
"required" : false,
"properties" : {
"port" : {
"type" : "integer",
"required" : true,
"minimum" : 0,
"maximum" : 65535
}
},
"additionalProperties" : false
}
},
"additionalProperties" : false
}

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,7 @@ SET(Utils_HEADERS
${CURRENT_HEADER_DIR}/RgbToRgbw.h
${CURRENT_HEADER_DIR}/jsonschema/QJsonFactory.h
${CURRENT_HEADER_DIR}/jsonschema/QJsonSchemaChecker.h
${CURRENT_HEADER_DIR}/jsonschema/QJsonUtils.h
${CURRENT_HEADER_DIR}/global_defines.h
${CURRENT_HEADER_DIR}/SysInfo.h
)

View File

@ -15,7 +15,6 @@
#include <QFile>
#include <QFileInfo>
#include <QJsonDocument>
#include <QVariantMap>
#include <QDir>
#include <QImage>
#include <QBuffer>
@ -594,6 +593,7 @@ void JsonProcessor::handleServerInfoCommand(const QJsonObject&, const QString& c
ledDevices["available"] = availableLedDevices;
info["ledDevices"] = ledDevices;
#if defined(ENABLE_DISPMANX) || defined(ENABLE_V4L2) || defined(ENABLE_FB) || defined(ENABLE_AMLOGIC) || defined(ENABLE_OSX) || defined(ENABLE_X11)
// get available grabbers
QJsonObject grabbers;
//grabbers["active"] = ????;
@ -605,6 +605,9 @@ void JsonProcessor::handleServerInfoCommand(const QJsonObject&, const QString& c
grabbers["available"] = availableGrabbers;
info["grabbers"] = grabbers;
#else
info["grabbers"] = QString("none");
#endif
// get available components
QJsonArray component;
@ -844,45 +847,19 @@ void JsonProcessor::handleSchemaGetCommand(const QJsonObject& message, const QSt
// make sure the resources are loaded (they may be left out after static linking)
Q_INIT_RESOURCE(resource);
QJsonParseError error;
// read the hyperion json schema from the resource
QFile schemaData(":/hyperion-schema-"+QString::number(_hyperion->getConfigVersionId()));
QString schemaFile = ":/hyperion-schema";
if (!schemaData.open(QIODevice::ReadOnly))
try
{
std::stringstream error;
error << "Schema not found: " << schemaData.errorString().toStdString();
throw std::runtime_error(error.str());
schemaJson = QJsonFactory::readSchema(schemaFile);
}
QByteArray schema = schemaData.readAll();
QJsonDocument doc = QJsonDocument::fromJson(schema, &error);
schemaData.close();
if (error.error != QJsonParseError::NoError)
catch(const std::runtime_error& error)
{
// report to the user the failure and their locations in the document.
int errorLine(0), errorColumn(0);
for( int i=0, count=qMin( error.offset,schema.size()); i<count; ++i )
{
++errorColumn;
if(schema.at(i) == '\n' )
{
errorColumn = 0;
++errorLine;
}
}
std::stringstream sstream;
sstream << "ERROR: Json schema wrong: " << error.errorString().toStdString() << " at Line: " << errorLine << ", Column: " << errorColumn;
throw std::runtime_error(sstream.str());
throw std::runtime_error(error.what());
}
schemaJson = doc.object();
// collect all LED Devices
properties = schemaJson["properties"].toObject();
alldevices = LedDevice::getLedDeviceSchemas();
@ -1139,7 +1116,7 @@ bool JsonProcessor::checkJson(const QJsonObject& message, const QString& schemaR
schemaChecker.setSchema(schemaJson.object());
// check the message
if (!schemaChecker.validate(message, ignoreRequired))
if (!schemaChecker.validate(message, ignoreRequired).first)
{
const QStringList & errors = schemaChecker.getMessages();
errorMessage = "{";

View File

@ -5,6 +5,7 @@
// Utils-Jsonschema includes
#include <utils/jsonschema/QJsonSchemaChecker.h>
#include <utils/jsonschema/QJsonUtils.h>
QJsonSchemaChecker::QJsonSchemaChecker()
{
@ -25,11 +26,12 @@ bool QJsonSchemaChecker::setSchema(const QJsonObject & schema)
return true;
}
bool QJsonSchemaChecker::validate(const QJsonObject & value, bool ignoreRequired)
QPair<bool, bool> QJsonSchemaChecker::validate(const QJsonObject & value, bool ignoreRequired)
{
// initialize state
_ignoreRequired = ignoreRequired;
_error = false;
_schemaError = false;
_messages.clear();
_currentPath.clear();
_currentPath.append("[root]");
@ -37,7 +39,27 @@ bool QJsonSchemaChecker::validate(const QJsonObject & value, bool ignoreRequired
// validate
validate(value, _qSchema);
return !_error;
return QPair<bool, bool>(!_error, !_schemaError);
}
QJsonObject QJsonSchemaChecker::getAutoCorrectedConfig(const QJsonObject& value, bool ignoreRequired)
{
_ignoreRequired = ignoreRequired;
QStringList sequence = QStringList() << "remove" << "modify" << "create";
_error = false;
_schemaError = false;
_messages.clear();
_autoCorrected = value;
for(const QString &correct : sequence)
{
_correct = correct;
_currentPath.clear();
_currentPath.append("[root]");
validate(_autoCorrected, _qSchema);
}
return _autoCorrected;
}
void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &schema)
@ -48,15 +70,17 @@ void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &s
QString attribute = i.key();
const QJsonValue & attributeValue = *i;
QJsonObject::const_iterator defaultValue = schema.find("default");
if (attribute == "type")
checkType(value, attributeValue);
checkType(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
else if (attribute == "properties")
{
if (value.isObject())
checkProperties(value.toObject(), attributeValue.toObject());
else
{
_error = true;
_schemaError = true;
setMessage("properties attribute is only valid for objects");
continue;
}
@ -76,19 +100,19 @@ void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &s
}
else
{
_error = true;
_schemaError = true;
setMessage("additional properties attribute is only valid for objects");
continue;
}
}
else if (attribute == "minimum")
checkMinimum(value, attributeValue);
checkMinimum(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
else if (attribute == "maximum")
checkMaximum(value, attributeValue);
checkMaximum(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
else if (attribute == "minLength")
checkMinLength(value, attributeValue);
checkMinLength(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
else if (attribute == "maxLength")
checkMaxLength(value, attributeValue);
checkMaxLength(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
else if (attribute == "items")
{
if (value.isArray())
@ -101,13 +125,13 @@ void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &s
}
}
else if (attribute == "minItems")
checkMinItems(value, attributeValue);
checkMinItems(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
else if (attribute == "maxItems")
checkMaxItems(value, attributeValue);
checkMaxItems(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
else if (attribute == "uniqueItems")
checkUniqueItems(value, attributeValue);
else if (attribute == "enum")
checkEnum(value, attributeValue);
checkEnum(value, attributeValue, (defaultValue != schema.end() ? defaultValue.value() : QJsonValue::Null));
else if (attribute == "required")
; // nothing to do. value is present so always oke
else if (attribute == "id")
@ -118,7 +142,7 @@ void QJsonSchemaChecker::validate(const QJsonValue & value, const QJsonObject &s
else
{
// no check function defined for this attribute
_error = true;
_schemaError = true;
setMessage("No check function defined for attribute " + attribute);
continue;
}
@ -135,7 +159,7 @@ const QStringList & QJsonSchemaChecker::getMessages() const
return _messages;
}
void QJsonSchemaChecker::checkType(const QJsonValue & value, const QJsonValue & schema)
void QJsonSchemaChecker::checkType(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue)
{
QString type = schema.toString();
@ -145,7 +169,12 @@ void QJsonSchemaChecker::checkType(const QJsonValue & value, const QJsonValue &
else if (type == "number")
wrongType = !value.isDouble();
else if (type == "integer")
wrongType = (rint(value.toDouble()) != value.toDouble());
{
if (value.isDouble()) //check if value type not boolean (true = 1 && false = 0)
wrongType = (rint(value.toDouble()) != value.toDouble());
else
wrongType = true;
}
else if (type == "double")
wrongType = !value.isDouble();
else if (type == "boolean")
@ -160,13 +189,16 @@ void QJsonSchemaChecker::checkType(const QJsonValue & value, const QJsonValue &
wrongType = !value.isString();
else if (type == "any")
wrongType = false;
// else
// assert(false);
if (wrongType)
{
_error = true;
setMessage(type + " expected");
if (_correct == "modify")
QJsonUtils::modify(_autoCorrected, _currentPath, defaultValue);
if (_correct == "")
setMessage(type + " expected");
}
}
@ -176,7 +208,7 @@ void QJsonSchemaChecker::checkProperties(const QJsonObject & value, const QJsonO
{
QString property = i.key();
const QJsonValue & propertyValue = i.value();
const QJsonValue & propertyValue = *i;
_currentPath.append("." + property);
QJsonObject::const_iterator required = propertyValue.toObject().find("required");
@ -185,11 +217,19 @@ void QJsonSchemaChecker::checkProperties(const QJsonObject & value, const QJsonO
{
validate(value[property], propertyValue.toObject());
}
else if (required != propertyValue.toObject().end() && required.value().toBool() && !_ignoreRequired)
else if (required != propertyValue.toObject().end() && propertyValue.toObject().find("required").value().toBool() && !_ignoreRequired)
{
_error = true;
setMessage("missing member");
if (_correct == "create")
QJsonUtils::modify(_autoCorrected, _currentPath, QJsonUtils::create(propertyValue, _ignoreRequired), property);
if (_correct == "")
setMessage("missing member");
}
else if (_correct == "create" && _ignoreRequired)
QJsonUtils::modify(_autoCorrected, _currentPath, QJsonUtils::create(propertyValue, _ignoreRequired), property);
_currentPath.removeLast();
}
}
@ -208,7 +248,12 @@ void QJsonSchemaChecker::checkAdditionalProperties(const QJsonObject & value, co
if (schema.toBool() == false)
{
_error = true;
setMessage("no schema definition");
if (_correct == "remove")
QJsonUtils::modify(_autoCorrected, _currentPath);
if (_correct == "")
setMessage("no schema definition");
}
}
else
@ -220,7 +265,7 @@ void QJsonSchemaChecker::checkAdditionalProperties(const QJsonObject & value, co
}
}
void QJsonSchemaChecker::checkMinimum(const QJsonValue & value, const QJsonValue & schema)
void QJsonSchemaChecker::checkMinimum(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue)
{
if (!value.isDouble())
{
@ -233,11 +278,18 @@ void QJsonSchemaChecker::checkMinimum(const QJsonValue & value, const QJsonValue
if (value.toDouble() < schema.toDouble())
{
_error = true;
setMessage("value is too small (minimum=" + schema.toString() + ")");
if (_correct == "modify")
(defaultValue != QJsonValue::Null) ?
QJsonUtils::modify(_autoCorrected, _currentPath, defaultValue) :
QJsonUtils::modify(_autoCorrected, _currentPath, schema);
if (_correct == "")
setMessage("value is too small (minimum=" + QString::number(schema.toDouble()) + ")");
}
}
void QJsonSchemaChecker::checkMaximum(const QJsonValue & value, const QJsonValue & schema)
void QJsonSchemaChecker::checkMaximum(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue)
{
if (!value.isDouble())
{
@ -250,11 +302,18 @@ void QJsonSchemaChecker::checkMaximum(const QJsonValue & value, const QJsonValue
if (value.toDouble() > schema.toDouble())
{
_error = true;
setMessage("value is too large (maximum=" + schema.toString() + ")");
if (_correct == "modify")
(defaultValue != QJsonValue::Null) ?
QJsonUtils::modify(_autoCorrected, _currentPath, defaultValue) :
QJsonUtils::modify(_autoCorrected, _currentPath, schema);
if (_correct == "")
setMessage("value is too large (maximum=" + QString::number(schema.toDouble()) + ")");
}
}
void QJsonSchemaChecker::checkMinLength(const QJsonValue & value, const QJsonValue & schema)
void QJsonSchemaChecker::checkMinLength(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue)
{
if (!value.isString())
{
@ -267,11 +326,18 @@ void QJsonSchemaChecker::checkMinLength(const QJsonValue & value, const QJsonVal
if (value.toString().size() < schema.toInt())
{
_error = true;
setMessage("value is too short (minLength=" + schema.toString() + ")");
if (_correct == "modify")
(defaultValue != QJsonValue::Null) ?
QJsonUtils::modify(_autoCorrected, _currentPath, defaultValue) :
QJsonUtils::modify(_autoCorrected, _currentPath, schema);
if (_correct == "")
setMessage("value is too short (minLength=" + QString::number(schema.toInt()) + ")");
}
}
void QJsonSchemaChecker::checkMaxLength(const QJsonValue & value, const QJsonValue & schema)
void QJsonSchemaChecker::checkMaxLength(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue)
{
if (!value.isString())
{
@ -284,7 +350,14 @@ void QJsonSchemaChecker::checkMaxLength(const QJsonValue & value, const QJsonVal
if (value.toString().size() > schema.toInt())
{
_error = true;
setMessage("value is too long (maxLength=" + schema.toString() + ")");
if (_correct == "modify")
(defaultValue != QJsonValue::Null) ?
QJsonUtils::modify(_autoCorrected, _currentPath, defaultValue) :
QJsonUtils::modify(_autoCorrected, _currentPath, schema);
if (_correct == "")
setMessage("value is too long (maxLength=" + QString::number(schema.toInt()) + ")");
}
}
@ -299,6 +372,11 @@ void QJsonSchemaChecker::checkItems(const QJsonValue & value, const QJsonObject
}
QJsonArray jArray = value.toArray();
if (_correct == "remove")
if (jArray.isEmpty())
QJsonUtils::modify(_autoCorrected, _currentPath);
for(int i = 0; i < jArray.size(); ++i)
{
// validate each item
@ -308,7 +386,7 @@ void QJsonSchemaChecker::checkItems(const QJsonValue & value, const QJsonObject
}
}
void QJsonSchemaChecker::checkMinItems(const QJsonValue & value, const QJsonValue & schema)
void QJsonSchemaChecker::checkMinItems(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue)
{
if (!value.isArray())
{
@ -318,17 +396,22 @@ void QJsonSchemaChecker::checkMinItems(const QJsonValue & value, const QJsonValu
return;
}
int minimum = schema.toInt();
QJsonArray jArray = value.toArray();
if (static_cast<int>(jArray.size()) < minimum)
if (jArray.size() < schema.toInt())
{
_error = true;
setMessage("array is too large (minimum=" + QString::number(minimum) + ")");
if (_correct == "modify")
(defaultValue != QJsonValue::Null) ?
QJsonUtils::modify(_autoCorrected, _currentPath, defaultValue) :
QJsonUtils::modify(_autoCorrected, _currentPath, schema);
if (_correct == "")
setMessage("array is too small (minimum=" + QString::number(schema.toInt()) + ")");
}
}
void QJsonSchemaChecker::checkMaxItems(const QJsonValue & value, const QJsonValue & schema)
void QJsonSchemaChecker::checkMaxItems(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue)
{
if (!value.isArray())
{
@ -338,13 +421,18 @@ void QJsonSchemaChecker::checkMaxItems(const QJsonValue & value, const QJsonValu
return;
}
int maximum = schema.toInt();
QJsonArray jArray = value.toArray();
if (static_cast<int>(jArray.size()) > maximum)
if (jArray.size() > schema.toInt())
{
_error = true;
setMessage("array is too large (maximum=" + QString::number(maximum) + ")");
if (_correct == "modify")
(defaultValue != QJsonValue::Null) ?
QJsonUtils::modify(_autoCorrected, _currentPath, defaultValue) :
QJsonUtils::modify(_autoCorrected, _currentPath, schema);
if (_correct == "")
setMessage("array is too large (maximum=" + QString::number(schema.toInt()) + ")");
}
}
@ -362,6 +450,8 @@ void QJsonSchemaChecker::checkUniqueItems(const QJsonValue & value, const QJsonV
{
// make sure no two items are identical
bool removeDuplicates = false;
QJsonArray jArray = value.toArray();
for(int i = 0; i < jArray.size(); ++i)
{
@ -371,14 +461,28 @@ void QJsonSchemaChecker::checkUniqueItems(const QJsonValue & value, const QJsonV
{
// found a value twice
_error = true;
setMessage("array must have unique values");
removeDuplicates = true;
if (_correct == "")
setMessage("array must have unique values");
}
}
}
if (removeDuplicates && _correct == "modify")
{
QJsonArray uniqueItemsArray;
for(int i = 0; i < jArray.size(); ++i)
if (!uniqueItemsArray.contains(jArray[i]))
uniqueItemsArray.append(jArray[i]);
QJsonUtils::modify(_autoCorrected, _currentPath, uniqueItemsArray);
}
}
}
void QJsonSchemaChecker::checkEnum(const QJsonValue & value, const QJsonValue & schema)
void QJsonSchemaChecker::checkEnum(const QJsonValue & value, const QJsonValue & schema, const QJsonValue & defaultValue)
{
if (schema.isArray())
{
@ -395,7 +499,16 @@ void QJsonSchemaChecker::checkEnum(const QJsonValue & value, const QJsonValue &
// nothing found
_error = true;
QJsonDocument doc(schema.toArray());
QString strJson(doc.toJson(QJsonDocument::Compact));
setMessage("Unknown enum value (allowed values are: " + schema.toString() + strJson+ ")");
if (_correct == "modify")
(defaultValue != QJsonValue::Null) ?
QJsonUtils::modify(_autoCorrected, _currentPath, defaultValue) :
QJsonUtils::modify(_autoCorrected, _currentPath, schema.toArray().first());
if (_correct == "")
{
QJsonDocument doc(schema.toArray());
QString strJson(doc.toJson(QJsonDocument::Compact));
setMessage("Unknown enum value (allowed values are: " + strJson+ ")");
}
}

View File

@ -105,14 +105,43 @@ void CgiHandler::cmd_cfg_set()
{
// make sure the resources are loaded (they may be left out after static linking)
Q_INIT_RESOURCE(resource);
QJsonObject schemaJson = QJsonFactory::readSchema(":/hyperion-schema-"+QString::number(_hyperion->getConfigVersionId()));
QString schemaFile = ":/hyperion-schema";
QJsonObject schemaJson;
try
{
schemaJson = QJsonFactory::readSchema(schemaFile);
}
catch(const std::runtime_error& error)
{
throw std::runtime_error(error.what());
}
QJsonSchemaChecker schemaChecker;
schemaChecker.setSchema(schemaJson);
if ( schemaChecker.validate(hyperionConfigJsonObj) )
QPair<bool, bool> validate = schemaChecker.validate(hyperionConfigJsonObj);
if (validate.first && validate.second)
{
QJsonFactory::writeJson(_hyperion->getConfigFileName(), hyperionConfigJsonObj);
}
else
else if (!validate.first && validate.second)
{
Warning(_log,"Errors have been found in the configuration file. Automatic correction is applied");
QStringList schemaErrors = schemaChecker.getMessages();
foreach (auto & schemaError, schemaErrors)
Info(_log, schemaError.toUtf8().constData());
hyperionConfigJsonObj = schemaChecker.getAutoCorrectedConfig(hyperionConfigJsonObj);
if (!QJsonFactory::writeJson(_hyperion->getConfigFileName(), hyperionConfigJsonObj))
throw std::runtime_error("ERROR: can not save configuration file, aborting ");
}
else //Error in Schema
{
QString errorMsg = "ERROR: Json validation failed: \n";
QStringList schemaErrors = schemaChecker.getMessages();
@ -121,6 +150,7 @@ void CgiHandler::cmd_cfg_set()
Error(_log, "config write validation: %s", QSTRING_CSTR(schemaError));
errorMsg += schemaError + "\n";
}
throw std::runtime_error(errorMsg.toStdString());
}
}

View File

@ -8,10 +8,6 @@ QT5_WRAP_CPP(Hyperiond_HEADERS_MOC ${Hyperiond_QT_HEADERS})
add_executable(hyperiond
${Hyperiond_QT_HEADERS}
${Hyperiond_HEADERS_MOC}
configMigratorBase.cpp
configMigratorBase.h
configMigrator.cpp
configMigrator.h
hyperiond.cpp
main.cpp
)

View File

@ -1,34 +0,0 @@
#include "configMigrator.h"
ConfigMigrator::ConfigMigrator()
{
}
ConfigMigrator::~ConfigMigrator()
{
}
bool ConfigMigrator::migrate(QString configFile, int fromVersion,int toVersion)
{
Debug(_log, "migrate config %s from version %d to %d.", configFile.toLocal8Bit().constData(), fromVersion, toVersion);
for (int v=fromVersion; v<toVersion; v++)
{
switch(v)
{
case 1: migrateFrom1(); break;
default:
throw std::runtime_error("ERROR: config migration - unknown version");
}
}
return true;
}
void ConfigMigrator::migrateFrom1()
{
throw std::runtime_error("ERROR: config migration not implemented");
}

View File

@ -1,21 +0,0 @@
#pragma once
#include "configMigratorBase.h"
#include <QString>
///
/// class that contains migration code
/// helper code goeas to base class
class ConfigMigrator : public ConfigMigratorBase
{
public:
ConfigMigrator();
~ConfigMigrator();
bool migrate(QString configFile, int fromVersion,int toVersion);
private:
void migrateFrom1();
};

View File

@ -1,11 +0,0 @@
#include "configMigratorBase.h"
ConfigMigratorBase::ConfigMigratorBase()
: _log(Logger::getInstance("ConfigMigrator"))
{
}
ConfigMigratorBase::~ConfigMigratorBase()
{
}

View File

@ -1,16 +0,0 @@
#pragma once
#include <utils/Logger.h>
#include <QString>
class ConfigMigratorBase
{
public:
ConfigMigratorBase();
~ConfigMigratorBase();
protected:
Logger * _log;
};

View File

@ -12,6 +12,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QPair>
#include <cstdint>
#include <limits>
@ -31,7 +32,6 @@
#include <udplistener/UDPListener.h>
#include "hyperiond.h"
#include "configMigrator.h"
HyperionDaemon::HyperionDaemon(QString configFile, QObject *parent)
: QObject(parent)
@ -52,7 +52,7 @@ HyperionDaemon::HyperionDaemon(QString configFile, QObject *parent)
, _hyperion(nullptr)
, _stats(nullptr)
{
loadConfig(configFile, CURRENT_CONFIG_VERSION );
loadConfig(configFile);
if (Logger::getLogLevel() == Logger::WARNING)
{
@ -135,16 +135,16 @@ void HyperionDaemon::run()
connect(_hyperion,SIGNAL(closing()),this,SLOT(freeObjects()));
}
int HyperionDaemon::tryLoadConfig(const QString & configFile, const int schemaVersion)
void HyperionDaemon::loadConfig(const QString & configFile)
{
Info(_log, "Selected configuration file: %s", configFile.toUtf8().constData());
// make sure the resources are loaded (they may be left out after static linking)
Q_INIT_RESOURCE(resource);
// read the json schema from the resource
QString schemaFile = ":/hyperion-schema";
if (schemaVersion > 0)
schemaFile += "-" + QString::number(schemaVersion);
QJsonObject schemaJson;
try
{
@ -159,45 +159,25 @@ int HyperionDaemon::tryLoadConfig(const QString & configFile, const int schemaVe
schemaChecker.setSchema(schemaJson);
_qconfig = QJsonFactory::readConfig(configFile);
if (!schemaChecker.validate(_qconfig))
QPair<bool, bool> validate = schemaChecker.validate(_qconfig);
if (!validate.first && validate.second)
{
Warning(_log,"Errors have been found in the configuration file. Automatic correction is applied");
_qconfig = schemaChecker.getAutoCorrectedConfig(_qconfig);
if (!QJsonFactory::writeJson(configFile, _qconfig))
throw std::runtime_error("ERROR: can not save configuration file, aborting ");
}
else if (validate.first && !validate.second) //Error in Schema
{
QStringList schemaErrors = schemaChecker.getMessages();
foreach (auto & schemaError, schemaErrors)
{
std::cout << schemaError.toStdString() << std::endl;
}
throw std::runtime_error("ERROR: Json validation failed");
}
const QJsonObject & generalConfig = _qconfig["general"].toObject();
return generalConfig["configVersion"].toInt(-1);
}
void HyperionDaemon::loadConfig(const QString & configFile, const int neededConfigVersion)
{
Info(_log, "Selected configuration file: %s", configFile.toUtf8().constData());
int configVersionId = tryLoadConfig(configFile,0);
// no config id found, assume legacy hyperion
if (configVersionId < 0)
{
Debug(_log, "config file has no version, assume old hyperion.");
configVersionId = tryLoadConfig(configFile,1);
}
Debug(_log, "config version: %d", configVersionId);
configVersionId = tryLoadConfig(configFile, configVersionId);
if (neededConfigVersion == configVersionId)
{
return;
}
// migrate configVersionId
ConfigMigrator migrator;
migrator.migrate(configFile, configVersionId, neededConfigVersion);
}

View File

@ -54,9 +54,8 @@ class HyperionDaemon : public QObject
public:
HyperionDaemon(QString configFile, QObject *parent=nullptr);
~HyperionDaemon();
int tryLoadConfig(const QString & configFile, const int schemaVersion);
void loadConfig(const QString & configFile, const int neededConfigVersion);
void loadConfig(const QString & configFile);
void run();
void startInitialEffect();

View File

@ -1,9 +1,6 @@
// STL includes
#include <cstdlib>
// QT includes
#include <QResource>
#include <QDebug>
// JsonSchema includes
#include <utils/jsonschema/QJsonFactory.h>
@ -12,7 +9,7 @@
#include <hyperion/LedString.h>
#include "HyperionConfig.h"
bool loadConfig(const QString & configFile)
bool loadConfig(const QString & configFile, bool correct, bool ignore)
{
// make sure the resources are loaded (they may be left out after static linking)
Q_INIT_RESOURCE(resource);
@ -39,46 +36,75 @@ bool loadConfig(const QString & configFile)
// read and validate the configuration file from the command line
////////////////////////////////////////////////////////////
const QJsonObject jsonConfig = QJsonFactory::readConfig(configFile);
if (!schemaChecker.validate(jsonConfig))
QJsonObject jsonConfig = QJsonFactory::readConfig(configFile);
if (!correct)
{
QStringList schemaErrors = schemaChecker.getMessages();
foreach (auto & schemaError, schemaErrors)
if (!schemaChecker.validate(jsonConfig).first)
{
std::cout << "config write validation: " << schemaError.toStdString() << std::endl;
QStringList schemaErrors = schemaChecker.getMessages();
foreach (auto & schemaError, schemaErrors)
{
qDebug() << "config write validation: " << schemaError;
}
qDebug() << "FAILED";
exit(1);
return false;
}
std::cout << "FAILED" << std::endl;
exit(1);
return false;
}
else
{
jsonConfig = schemaChecker.getAutoCorrectedConfig(jsonConfig, ignore); // The second parameter is to ignore the "required" keyword in hyperion schema
QJsonFactory::writeJson(configFile, jsonConfig);
}
return true;
}
void usage()
{
qDebug() << "Missing required configuration file to test";
qDebug() << "Usage: test_configfile <option> [configfile]";
qDebug() << "<option>:";
qDebug() << "\t--ac - for json auto correction";
qDebug() << "\t--ac-ignore-required - for json auto correction without paying attention 'required' keyword in hyperion schema";
}
int main(int argc, char** argv)
{
if (argc != 2)
if (argc < 2)
{
std::cerr << "Missing required configuration file to test" << std::endl;
std::cerr << "Usage: test_configfile [configfile]" << std::endl;
usage();
return 0;
}
const QString configFile(argv[1]);
std::cout << "Configuration file selected: " << configFile.toStdString() << std::endl;
std::cout << "Attemp to load..." << std::endl;
QString option = argv[1];
QString configFile;
if (option == "--ac" || option == "--ac-ignore-required")
if (argc > 2)
configFile = argv[2];
else
{
usage();
return 0;
}
else
configFile = argv[1];
qDebug() << "Configuration file selected: " << configFile;
qDebug() << "Attemp to load...";
try
{
if (loadConfig(configFile))
std::cout << "PASSED" << std::endl;
if (loadConfig(configFile, (option == "--ac" || option == "--ac-ignore-required"), option == "--ac-ignore-required"))
qDebug() << "PASSED";
return 0;
}
catch (std::runtime_error exception)
{
std::cout << "FAILED" << std::endl;
std::cout << exception.what() << std::endl;
qDebug() << "FAILED";
qDebug() << exception.what();
}
return 1;

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python
import json, sys, glob
from os import path
from jsonschema import Draft3Validator
from jsonschema import Draft3Validator, RefResolver
print('-- validate json file')
@ -11,9 +11,8 @@ schemaFileName = sys.argv[2]
try:
with open(schemaFileName) as schemaFile:
with open(jsonFileName) as jsonFile:
j = json.loads(jsonFile.read())
validator = Draft3Validator(json.loads(schemaFile.read()))
validator.validate(j)
resolver = RefResolver('file://%s/schema/' % path.abspath(path.dirname(schemaFileName)), None)
Draft3Validator(json.loads(schemaFile.read()), resolver=resolver).validate(json.loads(jsonFile.read()))
except Exception as e:
print('validation error: '+jsonFileName + ' '+schemaFileName+' ('+str(e)+')')
sys.exit(1)

View File

@ -4,46 +4,71 @@ An implementation of JSON Schema for Python
The main functionality is provided by the validator classes for each of the
supported JSON Schema versions.
Most commonly, the :function:`validate` function is the quickest way to simply
validate a given instance under a schema, and will create a validator for you.
Most commonly, :func:`validate` is the quickest way to simply validate a given
instance under a schema, and will create a validator for you.
"""
from __future__ import division, unicode_literals
import collections
import json
import itertools
import operator
import re
import sys
import warnings
__version__ = "0.7"
__version__ = "0.8.0"
FLOAT_TOLERANCE = 10 ** -15
PY3 = sys.version_info[0] >= 3
if PY3:
from urllib import parse as urlparse
from urllib.parse import unquote
from urllib.request import urlopen
basestring = unicode = str
iteritems = operator.methodcaller("items")
from urllib.parse import unquote
else:
from itertools import izip as zip
iteritems = operator.methodcaller("iteritems")
from urllib import unquote
from urllib2 import urlopen
import urlparse
iteritems = operator.methodcaller("iteritems")
FLOAT_TOLERANCE = 10 ** -15
validators = {}
def validates(version):
"""
Register the decorated validator for a ``version`` of the specification.
Registered validators and their meta schemas will be considered when
parsing ``$schema`` properties' URIs.
:argument str version: an identifier to use as the version's name
:returns: a class decorator to decorate the validator with the version
"""
def _validates(cls):
validators[version] = cls
return cls
return _validates
class UnknownType(Exception):
"""
An unknown type was given.
An attempt was made to check if an instance was of an unknown type.
"""
class InvalidRef(Exception):
class RefResolutionError(Exception):
"""
An invalid reference was given.
A JSON reference failed to resolve.
"""
@ -52,21 +77,23 @@ class SchemaError(Exception):
"""
The provided schema is malformed.
The same attributes exist for ``SchemaError``s as for ``ValidationError``s.
The same attributes are present as for :exc:`ValidationError`\s.
"""
validator = None
def __init__(self, message):
super(SchemaError, self).__init__(message)
def __init__(self, message, validator=None, path=()):
super(SchemaError, self).__init__(message, validator, path)
self.message = message
self.path = []
self.path = list(path)
self.validator = validator
def __str__(self):
return self.message
class ValidationError(Exception):
"""
The instance didn't properly validate with the provided schema.
The instance didn't properly validate under the provided schema.
Relevant attributes are:
* ``message`` : a human readable message explaining the error
@ -76,19 +103,20 @@ class ValidationError(Exception):
"""
# the failing validator will be set externally at whatever recursion level
# is immediately above the validation failure
validator = None
def __init__(self, message):
super(ValidationError, self).__init__(message)
def __init__(self, message, validator=None, path=()):
# Any validator that recurses (e.g. properties and items) must append
# to the ValidationError's path to properly maintain where in the
# instance the error occurred
super(ValidationError, self).__init__(message, validator, path)
self.message = message
self.path = list(path)
self.validator = validator
# Any validator that recurses must append to the ValidationError's
# path (e.g., properties and items)
self.path = []
def __str__(self):
return self.message
@validates("draft3")
class Draft3Validator(object):
"""
A validator for JSON Schema draft 3.
@ -100,37 +128,20 @@ class Draft3Validator(object):
"number" : (int, float), "object" : dict, "string" : basestring,
}
def __init__(self, schema, types=()):
"""
Initialize a validator.
``schema`` should be a *valid* JSON Schema object already converted to
a native Python object (typically a dict via ``json.load``).
``types`` is a mapping (or iterable of 2-tuples) containing additional
types or alternate types to verify via the 'type' property. For
instance, the default types for the 'number' JSON Schema type are
``int`` and ``float``. To override this behavior (e.g. for also
allowing ``decimal.Decimal``), pass ``types={"number" : (int, float,
decimal.Decimal)} *including* the default types if so desired, which
are fairly obvious but can be accessed via the ``DEFAULT_TYPES``
attribute on this class if necessary.
"""
def __init__(self, schema, types=(), resolver=None):
self._types = dict(self.DEFAULT_TYPES)
self._types.update(types)
self._types["any"] = tuple(self._types.values())
if resolver is None:
resolver = RefResolver.from_schema(schema)
self.resolver = resolver
self.schema = schema
def is_type(self, instance, type):
"""
Check if an ``instance`` is of the provided (JSON Schema) ``type``.
"""
if type not in self._types:
if type == "any":
return True
elif type not in self._types:
raise UnknownType(type)
type = self._types[type]
@ -142,36 +153,17 @@ class Draft3Validator(object):
return isinstance(instance, type)
def is_valid(self, instance, _schema=None):
"""
Check if the ``instance`` is valid under the current schema.
Returns a bool indicating whether validation succeeded.
"""
error = next(self.iter_errors(instance, _schema), None)
return error is None
@classmethod
def check_schema(cls, schema):
"""
Validate a ``schema`` against the meta-schema to see if it is valid.
"""
for error in cls(cls.META_SCHEMA).iter_errors(schema):
s = SchemaError(error.message)
s.path = error.path
s.validator = error.validator
# I think we're safer raising these always, not yielding them
raise s
raise SchemaError(
error.message, validator=error.validator, path=error.path,
)
def iter_errors(self, instance, _schema=None):
"""
Lazily yield each of the errors in the given ``instance``.
"""
if _schema is None:
_schema = self.schema
@ -183,17 +175,12 @@ class Draft3Validator(object):
errors = validator(v, instance, _schema) or ()
for error in errors:
# if the validator hasn't already been set (due to recursion)
# make sure to set it
error.validator = error.validator or k
# set the validator if it wasn't already set by the called fn
if error.validator is None:
error.validator = k
yield error
def validate(self, *args, **kwargs):
"""
Validate an ``instance`` under the given ``schema``.
"""
for error in self.iter_errors(*args, **kwargs):
raise error
@ -201,21 +188,12 @@ class Draft3Validator(object):
types = _list(types)
for type in types:
# Ouch. Brain hurts. Two paths here, either we have a schema, then
# check if the instance is valid under it
if ((
self.is_type(type, "object") and
self.is_valid(instance, type)
# Or we have a type as a string, just check if the instance is that
# type. Also, HACK: we can reach the `or` here if skip_types is
# something other than error. If so, bail out.
) or (
self.is_type(type, "string") and
(self.is_type(instance, type) or type not in self._types)
)):
return
if self.is_type(type, "object"):
if self.is_valid(instance, type):
return
elif self.is_type(type, "string"):
if self.is_type(instance, type):
return
else:
yield ValidationError(_types_msg(instance, types))
@ -229,14 +207,16 @@ class Draft3Validator(object):
error.path.append(property)
yield error
elif subschema.get("required", False):
error = ValidationError(
"%r is a required property" % (property,)
yield ValidationError(
"%r is a required property" % (property,),
validator="required",
path=[property],
)
error.path.append(property)
error.validator = "required"
yield error
def validate_patternProperties(self, patternProperties, instance, schema):
if not self.is_type(instance, "object"):
return
for pattern, subschema in iteritems(patternProperties):
for k, v in iteritems(instance):
if re.match(pattern, k):
@ -292,9 +272,10 @@ class Draft3Validator(object):
yield error
def validate_additionalItems(self, aI, instance, schema):
if not self.is_type(instance, "array"):
return
if not self.is_type(schema.get("items"), "array"):
if (
not self.is_type(instance, "array") or
not self.is_type(schema.get("items"), "array")
):
return
if self.is_type(aI, "object"):
@ -397,11 +378,7 @@ class Draft3Validator(object):
yield error
def validate_ref(self, ref, instance, schema):
if ref != "#" and not ref.startswith("#/"):
warnings.warn("jsonschema only supports json-pointer $refs")
return
resolved = resolve_json_pointer(self.schema, ref)
resolved = self.resolver.resolve(ref)
for error in self.iter_errors(instance, resolved):
yield error
@ -490,22 +467,89 @@ Draft3Validator.META_SCHEMA = {
}
class Validator(Draft3Validator):
class RefResolver(object):
"""
Deprecated: Use :class:`Draft3Validator` instead.
Resolve JSON References.
:argument str base_uri: URI of the referring document
:argument referrer: the actual referring document
:argument dict store: a mapping from URIs to documents to cache
"""
def __init__(
self, version=None, unknown_type="skip", unknown_property="skip",
*args, **kwargs
):
super(Validator, self).__init__({}, *args, **kwargs)
warnings.warn(
"Validator is deprecated and will be removed. "
"Use Draft3Validator instead.",
DeprecationWarning, stacklevel=2,
)
def __init__(self, base_uri, referrer, store=()):
self.base_uri = base_uri
self.referrer = referrer
self.store = dict(store, **_meta_schemas())
@classmethod
def from_schema(cls, schema, *args, **kwargs):
"""
Construct a resolver from a JSON schema object.
:argument schema schema: the referring schema
:rtype: :class:`RefResolver`
"""
return cls(schema.get("id", ""), schema, *args, **kwargs)
def resolve(self, ref):
"""
Resolve a JSON ``ref``.
:argument str ref: reference to resolve
:returns: the referrant document
"""
base_uri = self.base_uri
uri, fragment = urlparse.urldefrag(urlparse.urljoin(base_uri, ref))
if uri in self.store:
document = self.store[uri]
elif not uri or uri == self.base_uri:
document = self.referrer
else:
document = self.resolve_remote(uri)
return self.resolve_fragment(document, fragment.lstrip("/"))
def resolve_fragment(self, document, fragment):
"""
Resolve a ``fragment`` within the referenced ``document``.
:argument document: the referrant document
:argument str fragment: a URI fragment to resolve within it
"""
parts = unquote(fragment).split("/") if fragment else []
for part in parts:
part = part.replace("~1", "/").replace("~0", "~")
if part not in document:
raise RefResolutionError(
"Unresolvable JSON pointer: %r" % fragment
)
document = document[part]
return document
def resolve_remote(self, uri):
"""
Resolve a remote ``uri``.
Does not check the store first.
:argument str uri: the URI to resolve
:returns: the retrieved document
"""
return json.load(urlopen(uri))
class ErrorTree(object):
@ -528,6 +572,11 @@ class ErrorTree(object):
return k in self._contents
def __getitem__(self, k):
"""
Retrieve the child tree with key ``k``.
"""
return self._contents[k]
def __setitem__(self, k, v):
@ -537,36 +586,30 @@ class ErrorTree(object):
return iter(self._contents)
def __len__(self):
return self.total_errors
def __repr__(self):
return "<%s (%s total errors)>" % (self.__class__.__name__, len(self))
@property
def total_errors(self):
"""
The total number of errors in the entire tree, including children.
"""
child_errors = sum(len(tree) for _, tree in iteritems(self._contents))
return len(self.errors) + child_errors
def __repr__(self):
return "<%s (%s errors)>" % (self.__class__.__name__, len(self))
def resolve_json_pointer(schema, ref):
def _meta_schemas():
"""
Resolve a local reference ``ref`` within the given root ``schema``.
``ref`` should be a local ref whose ``#`` is still present.
Collect the urls and meta schemas from each known validator.
"""
if ref == "#":
return schema
parts = ref.lstrip("#/").split("/")
parts = map(unquote, parts)
parts = [part.replace('~1', '/').replace('~0', '~') for part in parts]
try:
for part in parts:
schema = schema[part]
except KeyError:
raise InvalidRef("Unresolvable json-pointer %r" % ref)
else:
return schema
meta_schemas = (v.META_SCHEMA for v in validators.values())
return dict((urlparse.urldefrag(m["id"])[0], m) for m in meta_schemas)
def _find_additional_properties(instance, schema):
@ -675,6 +718,19 @@ def _delist(thing):
return thing
def _unbool(element, true=object(), false=object()):
"""
A hack to make True and 1 and False and 0 unique for _uniq.
"""
if element is True:
return true
elif element is False:
return false
return element
def _uniq(container):
"""
Check if all of a container's elements are unique.
@ -686,17 +742,18 @@ def _uniq(container):
"""
try:
return len(set(container)) == len(container)
return len(set(_unbool(i) for i in container)) == len(container)
except TypeError:
try:
sort = sorted(container)
sliced = itertools.islice(container, 1, None)
for i, j in zip(container, sliced):
sort = sorted(_unbool(i) for i in container)
sliced = itertools.islice(sort, 1, None)
for i, j in zip(sort, sliced):
if i == j:
return False
except (NotImplementedError, TypeError):
seen = []
for e in container:
e = _unbool(e)
if e in seen:
return False
seen.append(e)
@ -707,28 +764,29 @@ def validate(instance, schema, cls=Draft3Validator, *args, **kwargs):
"""
Validate an ``instance`` under the given ``schema``.
First verifies that the provided schema is itself valid, since not doing so
can lead to less obvious failures when validating. If you know it is or
don't care, use ``YourValidator(schema).validate(instance)`` directly
instead (e.g. ``Draft3Validator``).
>>> validate([2, 3, 4], {"maxItems" : 2})
Traceback (most recent call last):
...
ValidationError: [2, 3, 4] is too long
:func:`validate` will first verify that the provided schema is itself
valid, since not doing so can lead to less obvious error messages and fail
in less obvious or consistent ways. If you know you have a valid schema
already or don't care, you might prefer using the ``validate`` method
directly on a specific validator (e.g. :meth:`Draft3Validator.validate`).
``cls`` is a validator class that will be used to validate the instance.
By default this is a draft 3 validator. Any other provided positional and
keyword arguments will be provided to this class when constructing a
validator.
:raises:
:exc:`ValidationError` if the instance is invalid
:exc:`SchemaError` if the schema itself is invalid
"""
meta_validate = kwargs.pop("meta_validate", None)
if meta_validate is not None:
warnings.warn(
"meta_validate is deprecated and will be removed. If you do not "
"want to validate a schema, use Draft3Validator.validate instead.",
DeprecationWarning, stacklevel=2,
)
if meta_validate is not False: # yes this is needed since True was default
cls.check_schema(schema)
cls.check_schema(schema)
cls(schema, *args, **kwargs).validate(instance)