Permalink
Browse files

Support nested validation errors

  • Loading branch information...
1 parent 3638fd6 commit afcc4a8827fadfcd6916b8ecf808e3262d26caa0 @logandk committed Dec 4, 2013
Showing with 113 additions and 11 deletions.
  1. +72 −10 include/restful_mapper/api.h
  2. +6 −0 include/restful_mapper/json.h
  3. +30 −0 src/json.cpp
  4. +1 −1 tests/mocks/requirements.txt
  5. +4 −0 tests/test_api.cpp
View
82 include/restful_mapper/api.h
@@ -3,6 +3,7 @@
#include <stdexcept>
#include <string>
+#include <algorithm>
#include <map>
#include <cctype>
#include <restful_mapper/json.h>
@@ -230,20 +231,19 @@ class ValidationError : public ApiError
std::string separator = "";
Json::Parser parser(json_struct);
- errors_ = parser.find("validation_errors").to_string_map();
+ std::map<std::string, Json::Node> errors;
+ errors = parser.find("validation_errors").to_map();
- FieldMap::const_iterator i, i_end = errors_.end();
- for (i = errors_.begin(); i != i_end; ++i)
+ std::map<std::string, Json::Node>::const_iterator i, i_end = errors.end();
+ for (i = errors.begin(); i != i_end; ++i)
{
- std::string field_name = i->first;
- std::string field_value = i->second;
+ std::string field_name = i->first;
+
+ errors_[field_name] = parse_errors(i->second, 1, false);
- // Capitalize field name
- field_name[0] = std::toupper(field_name[0]);
what_ += separator;
- what_ += field_name;
- what_ += " ";
- what_ += field_value;
+ what_ += format_field(field_name);
+ what_ += parse_errors(i->second);
separator = "\n";
}
}
@@ -280,6 +280,68 @@ class ValidationError : public ApiError
private:
FieldMap errors_;
std::string what_;
+
+ std::string parse_errors(const Json::Node &node, const unsigned int &indent = 1, const bool &add_space = true)
+ {
+ if (node.is_array())
+ {
+ std::string formatted;
+
+ std::vector<Json::Node> errors = node.to_array();
+
+ std::vector<Json::Node>::const_iterator i, i_end = errors.end();
+ for (i = errors.begin(); i != i_end; ++i)
+ {
+ formatted += parse_errors(*i, indent);
+ }
+
+ return formatted;
+ }
+ else if (node.is_map())
+ {
+ std::string formatted;
+
+ std::map<std::string, Json::Node> errors = node.to_map();
+
+ std::map<std::string, Json::Node>::const_iterator i, i_end = errors.end();
+ for (i = errors.begin(); i != i_end; ++i)
+ {
+ std::string field_name = i->first;
+ std::string field_value = parse_errors(i->second, indent + 1);
+
+ formatted += "\n";
+ for (unsigned int j = 0; j < indent; j++)
+ {
+ formatted += " ";
+ }
+ formatted += format_field(field_name);
+ formatted += field_value;
+ }
+
+ return formatted;
+ }
+ else if (node.is_string())
+ {
+ std::string formatted;
+
+ if (add_space)
+ {
+ formatted += " ";
+ }
+
+ formatted += node.to_string();
+
+ return formatted;
+ }
+ }
+
+ std::string format_field(const std::string &field)
+ {
+ std::string formatted(field);
+ std::replace(formatted.begin(), formatted.end(), '_', ' ');
+ formatted[0] = std::toupper(formatted[0]);
+ return formatted;
+ }
};
}
View
6 include/restful_mapper/json.h
@@ -122,6 +122,12 @@ class Json
std::map<std::string, std::string> dump_map() const;
bool is_null() const;
+ bool is_string() const;
+ bool is_int() const;
+ bool is_double() const;
+ bool is_bool() const;
+ bool is_array() const;
+ bool is_map() const;
std::string to_string() const;
long long to_int() const;
View
30 src/json.cpp
@@ -520,6 +520,36 @@ bool Json::Node::is_null() const
return YAJL_IS_NULL(JSON_TREE_HANDLE);
}
+bool Json::Node::is_string() const
+{
+ return YAJL_IS_STRING(JSON_TREE_HANDLE);
+}
+
+bool Json::Node::is_int() const
+{
+ return YAJL_IS_INTEGER(JSON_TREE_HANDLE);
+}
+
+bool Json::Node::is_double() const
+{
+ return YAJL_IS_DOUBLE(JSON_TREE_HANDLE);
+}
+
+bool Json::Node::is_bool() const
+{
+ return YAJL_IS_BOOLEAN(JSON_TREE_HANDLE);
+}
+
+bool Json::Node::is_array() const
+{
+ return YAJL_IS_ARRAY(JSON_TREE_HANDLE);
+}
+
+bool Json::Node::is_map() const
+{
+ return YAJL_IS_OBJECT(JSON_TREE_HANDLE);
+}
+
string Json::Node::to_string() const
{
if (!YAJL_IS_STRING(JSON_TREE_HANDLE))
View
2 tests/mocks/requirements.txt
@@ -1,2 +1,2 @@
-Flask-Restless==0.10
+Flask-Restless==0.12.1
Flask-SQLAlchemy==0.16
View
4 tests/test_api.cpp
@@ -63,6 +63,10 @@ TEST(ApiTest, ValidationErrorJson)
ValidationError err2("broken json");
ASSERT_STREQ("", err2.what());
+
+ ValidationError err3("{\"validation_errors\":{\"phone_no\":\"must have numbers\","
+ "\"company\":{\"title\":\"cannot be empty\"},\"addresses\":[{\"street\":\"not provided\"}]}}");
+ ASSERT_STREQ("Addresses\n Street not provided\nCompany\n Title cannot be empty\nPhone no must have numbers", err3.what());
}
TEST(ApiTest, ProxyValid)

0 comments on commit afcc4a8

Please sign in to comment.