Skip to content

Commit

Permalink
Create Twitter::Headers class
Browse files Browse the repository at this point in the history
  • Loading branch information
sferik committed Nov 6, 2014
1 parent 65773c7 commit e0d4c36
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 97 deletions.
10 changes: 6 additions & 4 deletions etc/erd.dot
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ digraph classes {
Twitter__Entity__UserMention [label="Twitter::Entity::UserMention"]
Twitter__Error [label="Twitter::Error"]
Twitter__Error__AlreadyFavorited [label="Twitter::Error::AlreadyFavorited"]
Twitter__Error__AlreadyPosted [label="Twitter::Error::AlreadyPosted"]
Twitter__Error__AlreadyRetweeted [label="Twitter::Error::AlreadyRetweeted"]
Twitter__Error__BadGateway [label="Twitter::Error::BadGateway"]
Twitter__Error__BadRequest [label="Twitter::Error::BadRequest"]
Twitter__Error__ClientError [label="Twitter::Error::ClientError"]
Twitter__Error__ConfigurationError [label="Twitter::Error::ConfigurationError"]
Twitter__Error__DuplicateStatus [label="Twitter::Error::DuplicateStatus"]
Twitter__Error__EnhanceYourCalm [label="Twitter::Error::EnhanceYourCalm"]
Twitter__Error__Forbidden [label="Twitter::Error::Forbidden"]
Twitter__Error__GatewayTimeout [label="Twitter::Error::GatewayTimeout"]
Twitter__Error__InternalServerError [label="Twitter::Error::InternalServerError"]
Expand All @@ -37,7 +38,6 @@ digraph classes {
Twitter__Error__RequestTimeout [label="Twitter::Error::RequestTimeout"]
Twitter__Error__ServerError [label="Twitter::Error::ServerError"]
Twitter__Error__ServiceUnavailable [label="Twitter::Error::ServiceUnavailable"]
Twitter__Error__TooManyRequests [label="Twitter::Error::TooManyRequests"]
Twitter__Error__UnacceptableIO [label="Twitter::Error::UnacceptableIO"]
Twitter__Error__Unauthorized [label="Twitter::Error::Unauthorized"]
Twitter__Error__UnprocessableEntity [label="Twitter::Error::UnprocessableEntity"]
Expand All @@ -47,6 +47,7 @@ digraph classes {
Twitter__GeoResults [label="Twitter::GeoResults"]
Twitter__Geo__Point [label="Twitter::Geo::Point"]
Twitter__Geo__Polygon [label="Twitter::Geo::Polygon"]
Twitter__Headers [label="Twitter::Headers"]
Twitter__Identity [label="Twitter::Identity"]
Twitter__Language [label="Twitter::Language"]
Twitter__List [label="Twitter::List"]
Expand Down Expand Up @@ -107,12 +108,13 @@ digraph classes {
Twitter__Entity__UserMention -> Twitter__Entity
Twitter__Error -> StandardError
Twitter__Error__AlreadyFavorited -> Twitter__Error__Forbidden
Twitter__Error__AlreadyPosted -> Twitter__Error__Forbidden
Twitter__Error__AlreadyRetweeted -> Twitter__Error__Forbidden
Twitter__Error__BadGateway -> Twitter__Error__ServerError
Twitter__Error__BadRequest -> Twitter__Error__ClientError
Twitter__Error__ClientError -> Twitter__Error
Twitter__Error__ConfigurationError -> ArgumentError
Twitter__Error__DuplicateStatus -> Twitter__Error__Forbidden
Twitter__Error__EnhanceYourCalm -> Twitter__Error__ClientError
Twitter__Error__Forbidden -> Twitter__Error__ClientError
Twitter__Error__GatewayTimeout -> Twitter__Error__ServerError
Twitter__Error__InternalServerError -> Twitter__Error__ServerError
Expand All @@ -121,7 +123,6 @@ digraph classes {
Twitter__Error__RequestTimeout -> Twitter__Error__ClientError
Twitter__Error__ServerError -> Twitter__Error
Twitter__Error__ServiceUnavailable -> Twitter__Error__ServerError
Twitter__Error__TooManyRequests -> Twitter__Error__ClientError
Twitter__Error__UnacceptableIO -> StandardError
Twitter__Error__Unauthorized -> Twitter__Error__ClientError
Twitter__Error__UnprocessableEntity -> Twitter__Error__ClientError
Expand All @@ -131,6 +132,7 @@ digraph classes {
Twitter__GeoResults -> Object
Twitter__Geo__Point -> Twitter__Geo
Twitter__Geo__Polygon -> Twitter__Geo
Twitter__Headers -> Object
Twitter__Identity -> Twitter__Base
Twitter__Language -> Twitter__Base
Twitter__List -> Twitter__Identity
Expand Down
Binary file modified etc/erd.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 0 additions & 7 deletions lib/twitter/client.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
require 'addressable/uri'
require 'simple_oauth'
require 'twitter/error'
require 'twitter/utils'
require 'twitter/version'
Expand Down Expand Up @@ -52,11 +50,6 @@ def credentials?
credentials.values.all?
end

def oauth_auth_header(method, uri, options = {})
uri = Addressable::URI.parse(uri)
SimpleOAuth::Header.new(method, uri, options, credentials)
end

private

# Ensures that all credentials set during configuration are of a
Expand Down
62 changes: 62 additions & 0 deletions lib/twitter/headers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
require 'addressable/uri'
require 'base64'
require 'simple_oauth'

module Twitter
class Headers
def initialize(client, request_method, url, options = {})
@client = client
@request_method = request_method.to_sym
@uri = Addressable::URI.parse(url)
@options = options
@signature_options = @request_method == :post && @options.values.any? { |value| value.respond_to?(:to_io) } ? {} : @options
end

def oauth_auth_header
SimpleOAuth::Header.new(@request_method, @uri, @signature_options, @client.credentials)
end

def request_headers
bearer_token_request = @options.delete(:bearer_token_request)
headers = {}
if bearer_token_request
headers[:accept] = '*/*'
headers[:authorization] = bearer_token_credentials_auth_header
headers[:content_type] = 'application/x-www-form-urlencoded; charset=UTF-8'
else
headers[:authorization] = auth_header
end
headers
end

private

def auth_header
if @client.user_token?
oauth_auth_header.to_s
else
@client.bearer_token = @client.token unless @client.bearer_token?
bearer_auth_header
end
end

def bearer_auth_header
bearer_token = @client.bearer_token
token = bearer_token.is_a?(Twitter::Token) && bearer_token.bearer? ? bearer_token.access_token : bearer_token
"Bearer #{token}"
end

# Generates authentication header for a bearer token request
#
# @return [String]
def bearer_token_credentials_auth_header
basic_auth_token = strict_encode64("#{@client.consumer_key}:#{@client.consumer_secret}")
"Basic #{basic_auth_token}"
end

# Base64.strict_encode64 is not available on Ruby 1.8.7
def strict_encode64(str)
Base64.encode64(str).gsub("\n", '')
end
end
end
43 changes: 0 additions & 43 deletions lib/twitter/rest/client.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
require 'base64'
require 'faraday'
require 'faraday/request/multipart'
require 'twitter/client'
Expand Down Expand Up @@ -95,48 +94,6 @@ def credentials?
def connection
@connection ||= Faraday.new(URL_PREFIX, connection_options)
end

def request_headers(method, url, options = {}, signature_options = options)
bearer_token_request = options.delete(:bearer_token_request)
headers = {}
if bearer_token_request
headers[:accept] = '*/*'
headers[:authorization] = bearer_token_credentials_auth_header
headers[:content_type] = 'application/x-www-form-urlencoded; charset=UTF-8'
else
headers[:authorization] = auth_header(method, url, options, signature_options)
end
headers
end

private

def auth_header(method, url, options = {}, signature_options = options)
if !user_token?
@bearer_token = token unless bearer_token?
bearer_auth_header
else
oauth_auth_header(method, url, signature_options).to_s
end
end

# Generates authentication header for a bearer token request
#
# @return [String]
def bearer_token_credentials_auth_header
basic_auth_token = strict_encode64("#{@consumer_key}:#{@consumer_secret}")
"Basic #{basic_auth_token}"
end

def bearer_auth_header
token = bearer_token.is_a?(Twitter::Token) && bearer_token.bearer? ? bearer_token.access_token : bearer_token
"Bearer #{token}"
end

# Base64.strict_encode64 is not available on Ruby 1.8.7
def strict_encode64(str)
Base64.encode64(str).gsub("\n", '')
end
end
end
end
3 changes: 2 additions & 1 deletion lib/twitter/rest/media.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'twitter/error'
require 'twitter/headers'
require 'twitter/rest/utils'

module Twitter
Expand All @@ -20,7 +21,7 @@ def upload(media, options = {})
path = '/1.1/media/upload.json'
conn = connection.dup
conn.url_prefix = url_prefix
headers = request_headers(:post, url_prefix + path, options)
headers = Twitter::Headers.new(self, :post, url_prefix + path, options).request_headers
options.merge!(:media => media)
conn.post(path, options) { |request| request.headers.update(headers) }.env.body[:media_id]
end
Expand Down
3 changes: 2 additions & 1 deletion lib/twitter/rest/oauth.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'twitter/headers'
require 'twitter/rest/utils'
require 'twitter/rest/response/parse_error_json'
require 'twitter/token'
Expand Down Expand Up @@ -55,7 +56,7 @@ def reverse_token
conn = connection.dup
conn.builder.swap(4, Twitter::REST::Response::ParseErrorJson)
response = conn.post('/oauth/request_token?x_auth_mode=reverse_auth') do |request|
request.headers[:authorization] = oauth_auth_header(:post, 'https://api.twitter.com/oauth/request_token', :x_auth_mode => 'reverse_auth').to_s
request.headers[:authorization] = Twitter::Headers.new(self, :post, 'https://api.twitter.com/oauth/request_token', :x_auth_mode => 'reverse_auth').oauth_auth_header.to_s
end
response.body
end
Expand Down
26 changes: 11 additions & 15 deletions lib/twitter/rest/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
require 'json'
require 'timeout'
require 'twitter/error'
require 'twitter/headers'
require 'twitter/rate_limit'

module Twitter
module REST
class Request
attr_accessor :client, :rate_limit, :request_method, :path, :options
attr_accessor :client, :headers, :rate_limit, :request_method, :path, :options
alias_method :verb, :request_method
URL_PREFIX = 'https://api.twitter.com'

Expand All @@ -23,24 +24,19 @@ def initialize(client, request_method, path, options = {})
@options = options
end

# @return [Hash]
# @return [Array, Hash]
def perform
signature_options = @request_method == :post && @options.values.any? { |value| value.respond_to?(:to_io) } ? {} : @options
headers = @client.request_headers(@request_method, URL_PREFIX + @path, @options, signature_options)
response = request(@request_method, @path, @options, headers)
@headers = Twitter::Headers.new(@client, @request_method, URL_PREFIX + @path, @options).request_headers
begin
response = @client.connection.send(@request_method, @path, @options) { |request| request.headers.update(@headers) }.env
rescue Faraday::Error::TimeoutError, Timeout::Error => error
raise(Twitter::Error::RequestTimeout.new(error))
rescue Faraday::Error::ClientError, JSON::ParserError => error
raise(Twitter::Error.new(error))
end
@rate_limit = Twitter::RateLimit.new(response.response_headers)
response.body
end

private

def request(method, path, options = {}, headers = {})
@client.connection.send(method.to_sym, path, options) { |request| request.headers.update(headers) }.env
rescue Faraday::Error::TimeoutError, Timeout::Error => error
raise(Twitter::Error::RequestTimeout.new(error))
rescue Faraday::Error::ClientError, JSON::ParserError => error
raise(Twitter::Error.new(error))
end
end
end
end
6 changes: 4 additions & 2 deletions lib/twitter/streaming/client.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
require 'http/request'
require 'twitter/arguments'
require 'twitter/client'
require 'twitter/headers'
require 'twitter/streaming/connection'
require 'twitter/streaming/response'
require 'twitter/streaming/message_parser'
Expand Down Expand Up @@ -106,8 +107,9 @@ def before_request(&block)

def request(method, uri, params)
before_request.call
headers = default_headers.merge(:authorization => oauth_auth_header(method, uri, params).to_s)
request = HTTP::Request.new(method, uri + '?' + to_url_params(params), headers)
authorization = Twitter::Headers.new(self, method, uri, params).oauth_auth_header.to_s
headers = default_headers.merge(:authorization => authorization)
request = HTTP::Request.new(method, uri + '?' + to_url_params(params), headers)
response = Streaming::Response.new do |data|
if item = Streaming::MessageParser.parse(data) # rubocop:disable AssignmentInCondition, IfUnlessModifier
yield(item)
Expand Down
29 changes: 26 additions & 3 deletions spec/twitter/client_spec.rb → spec/twitter/headers_spec.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
require 'helper'

describe Twitter::Client do
describe Twitter::Headers do

before do
@client = Twitter::REST::Client.new(:consumer_key => 'CK', :consumer_secret => 'CS', :access_token => 'AT', :access_token_secret => 'AS')
@headers = Twitter::Headers.new(@client, :get, Twitter::REST::Request::URL_PREFIX + '/path')
end

describe '#oauth_auth_header' do
it 'creates the correct auth headers' do
uri = Twitter::REST::Request::URL_PREFIX + '/path'
authorization = @client.send(:oauth_auth_header, :get, uri)
authorization = @headers.oauth_auth_header
expect(authorization.options[:signature_method]).to eq('HMAC-SHA1')
expect(authorization.options[:version]).to eq('1.0')
expect(authorization.options[:consumer_key]).to eq('CK')
Expand All @@ -36,4 +36,27 @@
expect(a_post('/1.1/statuses/update_with_media.json').with(:headers => {:authorization => headers[:authorization]})).to have_been_made
end
end

describe '#bearer_auth_header' do
it 'creates the correct auth headers with supplied bearer token' do
client = Twitter::REST::Client.new(:bearer_token => 'BT')
headers = Twitter::Headers.new(client, :get, Twitter::REST::Request::URL_PREFIX + '/path')
authorization = headers.send(:bearer_auth_header)
expect(authorization).to eq('Bearer BT')
end
it 'creates the correct auth headers with supplied Twitter::Token' do
token = Twitter::Token.new(:token_type => 'bearer', :access_token => 'BT')
client = Twitter::REST::Client.new(:bearer_token => token)
headers = Twitter::Headers.new(client, :get, Twitter::REST::Request::URL_PREFIX + '/path')
authorization = headers.send(:bearer_auth_header)
expect(authorization).to eq('Bearer BT')
end
end

describe '#bearer_token_credentials_auth_header' do
it 'creates the correct auth header with supplied consumer_key and consumer_secret' do
authorization = @headers.send(:bearer_token_credentials_auth_header)
expect(authorization).to eq('Basic Q0s6Q1M=')
end
end
end
21 changes: 0 additions & 21 deletions spec/twitter/rest/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -165,25 +165,4 @@
expect(c1.object_id).to eq(c2.object_id)
end
end

describe '#bearer_auth_header' do
it 'creates the correct auth headers with supplied bearer_token' do
client = Twitter::REST::Client.new(:bearer_token => 'BT')
authorization = client.send(:bearer_auth_header)
expect(authorization).to eq('Bearer BT')
end
it 'creates the correct auth headers with supplied bearer token' do
token = Twitter::Token.new(:token_type => 'bearer', :access_token => 'BT')
client = Twitter::REST::Client.new(:bearer_token => token)
authorization = client.send(:bearer_auth_header)
expect(authorization).to eq('Bearer BT')
end
end

describe '#bearer_token_credentials_auth_header' do
it 'creates the correct auth header with supplied consumer_key and consumer_secret' do
authorization = @client.send(:bearer_token_credentials_auth_header)
expect(authorization).to eq('Basic Q0s6Q1M=')
end
end
end

0 comments on commit e0d4c36

Please sign in to comment.