initial
This commit is contained in:
parent
06d58b9e84
commit
338d8defdb
@ -39,13 +39,13 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro
|
||||
|
||||
case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
|
||||
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not be followed by other values.");
|
||||
|
||||
|
||||
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
|
||||
|
||||
|
||||
case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member.");
|
||||
case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member.");
|
||||
case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member.");
|
||||
|
||||
|
||||
case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or ']' after an array element.");
|
||||
|
||||
case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string.");
|
||||
@ -104,7 +104,7 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode val
|
||||
case kValidateErrorType: return RAPIDJSON_ERROR_STRING("Property has a type '%actual' that is not in the following list: '%expected'.");
|
||||
|
||||
case kValidateErrorOneOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'oneOf', refer to following errors.");
|
||||
case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf'.");
|
||||
case kValidateErrorOneOfMatch: return RAPIDJSON_ERROR_STRING("Property matched more than one of the sub-schemas specified by 'oneOf', indices '%matches'.");
|
||||
case kValidateErrorAllOf: return RAPIDJSON_ERROR_STRING("Property did not match all of the sub-schemas specified by 'allOf', refer to following errors.");
|
||||
case kValidateErrorAnyOf: return RAPIDJSON_ERROR_STRING("Property did not match any of the sub-schemas specified by 'anyOf', refer to following errors.");
|
||||
case kValidateErrorNot: return RAPIDJSON_ERROR_STRING("Property matched the sub-schema specified by 'not'.");
|
||||
@ -113,6 +113,57 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode val
|
||||
}
|
||||
}
|
||||
|
||||
//! Maps error code of schema document compilation into error message.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\param schemaErrorCode Error code obtained from compiling the schema document.
|
||||
\return the error message.
|
||||
\note User can make a copy of this function for localization.
|
||||
Using switch-case is safer for future modification of error codes.
|
||||
*/
|
||||
inline const RAPIDJSON_ERROR_CHARTYPE* GetSchemaError_En(SchemaErrorCode schemaErrorCode) {
|
||||
switch (schemaErrorCode) {
|
||||
case kSchemaErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
||||
|
||||
case kSchemaErrorSpecUnknown: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not recognized.");
|
||||
case kSchemaErrorSpecUnsupported: return RAPIDJSON_ERROR_STRING("JSON schema draft or OpenAPI version is not supported.");
|
||||
case kSchemaErrorSpecIllegal: return RAPIDJSON_ERROR_STRING("Both JSON schema draft and OpenAPI version found in document.");
|
||||
case kSchemaErrorStartUnknown: return RAPIDJSON_ERROR_STRING("Pointer '%value' to start of schema does not resolve to a location in the document.");
|
||||
case kSchemaErrorRefPlainName: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' must be a JSON pointer.");
|
||||
case kSchemaErrorRefInvalid: return RAPIDJSON_ERROR_STRING("$ref must not be an empty string.");
|
||||
case kSchemaErrorRefPointerInvalid: return RAPIDJSON_ERROR_STRING("$ref fragment '%value' is not a valid JSON pointer at offset '%offset'.");
|
||||
case kSchemaErrorRefUnknown: return RAPIDJSON_ERROR_STRING("$ref '%value' does not resolve to a location in the target document.");
|
||||
case kSchemaErrorRefCyclical: return RAPIDJSON_ERROR_STRING("$ref '%value' is cyclical.");
|
||||
case kSchemaErrorRefNoRemoteProvider: return RAPIDJSON_ERROR_STRING("$ref is remote but there is no remote provider.");
|
||||
case kSchemaErrorRefNoRemoteSchema: return RAPIDJSON_ERROR_STRING("$ref '%value' is remote but the remote provider did not return a schema.");
|
||||
case kSchemaErrorReadOnlyAndWriteOnly: return RAPIDJSON_ERROR_STRING("Property must not be both 'readOnly' and 'writeOnly'.");
|
||||
case kSchemaErrorRegexInvalid: return RAPIDJSON_ERROR_STRING("Invalid regular expression '%value' in 'pattern' or 'patternProperties'.");
|
||||
|
||||
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
//! Maps error code of pointer parse into error message.
|
||||
/*!
|
||||
\ingroup RAPIDJSON_ERRORS
|
||||
\param pointerParseErrorCode Error code obtained from pointer parse.
|
||||
\return the error message.
|
||||
\note User can make a copy of this function for localization.
|
||||
Using switch-case is safer for future modification of error codes.
|
||||
*/
|
||||
inline const RAPIDJSON_ERROR_CHARTYPE* GetPointerParseError_En(PointerParseErrorCode pointerParseErrorCode) {
|
||||
switch (pointerParseErrorCode) {
|
||||
case kPointerParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
|
||||
|
||||
case kPointerParseErrorTokenMustBeginWithSolidus: return RAPIDJSON_ERROR_STRING("A token must begin with a '/'.");
|
||||
case kPointerParseErrorInvalidEscape: return RAPIDJSON_ERROR_STRING("Invalid escape.");
|
||||
case kPointerParseErrorInvalidPercentEncoding: return RAPIDJSON_ERROR_STRING("Invalid percent encoding in URI fragment.");
|
||||
case kPointerParseErrorCharacterMustPercentEncode: return RAPIDJSON_ERROR_STRING("A character must be percent encoded in a URI fragment.");
|
||||
|
||||
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
}
|
||||
}
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
|
@ -185,8 +185,8 @@ enum ValidateErrorCode {
|
||||
kValidateErrorPatternProperties, //!< See other errors.
|
||||
kValidateErrorDependencies, //!< Object has missing property or schema dependencies.
|
||||
|
||||
kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values
|
||||
kValidateErrorType, //!< Property has a type that is not allowed by the schema..
|
||||
kValidateErrorEnum, //!< Property has a value that is not one of its allowed enumerated values.
|
||||
kValidateErrorType, //!< Property has a type that is not allowed by the schema.
|
||||
|
||||
kValidateErrorOneOf, //!< Property did not match any of the sub-schemas specified by 'oneOf'.
|
||||
kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'.
|
||||
@ -207,6 +207,72 @@ enum ValidateErrorCode {
|
||||
*/
|
||||
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetValidateErrorFunc)(ValidateErrorCode);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// SchemaErrorCode
|
||||
|
||||
//! Error codes when validating.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
\see GenericSchemaValidator
|
||||
*/
|
||||
enum SchemaErrorCode {
|
||||
kSchemaErrorNone = 0, //!< No error.
|
||||
|
||||
kSchemaErrorSpecUnknown, //!< JSON schema draft or OpenAPI version is not recognized
|
||||
kSchemaErrorSpecUnsupported, //!< JSON schema draft or OpenAPI version is not supported
|
||||
kSchemaErrorSpecIllegal, //!< Both JSON schema draft and OpenAPI version found in document
|
||||
kSchemaErrorStartUnknown, //!< Pointer to start of schema does not resolve to a location in the document
|
||||
kSchemaErrorRefPlainName, //!< $ref fragment must be a JSON pointer
|
||||
kSchemaErrorRefInvalid, //!< $ref must not be an empty string
|
||||
kSchemaErrorRefPointerInvalid, //!< $ref fragment is not a valid JSON pointer at offset
|
||||
kSchemaErrorRefUnknown, //!< $ref does not resolve to a location in the target document
|
||||
kSchemaErrorRefCyclical, //!< $ref is cyclical
|
||||
kSchemaErrorRefNoRemoteProvider, //!< $ref is remote but there is no remote provider
|
||||
kSchemaErrorRefNoRemoteSchema, //!< $ref is remote but the remote provider did not return a schema
|
||||
kSchemaErrorReadOnlyAndWriteOnly, //!< Property must not be both 'readOnly' and 'writeOnly'
|
||||
kSchemaErrorRegexInvalid //!< Invalid regular expression in 'pattern' or 'patternProperties'
|
||||
};
|
||||
|
||||
//! Function pointer type of GetSchemaError().
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
|
||||
This is the prototype for \c GetSchemaError_X(), where \c X is a locale.
|
||||
User can dynamically change locale in runtime, e.g.:
|
||||
\code
|
||||
GetSchemaErrorFunc GetSchemaError = GetSchemaError_En; // or whatever
|
||||
const RAPIDJSON_ERROR_CHARTYPE* s = GetSchemaError(validator.GetInvalidSchemaCode());
|
||||
\endcode
|
||||
*/
|
||||
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetSchemaErrorFunc)(SchemaErrorCode);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// PointerParseErrorCode
|
||||
|
||||
//! Error code of JSON pointer parsing.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
\see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode
|
||||
*/
|
||||
enum PointerParseErrorCode {
|
||||
kPointerParseErrorNone = 0, //!< The parse is successful
|
||||
|
||||
kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/'
|
||||
kPointerParseErrorInvalidEscape, //!< Invalid escape
|
||||
kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment
|
||||
kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment
|
||||
};
|
||||
|
||||
//! Function pointer type of GetPointerParseError().
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
|
||||
This is the prototype for \c GetPointerParseError_X(), where \c X is a locale.
|
||||
User can dynamically change locale in runtime, e.g.:
|
||||
\code
|
||||
GetPointerParseErrorFunc GetPointerParseError = GetPointerParseError_En; // or whatever
|
||||
const RAPIDJSON_ERROR_CHARTYPE* s = GetPointerParseError(pointer.GetParseErrorCode());
|
||||
\endcode
|
||||
*/
|
||||
typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetPointerParseErrorFunc)(PointerParseErrorCode);
|
||||
|
||||
|
||||
RAPIDJSON_NAMESPACE_END
|
||||
|
||||
#ifdef __clang__
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "document.h"
|
||||
#include "uri.h"
|
||||
#include "internal/itoa.h"
|
||||
#include "error/error.h" // PointerParseErrorCode
|
||||
|
||||
#ifdef __clang__
|
||||
RAPIDJSON_DIAG_PUSH
|
||||
@ -31,19 +32,6 @@ RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
static const SizeType kPointerInvalidIndex = ~SizeType(0); //!< Represents an invalid index in GenericPointer::Token
|
||||
|
||||
//! Error code of parsing.
|
||||
/*! \ingroup RAPIDJSON_ERRORS
|
||||
\see GenericPointer::GenericPointer, GenericPointer::GetParseErrorCode
|
||||
*/
|
||||
enum PointerParseErrorCode {
|
||||
kPointerParseErrorNone = 0, //!< The parse is successful
|
||||
|
||||
kPointerParseErrorTokenMustBeginWithSolidus, //!< A token must begin with a '/'
|
||||
kPointerParseErrorInvalidEscape, //!< Invalid escape
|
||||
kPointerParseErrorInvalidPercentEncoding, //!< Invalid percent encoding in URI fragment
|
||||
kPointerParseErrorCharacterMustPercentEncode //!< A character must percent encoded in URI fragment
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// GenericPointer
|
||||
|
||||
|
@ -234,7 +234,8 @@ public:
|
||||
virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0;
|
||||
virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0;
|
||||
virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
|
||||
virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched) = 0;
|
||||
virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
|
||||
virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0;
|
||||
virtual void Disallowed() = 0;
|
||||
};
|
||||
|
||||
@ -594,8 +595,9 @@ public:
|
||||
|
||||
for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
|
||||
new (&patternProperties_[patternPropertyCount_]) PatternProperty();
|
||||
patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name);
|
||||
schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, q.Append(itr->name, allocator_), itr->value, document, id_);
|
||||
PointerType r = q.Append(itr->name, allocator_);
|
||||
patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name, schemaDocument, r);
|
||||
schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, r, itr->value, document, id_);
|
||||
patternPropertyCount_++;
|
||||
}
|
||||
}
|
||||
@ -675,7 +677,7 @@ public:
|
||||
AssignIfExist(maxLength_, value, GetMaxLengthString());
|
||||
|
||||
if (const ValueType* v = GetMember(value, GetPatternString()))
|
||||
pattern_ = CreatePattern(*v);
|
||||
pattern_ = CreatePattern(*v, schemaDocument, p.Append(GetPatternString(), allocator_));
|
||||
|
||||
// Number
|
||||
if (const ValueType* v = GetMember(value, GetMinimumString()))
|
||||
@ -828,16 +830,19 @@ public:
|
||||
|
||||
if (oneOf_.schemas) {
|
||||
bool oneValid = false;
|
||||
SizeType firstMatch = 0;
|
||||
for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
|
||||
if (context.validators[i]->IsValid()) {
|
||||
if (oneValid) {
|
||||
context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, true);
|
||||
context.error_handler.MultipleOneOf(firstMatch, i - oneOf_.begin);
|
||||
RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch);
|
||||
} else
|
||||
} else {
|
||||
oneValid = true;
|
||||
firstMatch = i - oneOf_.begin;
|
||||
}
|
||||
}
|
||||
if (!oneValid) {
|
||||
context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count, false);
|
||||
context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
|
||||
RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf);
|
||||
}
|
||||
}
|
||||
@ -1247,10 +1252,11 @@ private:
|
||||
|
||||
#if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
|
||||
template <typename ValueType>
|
||||
RegexType* CreatePattern(const ValueType& value) {
|
||||
RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) {
|
||||
if (value.IsString()) {
|
||||
RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_);
|
||||
if (!r->IsValid()) {
|
||||
sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength());
|
||||
r->~RegexType();
|
||||
AllocatorType::Free(r);
|
||||
r = 0;
|
||||
@ -1266,13 +1272,14 @@ private:
|
||||
}
|
||||
#elif RAPIDJSON_SCHEMA_USE_STDREGEX
|
||||
template <typename ValueType>
|
||||
RegexType* CreatePattern(const ValueType& value) {
|
||||
RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) {
|
||||
if (value.IsString()) {
|
||||
RegexType *r = static_cast<RegexType*>(allocator_->Malloc(sizeof(RegexType)));
|
||||
try {
|
||||
return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
|
||||
}
|
||||
catch (const std::regex_error&) {
|
||||
catch (const std::regex_error& e) {
|
||||
sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength());
|
||||
AllocatorType::Free(r);
|
||||
}
|
||||
}
|
||||
@ -1285,7 +1292,9 @@ private:
|
||||
}
|
||||
#else
|
||||
template <typename ValueType>
|
||||
RegexType* CreatePattern(const ValueType&) { return 0; }
|
||||
RegexType* CreatePattern(const ValueType&) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
|
||||
#endif // RAPIDJSON_SCHEMA_USE_STDREGEX
|
||||
@ -1632,8 +1641,9 @@ public:
|
||||
typedef typename EncodingType::Ch Ch;
|
||||
typedef internal::Schema<GenericSchemaDocument> SchemaType;
|
||||
typedef GenericPointer<ValueType, Allocator> PointerType;
|
||||
typedef GenericValue<EncodingType, AllocatorType> SValue;
|
||||
typedef GenericValue<EncodingType, AllocatorType> GValue;
|
||||
typedef GenericUri<ValueType, Allocator> UriType;
|
||||
typedef GenericStringRef<Ch> StringRefType;
|
||||
friend class internal::Schema<GenericSchemaDocument>;
|
||||
template <typename, typename, typename>
|
||||
friend class GenericSchemaValidator;
|
||||
@ -1658,7 +1668,9 @@ public:
|
||||
root_(),
|
||||
typeless_(),
|
||||
schemaMap_(allocator, kInitialSchemaMapSize),
|
||||
schemaRef_(allocator, kInitialSchemaRefSize)
|
||||
schemaRef_(allocator, kInitialSchemaRefSize),
|
||||
error_(kObjectType),
|
||||
currentError_()
|
||||
{
|
||||
if (!allocator_)
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
||||
@ -1680,6 +1692,11 @@ public:
|
||||
else if (const ValueType* v = pointer.Get(document)) {
|
||||
CreateSchema(&root_, pointer, *v, document, docId_);
|
||||
}
|
||||
else {
|
||||
GenericStringBuffer<EncodingType> sb;
|
||||
pointer.StringifyUriFragment(sb);
|
||||
SchemaErrorValue(kSchemaErrorStartUnknown, PointerType(), sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)));
|
||||
}
|
||||
|
||||
RAPIDJSON_ASSERT(root_ != 0);
|
||||
|
||||
@ -1697,7 +1714,9 @@ public:
|
||||
schemaMap_(std::move(rhs.schemaMap_)),
|
||||
schemaRef_(std::move(rhs.schemaRef_)),
|
||||
uri_(std::move(rhs.uri_)),
|
||||
docId_(rhs.docId_)
|
||||
docId_(rhs.docId_),
|
||||
error_(std::move(rhs.error_)),
|
||||
currentError_(std::move(rhs.currentError_))
|
||||
{
|
||||
rhs.remoteProvider_ = 0;
|
||||
rhs.allocator_ = 0;
|
||||
@ -1719,12 +1738,52 @@ public:
|
||||
RAPIDJSON_DELETE(ownAllocator_);
|
||||
}
|
||||
|
||||
const SValue& GetURI() const { return uri_; }
|
||||
const GValue& GetURI() const { return uri_; }
|
||||
|
||||
//! Get the root schema.
|
||||
const SchemaType& GetRoot() const { return *root_; }
|
||||
|
||||
private:
|
||||
//! Gets the error object.
|
||||
GValue& GetError() { return error_; }
|
||||
const GValue& GetError() const { return error_; }
|
||||
|
||||
static const StringRefType& GetSchemaErrorKeyword(SchemaErrorCode schemaErrorCode) {
|
||||
switch (schemaErrorCode) {
|
||||
case kSchemaErrorStartUnknown: return GetStartUnknownString();
|
||||
case kSchemaErrorRefPlainName: return GetRefPlainNameString();
|
||||
case kSchemaErrorRefInvalid: return GetRefInvalidString();
|
||||
case kSchemaErrorRefPointerInvalid: return GetRefPointerInvalidString();
|
||||
case kSchemaErrorRefUnknown: return GetRefUnknownString();
|
||||
case kSchemaErrorRefCyclical: return GetRefCyclicalString();
|
||||
case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString();
|
||||
case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString();
|
||||
case kSchemaErrorRegexInvalid: return GetRegexInvalidString();
|
||||
default: return GetNullString();
|
||||
}
|
||||
}
|
||||
|
||||
//! Default error method
|
||||
void SchemaError(const SchemaErrorCode code, const PointerType& location) {
|
||||
currentError_ = GValue(kObjectType);
|
||||
AddCurrentError(code, location);
|
||||
}
|
||||
|
||||
//! Method for error with single string value insert
|
||||
void SchemaErrorValue(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length) {
|
||||
currentError_ = GValue(kObjectType);
|
||||
currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_);
|
||||
AddCurrentError(code, location);
|
||||
}
|
||||
|
||||
//! Method for error with invalid pointer
|
||||
void SchemaErrorPointer(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length, const PointerType& pointer) {
|
||||
currentError_ = GValue(kObjectType);
|
||||
currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_);
|
||||
currentError_.AddMember(GetOffsetString(), static_cast<SizeType>(pointer.GetParseErrorOffset() / sizeof(Ch)), *allocator_);
|
||||
AddCurrentError(code, location);
|
||||
}
|
||||
|
||||
private:
|
||||
//! Prohibit copying
|
||||
GenericSchemaDocument(const GenericSchemaDocument&);
|
||||
//! Prohibit assignment
|
||||
@ -1745,6 +1804,58 @@ private:
|
||||
bool owned;
|
||||
};
|
||||
|
||||
void AddErrorInstanceLocation(GValue& result, const PointerType& location) {
|
||||
GenericStringBuffer<EncodingType> sb;
|
||||
location.StringifyUriFragment(sb);
|
||||
GValue instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)), *allocator_);
|
||||
result.AddMember(GetInstanceRefString(), instanceRef, *allocator_);
|
||||
}
|
||||
|
||||
void AddError(GValue& keyword, GValue& error) {
|
||||
typename GValue::MemberIterator member = error_.FindMember(keyword);
|
||||
if (member == error_.MemberEnd())
|
||||
error_.AddMember(keyword, error, *allocator_);
|
||||
else {
|
||||
if (member->value.IsObject()) {
|
||||
GValue errors(kArrayType);
|
||||
errors.PushBack(member->value, *allocator_);
|
||||
member->value = errors;
|
||||
}
|
||||
member->value.PushBack(error, *allocator_);
|
||||
}
|
||||
}
|
||||
|
||||
void AddCurrentError(const SchemaErrorCode code, const PointerType& location) {
|
||||
currentError_.AddMember(GetErrorCodeString(), code, *allocator_);
|
||||
AddErrorInstanceLocation(currentError_, location);
|
||||
AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_);
|
||||
}
|
||||
|
||||
#define RAPIDJSON_STRING_(name, ...) \
|
||||
static const StringRefType& Get##name##String() {\
|
||||
static const Ch s[] = { __VA_ARGS__, '\0' };\
|
||||
static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
|
||||
return v;\
|
||||
}
|
||||
|
||||
RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
|
||||
RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
|
||||
RAPIDJSON_STRING_(Value, 'v', 'a', 'l', 'u', 'e')
|
||||
RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't')
|
||||
|
||||
RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
|
||||
RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
|
||||
RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e')
|
||||
RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
|
||||
RAPIDJSON_STRING_(RefPointerInvalid, 'R', 'e', 'f', 'P', 'o', 'i', 'n', 't', 'e', 'r', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
|
||||
RAPIDJSON_STRING_(RefUnknown, 'R', 'e', 'f', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
|
||||
RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l')
|
||||
RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r')
|
||||
RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a')
|
||||
RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
|
||||
|
||||
#undef RAPIDJSON_STRING_
|
||||
|
||||
// Changed by PR #1393
|
||||
void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
|
||||
if (v.GetType() == kObjectType) {
|
||||
@ -1795,7 +1906,9 @@ private:
|
||||
|
||||
if (itr->value.IsString()) {
|
||||
SizeType len = itr->value.GetStringLength();
|
||||
if (len > 0) {
|
||||
if (len == 0)
|
||||
SchemaError(kSchemaErrorRefInvalid, source);
|
||||
else {
|
||||
// First resolve $ref against the in-scope id
|
||||
UriType scopeId = UriType(id, allocator_);
|
||||
UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_);
|
||||
@ -1805,26 +1918,32 @@ private:
|
||||
const ValueType *base = FindId(document, ref, basePointer, docId_, false);
|
||||
if (!base) {
|
||||
// Remote reference - call the remote document provider
|
||||
if (remoteProvider_) {
|
||||
if (!remoteProvider_)
|
||||
SchemaError(kSchemaErrorRefNoRemoteProvider, source);
|
||||
else {
|
||||
if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref)) {
|
||||
const Ch* s = ref.GetFragString();
|
||||
len = ref.GetFragStringLength();
|
||||
if (len <= 1 || s[1] == '/') {
|
||||
// JSON pointer fragment, absolute in the remote schema
|
||||
const PointerType pointer(s, len, allocator_);
|
||||
if (pointer.IsValid()) {
|
||||
if (!pointer.IsValid())
|
||||
SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, pointer);
|
||||
else {
|
||||
// Get the subschema
|
||||
if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) {
|
||||
if (schema)
|
||||
*schema = sc;
|
||||
AddSchemaRefs(const_cast<SchemaType *>(sc));
|
||||
return true;
|
||||
}
|
||||
} else
|
||||
SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
|
||||
}
|
||||
} else {
|
||||
// Plain name fragment, not allowed
|
||||
}
|
||||
}
|
||||
} else
|
||||
// Plain name fragment, not allowed in remote schema
|
||||
SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len);
|
||||
} else
|
||||
SchemaErrorValue(kSchemaErrorRefNoRemoteSchema, source, ref.GetString(), ref.GetStringLength());
|
||||
}
|
||||
}
|
||||
else { // Local reference
|
||||
@ -1833,16 +1952,18 @@ private:
|
||||
if (len <= 1 || s[1] == '/') {
|
||||
// JSON pointer fragment, relative to the resolved URI
|
||||
const PointerType relPointer(s, len, allocator_);
|
||||
if (relPointer.IsValid()) {
|
||||
if (!relPointer.IsValid())
|
||||
SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, relPointer);
|
||||
else {
|
||||
// Get the subschema
|
||||
if (const ValueType *pv = relPointer.Get(*base)) {
|
||||
// Now get the absolute JSON pointer by adding relative to base
|
||||
PointerType pointer(basePointer);
|
||||
for (SizeType i = 0; i < relPointer.GetTokenCount(); i++)
|
||||
pointer = pointer.Append(relPointer.GetTokens()[i], allocator_);
|
||||
//GenericStringBuffer<EncodingType> sb;
|
||||
//pointer.StringifyUriFragment(sb);
|
||||
if (pointer.IsValid() && !IsCyclicRef(pointer)) {
|
||||
if (IsCyclicRef(pointer))
|
||||
SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength());
|
||||
else {
|
||||
// Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
|
||||
// TODO: cache pointer <-> id mapping
|
||||
size_t unresolvedTokenIndex;
|
||||
@ -1850,17 +1971,18 @@ private:
|
||||
CreateSchema(schema, pointer, *pv, document, scopeId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else
|
||||
SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
|
||||
}
|
||||
} else {
|
||||
// Plain name fragment, relative to the resolved URI
|
||||
PointerType pointer = PointerType();
|
||||
// See if the fragment matches an id in this document.
|
||||
// Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer.
|
||||
PointerType pointer = PointerType();
|
||||
if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) {
|
||||
if (!IsCyclicRef(pointer)) {
|
||||
//GenericStringBuffer<EncodingType> sb;
|
||||
//pointer.StringifyUriFragment(sb);
|
||||
if (IsCyclicRef(pointer))
|
||||
SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength());
|
||||
else {
|
||||
// Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
|
||||
// TODO: cache pointer <-> id mapping
|
||||
size_t unresolvedTokenIndex;
|
||||
@ -1868,7 +1990,8 @@ private:
|
||||
CreateSchema(schema, pointer, *pv, document, scopeId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else
|
||||
SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1965,8 +2088,10 @@ private:
|
||||
SchemaType* typeless_;
|
||||
internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
|
||||
internal::Stack<Allocator> schemaRef_; // Stores Pointer(s) from $ref(s) until resolved
|
||||
SValue uri_; // Schema document URI
|
||||
GValue uri_; // Schema document URI
|
||||
UriType docId_;
|
||||
GValue error_;
|
||||
GValue currentError_;
|
||||
};
|
||||
|
||||
//! GenericSchemaDocument using Value type.
|
||||
@ -2099,13 +2224,12 @@ public:
|
||||
return flags_;
|
||||
}
|
||||
|
||||
//! Checks whether the current state is valid.
|
||||
// Implementation of ISchemaValidator
|
||||
virtual bool IsValid() const {
|
||||
if (!valid_) return false;
|
||||
if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false;
|
||||
return true;
|
||||
}
|
||||
//! End of Implementation of ISchemaValidator
|
||||
|
||||
//! Gets the error object.
|
||||
ValueType& GetError() { return error_; }
|
||||
@ -2313,8 +2437,16 @@ public:
|
||||
void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
|
||||
AddErrorArray(kValidateErrorAnyOf, subvalidators, count);
|
||||
}
|
||||
void NotOneOf(ISchemaValidator** subvalidators, SizeType count, bool matched = false) {
|
||||
AddErrorArray(matched ? kValidateErrorOneOfMatch : kValidateErrorOneOf, subvalidators, count);
|
||||
void NotOneOf(ISchemaValidator** subvalidators, SizeType count) {
|
||||
AddErrorArray(kValidateErrorOneOf, subvalidators, count);
|
||||
}
|
||||
void MultipleOneOf(SizeType index1, SizeType index2) {
|
||||
ValueType matches(kArrayType);
|
||||
matches.PushBack(index1, GetStateAllocator());
|
||||
matches.PushBack(index2, GetStateAllocator());
|
||||
currentError_.SetObject();
|
||||
currentError_.AddMember(GetMatchesString(), matches, GetStateAllocator());
|
||||
AddCurrentError(kValidateErrorOneOfMatch);
|
||||
}
|
||||
void Disallowed() {
|
||||
currentError_.SetObject();
|
||||
@ -2338,6 +2470,7 @@ public:
|
||||
RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
|
||||
RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e')
|
||||
RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
|
||||
RAPIDJSON_STRING_(Matches, 'm', 'a', 't', 'c', 'h', 'e', 's')
|
||||
|
||||
#undef RAPIDJSON_STRING_
|
||||
|
||||
@ -2482,6 +2615,7 @@ RAPIDJSON_MULTILINEMACRO_END
|
||||
virtual void FreeState(void* p) {
|
||||
StateAllocator::Free(p);
|
||||
}
|
||||
// End of implementation of ISchemaStateFactory<SchemaType>
|
||||
|
||||
private:
|
||||
typedef typename SchemaType::Context Context;
|
||||
|
@ -112,6 +112,12 @@ TEST(SchemaValidator, Hasher) {
|
||||
|
||||
#define VALIDATE(schema, json, expected) \
|
||||
{\
|
||||
VALIDATE_(schema, json, expected, true) \
|
||||
}
|
||||
|
||||
#define VALIDATE_(schema, json, expected, expected2) \
|
||||
{\
|
||||
EXPECT_TRUE(expected2 == schema.GetError().ObjectEmpty());\
|
||||
SchemaValidator validator(schema);\
|
||||
Document d;\
|
||||
/*printf("\n%s\n", json);*/\
|
||||
@ -149,6 +155,7 @@ TEST(SchemaValidator, Hasher) {
|
||||
#define INVALIDATE_(schema, json, invalidSchemaPointer, invalidSchemaKeyword, invalidDocumentPointer, error, \
|
||||
flags, SchemaValidatorType, PointerType) \
|
||||
{\
|
||||
EXPECT_TRUE(schema.GetError().ObjectEmpty());\
|
||||
SchemaValidatorType validator(schema);\
|
||||
validator.SetValidateFlags(flags);\
|
||||
Document d;\
|
||||
@ -188,6 +195,20 @@ TEST(SchemaValidator, Hasher) {
|
||||
}\
|
||||
}
|
||||
|
||||
// Use for checking whether a compiled schema document contains errors
|
||||
#define SCHEMAERROR(schema, error) \
|
||||
{\
|
||||
Document e;\
|
||||
e.Parse(error);\
|
||||
if (schema.GetError() != e) {\
|
||||
StringBuffer sb;\
|
||||
Writer<StringBuffer> w(sb);\
|
||||
schema.GetError().Accept(w);\
|
||||
printf("GetError() Expected: %s Actual: %s\n", error, sb.GetString());\
|
||||
ADD_FAILURE();\
|
||||
}\
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Typeless) {
|
||||
Document sd;
|
||||
sd.Parse("{}");
|
||||
@ -223,7 +244,7 @@ TEST(SchemaValidator, Enum_Typed) {
|
||||
"{ \"enum\": { \"errorCode\": 19, \"instanceRef\": \"#\", \"schemaRef\": \"#\" }}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Enum_Typless) {
|
||||
TEST(SchemaValidator, Enum_Typeless) {
|
||||
Document sd;
|
||||
sd.Parse("{ \"enum\": [\"red\", \"amber\", \"green\", null, 42] }");
|
||||
SchemaDocument s(sd);
|
||||
@ -333,7 +354,7 @@ TEST(SchemaValidator, OneOf) {
|
||||
" ]"
|
||||
"}}");
|
||||
INVALIDATE(s, "15", "", "oneOf", "",
|
||||
"{ \"oneOf\": { \"errorCode\": 22, \"instanceRef\": \"#\", \"schemaRef\": \"#\", \"errors\": [{}, {}]}}");
|
||||
"{ \"oneOf\": { \"errorCode\": 22, \"instanceRef\": \"#\", \"schemaRef\": \"#\", \"matches\": [0,1]}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Not) {
|
||||
@ -502,12 +523,13 @@ TEST(SchemaValidator, String_Pattern) {
|
||||
|
||||
TEST(SchemaValidator, String_Pattern_Invalid) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\":\"string\",\"pattern\":\"a{0}\"}"); // TODO: report regex is invalid somehow
|
||||
sd.Parse("{\"type\":\"string\",\"pattern\":\"a{0}\"}");
|
||||
SchemaDocument s(sd);
|
||||
SCHEMAERROR(s, "{\"RegexInvalid\":{\"errorCode\":9,\"instanceRef\":\"#/pattern\",\"value\":\"a{0}\"}}");
|
||||
|
||||
VALIDATE(s, "\"\"", true);
|
||||
VALIDATE(s, "\"a\"", true);
|
||||
VALIDATE(s, "\"aa\"", true);
|
||||
VALIDATE_(s, "\"\"", true, false);
|
||||
VALIDATE_(s, "\"a\"", true, false);
|
||||
VALIDATE_(s, "\"aa\"", true, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1886,12 +1908,6 @@ TEST(SchemaValidator, SchemaPointer) {
|
||||
" },"
|
||||
" \"f\": {"
|
||||
" \"type\": \"boolean\""
|
||||
" },"
|
||||
" \"cyclic_source\": {"
|
||||
" \"$ref\": \"#/definitions/Resp_200/properties/cyclic_target\""
|
||||
" },"
|
||||
" \"cyclic_target\": {"
|
||||
" \"$ref\": \"#/definitions/Resp_200/properties/cyclic_source\""
|
||||
" }"
|
||||
" },"
|
||||
" \"type\": \"object\""
|
||||
@ -2390,7 +2406,9 @@ TEST(SchemaValidator, Issue728_AllOfRef) {
|
||||
Document sd;
|
||||
sd.Parse("{\"allOf\": [{\"$ref\": \"#/abc\"}]}");
|
||||
SchemaDocument s(sd);
|
||||
VALIDATE(s, "{\"key1\": \"abc\", \"key2\": \"def\"}", true);
|
||||
SCHEMAERROR(s, "{\"RefUnknown\":{\"errorCode\":5,\"instanceRef\":\"#/allOf/0\",\"value\":\"#/abc\"}}");
|
||||
|
||||
VALIDATE_(s, "{\"key1\": \"abc\", \"key2\": \"def\"}", true, false);
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Issue1017_allOfHandler) {
|
||||
@ -2625,7 +2643,7 @@ TEST(SchemaValidator, Ref_remote_issue1210) {
|
||||
SchemaDocumentProvider(SchemaDocument** collection) : collection(collection) { }
|
||||
virtual const SchemaDocument* GetRemoteDocument(const char* uri, SizeType length) {
|
||||
int i = 0;
|
||||
while (collection[i] && SchemaDocument::SValue(uri, length) != collection[i]->GetURI()) ++i;
|
||||
while (collection[i] && SchemaDocument::GValue(uri, length) != collection[i]->GetURI()) ++i;
|
||||
return collection[i];
|
||||
}
|
||||
};
|
||||
@ -2656,7 +2674,7 @@ TEST(SchemaValidator, ContinueOnErrors) {
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
VALIDATE(s, "{\"version\": 1.0, \"address\": {\"number\": 24, \"street1\": \"The Woodlands\", \"street3\": \"Ham\", \"city\": \"Romsey\", \"area\": \"Kent\", \"country\": \"UK\", \"postcode\": \"SO51 0GP\"}, \"phones\": [\"0111-222333\", \"0777-666888\"], \"names\": [\"Fred\", \"Bloggs\"]}", true);
|
||||
INVALIDATE_(s, "{\"version\": 1.01, \"address\": {\"number\": 0, \"street2\": false, \"street3\": \"Ham\", \"city\": \"RomseyTownFC\", \"area\": \"BC\", \"country\": \"USA\", \"postcode\": \"999ABC\"}, \"phones\": [], \"planet\": \"Earth\", \"extra\": {\"S_xxx\": 123}}", "#", "errors", "#",
|
||||
INVALIDATE_(s, "{\"version\": 1.01, \"address\": {\"number\": 0, \"street2\": false, \"street3\": \"Ham\", \"city\": \"RomseyTownFC\", \"area\": \"Narnia\", \"country\": \"USA\", \"postcode\": \"999ABC\"}, \"phones\": [], \"planet\": \"Earth\", \"extra\": {\"S_xxx\": 123}}", "#", "errors", "#",
|
||||
"{ \"multipleOf\": {"
|
||||
" \"errorCode\": 1, \"instanceRef\": \"#/version\", \"schemaRef\": \"#/definitions/decimal_type\", \"expected\": 1.0, \"actual\": 1.01"
|
||||
" },"
|
||||
@ -2691,6 +2709,9 @@ TEST(SchemaValidator, ContinueOnErrors) {
|
||||
" },"
|
||||
" \"required\": {"
|
||||
" \"missing\": [\"street1\"], \"errorCode\": 15, \"instanceRef\": \"#/address\", \"schemaRef\": \"#/definitions/address_type\""
|
||||
" },"
|
||||
" \"oneOf\": {"
|
||||
" \"matches\": [0, 1], \"errorCode\": 22, \"instanceRef\": \"#/address/area\", \"schemaRef\": \"#/definitions/address_type/properties/area\""
|
||||
" }"
|
||||
"}",
|
||||
kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
|
||||
@ -2917,7 +2938,7 @@ TEST(SchemaValidator, ContinueOnErrors_RogueString) {
|
||||
|
||||
// Test that when kValidateContinueOnErrorFlag is set, an incorrect simple type with a sub-schema is handled correctly.
|
||||
// This tests that we don't blow up when there is a type mismatch but there is a sub-schema present
|
||||
TEST(SchemaValidator, ContinueOnErrors_Issue2) {
|
||||
TEST(SchemaValidator, ContinueOnErrors_BadSimpleType) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\":\"string\", \"anyOf\":[{\"maxLength\":2}]}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
@ -2943,10 +2964,148 @@ TEST(SchemaValidator, ContinueOnErrors_Issue2) {
|
||||
kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidator, Pointer);
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnknownError) {
|
||||
|
||||
TEST(SchemaValidator, UnknownValidationError) {
|
||||
ASSERT_TRUE(SchemaValidator::SchemaType::GetValidateErrorKeyword(kValidateErrors).GetString() == std::string("null"));
|
||||
}
|
||||
|
||||
// The first occurrence of a duplicate keyword is taken
|
||||
TEST(SchemaValidator, DuplicateKeyword) {
|
||||
Document sd;
|
||||
sd.Parse("{ \"title\": \"test\",\"type\": \"number\", \"type\": \"string\" }");
|
||||
EXPECT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
VALIDATE(s, "42", true);
|
||||
INVALIDATE(s, "\"Life, the universe, and everything\"", "", "type", "",
|
||||
"{ \"type\": {"
|
||||
" \"errorCode\": 20,"
|
||||
" \"instanceRef\": \"#\", \"schemaRef\": \"#\","
|
||||
" \"expected\": [\"number\"], \"actual\": \"string\""
|
||||
"}}");
|
||||
}
|
||||
|
||||
|
||||
// SchemaDocument tests
|
||||
|
||||
TEST(SchemaValidator, Schema_StartUnknown) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, SchemaDocument::PointerType("/nowhere"));
|
||||
SCHEMAERROR(s, "{\"StartUnknown\":{\"errorCode\":1,\"instanceRef\":\"#\", \"value\":\"#/nowhere\"}}");
|
||||
}
|
||||
|
||||
// $ref is a non-JSON pointer fragment - not allowed when OpenAPI
|
||||
TEST(SchemaValidator, Schema_RefPlainNameOpenApi) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"swagger\": \"2.0\", \"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"#myId\"}, \"myStr\": {\"type\": \"string\", \"id\": \"#myStrId\"}, \"myInt2\": {\"type\": \"integer\", \"id\": \"#myId\"}}}");
|
||||
SchemaDocumentType s(sd);
|
||||
SCHEMAERROR(s, "{\"RefPlainName\":{\"errorCode\":2,\"instanceRef\":\"#/properties/myInt1\",\"value\":\"#myId\"}}");
|
||||
}
|
||||
|
||||
// $ref is a non-JSON pointer fragment - not allowed when remote document
|
||||
TEST(SchemaValidator, Schema_RefPlainNameRemote) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#plainname\"}}}");
|
||||
SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider);
|
||||
SCHEMAERROR(s, "{\"RefPlainName\":{\"errorCode\":2,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#plainname\"}}");
|
||||
}
|
||||
|
||||
// $ref is an empty string
|
||||
TEST(SchemaValidator, Schema_RefEmptyString) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"\"}}}");
|
||||
SchemaDocumentType s(sd);
|
||||
SCHEMAERROR(s, "{\"RefInvalid\":{\"errorCode\":3,\"instanceRef\":\"#/properties/myInt1\"}}");
|
||||
}
|
||||
|
||||
// $ref is remote but no provider
|
||||
TEST(SchemaValidator, Schema_RefNoRemoteProvider) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#plainname\"}}}");
|
||||
SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, 0);
|
||||
SCHEMAERROR(s, "{\"RefNoRemoteProvider\":{\"errorCode\":7,\"instanceRef\":\"#/properties/myInt\"}}");
|
||||
}
|
||||
|
||||
// $ref is remote but no schema returned
|
||||
TEST(SchemaValidator, Schema_RefNoRemoteSchema) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/will-not-resolve.json\"}}}");
|
||||
SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider);
|
||||
SCHEMAERROR(s, "{\"RefNoRemoteSchema\":{\"errorCode\":8,\"instanceRef\":\"#/properties/myInt\",\"value\":\"http://localhost:1234/will-not-resolve.json\"}}");
|
||||
}
|
||||
|
||||
// $ref pointer is invalid
|
||||
TEST(SchemaValidator, Schema_RefPointerInvalid) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"#/&&&&&\"}}}");
|
||||
SchemaDocumentType s(sd);
|
||||
SCHEMAERROR(s, "{\"RefPointerInvalid\":{\"errorCode\":4,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#/&&&&&\",\"offset\":2}}");
|
||||
}
|
||||
|
||||
// $ref is remote and pointer is invalid
|
||||
TEST(SchemaValidator, Schema_RefPointerInvalidRemote) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/abc&&&&&\"}}}");
|
||||
SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider);
|
||||
SCHEMAERROR(s, "{\"RefPointerInvalid\":{\"errorCode\":4,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#/abc&&&&&\",\"offset\":5}}");
|
||||
}
|
||||
|
||||
// $ref is unknown non-pointer
|
||||
TEST(SchemaValidator, Schema_RefUnknownPlainName) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"#plainname\"}}}");
|
||||
SchemaDocumentType s(sd);
|
||||
SCHEMAERROR(s, "{\"RefUnknown\":{\"errorCode\":5,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#plainname\"}}");
|
||||
}
|
||||
|
||||
/// $ref is unknown pointer
|
||||
TEST(SchemaValidator, Schema_RefUnknownPointer) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"#/a/b\"}}}");
|
||||
SchemaDocumentType s(sd);
|
||||
SCHEMAERROR(s, "{\"RefUnknown\":{\"errorCode\":5,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#/a/b\"}}");
|
||||
}
|
||||
|
||||
// $ref is remote and unknown pointer
|
||||
TEST(SchemaValidator, Schema_RefUnknownPointerRemote) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"/subSchemas.json#/a/b\"}}}");
|
||||
SchemaDocumentType s(sd, "http://localhost:1234/xxxx", 26, &provider);
|
||||
SCHEMAERROR(s, "{\"RefUnknown\":{\"errorCode\":5,\"instanceRef\":\"#/properties/myInt\",\"value\":\"http://localhost:1234/subSchemas.json#/a/b\"}}");
|
||||
}
|
||||
|
||||
// $ref is cyclical
|
||||
TEST(SchemaValidator, Schema_RefCyclical) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {"
|
||||
" \"cyclic_source\": {"
|
||||
" \"$ref\": \"#/properties/cyclic_target\""
|
||||
" },"
|
||||
" \"cyclic_target\": {"
|
||||
" \"$ref\": \"#/properties/cyclic_source\""
|
||||
" }"
|
||||
"}}");
|
||||
SchemaDocumentType s(sd);
|
||||
SCHEMAERROR(s, "{\"RefCyclical\":{\"errorCode\":6,\"instanceRef\":\"#/properties/cyclic_target\",\"value\":\"#/properties/cyclic_source\"}}");
|
||||
}
|
||||
|
||||
|
||||
#if defined(_MSC_VER) || defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user