tim-vandecasteele/grape-swagger

View on GitHub
lib/grape-swagger/doc_methods/parse_params.rb

Summary

Maintainability
A
3 hrs
Test Coverage
module GrapeSwagger
  module DocMethods
    class ParseParams
      class << self
        def call(param, settings, route, definitions)
          path = route.path
          method = route.request_method

          additional_documentation = settings.fetch(:documentation, {})
          settings.merge!(additional_documentation)
          data_type = DataType.call(settings)

          value_type = settings.merge(data_type: data_type, path: path, param_name: param, method: method)

          # required properties
          @parsed_param = {
            in:   param_type(value_type),
            name: settings[:full_name] || param
          }

          # optional properties
          document_description(settings)
          document_type_and_format(data_type)
          document_array_param(value_type, definitions) if value_type[:is_array]
          document_default_value(settings)
          document_range_values(settings)
          document_required(settings)

          @parsed_param
        end

        private

        def document_description(settings)
          description = settings[:desc] || settings[:description]
          @parsed_param[:description] = description if description
        end

        def document_required(settings)
          @parsed_param[:required] = settings[:required] || false
          @parsed_param[:required] = true if @parsed_param[:in] == 'path'
        end

        def document_range_values(settings)
          values               = settings[:values] || nil
          enum_or_range_values = parse_enum_or_range_values(values)
          @parsed_param.merge!(enum_or_range_values) if enum_or_range_values
        end

        def document_default_value(settings)
          @parsed_param[:default] = settings[:default] if settings[:default].present?
        end

        def document_type_and_format(data_type)
          if DataType.primitive?(data_type)
            data = DataType.mapping(data_type)
            @parsed_param[:type], @parsed_param[:format] = data
          else
            @parsed_param[:type] = data_type
          end
        end

        def document_array_param(value_type, definitions)
          if value_type[:documentation].present?
            param_type = value_type[:documentation][:param_type]
            doc_type = value_type[:documentation][:type]
            type = DataType.mapping(doc_type) if doc_type && !DataType.request_primitive?(doc_type)
            collection_format = value_type[:documentation][:collectionFormat]
          end

          param_type ||= value_type[:param_type]

          array_items = {}
          if definitions[value_type[:data_type]]
            array_items['$ref'] = "#/definitions/#{@parsed_param[:type]}"
          else
            array_items[:type] = type || @parsed_param[:type] == 'array' ? 'string' : @parsed_param[:type]
          end
          array_items[:format] = @parsed_param.delete(:format) if @parsed_param[:format]

          @parsed_param[:in] = param_type || 'formData'
          @parsed_param[:items] = array_items
          @parsed_param[:type] = 'array'
          @parsed_param[:collectionFormat] = collection_format if DataType.collections.include?(collection_format)
        end

        def param_type(value_type)
          param_type = value_type[:param_type] || value_type[:in]
          if value_type[:path].include?("{#{value_type[:param_name]}}")
            'path'
          elsif param_type
            param_type
          elsif %w(POST PUT PATCH).include?(value_type[:method])
            DataType.request_primitive?(value_type[:data_type]) ? 'formData' : 'body'
          else
            'query'
          end
        end

        def parse_enum_or_range_values(values)
          case values
          when Range
            parse_range_values(values) if values.first.is_a?(Integer)
          when Proc
            values_result = values.call
            if values_result.is_a?(Range) && values_result.first.is_a?(Integer)
              parse_range_values(values_result)
            else
              { enum: values_result }
            end
          else
            { enum: values } if values
          end
        end

        def parse_range_values(values)
          { minimum: values.first, maximum: values.last }
        end
      end
    end
  end
end