diff --git a/lib/apipie-rails.rb b/lib/apipie-rails.rb index 5e647fc28..27ea554ad 100644 --- a/lib/apipie-rails.rb +++ b/lib/apipie-rails.rb @@ -27,3 +27,5 @@ require "apipie/generator/swagger/swagger" require "apipie/generator/swagger/warning" require "apipie/generator/swagger/warning_writer" +require "apipie/generator/swagger/type" +require "apipie/generator/swagger/type_extractor" diff --git a/lib/apipie/generator/generator.rb b/lib/apipie/generator/generator.rb new file mode 100644 index 000000000..548157f34 --- /dev/null +++ b/lib/apipie/generator/generator.rb @@ -0,0 +1,2 @@ +module Apipie::Generator +end diff --git a/lib/apipie/generator/swagger/swagger.rb b/lib/apipie/generator/swagger/swagger.rb new file mode 100644 index 000000000..b4b64ad57 --- /dev/null +++ b/lib/apipie/generator/swagger/swagger.rb @@ -0,0 +1,2 @@ +module Apipie::Generator::Swagger +end diff --git a/lib/apipie/generator/swagger/type.rb b/lib/apipie/generator/swagger/type.rb new file mode 100644 index 000000000..a94bbc331 --- /dev/null +++ b/lib/apipie/generator/swagger/type.rb @@ -0,0 +1,16 @@ +class Apipie::Generator::Swagger::Type + attr_reader :str_format + + def initialize(type, str_format = nil) + @type = type + @str_format = str_format + end + + def to_s + @type + end + + def ==(other) + other.to_s == self.to_s + end +end diff --git a/lib/apipie/generator/swagger/type_extractor.rb b/lib/apipie/generator/swagger/type_extractor.rb new file mode 100644 index 000000000..7dc35bc17 --- /dev/null +++ b/lib/apipie/generator/swagger/type_extractor.rb @@ -0,0 +1,69 @@ +class Apipie::Generator::Swagger::TypeExtractor + TYPES = { + numeric: 'number', + hash: 'object', + array: 'array', + enum: 'enum', + + # see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types + integer: Apipie::Generator::Swagger::Type.new('integer', 'int32'), + long: Apipie::Generator::Swagger::Type.new('integer', 'int64'), + number: Apipie::Generator::Swagger::Type.new('number'), + float: Apipie::Generator::Swagger::Type.new('number', 'float'), + double: Apipie::Generator::Swagger::Type.new('number', 'double'), + string: Apipie::Generator::Swagger::Type.new('string'), + byte: Apipie::Generator::Swagger::Type.new('string', 'byte'), + binary: Apipie::Generator::Swagger::Type.new('string', 'binary'), + boolean: Apipie::Generator::Swagger::Type.new('boolean'), + date: Apipie::Generator::Swagger::Type.new('string', 'date'), + dateTime: Apipie::Generator::Swagger::Type.new('string', 'date-time'), + password: Apipie::Generator::Swagger::Type.new('string', 'password') + } + + # @param [] validator + def initialize(validator) + @validator = validator + end + + # @param [Hash] warnings + def extract_with_warnings(warnings = {}) + if boolean? && warnings[:boolean].present? + Apipie::Generator::Swagger::WarningWriter.instance.warn(warnings[:boolean]) + end + + extract + end + + private + + def extract + expected_type = if string? + :string + elsif boolean? + :boolean + elsif enum? + :enum + else + @validator.expected_type.to_sym + end + + TYPES[expected_type] || @validator.expected_type + end + + def string? + @validator.blank? + end + + def enum? + @validator.is_a?(Apipie::Validator::EnumValidator) || + (@validator.respond_to?(:is_enum) && @validator.is_enum) + end + + def boolean? + @_boolean ||= enum? && boolean_values? + end + + def boolean_values? + @validator.values.to_set == Set.new([true, false]) + end +end diff --git a/lib/apipie/swagger_generator.rb b/lib/apipie/swagger_generator.rb index d1e9d1f53..893826b05 100644 --- a/lib/apipie/swagger_generator.rb +++ b/lib/apipie/swagger_generator.rb @@ -318,68 +318,21 @@ def swagger_op_id_for_path(http_method, path) http_method.downcase + path.gsub(/\//,'_').gsub(/:(\w+)/, '\1').gsub(/_$/,'') end - class SwaggerTypeWithFormat - attr_reader :str_format - def initialize(type, str_format) - @type = type - @str_format = str_format - end - - def to_s - @type - end - - def ==(other) - other.to_s == self.to_s - end - end - - def lookup - @lookup ||= { - numeric: "number", - hash: "object", - array: "array", - - # see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types - integer: SwaggerTypeWithFormat.new("integer", "int32"), - long: SwaggerTypeWithFormat.new("integer", "int64"), - number: SwaggerTypeWithFormat.new("number", nil), # here just for completeness - float: SwaggerTypeWithFormat.new("number", "float"), - double: SwaggerTypeWithFormat.new("number", "double"), - string: SwaggerTypeWithFormat.new("string", nil), # here just for completeness - byte: SwaggerTypeWithFormat.new("string", "byte"), - binary: SwaggerTypeWithFormat.new("string", "binary"), - boolean: SwaggerTypeWithFormat.new("boolean", nil), # here just for completeness - date: SwaggerTypeWithFormat.new("string", "date"), - dateTime: SwaggerTypeWithFormat.new("string", "date-time"), - password: SwaggerTypeWithFormat.new("string", "password"), - } - end - - def swagger_param_type(param_desc) - if param_desc.nil? - raise("problem") + if param_desc.blank? + raise ArgumentError, 'param_desc is required' end - v = param_desc.validator - if v.nil? - return "string" - end - - if v.class == Apipie::Validator::EnumValidator || (v.respond_to?(:is_enum?) && v.is_enum?) - if v.values - [true, false] == [] && [true, false] - v.values == [] - warn_inferring_boolean(param_desc.name) - return "boolean" - else - return "enum" - end - elsif v.class == Apipie::Validator::HashValidator - # pp v - end + method_id = ruby_name_for_method(@current_method) + warning = Apipie::Generator::Swagger::Warning.for_code( + Apipie::Generator::Swagger::Warning::INFERRING_BOOLEAN_CODE, + method_id, + { parameter: param_desc.name } + ) - return lookup[v.expected_type.to_sym] || v.expected_type + Apipie::Generator::Swagger::TypeExtractor.new(param_desc.validator). + extract_with_warnings({ boolean: warning }) end @@ -515,7 +468,7 @@ def save_field(entry, openapi_key, v, apipie_key=openapi_key, translate=false) swg_param_type = swagger_param_type(param_desc) swagger_def[:type] = swg_param_type.to_s - if (swg_param_type.is_a? SwaggerTypeWithFormat) && !swg_param_type.str_format.nil? + if (swg_param_type.is_a? Apipie::Generator::Swagger::Type) && !swg_param_type.str_format.nil? swagger_def[:format] = swg_param_type.str_format end diff --git a/spec/lib/generator/swagger/type_extractor_spec.rb b/spec/lib/generator/swagger/type_extractor_spec.rb new file mode 100644 index 000000000..ab7fdc6f4 --- /dev/null +++ b/spec/lib/generator/swagger/type_extractor_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' + +describe Apipie::Generator::Swagger::TypeExtractor do + let(:validator) {} + let(:extractor) { described_class.new(validator) } + + describe '#extarct_with_warnings' do + let(:warnings) { {} } + + before { Apipie.configuration.swagger_suppress_warnings = false } + + subject { extractor.extract_with_warnings(warnings) } + + it { is_expected.to eq(Apipie::Generator::Swagger::TypeExtractor::TYPES[:string]) } + + context "when enum validator is used" do + let(:enum_values) { ["Name"] } + + context "of type Apipie::Validator::EnumValidator" do + let(:validator) { Apipie::Validator::EnumValidator.new(nil, enum_values) } + + it { is_expected.to eq("enum") } + end + + context "that responds to is_enum?" do + let(:validator) do + Apipie::ResponseDescriptionAdapter::PropDesc::Validator.new('some-type', enum_values) + end + + it 'returns an enum type' do + expect(subject).to eq(Apipie::Generator::Swagger::TypeExtractor::TYPES[:enum]) + end + + context 'and has `true`, `false` as values' do + let(:param_description_name) { :visible } + let(:enum_values) { [true, false] } + + it 'returns a boolean type' do + expect(subject).to eq(Apipie::Generator::Swagger::TypeExtractor::TYPES[:boolean]) + end + + context 'and a boolean warning is passed' do + let(:boolean_warning) do + Apipie::Generator::Swagger::Warning.for_code( + Apipie::Generator::Swagger::Warning::INFERRING_BOOLEAN_CODE, + 'SampleController#action', + { parameter: 'some-param' } + ) + end + + let(:warnings) { { boolean: boolean_warning } } + + it 'outputs the warning' do + expect { subject }.to output(boolean_warning.warning_message).to_stderr + end + end + end + end + end + end +end