code & tests for openapi 2.0 & 3.0 suppprt
This commit is contained in:
parent
80b6d1c834
commit
55eca66f39
@ -109,6 +109,9 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode val
|
||||
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'.");
|
||||
|
||||
case kValidateErrorReadOnly: return RAPIDJSON_ERROR_STRING("Property is read-only but has been provided when validation is for writing.");
|
||||
case kValidateErrorWriteOnly: return RAPIDJSON_ERROR_STRING("Property is write-only but has been provided when validation is for reading.");
|
||||
|
||||
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
}
|
||||
}
|
||||
@ -134,6 +137,10 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetValidateError_En(ValidateErrorCode val
|
||||
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 kSchemaErrorRegexInvalid: return RAPIDJSON_ERROR_STRING("Invalid regular expression '%value' in 'pattern' or 'patternProperties'.");
|
||||
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 kSchemaErrorReadOnlyAndWriteOnly: return RAPIDJSON_ERROR_STRING("Property must not be both 'readOnly' and 'writeOnly'.");
|
||||
|
||||
default: return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
}
|
||||
|
@ -192,7 +192,10 @@ enum ValidateErrorCode {
|
||||
kValidateErrorOneOfMatch, //!< Property matched more than one of the sub-schemas specified by 'oneOf'.
|
||||
kValidateErrorAllOf, //!< Property did not match all of the sub-schemas specified by 'allOf'.
|
||||
kValidateErrorAnyOf, //!< Property did not match any of the sub-schemas specified by 'anyOf'.
|
||||
kValidateErrorNot //!< Property matched the sub-schema specified by 'not'.
|
||||
kValidateErrorNot, //!< Property matched the sub-schema specified by 'not'.
|
||||
|
||||
kValidateErrorReadOnly, //!< Property is read-only but has been provided when validation is for writing
|
||||
kValidateErrorWriteOnly //!< Property is write-only but has been provided when validation is for reading
|
||||
};
|
||||
|
||||
//! Function pointer type of GetValidateError().
|
||||
@ -225,7 +228,11 @@ enum SchemaErrorCode {
|
||||
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
|
||||
kSchemaErrorRegexInvalid //!< Invalid regular expression in 'pattern' or 'patternProperties'
|
||||
kSchemaErrorRegexInvalid, //!< Invalid regular expression in 'pattern' or 'patternProperties'
|
||||
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
|
||||
kSchemaErrorReadOnlyAndWriteOnly //!< Property must not be both 'readOnly' and 'writeOnly'
|
||||
};
|
||||
|
||||
//! Function pointer type of GetSchemaError().
|
||||
|
@ -74,48 +74,94 @@ RAPIDJSON_NAMESPACE_BEGIN
|
||||
|
||||
namespace internal {
|
||||
|
||||
inline void PrintInvalidKeyword(const char* keyword) {
|
||||
printf("Fail keyword: %s\n", keyword);
|
||||
inline void PrintInvalidKeywordData(const char* keyword) {
|
||||
printf(" Fail keyword: '%s'\n", keyword);
|
||||
}
|
||||
|
||||
inline void PrintInvalidKeyword(const wchar_t* keyword) {
|
||||
wprintf(L"Fail keyword: %ls\n", keyword);
|
||||
inline void PrintInvalidKeywordData(const wchar_t* keyword) {
|
||||
wprintf(L" Fail keyword: '%ls'\n", keyword);
|
||||
}
|
||||
|
||||
inline void PrintInvalidDocument(const char* document) {
|
||||
printf("Fail document: %s\n\n", document);
|
||||
inline void PrintInvalidDocumentData(const char* document) {
|
||||
printf(" Fail document: '%s'\n", document);
|
||||
}
|
||||
|
||||
inline void PrintInvalidDocument(const wchar_t* document) {
|
||||
wprintf(L"Fail document: %ls\n\n", document);
|
||||
inline void PrintInvalidDocumentData(const wchar_t* document) {
|
||||
wprintf(L" Fail document: '%ls'\n", document);
|
||||
}
|
||||
|
||||
inline void PrintValidatorPointers(unsigned depth, const char* s, const char* d) {
|
||||
printf("S: %*s%s\nD: %*s%s\n\n", depth * 4, " ", s, depth * 4, " ", d);
|
||||
inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) {
|
||||
printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d);
|
||||
}
|
||||
|
||||
inline void PrintValidatorPointers(unsigned depth, const wchar_t* s, const wchar_t* d) {
|
||||
wprintf(L"S: %*ls%ls\nD: %*ls%ls\n\n", depth * 4, L" ", s, depth * 4, L" ", d);
|
||||
inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) {
|
||||
wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d);
|
||||
}
|
||||
|
||||
inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) {
|
||||
printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved);
|
||||
}
|
||||
|
||||
inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) {
|
||||
wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved);
|
||||
}
|
||||
|
||||
inline void PrintMethodData(const char* method) {
|
||||
printf("%s\n", method);
|
||||
}
|
||||
|
||||
inline void PrintMethodData(const char* method, bool b) {
|
||||
printf("%s, Data: '%s'\n", method, b ? "true" : "false");
|
||||
}
|
||||
|
||||
inline void PrintMethodData(const char* method, int64_t i) {
|
||||
printf("%s, Data: '%" PRId64 "'\n", method, i);
|
||||
}
|
||||
|
||||
inline void PrintMethodData(const char* method, uint64_t u) {
|
||||
printf("%s, Data: '%" PRIu64 "'\n", method, u);
|
||||
}
|
||||
|
||||
inline void PrintMethodData(const char* method, double d) {
|
||||
printf("%s, Data: '%lf'\n", method, d);
|
||||
}
|
||||
|
||||
inline void PrintMethodData(const char* method, const char* s) {
|
||||
printf("%s, Data: '%s'\n", method, s);
|
||||
}
|
||||
|
||||
inline void PrintMethodData(const char* method, const wchar_t* s) {
|
||||
wprintf(L"%hs, Data: '%ls'\n", method, s);
|
||||
}
|
||||
|
||||
inline void PrintMethodData(const char* method, const char* s1, const char* s2) {
|
||||
printf("%s, Data: '%s', '%s'\n", method, s1, s2);
|
||||
}
|
||||
|
||||
inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) {
|
||||
wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
#endif // RAPIDJSON_SCHEMA_VERBOSE
|
||||
|
||||
#ifndef RAPIDJSON_SCHEMA_PRINT
|
||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||
#define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__)
|
||||
#else
|
||||
#define RAPIDJSON_SCHEMA_PRINT(name, ...)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// RAPIDJSON_INVALID_KEYWORD_RETURN
|
||||
|
||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||
#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword) internal::PrintInvalidKeyword(keyword)
|
||||
#else
|
||||
#define RAPIDJSON_INVALID_KEYWORD_VERBOSE(keyword)
|
||||
#endif
|
||||
|
||||
#define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\
|
||||
RAPIDJSON_MULTILINEMACRO_BEGIN\
|
||||
context.invalidCode = code;\
|
||||
context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\
|
||||
RAPIDJSON_INVALID_KEYWORD_VERBOSE(context.invalidKeyword);\
|
||||
RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\
|
||||
return false;\
|
||||
RAPIDJSON_MULTILINEMACRO_END
|
||||
|
||||
@ -138,9 +184,53 @@ RAPIDJSON_MULTILINEMACRO_END
|
||||
enum ValidateFlag {
|
||||
kValidateNoFlags = 0, //!< No flags are set.
|
||||
kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error.
|
||||
kValidateReadFlag = 2, //!< Validation is for a read semantic.
|
||||
kValidateWriteFlag = 4, //!< Validation is for a write semantic.
|
||||
kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Specification
|
||||
enum SchemaDraft {
|
||||
kDraftUnknown = -1,
|
||||
kDraftNone = 0,
|
||||
kDraft03 = 3,
|
||||
kDraftMin = 4, //!< Current minimum supported draft
|
||||
kDraft04 = 4,
|
||||
kDraft05 = 5,
|
||||
kDraftMax = 5, //!< Current maximum supported draft
|
||||
kDraft06 = 6,
|
||||
kDraft07 = 7,
|
||||
kDraft2019_09 = 8,
|
||||
kDraft2020_12 = 9
|
||||
};
|
||||
|
||||
enum OpenApiVersion {
|
||||
kVersionUnknown = -1,
|
||||
kVersionNone = 0,
|
||||
kVersionMin = 2, //!< Current minimum supported version
|
||||
kVersion20 = 2,
|
||||
kVersion30 = 3,
|
||||
kVersionMax = 3, //!< Current maximum supported version
|
||||
kVersion31 = 4,
|
||||
};
|
||||
|
||||
struct Specification {
|
||||
Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {}
|
||||
Specification(OpenApiVersion o) : oapi(o) {
|
||||
if (oapi == kVersion20) draft = kDraft04;
|
||||
else if (oapi == kVersion30) draft = kDraft05;
|
||||
else if (oapi == kVersion31) draft = kDraft2020_12;
|
||||
else draft = kDraft04;
|
||||
}
|
||||
~Specification() {}
|
||||
bool IsSupported() const {
|
||||
return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax)));
|
||||
}
|
||||
SchemaDraft draft;
|
||||
OpenApiVersion oapi;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Forward declarations
|
||||
|
||||
@ -233,6 +323,8 @@ public:
|
||||
virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
|
||||
virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0;
|
||||
virtual void Disallowed() = 0;
|
||||
virtual void DisallowedWhenWriting() = 0;
|
||||
virtual void DisallowedWhenReading() = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -350,10 +442,11 @@ struct SchemaValidationContext {
|
||||
kPatternValidatorWithAdditionalProperty
|
||||
};
|
||||
|
||||
SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s) :
|
||||
SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) :
|
||||
factory(f),
|
||||
error_handler(eh),
|
||||
schema(s),
|
||||
flags(fl),
|
||||
valueSchema(),
|
||||
invalidKeyword(),
|
||||
invalidCode(),
|
||||
@ -401,6 +494,7 @@ struct SchemaValidationContext {
|
||||
SchemaValidatorFactoryType& factory;
|
||||
ErrorHandlerType& error_handler;
|
||||
const SchemaType* schema;
|
||||
unsigned flags;
|
||||
const SchemaType* valueSchema;
|
||||
const Ch* invalidKeyword;
|
||||
ValidateErrorCode invalidCode;
|
||||
@ -443,6 +537,7 @@ public:
|
||||
allocator_(allocator),
|
||||
uri_(schemaDocument->GetURI(), *allocator),
|
||||
id_(id, allocator),
|
||||
spec_(schemaDocument->GetSpecification()),
|
||||
pointer_(p, allocator),
|
||||
typeless_(schemaDocument->GetTypeless()),
|
||||
enum_(),
|
||||
@ -475,8 +570,15 @@ public:
|
||||
maxLength_(~SizeType(0)),
|
||||
exclusiveMinimum_(false),
|
||||
exclusiveMaximum_(false),
|
||||
defaultValueLength_(0)
|
||||
defaultValueLength_(0),
|
||||
readOnly_(false),
|
||||
writeOnly_(false),
|
||||
nullable_(false)
|
||||
{
|
||||
GenericStringBuffer<EncodingType> sb;
|
||||
p.StringifyUriFragment(sb);
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString());
|
||||
|
||||
typedef typename ValueType::ConstValueIterator ConstValueIterator;
|
||||
typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
|
||||
|
||||
@ -495,10 +597,13 @@ public:
|
||||
return;
|
||||
|
||||
// If we have an id property, resolve it with the in-scope id
|
||||
// Not supported for open api 2.0 or 3.0
|
||||
if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
|
||||
if (const ValueType* v = GetMember(value, GetIdString())) {
|
||||
if (v->IsString()) {
|
||||
UriType local(*v, allocator);
|
||||
id_ = local.Resolve(id_, allocator);
|
||||
RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString());
|
||||
}
|
||||
}
|
||||
|
||||
@ -525,8 +630,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (schemaDocument) {
|
||||
if (schemaDocument)
|
||||
AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
|
||||
|
||||
// AnyOf, OneOf, Not not supported for open api 2.0
|
||||
if (schemaDocument && spec_.oapi != kVersion20) {
|
||||
AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
|
||||
AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
|
||||
|
||||
@ -555,6 +663,8 @@ public:
|
||||
if (itr->IsString())
|
||||
AddUniqueElement(allProperties, *itr);
|
||||
|
||||
// Dependencies not supported for open api 2.0 and 3.0
|
||||
if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
|
||||
if (dependencies && dependencies->IsObject())
|
||||
for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
|
||||
AddUniqueElement(allProperties, itr->name);
|
||||
@ -584,6 +694,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// PatternProperties not supported for open api 2.0 and 3.0
|
||||
if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
|
||||
if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
|
||||
PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
|
||||
patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
|
||||
@ -608,6 +720,8 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Dependencies not supported for open api 2.0 and 3.0
|
||||
if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
|
||||
if (dependencies && dependencies->IsObject()) {
|
||||
PointerType q = p.Append(GetDependenciesString(), allocator_);
|
||||
hasDependencies_ = true;
|
||||
@ -659,6 +773,8 @@ public:
|
||||
AssignIfExist(minItems_, value, GetMinItemsString());
|
||||
AssignIfExist(maxItems_, value, GetMaxItemsString());
|
||||
|
||||
// AdditionalItems not supported for openapi 2.0 and 3.0
|
||||
if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
|
||||
if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
|
||||
if (v->IsBool())
|
||||
additionalItems_ = v->GetBool();
|
||||
@ -696,6 +812,23 @@ public:
|
||||
if (v->IsString())
|
||||
defaultValueLength_ = v->GetStringLength();
|
||||
|
||||
// ReadOnly - open api only (until draft 7 supported)
|
||||
// WriteOnly - open api 3 only (until draft 7 supported)
|
||||
// Both can't be true
|
||||
if (spec_.oapi != kVersionNone)
|
||||
AssignIfExist(readOnly_, value, GetReadOnlyString());
|
||||
if (spec_.oapi >= kVersion30)
|
||||
AssignIfExist(writeOnly_, value, GetWriteOnlyString());
|
||||
if (readOnly_ && writeOnly_)
|
||||
schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p);
|
||||
|
||||
// Nullable - open api 3 only
|
||||
// If true add 'null' as allowable type
|
||||
if (spec_.oapi >= kVersion30) {
|
||||
AssignIfExist(nullable_, value, GetNullableString());
|
||||
if (nullable_)
|
||||
AddType(GetNullString());
|
||||
}
|
||||
}
|
||||
|
||||
~Schema() {
|
||||
@ -727,11 +860,16 @@ public:
|
||||
return id_;
|
||||
}
|
||||
|
||||
const Specification& GetSpecification() const {
|
||||
return spec_;
|
||||
}
|
||||
|
||||
const PointerType& GetPointer() const {
|
||||
return pointer_;
|
||||
}
|
||||
|
||||
bool BeginValue(Context& context) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue");
|
||||
if (context.inArray) {
|
||||
if (uniqueItems_)
|
||||
context.valueUniqueness = true;
|
||||
@ -763,6 +901,7 @@ public:
|
||||
}
|
||||
|
||||
RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue");
|
||||
// Only check pattern properties if we have validators
|
||||
if (context.patternPropertiesValidatorCount > 0) {
|
||||
bool otherValid = false;
|
||||
@ -853,6 +992,7 @@ public:
|
||||
}
|
||||
|
||||
bool Null(Context& context) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null");
|
||||
if (!(type_ & (1 << kNullSchemaType))) {
|
||||
DisallowedType(context, GetNullString());
|
||||
RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
||||
@ -860,39 +1000,43 @@ public:
|
||||
return CreateParallelValidator(context);
|
||||
}
|
||||
|
||||
bool Bool(Context& context, bool) const {
|
||||
if (!(type_ & (1 << kBooleanSchemaType))) {
|
||||
DisallowedType(context, GetBooleanString());
|
||||
RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
||||
}
|
||||
bool Bool(Context& context, bool b) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b);
|
||||
if (!CheckBool(context, b))
|
||||
return false;
|
||||
return CreateParallelValidator(context);
|
||||
}
|
||||
|
||||
bool Int(Context& context, int i) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i);
|
||||
if (!CheckInt(context, i))
|
||||
return false;
|
||||
return CreateParallelValidator(context);
|
||||
}
|
||||
|
||||
bool Uint(Context& context, unsigned u) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u);
|
||||
if (!CheckUint(context, u))
|
||||
return false;
|
||||
return CreateParallelValidator(context);
|
||||
}
|
||||
|
||||
bool Int64(Context& context, int64_t i) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i);
|
||||
if (!CheckInt(context, i))
|
||||
return false;
|
||||
return CreateParallelValidator(context);
|
||||
}
|
||||
|
||||
bool Uint64(Context& context, uint64_t u) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u);
|
||||
if (!CheckUint(context, u))
|
||||
return false;
|
||||
return CreateParallelValidator(context);
|
||||
}
|
||||
|
||||
bool Double(Context& context, double d) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d);
|
||||
if (!(type_ & (1 << kNumberSchemaType))) {
|
||||
DisallowedType(context, GetNumberString());
|
||||
RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
||||
@ -911,6 +1055,7 @@ public:
|
||||
}
|
||||
|
||||
bool String(Context& context, const Ch* str, SizeType length, bool) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str);
|
||||
if (!(type_ & (1 << kStringSchemaType))) {
|
||||
DisallowedType(context, GetStringString());
|
||||
RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
||||
@ -939,6 +1084,7 @@ public:
|
||||
}
|
||||
|
||||
bool StartObject(Context& context) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject");
|
||||
if (!(type_ & (1 << kObjectSchemaType))) {
|
||||
DisallowedType(context, GetObjectString());
|
||||
RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
||||
@ -960,6 +1106,8 @@ public:
|
||||
}
|
||||
|
||||
bool Key(Context& context, const Ch* str, SizeType len, bool) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str);
|
||||
|
||||
if (patternProperties_) {
|
||||
context.patternPropertiesSchemaCount = 0;
|
||||
for (SizeType i = 0; i < patternPropertyCount_; i++)
|
||||
@ -1011,6 +1159,7 @@ public:
|
||||
}
|
||||
|
||||
bool EndObject(Context& context, SizeType memberCount) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject");
|
||||
if (hasRequired_) {
|
||||
context.error_handler.StartMissingProperties();
|
||||
for (SizeType index = 0; index < propertyCount_; index++)
|
||||
@ -1058,6 +1207,7 @@ public:
|
||||
}
|
||||
|
||||
bool StartArray(Context& context) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray");
|
||||
context.arrayElementIndex = 0;
|
||||
context.inArray = true; // Ensure we note that we are in an array
|
||||
|
||||
@ -1070,6 +1220,7 @@ public:
|
||||
}
|
||||
|
||||
bool EndArray(Context& context, SizeType elementCount) const {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray");
|
||||
context.inArray = false;
|
||||
|
||||
if (elementCount < minItems_) {
|
||||
@ -1118,6 +1269,9 @@ public:
|
||||
case kValidateErrorAnyOf: return GetAnyOfString();
|
||||
case kValidateErrorNot: return GetNotString();
|
||||
|
||||
case kValidateErrorReadOnly: return GetReadOnlyString();
|
||||
case kValidateErrorWriteOnly: return GetWriteOnlyString();
|
||||
|
||||
default: return GetNullString();
|
||||
}
|
||||
}
|
||||
@ -1165,15 +1319,14 @@ public:
|
||||
RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
|
||||
RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
|
||||
RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't')
|
||||
RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a')
|
||||
RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f')
|
||||
RAPIDJSON_STRING_(Id, 'i', 'd')
|
||||
|
||||
RAPIDJSON_STRING_(SchemeEnd, ':')
|
||||
RAPIDJSON_STRING_(AuthStart, '/', '/')
|
||||
RAPIDJSON_STRING_(QueryStart, '?')
|
||||
RAPIDJSON_STRING_(FragStart, '#')
|
||||
RAPIDJSON_STRING_(Slash, '/')
|
||||
RAPIDJSON_STRING_(Dot, '.')
|
||||
RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r')
|
||||
RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i')
|
||||
RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y')
|
||||
RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y')
|
||||
RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e')
|
||||
|
||||
#undef RAPIDJSON_STRING_
|
||||
|
||||
@ -1307,6 +1460,7 @@ private:
|
||||
|
||||
// Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required.
|
||||
// Also creates a hasher for enums and array uniqueness, if required.
|
||||
// Also a useful place to add type-independent error checks.
|
||||
bool CreateParallelValidator(Context& context) const {
|
||||
if (enum_ || context.arrayUniqueness)
|
||||
context.hasher = context.factory.CreateHasher();
|
||||
@ -1337,6 +1491,16 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
// Add any other type-independent checks here
|
||||
if (readOnly_ && (context.flags & kValidateWriteFlag)) {
|
||||
context.error_handler.DisallowedWhenWriting();
|
||||
RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly);
|
||||
}
|
||||
if (writeOnly_ && (context.flags & kValidateReadFlag)) {
|
||||
context.error_handler.DisallowedWhenReading();
|
||||
RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1359,6 +1523,14 @@ private:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CheckBool(Context& context, bool) const {
|
||||
if (!(type_ & (1 << kBooleanSchemaType))) {
|
||||
DisallowedType(context, GetBooleanString());
|
||||
RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckInt(Context& context, int64_t i) const {
|
||||
if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
|
||||
DisallowedType(context, GetIntegerString());
|
||||
@ -1524,6 +1696,7 @@ private:
|
||||
AllocatorType* allocator_;
|
||||
SValue uri_;
|
||||
UriType id_;
|
||||
Specification spec_;
|
||||
PointerType pointer_;
|
||||
const SchemaType* typeless_;
|
||||
uint64_t* enum_;
|
||||
@ -1568,6 +1741,10 @@ private:
|
||||
bool exclusiveMaximum_;
|
||||
|
||||
SizeType defaultValueLength_;
|
||||
|
||||
bool readOnly_;
|
||||
bool writeOnly_;
|
||||
bool nullable_;
|
||||
};
|
||||
|
||||
template<typename Stack, typename Ch>
|
||||
@ -1614,7 +1791,12 @@ public:
|
||||
|
||||
virtual ~IGenericRemoteSchemaDocumentProvider() {}
|
||||
virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
|
||||
virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri<ValueType, AllocatorType> uri) { return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); }
|
||||
virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri<ValueType, AllocatorType> uri, Specification& spec) {
|
||||
// Default implementation just calls through for compatibility
|
||||
// Following line suppresses unused parameter warning
|
||||
if (false) printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi);
|
||||
return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength());
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@ -1656,10 +1838,12 @@ public:
|
||||
\param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
|
||||
\param allocator An optional allocator instance for allocating memory. Can be null.
|
||||
\param pointer An optional JSON pointer to the start of the schema document
|
||||
\param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04.
|
||||
*/
|
||||
explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0,
|
||||
IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0,
|
||||
const PointerType& pointer = PointerType()) : // PR #1393
|
||||
const PointerType& pointer = PointerType(), // PR #1393
|
||||
const Specification& spec = Specification(kDraft04)) :
|
||||
remoteProvider_(remoteProvider),
|
||||
allocator_(allocator),
|
||||
ownAllocator_(),
|
||||
@ -1667,9 +1851,11 @@ public:
|
||||
typeless_(),
|
||||
schemaMap_(allocator, kInitialSchemaMapSize),
|
||||
schemaRef_(allocator, kInitialSchemaRefSize),
|
||||
spec_(spec),
|
||||
error_(kObjectType),
|
||||
currentError_()
|
||||
{
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument");
|
||||
if (!allocator_)
|
||||
ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
|
||||
|
||||
@ -1680,6 +1866,10 @@ public:
|
||||
typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
|
||||
new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_);
|
||||
|
||||
// Establish the schema draft or open api version.
|
||||
// We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document.
|
||||
SetSchemaSpecification(document);
|
||||
|
||||
// Generate root schema, it will call CreateSchema() to create sub-schemas,
|
||||
// And call HandleRefSchema() if there are $ref.
|
||||
// PR #1393 use input pointer if supplied
|
||||
@ -1713,6 +1903,7 @@ public:
|
||||
schemaRef_(std::move(rhs.schemaRef_)),
|
||||
uri_(std::move(rhs.uri_)),
|
||||
docId_(std::move(rhs.docId_)),
|
||||
spec_(rhs.spec_),
|
||||
error_(std::move(rhs.error_)),
|
||||
currentError_(std::move(rhs.currentError_))
|
||||
{
|
||||
@ -1743,6 +1934,23 @@ public:
|
||||
|
||||
const GValue& GetURI() const { return uri_; }
|
||||
|
||||
const Specification& GetSpecification() const { return spec_; }
|
||||
bool IsSupportedSpecification() const { return spec_.IsSupported(); }
|
||||
|
||||
//! Static method to get the specification of any schema document
|
||||
// Returns kDraftNone if document is silent
|
||||
static const Specification GetSpecification(const ValueType& document) {
|
||||
SchemaDraft draft = GetSchemaDraft(document);
|
||||
if (draft != kDraftNone)
|
||||
return Specification(draft);
|
||||
else {
|
||||
OpenApiVersion oapi = GetOpenApiVersion(document);
|
||||
if (oapi != kVersionNone)
|
||||
return Specification(oapi);
|
||||
}
|
||||
return Specification(kDraftNone);
|
||||
}
|
||||
|
||||
//! Get the root schema.
|
||||
const SchemaType& GetRoot() const { return *root_; }
|
||||
|
||||
@ -1761,6 +1969,10 @@ public:
|
||||
case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString();
|
||||
case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString();
|
||||
case kSchemaErrorRegexInvalid: return GetRegexInvalidString();
|
||||
case kSchemaErrorSpecUnknown: return GetSpecUnknownString();
|
||||
case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString();
|
||||
case kSchemaErrorSpecIllegal: return GetSpecIllegalString();
|
||||
case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString();
|
||||
default: return GetNullString();
|
||||
}
|
||||
}
|
||||
@ -1829,6 +2041,7 @@ public:
|
||||
}
|
||||
|
||||
void AddCurrentError(const SchemaErrorCode code, const PointerType& location) {
|
||||
RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code));
|
||||
currentError_.AddMember(GetErrorCodeString(), code, *allocator_);
|
||||
AddErrorInstanceLocation(currentError_, location);
|
||||
AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_);
|
||||
@ -1847,6 +2060,9 @@ public:
|
||||
RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't')
|
||||
|
||||
RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
|
||||
RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
|
||||
RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd')
|
||||
RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', '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')
|
||||
@ -1855,10 +2071,94 @@ public:
|
||||
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_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y')
|
||||
RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
|
||||
|
||||
#undef RAPIDJSON_STRING_
|
||||
|
||||
// Static method to get schema draft of any schema document
|
||||
static SchemaDraft GetSchemaDraft(const ValueType& document) {
|
||||
static const Ch kDraft03String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '3', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
|
||||
static const Ch kDraft04String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
|
||||
static const Ch kDraft05String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '5', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
|
||||
static const Ch kDraft06String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '6', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
|
||||
static const Ch kDraft07String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '7', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
|
||||
static const Ch kDraft2019_09String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '1', '9', '-', '0', '9', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' };
|
||||
static const Ch kDraft2020_12String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '2', '0', '-', '1', '2', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' };
|
||||
|
||||
if (!document.IsObject()) {
|
||||
return kDraftNone;
|
||||
}
|
||||
|
||||
// Get the schema draft from the $schema keyword at the supplied location
|
||||
typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString());
|
||||
if (itr != document.MemberEnd()) {
|
||||
if (!itr->value.IsString()) return kDraftUnknown;
|
||||
const UriType draftUri(itr->value);
|
||||
// Check base uri for match
|
||||
if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04;
|
||||
if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05;
|
||||
if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06;
|
||||
if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07;
|
||||
if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03;
|
||||
if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09;
|
||||
if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12;
|
||||
return kDraftUnknown;
|
||||
}
|
||||
// $schema not found
|
||||
return kDraftNone;
|
||||
}
|
||||
|
||||
|
||||
// Get open api version of any schema document
|
||||
static OpenApiVersion GetOpenApiVersion(const ValueType& document) {
|
||||
static const Ch kVersion20String[] = { '2', '.', '0', '\0' };
|
||||
static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level
|
||||
static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level
|
||||
static SizeType len = internal::StrLen<Ch>(kVersion30String);
|
||||
|
||||
if (!document.IsObject()) {
|
||||
return kVersionNone;
|
||||
}
|
||||
|
||||
// Get the open api version from the swagger / openapi keyword at the supplied location
|
||||
typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString());
|
||||
if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString());
|
||||
if (itr != document.MemberEnd()) {
|
||||
if (!itr->value.IsString()) return kVersionUnknown;
|
||||
const ValueType kVersion20Value(kVersion20String);
|
||||
if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly
|
||||
const ValueType kVersion30Value(kVersion30String);
|
||||
if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x
|
||||
const ValueType kVersion31Value(kVersion31String);
|
||||
if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x
|
||||
return kVersionUnknown;
|
||||
}
|
||||
// swagger or openapi not found
|
||||
return kVersionNone;
|
||||
}
|
||||
|
||||
// Get the draft of the schema or the open api version (which implies the draft).
|
||||
// Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on.
|
||||
void SetSchemaSpecification(const ValueType& document) {
|
||||
// Look for '$schema', 'swagger' or 'openapi' keyword at document root
|
||||
SchemaDraft docDraft = GetSchemaDraft(document);
|
||||
OpenApiVersion docOapi = GetOpenApiVersion(document);
|
||||
// Error if both in document
|
||||
if (docDraft != kDraftNone && docOapi != kVersionNone)
|
||||
SchemaError(kSchemaErrorSpecIllegal, PointerType());
|
||||
// Use document draft or open api version if present or use spec from constructor
|
||||
if (docDraft != kDraftNone)
|
||||
spec_ = Specification(docDraft);
|
||||
else if (docOapi != kVersionNone)
|
||||
spec_ = Specification(docOapi);
|
||||
// Error if draft or version unknown
|
||||
if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown)
|
||||
SchemaError(kSchemaErrorSpecUnknown, PointerType());
|
||||
else if (!spec_.IsSupported())
|
||||
SchemaError(kSchemaErrorSpecUnsupported, PointerType());
|
||||
}
|
||||
|
||||
// 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) {
|
||||
@ -1875,6 +2175,9 @@ public:
|
||||
// Changed by PR #1393
|
||||
const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
|
||||
RAPIDJSON_ASSERT(pointer.IsValid());
|
||||
GenericStringBuffer<EncodingType> sb;
|
||||
pointer.StringifyUriFragment(sb);
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString());
|
||||
if (v.IsObject()) {
|
||||
if (const SchemaType* sc = GetSchema(pointer)) {
|
||||
if (schema)
|
||||
@ -1904,6 +2207,9 @@ public:
|
||||
if (itr == v.MemberEnd())
|
||||
return false;
|
||||
|
||||
GenericStringBuffer<EncodingType> sb;
|
||||
source.StringifyUriFragment(sb);
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString());
|
||||
// Resolve the source pointer to the $ref'ed schema (finally)
|
||||
new (schemaRef_.template Push<SchemaRefPtr>()) SchemaRefPtr(&source);
|
||||
|
||||
@ -1915,6 +2221,7 @@ public:
|
||||
// First resolve $ref against the in-scope id
|
||||
UriType scopeId = UriType(id, allocator_);
|
||||
UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_);
|
||||
RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString());
|
||||
// See if the resolved $ref minus the fragment matches a resolved id in this document
|
||||
// Search from the root. Returns the subschema in the document and its absolute JSON pointer.
|
||||
PointerType basePointer = PointerType();
|
||||
@ -1924,7 +2231,7 @@ public:
|
||||
if (!remoteProvider_)
|
||||
SchemaError(kSchemaErrorRefNoRemoteProvider, source);
|
||||
else {
|
||||
if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref)) {
|
||||
if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) {
|
||||
const Ch* s = ref.GetFragString();
|
||||
len = ref.GetFragStringLength();
|
||||
if (len <= 1 || s[1] == '/') {
|
||||
@ -1979,10 +2286,13 @@ public:
|
||||
}
|
||||
} else {
|
||||
// Plain name fragment, relative to the resolved URI
|
||||
// Not supported in open api 2.0 and 3.0
|
||||
PointerType pointer(allocator_);
|
||||
if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30)
|
||||
SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len);
|
||||
// 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.
|
||||
if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) {
|
||||
else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) {
|
||||
if (IsCyclicRef(pointer))
|
||||
SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength());
|
||||
else {
|
||||
@ -2024,6 +2334,7 @@ public:
|
||||
}
|
||||
// See if it matches
|
||||
if (localuri.Match(finduri, full)) {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString());
|
||||
resval = const_cast<ValueType *>(&doc);
|
||||
resptr = here;
|
||||
return resval;
|
||||
@ -2050,6 +2361,7 @@ public:
|
||||
|
||||
// Added by PR #1393
|
||||
void AddSchemaRefs(SchemaType* schema) {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs");
|
||||
while (!schemaRef_.Empty()) {
|
||||
SchemaRefPtr *ref = schemaRef_.template Pop<SchemaRefPtr>(1);
|
||||
SchemaEntry *entry = schemaMap_.template Push<SchemaEntry>();
|
||||
@ -2093,6 +2405,7 @@ public:
|
||||
internal::Stack<Allocator> schemaRef_; // Stores Pointer(s) from $ref(s) until resolved
|
||||
GValue uri_; // Schema document URI
|
||||
UriType docId_;
|
||||
Specification spec_;
|
||||
GValue error_;
|
||||
GValue currentError_;
|
||||
};
|
||||
@ -2158,11 +2471,10 @@ public:
|
||||
currentError_(),
|
||||
missingDependents_(),
|
||||
valid_(true),
|
||||
flags_(kValidateDefaultFlags)
|
||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||
, depth_(0)
|
||||
#endif
|
||||
flags_(kValidateDefaultFlags),
|
||||
depth_(0)
|
||||
{
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator");
|
||||
}
|
||||
|
||||
//! Constructor with output handler.
|
||||
@ -2190,11 +2502,10 @@ public:
|
||||
currentError_(),
|
||||
missingDependents_(),
|
||||
valid_(true),
|
||||
flags_(kValidateDefaultFlags)
|
||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||
, depth_(0)
|
||||
#endif
|
||||
flags_(kValidateDefaultFlags),
|
||||
depth_(0)
|
||||
{
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)");
|
||||
}
|
||||
|
||||
//! Destructor.
|
||||
@ -2455,6 +2766,14 @@ public:
|
||||
currentError_.SetObject();
|
||||
AddCurrentError(kValidateErrorNot);
|
||||
}
|
||||
void DisallowedWhenWriting() {
|
||||
currentError_.SetObject();
|
||||
AddCurrentError(kValidateErrorReadOnly);
|
||||
}
|
||||
void DisallowedWhenReading() {
|
||||
currentError_.SetObject();
|
||||
AddCurrentError(kValidateErrorWriteOnly);
|
||||
}
|
||||
|
||||
#define RAPIDJSON_STRING_(name, ...) \
|
||||
static const StringRefType& Get##name##String() {\
|
||||
@ -2477,21 +2796,12 @@ public:
|
||||
|
||||
#undef RAPIDJSON_STRING_
|
||||
|
||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_() \
|
||||
RAPIDJSON_MULTILINEMACRO_BEGIN\
|
||||
*documentStack_.template Push<Ch>() = '\0';\
|
||||
documentStack_.template Pop<Ch>(1);\
|
||||
internal::PrintInvalidDocument(documentStack_.template Bottom<Ch>());\
|
||||
RAPIDJSON_MULTILINEMACRO_END
|
||||
#else
|
||||
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_()
|
||||
#endif
|
||||
|
||||
#define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
|
||||
if (!valid_) return false; \
|
||||
if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\
|
||||
RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_();\
|
||||
*documentStack_.template Push<Ch>() = '\0';\
|
||||
documentStack_.template Pop<Ch>(1);\
|
||||
RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom<Ch>());\
|
||||
valid_ = false;\
|
||||
return valid_;\
|
||||
}
|
||||
@ -2530,6 +2840,7 @@ RAPIDJSON_MULTILINEMACRO_END
|
||||
{ RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
|
||||
|
||||
bool StartObject() {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartObject");
|
||||
RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
|
||||
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
|
||||
valid_ = !outputHandler_ || outputHandler_->StartObject();
|
||||
@ -2537,6 +2848,7 @@ RAPIDJSON_MULTILINEMACRO_END
|
||||
}
|
||||
|
||||
bool Key(const Ch* str, SizeType len, bool copy) {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str);
|
||||
if (!valid_) return false;
|
||||
AppendToken(str, len);
|
||||
if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) {
|
||||
@ -2549,6 +2861,7 @@ RAPIDJSON_MULTILINEMACRO_END
|
||||
}
|
||||
|
||||
bool EndObject(SizeType memberCount) {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject");
|
||||
if (!valid_) return false;
|
||||
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
|
||||
if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) {
|
||||
@ -2559,6 +2872,7 @@ RAPIDJSON_MULTILINEMACRO_END
|
||||
}
|
||||
|
||||
bool StartArray() {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray");
|
||||
RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
|
||||
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
|
||||
valid_ = !outputHandler_ || outputHandler_->StartArray();
|
||||
@ -2566,6 +2880,7 @@ RAPIDJSON_MULTILINEMACRO_END
|
||||
}
|
||||
|
||||
bool EndArray(SizeType elementCount) {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray");
|
||||
if (!valid_) return false;
|
||||
RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
|
||||
if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) {
|
||||
@ -2575,17 +2890,16 @@ RAPIDJSON_MULTILINEMACRO_END
|
||||
RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
|
||||
}
|
||||
|
||||
#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_VERBOSE_
|
||||
#undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
|
||||
#undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
|
||||
#undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
|
||||
|
||||
// Implementation of ISchemaStateFactory<SchemaType>
|
||||
virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) {
|
||||
*documentStack_.template Push<Ch>() = '\0';
|
||||
documentStack_.template Pop<Ch>(1);
|
||||
ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
|
||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||
depth_ + 1,
|
||||
#endif
|
||||
&GetStateAllocator());
|
||||
sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~(unsigned)kValidateContinueOnErrorFlag);
|
||||
return sv;
|
||||
@ -2629,9 +2943,7 @@ private:
|
||||
const SchemaDocumentType& schemaDocument,
|
||||
const SchemaType& root,
|
||||
const char* basePath, size_t basePathSize,
|
||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||
unsigned depth,
|
||||
#endif
|
||||
StateAllocator* allocator = 0,
|
||||
size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
|
||||
size_t documentStackCapacity = kDefaultDocumentStackCapacity)
|
||||
@ -2647,11 +2959,10 @@ private:
|
||||
currentError_(),
|
||||
missingDependents_(),
|
||||
valid_(true),
|
||||
flags_(kValidateDefaultFlags)
|
||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||
, depth_(depth)
|
||||
#endif
|
||||
flags_(kValidateDefaultFlags),
|
||||
depth_(depth)
|
||||
{
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : "");
|
||||
if (basePath && basePathSize)
|
||||
memcpy(documentStack_.template Push<char>(basePathSize), basePath, basePathSize);
|
||||
}
|
||||
@ -2667,6 +2978,7 @@ private:
|
||||
}
|
||||
|
||||
bool BeginValue() {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue");
|
||||
if (schemaStack_.Empty())
|
||||
PushSchema(root_);
|
||||
else {
|
||||
@ -2699,17 +3011,15 @@ private:
|
||||
}
|
||||
|
||||
bool EndValue() {
|
||||
RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue");
|
||||
if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors())
|
||||
return false;
|
||||
|
||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||
GenericStringBuffer<EncodingType> sb;
|
||||
schemaDocument_->GetPointer(&CurrentSchema()).Stringify(sb);
|
||||
|
||||
schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb);
|
||||
*documentStack_.template Push<Ch>() = '\0';
|
||||
documentStack_.template Pop<Ch>(1);
|
||||
internal::PrintValidatorPointers(depth_, sb.GetString(), documentStack_.template Bottom<Ch>());
|
||||
#endif
|
||||
RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom<Ch>(), depth_);
|
||||
void* hasher = CurrentContext().hasher;
|
||||
uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast<HasherType*>(hasher)->GetHashCode() : 0;
|
||||
|
||||
@ -2760,7 +3070,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, *this, &schema); }
|
||||
RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, *this, &schema, flags_); }
|
||||
|
||||
RAPIDJSON_FORCEINLINE void PopSchema() {
|
||||
Context* c = schemaStack_.template Pop<Context>(1);
|
||||
@ -2862,9 +3172,7 @@ private:
|
||||
ValueType missingDependents_;
|
||||
bool valid_;
|
||||
unsigned flags_;
|
||||
#if RAPIDJSON_SCHEMA_VERBOSE
|
||||
unsigned depth_;
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
|
||||
|
@ -118,12 +118,7 @@ TEST(SchemaValidator, Hasher) {
|
||||
#define VALIDATE_(schema, json, expected, expected2) \
|
||||
{\
|
||||
EXPECT_TRUE(expected2 == schema.GetError().ObjectEmpty());\
|
||||
if (expected2 && !schema.GetError().ObjectEmpty()) {\
|
||||
StringBuffer ssb;\
|
||||
Writer<StringBuffer> ws(ssb);\
|
||||
schema.GetError().Accept(ws);\
|
||||
printf("Schema error: %s\n", ssb.GetString());\
|
||||
}\
|
||||
EXPECT_TRUE(schema.IsSupportedSpecification());\
|
||||
SchemaValidator validator(schema);\
|
||||
Document d;\
|
||||
/*printf("\n%s\n", json);*/\
|
||||
@ -162,12 +157,7 @@ TEST(SchemaValidator, Hasher) {
|
||||
flags, SchemaValidatorType, PointerType) \
|
||||
{\
|
||||
EXPECT_TRUE(schema.GetError().ObjectEmpty());\
|
||||
if (!schema.GetError().ObjectEmpty()) {\
|
||||
StringBuffer ssb;\
|
||||
Writer<StringBuffer> ws(ssb);\
|
||||
schema.GetError().Accept(ws);\
|
||||
printf("Schema error: %s\n", ssb.GetString());\
|
||||
}\
|
||||
EXPECT_TRUE(schema.IsSupportedSpecification());\
|
||||
SchemaValidatorType validator(schema);\
|
||||
validator.SetValidateFlags(flags);\
|
||||
Document d;\
|
||||
@ -2163,9 +2153,13 @@ public:
|
||||
}
|
||||
|
||||
virtual const SchemaDocumentType* GetRemoteDocument(const char* uri, SizeType length) {
|
||||
//printf("GetRemoteDocument : %s\n", uri);
|
||||
for (size_t i = 0; i < kCount; i++)
|
||||
if (typename SchemaDocumentType::GValue(uri, length) == sd_[i]->GetURI())
|
||||
if (typename SchemaDocumentType::GValue(uri, length) == sd_[i]->GetURI()) {
|
||||
//printf("Matched document");
|
||||
return sd_[i];
|
||||
}
|
||||
//printf("No matched document");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2999,6 +2993,334 @@ TEST(SchemaValidator, DuplicateKeyword) {
|
||||
|
||||
// SchemaDocument tests
|
||||
|
||||
// Specification (schema draft, open api version)
|
||||
TEST(SchemaValidator, Schema_SupportedNotObject) {
|
||||
Document sd;
|
||||
sd.Parse("true");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
EXPECT_TRUE(s.GetError().ObjectEmpty());
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedNoSpec) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
EXPECT_TRUE(s.GetError().ObjectEmpty());
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedNoSpecStatic) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
Specification spec = SchemaDocumentType::GetSpecification(sd);
|
||||
ASSERT_FALSE(spec.IsSupported());
|
||||
ASSERT_TRUE(spec.draft == kDraftNone);
|
||||
ASSERT_TRUE(spec.oapi == kVersionNone);
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedDraft5Static) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\":\"http://json-schema.org/draft-05/schema#\", \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
Specification spec = SchemaDocumentType::GetSpecification(sd);
|
||||
ASSERT_TRUE(spec.IsSupported());
|
||||
ASSERT_TRUE(spec.draft == kDraft05);
|
||||
ASSERT_TRUE(spec.oapi == kVersionNone);
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedDraft4) {
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\":\"http://json-schema.org/draft-04/schema#\", \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
EXPECT_TRUE(s.GetError().ObjectEmpty());
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedDraft4NoFrag) {
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\":\"http://json-schema.org/draft-04/schema\", \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
EXPECT_TRUE(s.GetError().ObjectEmpty());
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedDraft5) {
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\":\"http://json-schema.org/draft-05/schema#\", \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft05);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
EXPECT_TRUE(s.GetError().ObjectEmpty());
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedDraft5NoFrag) {
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\":\"http://json-schema.org/draft-05/schema\", \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft05);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
EXPECT_TRUE(s.GetError().ObjectEmpty());
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_IgnoreDraftEmbedded) {
|
||||
Document sd;
|
||||
sd.Parse("{\"root\": {\"$schema\":\"http://json-schema.org/draft-05/schema#\", \"type\": \"integer\"}}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, SchemaDocument::PointerType("/root"));
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
EXPECT_TRUE(s.GetError().ObjectEmpty());
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedDraftOverride) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kDraft04));
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
EXPECT_TRUE(s.GetError().ObjectEmpty());
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnknownDraftOverride) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kDraftUnknown));
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraftUnknown);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnsupportedDraftOverride) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kDraft03));
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft03);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnknownDraft) {
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\":\"http://json-schema.org/draft-xxx/schema#\", \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraftUnknown);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnknownDraftNotString) {
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\": 4, \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraftUnknown);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnsupportedDraft3) {
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\":\"http://json-schema.org/draft-03/schema#\", \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft03);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnsupportedDraft6) {
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\":\"http://json-schema.org/draft-06/schema#\", \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft06);
|
||||
SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnsupportedDraft7) {
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\":\"http://json-schema.org/draft-07/schema#\", \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft07);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnsupportedDraft2019_09) {
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\":\"https://json-schema.org/draft/2019-09/schema\", \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft2019_09);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnsupportedDraft2020_12) {
|
||||
Document sd;
|
||||
sd.Parse("{\"$schema\":\"https://json-schema.org/draft/2020-12/schema\", \"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft2020_12);
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionNone);
|
||||
SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedVersion20Static) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"swagger\":\"2.0\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
Specification spec = SchemaDocumentType::GetSpecification(sd);
|
||||
ASSERT_TRUE(spec.IsSupported());
|
||||
ASSERT_TRUE(spec.draft == kDraft04);
|
||||
ASSERT_TRUE(spec.oapi == kVersion20);
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedVersion20) {
|
||||
Document sd;
|
||||
sd.Parse("{\"swagger\":\"2.0\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersion20);
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
EXPECT_TRUE(s.GetError().ObjectEmpty());
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedVersion30x) {
|
||||
Document sd;
|
||||
sd.Parse("{\"openapi\":\"3.0.0\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersion30);
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft05);
|
||||
EXPECT_TRUE(s.GetError().ObjectEmpty());
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_SupportedVersionOverride) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kVersion20));
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersion20);
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
EXPECT_TRUE(s.GetError().ObjectEmpty());
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnknownVersionOverride) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kVersionUnknown));
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionUnknown);
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnsupportedVersionOverride) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"integer\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kVersion31));
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersion31);
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft2020_12);
|
||||
SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnknownVersion) {
|
||||
Document sd;
|
||||
sd.Parse("{\"openapi\":\"1.0\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionUnknown);
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnknownVersionShort) {
|
||||
Document sd;
|
||||
sd.Parse("{\"openapi\":\"3.0.\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionUnknown);
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnknownVersionNotString) {
|
||||
Document sd;
|
||||
sd.Parse("{\"swagger\": 2}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersionUnknown);
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft04);
|
||||
SCHEMAERROR(s, "{\"SpecUnknown\":{\"errorCode\":10,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_UnsupportedVersion31) {
|
||||
Document sd;
|
||||
sd.Parse("{\"openapi\":\"3.1.0\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_FALSE(s.IsSupportedSpecification());
|
||||
ASSERT_TRUE(s.GetSpecification().oapi == kVersion31);
|
||||
ASSERT_TRUE(s.GetSpecification().draft == kDraft2020_12);
|
||||
SCHEMAERROR(s, "{\"SpecUnsupported\":{\"errorCode\":11,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_DraftAndVersion) {
|
||||
Document sd;
|
||||
sd.Parse("{\"swagger\": \"2.0\", \"$schema\": \"http://json-schema.org/draft-04/schema#\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
ASSERT_TRUE(s.IsSupportedSpecification());
|
||||
SCHEMAERROR(s, "{\"SpecIllegal\":{\"errorCode\":12,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_StartUnknown) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"integer\"}");
|
||||
@ -3007,6 +3329,25 @@ TEST(SchemaValidator, Schema_StartUnknown) {
|
||||
SCHEMAERROR(s, "{\"StartUnknown\":{\"errorCode\":1,\"instanceRef\":\"#\", \"value\":\"#/nowhere\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_MultipleErrors) {
|
||||
Document sd;
|
||||
sd.Parse("{\"swagger\": \"foo\", \"$schema\": \"bar\"}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s(sd);
|
||||
SCHEMAERROR(s, "{ \"SpecUnknown\": {\"errorCode\":10,\"instanceRef\":\"#\"},"
|
||||
" \"SpecIllegal\": {\"errorCode\":12,\"instanceRef\":\"#\"}"
|
||||
"}");
|
||||
}
|
||||
|
||||
// $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;
|
||||
@ -3019,9 +3360,10 @@ TEST(SchemaValidator, Schema_RefPlainNameRemote) {
|
||||
|
||||
// $ref is an empty string
|
||||
TEST(SchemaValidator, Schema_RefEmptyString) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt1\": {\"$ref\": \"\"}}}");
|
||||
SchemaDocument s(sd);
|
||||
SchemaDocumentType s(sd);
|
||||
SCHEMAERROR(s, "{\"RefInvalid\":{\"errorCode\":3,\"instanceRef\":\"#/properties/myInt1\"}}");
|
||||
}
|
||||
|
||||
@ -3046,9 +3388,10 @@ TEST(SchemaValidator, Schema_RefNoRemoteSchema) {
|
||||
|
||||
// $ref pointer is invalid
|
||||
TEST(SchemaValidator, Schema_RefPointerInvalid) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"#/&&&&&\"}}}");
|
||||
SchemaDocument s(sd);
|
||||
SchemaDocumentType s(sd);
|
||||
SCHEMAERROR(s, "{\"RefPointerInvalid\":{\"errorCode\":4,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#/&&&&&\",\"offset\":2}}");
|
||||
}
|
||||
|
||||
@ -3064,17 +3407,19 @@ TEST(SchemaValidator, Schema_RefPointerInvalidRemote) {
|
||||
|
||||
// $ref is unknown non-pointer
|
||||
TEST(SchemaValidator, Schema_RefUnknownPlainName) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {\"myInt\": {\"$ref\": \"#plainname\"}}}");
|
||||
SchemaDocument s(sd);
|
||||
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\"}}}");
|
||||
SchemaDocument s(sd);
|
||||
SchemaDocumentType s(sd);
|
||||
SCHEMAERROR(s, "{\"RefUnknown\":{\"errorCode\":5,\"instanceRef\":\"#/properties/myInt\",\"value\":\"#/a/b\"}}");
|
||||
}
|
||||
|
||||
@ -3090,6 +3435,7 @@ TEST(SchemaValidator, Schema_RefUnknownPointerRemote) {
|
||||
|
||||
// $ref is cyclical
|
||||
TEST(SchemaValidator, Schema_RefCyclical) {
|
||||
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"object\", \"properties\": {"
|
||||
" \"cyclic_source\": {"
|
||||
@ -3099,10 +3445,130 @@ TEST(SchemaValidator, Schema_RefCyclical) {
|
||||
" \"$ref\": \"#/properties/cyclic_source\""
|
||||
" }"
|
||||
"}}");
|
||||
SchemaDocument s(sd);
|
||||
SchemaDocumentType s(sd);
|
||||
SCHEMAERROR(s, "{\"RefCyclical\":{\"errorCode\":6,\"instanceRef\":\"#/properties/cyclic_target\",\"value\":\"#/properties/cyclic_source\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, Schema_ReadOnlyAndWriteOnly) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"integer\", \"readOnly\": true, \"writeOnly\": true}");
|
||||
ASSERT_FALSE(sd.HasParseError());
|
||||
SchemaDocument s1(sd, 0, 0, 0, 0, 0, Specification(kDraft04));
|
||||
EXPECT_TRUE(s1.GetError().ObjectEmpty());
|
||||
SchemaDocument s2(sd, 0, 0, 0, 0, 0, Specification(kVersion30));
|
||||
SCHEMAERROR(s2, "{\"ReadOnlyAndWriteOnly\":{\"errorCode\":13,\"instanceRef\":\"#\"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, ReadOnlyWhenWriting) {
|
||||
Document sd;
|
||||
sd.Parse(
|
||||
"{"
|
||||
" \"type\":\"object\","
|
||||
" \"properties\": {"
|
||||
" \"rprop\" : {"
|
||||
" \"type\": \"string\","
|
||||
" \"readOnly\": true"
|
||||
" }"
|
||||
" }"
|
||||
"}");
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kVersion20));
|
||||
VALIDATE(s, "{ \"rprop\": \"hello\" }", true);
|
||||
INVALIDATE_(s, "{ \"rprop\": \"hello\" }", "/properties/rprop", "readOnly", "/rprop",
|
||||
"{ \"readOnly\": {"
|
||||
" \"errorCode\": 26, \"instanceRef\": \"#/rprop\", \"schemaRef\": \"#/properties/rprop\""
|
||||
" }"
|
||||
"}",
|
||||
kValidateDefaultFlags | kValidateWriteFlag, SchemaValidator, Pointer);
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, WriteOnlyWhenReading) {
|
||||
Document sd;
|
||||
sd.Parse(
|
||||
"{"
|
||||
" \"type\":\"object\","
|
||||
" \"properties\": {"
|
||||
" \"wprop\" : {"
|
||||
" \"type\": \"boolean\","
|
||||
" \"writeOnly\": true"
|
||||
" }"
|
||||
" }"
|
||||
"}");
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, 0, Specification(kVersion30));
|
||||
VALIDATE(s, "{ \"wprop\": true }", true);
|
||||
INVALIDATE_(s, "{ \"wprop\": true }", "/properties/wprop", "writeOnly", "/wprop",
|
||||
"{ \"writeOnly\": {"
|
||||
" \"errorCode\": 27, \"instanceRef\": \"#/wprop\", \"schemaRef\": \"#/properties/wprop\""
|
||||
" }"
|
||||
"}",
|
||||
kValidateDefaultFlags | kValidateReadFlag, SchemaValidator, Pointer);
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, NullableTrue) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"string\", \"nullable\": true}");
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, 0, kVersion20);
|
||||
|
||||
VALIDATE(s, "\"hello\"", true);
|
||||
INVALIDATE(s, "null", "", "type", "",
|
||||
"{ \"type\": {"
|
||||
" \"errorCode\": 20,"
|
||||
" \"instanceRef\": \"#\", \"schemaRef\": \"#\","
|
||||
" \"expected\": [\"string\"], \"actual\": \"null\""
|
||||
"}}");
|
||||
INVALIDATE(s, "false", "", "type", "",
|
||||
"{ \"type\": {"
|
||||
" \"errorCode\": 20,"
|
||||
" \"instanceRef\": \"#\", \"schemaRef\": \"#\","
|
||||
" \"expected\": [\"string\"], \"actual\": \"boolean\""
|
||||
"}}");
|
||||
|
||||
SchemaDocument s30(sd, 0, 0, 0, 0, 0, kVersion30);
|
||||
|
||||
VALIDATE(s30, "\"hello\"", true);
|
||||
VALIDATE(s30, "null", true);
|
||||
INVALIDATE(s30, "false", "", "type", "",
|
||||
"{ \"type\": {"
|
||||
" \"errorCode\": 20,"
|
||||
" \"instanceRef\": \"#\", \"schemaRef\": \"#\","
|
||||
" \"expected\": [\"null\", \"string\"], \"actual\": \"boolean\""
|
||||
"}}");
|
||||
}
|
||||
|
||||
TEST(SchemaValidator, NullableFalse) {
|
||||
Document sd;
|
||||
sd.Parse("{\"type\": \"string\", \"nullable\": false}");
|
||||
SchemaDocument s(sd, 0, 0, 0, 0, 0, kVersion20);
|
||||
|
||||
VALIDATE(s, "\"hello\"", true);
|
||||
INVALIDATE(s, "null", "", "type", "",
|
||||
"{ \"type\": {"
|
||||
" \"errorCode\": 20,"
|
||||
" \"instanceRef\": \"#\", \"schemaRef\": \"#\","
|
||||
" \"expected\": [\"string\"], \"actual\": \"null\""
|
||||
"}}");
|
||||
INVALIDATE(s, "false", "", "type", "",
|
||||
"{ \"type\": {"
|
||||
" \"errorCode\": 20,"
|
||||
" \"instanceRef\": \"#\", \"schemaRef\": \"#\","
|
||||
" \"expected\": [\"string\"], \"actual\": \"boolean\""
|
||||
"}}");
|
||||
|
||||
SchemaDocument s30(sd, 0, 0, 0, 0, 0, kVersion30);
|
||||
|
||||
VALIDATE(s30, "\"hello\"", true);
|
||||
INVALIDATE(s, "null", "", "type", "",
|
||||
"{ \"type\": {"
|
||||
" \"errorCode\": 20,"
|
||||
" \"instanceRef\": \"#\", \"schemaRef\": \"#\","
|
||||
" \"expected\": [\"string\"], \"actual\": \"null\""
|
||||
"}}");
|
||||
INVALIDATE(s30, "false", "", "type", "",
|
||||
"{ \"type\": {"
|
||||
" \"errorCode\": 20,"
|
||||
" \"instanceRef\": \"#\", \"schemaRef\": \"#\","
|
||||
" \"expected\": [\"string\"], \"actual\": \"boolean\""
|
||||
"}}");
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER) || defined(__clang__)
|
||||
RAPIDJSON_DIAG_POP
|
||||
|
Loading…
x
Reference in New Issue
Block a user