extendi-instagram 2.0.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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/.yardopts +9 -0
- data/Gemfile +3 -0
- data/LICENSE.md +30 -0
- data/PATENTS.md +23 -0
- data/README.md +260 -0
- data/Rakefile +27 -0
- data/instagram.gemspec +50 -0
- data/lib/faraday/loud_logger.rb +78 -0
- data/lib/faraday/oauth2.rb +45 -0
- data/lib/faraday/raise_http_exception.rb +73 -0
- data/lib/instagram/api.rb +31 -0
- data/lib/instagram/client/comments.rb +62 -0
- data/lib/instagram/client/embedding.rb +28 -0
- data/lib/instagram/client/geographies.rb +29 -0
- data/lib/instagram/client/likes.rb +58 -0
- data/lib/instagram/client/locations.rb +75 -0
- data/lib/instagram/client/media.rb +82 -0
- data/lib/instagram/client/subscriptions.rb +211 -0
- data/lib/instagram/client/tags.rb +59 -0
- data/lib/instagram/client/users.rb +310 -0
- data/lib/instagram/client/utils.rb +28 -0
- data/lib/instagram/client.rb +21 -0
- data/lib/instagram/configuration.rb +125 -0
- data/lib/instagram/connection.rb +31 -0
- data/lib/instagram/error.rb +34 -0
- data/lib/instagram/oauth.rb +36 -0
- data/lib/instagram/request.rb +83 -0
- data/lib/instagram/response.rb +22 -0
- data/lib/instagram/version.rb +3 -0
- data/lib/instagram.rb +27 -0
- data/spec/faraday/response_spec.rb +101 -0
- data/spec/fixtures/access_token.json +9 -0
- data/spec/fixtures/approve_user.json +8 -0
- data/spec/fixtures/block_user.json +8 -0
- data/spec/fixtures/deny_user.json +8 -0
- data/spec/fixtures/follow_user.json +8 -0
- data/spec/fixtures/followed_by.json +1 -0
- data/spec/fixtures/follows.json +1 -0
- data/spec/fixtures/geography_recent_media.json +1 -0
- data/spec/fixtures/liked_media.json +1 -0
- data/spec/fixtures/location.json +1 -0
- data/spec/fixtures/location_recent_media.json +1 -0
- data/spec/fixtures/location_search.json +1 -0
- data/spec/fixtures/location_search_facebook.json +1 -0
- data/spec/fixtures/media.json +1 -0
- data/spec/fixtures/media_comment.json +1 -0
- data/spec/fixtures/media_comment_deleted.json +1 -0
- data/spec/fixtures/media_comments.json +1 -0
- data/spec/fixtures/media_liked.json +1 -0
- data/spec/fixtures/media_likes.json +1 -0
- data/spec/fixtures/media_popular.json +1 -0
- data/spec/fixtures/media_search.json +1 -0
- data/spec/fixtures/media_shortcode.json +1 -0
- data/spec/fixtures/media_unliked.json +1 -0
- data/spec/fixtures/mikeyk.json +1 -0
- data/spec/fixtures/oembed.json +14 -0
- data/spec/fixtures/recent_media.json +1 -0
- data/spec/fixtures/relationship.json +9 -0
- data/spec/fixtures/requested_by.json +12 -0
- data/spec/fixtures/shayne.json +1 -0
- data/spec/fixtures/subscription.json +12 -0
- data/spec/fixtures/subscription_deleted.json +1 -0
- data/spec/fixtures/subscription_payload.json +14 -0
- data/spec/fixtures/subscriptions.json +22 -0
- data/spec/fixtures/tag.json +1 -0
- data/spec/fixtures/tag_recent_media.json +1 -0
- data/spec/fixtures/tag_search.json +1 -0
- data/spec/fixtures/unblock_user.json +8 -0
- data/spec/fixtures/unfollow_user.json +8 -0
- data/spec/fixtures/user_media_feed.json +1 -0
- data/spec/fixtures/user_search.json +1 -0
- data/spec/instagram/api_spec.rb +285 -0
- data/spec/instagram/client/comments_spec.rb +71 -0
- data/spec/instagram/client/embedding_spec.rb +36 -0
- data/spec/instagram/client/geography_spec.rb +37 -0
- data/spec/instagram/client/likes_spec.rb +66 -0
- data/spec/instagram/client/locations_spec.rb +127 -0
- data/spec/instagram/client/media_spec.rb +99 -0
- data/spec/instagram/client/subscriptions_spec.rb +174 -0
- data/spec/instagram/client/tags_spec.rb +79 -0
- data/spec/instagram/client/users_spec.rb +432 -0
- data/spec/instagram/client/utils_spec.rb +32 -0
- data/spec/instagram/client_spec.rb +23 -0
- data/spec/instagram/request_spec.rb +56 -0
- data/spec/instagram_spec.rb +109 -0
- data/spec/spec_helper.rb +71 -0
- metadata +322 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
module Instagram
|
2
|
+
class Client
|
3
|
+
# @private
|
4
|
+
module Utils
|
5
|
+
# Returns the raw full response including all headers. Can be used to access the values for 'X-Ratelimit-Limit' and 'X-Ratelimit-Remaining'
|
6
|
+
# ==== Examples
|
7
|
+
#
|
8
|
+
# client = Instagram.client(:access_token => session[:access_token])
|
9
|
+
# response = client.utils_raw_response
|
10
|
+
# remaining = response.headers[:x_ratelimit_remaining]
|
11
|
+
# limit = response.headers[:x_ratelimit_limit]
|
12
|
+
#
|
13
|
+
def utils_raw_response
|
14
|
+
response = get('users/self/feed',nil, false, true)
|
15
|
+
response
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# Returns the configured user name or the user name of the authenticated user
|
21
|
+
#
|
22
|
+
# @return [String]
|
23
|
+
def get_username
|
24
|
+
@user_name ||= self.user.username
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Instagram
|
2
|
+
# Wrapper for the Instagram REST API
|
3
|
+
#
|
4
|
+
# @note All methods have been separated into modules and follow the same grouping used in http://instagram.com/developer/
|
5
|
+
# @see http://instagram.com/developer/
|
6
|
+
class Client < API
|
7
|
+
Dir[File.expand_path('../client/*.rb', __FILE__)].each{|f| require f}
|
8
|
+
|
9
|
+
include Instagram::Client::Utils
|
10
|
+
|
11
|
+
include Instagram::Client::Users
|
12
|
+
include Instagram::Client::Media
|
13
|
+
include Instagram::Client::Locations
|
14
|
+
include Instagram::Client::Geographies
|
15
|
+
include Instagram::Client::Tags
|
16
|
+
include Instagram::Client::Comments
|
17
|
+
include Instagram::Client::Likes
|
18
|
+
include Instagram::Client::Subscriptions
|
19
|
+
include Instagram::Client::Embedding
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
#if using typhoeus as the adapter uncomment these two requires to avoid seeing "Ethon::Errors::InvalidOption: The option: disable_ssl_peer_verification is invalid." (https://github.com/typhoeus/typhoeus/issues/270)
|
3
|
+
#require 'typhoeus'
|
4
|
+
#require 'typhoeus/adapters/faraday'
|
5
|
+
require File.expand_path('../version', __FILE__)
|
6
|
+
|
7
|
+
module Instagram
|
8
|
+
# Defines constants and methods related to configuration
|
9
|
+
module Configuration
|
10
|
+
# An array of valid keys in the options hash when configuring a {Instagram::API}
|
11
|
+
VALID_OPTIONS_KEYS = [
|
12
|
+
:access_token,
|
13
|
+
:adapter,
|
14
|
+
:client_id,
|
15
|
+
:client_secret,
|
16
|
+
:client_ips,
|
17
|
+
:connection_options,
|
18
|
+
:scope,
|
19
|
+
:redirect_uri,
|
20
|
+
:endpoint,
|
21
|
+
:format,
|
22
|
+
:proxy,
|
23
|
+
:user_agent,
|
24
|
+
:no_response_wrapper,
|
25
|
+
:loud_logger,
|
26
|
+
:sign_requests,
|
27
|
+
].freeze
|
28
|
+
|
29
|
+
# By default, don't set a user access token
|
30
|
+
DEFAULT_ACCESS_TOKEN = nil
|
31
|
+
|
32
|
+
# The adapter that will be used to connect if none is set
|
33
|
+
#
|
34
|
+
# @note The default faraday adapter is Net::HTTP.
|
35
|
+
DEFAULT_ADAPTER = Faraday.default_adapter
|
36
|
+
|
37
|
+
# By default, don't set an application ID
|
38
|
+
DEFAULT_CLIENT_ID = nil
|
39
|
+
|
40
|
+
# By default, don't set an application secret
|
41
|
+
DEFAULT_CLIENT_SECRET = nil
|
42
|
+
|
43
|
+
# By default, don't set application IPs
|
44
|
+
DEFAULT_CLIENT_IPS = nil
|
45
|
+
|
46
|
+
# By default, don't set any connection options
|
47
|
+
DEFAULT_CONNECTION_OPTIONS = {}
|
48
|
+
|
49
|
+
# The endpoint that will be used to connect if none is set
|
50
|
+
#
|
51
|
+
# @note There is no reason to use any other endpoint at this time
|
52
|
+
DEFAULT_ENDPOINT = 'https://api.instagram.com/v1/'.freeze
|
53
|
+
|
54
|
+
# The response format appended to the path and sent in the 'Accept' header if none is set
|
55
|
+
#
|
56
|
+
# @note JSON is the only available format at this time
|
57
|
+
DEFAULT_FORMAT = :json
|
58
|
+
|
59
|
+
# By default, don't use a proxy server
|
60
|
+
DEFAULT_PROXY = nil
|
61
|
+
|
62
|
+
# By default, don't set an application redirect uri
|
63
|
+
DEFAULT_REDIRECT_URI = nil
|
64
|
+
|
65
|
+
# By default, don't set a user scope
|
66
|
+
DEFAULT_SCOPE = nil
|
67
|
+
|
68
|
+
# By default, don't wrap responses with meta data (i.e. pagination)
|
69
|
+
DEFAULT_NO_RESPONSE_WRAPPER = false
|
70
|
+
|
71
|
+
# The user agent that will be sent to the API endpoint if none is set
|
72
|
+
DEFAULT_USER_AGENT = "Instagram Ruby Gem #{Instagram::VERSION}".freeze
|
73
|
+
|
74
|
+
# An array of valid request/response formats
|
75
|
+
#
|
76
|
+
# @note Not all methods support the XML format.
|
77
|
+
VALID_FORMATS = [
|
78
|
+
:json].freeze
|
79
|
+
|
80
|
+
# By default, don't turn on loud logging
|
81
|
+
DEFAULT_LOUD_LOGGER = nil
|
82
|
+
|
83
|
+
# By default, requests are not signed
|
84
|
+
DEFAULT_SIGN_REQUESTS = false
|
85
|
+
|
86
|
+
# @private
|
87
|
+
attr_accessor *VALID_OPTIONS_KEYS
|
88
|
+
|
89
|
+
# When this module is extended, set all configuration options to their default values
|
90
|
+
def self.extended(base)
|
91
|
+
base.reset
|
92
|
+
end
|
93
|
+
|
94
|
+
# Convenience method to allow configuration options to be set in a block
|
95
|
+
def configure
|
96
|
+
yield self
|
97
|
+
end
|
98
|
+
|
99
|
+
# Create a hash of options and their values
|
100
|
+
def options
|
101
|
+
VALID_OPTIONS_KEYS.inject({}) do |option, key|
|
102
|
+
option.merge!(key => send(key))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Reset all configuration options to defaults
|
107
|
+
def reset
|
108
|
+
self.access_token = DEFAULT_ACCESS_TOKEN
|
109
|
+
self.adapter = DEFAULT_ADAPTER
|
110
|
+
self.client_id = DEFAULT_CLIENT_ID
|
111
|
+
self.client_secret = DEFAULT_CLIENT_SECRET
|
112
|
+
self.client_ips = DEFAULT_CLIENT_IPS
|
113
|
+
self.connection_options = DEFAULT_CONNECTION_OPTIONS
|
114
|
+
self.scope = DEFAULT_SCOPE
|
115
|
+
self.redirect_uri = DEFAULT_REDIRECT_URI
|
116
|
+
self.endpoint = DEFAULT_ENDPOINT
|
117
|
+
self.format = DEFAULT_FORMAT
|
118
|
+
self.proxy = DEFAULT_PROXY
|
119
|
+
self.user_agent = DEFAULT_USER_AGENT
|
120
|
+
self.no_response_wrapper= DEFAULT_NO_RESPONSE_WRAPPER
|
121
|
+
self.loud_logger = DEFAULT_LOUD_LOGGER
|
122
|
+
self.sign_requests = DEFAULT_SIGN_REQUESTS
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'faraday_middleware'
|
2
|
+
Dir[File.expand_path('../../faraday/*.rb', __FILE__)].each{|f| require f}
|
3
|
+
|
4
|
+
module Instagram
|
5
|
+
# @private
|
6
|
+
module Connection
|
7
|
+
private
|
8
|
+
|
9
|
+
def connection(raw=false)
|
10
|
+
options = {
|
11
|
+
:headers => {'Accept' => "application/#{format}; charset=utf-8", 'User-Agent' => user_agent},
|
12
|
+
:proxy => proxy,
|
13
|
+
:url => endpoint,
|
14
|
+
}.merge(connection_options)
|
15
|
+
|
16
|
+
Faraday::Connection.new(options) do |connection|
|
17
|
+
connection.use FaradayMiddleware::InstagramOAuth2, client_id, access_token
|
18
|
+
connection.use Faraday::Request::UrlEncoded
|
19
|
+
connection.use FaradayMiddleware::Mashify unless raw
|
20
|
+
unless raw
|
21
|
+
case format.to_s.downcase
|
22
|
+
when 'json' then connection.use Faraday::Response::ParseJson
|
23
|
+
end
|
24
|
+
end
|
25
|
+
connection.use FaradayMiddleware::RaiseHttpException
|
26
|
+
connection.use FaradayMiddleware::LoudLogger if loud_logger
|
27
|
+
connection.adapter(adapter)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Instagram
|
2
|
+
# Custom error class for rescuing from all Instagram errors
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
# Raised when Instagram returns the HTTP status code 400
|
6
|
+
class BadRequest < Error; end
|
7
|
+
|
8
|
+
# Raised when Instagram returns the HTTP status code 403
|
9
|
+
class Forbidden < Error; end
|
10
|
+
|
11
|
+
# Raised when Instagram returns the HTTP status code 404
|
12
|
+
class NotFound < Error; end
|
13
|
+
|
14
|
+
# Raised when Instagram returns the HTTP status code 429
|
15
|
+
class TooManyRequests < Error; end
|
16
|
+
|
17
|
+
# Raised when Instagram returns the HTTP status code 500
|
18
|
+
class InternalServerError < Error; end
|
19
|
+
|
20
|
+
# Raised when Instagram returns the HTTP status code 502
|
21
|
+
class BadGateway < Error; end
|
22
|
+
|
23
|
+
# Raised when Instagram returns the HTTP status code 503
|
24
|
+
class ServiceUnavailable < Error; end
|
25
|
+
|
26
|
+
# Raised when Instagram returns the HTTP status code 504
|
27
|
+
class GatewayTimeout < Error; end
|
28
|
+
|
29
|
+
# Raised when a subscription payload hash is invalid
|
30
|
+
class InvalidSignature < Error; end
|
31
|
+
|
32
|
+
# Raised when Instagram returns the HTTP status code 429
|
33
|
+
class RateLimitExceeded < Error; end
|
34
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Instagram
|
2
|
+
# Defines HTTP request methods
|
3
|
+
module OAuth
|
4
|
+
# Return URL for OAuth authorization
|
5
|
+
def authorize_url(options={})
|
6
|
+
options[:response_type] ||= "code"
|
7
|
+
options[:scope] ||= scope if !scope.nil? && !scope.empty?
|
8
|
+
options[:redirect_uri] ||= self.redirect_uri
|
9
|
+
params = authorization_params.merge(options)
|
10
|
+
connection.build_url("/oauth/authorize/", params).to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
# Return an access token from authorization
|
14
|
+
def get_access_token(code, options={})
|
15
|
+
options[:grant_type] ||= "authorization_code"
|
16
|
+
options[:redirect_uri] ||= self.redirect_uri
|
17
|
+
params = access_token_params.merge(options)
|
18
|
+
post("/oauth/access_token/", params.merge(:code => code), signature=false, raw=false, unformatted=true, no_response_wrapper=true)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def authorization_params
|
24
|
+
{
|
25
|
+
:client_id => client_id
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def access_token_params
|
30
|
+
{
|
31
|
+
:client_id => client_id,
|
32
|
+
:client_secret => client_secret
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'base64'
|
3
|
+
|
4
|
+
module Instagram
|
5
|
+
# Defines HTTP request methods
|
6
|
+
module Request
|
7
|
+
# Perform an HTTP GET request
|
8
|
+
def get(path, options={}, signature=false, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper(), signed=sign_requests)
|
9
|
+
request(:get, path, options, signature, raw, unformatted, no_response_wrapper, signed)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Perform an HTTP POST request
|
13
|
+
def post(path, options={}, signature=false, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper(), signed=sign_requests)
|
14
|
+
request(:post, path, options, signature, raw, unformatted, no_response_wrapper, signed)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Perform an HTTP PUT request
|
18
|
+
def put(path, options={}, signature=false, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper(), signed=sign_requests)
|
19
|
+
request(:put, path, options, signature, raw, unformatted, no_response_wrapper, signed)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Perform an HTTP DELETE request
|
23
|
+
def delete(path, options={}, signature=false, raw=false, unformatted=false, no_response_wrapper=no_response_wrapper(), signed=sign_requests)
|
24
|
+
request(:delete, path, options, signature, raw, unformatted, no_response_wrapper, signed)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
# Perform an HTTP request
|
30
|
+
def request(method, path, options, signature=false, raw=false, unformatted=false, no_response_wrapper=false, signed=sign_requests)
|
31
|
+
response = connection(raw).send(method) do |request|
|
32
|
+
path = formatted_path(path) unless unformatted
|
33
|
+
|
34
|
+
if signed == true
|
35
|
+
if client_id != nil
|
36
|
+
sig_options = options.merge({:client_id => client_id})
|
37
|
+
end
|
38
|
+
if access_token != nil
|
39
|
+
sig_options = options.merge({:access_token => access_token})
|
40
|
+
end
|
41
|
+
sig = generate_sig("/"+path, sig_options, client_secret)
|
42
|
+
options[:sig] = sig
|
43
|
+
end
|
44
|
+
|
45
|
+
case method
|
46
|
+
when :get, :delete
|
47
|
+
request.url(URI.encode(path), options)
|
48
|
+
when :post, :put
|
49
|
+
request.path = URI.encode(path)
|
50
|
+
request.body = options unless options.empty?
|
51
|
+
end
|
52
|
+
if signature && client_ips != nil
|
53
|
+
request.headers["X-Insta-Forwarded-For"] = get_insta_fowarded_for(client_ips, client_secret)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
return response if raw
|
57
|
+
return response.body if no_response_wrapper
|
58
|
+
return Response.create( response.body, {:limit => response.headers['x-ratelimit-limit'].to_i,
|
59
|
+
:remaining => response.headers['x-ratelimit-remaining'].to_i} )
|
60
|
+
end
|
61
|
+
|
62
|
+
def formatted_path(path)
|
63
|
+
[path, format].compact.join('.')
|
64
|
+
end
|
65
|
+
|
66
|
+
def get_insta_fowarded_for(ips, secret)
|
67
|
+
digest = OpenSSL::Digest.new('sha256')
|
68
|
+
signature = OpenSSL::HMAC.hexdigest(digest, secret, ips)
|
69
|
+
return [ips, signature].join('|')
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate_sig(endpoint, params, secret)
|
73
|
+
sig = endpoint
|
74
|
+
params = params.sort_by{|c|c[0].to_s}
|
75
|
+
params.map do |key, val|
|
76
|
+
sig += '|%s=%s' % [key, val]
|
77
|
+
end
|
78
|
+
digest = OpenSSL::Digest.new('sha256')
|
79
|
+
return OpenSSL::HMAC.hexdigest(digest, secret, sig)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Instagram
|
2
|
+
module Response
|
3
|
+
def self.create( response_hash, ratelimit_hash )
|
4
|
+
response_hash = {} unless response_hash
|
5
|
+
data = response_hash.data.dup rescue response_hash
|
6
|
+
data.extend( self )
|
7
|
+
data.instance_exec do
|
8
|
+
%w{pagination meta}.each do |k|
|
9
|
+
response_hash.public_send(k).tap do |v|
|
10
|
+
instance_variable_set("@#{k}", v) if v
|
11
|
+
end
|
12
|
+
end
|
13
|
+
@ratelimit = ::Hashie::Mash.new(ratelimit_hash)
|
14
|
+
end
|
15
|
+
data
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_reader :pagination
|
19
|
+
attr_reader :meta
|
20
|
+
attr_reader :ratelimit
|
21
|
+
end
|
22
|
+
end
|
data/lib/instagram.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path('../instagram/error', __FILE__)
|
2
|
+
require File.expand_path('../instagram/configuration', __FILE__)
|
3
|
+
require File.expand_path('../instagram/api', __FILE__)
|
4
|
+
require File.expand_path('../instagram/client', __FILE__)
|
5
|
+
require File.expand_path('../instagram/response', __FILE__)
|
6
|
+
|
7
|
+
module Instagram
|
8
|
+
extend Configuration
|
9
|
+
|
10
|
+
# Alias for Instagram::Client.new
|
11
|
+
#
|
12
|
+
# @return [Instagram::Client]
|
13
|
+
def self.client(options={})
|
14
|
+
Instagram::Client.new(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Delegate to Instagram::Client
|
18
|
+
def self.method_missing(method, *args, &block)
|
19
|
+
return super unless client.respond_to?(method)
|
20
|
+
client.send(method, *args, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Delegate to Instagram::Client
|
24
|
+
def self.respond_to?(method, include_all=false)
|
25
|
+
return client.respond_to?(method, include_all) || super
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Faraday::Response do
|
4
|
+
before do
|
5
|
+
@client = Instagram::Client.new
|
6
|
+
end
|
7
|
+
|
8
|
+
{
|
9
|
+
400 => Instagram::BadRequest,
|
10
|
+
403 => Instagram::Forbidden,
|
11
|
+
404 => Instagram::NotFound,
|
12
|
+
429 => Instagram::TooManyRequests,
|
13
|
+
500 => Instagram::InternalServerError,
|
14
|
+
503 => Instagram::ServiceUnavailable
|
15
|
+
}.each do |status, exception|
|
16
|
+
context "when HTTP status is #{status}" do
|
17
|
+
|
18
|
+
before do
|
19
|
+
stub_get('users/self/feed.json').
|
20
|
+
to_return(:status => status)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should raise #{exception.name} error" do
|
24
|
+
expect do
|
25
|
+
@client.user_media_feed()
|
26
|
+
end.to raise_error(exception)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when a 400 is raised" do
|
33
|
+
before do
|
34
|
+
stub_get('users/self/feed.json').
|
35
|
+
to_return(:body => '{"meta":{"error_message": "Bad words are bad."}}', :status => 400)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should return the body error message" do
|
39
|
+
expect do
|
40
|
+
@client.user_media_feed()
|
41
|
+
end.to raise_error(Instagram::BadRequest, /Bad words are bad\./)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when a 400 is raised with no meta but an error_message" do
|
46
|
+
before do
|
47
|
+
stub_get('users/self/feed.json').
|
48
|
+
to_return(:body => '{"error_type": "OAuthException", "error_message": "No matching code found."}', :status => 400)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should return the body error type and message" do
|
52
|
+
expect do
|
53
|
+
@client.user_media_feed()
|
54
|
+
end.to raise_error(Instagram::BadRequest, /OAuthException: No matching code found\./)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when a 400 is raised with an HTML response" do
|
59
|
+
before do
|
60
|
+
stub_get('users/self/feed.json').to_return(
|
61
|
+
:body => '<html><body><h1>400 Bad Request</h1> The server returned an invalid or incomplete response. </body></html>',
|
62
|
+
:status => 400)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should return the body error type" do
|
66
|
+
expect do
|
67
|
+
@client.user_media_feed()
|
68
|
+
end.to raise_error(Instagram::BadRequest)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'when a 502 is raised with an HTML response' do
|
73
|
+
before do
|
74
|
+
stub_get('users/self/feed.json').to_return(
|
75
|
+
:body => '<html><body><h1>502 Bad Gateway</h1> The server returned an invalid or incomplete response. </body></html>',
|
76
|
+
:status => 502
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should raise an Instagram::BadGateway' do
|
81
|
+
expect do
|
82
|
+
@client.user_media_feed()
|
83
|
+
end.to raise_error(Instagram::BadGateway)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'when a 504 is raised with an HTML response' do
|
88
|
+
before do
|
89
|
+
stub_get('users/self/feed.json').to_return(
|
90
|
+
:body => '<html> <head><title>504 Gateway Time-out</title></head> <body bgcolor="white"> <center><h1>504 Gateway Time-out</h1></center> <hr><center>nginx</center> </body> </html>',
|
91
|
+
:status => 504
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should raise an Instagram::GatewayTimeout' do
|
96
|
+
expect do
|
97
|
+
@client.user_media_feed()
|
98
|
+
end.to raise_error(Instagram::GatewayTimeout)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|