Skip to content

Commit

Permalink
Add an implicit rule for minProperties (#230)
Browse files Browse the repository at this point in the history
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
  • Loading branch information
jviotti committed Sep 11, 2024
1 parent 8f4bd83 commit f1ca303
Show file tree
Hide file tree
Showing 8 changed files with 264 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/linter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ noa_library(NAMESPACE sourcemeta PROJECT alterschema NAME linter
implicit/max_contains_covered_by_max_items.h
implicit/min_length_implicit.h
implicit/min_properties_covered_by_required.h
implicit/min_properties_implicit.h
implicit/multiple_of_implicit.h
implicit/properties_implicit.h

Expand Down
35 changes: 35 additions & 0 deletions src/linter/implicit/min_properties_implicit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
class MinPropertiesImplicit final : public Rule {
public:
MinPropertiesImplicit()
: Rule{"min_properties_implicit",
"The `minProperties` keyword has a logical default of 0 or the "
"size of `required`"} {};

[[nodiscard]] auto
condition(const sourcemeta::jsontoolkit::JSON &schema, const std::string &,
const std::set<std::string> &vocabularies,
const sourcemeta::jsontoolkit::Pointer &) const -> bool override {
return contains_any(
vocabularies,
{"https://json-schema.org/draft/2020-12/vocab/validation",
"https://json-schema.org/draft/2019-09/vocab/validation",
"http://json-schema.org/draft-07/schema#",
"http://json-schema.org/draft-06/schema#",
"http://json-schema.org/draft-04/schema#"}) &&
schema.is_object() && schema.defines("type") &&
schema.at("type").is_string() &&
schema.at("type").to_string() == "object" &&
!schema.defines("minProperties");
}

auto transform(Transformer &transformer) const -> void override {
if (transformer.schema().defines("required") &&
transformer.schema().at("required").is_array()) {
transformer.assign("minProperties",
sourcemeta::jsontoolkit::JSON{
transformer.schema().at("required").size()});
} else {
transformer.assign("minProperties", sourcemeta::jsontoolkit::JSON{0});
}
}
};
2 changes: 2 additions & 0 deletions src/linter/linter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ template <typename T> auto every_item_is_boolean(const T &container) -> bool {
#include "implicit/max_contains_covered_by_max_items.h"
#include "implicit/min_length_implicit.h"
#include "implicit/min_properties_covered_by_required.h"
#include "implicit/min_properties_implicit.h"
#include "implicit/multiple_of_implicit.h"
#include "implicit/properties_implicit.h"
// Superfluous
Expand Down Expand Up @@ -220,6 +221,7 @@ auto add(Bundle &bundle, const LinterCategory category) -> void {
bundle.add<MaxContainsCoveredByMaxItems>();
bundle.add<MinLengthImplicit>();
bundle.add<MinPropertiesCoveredByRequired>();
bundle.add<MinPropertiesImplicit>();
bundle.add<MultipleOfImplicit>();
bundle.add<PropertiesImplicit>();
break;
Expand Down
45 changes: 45 additions & 0 deletions test/linter/2019_09_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1610,3 +1610,48 @@ TEST(Lint_2019_09, min_properties_covered_by_required_1) {

EXPECT_EQ(document, expected);
}

TEST(Lint_2019_09, min_properties_implicit_1) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [ "foo", "bar" ]
})JSON");

LINT_AND_FIX_FOR_ANALYSIS(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [ "foo", "bar" ],
"properties": {},
"minProperties": 2
})JSON");

EXPECT_EQ(document, expected);
}

TEST(Lint_2019_09, min_properties_implicit_2) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"minProperties": 2,
"required": [ "foo", "bar" ]
})JSON");

LINT_AND_FIX_FOR_ANALYSIS(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2019-09/schema",
"type": "object",
"properties": {},
"required": [ "foo", "bar" ],
"minProperties": 2
})JSON");

EXPECT_EQ(document, expected);
}
46 changes: 46 additions & 0 deletions test/linter/2020_12_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1665,6 +1665,7 @@ TEST(Lint_2020_12, type_array_to_any_of_3) {
{
"type": "object",
"properties": {},
"minProperties": 0,
"additionalProperties": {
"$anchor": "foo",
"type": "string",
Expand Down Expand Up @@ -1763,3 +1764,48 @@ TEST(Lint_2020_12, min_properties_covered_by_required_1) {

EXPECT_EQ(document, expected);
}

TEST(Lint_2020_12, min_properties_implicit_1) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [ "foo", "bar" ]
})JSON");

LINT_AND_FIX_FOR_ANALYSIS(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": [ "foo", "bar" ],
"properties": {},
"minProperties": 2
})JSON");

EXPECT_EQ(document, expected);
}

TEST(Lint_2020_12, min_properties_implicit_2) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"minProperties": 2,
"required": [ "foo", "bar" ]
})JSON");

LINT_AND_FIX_FOR_ANALYSIS(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {},
"required": [ "foo", "bar" ],
"minProperties": 2
})JSON");

EXPECT_EQ(document, expected);
}
45 changes: 45 additions & 0 deletions test/linter/draft4_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -735,3 +735,48 @@ TEST(Lint_draft4, min_properties_covered_by_required_1) {

EXPECT_EQ(document, expected);
}

TEST(Lint_draft4, min_properties_implicit_1) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"required": [ "foo", "bar" ]
})JSON");

LINT_AND_FIX_FOR_ANALYSIS(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"required": [ "foo", "bar" ],
"properties": {},
"minProperties": 2
})JSON");

EXPECT_EQ(document, expected);
}

TEST(Lint_draft4, min_properties_implicit_2) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"minProperties": 2,
"required": [ "foo", "bar" ]
})JSON");

LINT_AND_FIX_FOR_ANALYSIS(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {},
"required": [ "foo", "bar" ],
"minProperties": 2
})JSON");

EXPECT_EQ(document, expected);
}
45 changes: 45 additions & 0 deletions test/linter/draft6_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1124,3 +1124,48 @@ TEST(Lint_draft6, min_properties_covered_by_required_1) {

EXPECT_EQ(document, expected);
}

TEST(Lint_draft6, min_properties_implicit_1) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"required": [ "foo", "bar" ]
})JSON");

LINT_AND_FIX_FOR_ANALYSIS(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"required": [ "foo", "bar" ],
"properties": {},
"minProperties": 2
})JSON");

EXPECT_EQ(document, expected);
}

TEST(Lint_draft6, min_properties_implicit_2) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"minProperties": 2,
"required": [ "foo", "bar" ]
})JSON");

LINT_AND_FIX_FOR_ANALYSIS(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"properties": {},
"required": [ "foo", "bar" ],
"minProperties": 2
})JSON");

EXPECT_EQ(document, expected);
}
45 changes: 45 additions & 0 deletions test/linter/draft7_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1232,3 +1232,48 @@ TEST(Lint_draft7, min_properties_covered_by_required_1) {

EXPECT_EQ(document, expected);
}

TEST(Lint_draft7, min_properties_implicit_1) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [ "foo", "bar" ]
})JSON");

LINT_AND_FIX_FOR_ANALYSIS(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": [ "foo", "bar" ],
"properties": {},
"minProperties": 2
})JSON");

EXPECT_EQ(document, expected);
}

TEST(Lint_draft7, min_properties_implicit_2) {
sourcemeta::jsontoolkit::JSON document =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"minProperties": 2,
"required": [ "foo", "bar" ]
})JSON");

LINT_AND_FIX_FOR_ANALYSIS(document);

const sourcemeta::jsontoolkit::JSON expected =
sourcemeta::jsontoolkit::parse(R"JSON({
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {},
"required": [ "foo", "bar" ],
"minProperties": 2
})JSON");

EXPECT_EQ(document, expected);
}

0 comments on commit f1ca303

Please sign in to comment.