Merge pull request from rayvincent2/feature/add-urn-reference-support

Add support for urn document references
This commit is contained in:
Tristan Penman 2021-08-26 10:12:09 +10:00 committed by GitHub
commit ad7dac75a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 108 additions and 12 deletions

View File

@ -95,7 +95,8 @@ if(valijson_BUILD_TESTS)
set(TEST_SOURCES set(TEST_SOURCES
tests/test_adapter_comparison.cpp tests/test_adapter_comparison.cpp
tests/test_fetch_document_callback.cpp tests/test_fetch_urn_document_callback.cpp
tests/test_fetch_absolute_uri_document_callback.cpp
tests/test_json_pointer.cpp tests/test_json_pointer.cpp
tests/test_json11_adapter.cpp tests/test_json11_adapter.cpp
tests/test_jsoncpp_adapter.cpp tests/test_jsoncpp_adapter.cpp

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
#include <regex>
#include <string> #include <string>
namespace valijson { namespace valijson {
@ -19,8 +20,20 @@ inline bool isUriAbsolute(const std::string &documentUri)
} }
/** /**
* Placeholder function to resolve a relative URI within a given scope * @brief Placeholder function to check whether a URI is a URN
*/ *
* This function validates that the URI matches the RFC 8141 spec
*/
inline bool isUrn(const std::string &documentUri) {
static const std::regex pattern(
"^((urn)|(URN)):(?!urn:)([a-zA-Z0-9][a-zA-Z0-9-]{1,31})(:[-a-zA-Z0-9\\\\._~%!$&'()\\/*+,;=]+)+(\\?[-a-zA-Z0-9\\\\._~%!$&'()\\/*+,;:=]+){0,1}(#[-a-zA-Z0-9\\\\._~%!$&'()\\/*+,;:=]+){0,1}$");
return std::regex_match(documentUri, pattern);
}
/**
* Placeholder function to resolve a relative URI within a given scope
*/
inline std::string resolveRelativeUri( inline std::string resolveRelativeUri(
const std::string &resolutionScope, const std::string &resolutionScope,
const std::string &relativeUri) const std::string &relativeUri)

View File

@ -200,7 +200,7 @@ private:
{ {
if (resolutionScope) { if (resolutionScope) {
if (documentUri) { if (documentUri) {
if (internal::uri::isUriAbsolute(*documentUri)) { if (internal::uri::isUriAbsolute(*documentUri) || internal::uri::isUrn(*documentUri)) {
return *documentUri; return *documentUri;
} else { } else {
return internal::uri::resolveRelativeUri(*resolutionScope, *documentUri); return internal::uri::resolveRelativeUri(*resolutionScope, *documentUri);
@ -210,6 +210,8 @@ private:
} }
} else if (documentUri && internal::uri::isUriAbsolute(*documentUri)) { } else if (documentUri && internal::uri::isUriAbsolute(*documentUri)) {
return *documentUri; return *documentUri;
} else if (documentUri && internal::uri::isUrn(*documentUri)) {
return *documentUri;
} else { } else {
return opt::optional<std::string>(); return opt::optional<std::string>();
} }
@ -600,7 +602,7 @@ private:
if ((itr = object.find("id")) != object.end() && itr->second.maybeString()) { if ((itr = object.find("id")) != object.end() && itr->second.maybeString()) {
const std::string id = itr->second.asString(); const std::string id = itr->second.asString();
rootSchema.setSubschemaId(&subschema, itr->second.asString()); rootSchema.setSubschemaId(&subschema, itr->second.asString());
if (!currentScope || internal::uri::isUriAbsolute(id)) { if (!currentScope || internal::uri::isUriAbsolute(id) || internal::uri::isUrn(id)) {
updatedScope = id; updatedScope = id;
} else { } else {
updatedScope = internal::uri::resolveRelativeUri(*currentScope, id); updatedScope = internal::uri::resolveRelativeUri(*currentScope, id);
@ -993,7 +995,7 @@ private:
const std::string actualJsonPointer = sanitiseJsonPointer( const std::string actualJsonPointer = sanitiseJsonPointer(
internal::json_reference::getJsonReferencePointer(jsonRef)); internal::json_reference::getJsonReferencePointer(jsonRef));
if (documentUri && internal::uri::isUriAbsolute(*documentUri)) { if (documentUri && (internal::uri::isUriAbsolute(*documentUri) || internal::uri::isUrn(*documentUri))) {
// Resolve reference against remote document // Resolve reference against remote document
if (!fetchDoc) { if (!fetchDoc) {
throwRuntimeError("Fetching of remote JSON References not enabled."); throwRuntimeError("Fetching of remote JSON References not enabled.");

View File

@ -12,12 +12,12 @@ using valijson::SchemaParser;
using valijson::adapters::RapidJsonAdapter; using valijson::adapters::RapidJsonAdapter;
using valijson::Validator; using valijson::Validator;
class TestFetchDocumentCallback : public ::testing::Test class TestFetchAbsoluteUriDocumentCallback : public ::testing::Test
{ {
}; };
const rapidjson::Document * fetchDocument(const std::string &uri) const rapidjson::Document * fetchAbsoluteUriDocument(const std::string &uri)
{ {
EXPECT_STREQ("http://localhost:1234/", uri.c_str()); EXPECT_STREQ("http://localhost:1234/", uri.c_str());
@ -43,12 +43,12 @@ const rapidjson::Document * fetchDocument(const std::string &uri)
return fetchedRoot; return fetchedRoot;
} }
void freeDocument(const rapidjson::Document *adapter) void freeAbsoluteUriDocument(const rapidjson::Document *adapter)
{ {
delete adapter; delete adapter;
} }
TEST_F(TestFetchDocumentCallback, Basics) TEST_F(TestFetchAbsoluteUriDocumentCallback, Basics)
{ {
// Define schema // Define schema
rapidjson::Document schemaDocument; rapidjson::Document schemaDocument;
@ -60,8 +60,8 @@ TEST_F(TestFetchDocumentCallback, Basics)
// Parse schema document // Parse schema document
Schema schema; Schema schema;
SchemaParser schemaParser; SchemaParser schemaParser;
schemaParser.populateSchema(schemaDocumentAdapter, schema, fetchDocument, schemaParser.populateSchema(schemaDocumentAdapter, schema, fetchAbsoluteUriDocument,
freeDocument); freeAbsoluteUriDocument);
// Test resulting schema with a valid document // Test resulting schema with a valid document
rapidjson::Document validDocument; rapidjson::Document validDocument;

View File

@ -0,0 +1,80 @@
#include <gtest/gtest.h>
#include <valijson/adapters/rapidjson_adapter.hpp>
#include <valijson/schema.hpp>
#include <valijson/schema_parser.hpp>
#include <valijson/validator.hpp>
using valijson::Schema;
using valijson::SchemaParser;
using valijson::adapters::RapidJsonAdapter;
using valijson::Validator;
class TestFetchUrnDocumentCallback : public ::testing::Test
{
};
const rapidjson::Document * fetchUrnDocument(const std::string &uri)
{
EXPECT_STREQ("urn:mvn:example.schema.common:status:1.1.0", uri.c_str());
rapidjson::Document *fetchedRoot = new rapidjson::Document();
fetchedRoot->SetObject();
rapidjson::Value valueOfTypeAttribute;
valueOfTypeAttribute.SetString("string", fetchedRoot->GetAllocator());
rapidjson::Value schemaOfTestProperty;
schemaOfTestProperty.SetObject();
schemaOfTestProperty.AddMember("type", valueOfTypeAttribute,
fetchedRoot->GetAllocator());
rapidjson::Value propertiesConstraint;
propertiesConstraint.SetObject();
propertiesConstraint.AddMember("test", schemaOfTestProperty,
fetchedRoot->GetAllocator());
fetchedRoot->AddMember("properties", propertiesConstraint,
fetchedRoot->GetAllocator());
return fetchedRoot;
}
void freeUrnDocument(const rapidjson::Document *adapter)
{
delete adapter;
}
TEST_F(TestFetchUrnDocumentCallback, Basics)
{
// Define schema
rapidjson::Document schemaDocument;
RapidJsonAdapter schemaDocumentAdapter(schemaDocument);
schemaDocument.SetObject();
schemaDocument.AddMember("$ref", "urn:mvn:example.schema.common:status:1.1.0",
schemaDocument.GetAllocator());
// Parse schema document
Schema schema;
SchemaParser schemaParser;
schemaParser.populateSchema(schemaDocumentAdapter, schema, fetchUrnDocument,
freeUrnDocument);
// Test resulting schema with a valid document
rapidjson::Document validDocument;
validDocument.SetObject();
validDocument.AddMember("test", "valid", schemaDocument.GetAllocator());
Validator validator;
EXPECT_TRUE(validator.validate(schema, RapidJsonAdapter(validDocument),
NULL));
// Test resulting schema with an invalid document
rapidjson::Document invalidDocument;
invalidDocument.SetObject();
invalidDocument.AddMember("test", 123, schemaDocument.GetAllocator());
EXPECT_FALSE(validator.validate(schema, RapidJsonAdapter(invalidDocument),
NULL));
}