class RSpec::Rails::Api::Metadata

Handles contexts and examples metadatas.

Attributes

current_resource[R]
entities[R]
parameters[R]
resources[R]

Public Class Methods

new() click to toggle source
# File lib/rspec/rails/api/metadata.rb, line 14
def initialize
  @resources  = {}
  @entities   = {}
  @parameters = {}
  # Only used when building metadata during RSpec boot
  @current_resource = nil
  @current_method   = nil
  @current_url      = nil
  @current_code     = nil
end

Public Instance Methods

add_action(method, url, description) click to toggle source
# File lib/rspec/rails/api/metadata.rb, line 78
def add_action(method, url, description)
  check_current_context :resource

  Utils.deep_set(@resources, "#{@current_resource}.paths.#{url}.actions.#{method}",
                 description: description,
                 statuses:    {},
                 params:      {})

  @current_url    = url
  @current_method = method
end
add_entity(type, fields) click to toggle source
# File lib/rspec/rails/api/metadata.rb, line 30
def add_entity(type, fields)
  Utils.deep_set(@resources,
                 "#{@current_resource}.entities.#{type}",
                 EntityConfig.new(fields))
end
add_parameter(type, fields) click to toggle source
# File lib/rspec/rails/api/metadata.rb, line 36
def add_parameter(type, fields)
  raise "Parameter #{type} is already defined" if @parameters[type]

  @parameters[type] = fields
end
add_path_params(fields) click to toggle source
# File lib/rspec/rails/api/metadata.rb, line 42
def add_path_params(fields) # rubocop:disable Metrics/MethodLength
  check_current_context :resource, :url

  chunks = @current_url.split('?')

  fields.each do |name, field|
    valid_attribute = Utils.check_attribute_type(field[:type], except: %i[array object])
    raise "Field type not allowed: #{field[:type]}" unless valid_attribute

    scope = path_param_scope(chunks, name)
    Utils.deep_set(@resources, "#{@current_resource}.paths.#{@current_url}.path_params.#{name}",
                   description: field[:description] || nil,
                   type:        field[:type] || nil,
                   required:    field[:required] || true,
                   scope:       scope)
  end
end
add_request_example(url: nil, action: nil, status_code: nil, response: nil, path_params: nil, params: nil) click to toggle source

rubocop:disable Metrics/ParameterLists

# File lib/rspec/rails/api/metadata.rb, line 103
def add_request_example(url: nil, action: nil, status_code: nil, response: nil, path_params: nil, params: nil)
  resource = nil
  @resources.each do |key, res|
    resource = key if Utils.deep_get(res, "paths.#{url}.actions.#{action}.statuses.#{status_code}")
  end

  raise "Resource not found for #{action.upcase} #{url}" unless resource

  Utils.deep_set(@resources,
                 "#{resource}.paths.#{url}.actions.#{action}.statuses.#{status_code}.example",
                 path_params: path_params,
                 params:      params,
                 response:    response)
end
add_request_params(fields) click to toggle source

Fields should be something like:

id:   {type: :number, description: 'Something'},
name: {type: string, description: 'Something'}

Ex. with sub elements:

id: {type: :number, description: 'Something'},
something: {type: :object, description: 'Something', properties: {
  property: {type: :string, description: 'Something'},
  ...
}}
# File lib/rspec/rails/api/metadata.rb, line 69
def add_request_params(fields)
  check_current_context :resource, :url, :method

  params = organize_params fields
  Utils.deep_set(@resources,
                 "#{@current_resource}.paths.#{@current_url}.actions.#{@current_method}.params",
                 params)
end
add_resource(name, description) click to toggle source
# File lib/rspec/rails/api/metadata.rb, line 25
def add_resource(name, description)
  @resources[name.to_sym] = { description: description, paths: {} }
  @current_resource       = name.to_sym
end
add_status_code(status_code, description) click to toggle source

rubocop:disable Layout/LineLength

# File lib/rspec/rails/api/metadata.rb, line 91
def add_status_code(status_code, description)
  check_current_context :resource, :url, :method

  Utils.deep_set(@resources,
                 "#{@current_resource}.paths.#{@current_url}.actions.#{@current_method}.statuses.#{status_code}",
                 description: description,
                 example:     { response: nil })
  @current_code = status_code
end
to_h() click to toggle source

rubocop:enable Metrics/ParameterLists

# File lib/rspec/rails/api/metadata.rb, line 119
def to_h
  {
    resources: @resources,
    entities:  @entities,
  }
end

Private Instance Methods

check_current_context(*scope) click to toggle source
# File lib/rspec/rails/api/metadata.rb, line 128
def check_current_context(*scope) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
  scope ||= []
  raise 'No resource declared' if scope.include?(:resource) && !@current_resource
  raise 'No action declared' if scope.include?(:method) && !@current_method
  raise 'No url declared' if scope.include?(:url) && !@current_url
  raise 'No status code declared' if scope.include?(:code) && !@current_code
end
fill_request_param(field) click to toggle source
# File lib/rspec/rails/api/metadata.rb, line 162
def fill_request_param(field)
  if field[:type] == :object && field[:properties]
    organize_params field[:properties]
  else
    properties = {
      type:        PARAM_TYPES[field[:type]][:type],
      description: field[:description] || nil,
    }

    properties[:items] = organize_params field[:of] if field[:type] == :array && field[:of]
    properties
  end
end
organize_params(fields) click to toggle source
# File lib/rspec/rails/api/metadata.rb, line 146
def organize_params(fields) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
  out      = { properties: {} }
  required = []
  allowed_types = %i[array object]
  fields.each do |name, field|
    allowed_type = allowed_types.include?(field[:type]) || PARAM_TYPES.key?(field[:type])
    raise "Field type not allowed: #{field[:type]}" unless allowed_type

    required.push name.to_s if field[:required]

    out[:properties][name] = fill_request_param field
  end
  out[:required] = required if required.count.positive?
  out
end
path_param_scope(url_chunks, name) click to toggle source
# File lib/rspec/rails/api/metadata.rb, line 136
def path_param_scope(url_chunks, name)
  if /:#{name}/.match?(url_chunks[0])
    :path
  elsif url_chunks[1] && /:#{name}/ =~ url_chunks[1]
    :query
  else
    raise "#{name} not found in URL #{@current_url}"
  end
end