Skip to content

Commit e620979

Browse files
Karan-Palanjviotti
andauthored
Linter: create rule to remove empty fragment (#1825)
Signed-off-by: karan-palan <karanpalan007@gmail.com> Co-authored-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent 977dc10 commit e620979

File tree

7 files changed

+146
-1
lines changed

7 files changed

+146
-1
lines changed

src/core/jsonschema/jsonschema.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,8 @@ auto sourcemeta::core::vocabularies(const SchemaResolver &resolver,
443443
// complexity of the generic `id` function.
444444
assert(schema_dialect.defines("$id") &&
445445
schema_dialect.at("$id").is_string() &&
446-
schema_dialect.at("$id").to_string() == dialect);
446+
URI::canonicalize(schema_dialect.at("$id").to_string()) ==
447+
URI::canonicalize(dialect));
447448

448449
/*
449450
* (4) Retrieve the vocabularies explicitly or implicitly declared by the

src/core/jsonschema/official_resolver.in.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ auto sourcemeta::core::schema_official_resolver(std::string_view identifier)
5656
} else if (identifier == "https://json-schema.org/draft/2020-12/schema#") {
5757
return sourcemeta::core::parse_json(
5858
R"EOF(@METASCHEMA_JSONSCHEMA_2020_12@)EOF");
59+
} else if (identifier ==
60+
"https://json-schema.org/draft/2020-12/hyper-schema#") {
61+
return sourcemeta::core::parse_json(
62+
R"EOF(@METASCHEMA_HYPERSCHEMA_2020_12@)EOF");
5963

6064
// JSON Schema 2019-09
6165
} else if (identifier == "https://json-schema.org/draft/2019-09/schema") {
@@ -107,6 +111,10 @@ auto sourcemeta::core::schema_official_resolver(std::string_view identifier)
107111
} else if (identifier == "https://json-schema.org/draft/2019-09/schema#") {
108112
return sourcemeta::core::parse_json(
109113
R"EOF(@METASCHEMA_JSONSCHEMA_2019_09@)EOF");
114+
} else if (identifier ==
115+
"https://json-schema.org/draft/2019-09/hyper-schema#") {
116+
return sourcemeta::core::parse_json(
117+
R"EOF(@METASCHEMA_HYPERSCHEMA_2019_09@)EOF");
110118

111119
// JSON Schema Draft7
112120
} else if (identifier == "http://json-schema.org/draft-07/schema#" ||

src/extension/alterschema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema
5959
linter/if_without_then_else.h
6060
linter/max_contains_without_contains.h
6161
linter/min_contains_without_contains.h
62+
linter/modern_official_dialect_with_empty_fragment.h
6263
linter/then_without_if.h)
6364

6465
if(SOURCEMETA_CORE_INSTALL)

src/extension/alterschema/alterschema.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ contains_any(const Vocabularies &container,
6363
#include "linter/maximum_real_for_integer.h"
6464
#include "linter/min_contains_without_contains.h"
6565
#include "linter/minimum_real_for_integer.h"
66+
#include "linter/modern_official_dialect_with_empty_fragment.h"
6667
#include "linter/multiple_of_default.h"
6768
#include "linter/non_applicable_type_specific_keywords.h"
6869
#include "linter/pattern_properties_default.h"
@@ -109,6 +110,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode)
109110
bundle.add<DuplicateRequiredValues>();
110111
bundle.add<ConstWithType>();
111112
bundle.add<AdditionalItemsWithSchemaItems>();
113+
bundle.add<ModernOfficialDialectWithEmptyFragment>();
112114
bundle.add<ExclusiveMaximumNumberAndMaximum>();
113115
bundle.add<ExclusiveMinimumNumberAndMinimum>();
114116

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
class ModernOfficialDialectWithEmptyFragment final
2+
: public SchemaTransformRule {
3+
public:
4+
ModernOfficialDialectWithEmptyFragment()
5+
: SchemaTransformRule{
6+
"modern_official_dialect_with_empty_fragment",
7+
"The official dialect URI of 2019-09 and newer versions must "
8+
"not contain the empty fragment"} {};
9+
10+
[[nodiscard]] auto condition(const sourcemeta::core::JSON &schema,
11+
const sourcemeta::core::JSON &,
12+
const sourcemeta::core::Vocabularies &,
13+
const sourcemeta::core::SchemaFrame &,
14+
const sourcemeta::core::SchemaFrame::Location &,
15+
const sourcemeta::core::SchemaWalker &,
16+
const sourcemeta::core::SchemaResolver &) const
17+
-> sourcemeta::core::SchemaTransformRule::Result override {
18+
if (!schema.is_object() || !schema.defines("$schema") ||
19+
!schema.at("$schema").is_string()) {
20+
return false;
21+
}
22+
23+
const auto &schema_value = schema.at("$schema").to_string();
24+
return (
25+
schema_value == "https://json-schema.org/draft/2019-09/schema#" ||
26+
schema_value == "https://json-schema.org/draft/2019-09/hyper-schema#" ||
27+
schema_value == "https://json-schema.org/draft/2020-12/schema#" ||
28+
schema_value == "https://json-schema.org/draft/2020-12/hyper-schema#");
29+
}
30+
31+
auto transform(sourcemeta::core::JSON &schema) const -> void override {
32+
auto schema_value = schema.at("$schema").to_string();
33+
schema_value.pop_back();
34+
schema.at("$schema").into(sourcemeta::core::JSON{std::move(schema_value)});
35+
}
36+
};

test/alterschema/alterschema_lint_2019_09_test.cc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1984,3 +1984,51 @@ TEST(AlterSchema_lint_2019_09, additional_items_with_schema_items_array_items) {
19841984

19851985
EXPECT_EQ(document, expected);
19861986
}
1987+
1988+
TEST(AlterSchema_lint_2019_09, modern_official_dialect_with_empty_fragment_1) {
1989+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
1990+
"$schema": "https://json-schema.org/draft/2019-09/schema#",
1991+
"type": "string"
1992+
})JSON");
1993+
1994+
LINT_AND_FIX_FOR_READABILITY(document);
1995+
1996+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
1997+
"$schema": "https://json-schema.org/draft/2019-09/schema",
1998+
"type": "string"
1999+
})JSON");
2000+
2001+
EXPECT_EQ(document, expected);
2002+
}
2003+
2004+
TEST(AlterSchema_lint_2019_09, modern_official_dialect_with_empty_fragment_2) {
2005+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2006+
"$schema": "https://json-schema.org/draft/2019-09/hyper-schema#",
2007+
"type": "string"
2008+
})JSON");
2009+
2010+
LINT_AND_FIX_FOR_READABILITY(document);
2011+
2012+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
2013+
"$schema": "https://json-schema.org/draft/2019-09/hyper-schema",
2014+
"type": "string"
2015+
})JSON");
2016+
2017+
EXPECT_EQ(document, expected);
2018+
}
2019+
2020+
TEST(AlterSchema_lint_2019_09, modern_official_dialect_with_empty_fragment_3) {
2021+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2022+
"$schema": "https://json-schema.org/draft/2019-09/schema",
2023+
"type": "string"
2024+
})JSON");
2025+
2026+
LINT_AND_FIX_FOR_READABILITY(document);
2027+
2028+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
2029+
"$schema": "https://json-schema.org/draft/2019-09/schema",
2030+
"type": "string"
2031+
})JSON");
2032+
2033+
EXPECT_EQ(document, expected);
2034+
}

test/alterschema/alterschema_lint_2020_12_test.cc

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2158,3 +2158,52 @@ TEST(AlterSchema_lint_2020_12, unnecessary_allof_wrapper_properties_4) {
21582158

21592159
EXPECT_EQ(document, expected);
21602160
}
2161+
2162+
TEST(AlterSchema_lint_2020_12, modern_official_dialect_with_empty_fragment_1) {
2163+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2164+
"$schema": "https://json-schema.org/draft/2020-12/schema#",
2165+
"type": "string"
2166+
})JSON");
2167+
2168+
LINT_AND_FIX_FOR_READABILITY(document);
2169+
2170+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
2171+
"$schema": "https://json-schema.org/draft/2020-12/schema",
2172+
"type": "string"
2173+
})JSON");
2174+
2175+
EXPECT_EQ(document, expected);
2176+
}
2177+
2178+
TEST(AlterSchema_lint_2020_12, modern_official_dialect_with_empty_fragment_2) {
2179+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2180+
"$schema": "https://json-schema.org/draft/2020-12/hyper-schema#",
2181+
"type": "string"
2182+
})JSON");
2183+
2184+
LINT_AND_FIX_FOR_READABILITY(document);
2185+
2186+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
2187+
"$schema": "https://json-schema.org/draft/2020-12/hyper-schema",
2188+
"type": "string"
2189+
})JSON");
2190+
2191+
EXPECT_EQ(document, expected);
2192+
}
2193+
2194+
TEST(AlterSchema_lint_2020_12, modern_official_dialect_with_empty_fragment_3) {
2195+
sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({
2196+
"$schema": "https://json-schema.org/draft/2020-12/schema",
2197+
"type": "string"
2198+
})JSON");
2199+
2200+
LINT_AND_FIX_FOR_READABILITY(document);
2201+
2202+
// Should remain unchanged since there's no empty fragment
2203+
const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({
2204+
"$schema": "https://json-schema.org/draft/2020-12/schema",
2205+
"type": "string"
2206+
})JSON");
2207+
2208+
EXPECT_EQ(document, expected);
2209+
}

0 commit comments

Comments
 (0)