faraday_middleware 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|