proxygen
JSONSchema.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2015-present Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
17 
18 #include <boost/algorithm/string/replace.hpp>
19 #include <boost/regex.hpp>
20 
21 #include <folly/CPortability.h>
22 #include <folly/Conv.h>
23 #include <folly/Memory.h>
24 #include <folly/Optional.h>
25 #include <folly/Singleton.h>
26 #include <folly/String.h>
27 #include <folly/json.h>
28 
29 namespace folly {
30 namespace jsonschema {
31 
32 namespace {
33 
37 struct FOLLY_EXPORT SchemaError : std::runtime_error {
38  SchemaError(SchemaError&&) = default;
39  SchemaError(const SchemaError&) = default;
40 
41  SchemaError(folly::StringPiece expected, const dynamic& value)
42  : std::runtime_error(to<std::string>(
43  "Expected to get ",
44  expected,
45  " for value ",
46  toJson(value))) {}
47  SchemaError(
48  folly::StringPiece expected,
49  const dynamic& schema,
50  const dynamic& value)
51  : std::runtime_error(to<std::string>(
52  "Expected to get ",
53  expected,
54  toJson(schema),
55  " for value ",
56  toJson(value))) {}
57 };
58 
59 template <class... Args>
60 Optional<SchemaError> makeError(Args&&... args) {
61  return Optional<SchemaError>(SchemaError(std::forward<Args>(args)...));
62 }
63 
64 struct ValidationContext;
65 
66 struct IValidator {
67  virtual ~IValidator() = default;
68 
69  private:
70  friend struct ValidationContext;
71 
72  virtual Optional<SchemaError> validate(
73  ValidationContext&,
74  const dynamic& value) const = 0;
75 };
76 
82 struct ValidationContext {
83  Optional<SchemaError> validate(IValidator* validator, const dynamic& value) {
84  auto ret = seen.insert(std::make_pair(validator, &value));
85  if (!ret.second) {
86  throw std::runtime_error("Infinite recursion detected");
87  }
88  return validator->validate(*this, value);
89  }
90 
91  private:
92  std::unordered_set<std::pair<const IValidator*, const dynamic*>> seen;
93 };
94 
100 struct SchemaValidatorContext final {
101  explicit SchemaValidatorContext(const dynamic& s) : schema(s) {}
102 
103  const dynamic& schema;
104  std::unordered_map<std::string, IValidator*> refs;
105 };
106 
110 struct SchemaValidator final : IValidator, public Validator {
111  SchemaValidator() = default;
112  void loadSchema(SchemaValidatorContext& context, const dynamic& schema);
113 
114  Optional<SchemaError> validate(ValidationContext&, const dynamic& value)
115  const override;
116 
117  // Validator interface
118  void validate(const dynamic& value) const override;
119  exception_wrapper try_validate(const dynamic& value) const noexcept override;
120 
121  static std::unique_ptr<SchemaValidator> make(
122  SchemaValidatorContext& context,
123  const dynamic& schema) {
124  // We break apart the constructor and actually loading the schema so that
125  // we can handle the case where a schema refers to itself, e.g. via
126  // "$ref": "#".
127  auto v = std::make_unique<SchemaValidator>();
128  v->loadSchema(context, schema);
129  return v;
130  }
131 
132  private:
133  std::vector<std::unique_ptr<IValidator>> validators_;
134 };
135 
136 struct MultipleOfValidator final : IValidator {
137  explicit MultipleOfValidator(dynamic schema) : schema_(std::move(schema)) {}
138  Optional<SchemaError> validate(ValidationContext&, const dynamic& value)
139  const override {
140  if (!schema_.isNumber() || !value.isNumber()) {
141  return none;
142  }
143  if (schema_.isDouble() || value.isDouble()) {
144  const auto rem = std::remainder(value.asDouble(), schema_.asDouble());
145  if (std::abs(rem) > std::numeric_limits<double>::epsilon()) {
146  return makeError("a multiple of ", schema_, value);
147  }
148  } else { // both ints
149  if ((value.getInt() % schema_.getInt()) != 0) {
150  return makeError("a multiple of ", schema_, value);
151  }
152  }
153  return none;
154  }
155  dynamic schema_;
156 };
157 
158 struct ComparisonValidator final : IValidator {
159  enum class Type { MIN, MAX };
160  ComparisonValidator(dynamic schema, const dynamic* exclusive, Type type)
161  : schema_(std::move(schema)), exclusive_(false), type_(type) {
162  if (exclusive && exclusive->isBool()) {
163  exclusive_ = exclusive->getBool();
164  }
165  }
166 
167  template <typename Numeric>
168  Optional<SchemaError>
169  validateHelper(const dynamic& value, Numeric s, Numeric v) const {
170  if (type_ == Type::MIN) {
171  if (exclusive_) {
172  if (v <= s) {
173  return makeError("greater than ", schema_, value);
174  }
175  } else {
176  if (v < s) {
177  return makeError("greater than or equal to ", schema_, value);
178  }
179  }
180  } else if (type_ == Type::MAX) {
181  if (exclusive_) {
182  if (v >= s) {
183  return makeError("less than ", schema_, value);
184  }
185  } else {
186  if (v > s) {
187  return makeError("less than or equal to ", schema_, value);
188  }
189  }
190  }
191  return none;
192  }
193 
194  Optional<SchemaError> validate(ValidationContext&, const dynamic& value)
195  const override {
196  if (!schema_.isNumber() || !value.isNumber()) {
197  return none;
198  }
199  if (schema_.isDouble() || value.isDouble()) {
200  return validateHelper(value, schema_.asDouble(), value.asDouble());
201  } else { // both ints
202  return validateHelper(value, schema_.asInt(), value.asInt());
203  }
204  }
205 
206  dynamic schema_;
209 };
210 
211 template <class Comparison>
212 struct SizeValidator final : IValidator {
213  explicit SizeValidator(const dynamic& schema, dynamic::Type type)
214  : length_(-1), type_(type) {
215  if (schema.isInt()) {
216  length_ = schema.getInt();
217  }
218  }
219 
220  Optional<SchemaError> validate(ValidationContext&, const dynamic& value)
221  const override {
222  if (length_ < 0) {
223  return none;
224  }
225  if (value.type() != type_) {
226  return none;
227  }
228  if (!Comparison()(length_, int64_t(value.size()))) {
229  return makeError("different length string/array/object", value);
230  }
231  return none;
232  }
235 };
236 
237 struct StringPatternValidator final : IValidator {
238  explicit StringPatternValidator(const dynamic& schema) {
239  if (schema.isString()) {
240  regex_ = boost::regex(schema.getString());
241  }
242  }
243 
244  Optional<SchemaError> validate(ValidationContext&, const dynamic& value)
245  const override {
246  if (!value.isString() || regex_.empty()) {
247  return none;
248  }
249  if (!boost::regex_search(value.getString(), regex_)) {
250  return makeError("string matching regex", value);
251  }
252  return none;
253  }
255 };
256 
257 struct ArrayUniqueValidator final : IValidator {
258  explicit ArrayUniqueValidator(const dynamic& schema) : unique_(false) {
259  if (schema.isBool()) {
260  unique_ = schema.getBool();
261  }
262  }
263 
264  Optional<SchemaError> validate(ValidationContext&, const dynamic& value)
265  const override {
266  if (!unique_ || !value.isArray()) {
267  return none;
268  }
269  for (const auto& i : value) {
270  for (const auto& j : value) {
271  if (&i != &j && i == j) {
272  return makeError("unique items in array", value);
273  }
274  }
275  }
276  return none;
277  }
278  bool unique_;
279 };
280 
281 struct ArrayItemsValidator final : IValidator {
282  ArrayItemsValidator(
283  SchemaValidatorContext& context,
284  const dynamic* items,
285  const dynamic* additionalItems)
286  : allowAdditionalItems_(true) {
287  if (items && items->isObject()) {
288  itemsValidator_ = SchemaValidator::make(context, *items);
289  return; // Additional items is ignored
290  } else if (items && items->isArray()) {
291  for (const auto& item : *items) {
292  itemsValidators_.emplace_back(SchemaValidator::make(context, item));
293  }
294  } else {
295  // If items isn't present or is invalid, it defaults to an empty schema.
296  itemsValidator_ = SchemaValidator::make(context, dynamic::object);
297  }
298  if (additionalItems) {
299  if (additionalItems->isBool()) {
300  allowAdditionalItems_ = additionalItems->getBool();
301  } else if (additionalItems->isObject()) {
303  SchemaValidator::make(context, *additionalItems);
304  }
305  }
306  }
307 
308  Optional<SchemaError> validate(ValidationContext& vc, const dynamic& value)
309  const override {
310  if (!value.isArray()) {
311  return none;
312  }
313  if (itemsValidator_) {
314  for (const auto& v : value) {
315  if (auto se = vc.validate(itemsValidator_.get(), v)) {
316  return se;
317  }
318  }
319  return none;
320  }
321  size_t pos = 0;
322  for (; pos < value.size() && pos < itemsValidators_.size(); ++pos) {
323  if (auto se = vc.validate(itemsValidators_[pos].get(), value[pos])) {
324  return se;
325  }
326  }
327  if (!allowAdditionalItems_ && pos < value.size()) {
328  return makeError("no more additional items", value);
329  }
331  for (; pos < value.size(); ++pos) {
332  if (auto se =
333  vc.validate(additionalItemsValidator_.get(), value[pos])) {
334  return se;
335  }
336  }
337  }
338  return none;
339  }
340  std::unique_ptr<IValidator> itemsValidator_;
341  std::vector<std::unique_ptr<IValidator>> itemsValidators_;
342  std::unique_ptr<IValidator> additionalItemsValidator_;
344 };
345 
346 struct RequiredValidator final : IValidator {
347  explicit RequiredValidator(const dynamic& schema) {
348  if (schema.isArray()) {
349  for (const auto& item : schema) {
350  if (item.isString()) {
351  properties_.emplace_back(item.getString());
352  }
353  }
354  }
355  }
356 
357  Optional<SchemaError> validate(ValidationContext&, const dynamic& value)
358  const override {
359  if (value.isObject()) {
360  for (const auto& prop : properties_) {
361  if (!value.get_ptr(prop)) {
362  return makeError("property ", prop, value);
363  }
364  }
365  }
366  return none;
367  }
368 
369  private:
370  std::vector<std::string> properties_;
371 };
372 
373 struct PropertiesValidator final : IValidator {
374  PropertiesValidator(
375  SchemaValidatorContext& context,
376  const dynamic* properties,
377  const dynamic* patternProperties,
378  const dynamic* additionalProperties)
380  if (properties && properties->isObject()) {
381  for (const auto& pair : properties->items()) {
382  if (pair.first.isString()) {
383  propertyValidators_[pair.first.getString()] =
384  SchemaValidator::make(context, pair.second);
385  }
386  }
387  }
388  if (patternProperties && patternProperties->isObject()) {
389  for (const auto& pair : patternProperties->items()) {
390  if (pair.first.isString()) {
391  patternPropertyValidators_.emplace_back(
392  boost::regex(pair.first.getString()),
393  SchemaValidator::make(context, pair.second));
394  }
395  }
396  }
397  if (additionalProperties) {
398  if (additionalProperties->isBool()) {
399  allowAdditionalProperties_ = additionalProperties->getBool();
400  } else if (additionalProperties->isObject()) {
402  SchemaValidator::make(context, *additionalProperties);
403  }
404  }
405  }
406 
407  Optional<SchemaError> validate(ValidationContext& vc, const dynamic& value)
408  const override {
409  if (!value.isObject()) {
410  return none;
411  }
412  for (const auto& pair : value.items()) {
413  if (!pair.first.isString()) {
414  continue;
415  }
416  const std::string& key = pair.first.getString();
417  auto it = propertyValidators_.find(key);
418  bool matched = false;
419  if (it != propertyValidators_.end()) {
420  if (auto se = vc.validate(it->second.get(), pair.second)) {
421  return se;
422  }
423  matched = true;
424  }
425 
426  const std::string& strkey = key;
427  for (const auto& ppv : patternPropertyValidators_) {
428  if (boost::regex_search(strkey, ppv.first)) {
429  if (auto se = vc.validate(ppv.second.get(), pair.second)) {
430  return se;
431  }
432  matched = true;
433  }
434  }
435  if (matched) {
436  continue;
437  }
439  return makeError("no more additional properties", value);
440  }
442  if (auto se =
443  vc.validate(additionalPropertyValidator_.get(), pair.second)) {
444  return se;
445  }
446  }
447  }
448  return none;
449  }
450 
451  std::unordered_map<std::string, std::unique_ptr<IValidator>>
453  std::vector<std::pair<boost::regex, std::unique_ptr<IValidator>>>
455  std::unique_ptr<IValidator> additionalPropertyValidator_;
457 };
458 
459 struct DependencyValidator final : IValidator {
460  DependencyValidator(SchemaValidatorContext& context, const dynamic& schema) {
461  if (!schema.isObject()) {
462  return;
463  }
464  for (const auto& pair : schema.items()) {
465  if (!pair.first.isString()) {
466  continue;
467  }
468  if (pair.second.isArray()) {
469  auto p = make_pair(pair.first.getString(), std::vector<std::string>());
470  for (const auto& item : pair.second) {
471  if (item.isString()) {
472  p.second.push_back(item.getString());
473  }
474  }
475  propertyDep_.emplace_back(std::move(p));
476  }
477  if (pair.second.isObject()) {
478  schemaDep_.emplace_back(
479  pair.first.getString(),
480  SchemaValidator::make(context, pair.second));
481  }
482  }
483  }
484 
485  Optional<SchemaError> validate(ValidationContext& vc, const dynamic& value)
486  const override {
487  if (!value.isObject()) {
488  return none;
489  }
490  for (const auto& pair : propertyDep_) {
491  if (value.count(pair.first)) {
492  for (const auto& prop : pair.second) {
493  if (!value.count(prop)) {
494  return makeError("property ", prop, value);
495  }
496  }
497  }
498  }
499  for (const auto& pair : schemaDep_) {
500  if (value.count(pair.first)) {
501  if (auto se = vc.validate(pair.second.get(), value)) {
502  return se;
503  }
504  }
505  }
506  return none;
507  }
508 
509  std::vector<std::pair<std::string, std::vector<std::string>>> propertyDep_;
510  std::vector<std::pair<std::string, std::unique_ptr<IValidator>>> schemaDep_;
511 };
512 
513 struct EnumValidator final : IValidator {
514  explicit EnumValidator(dynamic schema) : schema_(std::move(schema)) {}
515 
516  Optional<SchemaError> validate(ValidationContext&, const dynamic& value)
517  const override {
518  if (!schema_.isArray()) {
519  return none;
520  }
521  for (const auto& item : schema_) {
522  if (value == item) {
523  return none;
524  }
525  }
526  return makeError("one of enum values: ", schema_, value);
527  }
528  dynamic schema_;
529 };
530 
531 struct TypeValidator final : IValidator {
532  explicit TypeValidator(const dynamic& schema) {
533  if (schema.isString()) {
534  addType(schema.stringPiece());
535  } else if (schema.isArray()) {
536  for (const auto& item : schema) {
537  if (item.isString()) {
538  addType(item.stringPiece());
539  }
540  }
541  }
542  }
543 
544  Optional<SchemaError> validate(ValidationContext&, const dynamic& value)
545  const override {
546  auto it =
547  std::find(allowedTypes_.begin(), allowedTypes_.end(), value.type());
548  if (it == allowedTypes_.end()) {
549  return makeError("a value of type ", typeStr_, value);
550  }
551  return none;
552  }
553 
554  private:
555  std::vector<dynamic::Type> allowedTypes_;
556  std::string typeStr_; // for errors
557 
558  void addType(StringPiece value) {
559  if (value == "array") {
560  allowedTypes_.push_back(dynamic::Type::ARRAY);
561  } else if (value == "boolean") {
562  allowedTypes_.push_back(dynamic::Type::BOOL);
563  } else if (value == "integer") {
564  allowedTypes_.push_back(dynamic::Type::INT64);
565  } else if (value == "number") {
566  allowedTypes_.push_back(dynamic::Type::INT64);
567  allowedTypes_.push_back(dynamic::Type::DOUBLE);
568  } else if (value == "null") {
569  allowedTypes_.push_back(dynamic::Type::NULLT);
570  } else if (value == "object") {
571  allowedTypes_.push_back(dynamic::Type::OBJECT);
572  } else if (value == "string") {
573  allowedTypes_.push_back(dynamic::Type::STRING);
574  } else {
575  return;
576  }
577  if (!typeStr_.empty()) {
578  typeStr_ += ", ";
579  }
580  typeStr_ += value.str();
581  }
582 };
583 
584 struct AllOfValidator final : IValidator {
585  AllOfValidator(SchemaValidatorContext& context, const dynamic& schema) {
586  if (schema.isArray()) {
587  for (const auto& item : schema) {
588  validators_.emplace_back(SchemaValidator::make(context, item));
589  }
590  }
591  }
592 
593  Optional<SchemaError> validate(ValidationContext& vc, const dynamic& value)
594  const override {
595  for (const auto& val : validators_) {
596  if (auto se = vc.validate(val.get(), value)) {
597  return se;
598  }
599  }
600  return none;
601  }
602 
603  std::vector<std::unique_ptr<IValidator>> validators_;
604 };
605 
606 struct AnyOfValidator final : IValidator {
607  enum class Type { EXACTLY_ONE, ONE_OR_MORE };
608 
609  AnyOfValidator(
610  SchemaValidatorContext& context,
611  const dynamic& schema,
612  Type type)
613  : type_(type) {
614  if (schema.isArray()) {
615  for (const auto& item : schema) {
616  validators_.emplace_back(SchemaValidator::make(context, item));
617  }
618  }
619  }
620 
621  Optional<SchemaError> validate(ValidationContext& vc, const dynamic& value)
622  const override {
623  std::vector<SchemaError> errors;
624  for (const auto& val : validators_) {
625  if (auto se = vc.validate(val.get(), value)) {
626  errors.emplace_back(*se);
627  }
628  }
629  const auto success = validators_.size() - errors.size();
630  if (success == 0) {
631  return makeError("at least one valid schema", value);
632  } else if (success > 1 && type_ == Type::EXACTLY_ONE) {
633  return makeError("exactly one valid schema", value);
634  }
635  return none;
636  }
637 
638  Type type_;
639  std::vector<std::unique_ptr<IValidator>> validators_;
640 };
641 
642 struct RefValidator final : IValidator {
643  explicit RefValidator(IValidator* validator) : validator_(validator) {}
644 
645  Optional<SchemaError> validate(ValidationContext& vc, const dynamic& value)
646  const override {
647  return vc.validate(validator_, value);
648  }
649  IValidator* validator_;
650 };
651 
652 struct NotValidator final : IValidator {
653  NotValidator(SchemaValidatorContext& context, const dynamic& schema)
654  : validator_(SchemaValidator::make(context, schema)) {}
655 
656  Optional<SchemaError> validate(ValidationContext& vc, const dynamic& value)
657  const override {
658  if (vc.validate(validator_.get(), value)) {
659  return none;
660  }
661  return makeError("Expected schema validation to fail", value);
662  }
663  std::unique_ptr<IValidator> validator_;
664 };
665 
666 void SchemaValidator::loadSchema(
667  SchemaValidatorContext& context,
668  const dynamic& schema) {
669  if (!schema.isObject() || schema.empty()) {
670  return;
671  }
672 
673  // Check for $ref, if we have one we won't apply anything else. Refs are
674  // pointers to other parts of the json, e.g. #/foo/bar points to the schema
675  // located at root["foo"]["bar"].
676  if (const auto* p = schema.get_ptr("$ref")) {
677  // We only support absolute refs, i.e. those starting with '#'
678  if (p->isString() && p->stringPiece()[0] == '#') {
679  auto it = context.refs.find(p->getString());
680  if (it != context.refs.end()) {
681  validators_.emplace_back(std::make_unique<RefValidator>(it->second));
682  return;
683  }
684 
685  // This is a ref, but we haven't loaded it yet. Find where it is based on
686  // the root schema.
687  std::vector<std::string> parts;
688  split("/", p->stringPiece(), parts);
689  const auto* s = &context.schema; // First part is '#'
690  for (size_t i = 1; s && i < parts.size(); ++i) {
691  // Per the standard, we must replace ~1 with / and then ~0 with ~
692  boost::replace_all(parts[i], "~1", "/");
693  boost::replace_all(parts[i], "~0", "~");
694  if (s->isObject()) {
695  s = s->get_ptr(parts[i]);
696  continue;
697  }
698  if (s->isArray()) {
699  try {
700  const size_t pos = to<size_t>(parts[i]);
701  if (pos < s->size()) {
702  s = s->get_ptr(pos);
703  continue;
704  }
705  } catch (const std::range_error&) {
706  // ignore
707  }
708  }
709  break;
710  }
711  // If you have a self-recursive reference, this avoids getting into an
712  // infinite recursion, where we try to load a schema that just references
713  // itself, and then we try to load it again, and so on.
714  // Instead we load a pointer to the schema into the refs, so that any
715  // future references to it will just see that pointer and won't try to
716  // keep parsing further.
717  if (s) {
718  auto v = std::make_unique<SchemaValidator>();
719  context.refs[p->getString()] = v.get();
720  v->loadSchema(context, *s);
721  validators_.emplace_back(std::move(v));
722  return;
723  }
724  }
725  }
726 
727  // Numeric validators
728  if (const auto* p = schema.get_ptr("multipleOf")) {
729  validators_.emplace_back(std::make_unique<MultipleOfValidator>(*p));
730  }
731  if (const auto* p = schema.get_ptr("maximum")) {
732  validators_.emplace_back(std::make_unique<ComparisonValidator>(
733  *p,
734  schema.get_ptr("exclusiveMaximum"),
735  ComparisonValidator::Type::MAX));
736  }
737  if (const auto* p = schema.get_ptr("minimum")) {
738  validators_.emplace_back(std::make_unique<ComparisonValidator>(
739  *p,
740  schema.get_ptr("exclusiveMinimum"),
742  }
743 
744  // String validators
745  if (const auto* p = schema.get_ptr("maxLength")) {
746  validators_.emplace_back(
747  std::make_unique<SizeValidator<std::greater_equal<int64_t>>>(
748  *p, dynamic::Type::STRING));
749  }
750  if (const auto* p = schema.get_ptr("minLength")) {
751  validators_.emplace_back(
752  std::make_unique<SizeValidator<std::less_equal<int64_t>>>(
753  *p, dynamic::Type::STRING));
754  }
755  if (const auto* p = schema.get_ptr("pattern")) {
756  validators_.emplace_back(std::make_unique<StringPatternValidator>(*p));
757  }
758 
759  // Array validators
760  const auto* items = schema.get_ptr("items");
761  const auto* additionalItems = schema.get_ptr("additionalItems");
762  if (items || additionalItems) {
763  validators_.emplace_back(
764  std::make_unique<ArrayItemsValidator>(context, items, additionalItems));
765  }
766  if (const auto* p = schema.get_ptr("maxItems")) {
767  validators_.emplace_back(
768  std::make_unique<SizeValidator<std::greater_equal<int64_t>>>(
769  *p, dynamic::Type::ARRAY));
770  }
771  if (const auto* p = schema.get_ptr("minItems")) {
772  validators_.emplace_back(
773  std::make_unique<SizeValidator<std::less_equal<int64_t>>>(
774  *p, dynamic::Type::ARRAY));
775  }
776  if (const auto* p = schema.get_ptr("uniqueItems")) {
777  validators_.emplace_back(std::make_unique<ArrayUniqueValidator>(*p));
778  }
779 
780  // Object validators
781  const auto* properties = schema.get_ptr("properties");
782  const auto* patternProperties = schema.get_ptr("patternProperties");
783  const auto* additionalProperties = schema.get_ptr("additionalProperties");
784  if (properties || patternProperties || additionalProperties) {
785  validators_.emplace_back(std::make_unique<PropertiesValidator>(
786  context, properties, patternProperties, additionalProperties));
787  }
788  if (const auto* p = schema.get_ptr("maxProperties")) {
789  validators_.emplace_back(
790  std::make_unique<SizeValidator<std::greater_equal<int64_t>>>(
791  *p, dynamic::Type::OBJECT));
792  }
793  if (const auto* p = schema.get_ptr("minProperties")) {
794  validators_.emplace_back(
795  std::make_unique<SizeValidator<std::less_equal<int64_t>>>(
796  *p, dynamic::Type::OBJECT));
797  }
798  if (const auto* p = schema.get_ptr("required")) {
799  validators_.emplace_back(std::make_unique<RequiredValidator>(*p));
800  }
801 
802  // Misc validators
803  if (const auto* p = schema.get_ptr("dependencies")) {
804  validators_.emplace_back(
805  std::make_unique<DependencyValidator>(context, *p));
806  }
807  if (const auto* p = schema.get_ptr("enum")) {
808  validators_.emplace_back(std::make_unique<EnumValidator>(*p));
809  }
810  if (const auto* p = schema.get_ptr("type")) {
811  validators_.emplace_back(std::make_unique<TypeValidator>(*p));
812  }
813  if (const auto* p = schema.get_ptr("allOf")) {
814  validators_.emplace_back(std::make_unique<AllOfValidator>(context, *p));
815  }
816  if (const auto* p = schema.get_ptr("anyOf")) {
817  validators_.emplace_back(std::make_unique<AnyOfValidator>(
818  context, *p, AnyOfValidator::Type::ONE_OR_MORE));
819  }
820  if (const auto* p = schema.get_ptr("oneOf")) {
821  validators_.emplace_back(std::make_unique<AnyOfValidator>(
822  context, *p, AnyOfValidator::Type::EXACTLY_ONE));
823  }
824  if (const auto* p = schema.get_ptr("not")) {
825  validators_.emplace_back(std::make_unique<NotValidator>(context, *p));
826  }
827 }
828 
829 void SchemaValidator::validate(const dynamic& value) const {
830  ValidationContext vc;
831  if (auto se = validate(vc, value)) {
832  throw *se;
833  }
834 }
835 
836 exception_wrapper SchemaValidator::try_validate(const dynamic& value) const
837  noexcept {
838  try {
839  ValidationContext vc;
840  if (auto se = validate(vc, value)) {
841  return make_exception_wrapper<SchemaError>(*se);
842  }
843  } catch (const std::exception& e) {
844  return exception_wrapper(std::current_exception(), e);
845  } catch (...) {
846  return exception_wrapper(std::current_exception());
847  }
848  return exception_wrapper();
849 }
850 
851 Optional<SchemaError> SchemaValidator::validate(
852  ValidationContext& vc,
853  const dynamic& value) const {
854  for (const auto& validator : validators_) {
855  if (auto se = vc.validate(validator.get(), value)) {
856  return se;
857  }
858  }
859  return none;
860 }
861 
866 const char* metaschemaJson =
867  "\
868 { \
869  \"id\": \"http://json-schema.org/draft-04/schema#\", \
870  \"$schema\": \"http://json-schema.org/draft-04/schema#\", \
871  \"description\": \"Core schema meta-schema\", \
872  \"definitions\": { \
873  \"schemaArray\": { \
874  \"type\": \"array\", \
875  \"minItems\": 1, \
876  \"items\": { \"$ref\": \"#\" } \
877  }, \
878  \"positiveInteger\": { \
879  \"type\": \"integer\", \
880  \"minimum\": 0 \
881  }, \
882  \"positiveIntegerDefault0\": { \
883  \"allOf\": [ \
884  { \"$ref\": \"#/definitions/positiveInteger\" }, { \"default\": 0 } ]\
885  }, \
886  \"simpleTypes\": { \
887  \"enum\": [ \"array\", \"boolean\", \"integer\", \
888  \"null\", \"number\", \"object\", \"string\" ] \
889  }, \
890  \"stringArray\": { \
891  \"type\": \"array\", \
892  \"items\": { \"type\": \"string\" }, \
893  \"minItems\": 1, \
894  \"uniqueItems\": true \
895  } \
896  }, \
897  \"type\": \"object\", \
898  \"properties\": { \
899  \"id\": { \
900  \"type\": \"string\", \
901  \"format\": \"uri\" \
902  }, \
903  \"$schema\": { \
904  \"type\": \"string\", \
905  \"format\": \"uri\" \
906  }, \
907  \"title\": { \
908  \"type\": \"string\" \
909  }, \
910  \"description\": { \
911  \"type\": \"string\" \
912  }, \
913  \"default\": {}, \
914  \"multipleOf\": { \
915  \"type\": \"number\", \
916  \"minimum\": 0, \
917  \"exclusiveMinimum\": true \
918  }, \
919  \"maximum\": { \
920  \"type\": \"number\" \
921  }, \
922  \"exclusiveMaximum\": { \
923  \"type\": \"boolean\", \
924  \"default\": false \
925  }, \
926  \"minimum\": { \
927  \"type\": \"number\" \
928  }, \
929  \"exclusiveMinimum\": { \
930  \"type\": \"boolean\", \
931  \"default\": false \
932  }, \
933  \"maxLength\": { \"$ref\": \"#/definitions/positiveInteger\" }, \
934  \"minLength\": { \"$ref\": \"#/definitions/positiveIntegerDefault0\" },\
935  \"pattern\": { \
936  \"type\": \"string\", \
937  \"format\": \"regex\" \
938  }, \
939  \"additionalItems\": { \
940  \"anyOf\": [ \
941  { \"type\": \"boolean\" }, \
942  { \"$ref\": \"#\" } \
943  ], \
944  \"default\": {} \
945  }, \
946  \"items\": { \
947  \"anyOf\": [ \
948  { \"$ref\": \"#\" }, \
949  { \"$ref\": \"#/definitions/schemaArray\" } \
950  ], \
951  \"default\": {} \
952  }, \
953  \"maxItems\": { \"$ref\": \"#/definitions/positiveInteger\" }, \
954  \"minItems\": { \"$ref\": \"#/definitions/positiveIntegerDefault0\" }, \
955  \"uniqueItems\": { \
956  \"type\": \"boolean\", \
957  \"default\": false \
958  }, \
959  \"maxProperties\": { \"$ref\": \"#/definitions/positiveInteger\" }, \
960  \"minProperties\": { \
961  \"$ref\": \"#/definitions/positiveIntegerDefault0\" }, \
962  \"required\": { \"$ref\": \"#/definitions/stringArray\" }, \
963  \"additionalProperties\": { \
964  \"anyOf\": [ \
965  { \"type\": \"boolean\" }, \
966  { \"$ref\": \"#\" } \
967  ], \
968  \"default\": {} \
969  }, \
970  \"definitions\": { \
971  \"type\": \"object\", \
972  \"additionalProperties\": { \"$ref\": \"#\" }, \
973  \"default\": {} \
974  }, \
975  \"properties\": { \
976  \"type\": \"object\", \
977  \"additionalProperties\": { \"$ref\": \"#\" }, \
978  \"default\": {} \
979  }, \
980  \"patternProperties\": { \
981  \"type\": \"object\", \
982  \"additionalProperties\": { \"$ref\": \"#\" }, \
983  \"default\": {} \
984  }, \
985  \"dependencies\": { \
986  \"type\": \"object\", \
987  \"additionalProperties\": { \
988  \"anyOf\": [ \
989  { \"$ref\": \"#\" }, \
990  { \"$ref\": \"#/definitions/stringArray\" } \
991  ] \
992  } \
993  }, \
994  \"enum\": { \
995  \"type\": \"array\", \
996  \"minItems\": 1, \
997  \"uniqueItems\": true \
998  }, \
999  \"type\": { \
1000  \"anyOf\": [ \
1001  { \"$ref\": \"#/definitions/simpleTypes\" }, \
1002  { \
1003  \"type\": \"array\", \
1004  \"items\": { \"$ref\": \"#/definitions/simpleTypes\" }, \
1005  \"minItems\": 1, \
1006  \"uniqueItems\": true \
1007  } \
1008  ] \
1009  }, \
1010  \"allOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \
1011  \"anyOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \
1012  \"oneOf\": { \"$ref\": \"#/definitions/schemaArray\" }, \
1013  \"not\": { \"$ref\": \"#\" } \
1014  }, \
1015  \"dependencies\": { \
1016  \"exclusiveMaximum\": [ \"maximum\" ], \
1017  \"exclusiveMinimum\": [ \"minimum\" ] \
1018  }, \
1019  \"default\": {} \
1020 }";
1021 
1022 folly::Singleton<Validator> schemaValidator([]() {
1023  return makeValidator(parseJson(metaschemaJson)).release();
1024 });
1025 } // namespace
1026 
1027 Validator::~Validator() = default;
1028 
1029 std::unique_ptr<Validator> makeValidator(const dynamic& schema) {
1030  auto v = std::make_unique<SchemaValidator>();
1031  SchemaValidatorContext context(schema);
1032  context.refs["#"] = v.get();
1033  v->loadSchema(context, schema);
1034  return std::move(v);
1035 }
1036 
1037 std::shared_ptr<Validator> makeSchemaValidator() {
1038  return schemaValidator.try_get();
1039 }
1040 } // namespace jsonschema
1041 } // namespace folly
std::vector< std::string > properties_
Definition: JSONSchema.cpp:370
static ObjectMaker object()
Definition: dynamic-inl.h:240
int64_t length_
Definition: JSONSchema.cpp:233
auto v
regex
Definition: CMakeCache.txt:563
dynamic parseJson(StringPiece range)
Definition: json.cpp:900
dynamic schema_
Definition: JSONSchema.cpp:155
std::shared_ptr< Validator > makeSchemaValidator()
std::unique_ptr< IValidator > itemsValidator_
Definition: JSONSchema.cpp:340
std::unordered_map< std::string, std::unique_ptr< IValidator > > propertyValidators_
Definition: JSONSchema.cpp:452
PskType type
context
Definition: CMakeCache.txt:563
bool unique_
Definition: JSONSchema.cpp:278
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
double val
Definition: String.cpp:273
IValidator * validator_
Definition: JSONSchema.cpp:649
std::unordered_map< std::string, IValidator * > refs
Definition: JSONSchema.cpp:104
#define MIN(a, b)
Definition: http_parser.c:46
std::vector< std::unique_ptr< IValidator > > validators_
Definition: JSONSchema.cpp:133
internal::ArgsMatcher< InnerMatcher > Args(const InnerMatcher &matcher)
std::vector< std::unique_ptr< IValidator > > itemsValidators_
Definition: JSONSchema.cpp:341
—— Concurrent Priority Queue Implementation ——
Definition: AtomicBitSet.h:29
#define FOLLY_EXPORT
Definition: CPortability.h:133
requires E e noexcept(noexcept(s.error(std::move(e))))
const dynamic & schema
Definition: JSONSchema.cpp:103
#define STRING
void split(const Delim &delimiter, const String &input, std::vector< OutputType > &out, bool ignoreEmpty)
Definition: String-inl.h:382
std::unordered_set< std::pair< const IValidator *, const dynamic * > > seen
Definition: JSONSchema.cpp:92
boost::regex regex_
Definition: JSONSchema.cpp:254
constexpr auto size(C const &c) -> decltype(c.size())
Definition: Access.h:45
bool allowAdditionalProperties_
Definition: JSONSchema.cpp:456
Type type_
Definition: JSONSchema.cpp:208
std::unique_ptr< Validator > makeValidator(const dynamic &schema)
std::vector< std::pair< std::string, std::unique_ptr< IValidator > > > schemaDep_
Definition: JSONSchema.cpp:510
std::unique_ptr< IValidator > additionalItemsValidator_
Definition: JSONSchema.cpp:342
std::vector< std::pair< std::string, std::vector< std::string > > > propertyDep_
Definition: JSONSchema.cpp:509
std::unique_ptr< IValidator > additionalPropertyValidator_
Definition: JSONSchema.cpp:455
std::enable_if<!std::is_array< T >::value, std::unique_ptr< T > >::type make_unique(Args &&...args)
Definition: Memory.h:259
std::string typeStr_
Definition: JSONSchema.cpp:556
std::vector< dynamic::Type > allowedTypes_
Definition: JSONSchema.cpp:555
const char * string
Definition: Conv.cpp:212
static set< string > s
Definition: Traits.h:577
uint64_t value(const typename LockFreeRingBuffer< T, Atom >::Cursor &rbcursor)
std::vector< std::pair< boost::regex, std::unique_ptr< IValidator > > > patternPropertyValidators_
Definition: JSONSchema.cpp:454
Range< const char * > StringPiece
std::string toJson(dynamic const &dyn)
Definition: json.cpp:915
bool exclusive_
Definition: JSONSchema.cpp:207
bool allowAdditionalItems_
Definition: JSONSchema.cpp:343
constexpr None none
Definition: Optional.h:87