From 07a9257ee9f9dc912e07a53087246d89e51a2218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Armand=20M=C3=A9grot?= Date: Fri, 5 Jan 2024 08:01:08 +0100 Subject: [PATCH] [Ruby][client] Handle enums (and other scalars) in oneOf and anyOf schemas (#17515) * Handle enums in oneOf and anyOf schemas * Update specs --- .../ruby-client/partial_anyof_module.mustache | 2 +- .../ruby-client/partial_oneof_module.mustache | 2 +- ...ith-fake-endpoints-models-for-testing.yaml | 16 ++- .../ruby-httpx/.openapi-generator/FILES | 6 + samples/client/petstore/ruby-httpx/README.md | 2 + .../client/petstore/ruby-httpx/docs/Cow.md | 15 +++ .../petstore/ruby-httpx/docs/MamalWithEnum.md | 47 ++++++++ .../petstore/ruby-httpx/lib/petstore.rb | 2 + .../ruby-httpx/lib/petstore/models/cow.rb | 40 +++++++ .../lib/petstore/models/mamal_with_enum.rb | 104 ++++++++++++++++++ .../lib/petstore/models/mammal_anyof.rb | 2 +- .../models/mammal_without_discriminator.rb | 2 +- .../ruby-httpx/spec/models/cow_spec.rb | 30 +++++ .../spec/models/mamal_with_enum_spec.rb | 32 ++++++ .../petstore/ruby/.openapi-generator/FILES | 6 + samples/client/petstore/ruby/README.md | 2 + samples/client/petstore/ruby/docs/Cow.md | 15 +++ .../petstore/ruby/docs/MamalWithEnum.md | 47 ++++++++ samples/client/petstore/ruby/lib/petstore.rb | 2 + .../petstore/ruby/lib/petstore/models/cow.rb | 40 +++++++ .../lib/petstore/models/mamal_with_enum.rb | 104 ++++++++++++++++++ .../ruby/lib/petstore/models/mammal_anyof.rb | 2 +- .../models/mammal_without_discriminator.rb | 2 +- .../petstore/ruby/spec/models/cow_spec.rb | 30 +++++ .../ruby/spec/models/mamal_with_enum_spec.rb | 36 ++++++ 25 files changed, 578 insertions(+), 10 deletions(-) create mode 100644 samples/client/petstore/ruby-httpx/docs/Cow.md create mode 100644 samples/client/petstore/ruby-httpx/docs/MamalWithEnum.md create mode 100644 samples/client/petstore/ruby-httpx/lib/petstore/models/cow.rb create mode 100644 samples/client/petstore/ruby-httpx/lib/petstore/models/mamal_with_enum.rb create mode 100644 samples/client/petstore/ruby-httpx/spec/models/cow_spec.rb create mode 100644 samples/client/petstore/ruby-httpx/spec/models/mamal_with_enum_spec.rb create mode 100644 samples/client/petstore/ruby/docs/Cow.md create mode 100644 samples/client/petstore/ruby/docs/MamalWithEnum.md create mode 100644 samples/client/petstore/ruby/lib/petstore/models/cow.rb create mode 100644 samples/client/petstore/ruby/lib/petstore/models/mamal_with_enum.rb create mode 100644 samples/client/petstore/ruby/spec/models/cow_spec.rb create mode 100644 samples/client/petstore/ruby/spec/models/mamal_with_enum_spec.rb diff --git a/modules/openapi-generator/src/main/resources/ruby-client/partial_anyof_module.mustache b/modules/openapi-generator/src/main/resources/ruby-client/partial_anyof_module.mustache index b41dd20a7d0..778568c2c31 100644 --- a/modules/openapi-generator/src/main/resources/ruby-client/partial_anyof_module.mustache +++ b/modules/openapi-generator/src/main/resources/ruby-client/partial_anyof_module.mustache @@ -79,7 +79,7 @@ return model if model else # raise if data contains keys that are not known to the model - raise unless (data.keys - const.acceptable_attributes).empty? + raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty? model = const.build_from_hash(data) return model if model end diff --git a/modules/openapi-generator/src/main/resources/ruby-client/partial_oneof_module.mustache b/modules/openapi-generator/src/main/resources/ruby-client/partial_oneof_module.mustache index 49e7b58d015..d3a25f69ed5 100644 --- a/modules/openapi-generator/src/main/resources/ruby-client/partial_oneof_module.mustache +++ b/modules/openapi-generator/src/main/resources/ruby-client/partial_oneof_module.mustache @@ -121,7 +121,7 @@ return model if model else # raise if data contains keys that are not known to the model - raise unless (data.keys - const.acceptable_attributes).empty? + raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty? model = const.build_from_hash(data) return model if model end diff --git a/modules/openapi-generator/src/test/resources/3_0/ruby/petstore-with-fake-endpoints-models-for-testing.yaml b/modules/openapi-generator/src/test/resources/3_0/ruby/petstore-with-fake-endpoints-models-for-testing.yaml index 1fbc416d8f9..a0fafbbcbf4 100644 --- a/modules/openapi-generator/src/test/resources/3_0/ruby/petstore-with-fake-endpoints-models-for-testing.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/ruby/petstore-with-fake-endpoints-models-for-testing.yaml @@ -1313,7 +1313,7 @@ paths: operationId: getParameterNameMapping parameters: - name: _type - in: header + in: header description: _type required: true schema: @@ -1326,7 +1326,7 @@ paths: schema: type: string - name: type_ - in: header + in: header description: type_ required: true schema: @@ -2091,6 +2091,9 @@ components: oneOf: - $ref: '#/components/schemas/whale' - $ref: '#/components/schemas/zebra' + mamal_with_enum: + oneOf: + - $ref: '#/components/schemas/cow' mammal: oneOf: - $ref: '#/components/schemas/whale' @@ -2122,9 +2125,14 @@ components: required: - classname additionalProperties: true + cow: + type: string + enum: + - BlackAndWhiteCow + - BrownCow PropertyNameMapping: - properties: - http_debug_operation: + properties: + http_debug_operation: type: string _type: type: string diff --git a/samples/client/petstore/ruby-httpx/.openapi-generator/FILES b/samples/client/petstore/ruby-httpx/.openapi-generator/FILES index 41f69482281..4f18157aa0d 100644 --- a/samples/client/petstore/ruby-httpx/.openapi-generator/FILES +++ b/samples/client/petstore/ruby-httpx/.openapi-generator/FILES @@ -19,6 +19,7 @@ docs/Cat.md docs/Category.md docs/ClassModel.md docs/Client.md +docs/Cow.md docs/DefaultApi.md docs/DeprecatedObject.md docs/Dog.md @@ -36,6 +37,7 @@ docs/FormatTest.md docs/HasOnlyReadOnly.md docs/HealthCheckResult.md docs/List.md +docs/MamalWithEnum.md docs/Mammal.md docs/MammalAnyof.md docs/MammalWithoutDiscriminator.md @@ -91,6 +93,7 @@ lib/petstore/models/cat.rb lib/petstore/models/category.rb lib/petstore/models/class_model.rb lib/petstore/models/client.rb +lib/petstore/models/cow.rb lib/petstore/models/deprecated_object.rb lib/petstore/models/dog.rb lib/petstore/models/enum_arrays.rb @@ -105,6 +108,7 @@ lib/petstore/models/format_test.rb lib/petstore/models/has_only_read_only.rb lib/petstore/models/health_check_result.rb lib/petstore/models/list.rb +lib/petstore/models/mamal_with_enum.rb lib/petstore/models/mammal.rb lib/petstore/models/mammal_anyof.rb lib/petstore/models/mammal_without_discriminator.rb @@ -135,4 +139,6 @@ lib/petstore/models/whale.rb lib/petstore/models/zebra.rb lib/petstore/version.rb petstore.gemspec +spec/models/cow_spec.rb +spec/models/mamal_with_enum_spec.rb spec/spec_helper.rb diff --git a/samples/client/petstore/ruby-httpx/README.md b/samples/client/petstore/ruby-httpx/README.md index 534bd931faa..daa2a6d5b0f 100644 --- a/samples/client/petstore/ruby-httpx/README.md +++ b/samples/client/petstore/ruby-httpx/README.md @@ -136,6 +136,7 @@ Class | Method | HTTP request | Description - [Petstore::Category](docs/Category.md) - [Petstore::ClassModel](docs/ClassModel.md) - [Petstore::Client](docs/Client.md) + - [Petstore::Cow](docs/Cow.md) - [Petstore::DeprecatedObject](docs/DeprecatedObject.md) - [Petstore::Dog](docs/Dog.md) - [Petstore::EnumArrays](docs/EnumArrays.md) @@ -150,6 +151,7 @@ Class | Method | HTTP request | Description - [Petstore::HasOnlyReadOnly](docs/HasOnlyReadOnly.md) - [Petstore::HealthCheckResult](docs/HealthCheckResult.md) - [Petstore::List](docs/List.md) + - [Petstore::MamalWithEnum](docs/MamalWithEnum.md) - [Petstore::Mammal](docs/Mammal.md) - [Petstore::MammalAnyof](docs/MammalAnyof.md) - [Petstore::MammalWithoutDiscriminator](docs/MammalWithoutDiscriminator.md) diff --git a/samples/client/petstore/ruby-httpx/docs/Cow.md b/samples/client/petstore/ruby-httpx/docs/Cow.md new file mode 100644 index 00000000000..6fa7084b8a4 --- /dev/null +++ b/samples/client/petstore/ruby-httpx/docs/Cow.md @@ -0,0 +1,15 @@ +# Petstore::Cow + +## Properties + +| Name | Type | Description | Notes | +| ---- | ---- | ----------- | ----- | + +## Example + +```ruby +require 'petstore' + +instance = Petstore::Cow.new() +``` + diff --git a/samples/client/petstore/ruby-httpx/docs/MamalWithEnum.md b/samples/client/petstore/ruby-httpx/docs/MamalWithEnum.md new file mode 100644 index 00000000000..37b9252a2c8 --- /dev/null +++ b/samples/client/petstore/ruby-httpx/docs/MamalWithEnum.md @@ -0,0 +1,47 @@ +# Petstore::MamalWithEnum + +## Class instance methods + +### `openapi_one_of` + +Returns the list of classes defined in oneOf. + +#### Example + +```ruby +require 'petstore' + +Petstore::MamalWithEnum.openapi_one_of +# => +# [ +# :'Cow' +# ] +``` + +### build + +Find the appropriate object from the `openapi_one_of` list and casts the data into it. + +#### Example + +```ruby +require 'petstore' + +Petstore::MamalWithEnum.build(data) +# => # + +Petstore::MamalWithEnum.build(data_that_doesnt_match) +# => nil +``` + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| **data** | **Mixed** | data to be matched against the list of oneOf items | + +#### Return type + +- `Cow` +- `nil` (if no type matches) + diff --git a/samples/client/petstore/ruby-httpx/lib/petstore.rb b/samples/client/petstore/ruby-httpx/lib/petstore.rb index fa0658b97c2..b6662f07ea5 100644 --- a/samples/client/petstore/ruby-httpx/lib/petstore.rb +++ b/samples/client/petstore/ruby-httpx/lib/petstore.rb @@ -28,6 +28,7 @@ require 'petstore/models/capitalization' require 'petstore/models/category' require 'petstore/models/class_model' require 'petstore/models/client' +require 'petstore/models/cow' require 'petstore/models/deprecated_object' require 'petstore/models/enum_arrays' require 'petstore/models/enum_class' @@ -41,6 +42,7 @@ require 'petstore/models/format_test' require 'petstore/models/has_only_read_only' require 'petstore/models/health_check_result' require 'petstore/models/list' +require 'petstore/models/mamal_with_enum' require 'petstore/models/mammal' require 'petstore/models/mammal_anyof' require 'petstore/models/mammal_without_discriminator' diff --git a/samples/client/petstore/ruby-httpx/lib/petstore/models/cow.rb b/samples/client/petstore/ruby-httpx/lib/petstore/models/cow.rb new file mode 100644 index 00000000000..c209f752ce8 --- /dev/null +++ b/samples/client/petstore/ruby-httpx/lib/petstore/models/cow.rb @@ -0,0 +1,40 @@ +=begin +#OpenAPI Petstore + +#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + +The version of the OpenAPI document: 1.0.0 + +Generated by: https://openapi-generator.tech +OpenAPI Generator version: 7.3.0-SNAPSHOT + +=end + +require 'date' +require 'time' + +module Petstore + class Cow + BLACK_AND_WHITE_COW = "BlackAndWhiteCow".freeze + BROWN_COW = "BrownCow".freeze + + def self.all_vars + @all_vars ||= [BLACK_AND_WHITE_COW, BROWN_COW].freeze + end + + # Builds the enum from string + # @param [String] The enum value in the form of the string + # @return [String] The enum value + def self.build_from_hash(value) + new.build_from_hash(value) + end + + # Builds the enum from string + # @param [String] The enum value in the form of the string + # @return [String] The enum value + def build_from_hash(value) + return value if Cow.all_vars.include?(value) + raise "Invalid ENUM value #{value} for class #Cow" + end + end +end diff --git a/samples/client/petstore/ruby-httpx/lib/petstore/models/mamal_with_enum.rb b/samples/client/petstore/ruby-httpx/lib/petstore/models/mamal_with_enum.rb new file mode 100644 index 00000000000..4cf499b269d --- /dev/null +++ b/samples/client/petstore/ruby-httpx/lib/petstore/models/mamal_with_enum.rb @@ -0,0 +1,104 @@ +=begin +#OpenAPI Petstore + +#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + +The version of the OpenAPI document: 1.0.0 + +Generated by: https://openapi-generator.tech +OpenAPI Generator version: 7.3.0-SNAPSHOT + +=end + +require 'date' +require 'time' + +module Petstore + module MamalWithEnum + class << self + # List of class defined in oneOf (OpenAPI v3) + def openapi_one_of + [ + :'Cow' + ] + end + + # Builds the object + # @param [Mixed] Data to be matched against the list of oneOf items + # @return [Object] Returns the model or the data itself + def build(data) + # Go through the list of oneOf items and attempt to identify the appropriate one. + # Note: + # - We do not attempt to check whether exactly one item matches. + # - No advanced validation of types in some cases (e.g. "x: { type: string }" will happily match { x: 123 }) + # due to the way the deserialization is made in the base_object template (it just casts without verifying). + # - TODO: scalar values are de facto behaving as if they were nullable. + # - TODO: logging when debugging is set. + openapi_one_of.each do |klass| + begin + next if klass == :AnyType # "nullable: true" + typed_data = find_and_cast_into_type(klass, data) + return typed_data if typed_data + rescue # rescue all errors so we keep iterating even if the current item lookup raises + end + end + + openapi_one_of.include?(:AnyType) ? data : nil + end + + private + + SchemaMismatchError = Class.new(StandardError) + + # Note: 'File' is missing here because in the regular case we get the data _after_ a call to JSON.parse. + def find_and_cast_into_type(klass, data) + return if data.nil? + + case klass.to_s + when 'Boolean' + return data if data.instance_of?(TrueClass) || data.instance_of?(FalseClass) + when 'Float' + return data if data.instance_of?(Float) + when 'Integer' + return data if data.instance_of?(Integer) + when 'Time' + return Time.parse(data) + when 'Date' + return Date.parse(data) + when 'String' + return data if data.instance_of?(String) + when 'Object' # "type: object" + return data if data.instance_of?(Hash) + when /\AArray<(?.+)>\z/ # "type: array" + if data.instance_of?(Array) + sub_type = Regexp.last_match[:sub_type] + return data.map { |item| find_and_cast_into_type(sub_type, item) } + end + when /\AHash.+)>\z/ # "type: object" with "additionalProperties: { ... }" + if data.instance_of?(Hash) && data.keys.all? { |k| k.instance_of?(Symbol) || k.instance_of?(String) } + sub_type = Regexp.last_match[:sub_type] + return data.each_with_object({}) { |(k, v), hsh| hsh[k] = find_and_cast_into_type(sub_type, v) } + end + else # model + const = Petstore.const_get(klass) + if const + if const.respond_to?(:openapi_one_of) # nested oneOf model + model = const.build(data) + return model if model + else + # raise if data contains keys that are not known to the model + raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty? + model = const.build_from_hash(data) + return model if model + end + end + end + + raise # if no match by now, raise + rescue + raise SchemaMismatchError, "#{data} doesn't match the #{klass} type" + end + end + end + +end diff --git a/samples/client/petstore/ruby-httpx/lib/petstore/models/mammal_anyof.rb b/samples/client/petstore/ruby-httpx/lib/petstore/models/mammal_anyof.rb index b7455a5f251..7d5aed31496 100644 --- a/samples/client/petstore/ruby-httpx/lib/petstore/models/mammal_anyof.rb +++ b/samples/client/petstore/ruby-httpx/lib/petstore/models/mammal_anyof.rb @@ -87,7 +87,7 @@ module Petstore return model if model else # raise if data contains keys that are not known to the model - raise unless (data.keys - const.acceptable_attributes).empty? + raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty? model = const.build_from_hash(data) return model if model end diff --git a/samples/client/petstore/ruby-httpx/lib/petstore/models/mammal_without_discriminator.rb b/samples/client/petstore/ruby-httpx/lib/petstore/models/mammal_without_discriminator.rb index ef513ff8a66..6eaac608cf7 100644 --- a/samples/client/petstore/ruby-httpx/lib/petstore/models/mammal_without_discriminator.rb +++ b/samples/client/petstore/ruby-httpx/lib/petstore/models/mammal_without_discriminator.rb @@ -88,7 +88,7 @@ module Petstore return model if model else # raise if data contains keys that are not known to the model - raise unless (data.keys - const.acceptable_attributes).empty? + raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty? model = const.build_from_hash(data) return model if model end diff --git a/samples/client/petstore/ruby-httpx/spec/models/cow_spec.rb b/samples/client/petstore/ruby-httpx/spec/models/cow_spec.rb new file mode 100644 index 00000000000..e1cf66c03ac --- /dev/null +++ b/samples/client/petstore/ruby-httpx/spec/models/cow_spec.rb @@ -0,0 +1,30 @@ +=begin +#OpenAPI Petstore + +#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + +The version of the OpenAPI document: 1.0.0 + +Generated by: https://openapi-generator.tech +OpenAPI Generator version: 7.3.0-SNAPSHOT + +=end + +require 'spec_helper' +require 'json' +require 'date' + +# Unit tests for Petstore::Cow +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe Petstore::Cow do + let(:instance) { Petstore::Cow.new } + + describe 'test an instance of Cow' do + it 'should create an instance of Cow' do + # uncomment below to test the instance creation + #expect(instance).to be_instance_of(Petstore::Cow) + end + end + +end diff --git a/samples/client/petstore/ruby-httpx/spec/models/mamal_with_enum_spec.rb b/samples/client/petstore/ruby-httpx/spec/models/mamal_with_enum_spec.rb new file mode 100644 index 00000000000..95002b49ea4 --- /dev/null +++ b/samples/client/petstore/ruby-httpx/spec/models/mamal_with_enum_spec.rb @@ -0,0 +1,32 @@ +=begin +#OpenAPI Petstore + +#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + +The version of the OpenAPI document: 1.0.0 + +Generated by: https://openapi-generator.tech +OpenAPI Generator version: 7.3.0-SNAPSHOT + +=end + +require 'spec_helper' +require 'json' +require 'date' + +# Unit tests for Petstore::MamalWithEnum +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe Petstore::MamalWithEnum do + describe '.openapi_one_of' do + it 'lists the items referenced in the oneOf array' do + expect(described_class.openapi_one_of).to_not be_empty + end + end + + describe '.build' do + it 'returns the correct model' do + # assertion here. ref: https://rspec.info/features/3-12/rspec-expectations/built-in-matchers/ + end + end +end diff --git a/samples/client/petstore/ruby/.openapi-generator/FILES b/samples/client/petstore/ruby/.openapi-generator/FILES index 41f69482281..4f18157aa0d 100644 --- a/samples/client/petstore/ruby/.openapi-generator/FILES +++ b/samples/client/petstore/ruby/.openapi-generator/FILES @@ -19,6 +19,7 @@ docs/Cat.md docs/Category.md docs/ClassModel.md docs/Client.md +docs/Cow.md docs/DefaultApi.md docs/DeprecatedObject.md docs/Dog.md @@ -36,6 +37,7 @@ docs/FormatTest.md docs/HasOnlyReadOnly.md docs/HealthCheckResult.md docs/List.md +docs/MamalWithEnum.md docs/Mammal.md docs/MammalAnyof.md docs/MammalWithoutDiscriminator.md @@ -91,6 +93,7 @@ lib/petstore/models/cat.rb lib/petstore/models/category.rb lib/petstore/models/class_model.rb lib/petstore/models/client.rb +lib/petstore/models/cow.rb lib/petstore/models/deprecated_object.rb lib/petstore/models/dog.rb lib/petstore/models/enum_arrays.rb @@ -105,6 +108,7 @@ lib/petstore/models/format_test.rb lib/petstore/models/has_only_read_only.rb lib/petstore/models/health_check_result.rb lib/petstore/models/list.rb +lib/petstore/models/mamal_with_enum.rb lib/petstore/models/mammal.rb lib/petstore/models/mammal_anyof.rb lib/petstore/models/mammal_without_discriminator.rb @@ -135,4 +139,6 @@ lib/petstore/models/whale.rb lib/petstore/models/zebra.rb lib/petstore/version.rb petstore.gemspec +spec/models/cow_spec.rb +spec/models/mamal_with_enum_spec.rb spec/spec_helper.rb diff --git a/samples/client/petstore/ruby/README.md b/samples/client/petstore/ruby/README.md index 534bd931faa..daa2a6d5b0f 100644 --- a/samples/client/petstore/ruby/README.md +++ b/samples/client/petstore/ruby/README.md @@ -136,6 +136,7 @@ Class | Method | HTTP request | Description - [Petstore::Category](docs/Category.md) - [Petstore::ClassModel](docs/ClassModel.md) - [Petstore::Client](docs/Client.md) + - [Petstore::Cow](docs/Cow.md) - [Petstore::DeprecatedObject](docs/DeprecatedObject.md) - [Petstore::Dog](docs/Dog.md) - [Petstore::EnumArrays](docs/EnumArrays.md) @@ -150,6 +151,7 @@ Class | Method | HTTP request | Description - [Petstore::HasOnlyReadOnly](docs/HasOnlyReadOnly.md) - [Petstore::HealthCheckResult](docs/HealthCheckResult.md) - [Petstore::List](docs/List.md) + - [Petstore::MamalWithEnum](docs/MamalWithEnum.md) - [Petstore::Mammal](docs/Mammal.md) - [Petstore::MammalAnyof](docs/MammalAnyof.md) - [Petstore::MammalWithoutDiscriminator](docs/MammalWithoutDiscriminator.md) diff --git a/samples/client/petstore/ruby/docs/Cow.md b/samples/client/petstore/ruby/docs/Cow.md new file mode 100644 index 00000000000..6fa7084b8a4 --- /dev/null +++ b/samples/client/petstore/ruby/docs/Cow.md @@ -0,0 +1,15 @@ +# Petstore::Cow + +## Properties + +| Name | Type | Description | Notes | +| ---- | ---- | ----------- | ----- | + +## Example + +```ruby +require 'petstore' + +instance = Petstore::Cow.new() +``` + diff --git a/samples/client/petstore/ruby/docs/MamalWithEnum.md b/samples/client/petstore/ruby/docs/MamalWithEnum.md new file mode 100644 index 00000000000..37b9252a2c8 --- /dev/null +++ b/samples/client/petstore/ruby/docs/MamalWithEnum.md @@ -0,0 +1,47 @@ +# Petstore::MamalWithEnum + +## Class instance methods + +### `openapi_one_of` + +Returns the list of classes defined in oneOf. + +#### Example + +```ruby +require 'petstore' + +Petstore::MamalWithEnum.openapi_one_of +# => +# [ +# :'Cow' +# ] +``` + +### build + +Find the appropriate object from the `openapi_one_of` list and casts the data into it. + +#### Example + +```ruby +require 'petstore' + +Petstore::MamalWithEnum.build(data) +# => # + +Petstore::MamalWithEnum.build(data_that_doesnt_match) +# => nil +``` + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| **data** | **Mixed** | data to be matched against the list of oneOf items | + +#### Return type + +- `Cow` +- `nil` (if no type matches) + diff --git a/samples/client/petstore/ruby/lib/petstore.rb b/samples/client/petstore/ruby/lib/petstore.rb index fa0658b97c2..b6662f07ea5 100644 --- a/samples/client/petstore/ruby/lib/petstore.rb +++ b/samples/client/petstore/ruby/lib/petstore.rb @@ -28,6 +28,7 @@ require 'petstore/models/capitalization' require 'petstore/models/category' require 'petstore/models/class_model' require 'petstore/models/client' +require 'petstore/models/cow' require 'petstore/models/deprecated_object' require 'petstore/models/enum_arrays' require 'petstore/models/enum_class' @@ -41,6 +42,7 @@ require 'petstore/models/format_test' require 'petstore/models/has_only_read_only' require 'petstore/models/health_check_result' require 'petstore/models/list' +require 'petstore/models/mamal_with_enum' require 'petstore/models/mammal' require 'petstore/models/mammal_anyof' require 'petstore/models/mammal_without_discriminator' diff --git a/samples/client/petstore/ruby/lib/petstore/models/cow.rb b/samples/client/petstore/ruby/lib/petstore/models/cow.rb new file mode 100644 index 00000000000..c209f752ce8 --- /dev/null +++ b/samples/client/petstore/ruby/lib/petstore/models/cow.rb @@ -0,0 +1,40 @@ +=begin +#OpenAPI Petstore + +#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + +The version of the OpenAPI document: 1.0.0 + +Generated by: https://openapi-generator.tech +OpenAPI Generator version: 7.3.0-SNAPSHOT + +=end + +require 'date' +require 'time' + +module Petstore + class Cow + BLACK_AND_WHITE_COW = "BlackAndWhiteCow".freeze + BROWN_COW = "BrownCow".freeze + + def self.all_vars + @all_vars ||= [BLACK_AND_WHITE_COW, BROWN_COW].freeze + end + + # Builds the enum from string + # @param [String] The enum value in the form of the string + # @return [String] The enum value + def self.build_from_hash(value) + new.build_from_hash(value) + end + + # Builds the enum from string + # @param [String] The enum value in the form of the string + # @return [String] The enum value + def build_from_hash(value) + return value if Cow.all_vars.include?(value) + raise "Invalid ENUM value #{value} for class #Cow" + end + end +end diff --git a/samples/client/petstore/ruby/lib/petstore/models/mamal_with_enum.rb b/samples/client/petstore/ruby/lib/petstore/models/mamal_with_enum.rb new file mode 100644 index 00000000000..4cf499b269d --- /dev/null +++ b/samples/client/petstore/ruby/lib/petstore/models/mamal_with_enum.rb @@ -0,0 +1,104 @@ +=begin +#OpenAPI Petstore + +#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + +The version of the OpenAPI document: 1.0.0 + +Generated by: https://openapi-generator.tech +OpenAPI Generator version: 7.3.0-SNAPSHOT + +=end + +require 'date' +require 'time' + +module Petstore + module MamalWithEnum + class << self + # List of class defined in oneOf (OpenAPI v3) + def openapi_one_of + [ + :'Cow' + ] + end + + # Builds the object + # @param [Mixed] Data to be matched against the list of oneOf items + # @return [Object] Returns the model or the data itself + def build(data) + # Go through the list of oneOf items and attempt to identify the appropriate one. + # Note: + # - We do not attempt to check whether exactly one item matches. + # - No advanced validation of types in some cases (e.g. "x: { type: string }" will happily match { x: 123 }) + # due to the way the deserialization is made in the base_object template (it just casts without verifying). + # - TODO: scalar values are de facto behaving as if they were nullable. + # - TODO: logging when debugging is set. + openapi_one_of.each do |klass| + begin + next if klass == :AnyType # "nullable: true" + typed_data = find_and_cast_into_type(klass, data) + return typed_data if typed_data + rescue # rescue all errors so we keep iterating even if the current item lookup raises + end + end + + openapi_one_of.include?(:AnyType) ? data : nil + end + + private + + SchemaMismatchError = Class.new(StandardError) + + # Note: 'File' is missing here because in the regular case we get the data _after_ a call to JSON.parse. + def find_and_cast_into_type(klass, data) + return if data.nil? + + case klass.to_s + when 'Boolean' + return data if data.instance_of?(TrueClass) || data.instance_of?(FalseClass) + when 'Float' + return data if data.instance_of?(Float) + when 'Integer' + return data if data.instance_of?(Integer) + when 'Time' + return Time.parse(data) + when 'Date' + return Date.parse(data) + when 'String' + return data if data.instance_of?(String) + when 'Object' # "type: object" + return data if data.instance_of?(Hash) + when /\AArray<(?.+)>\z/ # "type: array" + if data.instance_of?(Array) + sub_type = Regexp.last_match[:sub_type] + return data.map { |item| find_and_cast_into_type(sub_type, item) } + end + when /\AHash.+)>\z/ # "type: object" with "additionalProperties: { ... }" + if data.instance_of?(Hash) && data.keys.all? { |k| k.instance_of?(Symbol) || k.instance_of?(String) } + sub_type = Regexp.last_match[:sub_type] + return data.each_with_object({}) { |(k, v), hsh| hsh[k] = find_and_cast_into_type(sub_type, v) } + end + else # model + const = Petstore.const_get(klass) + if const + if const.respond_to?(:openapi_one_of) # nested oneOf model + model = const.build(data) + return model if model + else + # raise if data contains keys that are not known to the model + raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty? + model = const.build_from_hash(data) + return model if model + end + end + end + + raise # if no match by now, raise + rescue + raise SchemaMismatchError, "#{data} doesn't match the #{klass} type" + end + end + end + +end diff --git a/samples/client/petstore/ruby/lib/petstore/models/mammal_anyof.rb b/samples/client/petstore/ruby/lib/petstore/models/mammal_anyof.rb index b7455a5f251..7d5aed31496 100644 --- a/samples/client/petstore/ruby/lib/petstore/models/mammal_anyof.rb +++ b/samples/client/petstore/ruby/lib/petstore/models/mammal_anyof.rb @@ -87,7 +87,7 @@ module Petstore return model if model else # raise if data contains keys that are not known to the model - raise unless (data.keys - const.acceptable_attributes).empty? + raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty? model = const.build_from_hash(data) return model if model end diff --git a/samples/client/petstore/ruby/lib/petstore/models/mammal_without_discriminator.rb b/samples/client/petstore/ruby/lib/petstore/models/mammal_without_discriminator.rb index ef513ff8a66..6eaac608cf7 100644 --- a/samples/client/petstore/ruby/lib/petstore/models/mammal_without_discriminator.rb +++ b/samples/client/petstore/ruby/lib/petstore/models/mammal_without_discriminator.rb @@ -88,7 +88,7 @@ module Petstore return model if model else # raise if data contains keys that are not known to the model - raise unless (data.keys - const.acceptable_attributes).empty? + raise if const.respond_to?(:acceptable_attributes) && !(data.keys - const.acceptable_attributes).empty? model = const.build_from_hash(data) return model if model end diff --git a/samples/client/petstore/ruby/spec/models/cow_spec.rb b/samples/client/petstore/ruby/spec/models/cow_spec.rb new file mode 100644 index 00000000000..e1cf66c03ac --- /dev/null +++ b/samples/client/petstore/ruby/spec/models/cow_spec.rb @@ -0,0 +1,30 @@ +=begin +#OpenAPI Petstore + +#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + +The version of the OpenAPI document: 1.0.0 + +Generated by: https://openapi-generator.tech +OpenAPI Generator version: 7.3.0-SNAPSHOT + +=end + +require 'spec_helper' +require 'json' +require 'date' + +# Unit tests for Petstore::Cow +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe Petstore::Cow do + let(:instance) { Petstore::Cow.new } + + describe 'test an instance of Cow' do + it 'should create an instance of Cow' do + # uncomment below to test the instance creation + #expect(instance).to be_instance_of(Petstore::Cow) + end + end + +end diff --git a/samples/client/petstore/ruby/spec/models/mamal_with_enum_spec.rb b/samples/client/petstore/ruby/spec/models/mamal_with_enum_spec.rb new file mode 100644 index 00000000000..e3c1f1255c1 --- /dev/null +++ b/samples/client/petstore/ruby/spec/models/mamal_with_enum_spec.rb @@ -0,0 +1,36 @@ +=begin +#OpenAPI Petstore + +#This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + +The version of the OpenAPI document: 1.0.0 + +Generated by: https://openapi-generator.tech +OpenAPI Generator version: 7.3.0-SNAPSHOT + +=end + +require 'spec_helper' +require 'json' +require 'date' + +# Unit tests for Petstore::MamalWithEnum +# Automatically generated by openapi-generator (https://openapi-generator.tech) +# Please update as you see appropriate +describe Petstore::MamalWithEnum do + describe '.openapi_one_of' do + it 'lists the items referenced in the oneOf array' do + expect(described_class.openapi_one_of).to_not be_empty + end + end + + describe '.build' do + it 'returns the correct model' do + expect(described_class.build("BlackAndWhiteCow")).to eq(Petstore::Cow::BLACK_AND_WHITE_COW) + end + + it 'returns nil for unknown model' do + expect(described_class.build({ classname: 'monkey', type: 'gorilla' })).to be_nil + end + end +end