mirror of
https://github.com/tristanpenman/valijson.git
synced 2024-12-13 10:32:58 +01:00
Add support for 'contains' constraint
This commit is contained in:
parent
ba44a8d641
commit
f71355b90f
@ -165,6 +165,36 @@ private:
|
||||
const Subschema *elseSubschema;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'contains' constraint
|
||||
*
|
||||
* An contains constraint provides a collection of values that must be present
|
||||
* in an array.
|
||||
*/
|
||||
class ContainsConstraint: public BasicConstraint<ContainsConstraint>
|
||||
{
|
||||
public:
|
||||
ContainsConstraint()
|
||||
: subschema(nullptr) { }
|
||||
|
||||
ContainsConstraint(CustomAlloc allocFn, CustomFree freeFn)
|
||||
: BasicConstraint(allocFn, freeFn),
|
||||
subschema(nullptr) { }
|
||||
|
||||
const Subschema * getSubschema() const
|
||||
{
|
||||
return subschema;
|
||||
}
|
||||
|
||||
void setSubschema(const Subschema *subschema)
|
||||
{
|
||||
this->subschema = subschema;
|
||||
}
|
||||
|
||||
private:
|
||||
const Subschema *subschema;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Represents a 'dependencies' constraint.
|
||||
*
|
||||
@ -307,7 +337,6 @@ public:
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
// Delete values already added to constraint
|
||||
for (const EnumValue *value : enumValues) {
|
||||
|
@ -6,6 +6,7 @@ namespace constraints {
|
||||
class AllOfConstraint;
|
||||
class AnyOfConstraint;
|
||||
class ConditionalConstraint;
|
||||
class ContainsConstraint;
|
||||
class DependenciesConstraint;
|
||||
class EnumConstraint;
|
||||
class LinearItemsConstraint;
|
||||
@ -39,6 +40,7 @@ protected:
|
||||
typedef constraints::AllOfConstraint AllOfConstraint;
|
||||
typedef constraints::AnyOfConstraint AnyOfConstraint;
|
||||
typedef constraints::ConditionalConstraint ConditionalConstraint;
|
||||
typedef constraints::ContainsConstraint ContainsConstraint;
|
||||
typedef constraints::DependenciesConstraint DependenciesConstraint;
|
||||
typedef constraints::EnumConstraint EnumConstraint;
|
||||
typedef constraints::LinearItemsConstraint LinearItemsConstraint;
|
||||
@ -67,6 +69,7 @@ public:
|
||||
virtual bool visit(const AllOfConstraint &) = 0;
|
||||
virtual bool visit(const AnyOfConstraint &) = 0;
|
||||
virtual bool visit(const ConditionalConstraint &) = 0;
|
||||
virtual bool visit(const ContainsConstraint &) = 0;
|
||||
virtual bool visit(const DependenciesConstraint &) = 0;
|
||||
virtual bool visit(const EnumConstraint &) = 0;
|
||||
virtual bool visit(const LinearItemsConstraint &) = 0;
|
||||
|
@ -649,6 +649,13 @@ private:
|
||||
&subschema);
|
||||
}
|
||||
|
||||
if ((itr = object.find("contains")) != object.end()) {
|
||||
rootSchema.addConstraintToSubschema(
|
||||
makeContainsConstraint(rootSchema, rootNode, itr->second,
|
||||
updatedScope, nodePath + "/contains", fetchDoc,
|
||||
docCache, schemaCache), &subschema);
|
||||
}
|
||||
|
||||
if ((itr = object.find("dependencies")) != object.end()) {
|
||||
rootSchema.addConstraintToSubschema(
|
||||
makeDependenciesConstraint(rootSchema, rootNode,
|
||||
@ -689,8 +696,7 @@ private:
|
||||
}
|
||||
|
||||
if ((itr = object.find("enum")) != object.end()) {
|
||||
rootSchema.addConstraintToSubschema(makeEnumConstraint(itr->second),
|
||||
&subschema);
|
||||
rootSchema.addConstraintToSubschema(makeEnumConstraint(itr->second), &subschema);
|
||||
}
|
||||
|
||||
{
|
||||
@ -1177,6 +1183,35 @@ private:
|
||||
return constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a new ConditionalConstraint object.
|
||||
*
|
||||
* @param rootSchema The Schema instance, and root subschema,
|
||||
* through which other subschemas can be
|
||||
* created and modified
|
||||
* @param rootNode Reference to the node from which JSON
|
||||
* References will be resolved when they refer
|
||||
* to the current document; used for recursive
|
||||
* parsing of schemas
|
||||
* @param ifNode Schema that will be used to evaluate the
|
||||
* conditional.
|
||||
* @param thenNode Optional pointer to a JSON node containing
|
||||
* a schema that will be used when the conditional
|
||||
* evaluates to true.
|
||||
* @param elseNode Optional pointer to a JSON node containing
|
||||
* a schema that will be used when the conditional
|
||||
* evaluates to false.
|
||||
* @param currentScope URI for current resolution scope
|
||||
* @param containsPath JSON Pointer representing the path to
|
||||
* the 'contains' node
|
||||
* @param fetchDoc Function to fetch remote JSON documents
|
||||
* (optional)
|
||||
* @param docCache Cache of resolved and fetched remote
|
||||
* documents
|
||||
* @param schemaCache Cache of populated schemas
|
||||
*
|
||||
* @return pointer to a new ContainsConstraint that belongs to the caller
|
||||
*/
|
||||
template<typename AdapterType>
|
||||
constraints::ConditionalConstraint makeConditionalConstraint(
|
||||
Schema &rootSchema,
|
||||
@ -1215,6 +1250,63 @@ private:
|
||||
return constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a new ContainsConstraint object.
|
||||
*
|
||||
* @param rootSchema The Schema instance, and root subschema,
|
||||
* through which other subschemas can be
|
||||
* created and modified
|
||||
* @param rootNode Reference to the node from which JSON
|
||||
* References will be resolved when they refer
|
||||
* to the current document; used for recursive
|
||||
* parsing of schemas
|
||||
* @param contains Optional pointer to a JSON node containing
|
||||
* an object mapping property names to
|
||||
* schemas.
|
||||
* @param currentScope URI for current resolution scope
|
||||
* @param containsPath JSON Pointer representing the path to
|
||||
* the 'contains' node
|
||||
* @param fetchDoc Function to fetch remote JSON documents
|
||||
* (optional)
|
||||
* @param docCache Cache of resolved and fetched remote
|
||||
* documents
|
||||
* @param schemaCache Cache of populated schemas
|
||||
*
|
||||
* @return pointer to a new ContainsConstraint that belongs to the caller
|
||||
*/
|
||||
template<typename AdapterType>
|
||||
constraints::ContainsConstraint makeContainsConstraint(
|
||||
Schema &rootSchema,
|
||||
const AdapterType &rootNode,
|
||||
const AdapterType &contains,
|
||||
const opt::optional<std::string> currentScope,
|
||||
const std::string &containsPath,
|
||||
const typename FunctionPtrs<AdapterType>::FetchDoc fetchDoc,
|
||||
typename DocumentCache<AdapterType>::Type &docCache,
|
||||
SchemaCache &schemaCache)
|
||||
{
|
||||
constraints::ContainsConstraint constraint;
|
||||
|
||||
if (contains.isObject() || (version == kDraft7 && contains.maybeBool())) {
|
||||
const Subschema *subschema = makeOrReuseSchema<AdapterType>(
|
||||
rootSchema, rootNode, contains, currentScope, containsPath,
|
||||
fetchDoc, NULL, NULL, docCache, schemaCache);
|
||||
constraint.setSubschema(subschema);
|
||||
|
||||
} else if (contains.maybeObject()) {
|
||||
// If a loosely-typed Adapter type is being used, then we'll
|
||||
// assume that an empty schema has been provided.
|
||||
constraint.setSubschema(rootSchema.emptySubschema());
|
||||
|
||||
} else {
|
||||
// All other formats will result in an exception being thrown.
|
||||
throw std::runtime_error(
|
||||
"Expected valid schema for 'contains' constraint.");
|
||||
}
|
||||
|
||||
return constraint;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Make a new DependenciesConstraint object
|
||||
*
|
||||
|
@ -189,6 +189,46 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate current node using a 'contains' constraint
|
||||
*
|
||||
* A contains constraint is satisfied if the target is not an array, or if it is an array,
|
||||
* only if it contains at least one value that matches the specified schema.
|
||||
*
|
||||
* @param constraint ContainsConstraint that the current node must validate against
|
||||
*
|
||||
* @return \c true if validation passes; \c false otherwise
|
||||
*/
|
||||
virtual bool visit(const ContainsConstraint &constraint)
|
||||
{
|
||||
if ((strictTypes && !target.isArray()) || !target.maybeArray()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const Subschema *subschema = constraint.getSubschema();
|
||||
const typename AdapterType::Array arr = target.asArray();
|
||||
|
||||
bool validated = false;
|
||||
for (auto itr = arr.begin(); itr != arr.end(); ++itr) {
|
||||
ValidationVisitor containsValidator(*itr, context, strictTypes, nullptr);
|
||||
if (containsValidator.validateSchema(*subschema)) {
|
||||
validated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!validated) {
|
||||
if (results) {
|
||||
results->pushError(context,
|
||||
"Failed to any values against subschema in 'contains' constraint.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return validated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Validate current node against a 'dependencies' constraint
|
||||
*
|
||||
|
@ -453,7 +453,12 @@ TEST_F(TestValidator, Draft7_BooleanSchema)
|
||||
|
||||
// TODO: untested const
|
||||
|
||||
// TODO: untested contains
|
||||
TEST_F(TestValidator, Draft7_Contains)
|
||||
{
|
||||
// TODO: currently failing due to missing support for const
|
||||
|
||||
processDraft7TestFile(TEST_SUITE_DIR "draft7/contains.json");
|
||||
}
|
||||
|
||||
// TOOD: untested default
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user