Schema checking for all commands added

This commit is contained in:
johan 2013-08-18 12:02:17 +02:00
parent b66c397a46
commit 46076998a0
12 changed files with 231 additions and 67 deletions

View File

@ -16,20 +16,8 @@
JsonClientConnection::JsonClientConnection(QTcpSocket *socket) : JsonClientConnection::JsonClientConnection(QTcpSocket *socket) :
QObject(), QObject(),
_socket(socket), _socket(socket),
_schemaChecker(),
_receiveBuffer() _receiveBuffer()
{ {
// read the json schema from the resource
QResource schemaData(":/schema.json");
assert(schemaData.isValid());
Json::Reader jsonReader;
Json::Value schemaJson;
if (!jsonReader.parse(reinterpret_cast<const char *>(schemaData.data()), reinterpret_cast<const char *>(schemaData.data()) + schemaData.size(), schemaJson, false))
{
throw std::runtime_error("Schema error: " + jsonReader.getFormattedErrorMessages()) ;
}
_schemaChecker.setSchema(schemaJson);
// connect internal signals and slots // connect internal signals and slots
connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed())); connect(_socket, SIGNAL(disconnected()), this, SLOT(socketClosed()));
connect(_socket, SIGNAL(readyRead()), this, SLOT(readData())); connect(_socket, SIGNAL(readyRead()), this, SLOT(readData()));
@ -64,33 +52,80 @@ void JsonClientConnection::socketClosed()
emit connectionClosed(this); emit connectionClosed(this);
} }
void JsonClientConnection::handleMessage(const std::string &message) void JsonClientConnection::handleMessage(const std::string &messageString)
{ {
Json::Reader reader; Json::Reader reader;
Json::Value messageRoot; Json::Value message;
if (!reader.parse(message, messageRoot, false)) if (!reader.parse(messageString, message, false))
{ {
sendErrorReply("Error while parsing json: " + reader.getFormattedErrorMessages()); sendErrorReply("Error while parsing json: " + reader.getFormattedErrorMessages());
return; return;
} }
if (!_schemaChecker.validate(messageRoot)) // check basic message
std::string errors;
if (!checkJson(message, ":schema", errors))
{ {
const std::list<std::string> & errors = _schemaChecker.getMessages(); sendErrorReply("Error while validating json: " + errors);
std::stringstream ss;
ss << "Error while validating json: {";
foreach (const std::string & error, errors) {
ss << error << ", ";
}
ss << "}";
sendErrorReply(ss.str());
return; return;
} }
handleNotImplemented(messageRoot); // check specific message
const std::string command = message["command"].asString();
if (!checkJson(message, QString("schema-%1").arg(QString::fromStdString(command)), errors))
{
sendErrorReply("Error while validating json: " + errors);
return;
}
// switch over all possible commands and handle them
if (command == "color")
handleColorCommand(message);
else if (command == "image")
handleImageCommand(message);
else if (command == "serverinfo")
handleServerInfoCommand(message);
else if (command == "clear")
handleClearCommand(message);
else if (command == "clearall")
handleClearallCommand(message);
else if (command == "transform")
handleTransformCommand(message);
else
handleNotImplemented();
} }
void JsonClientConnection::handleNotImplemented(const Json::Value & message) void JsonClientConnection::handleColorCommand(const Json::Value &message)
{
handleNotImplemented();
}
void JsonClientConnection::handleImageCommand(const Json::Value &message)
{
handleNotImplemented();
}
void JsonClientConnection::handleServerInfoCommand(const Json::Value &message)
{
handleNotImplemented();
}
void JsonClientConnection::handleClearCommand(const Json::Value &message)
{
handleNotImplemented();
}
void JsonClientConnection::handleClearallCommand(const Json::Value &message)
{
handleNotImplemented();
}
void JsonClientConnection::handleTransformCommand(const Json::Value &message)
{
handleNotImplemented();
}
void JsonClientConnection::handleNotImplemented()
{ {
sendErrorReply("Command not implemented"); sendErrorReply("Command not implemented");
} }
@ -112,3 +147,36 @@ void JsonClientConnection::sendErrorReply(const std::string &error)
// send reply // send reply
sendMessage(reply); sendMessage(reply);
} }
bool JsonClientConnection::checkJson(const Json::Value & message, const QString & schemaResource, std::string & errorMessage)
{
// read the json schema from the resource
QResource schemaData(schemaResource);
assert(schemaData.isValid());
Json::Reader jsonReader;
Json::Value schemaJson;
if (!jsonReader.parse(reinterpret_cast<const char *>(schemaData.data()), reinterpret_cast<const char *>(schemaData.data()) + schemaData.size(), schemaJson, false))
{
throw std::runtime_error("Schema error: " + jsonReader.getFormattedErrorMessages()) ;
}
// create schema checker
JsonSchemaChecker schema;
schema.setSchema(schemaJson);
// check the message
if (!schema.validate(message))
{
const std::list<std::string> & errors = schema.getMessages();
std::stringstream ss;
ss << "{";
foreach (const std::string & error, errors) {
ss << error << " ";
}
ss << "}";
errorMessage = ss.str();
return false;
}
return true;
}

View File

@ -30,15 +30,22 @@ private slots:
private: private:
void handleMessage(const std::string & message); void handleMessage(const std::string & message);
void handleNotImplemented(const Json::Value & message); void handleColorCommand(const Json::Value & message);
void handleImageCommand(const Json::Value & message);
void handleServerInfoCommand(const Json::Value & message);
void handleClearCommand(const Json::Value & message);
void handleClearallCommand(const Json::Value & message);
void handleTransformCommand(const Json::Value & message);
void handleNotImplemented();
void sendMessage(const Json::Value & message); void sendMessage(const Json::Value & message);
void sendErrorReply(const std::string & error); void sendErrorReply(const std::string & error);
private: private:
QTcpSocket * _socket; bool checkJson(const Json::Value & message, const QString &schemaResource, std::string & errors);
JsonSchemaChecker _schemaChecker; private:
QTcpSocket * _socket;
QByteArray _receiveBuffer; QByteArray _receiveBuffer;
}; };

View File

@ -37,11 +37,11 @@ uint16_t JsonServer::getPort() const
void JsonServer::newConnection() void JsonServer::newConnection()
{ {
std::cout << "New incoming json connection" << std::endl;
QTcpSocket * socket = _server.nextPendingConnection(); QTcpSocket * socket = _server.nextPendingConnection();
if (socket != nullptr) if (socket != nullptr)
{ {
std::cout << "New json connection" << std::endl;
JsonClientConnection * connection = new JsonClientConnection(socket); JsonClientConnection * connection = new JsonClientConnection(socket);
_openConnections.insert(connection); _openConnections.insert(connection);

View File

@ -1,5 +1,11 @@
<RCC> <RCC>
<qresource prefix="/"> <qresource prefix="/">
<file>schema.json</file> <file alias="schema">schema/schema.json</file>
</qresource> <file alias="schema-color">schema/schema-color.json</file>
<file alias="schema-image">schema/schema-image.json</file>
<file alias="schema-serverinfo">schema/schema-serverinfo.json</file>
<file alias="schema-clear">schema/schema-clear.json</file>
<file alias="schema-clearall">schema/schema-clearall.json</file>
<file alias="schema-transform">schema/schema-transform.json</file>
</qresource>
</RCC> </RCC>

View File

@ -0,0 +1,16 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["clear"]
},
"priority": {
"type": "integer",
"required": true
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,12 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["clearall"]
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,29 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["color"]
},
"priority": {
"type": "integer",
"required": true
},
"duration": {
"type": "integer",
"required": false
},
"color": {
"type": "array",
"required": true,
"items" :{
"type" : "integer"
},
"minItems": 3,
"maxItems": 3
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,34 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["image"]
},
"priority": {
"type": "integer",
"required": true
},
"duration": {
"type": "integer",
"required": false
},
"imagewidth": {
"type" : "integer",
"required": true,
"minimum": 0
},
"imageheight": {
"type" : "integer",
"required": true,
"minimum": 0
},
"imagedata": {
"type": "string",
"required": true
}
},
"additionalProperties": false
}

View File

@ -0,0 +1,12 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["serverinfo"]
}
},
"additionalProperties": false
}

View File

@ -5,42 +5,11 @@
"command": { "command": {
"type" : "string", "type" : "string",
"required" : true, "required" : true,
"enum" : ["color", "image", "serverinfo", "clear", "clearall", "transform"] "enum" : ["transform"]
},
"priority": {
"type": "integer",
"required": false
},
"duration": {
"type": "integer",
"required": false
},
"color": {
"type": "array",
"required": false,
"items" :{
"type" : "integer"
},
"minItems": 3,
"maxItems": 3
},
"imagewidth": {
"type" : "integer",
"required": false,
"minimum": 0
},
"imageheight": {
"type" : "integer",
"required": false,
"minimum": 0
},
"imagedata": {
"type": "string",
"required": false
}, },
"transform": { "transform": {
"type": "object", "type": "object",
"required": false, "required": true,
"properties": { "properties": {
"threshold": { "threshold": {
"type": "array", "type": "array",

View File

@ -0,0 +1,11 @@
{
"type":"object",
"required":true,
"properties":{
"command": {
"type" : "string",
"required" : true,
"enum" : ["color", "image", "serverinfo", "clear", "clearall", "transform"]
}
}
}

View File

@ -275,7 +275,7 @@ bool JsonConnection::parseReply(const Json::Value &reply)
if (!success) if (!success)
{ {
throw std::runtime_error("Error while executing command: " + reason); throw std::runtime_error("Error: " + reason);
} }
return success; return success;