mirror of
https://github.com/tristanpenman/valijson.git
synced 2024-12-12 10:13:51 +01:00
Add polymorphic constraint, and modifications neccessary for using them
Modify CMakeLists.txt to make third party include files be included by -isystem. This reduces external warnings/errors. Add a unit test for the PolyConstraints. The test doesn't pass, because the right method for inserting a constraint isn't clear to me. Add Authors file for tracking modifications Add Akamai to the License file.
This commit is contained in:
parent
00d0fa8d43
commit
8f446704f7
2
Authors
Normal file
2
Authors
Normal file
@ -0,0 +1,2 @@
|
||||
James Jensen, jjensen@akamai.com, changes Copyright (c) 2016 Akamai Technologies
|
||||
Polymorphic constraint support
|
@ -44,16 +44,18 @@ add_subdirectory(thirdparty/gtest-1.7.0)
|
||||
# Include path
|
||||
include_directories(
|
||||
include
|
||||
)
|
||||
|
||||
include_directories(SYSTEM
|
||||
${Boost_INCLUDE_DIRS}
|
||||
thirdparty/gtest-1.7.0/include
|
||||
thirdparty/jsoncpp-0.9.4/include
|
||||
thirdparty/rapidjson-1.0.2/include
|
||||
thirdparty/picojson-1.3.0
|
||||
thirdparty/nlohmann-json-1.1.0
|
||||
${Boost_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
if(VALIJSON_CXX11_ADAPTERS_ARE_ENABLED)
|
||||
include_directories(
|
||||
include_directories(SYSTEM
|
||||
thirdparty/json11-2016-01-26
|
||||
)
|
||||
endif()
|
||||
@ -78,6 +80,7 @@ set(TEST_SOURCES
|
||||
tests/test_picojson_adapter.cpp
|
||||
tests/test_validation_errors.cpp
|
||||
tests/test_validator.cpp
|
||||
tests/test_poly_constraint.cpp
|
||||
)
|
||||
|
||||
if(VALIJSON_CXX11_ADAPTERS_ARE_ENABLED)
|
||||
|
1
LICENSE
1
LICENSE
@ -1,4 +1,5 @@
|
||||
Copyright (c) 2016, Tristan Penman
|
||||
Copyright (c) 2016, Akamai Technolgies, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <valijson/schema.hpp>
|
||||
|
||||
namespace valijson {
|
||||
class ValidationResults;
|
||||
namespace constraints {
|
||||
|
||||
/**
|
||||
@ -1060,6 +1061,12 @@ public:
|
||||
: BasicConstraint(allocFn, freeFn) { }
|
||||
};
|
||||
|
||||
class PolyConstraint : public BasicConstraint<PolyConstraint> {
|
||||
public:
|
||||
virtual bool validate(const adapters::Adapter &target, const std::vector<std::string>& context, valijson::ValidationResults *results) const {throw std::runtime_error("attempt to validate incomplete"); }
|
||||
virtual PolyConstraint * clone() const {throw std::runtime_error("attempt to validate incomplete"); }
|
||||
};
|
||||
|
||||
} // namespace constraints
|
||||
} // namespace valijson
|
||||
|
||||
|
@ -28,6 +28,7 @@ class RequiredConstraint;
|
||||
class SingularItemsConstraint;
|
||||
class TypeConstraint;
|
||||
class UniqueItemsConstraint;
|
||||
class PolyConstraint;
|
||||
|
||||
/// Interface to allow usage of the visitor pattern with Constraints
|
||||
class ConstraintVisitor
|
||||
@ -58,6 +59,7 @@ protected:
|
||||
typedef constraints::SingularItemsConstraint SingularItemsConstraint;
|
||||
typedef constraints::TypeConstraint TypeConstraint;
|
||||
typedef constraints::UniqueItemsConstraint UniqueItemsConstraint;
|
||||
typedef constraints::PolyConstraint PolyConstraint;
|
||||
|
||||
public:
|
||||
|
||||
@ -84,6 +86,7 @@ public:
|
||||
virtual bool visit(const SingularItemsConstraint &) = 0;
|
||||
virtual bool visit(const TypeConstraint &) = 0;
|
||||
virtual bool visit(const UniqueItemsConstraint &) = 0;
|
||||
virtual bool visit(const PolyConstraint &) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
@ -81,6 +81,47 @@ public:
|
||||
mutableSubschema(subschema)->addConstraint(constraint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief move a constraint to a specific sub-schema
|
||||
*
|
||||
* @param constraint reference to a constraint that will be copied into
|
||||
* the sub-schema
|
||||
* @param subschema pointer to the sub-schema that will own the copied
|
||||
* constraint
|
||||
*
|
||||
* @throws std::runtime_error if the sub-schema is not owned by this Schema
|
||||
* instance
|
||||
*/
|
||||
void addConstraintToSubschema(Constraint* constraint,
|
||||
const Subschema *subschema)
|
||||
{
|
||||
// TODO: Check heirarchy for subschemas that do not belong...
|
||||
|
||||
mutableSubschema(subschema)->addConstraint(constraint);
|
||||
}
|
||||
//#if _cplusplus >=201103L
|
||||
#if 0
|
||||
/**
|
||||
* @brief Copy a constraint to a specific sub-schema
|
||||
* exception safe.
|
||||
*
|
||||
* @param constraint reference to a constraint that will be copied into
|
||||
* the sub-schema
|
||||
* @param subschema pointer to the sub-schema that will own the copied
|
||||
* constraint
|
||||
*
|
||||
* @throws std::runtime_error if the sub-schema is not owned by this Schema
|
||||
* instance
|
||||
*/
|
||||
void addConstraintToSubschema(std::unique_ptr<Constraint> constraint,
|
||||
const Subschema *subschema)
|
||||
{
|
||||
// TODO: Check heirarchy for subschemas that do not belong...
|
||||
|
||||
mutableSubschema(subschema)->addConstraint(std::move(constraint));
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Create a new Subschema instance that is owned by this Schema
|
||||
*
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <memory>
|
||||
|
||||
#include <valijson/constraints/constraint.hpp>
|
||||
|
||||
@ -93,6 +94,39 @@ public:
|
||||
constraints.push_back(constraint.clone(allocFn, freeFn));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief move a constraint to this sub-schema
|
||||
*
|
||||
* The constraint will be added to the list of constraints for this
|
||||
* Subschema.
|
||||
*
|
||||
* @param constraint pointer to the constraint to be added. Ownership
|
||||
* assumed.
|
||||
*/
|
||||
void addConstraint(const Constraint *constraint)
|
||||
{
|
||||
constraints.push_back(constraint);
|
||||
}
|
||||
|
||||
//#if _cplusplus >=201103L
|
||||
#if 0
|
||||
/**
|
||||
* @brief Add a constraint to this sub-schema
|
||||
*
|
||||
* The constraint will be copied before being added to the list of
|
||||
* constraints for this Subschema. Note that constraints will be copied
|
||||
* only as deep as references to other Subschemas - e.g. copies of
|
||||
* constraints that refer to sub-schemas, will continue to refer to the
|
||||
* same Subschema instances.
|
||||
*
|
||||
* @param constraint Reference to the constraint to copy
|
||||
*/
|
||||
void addConstraint(std::unique_ptr<Constraint>constraint)
|
||||
{
|
||||
constraints.push_back(constraint.release());
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Invoke a function on each child Constraint
|
||||
*
|
||||
|
@ -794,6 +794,18 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate a value against a PatternConstraint
|
||||
*
|
||||
* @param constraint Constraint that the target must validate against
|
||||
*
|
||||
* @return \c true if the constraint is satisfied; \c false otherwise
|
||||
*/
|
||||
virtual bool visit(const constraints::PolyConstraint &constraint)
|
||||
{
|
||||
return constraint.validate(target, context, results);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate a value against a PropertiesConstraint
|
||||
*
|
||||
|
171
tests/test_poly_constraint.cpp
Normal file
171
tests/test_poly_constraint.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "json/json.h" // jsoncpp rolled up
|
||||
|
||||
#define STRINGY1(arg) #arg
|
||||
#define STRINGY2(arg) STRINGY1(arg)
|
||||
|
||||
#define deblog(args) { \
|
||||
std::cerr << __FILE__ ":" STRINGY2(__LINE__ ) ": " ; \
|
||||
std::cerr << args << " "<< __PRETTY_FUNCTION__ << '\n'; \
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include <valijson/adapters/jsoncpp_adapter.hpp>
|
||||
#include <valijson/schema.hpp>
|
||||
#include <valijson/schema_parser.hpp>
|
||||
#include <valijson/validation_results.hpp>
|
||||
#include <valijson/validator.hpp>
|
||||
|
||||
namespace {
|
||||
using namespace valijson;
|
||||
using valijson::Schema;
|
||||
using valijson::SchemaParser;
|
||||
using valijson::Validator;
|
||||
using valijson::ValidationResults;
|
||||
using valijson::adapters::JsonCppAdapter;
|
||||
|
||||
using namespace std;
|
||||
Json::Value jsonParse(const string &jsonStr) {
|
||||
Json::Value retJson;
|
||||
Json::Reader json_reader;
|
||||
if (jsonStr.length() != 0) {
|
||||
if (!json_reader.parse(jsonStr, retJson)) {
|
||||
throw runtime_error("json parse failure\n"
|
||||
+ json_reader.getFormattedErrorMessages());
|
||||
}
|
||||
}
|
||||
return retJson;
|
||||
}
|
||||
|
||||
const static string emplrec (R"(
|
||||
{
|
||||
"employee" : {
|
||||
"John" : {
|
||||
"fullname": "John Doe"
|
||||
}
|
||||
},
|
||||
"elist" : [
|
||||
{"rec" : "John"},
|
||||
{"rec" : "Jane"}
|
||||
]
|
||||
}
|
||||
)");
|
||||
|
||||
const static string tschema(R"(
|
||||
{
|
||||
"type" : "object",
|
||||
"id" : "eroot",
|
||||
"properties" : {
|
||||
"employee": {
|
||||
"type":"object"
|
||||
},
|
||||
"elist" : {
|
||||
"type" : "array",
|
||||
"items": {
|
||||
"type" : "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["rec"],
|
||||
"properties" : {
|
||||
"rec" : {
|
||||
"type" : "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
)");
|
||||
|
||||
std::string err2String(ValidationResults& results) {
|
||||
ValidationResults::Error error;
|
||||
std::stringstream strstr;
|
||||
while (results.popError(error)) {
|
||||
strstr << error.description;
|
||||
for (std::string& str : error.context)
|
||||
strstr << str;
|
||||
strstr << "\n";
|
||||
}
|
||||
return strstr.str();
|
||||
}
|
||||
|
||||
using namespace constraints;
|
||||
|
||||
Json::Value *root; // needs to be a better way to do this.
|
||||
|
||||
class PathConstraint : public valijson::constraints::PolyConstraint {
|
||||
const std::string path;
|
||||
|
||||
public:
|
||||
PathConstraint(const std::string &path) : path(path){ }
|
||||
|
||||
virtual valijson::constraints::PolyConstraint * clone() const {
|
||||
deblog("clone !");
|
||||
return new PathConstraint(path);
|
||||
};
|
||||
|
||||
virtual bool validate(const adapters::Adapter &target, const std::vector<std::string> &context, ValidationResults *results) const {
|
||||
std::string spath(path + "." + target.asString());
|
||||
const Json::Path jpath(spath);
|
||||
const Json::Value& find(jpath.resolve(*root));
|
||||
if (!find) {
|
||||
std::string estring("Failed to find " + spath + " in input");
|
||||
if (results) {
|
||||
results->pushError(context, estring);
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
virtual Constraint * clone(CustomAlloc allocFn, CustomFree freeFn) const
|
||||
{
|
||||
void *ptr = allocFn(sizeof(PathConstraint));
|
||||
if (!ptr) {
|
||||
throw std::runtime_error(
|
||||
"Failed to allocate memory for cloned constraint");
|
||||
}
|
||||
|
||||
try {
|
||||
return new (ptr) PathConstraint(
|
||||
*static_cast<const PathConstraint*>(this));
|
||||
} catch (...) {
|
||||
freeFn(ptr);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
TEST(PolyConstraint, Insert) {
|
||||
Json::Value schemaJson(jsonParse(tschema));
|
||||
|
||||
JsonCppAdapter schemaDocumentAdapter(schemaJson);
|
||||
|
||||
SchemaParser parser;
|
||||
Schema schema;
|
||||
parser.populateSchema(schemaDocumentAdapter, schema);
|
||||
std::string empl(".employee");
|
||||
PathConstraint empConstraint(empl);
|
||||
// how to add this to schema?
|
||||
|
||||
Json::Value doc = jsonParse(emplrec);
|
||||
root = &doc;
|
||||
|
||||
JsonCppAdapter targetDocumentAdapter(doc);
|
||||
deblog("doc " << doc);
|
||||
deblog("schema" << schemaJson);
|
||||
ValidationResults results;
|
||||
Validator validator;
|
||||
std::regex John("Failed.*John");
|
||||
std::regex Jane("Failed.*Jane");
|
||||
EXPECT_FALSE(validator.validate(schema,targetDocumentAdapter, &results));
|
||||
std::string err(err2String(results));
|
||||
deblog("err " << err);
|
||||
EXPECT_FALSE(regex_search(err, John));
|
||||
EXPECT_TRUE(regex_search(err, Jane));
|
||||
}
|
Loading…
Reference in New Issue
Block a user