faraday_middleware 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +0 -1
- data/.travis.yml +1 -2
- data/CHANGELOG.md +9 -9
- data/Gemfile +3 -3
- data/README.md +2 -2
- data/Rakefile +9 -2
- data/faraday_middleware.gemspec +16 -24
- data/lib/faraday_middleware.rb +51 -11
- data/lib/faraday_middleware/addressable_patch.rb +20 -0
- data/lib/faraday_middleware/backwards_compatibility.rb +30 -0
- data/lib/faraday_middleware/instrumentation.rb +30 -0
- data/lib/faraday_middleware/rack_compatible.rb +76 -0
- data/lib/faraday_middleware/request/encode_json.rb +50 -0
- data/lib/faraday_middleware/request/oauth.rb +61 -0
- data/lib/faraday_middleware/request/oauth2.rb +60 -0
- data/lib/faraday_middleware/response/caching.rb +76 -0
- data/lib/faraday_middleware/response/follow_redirects.rb +53 -0
- data/lib/{faraday → faraday_middleware}/response/mashify.rb +2 -2
- data/lib/faraday_middleware/response/parse_json.rb +35 -0
- data/lib/faraday_middleware/response/parse_marshal.rb +10 -0
- data/lib/faraday_middleware/response/parse_xml.rb +11 -0
- data/lib/faraday_middleware/response/parse_yaml.rb +10 -0
- data/lib/faraday_middleware/response/rashify.rb +9 -0
- data/lib/faraday_middleware/response_middleware.rb +78 -0
- data/lib/faraday_middleware/version.rb +1 -1
- data/spec/caching_test.rb +122 -0
- data/spec/encode_json_spec.rb +95 -0
- data/spec/follow_redirects_spec.rb +33 -0
- data/spec/helper.rb +27 -12
- data/spec/mashify_spec.rb +8 -7
- data/spec/oauth2_spec.rb +100 -32
- data/spec/oauth_spec.rb +83 -28
- data/spec/parse_json_spec.rb +71 -46
- data/spec/parse_marshal_spec.rb +9 -26
- data/spec/parse_xml_spec.rb +56 -24
- data/spec/parse_yaml_spec.rb +40 -20
- data/spec/rashify_spec.rb +4 -3
- metadata +59 -57
- data/lib/faraday/request/oauth.rb +0 -23
- data/lib/faraday/request/oauth2.rb +0 -24
- data/lib/faraday/response/parse_json.rb +0 -20
- data/lib/faraday/response/parse_marshal.rb +0 -10
- data/lib/faraday/response/parse_xml.rb +0 -11
- data/lib/faraday/response/parse_yaml.rb +0 -11
- data/lib/faraday/response/rashify.rb +0 -19
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'faraday_middleware/request/encode_json'
|
3
|
+
|
4
|
+
describe FaradayMiddleware::EncodeJson do
|
5
|
+
let(:middleware) { described_class.new(lambda{|env| env}) }
|
6
|
+
|
7
|
+
def process(body, content_type = nil)
|
8
|
+
env = {:body => body, :request_headers => Faraday::Utils::Headers.new}
|
9
|
+
env[:request_headers]['content-type'] = content_type if content_type
|
10
|
+
middleware.call(env)
|
11
|
+
end
|
12
|
+
|
13
|
+
def result_body() result[:body] end
|
14
|
+
def result_type() result[:request_headers]['content-type'] end
|
15
|
+
|
16
|
+
context "no body" do
|
17
|
+
let(:result) { process(nil) }
|
18
|
+
|
19
|
+
it "doesn't change body" do
|
20
|
+
result_body.should be_nil
|
21
|
+
end
|
22
|
+
|
23
|
+
it "doesn't add content type" do
|
24
|
+
result_type.should be_nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "empty body" do
|
29
|
+
let(:result) { process('') }
|
30
|
+
|
31
|
+
it "doesn't change body" do
|
32
|
+
result_body.should be_empty
|
33
|
+
end
|
34
|
+
|
35
|
+
it "doesn't add content type" do
|
36
|
+
result_type.should be_nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "string body" do
|
41
|
+
let(:result) { process('{"a":1}') }
|
42
|
+
|
43
|
+
it "doesn't change body" do
|
44
|
+
result_body.should eql('{"a":1}')
|
45
|
+
end
|
46
|
+
|
47
|
+
it "adds content type" do
|
48
|
+
result_type.should eql('application/json')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "object body" do
|
53
|
+
let(:result) { process({:a => 1}) }
|
54
|
+
|
55
|
+
it "encodes body" do
|
56
|
+
result_body.should eql('{"a":1}')
|
57
|
+
end
|
58
|
+
|
59
|
+
it "adds content type" do
|
60
|
+
result_type.should eql('application/json')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "empty object body" do
|
65
|
+
let(:result) { process({}) }
|
66
|
+
|
67
|
+
it "encodes body" do
|
68
|
+
result_body.should eql('{}')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "object body with json type" do
|
73
|
+
let(:result) { process({:a => 1}, 'application/json; charset=utf-8') }
|
74
|
+
|
75
|
+
it "encodes body" do
|
76
|
+
result_body.should eql('{"a":1}')
|
77
|
+
end
|
78
|
+
|
79
|
+
it "doesn't change content type" do
|
80
|
+
result_type.should eql('application/json; charset=utf-8')
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "object body with incompatible type" do
|
85
|
+
let(:result) { process({:a => 1}, 'application/xml; charset=utf-8') }
|
86
|
+
|
87
|
+
it "doesn't change body" do
|
88
|
+
result_body.should eql({:a => 1})
|
89
|
+
end
|
90
|
+
|
91
|
+
it "doesn't change content type" do
|
92
|
+
result_type.should eql('application/xml; charset=utf-8')
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'helper'
|
2
|
+
require 'faraday_middleware/response/follow_redirects'
|
3
|
+
require 'faraday'
|
4
|
+
require 'forwardable'
|
5
|
+
|
6
|
+
describe FaradayMiddleware::FollowRedirects do
|
7
|
+
let(:connection) {
|
8
|
+
Faraday.new do |c|
|
9
|
+
c.use described_class
|
10
|
+
c.adapter :test do |stub|
|
11
|
+
stub.get('/') { [301, {'Location' => '/found'}, ''] }
|
12
|
+
stub.post('/create') { [302, {'Location' => '/'}, ''] }
|
13
|
+
stub.get('/found') { [200, {'Content-Type' => 'text/plain'}, 'fin'] }
|
14
|
+
stub.get('/loop') { [302, {'Location' => '/loop'}, ''] }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
}
|
18
|
+
|
19
|
+
extend Forwardable
|
20
|
+
def_delegators :connection, :get, :post
|
21
|
+
|
22
|
+
it "follows redirect" do
|
23
|
+
get('/').body.should eql('fin')
|
24
|
+
end
|
25
|
+
|
26
|
+
it "follows redirect twice" do
|
27
|
+
post('/create').body.should eql('fin')
|
28
|
+
end
|
29
|
+
|
30
|
+
it "raises exception on loop" do
|
31
|
+
expect { get('/loop') }.to raise_error(FaradayMiddleware::RedirectLimitReached)
|
32
|
+
end
|
33
|
+
end
|
data/spec/helper.rb
CHANGED
@@ -1,18 +1,33 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
if ENV['COVERAGE']
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start do
|
4
|
+
# add_filter 'faraday_middleware.rb'
|
5
|
+
add_filter 'backwards_compatibility.rb'
|
6
|
+
end
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
attr_accessor :env
|
9
|
+
require 'rspec'
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
module ResponseMiddlewareExampleGroup
|
12
|
+
def self.included(base)
|
13
|
+
base.let(:options) { Hash.new }
|
14
|
+
base.let(:middleware) {
|
15
|
+
described_class.new(lambda {|env|
|
16
|
+
Faraday::Response.new(env)
|
17
|
+
}, options)
|
18
|
+
}
|
13
19
|
end
|
14
20
|
|
15
|
-
def
|
16
|
-
|
21
|
+
def process(body, content_type = nil, options = {})
|
22
|
+
env = {
|
23
|
+
:body => body, :request => options,
|
24
|
+
:response_headers => Faraday::Utils::Headers.new
|
25
|
+
}
|
26
|
+
env[:response_headers]['content-type'] = content_type if content_type
|
27
|
+
middleware.call(env)
|
17
28
|
end
|
18
29
|
end
|
30
|
+
|
31
|
+
RSpec.configure do |config|
|
32
|
+
config.include ResponseMiddlewareExampleGroup, :type => :response
|
33
|
+
end
|
data/spec/mashify_spec.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
require 'helper'
|
2
|
+
require 'faraday_middleware/response/mashify'
|
2
3
|
|
3
|
-
describe
|
4
|
+
describe FaradayMiddleware::Mashify do
|
4
5
|
context 'during configuration' do
|
5
6
|
it 'should allow for a custom Mash class to be set' do
|
6
|
-
|
7
|
-
|
7
|
+
described_class.should respond_to(:mash_class)
|
8
|
+
described_class.should respond_to(:mash_class=)
|
8
9
|
end
|
9
10
|
end
|
10
11
|
|
11
12
|
context 'when used' do
|
12
|
-
before(:each) {
|
13
|
-
let(:mashify) {
|
13
|
+
before(:each) { described_class.mash_class = ::Hashie::Mash }
|
14
|
+
let(:mashify) { described_class.new }
|
14
15
|
|
15
16
|
it 'should create a Hashie::Mash from the body' do
|
16
17
|
env = { :body => { "name" => "Erik Michaels-Ober", "username" => "sferik" } }
|
@@ -48,7 +49,7 @@ describe Faraday::Response::Mashify do
|
|
48
49
|
|
49
50
|
it 'should allow for use of custom Mash subclasses' do
|
50
51
|
class MyMash < ::Hashie::Mash; end
|
51
|
-
|
52
|
+
described_class.mash_class = MyMash
|
52
53
|
|
53
54
|
env = { :body => { "name" => "Erik Michaels-Ober", "username" => "sferik" } }
|
54
55
|
me = mashify.on_complete(env)
|
@@ -62,7 +63,7 @@ describe Faraday::Response::Mashify do
|
|
62
63
|
let(:connection) do
|
63
64
|
Faraday::Connection.new do |builder|
|
64
65
|
builder.adapter :test, stubs
|
65
|
-
builder.use
|
66
|
+
builder.use described_class
|
66
67
|
end
|
67
68
|
end
|
68
69
|
|
data/spec/oauth2_spec.rb
CHANGED
@@ -1,50 +1,118 @@
|
|
1
1
|
require 'helper'
|
2
|
+
require 'uri'
|
3
|
+
require 'faraday_middleware/request/oauth2'
|
4
|
+
require 'faraday/utils'
|
2
5
|
|
3
|
-
describe
|
6
|
+
describe FaradayMiddleware::OAuth2 do
|
4
7
|
|
5
|
-
|
6
|
-
|
8
|
+
def query_params(env)
|
9
|
+
Faraday::Utils.parse_query env[:url].query
|
10
|
+
end
|
11
|
+
|
12
|
+
def auth_header(env)
|
13
|
+
env[:request_headers]['Authorization']
|
14
|
+
end
|
15
|
+
|
16
|
+
def perform(params = {}, headers = {})
|
17
|
+
env = {
|
18
|
+
:url => URI('http://example.com/?' + Faraday::Utils.build_query(params)),
|
19
|
+
:request_headers => Faraday::Utils::Headers.new.update(headers)
|
20
|
+
}
|
21
|
+
app = make_app
|
22
|
+
app.call(env)
|
23
|
+
end
|
7
24
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
25
|
+
def make_app
|
26
|
+
described_class.new(lambda{|env| env}, *Array(options))
|
27
|
+
end
|
28
|
+
|
29
|
+
context "no token configured" do
|
30
|
+
let(:options) { nil }
|
31
|
+
|
32
|
+
it "doesn't add params" do
|
33
|
+
request = perform(:q => 'hello')
|
34
|
+
query_params(request).should eq('q' => 'hello')
|
35
|
+
end
|
13
36
|
|
14
|
-
|
15
|
-
|
16
|
-
|
37
|
+
it "doesn't add headers" do
|
38
|
+
auth_header(perform).should be_nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "creates header for explicit token" do
|
42
|
+
request = perform(:q => 'hello', :access_token => 'abc123')
|
43
|
+
query_params(request).should eq('q' => 'hello', 'access_token' => 'abc123')
|
44
|
+
auth_header(request).should eq(%(Token token="abc123"))
|
17
45
|
end
|
18
46
|
end
|
19
47
|
|
20
|
-
context
|
21
|
-
let(:
|
48
|
+
context "default token configured" do
|
49
|
+
let(:options) { 'XYZ' }
|
50
|
+
|
51
|
+
it "adds token param" do
|
52
|
+
query_params(perform(:q => 'hello')).should eq('q' => 'hello', 'access_token' => 'XYZ')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "adds token header" do
|
56
|
+
auth_header(perform).should eq(%(Token token="XYZ"))
|
57
|
+
end
|
22
58
|
|
23
|
-
it
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
59
|
+
it "overrides default with explicit token" do
|
60
|
+
request = perform(:q => 'hello', :access_token => 'abc123')
|
61
|
+
query_params(request).should eq('q' => 'hello', 'access_token' => 'abc123')
|
62
|
+
auth_header(request).should eq(%(Token token="abc123"))
|
63
|
+
end
|
28
64
|
|
29
|
-
|
30
|
-
request
|
31
|
-
request
|
65
|
+
it "clears default with empty explicit token" do
|
66
|
+
request = perform(:q => 'hello', :access_token => nil)
|
67
|
+
query_params(request).should eq('q' => 'hello', 'access_token' => nil)
|
68
|
+
auth_header(request).should be_nil
|
32
69
|
end
|
33
70
|
end
|
34
71
|
|
35
|
-
context
|
36
|
-
let(:
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
72
|
+
context "existing Authorization header" do
|
73
|
+
let(:options) { 'XYZ' }
|
74
|
+
subject { perform({:q => 'hello'}, 'Authorization' => 'custom') }
|
75
|
+
|
76
|
+
it "adds token param" do
|
77
|
+
query_params(subject).should eq('q' => 'hello', 'access_token' => 'XYZ')
|
42
78
|
end
|
43
79
|
|
44
|
-
it '
|
45
|
-
|
46
|
-
|
47
|
-
|
80
|
+
it "doesn't override existing header" do
|
81
|
+
auth_header(subject).should eq('custom')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "custom param name configured" do
|
86
|
+
let(:options) { ['XYZ', {:param_name => :oauth}] }
|
87
|
+
|
88
|
+
it "adds token param" do
|
89
|
+
query_params(perform).should eq('oauth' => 'XYZ')
|
90
|
+
end
|
91
|
+
|
92
|
+
it "overrides default with explicit token" do
|
93
|
+
request = perform(:oauth => 'abc123')
|
94
|
+
query_params(request).should eq('oauth' => 'abc123')
|
95
|
+
auth_header(request).should eq(%(Token token="abc123"))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context "options without token configuration" do
|
100
|
+
let(:options) { [{:param_name => :oauth}] }
|
101
|
+
|
102
|
+
it "doesn't add param" do
|
103
|
+
query_params(perform).should be_empty
|
104
|
+
end
|
105
|
+
|
106
|
+
it "overrides default with explicit token" do
|
107
|
+
query_params(perform(:oauth => 'abc123')).should eq('oauth' => 'abc123')
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "invalid param name configured" do
|
112
|
+
let(:options) { ['XYZ', {:param_name => nil}] }
|
113
|
+
|
114
|
+
it "raises error" do
|
115
|
+
expect { make_app }.to raise_error(ArgumentError, ":param_name can't be blank")
|
48
116
|
end
|
49
117
|
end
|
50
118
|
end
|
data/spec/oauth_spec.rb
CHANGED
@@ -1,46 +1,101 @@
|
|
1
1
|
require 'helper'
|
2
|
+
require 'faraday_middleware/request/oauth'
|
3
|
+
require 'uri'
|
2
4
|
|
3
|
-
describe
|
4
|
-
|
5
|
+
describe FaradayMiddleware::OAuth do
|
6
|
+
def auth_header(env)
|
7
|
+
env[:request_headers]['Authorization']
|
8
|
+
end
|
9
|
+
|
10
|
+
def auth_values(env)
|
11
|
+
if auth = auth_header(env)
|
12
|
+
raise "invalid header: #{auth.inspect}" unless auth.sub!('OAuth ', '')
|
13
|
+
Hash[*auth.split(/, |=/)]
|
14
|
+
end
|
15
|
+
end
|
5
16
|
|
6
|
-
|
7
|
-
{
|
8
|
-
:
|
9
|
-
:
|
10
|
-
:
|
11
|
-
:token_secret => '1234'
|
17
|
+
def perform(oauth_options = {}, headers = {})
|
18
|
+
env = {
|
19
|
+
:url => URI('http://example.com/'),
|
20
|
+
:request_headers => Faraday::Utils::Headers.new.update(headers),
|
21
|
+
:request => {}
|
12
22
|
}
|
23
|
+
unless oauth_options.is_a? Hash and oauth_options.empty?
|
24
|
+
env[:request][:oauth] = oauth_options
|
25
|
+
end
|
26
|
+
app = make_app
|
27
|
+
app.call(env)
|
13
28
|
end
|
14
29
|
|
15
|
-
|
16
|
-
|
30
|
+
def make_app
|
31
|
+
described_class.new(lambda{|env| env}, *Array(options))
|
32
|
+
end
|
17
33
|
|
18
|
-
|
19
|
-
|
34
|
+
context "invalid options" do
|
35
|
+
let(:options) { nil }
|
36
|
+
|
37
|
+
it "should error out" do
|
38
|
+
expect { make_app }.to raise_error(ArgumentError)
|
20
39
|
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "empty options" do
|
43
|
+
let(:options) { [{}] }
|
21
44
|
|
22
|
-
it
|
23
|
-
|
24
|
-
|
45
|
+
it "should sign request" do
|
46
|
+
auth = auth_values(perform)
|
47
|
+
expected_keys = %w[ oauth_nonce
|
48
|
+
oauth_signature oauth_signature_method
|
49
|
+
oauth_timestamp oauth_version ]
|
50
|
+
|
51
|
+
auth.keys.should =~ expected_keys
|
25
52
|
end
|
26
53
|
end
|
27
54
|
|
55
|
+
context "configured with consumer and token" do
|
56
|
+
let(:options) do
|
57
|
+
[{ :consumer_key => 'CKEY', :consumer_secret => 'CSECRET',
|
58
|
+
:token => 'TOKEN', :token_secret => 'TSECRET'
|
59
|
+
}]
|
60
|
+
end
|
28
61
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
62
|
+
it "adds auth info to the header" do
|
63
|
+
auth = auth_values(perform)
|
64
|
+
expected_keys = %w[ oauth_consumer_key oauth_nonce
|
65
|
+
oauth_signature oauth_signature_method
|
66
|
+
oauth_timestamp oauth_token oauth_version ]
|
67
|
+
|
68
|
+
auth.keys.should =~ expected_keys
|
69
|
+
auth['oauth_version'].should eq(%("1.0"))
|
70
|
+
auth['oauth_signature_method'].should eq(%("HMAC-SHA1"))
|
71
|
+
auth['oauth_consumer_key'].should eq(%("CKEY"))
|
72
|
+
auth['oauth_token'].should eq(%("TOKEN"))
|
73
|
+
end
|
74
|
+
|
75
|
+
it "doesn't override existing header" do
|
76
|
+
request = perform({}, "Authorization" => "iz me!")
|
77
|
+
auth_header(request).should eq("iz me!")
|
78
|
+
end
|
79
|
+
|
80
|
+
it "can override oauth options per-request" do
|
81
|
+
auth = auth_values(perform(:consumer_key => 'CKEY2'))
|
82
|
+
|
83
|
+
auth['oauth_consumer_key'].should eq(%("CKEY2"))
|
84
|
+
auth['oauth_token'].should eq(%("TOKEN"))
|
85
|
+
end
|
86
|
+
|
87
|
+
it "can turn off oauth signing per-request" do
|
88
|
+
auth_header(perform(false)).should be_nil
|
36
89
|
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "configured without token" do
|
93
|
+
let(:options) { [{ :consumer_key => 'CKEY', :consumer_secret => 'CSECRET' }] }
|
37
94
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
me = connection.get('http://www.github.com/me')
|
43
|
-
me.body.should == 'sferik'
|
95
|
+
it "adds auth info to the header" do
|
96
|
+
auth = auth_values(perform)
|
97
|
+
auth.should include('oauth_consumer_key')
|
98
|
+
auth.should_not include('oauth_token')
|
44
99
|
end
|
45
100
|
end
|
46
101
|
end
|