lib/rspec/rails/swagger/request_builder.rb
module RSpec
module Rails
module Swagger
class RequestBuilder
attr_reader :metadata, :instance
##
# Creates a new RequestBuilder from the Example class's +metadata+ hash
# and a test +instance+ that we can use to populate the parameter
# values.
def initialize(metadata, instance)
@metadata, @instance = metadata, instance
end
##
# Finds the Document associated with this request so things like schema
# and parameter references can be resolved.
def document
@document ||= begin
Document.new(RSpec.configuration.swagger_docs[metadata[:swagger_doc]])
end
end
def method
metadata[:swagger_operation][:method]
end
def produces
Array(metadata[:swagger_operation][:produces]).presence || Array(document[:produces])
end
def consumes
Array(metadata[:swagger_operation][:consumes]).presence || Array(document[:consumes])
end
##
# Returns parameters defined in the operation and path item. Providing
# a +location+ will filter the parameters to those with a matching +in+
# value.
def parameters location = nil
path_item = metadata[:swagger_path_item] || {}
operation = metadata[:swagger_operation] || {}
params = path_item.fetch(:parameters, {}).merge(operation.fetch(:parameters, {}))
if location.present?
params.select{ |k, _| k.starts_with? "#{location}&" }
else
params
end
end
def parameter_values location
values = parameters(location).
map{ |_, p| p['$ref'] ? document.resolve_ref(p['$ref']) : p }.
select{ |p| p[:required] || instance.respond_to?(p[:name]) }.
map{ |p| [p[:name], instance.send(p[:name])] }
Hash[values]
end
def headers
headers = {}
# Match the names that Rails uses internally
headers['HTTP_ACCEPT'] = produces.first if produces.present?
headers['CONTENT_TYPE'] = consumes.first if consumes.present?
# TODO: do we need to do some capitalization to match the rack
# conventions?
parameter_values(:header).each { |k, v| headers[k] = v }
headers
end
##
# If +instance+ defines an +env+ method this will return those values
# for inclusion in the Rack env hash.
def env
return {} unless instance.respond_to? :env
instance.env
end
def path
base_path = document[:basePath] || ''
# Find params in the path and replace them with values defined in
# in the example group.
base_path + metadata[:swagger_path_item][:path].gsub(/(\{.*?\})/) do |match|
# QUESTION: Should check that the parameter is actually defined in
# `parameters` before fetch a value?
instance.send(match[1...-1])
end
end
def query
query_params = parameter_values(:query).to_query
"?#{query_params}" unless query_params.blank?
end
def body
# And here all we need is the first half of the key to find the body
# parameter and its name to fetch a value.
key = parameters(:body).keys.first
if key
instance.send(key.split('&').last).to_json
end
end
end
end
end
end