Ver código fonte

fix: construction of Nodes, and handling of $ref

Sam Jaffe 1 ano atrás
pai
commit
6c3c6d30c8
1 arquivos alterados com 29 adições e 18 exclusões
  1. 29 18
      include/jvalidate/schema.h

+ 29 - 18
include/jvalidate/schema.h

@@ -37,7 +37,7 @@ protected:
 public:
   Node() = default;
   Node(std::string const & rejection_reason) : rejects_all_(rejection_reason) {}
-  template <Adapter A> Node(detail::ParserContext<A> context);
+  template <Adapter A> void construct(detail::ParserContext<A> context);
 
   bool is_pure_reference() const {
     return reference_ && constraints_.empty() && post_constraints_.empty() && not default_;
@@ -176,7 +176,7 @@ public:
 
     external.cache_reference(URI(), json);
     detail::ParserContext<A> root{*this, json, version, factory, external};
-    schema::Node::operator=(root);
+    construct(root);
   }
 
   /**
@@ -299,7 +299,7 @@ private:
     std::optional schema = context.external.try_load(ref);
     if (not schema.has_value()) {
       std::string error = "URIResolver could not resolve " + std::string(ref.uri());
-      return alias(context.where, &cache_.try_emplace(context.where, error).first->second);
+      return alias(ref, &cache_.try_emplace(ref, error).first->second);
     }
 
     return fetch_schema(context.rebind(*schema, ref));
@@ -332,7 +332,7 @@ private:
 
     // Do this here first in order to protect from infinite loops
     alias(context.where, &it->second);
-    it->second = schema::Node(context);
+    it->second.construct(context);
     if (not it->second.is_pure_reference()) {
       return &it->second;
     }
@@ -424,7 +424,7 @@ template <Adapter A> bool Node::resolve_reference(detail::ParserContext<A> const
   return false;
 }
 
-template <Adapter A> Node::Node(detail::ParserContext<A> context) {
+template <Adapter A> void Node::construct(detail::ParserContext<A> context) {
   EXPECT(context.schema.type() == adapter::Type::Object);
 
   auto const schema = context.schema.as_object();
@@ -459,21 +459,32 @@ template <Adapter A> Node::Node(detail::ParserContext<A> context) {
     description_ = schema["description"].as_string();
   }
 
-  for (auto [key, subschema] : schema) {
+  // Prior to Draft 2019-09, reference keywords take precedence over everything
+  // else (instead of allowing direct extensions).
+  if (has_reference && context.version < Version::Draft2019_09) {
+    return;
+  }
+
+  for (auto const & [key, subschema] : schema) {
     // Using a constraint store allows overriding certain rules, or the creation
     // of user-defined extention vocabularies.
-    if (auto make_constraint = context.factory(key, context.version)) {
-      EXPECT_M(not has_reference || context.version >= Version::Draft2019_09,
-               "Cannot directly extend $ref schemas before Draft2019-09");
-      // A constraint may return null if it is not applicable - but otherwise
-      // well-formed. For example, before Draft-06 "exclusiveMaximum" was a
-      // modifier property for "maximum", and not a unique constaint on its own.
-      // Therefore, we parse it alongside parsing "maximum", and could return
-      // nullptr when requesting a constraint pointer for "exclusiveMaximum".
-      if (auto constraint = make_constraint(context.child(subschema, key))) {
-        auto & into = context.factory.is_post_constraint(key) ? post_constraints_ : constraints_;
-        into.emplace(key, std::move(constraint));
-      }
+    auto make_constraint = context.factory(key, context.version);
+    if (not make_constraint) {
+      continue;
+    }
+    // A constraint may return null if it is not applicable - but otherwise
+    // well-formed. For example, before Draft-06 "exclusiveMaximum" was a
+    // modifier property for "maximum", and not a unique constaint on its own.
+    // Therefore, we parse it alongside parsing "maximum", and could return
+    // nullptr when requesting a constraint pointer for "exclusiveMaximum".
+    auto constraint = make_constraint(context.child(subschema, key));
+    if (not constraint) {
+      continue;
+    }
+    if (context.factory.is_post_constraint(key)) {
+      post_constraints_.emplace(key, std::move(constraint));
+    } else {
+      constraints_.emplace(key, std::move(constraint));
     }
   }
 }