diff --git a/src/extension/alterschema/CMakeLists.txt b/src/extension/alterschema/CMakeLists.txt index 74d69b07e..c75f78c1d 100644 --- a/src/extension/alterschema/CMakeLists.txt +++ b/src/extension/alterschema/CMakeLists.txt @@ -47,6 +47,7 @@ sourcemeta_library(NAMESPACE sourcemeta PROJECT core NAME alterschema linter/unsatisfiable_min_properties.h linter/content_media_type_without_encoding.h linter/content_schema_without_media_type.h + linter/additional_items_with_schema_items.h linter/non_applicable_type_specific_keywords.h linter/unnecessary_allof_wrapper_modern.h linter/unnecessary_allof_wrapper_draft.h diff --git a/src/extension/alterschema/alterschema.cc b/src/extension/alterschema/alterschema.cc index 8869201c6..31a8c653f 100644 --- a/src/extension/alterschema/alterschema.cc +++ b/src/extension/alterschema/alterschema.cc @@ -35,6 +35,7 @@ contains_any(const Vocabularies &container, #include "canonicalizer/type_union_implicit.h" // Linter +#include "linter/additional_items_with_schema_items.h" #include "linter/additional_properties_default.h" #include "linter/const_with_type.h" #include "linter/content_media_type_without_encoding.h" @@ -105,6 +106,7 @@ auto add(SchemaTransformer &bundle, const AlterSchemaMode mode) bundle.add(); bundle.add(); bundle.add(); + bundle.add(); bundle.add(); bundle.add(); diff --git a/src/extension/alterschema/linter/additional_items_with_schema_items.h b/src/extension/alterschema/linter/additional_items_with_schema_items.h new file mode 100644 index 000000000..595a0da75 --- /dev/null +++ b/src/extension/alterschema/linter/additional_items_with_schema_items.h @@ -0,0 +1,31 @@ +class AdditionalItemsWithSchemaItems final : public SchemaTransformRule { +public: + AdditionalItemsWithSchemaItems() + : SchemaTransformRule{"additional_items_with_schema_items", + "The `additionalItems` keyword is ignored when the " + "`items` keyword is set to a schema"} {}; + + [[nodiscard]] auto + condition(const sourcemeta::core::JSON &schema, + const sourcemeta::core::JSON &, + const sourcemeta::core::Vocabularies &vocabularies, + const sourcemeta::core::SchemaFrame &, + const sourcemeta::core::SchemaFrame::Location &, + const sourcemeta::core::SchemaWalker &, + const sourcemeta::core::SchemaResolver &) const + -> sourcemeta::core::SchemaTransformRule::Result override { + return contains_any( + vocabularies, + {"https://json-schema.org/draft/2019-09/vocab/applicator", + "http://json-schema.org/draft-07/schema#", + "http://json-schema.org/draft-06/schema#", + "http://json-schema.org/draft-04/schema#", + "http://json-schema.org/draft-03/schema#"}) && + schema.is_object() && schema.defines("items") && + schema.defines("additionalItems") && is_schema(schema.at("items")); + } + + auto transform(JSON &schema) const -> void override { + schema.erase("additionalItems"); + } +}; diff --git a/test/alterschema/alterschema_lint_2019_09_test.cc b/test/alterschema/alterschema_lint_2019_09_test.cc index 581c6f30d..8edb8f8ab 100644 --- a/test/alterschema/alterschema_lint_2019_09_test.cc +++ b/test/alterschema/alterschema_lint_2019_09_test.cc @@ -1879,3 +1879,108 @@ TEST(AlterSchema_lint_2019_09, unnecessary_allof_wrapper_properties_4) { EXPECT_EQ(document, expected); } + +TEST(AlterSchema_lint_2019_09, additional_items_with_schema_items_1) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "items": { + "type": "number" + }, + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "items": { + "type": "number" + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2019_09, additional_items_with_schema_items_2) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "items": { + "unevaluatedProperties": false + }, + "additionalItems": { + "type": "string" + } + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "items": { + "unevaluatedProperties": false + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2019_09, + additional_items_with_schema_items_boolean_items_true) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "items": true, + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2019_09, + additional_items_with_schema_items_boolean_items_false) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "items": false, + "additionalItems": { + "type": "string" + } + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "items": false + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_2019_09, additional_items_with_schema_items_array_items) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "items": [ + { "type": "string" }, + { "type": "number" } + ], + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "items": [ + { "type": "string" }, + { "type": "number" } + ], + "additionalItems": false + })JSON"); + + EXPECT_EQ(document, expected); +} diff --git a/test/alterschema/alterschema_lint_draft3_test.cc b/test/alterschema/alterschema_lint_draft3_test.cc index 210d322da..6c9dc91c1 100644 --- a/test/alterschema/alterschema_lint_draft3_test.cc +++ b/test/alterschema/alterschema_lint_draft3_test.cc @@ -579,3 +579,108 @@ TEST(AlterSchema_lint_draft3, equal_numeric_bounds_to_enum_2) { EXPECT_EQ(document, expected); } + +TEST(AlterSchema_lint_draft3, additional_items_with_schema_items_1) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "items": { + "type": "number" + }, + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "items": { + "type": "number" + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft3, additional_items_with_schema_items_2) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "items": { + "type": "string" + }, + "additionalItems": { + "type": "boolean" + } + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "items": { + "type": "string" + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft3, + additional_items_with_schema_items_boolean_items_true) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "items": true, + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft3, + additional_items_with_schema_items_boolean_items_false) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "items": false, + "additionalItems": { + "type": "string" + } + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "items": false + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft3, additional_items_with_schema_items_array_items) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "items": [ + { "type": "string" }, + { "type": "number" } + ], + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-03/schema#", + "items": [ + { "type": "string" }, + { "type": "number" } + ], + "additionalItems": false + })JSON"); + + EXPECT_EQ(document, expected); +} diff --git a/test/alterschema/alterschema_lint_draft4_test.cc b/test/alterschema/alterschema_lint_draft4_test.cc index 121cd4d30..ef0fd91c4 100644 --- a/test/alterschema/alterschema_lint_draft4_test.cc +++ b/test/alterschema/alterschema_lint_draft4_test.cc @@ -921,3 +921,128 @@ TEST(AlterSchema_lint_draft4, unnecessary_allof_wrapper_properties_1) { EXPECT_EQ(document, expected); } + +TEST(AlterSchema_lint_draft4, additional_items_with_schema_items_1) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "items": { + "type": "number" + }, + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "items": { + "type": "number" + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft4, additional_items_with_schema_items_2) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "items": { + "type": "string" + }, + "additionalItems": { + "type": "boolean" + } + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "array", + "items": { + "type": "string" + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft4, + additional_items_with_schema_items_no_change_array_items) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "items": [ + { "type": "string" }, + { "type": "number" } + ], + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "items": [ + { "type": "string" }, + { "type": "number" } + ], + "additionalItems": false + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft4, + additional_items_with_schema_items_no_change_no_items) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "additionalItems": false + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft4, + additional_items_with_schema_items_boolean_items_true) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "items": true, + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft4, + additional_items_with_schema_items_boolean_items_false) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "items": false, + "additionalItems": { + "type": "string" + } + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-04/schema#", + "items": false + })JSON"); + + EXPECT_EQ(document, expected); +} diff --git a/test/alterschema/alterschema_lint_draft6_test.cc b/test/alterschema/alterschema_lint_draft6_test.cc index 0a85afd5d..0990d943a 100644 --- a/test/alterschema/alterschema_lint_draft6_test.cc +++ b/test/alterschema/alterschema_lint_draft6_test.cc @@ -1183,3 +1183,106 @@ TEST(AlterSchema_lint_draft6, unnecessary_allof_wrapper_properties_1) { EXPECT_EQ(document, expected); } + +TEST(AlterSchema_lint_draft6, additional_items_with_schema_items_1) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "items": { + "type": "number" + }, + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "items": { + "type": "number" + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft6, additional_items_with_schema_items_2) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "items": { + "const": "foo" + }, + "additionalItems": true + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "items": { + "const": "foo" + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft6, + additional_items_with_schema_items_boolean_items_true) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "items": true, + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft6, + additional_items_with_schema_items_boolean_items_false) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "items": false, + "additionalItems": { + "type": "string" + } + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "items": false + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft6, additional_items_with_schema_items_array_items) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "items": [ + { "type": "string" }, + { "type": "number" } + ], + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-06/schema#", + "items": [ + { "type": "string" }, + { "type": "number" } + ], + "additionalItems": false + })JSON"); + + EXPECT_EQ(document, expected); +} diff --git a/test/alterschema/alterschema_lint_draft7_test.cc b/test/alterschema/alterschema_lint_draft7_test.cc index c4912b494..4c2cce295 100644 --- a/test/alterschema/alterschema_lint_draft7_test.cc +++ b/test/alterschema/alterschema_lint_draft7_test.cc @@ -1283,3 +1283,112 @@ TEST(AlterSchema_lint_draft7, unnecessary_allof_wrapper_properties_1) { EXPECT_EQ(document, expected); } + +TEST(AlterSchema_lint_draft7, additional_items_with_schema_items_1) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "items": { + "type": "number" + }, + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "items": { + "type": "number" + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft7, additional_items_with_schema_items_2) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "array", + "items": { + "if": { "type": "string" }, + "then": { "minLength": 1 } + }, + "additionalItems": { + "type": "number" + } + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "array", + "items": { + "if": { "type": "string" }, + "then": { "minLength": 1 } + } + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft7, + additional_items_with_schema_items_boolean_items_true) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "items": true, + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#" + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft7, + additional_items_with_schema_items_boolean_items_false) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "items": false, + "additionalItems": { + "type": "string" + } + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "items": false + })JSON"); + + EXPECT_EQ(document, expected); +} + +TEST(AlterSchema_lint_draft7, additional_items_with_schema_items_array_items) { + sourcemeta::core::JSON document = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "items": [ + { "type": "string" }, + { "type": "number" } + ], + "additionalItems": false + })JSON"); + + LINT_AND_FIX_FOR_READABILITY(document); + + const sourcemeta::core::JSON expected = sourcemeta::core::parse_json(R"JSON({ + "$schema": "http://json-schema.org/draft-07/schema#", + "items": [ + { "type": "string" }, + { "type": "number" } + ], + "additionalItems": false + })JSON"); + + EXPECT_EQ(document, expected); +}