Skip to content

Fixing curl generation when nil header is passed #107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ PATH
activesupport (>= 3.0.0)
i18n (>= 0.1.0)
json (>= 1.4.6)
multipart-parser
mustache (>= 0.99.4)
rspec (>= 2.14.0)

Expand Down Expand Up @@ -54,6 +55,7 @@ GEM
minitest (4.7.5)
multi_json (1.8.2)
multi_test (0.0.2)
multipart-parser (0.1.1)
mustache (0.99.5)
nokogiri (1.6.0)
mini_portile (~> 0.5.0)
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ RspecApiDocumentation.configure do |config|
# Allows you to filter out headers that are not needed in the cURL request,
# such as "Host" and "Cookie". Set as an array.
config.curl_headers_to_filter = nil

# Allows you to filter out headers with empty values in the generated cURL.
# Set as a boolean.
config.curl_filter_empty_headers = false

# By default examples and resources are ordered by description. Set to true keep
# the source order.
Expand Down
1 change: 1 addition & 0 deletions lib/rspec_api_documentation/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ def self.add_setting(name, opts = {})
}

add_setting :curl_headers_to_filter, :default => nil
add_setting :curl_filter_empty_headers, :default => false
add_setting :curl_host, :default => nil
add_setting :keep_source_order, :default => false
add_setting :api_name, :default => "API Documentation"
Expand Down
86 changes: 71 additions & 15 deletions lib/rspec_api_documentation/curl.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
require 'active_support/core_ext/object/to_query'
require 'multipart_parser/reader'

module RspecApiDocumentation

class Curl < Struct.new(:method, :path, :data, :headers)
attr_accessor :host

def output(config_host, config_headers_to_filer = nil)
def output(config_host, config_headers_to_filter = nil, config_filter_empty_headers = false)
self.host = config_host
@config_headers_to_filer = Array(config_headers_to_filer)
@config_headers_to_filter = config_headers_to_filter
@config_filter_empty_headers = config_filter_empty_headers

append_filters

send(method.downcase)
end

def self.format_header(header)
header.gsub(/^HTTP_/, '').titleize.split.join("-")
end

def post
"curl \"#{url}\" #{post_data} -X POST #{headers}"
end
Expand Down Expand Up @@ -38,8 +48,14 @@ def url
"#{host}#{path}"
end

alias :original_headers :headers

def is_multipart?
original_headers["Content-Type"].try(:match, /\Amultipart\/form-data/)
end

def headers
filter_headers(super).map do |k, v|
filter_headers(super).reject{ |k, v| k.eql?("Content-Type") && v.match(/multipart\/form-data/) }.map do |k, v|
"\\\n\t-H \"#{format_full_header(k, v)}\""
end.join(" ")
end
Expand All @@ -49,28 +65,68 @@ def get_data
end

def post_data
escaped_data = data.to_s.gsub("'", "\\u0027")
"-d '#{escaped_data}'"
if is_multipart?
boundary = MultipartParser::Reader.extract_boundary_value(original_headers["Content-Type"])
reader = MultipartParser::Reader.new(boundary)
flags = []
reader.on_part do |part|
value = ""
unless part.filename.nil?
value = "@#{part.filename};type=#{part.mime}"
else
part.on_data do |data|
value += data
end
end
part.on_end do
flags.push "-F '#{part.name}=#{value.gsub("'", "\\u0027")}'"
end
end
reader.write(data.to_s)
flags.join(" ")
else
escaped_data = data.to_s.gsub("'", "\\u0027")
"-d '#{escaped_data}'"
end
end

private

def format_header(header)
header.gsub(/^HTTP_/, '').titleize.split.join("-")
def append_filters
@filters = Array.new
@filters << ConfiguredHeadersFilter.new(@config_headers_to_filter) if @config_headers_to_filter
@filters << EmptyHeaderFilter.new if @config_filter_empty_headers
end

def format_full_header(header, value)
formatted_value = value.gsub(/"/, "\\\"")
"#{format_header(header)}: #{formatted_value}"
formatted_value = value ? value.gsub(/"/, "\\\"") : ''
"#{Curl.format_header(header)}: #{formatted_value}"
end

def filter_headers(headers)
if !@config_headers_to_filer.empty?
headers.reject do |header|
@config_headers_to_filer.include?(format_header(header))
end
else
headers
@filters.inject(headers) do |headers, filter|
filter.call(headers)
end
end
end

class EmptyHeaderFilter
def call(headers)
headers.reject do |header, value|
value.blank?
end
end
end

class ConfiguredHeadersFilter

def initialize(headers_to_filter)
@headers_to_filter = Array(headers_to_filter)
end

def call(headers)
headers.reject do |header|
@headers_to_filter.include?(Curl.format_header(header))
end
end
end
Expand Down
12 changes: 11 additions & 1 deletion lib/rspec_api_documentation/example.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,17 @@ def explanation
end

def requests
metadata[:requests] || []
reqs = metadata[:requests] || []
reqs.each do |req|
if req[:request_headers]["Content-Type"].try(:match, /\Amultipart\/form-data/)
i = req[:request_body].index /^Content-Disposition: form-data.* filename=\"/
i = req[:request_body].index "\r\n\r\n", i unless i.nil?
unless i.nil?
req[:request_body] = "#{req[:request_body][0..i+3]}...[truncated file data]..."
end
end
end
reqs
end
end
end
3 changes: 2 additions & 1 deletion lib/rspec_api_documentation/writers/json_writer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def initialize(example, configuration)
@example = example
@host = configuration.curl_host
@filter_headers = configuration.curl_headers_to_filter
@filter_empty = configuration.curl_filter_empty_headers
end

def method_missing(method, *args, &block)
Expand Down Expand Up @@ -98,7 +99,7 @@ def requests
super.map do |hash|
if @host
if hash[:curl].is_a? RspecApiDocumentation::Curl
hash[:curl] = hash[:curl].output(@host, @filter_headers)
hash[:curl] = hash[:curl].output(@host, @filter_headers, @filter_empty)
end
else
hash[:curl] = nil
Expand Down
1 change: 1 addition & 0 deletions rspec_api_documentation.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Gem::Specification.new do |s|
s.add_runtime_dependency "i18n", ">= 0.1.0"
s.add_runtime_dependency "mustache", ">= 0.99.4"
s.add_runtime_dependency "json", ">= 1.4.6"
s.add_runtime_dependency "multipart-parser"

s.add_development_dependency "fakefs"
s.add_development_dependency "sinatra"
Expand Down
34 changes: 33 additions & 1 deletion spec/curl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
"HTTP_X_HEADER" => "header",
"HTTP_AUTHORIZATION" => %{Token token="mytoken"},
"HTTP_HOST" => "example.org",
"HTTP_COOKIES" => ""
"HTTP_COOKIES" => "",
"HTTP_SERVER" => nil
}
end

Expand All @@ -26,6 +27,7 @@
it { should =~ /-H "Accept: application\/json"/ }
it { should =~ /-H "X-Header: header"/ }
it { should =~ /-H "Authorization: Token token=\\"mytoken\\""/ }
it { should =~ /-H "Server: "/ }
it { should_not =~ /-H "Host: example\.org"/ }
it { should_not =~ /-H "Cookies: "/ }

Expand Down Expand Up @@ -171,4 +173,34 @@
curl.output(host)
end
end

describe "Filter empty headers" do
subject { curl.output(host, nil, true) }

let(:method) { "POST" }
let(:path) { "/orders" }
let(:data) { "order%5Bsize%5D=large&order%5Btype%5D=cart" }
let(:headers) do
{
"HTTP_ACCEPT" => "application/json",
"HTTP_X_HEADER" => "header",
"HTTP_AUTHORIZATION" => %{Token token="mytoken"},
"HTTP_HOST" => "example.org",
"HTTP_COOKIES" => "",
"HTTP_SERVER" => nil
}
end

it { should =~ /^curl/ }
it { should =~ /http:\/\/example\.com\/orders/ }
it { should =~ /-d 'order%5Bsize%5D=large&order%5Btype%5D=cart'/ }
it { should =~ /-X POST/ }
it { should =~ /-H "Accept: application\/json"/ }
it { should =~ /-H "X-Header: header"/ }
it { should =~ /-H "Authorization: Token token=\\"mytoken\\""/ }
it { should =~ /-H "Host: example\.org"/ }
it { should_not =~ /-H "Server: "/ }
it { should_not =~ /-H "Cookies: "/ }

end
end