class Swagalicious::RequestFactory

Public Class Methods

new(config = ::Swagalicious.config) click to toggle source
# File lib/swagalicious/request_factory.rb, line 6
def initialize(config = ::Swagalicious.config)
  @config = config
end

Public Instance Methods

build_request(metadata, example) click to toggle source
# File lib/swagalicious/request_factory.rb, line 10
def build_request(metadata, example)
  swagger_doc = @config.get_swagger_doc(metadata[:swagger_doc])
  parameters  = expand_parameters(metadata, swagger_doc, example)

  {}.tap do |request|
    add_verb(request, metadata)
    add_path(request, metadata, swagger_doc, parameters, example)
    add_headers(request, metadata, swagger_doc, parameters, example)
    add_payload(request, parameters, example)
  end
end

Private Instance Methods

add_headers(request, metadata, swagger_doc, parameters, example) click to toggle source
# File lib/swagalicious/request_factory.rb, line 121
def add_headers(request, metadata, swagger_doc, parameters, example)
  tuples = parameters
    .select { |p| p[:in] == :header }
    .map { |p| [p[:name], example.send(p[:name]).to_s] }

  # Accept header
  produces = metadata[:operation][:produces] || swagger_doc[:produces]
  if produces
    accept = example.respond_to?(:Accept) ? example.send(:Accept) : produces.first
    tuples << ["Accept", accept]
  end

  # Content-Type header
  consumes = metadata[:operation][:consumes] || swagger_doc[:consumes]
  if consumes
    content_type = example.respond_to?(:"Content-Type") ? example.send(:"Content-Type") : consumes.first
    tuples << ["Content-Type", content_type]
  end

  # Rails test infrastructure requires rackified headers
  rackified_tuples = tuples.map do |pair|
    [
      case pair[0]
      when "Accept" then "HTTP_ACCEPT"
      when "Content-Type" then "CONTENT_TYPE"
      when "Authorization" then "HTTP_AUTHORIZATION"
      else pair[0]
      end,
      pair[1]
    ]
  end

  request[:headers] = Hash[rackified_tuples]
end
add_path(request, metadata, swagger_doc, parameters, example) click to toggle source
# File lib/swagalicious/request_factory.rb, line 88
def add_path(request, metadata, swagger_doc, parameters, example)
  template = (swagger_doc[:basePath] || (swagger_doc[:servers]&.first || {})[:url] || "") + metadata[:path_item][:template]

  request[:path] = template.tap do |path_template|
    parameters.select { |p| p[:in] == :path }.each do |p|
      path_template.gsub!("{#{p[:name]}}", example.send(p[:name]).to_s)
    end

    parameters.select { |p| p[:in] == :query }.each_with_index do |p, i|
      path_template.concat(i.zero? ? "?" : "&")
      path_template.concat(build_query_string_part(p, example.send(p[:name])))
    end
  end
end
add_payload(request, parameters, example) click to toggle source
# File lib/swagalicious/request_factory.rb, line 156
def add_payload(request, parameters, example)
  content_type = request[:headers]["CONTENT_TYPE"]
  return if content_type.nil?

  if ["application/x-www-form-urlencoded", "multipart/form-data"].include?(content_type)
    request[:payload] = build_form_payload(parameters, example)
  else
    request[:payload] = build_json_payload(parameters, example)
  end
end
add_verb(request, metadata) click to toggle source
# File lib/swagalicious/request_factory.rb, line 84
def add_verb(request, metadata)
  request[:verb] = metadata[:operation][:verb]
end
build_form_payload(parameters, example) click to toggle source
# File lib/swagalicious/request_factory.rb, line 167
def build_form_payload(parameters, example)
  # See http://seejohncode.com/2012/04/29/quick-tip-testing-multipart-uploads-with-rspec/
  # Rather that serializing with the appropriate encoding (e.g. multipart/form-data),
  # Rails test infrastructure allows us to send the values directly as a hash
  # PROS: simple to implement, CONS: serialization/deserialization is bypassed in test
  tuples = parameters
    .select { |p| p[:in] == :formData }
    .map { |p| [p[:name], example.send(p[:name])] }
  Hash[tuples]
end
build_json_payload(parameters, example) click to toggle source
# File lib/swagalicious/request_factory.rb, line 178
def build_json_payload(parameters, example)
  body_param = parameters.select { |p| p[:in] == :body }.first
  body_param ? example.send(body_param[:name]).to_json : nil
end
build_query_string_part(param, value) click to toggle source
# File lib/swagalicious/request_factory.rb, line 103
def build_query_string_part(param, value)
  name = param[:name]
  return "#{name}=#{value}" unless param[:type].to_sym == :array

  case param[:collectionFormat]
  when :ssv
    "#{name}=#{value.join(" ")}"
  when :tsv
    "#{name}=#{value.join("\t")}"
  when :pipes
    "#{name}=#{value.join("|")}"
  when :multi
    value.map { |v| "#{name}=#{v}" }.join("&")
  else
    "#{name}=#{value.join(",")}" # csv is default
  end
end
definition_version(swagger_doc) click to toggle source
# File lib/swagalicious/request_factory.rb, line 74
def definition_version(swagger_doc)
  if swagger_doc.key?(:parameters)
    puts "Swagalicious: WARNING: parameters is replaced in OpenAPI3! Rename to components/parameters (in swagger_helper.rb)"
    swagger_doc[:parameters]
  else
    components = swagger_doc[:components] || {}
    components[:parameters]
  end
end
derive_security_params(metadata, swagger_doc) click to toggle source
# File lib/swagalicious/request_factory.rb, line 36
def derive_security_params(metadata, swagger_doc)
  requirements = metadata[:operation][:security] || swagger_doc[:security] || []
  scheme_names = requirements.flat_map(&:keys)
  schemes      = security_version(scheme_names, swagger_doc)

  schemes.map do |scheme|
    param = (scheme[:type] == :apiKey) ? scheme.slice(:name, :in) : { name: "Authorization", in: :header }
    param.merge(type: :string, required: requirements.one?)
  end
end
doc_version(doc) click to toggle source
# File lib/swagalicious/request_factory.rb, line 183
def doc_version(doc)
  doc[:openapi] || doc[:swagger] || "3"
end
expand_parameters(metadata, swagger_doc, example) click to toggle source
# File lib/swagalicious/request_factory.rb, line 24
def expand_parameters(metadata, swagger_doc, example)
  operation_params = metadata[:operation][:parameters] || []
  path_item_params = metadata[:path_item][:parameters] || []
  security_params  = derive_security_params(metadata, swagger_doc)

  # NOTE: Use of + instead of concat to avoid mutation of the metadata object
  (operation_params + path_item_params + security_params)
    .map { |p| p["$ref"] ? resolve_parameter(p["$ref"], swagger_doc) : p }
    .uniq { |p| p[:name] }
    .reject { |p| p[:required] == false && !example.respond_to?(p[:name]) }
end
key_version(ref, swagger_doc) click to toggle source
# File lib/swagalicious/request_factory.rb, line 65
def key_version(ref, swagger_doc)
  if ref.start_with?("#/parameters/")
    puts "Swagalicious: WARNING: #/parameters/ refs are replaced in OpenAPI3! Rename to #/components/parameters/"
    ref.sub("#/parameters/", "").to_sym
  else
    ref.sub("#/components/parameters/", "").to_sym
  end
end
resolve_parameter(ref, swagger_doc) click to toggle source
# File lib/swagalicious/request_factory.rb, line 57
def resolve_parameter(ref, swagger_doc)
  key         = key_version(ref, swagger_doc)
  definitions = definition_version(swagger_doc)
  raise "Referenced parameter '#{ref}' must be defined" unless definitions && definitions[key]

  definitions[key]
end
security_version(scheme_names, swagger_doc) click to toggle source
# File lib/swagalicious/request_factory.rb, line 47
def security_version(scheme_names, swagger_doc)
  if swagger_doc.key?(:securityDefinitions)
    puts "Swagalicious: WARNING: securityDefinitions is replaced in OpenAPI3! Rename to components/securitySchemes (in swagger_helper.rb)"
    swagger_doc[:components] ||= { securitySchemes: swagger_doc[:securityDefinitions] }
    swagger_doc.delete(:securityDefinitions)
  end
  components = swagger_doc[:components] || {}
  (components[:securitySchemes] || {}).slice(*scheme_names).values
end