mirror of
https://github.com/tristanpenman/valijson.git
synced 2025-01-06 00:31:11 +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 path
|
||||||
include_directories(
|
include_directories(
|
||||||
include
|
include
|
||||||
|
)
|
||||||
|
|
||||||
|
include_directories(SYSTEM
|
||||||
|
${Boost_INCLUDE_DIRS}
|
||||||
thirdparty/gtest-1.7.0/include
|
thirdparty/gtest-1.7.0/include
|
||||||
thirdparty/jsoncpp-0.9.4/include
|
thirdparty/jsoncpp-0.9.4/include
|
||||||
thirdparty/rapidjson-1.0.2/include
|
thirdparty/rapidjson-1.0.2/include
|
||||||
thirdparty/picojson-1.3.0
|
thirdparty/picojson-1.3.0
|
||||||
thirdparty/nlohmann-json-1.1.0
|
thirdparty/nlohmann-json-1.1.0
|
||||||
${Boost_INCLUDE_DIRS}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if(VALIJSON_CXX11_ADAPTERS_ARE_ENABLED)
|
if(VALIJSON_CXX11_ADAPTERS_ARE_ENABLED)
|
||||||
include_directories(
|
include_directories(SYSTEM
|
||||||
thirdparty/json11-2016-01-26
|
thirdparty/json11-2016-01-26
|
||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
@ -78,6 +80,7 @@ set(TEST_SOURCES
|
|||||||
tests/test_picojson_adapter.cpp
|
tests/test_picojson_adapter.cpp
|
||||||
tests/test_validation_errors.cpp
|
tests/test_validation_errors.cpp
|
||||||
tests/test_validator.cpp
|
tests/test_validator.cpp
|
||||||
|
tests/test_poly_constraint.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if(VALIJSON_CXX11_ADAPTERS_ARE_ENABLED)
|
if(VALIJSON_CXX11_ADAPTERS_ARE_ENABLED)
|
||||||
|
1
LICENSE
1
LICENSE
@ -1,4 +1,5 @@
|
|||||||
Copyright (c) 2016, Tristan Penman
|
Copyright (c) 2016, Tristan Penman
|
||||||
|
Copyright (c) 2016, Akamai Technolgies, Inc.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
#include <valijson/schema.hpp>
|
#include <valijson/schema.hpp>
|
||||||
|
|
||||||
namespace valijson {
|
namespace valijson {
|
||||||
|
class ValidationResults;
|
||||||
namespace constraints {
|
namespace constraints {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1060,6 +1061,12 @@ public:
|
|||||||
: BasicConstraint(allocFn, freeFn) { }
|
: 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 constraints
|
||||||
} // namespace valijson
|
} // namespace valijson
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ class RequiredConstraint;
|
|||||||
class SingularItemsConstraint;
|
class SingularItemsConstraint;
|
||||||
class TypeConstraint;
|
class TypeConstraint;
|
||||||
class UniqueItemsConstraint;
|
class UniqueItemsConstraint;
|
||||||
|
class PolyConstraint;
|
||||||
|
|
||||||
/// Interface to allow usage of the visitor pattern with Constraints
|
/// Interface to allow usage of the visitor pattern with Constraints
|
||||||
class ConstraintVisitor
|
class ConstraintVisitor
|
||||||
@ -58,6 +59,7 @@ protected:
|
|||||||
typedef constraints::SingularItemsConstraint SingularItemsConstraint;
|
typedef constraints::SingularItemsConstraint SingularItemsConstraint;
|
||||||
typedef constraints::TypeConstraint TypeConstraint;
|
typedef constraints::TypeConstraint TypeConstraint;
|
||||||
typedef constraints::UniqueItemsConstraint UniqueItemsConstraint;
|
typedef constraints::UniqueItemsConstraint UniqueItemsConstraint;
|
||||||
|
typedef constraints::PolyConstraint PolyConstraint;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
@ -84,6 +86,7 @@ public:
|
|||||||
virtual bool visit(const SingularItemsConstraint &) = 0;
|
virtual bool visit(const SingularItemsConstraint &) = 0;
|
||||||
virtual bool visit(const TypeConstraint &) = 0;
|
virtual bool visit(const TypeConstraint &) = 0;
|
||||||
virtual bool visit(const UniqueItemsConstraint &) = 0;
|
virtual bool visit(const UniqueItemsConstraint &) = 0;
|
||||||
|
virtual bool visit(const PolyConstraint &) = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -81,6 +81,47 @@ public:
|
|||||||
mutableSubschema(subschema)->addConstraint(constraint);
|
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
|
* @brief Create a new Subschema instance that is owned by this Schema
|
||||||
*
|
*
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/function.hpp>
|
#include <boost/function.hpp>
|
||||||
#include <boost/optional.hpp>
|
#include <boost/optional.hpp>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <valijson/constraints/constraint.hpp>
|
#include <valijson/constraints/constraint.hpp>
|
||||||
|
|
||||||
@ -93,6 +94,39 @@ public:
|
|||||||
constraints.push_back(constraint.clone(allocFn, freeFn));
|
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
|
* @brief Invoke a function on each child Constraint
|
||||||
*
|
*
|
||||||
|
@ -794,6 +794,18 @@ public:
|
|||||||
return true;
|
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
|
* @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