module RSpec::OpenAPI::SchemaBuilder

Public Class Methods

build(record) click to toggle source

@param [RSpec::OpenAPI::Record] record @return [Hash]

# File lib/rspec/openapi/schema_builder.rb, line 4
def build(record)
  response = {
    description: record.description,
  }

  if record.response_body
    disposition = normalize_content_disposition(record.response_content_disposition)
    response[:content] = {
      normalize_content_type(record.response_content_type) => {
        schema: build_property(record.response_body, disposition: disposition),
        example: response_example(record, disposition: disposition),
      }.compact,
    }
  end

  {
    paths: {
      normalize_path(record.path) => {
        record.method.downcase => {
          summary: record.summary,
          tags: record.tags,
          parameters: build_parameters(record),
          requestBody: build_request_body(record),
          responses: {
            record.status.to_s => response,
          },
        }.compact,
      },
    },
  }
end

Private Class Methods

build_example(value) click to toggle source
# File lib/rspec/openapi/schema_builder.rb, line 158
def build_example(value)
  return nil if value.nil?
  value = value.dup
  value.each do |key, v|
    if v.is_a?(ActionDispatch::Http::UploadedFile)
      value[key] = v.original_filename
    end
  end
end
build_parameter_name(key, value) click to toggle source
# File lib/rspec/openapi/schema_builder.rb, line 84
def build_parameter_name(key, value)
  key = key.to_s
  if value.is_a?(Hash) && (value_keys = value.keys).size == 1
    value_key = value_keys.first
    build_parameter_name("#{key}[#{value_key}]", value[value_key])
  else
    key
  end
end
build_parameters(record) click to toggle source
# File lib/rspec/openapi/schema_builder.rb, line 48
def build_parameters(record)
  parameters = []

  record.path_params.each do |key, value|
    parameters << {
      name: build_parameter_name(key, value),
      in: 'path',
      required: true,
      schema: build_property(try_cast(value)),
      example: (try_cast(value) if example_enabled?),
    }.compact
  end

  record.query_params.each do |key, value|
    parameters << {
      name: build_parameter_name(key, value),
      in: 'query',
      schema: build_property(try_cast(value)),
      example: (try_cast(value) if example_enabled?),
    }.compact
  end

  record.request_headers.each do |key, value|
    parameters << {
      name: build_parameter_name(key, value),
      in: 'header',
      required: true,
      schema: build_property(try_cast(value)),
      example: (try_cast(value) if example_enabled?),
    }.compact
  end

  return nil if parameters.empty?
  parameters
end
build_property(value, disposition: nil) click to toggle source
# File lib/rspec/openapi/schema_builder.rb, line 108
def build_property(value, disposition: nil)
  property = build_type(value, disposition)

  case value
  when Array
    property[:items] = build_property(value.first)
  when Hash
    property[:properties] = {}.tap do |properties|
      value.each do |key, v|
        properties[key] = build_property(v)
      end
    end
  end
  property
end
build_request_body(record) click to toggle source
# File lib/rspec/openapi/schema_builder.rb, line 94
def build_request_body(record)
  return nil if record.request_content_type.nil?
  return nil if record.request_params.empty?

  {
    content: {
      normalize_content_type(record.request_content_type) => {
        schema: build_property(record.request_params),
        example: (build_example(record.request_params) if example_enabled?),
      }.compact
    }
  }
end
build_type(value, disposition) click to toggle source
# File lib/rspec/openapi/schema_builder.rb, line 124
def build_type(value, disposition)
  return { type: 'string', format: 'binary' } if disposition

  case value
  when String
    { type: 'string' }
  when Integer
    { type: 'integer' }
  when Float
    { type: 'number', format: 'float' }
  when TrueClass, FalseClass
    { type: 'boolean' }
  when Array
    { type: 'array' }
  when Hash
    { type: 'object' }
  when ActionDispatch::Http::UploadedFile
    { type: 'string', format: 'binary' }
  when NilClass
    { nullable: true }
  else
    raise NotImplementedError, "type detection is not implemented for: #{value.inspect}"
  end
end
example_enabled?() click to toggle source
# File lib/rspec/openapi/schema_builder.rb, line 44
def example_enabled?
  RSpec::OpenAPI.enable_example
end
normalize_content_disposition(content_disposition) click to toggle source
# File lib/rspec/openapi/schema_builder.rb, line 176
def normalize_content_disposition(content_disposition)
  content_disposition&.sub(/;.+\z/, '')
end
normalize_content_type(content_type) click to toggle source
# File lib/rspec/openapi/schema_builder.rb, line 172
def normalize_content_type(content_type)
  content_type&.sub(/;.+\z/, '')
end
normalize_path(path) click to toggle source
# File lib/rspec/openapi/schema_builder.rb, line 168
def normalize_path(path)
  path.gsub(%r|/:([^:/]+)|, '/{\1}')
end
response_example(record, disposition:) click to toggle source
# File lib/rspec/openapi/schema_builder.rb, line 38
def response_example(record, disposition:)
  return nil if !example_enabled? || disposition

  record.response_body
end
try_cast(value) click to toggle source

Convert an always-String param to an appropriate type

# File lib/rspec/openapi/schema_builder.rb, line 150
def try_cast(value)
  begin
    Integer(value)
  rescue TypeError, ArgumentError
    value
  end
end