From 0d34dbcb491cbddb07f980f154c628e8870c5b61 Mon Sep 17 00:00:00 2001 From: Denis Yagofarov Date: Tue, 18 Sep 2012 21:07:14 +0300 Subject: [PATCH 1/5] Serialize request parameters for GET requests properly, using ActiveSupport and to_query. --- lib/rspec_api_documentation.rb | 1 + lib/rspec_api_documentation/dsl/endpoint.rb | 6 +--- spec/dsl_spec.rb | 35 +++++++++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/lib/rspec_api_documentation.rb b/lib/rspec_api_documentation.rb index 33af972a..12991406 100644 --- a/lib/rspec_api_documentation.rb +++ b/lib/rspec_api_documentation.rb @@ -2,6 +2,7 @@ require 'active_support/inflector' require 'cgi' require 'json' +require 'active_support/core_ext/object/to_query' module RspecApiDocumentation extend ActiveSupport::Autoload diff --git a/lib/rspec_api_documentation/dsl/endpoint.rb b/lib/rspec_api_documentation/dsl/endpoint.rb index 76ba1402..50db4609 100644 --- a/lib/rspec_api_documentation/dsl/endpoint.rb +++ b/lib/rspec_api_documentation/dsl/endpoint.rb @@ -45,11 +45,7 @@ def do_request(extra_params = {}) end def query_string - query = params.to_a.map do |param| - param.map! { |a| CGI.escape(a.to_s) } - param.join("=") - end - query.join("&") + (params || {}).to_query end def params diff --git a/spec/dsl_spec.rb b/spec/dsl_spec.rb index c7a1420c..863b44db 100644 --- a/spec/dsl_spec.rb +++ b/spec/dsl_spec.rb @@ -380,6 +380,41 @@ end end + context "proper query_string serialization" do + get "/orders" do + context "with an array parameter" do + parameter :id_eq, "List of IDs" + + let(:id_eq) { [1, 2] } + + example "parsed properly" do + client.should_receive(:get).with do |path, data, headers| + Addressable::URI.parse(path).query_values.should eq({"id_eq"=>['1', '2']}) + end + do_request + end + end + + context "with a deep nested parameter, including Hashes and Arrays" do + parameter :within_id, "Fancy search condition" + + let(:within_id) { {"first" => 1, "last" => 10, "exclude" => [3,5,7]} } + scope_parameters :search, :all + + example "parsed properly" do + client.should_receive(:get).with do |path, data, headers| + Addressable::URI.parse(path).query_values.should eq({ + "search" => { "within_id" => {"first" => '1', "last" => '10', "exclude" => ['3','5','7']}} + }) + end + do_request + end + end + end + end + + + context "auto request" do post "/orders" do parameter :order_type, "Type of order" From 40603f5377f10edb1b9e8c2d71eddc88864dc02e Mon Sep 17 00:00:00 2001 From: Denis Yagofarov Date: Tue, 18 Sep 2012 22:14:44 +0300 Subject: [PATCH 2/5] Use Rack::Utils for serialization/deserialization of deep query parameters --- lib/rspec_api_documentation.rb | 1 - lib/rspec_api_documentation/dsl/endpoint.rb | 5 ++++- spec/dsl_spec.rb | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/rspec_api_documentation.rb b/lib/rspec_api_documentation.rb index 12991406..33af972a 100644 --- a/lib/rspec_api_documentation.rb +++ b/lib/rspec_api_documentation.rb @@ -2,7 +2,6 @@ require 'active_support/inflector' require 'cgi' require 'json' -require 'active_support/core_ext/object/to_query' module RspecApiDocumentation extend ActiveSupport::Autoload diff --git a/lib/rspec_api_documentation/dsl/endpoint.rb b/lib/rspec_api_documentation/dsl/endpoint.rb index 50db4609..a0a26c0b 100644 --- a/lib/rspec_api_documentation/dsl/endpoint.rb +++ b/lib/rspec_api_documentation/dsl/endpoint.rb @@ -1,8 +1,11 @@ require 'rspec/core/formatters/base_formatter' +require 'rack/utils' +require 'rack/test/utils' module RspecApiDocumentation::DSL module Endpoint extend ActiveSupport::Concern + include Rack::Test::Utils delegate :response_headers, :status, :response_status, :response_body, :to => :client @@ -45,7 +48,7 @@ def do_request(extra_params = {}) end def query_string - (params || {}).to_query + build_nested_query(params || {}) end def params diff --git a/spec/dsl_spec.rb b/spec/dsl_spec.rb index 863b44db..a17e6f51 100644 --- a/spec/dsl_spec.rb +++ b/spec/dsl_spec.rb @@ -389,7 +389,7 @@ example "parsed properly" do client.should_receive(:get).with do |path, data, headers| - Addressable::URI.parse(path).query_values.should eq({"id_eq"=>['1', '2']}) + Rack::Utils.parse_nested_query(path.gsub('/orders?', '')).should eq({"id_eq"=>['1', '2']}) end do_request end @@ -403,7 +403,7 @@ example "parsed properly" do client.should_receive(:get).with do |path, data, headers| - Addressable::URI.parse(path).query_values.should eq({ + Rack::Utils.parse_nested_query(path.gsub('/orders?', '')).should eq({ "search" => { "within_id" => {"first" => '1', "last" => '10', "exclude" => ['3','5','7']}} }) end From 69b62224b9b18ba733fc59741e5a2f7fcaeafbbb Mon Sep 17 00:00:00 2001 From: Denis Yagofarov Date: Wed, 19 Sep 2012 13:54:23 +0300 Subject: [PATCH 3/5] Added a hack to fix OAuth2MacClient and nested/array params serialization --- features/oauth2_mac_client.feature | 67 ++++++++++++++++++- .../oauth2_mac_client.rb | 6 ++ spec/dsl_spec.rb | 1 - 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/features/oauth2_mac_client.feature b/features/oauth2_mac_client.feature index a80da515..9fabc2da 100644 --- a/features/oauth2_mac_client.feature +++ b/features/oauth2_mac_client.feature @@ -31,6 +31,38 @@ Feature: Use OAuth2 MAC client as a test client response.finish end + run app + end + + map "/multiple" do + app = lambda do |env| + if env["HTTP_AUTHORIZATION"].blank? + return [401, {"Content-Type" => "text/plain"}, [""]] + end + + request = Rack::Request.new(env) + response = Rack::Response.new + response["Content-Type"] = "text/plain" + response.write("hello #{request.params["targets"].join(", ")}") + response.finish + end + + run app + end + + map "/multiple_nested" do + app = lambda do |env| + if env["HTTP_AUTHORIZATION"].blank? + return [401, {"Content-Type" => "text/plain"}, [""]] + end + + request = Rack::Request.new(env) + response = Rack::Response.new + response["Content-Type"] = "text/plain" + response.write("hello #{request.params["targets"].map {|company, products| company.to_s + ' with ' + products.join(' and ')}.join(", ")}") + response.finish + end + run app end end @@ -50,6 +82,35 @@ Feature: Use OAuth2 MAC client as a test client response_body.should eq('hello rspec_api_documentation') end end + + get "/multiple" do + parameter :targets, "The people you want to greet" + + let(:targets) { ["eric", "sam"] } + + example "Greeting your favorite people" do + do_request + + response_headers["Content-Type"].should eq("text/plain") + status.should eq(200) + response_body.should eq("hello eric, sam") + end + end + + get "/multiple_nested" do + parameter :targets, "The companies you want to greet" + + let(:targets) { { "apple" => ['mac', 'ios'], "google" => ['search', 'mail']} } + + example "Greeting your favorite companies" do + do_request + + response_headers["Content-Type"].should eq("text/plain") + status.should eq(200) + response_body.should eq("hello apple with mac and ios, google with search and mail") + end + end + end """ When I run `rspec app_spec.rb --format RspecApiDocumentation::ApiFormatter` @@ -61,6 +122,10 @@ Feature: Use OAuth2 MAC client as a test client Greetings GET / * Greeting your favorite gem + GET /multiple + * Greeting your favorite people + GET /multiple_nested + * Greeting your favorite companies """ - And the output should contain "1 example, 0 failures" + And the output should contain "3 examples, 0 failures" And the exit status should be 0 diff --git a/lib/rspec_api_documentation/oauth2_mac_client.rb b/lib/rspec_api_documentation/oauth2_mac_client.rb index 712afb4d..c799f0c6 100644 --- a/lib/rspec_api_documentation/oauth2_mac_client.rb +++ b/lib/rspec_api_documentation/oauth2_mac_client.rb @@ -48,9 +48,15 @@ def do_request(method, path, params, request_headers) class ProxyApp < Struct.new(:client, :app) def call(env) + env["QUERY_STRING"] = query_string_hack(env) client.last_request = Struct.new(:env, :content_type).new(env, env["CONTENT_TYPE"]) app.call(env.merge("SCRIPT_NAME" => "")) end + + private + def query_string_hack(env) + env["QUERY_STRING"].gsub('%5B', '[').gsub('%5D', ']').gsub(/\[\d+/) { |s| "#{$1}[" } + end end def access_token diff --git a/spec/dsl_spec.rb b/spec/dsl_spec.rb index a17e6f51..fa69e314 100644 --- a/spec/dsl_spec.rb +++ b/spec/dsl_spec.rb @@ -246,7 +246,6 @@ trigger_callback do uri = URI.parse(callback_url) Net::HTTP.start(uri.host, uri.port) do |http| - # debugger http.request Net::HTTP::Post.new(uri.path) end end From 378f3ad7bd67e0a078a38341444d727645015b83 Mon Sep 17 00:00:00 2001 From: Denis Yagofarov Date: Wed, 19 Sep 2012 14:07:24 +0300 Subject: [PATCH 4/5] Raised minimum requirement for WebMock from 1.8.0 to 1.8.8 --- gemfiles/minimum_dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gemfiles/minimum_dependencies b/gemfiles/minimum_dependencies index 82c901e7..4574b05c 100644 --- a/gemfiles/minimum_dependencies +++ b/gemfiles/minimum_dependencies @@ -6,7 +6,7 @@ gem "rspec", "2.6.0" gem "activesupport", "3.0.0" gem "i18n", "0.4.0" gem "mustache", "0.99.4" -gem "webmock", "1.8.0" +gem "webmock", "1.8.8" gem "json", "1.4.6" gem "rack-test", "0.5.5" gem "rack-oauth2", "0.14.4" From 4c12172bda5e3c52d583b5d0c48930e01a631ce5 Mon Sep 17 00:00:00 2001 From: Denis Yagofarov Date: Wed, 19 Sep 2012 14:12:45 +0300 Subject: [PATCH 5/5] Fixed sorting in OAuth2 Mac Client feature for ruby 1.8.7 --- features/oauth2_mac_client.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/oauth2_mac_client.feature b/features/oauth2_mac_client.feature index 9fabc2da..e528a109 100644 --- a/features/oauth2_mac_client.feature +++ b/features/oauth2_mac_client.feature @@ -59,7 +59,7 @@ Feature: Use OAuth2 MAC client as a test client request = Rack::Request.new(env) response = Rack::Response.new response["Content-Type"] = "text/plain" - response.write("hello #{request.params["targets"].map {|company, products| company.to_s + ' with ' + products.join(' and ')}.join(", ")}") + response.write("hello #{request.params["targets"].sort.map {|company, products| company.to_s + ' with ' + products.join(' and ')}.join(", ")}") response.finish end