From 07dabbffb32b9d54b063545cc743f4824670a7ce Mon Sep 17 00:00:00 2001 From: Kevin Dew Date: Thu, 30 Jan 2025 14:44:56 +0000 Subject: [PATCH] Move additionalProperties to 3.0 Schema node This will be modelled differently for OpenAPI 3.1 where a JSON schema can itself have a boolean value and thus we won't be using Boolean or Schema anymore as a 3.1 Schema will suffice. --- lib/openapi3_parser/node/schema.rb | 13 -------- lib/openapi3_parser/node/schema/v3_0.rb | 13 ++++++++ .../node_factory/schema/common.rb | 17 ---------- .../node_factory/schema/v3_0.rb | 21 ++++++++++++- .../node_factory/schema/v3_0_spec.rb | 31 +++++++++++++++++++ spec/support/schema_common.rb | 31 ------------------- 6 files changed, 64 insertions(+), 62 deletions(-) diff --git a/lib/openapi3_parser/node/schema.rb b/lib/openapi3_parser/node/schema.rb index 44aaf67..4f6271f 100644 --- a/lib/openapi3_parser/node/schema.rb +++ b/lib/openapi3_parser/node/schema.rb @@ -157,19 +157,6 @@ def properties self["properties"] end - # @return [Boolean] - def additional_properties? - self["additionalProperties"] != false - end - - # @return [Schema, nil] - def additional_properties_schema - properties = self["additionalProperties"] - return if [true, false].include?(properties) - - properties - end - # @return [String, nil] def description self["description"] diff --git a/lib/openapi3_parser/node/schema/v3_0.rb b/lib/openapi3_parser/node/schema/v3_0.rb index bf0da0d..230d703 100644 --- a/lib/openapi3_parser/node/schema/v3_0.rb +++ b/lib/openapi3_parser/node/schema/v3_0.rb @@ -21,6 +21,19 @@ def exclusive_maximum? def exclusive_minimum? self["exclusiveMinimum"] end + + # @return [Boolean] + def additional_properties? + self["additionalProperties"] != false + end + + # @return [Schema, nil] + def additional_properties_schema + properties = self["additionalProperties"] + return if [true, false].include?(properties) + + properties + end end end end diff --git a/lib/openapi3_parser/node_factory/schema/common.rb b/lib/openapi3_parser/node_factory/schema/common.rb index 1018cf1..0e2c296 100644 --- a/lib/openapi3_parser/node_factory/schema/common.rb +++ b/lib/openapi3_parser/node_factory/schema/common.rb @@ -33,10 +33,6 @@ def self.included(base) base.field "not", factory: :referenceable_schema base.field "items", factory: :referenceable_schema base.field "properties", factory: :schema_map_factory - base.field "additionalProperties", - validate: :additional_properties_input_type, - factory: :additional_properties_factory, - default: false base.field "description", input_type: String base.field "format", input_type: String base.field "default" @@ -106,19 +102,6 @@ def referenceable_schema_array(context) value_factory: NodeFactory::Schema.factory(context) ) end - - def additional_properties_input_type(validatable) - input = validatable.input - return if [true, false].include?(input) || input.is_a?(Hash) - - validatable.add_error("Expected a Boolean or an Object") - end - - def additional_properties_factory(context) - return context.input if [true, false].include?(context.input) - - referenceable_schema(context) - end end end end diff --git a/lib/openapi3_parser/node_factory/schema/v3_0.rb b/lib/openapi3_parser/node_factory/schema/v3_0.rb index 2efa68c..df8d7e1 100644 --- a/lib/openapi3_parser/node_factory/schema/v3_0.rb +++ b/lib/openapi3_parser/node_factory/schema/v3_0.rb @@ -13,10 +13,16 @@ class V3_0 < NodeFactory::Object # rubocop:disable Naming/ClassAndModuleCamelCas field "type", input_type: String # JSON Schema 2016 has these exclusive fields as booleans whereas - # in JSON Schema 2021 (OpenAPI 3.1) these are numbers + # in JSON Schema 2020 (OpenAPI 3.1) these are numbers field "exclusiveMaximum", input_type: :boolean, default: false field "exclusiveMinimum", input_type: :boolean, default: false + # JSON Schema 2020 accepts a schema (albeit a more complex one) than + # this schema or boolean approach + field "additionalProperties", validate: :additional_properties_input_type, + factory: :additional_properties_factory, + default: false + validate :items_for_array def build_node(data, node_context) @@ -36,6 +42,19 @@ def items_for_array(validatable) validatable.add_error("items must be defined for a type of array") end + + def additional_properties_input_type(validatable) + input = validatable.input + return if [true, false].include?(input) || input.is_a?(Hash) + + validatable.add_error("Expected a Boolean or an Object") + end + + def additional_properties_factory(context) + return context.input if [true, false].include?(context.input) + + referenceable_schema(context) + end end end end diff --git a/spec/lib/openapi3_parser/node_factory/schema/v3_0_spec.rb b/spec/lib/openapi3_parser/node_factory/schema/v3_0_spec.rb index ee1b92f..3a2022f 100644 --- a/spec/lib/openapi3_parser/node_factory/schema/v3_0_spec.rb +++ b/spec/lib/openapi3_parser/node_factory/schema/v3_0_spec.rb @@ -72,4 +72,35 @@ .with_message("items must be defined for a type of array") end end + + describe "validating additionalProperties" do + it "is valid for a boolean" do + true_instance = described_class.new( + create_node_factory_context({ "additionalProperties" => true }) + ) + expect(true_instance).to be_valid + + false_instance = described_class.new( + create_node_factory_context({ "additionalProperties" => false }) + ) + expect(false_instance).to be_valid + end + + it "is valid for a schema" do + instance = described_class.new( + create_node_factory_context({ "additionalProperties" => { "type" => "object" } }) + ) + expect(instance).to be_valid + end + + it "is invalid for something different" do + instance = described_class.new( + create_node_factory_context({ "additionalProperties" => %w[item1 item2] }) + ) + expect(instance).not_to be_valid + expect(instance) + .to have_validation_error("#/additionalProperties") + .with_message("Expected a Boolean or an Object") + end + end end diff --git a/spec/support/schema_common.rb b/spec/support/schema_common.rb index 90fafd9..8956ede 100644 --- a/spec/support/schema_common.rb +++ b/spec/support/schema_common.rb @@ -88,37 +88,6 @@ expect(read_only).to be_valid end end - - describe "validating additionalProperties" do - it "is valid for a boolean" do - true_instance = described_class.new( - create_node_factory_context({ "additionalProperties" => true }) - ) - expect(true_instance).to be_valid - - false_instance = described_class.new( - create_node_factory_context({ "additionalProperties" => false }) - ) - expect(false_instance).to be_valid - end - - it "is valid for a schema" do - instance = described_class.new( - create_node_factory_context({ "additionalProperties" => { "type" => "object" } }) - ) - expect(instance).to be_valid - end - - it "is invalid for something different" do - instance = described_class.new( - create_node_factory_context({ "additionalProperties" => %w[item1 item2] }) - ) - expect(instance).not_to be_valid - expect(instance) - .to have_validation_error("#/additionalProperties") - .with_message("Expected a Boolean or an Object") - end - end end RSpec.shared_examples "schema node" do |openapi_version:|