ohanakapa 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/ohanakapa.rb +33 -0
- data/lib/ohanakapa/arguments.rb +14 -0
- data/lib/ohanakapa/authentication.rb +25 -0
- data/lib/ohanakapa/client.rb +226 -0
- data/lib/ohanakapa/client/categories.rb +42 -0
- data/lib/ohanakapa/client/keywords.rb +24 -0
- data/lib/ohanakapa/client/locations.rb +67 -0
- data/lib/ohanakapa/client/organizations.rb +43 -0
- data/lib/ohanakapa/client/rate_limit.rb +34 -0
- data/lib/ohanakapa/client/search.rb +23 -0
- data/lib/ohanakapa/configurable.rb +78 -0
- data/lib/ohanakapa/default.rb +88 -0
- data/lib/ohanakapa/error.rb +130 -0
- data/lib/ohanakapa/rate_limit.rb +31 -0
- data/lib/ohanakapa/response/raise_error.rb +21 -0
- data/lib/ohanakapa/version.rb +6 -0
- data/spec/cassettes/Ohanakapa_Client/_get/handles_query_params.json +1 -0
- data/spec/cassettes/Ohanakapa_Client/_head/handles_query_params.json +1 -0
- data/spec/cassettes/Ohanakapa_Client/_last_response/caches_the_last_agent_response.json +1 -0
- data/spec/cassettes/Ohanakapa_Client/auto_pagination/fetches_all_the_pages.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Categories/_add_keywords_to_a_service/adds_keywords_to_a_given_service.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Categories/_categories/returns_all_categories.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Categories/_replace_all_categories/replaces_all_categories_for_a_service.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Locations/_location/returns_an_location.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Locations/_locations/returns_all_locations.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Locations/_nearby/returns_locations_near_the_queried_location.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Locations/_update_location/updates_a_location_s_attributes.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Organizations/_organization/returns_an_organization.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Organizations/_organizations/returns_all_organizations.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Search/_search/searches_for_keyword_food_and_language_Spanish_.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Search/_search/searches_for_keyword_market_.json +1 -0
- data/spec/cassettes/Ohanakapa_Client_Search/_search/searches_for_location_san_mateo_ca_.json +1 -0
- data/spec/cassettes/rate_limit.json +1 -0
- data/spec/cassettes/root.json +1 -0
- data/spec/ohanakapa/client/categories_spec.rb +27 -0
- data/spec/ohanakapa/client/keywords_spec.rb +20 -0
- data/spec/ohanakapa/client/locations_spec.rb +41 -0
- data/spec/ohanakapa/client/organizations_spec.rb +23 -0
- data/spec/ohanakapa/client/rate_limit_spec.rb +40 -0
- data/spec/ohanakapa/client/search_spec.rb +38 -0
- data/spec/ohanakapa/client_spec.rb +363 -0
- data/spec/ohanakapa/rate_limit_spec.rb +25 -0
- data/spec/ohanakapa_spec.rb +46 -0
- data/spec/spec_helper.rb +88 -0
- metadata +146 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
module Ohanakapa
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# Methods for the Organizations API
|
5
|
+
#
|
6
|
+
# @see http://ohanapi.herokuapp.com/api/docs
|
7
|
+
module Organizations
|
8
|
+
|
9
|
+
# List all organizations
|
10
|
+
#
|
11
|
+
# This provides a dump of every organization, in the order that they
|
12
|
+
# were uploaded to the Ohana DB.
|
13
|
+
#
|
14
|
+
# @see http://ohanapi.herokuapp.com/api/docs#!/api/GET-api-organizations---format-_get_0
|
15
|
+
#
|
16
|
+
# @return [Array<Sawyer::Resource>] List of Organizations.
|
17
|
+
#
|
18
|
+
# @example
|
19
|
+
# Ohanakapa.organizations
|
20
|
+
# @example
|
21
|
+
# Ohanakapa.orgs
|
22
|
+
def organizations
|
23
|
+
get "organizations"
|
24
|
+
end
|
25
|
+
alias :orgs :organizations
|
26
|
+
|
27
|
+
# Get a single organization based on its ID
|
28
|
+
# @see http://ohanapi.herokuapp.com/api/docs#!/api/GET-api-organizations--id---format-_get_1
|
29
|
+
#
|
30
|
+
# @param id [String] Organization ID.
|
31
|
+
# @return [Sawyer::Resource]
|
32
|
+
# @example
|
33
|
+
# Ohanakapa.organization('519c44065634241897000023')
|
34
|
+
# @example
|
35
|
+
# Ohanakapa.org('519c44065634241897000023')
|
36
|
+
def organization(id)
|
37
|
+
get("organizations/#{id}")
|
38
|
+
end
|
39
|
+
alias :org :organization
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Ohanakapa
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# Methods for API rate limiting info
|
5
|
+
#
|
6
|
+
# @see http://ohanapi.herokuapp.com/api/docs
|
7
|
+
module RateLimit
|
8
|
+
|
9
|
+
# Get rate limit info from last response if available
|
10
|
+
# or make a new request to fetch rate limit
|
11
|
+
#
|
12
|
+
# @see http://ohanapi.herokuapp.com/api/docs
|
13
|
+
# @return [Ohanakapa::RateLimit] Rate limit info
|
14
|
+
def rate_limit(options = {})
|
15
|
+
return rate_limit! if last_response.nil?
|
16
|
+
|
17
|
+
Ohanakapa::RateLimit.from_response(last_response)
|
18
|
+
end
|
19
|
+
alias ratelimit rate_limit
|
20
|
+
|
21
|
+
|
22
|
+
# Refresh rate limit info by making a new request
|
23
|
+
#
|
24
|
+
# @see http://ohanapi.herokuapp.com/api/docs
|
25
|
+
# @return [Ohanakapa::RateLimit] Rate limit info
|
26
|
+
def rate_limit!(options = {})
|
27
|
+
get "rate_limit"
|
28
|
+
Ohanakapa::RateLimit.from_response(last_response)
|
29
|
+
end
|
30
|
+
alias ratelimit! rate_limit!
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Ohanakapa
|
2
|
+
class Client
|
3
|
+
|
4
|
+
# Methods for the Search API
|
5
|
+
#
|
6
|
+
# @see http://ohanapi.herokuapp.com/api/docs#!/api/GET-api-search---format-_get_11
|
7
|
+
module Search
|
8
|
+
|
9
|
+
# Performs a query of the API
|
10
|
+
# @param options [Hash] Search term and qualifiers
|
11
|
+
# @option options [String] :keyword Keyword search term
|
12
|
+
# @option options [String] :location Location search term
|
13
|
+
# @option options [Float] :radius Distance in miles from Location
|
14
|
+
# @option options [String] :language Language spoken at Location
|
15
|
+
# @option options [Fixnum] :page Page of paginated results
|
16
|
+
# @return [Sawyer::Resource] Search results object
|
17
|
+
def search(path, options = {})
|
18
|
+
paginate path, options
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Ohanakapa
|
2
|
+
|
3
|
+
# Configuration options for {Client}, defaulting to values
|
4
|
+
# in {Default}
|
5
|
+
module Configurable
|
6
|
+
# @!attribute api_endpoint
|
7
|
+
# @return [String] Base URL for API requests. default: http://ohanapi.herokuapp.com/api/
|
8
|
+
# @!attribute auto_paginate
|
9
|
+
# @return [Boolean] Auto fetch next page of results until rate limit reached
|
10
|
+
# @!attribute [w] api_token
|
11
|
+
# @return [String] Configure OAuth app secret
|
12
|
+
# @!attribute default_media_type
|
13
|
+
# @see http://developer.github.com/v3/media/
|
14
|
+
# @return [String] Configure preferred media type (for API versioning, for example)
|
15
|
+
# @!attribute connection_options
|
16
|
+
# @see https://github.com/lostisland/faraday
|
17
|
+
# @return [Hash] Configure connection options for Faraday
|
18
|
+
# @!attribute login
|
19
|
+
# @return [String] GitHub username for Basic Authentication
|
20
|
+
# @!attribute middleware
|
21
|
+
# @see https://github.com/lostisland/faraday
|
22
|
+
# @return [Faraday::Builder] Configure middleware for Faraday
|
23
|
+
# @!attribute per_page
|
24
|
+
# @return [String] Configure page size for paginated results. API default: 30
|
25
|
+
# @!attribute proxy
|
26
|
+
# @see https://github.com/lostisland/faraday
|
27
|
+
# @return [String] URI for proxy server
|
28
|
+
# @!attribute user_agent
|
29
|
+
# @return [String] Configure User-Agent header for requests.
|
30
|
+
|
31
|
+
attr_accessor :api_endpoint, :auto_paginate, :connection_options,
|
32
|
+
:default_media_type, :middleware, :proxy,
|
33
|
+
:user_agent
|
34
|
+
attr_writer :api_token
|
35
|
+
|
36
|
+
class << self
|
37
|
+
|
38
|
+
# List of configurable keys for {Ohanakapa::Client}
|
39
|
+
# @return [Array] of option keys
|
40
|
+
def keys
|
41
|
+
@keys ||= [
|
42
|
+
:api_endpoint,
|
43
|
+
:auto_paginate,
|
44
|
+
:api_token,
|
45
|
+
:connection_options,
|
46
|
+
:default_media_type,
|
47
|
+
:middleware,
|
48
|
+
:proxy,
|
49
|
+
:user_agent
|
50
|
+
]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Set configuration options using a block
|
55
|
+
def configure
|
56
|
+
yield self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Reset configuration options to default values
|
60
|
+
def reset!
|
61
|
+
Ohanakapa::Configurable.keys.each do |key|
|
62
|
+
instance_variable_set(:"@#{key}", Ohanakapa::Default.options[key])
|
63
|
+
end
|
64
|
+
self
|
65
|
+
end
|
66
|
+
alias setup reset!
|
67
|
+
|
68
|
+
def api_endpoint
|
69
|
+
File.join(@api_endpoint, "")
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def options
|
75
|
+
Hash[Ohanakapa::Configurable.keys.map{|key| [key, instance_variable_get(:"@#{key}")]}]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'ohanakapa/response/raise_error'
|
2
|
+
require 'ohanakapa/version'
|
3
|
+
|
4
|
+
module Ohanakapa
|
5
|
+
|
6
|
+
# Default configuration options for {Client}
|
7
|
+
module Default
|
8
|
+
|
9
|
+
# Default API endpoint
|
10
|
+
API_ENDPOINT = "http://ohanapi.herokuapp.com/api".freeze
|
11
|
+
|
12
|
+
# Default User Agent header string
|
13
|
+
USER_AGENT = "Ohanakapa Ruby Gem #{Ohanakapa::VERSION}".freeze
|
14
|
+
|
15
|
+
# Default media type
|
16
|
+
MEDIA_TYPE = "application/vnd.ohanapi-v1+json"
|
17
|
+
|
18
|
+
# Default Faraday middleware stack
|
19
|
+
MIDDLEWARE = Faraday::Builder.new do |builder|
|
20
|
+
builder.use Ohanakapa::Response::RaiseError
|
21
|
+
builder.adapter Faraday.default_adapter
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
|
26
|
+
# Configuration options
|
27
|
+
# @return [Hash]
|
28
|
+
def options
|
29
|
+
Hash[Ohanakapa::Configurable.keys.map{|key| [key, send(key)]}]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Default API endpoint from ENV or {API_ENDPOINT}
|
33
|
+
# @return [String]
|
34
|
+
def api_endpoint
|
35
|
+
ENV['OHANAKAPA_API_ENDPOINT'] || API_ENDPOINT
|
36
|
+
end
|
37
|
+
|
38
|
+
# Default pagination preference from ENV
|
39
|
+
# @return [String]
|
40
|
+
def auto_paginate
|
41
|
+
ENV['OHANAKAPA_AUTO_PAGINATE']
|
42
|
+
end
|
43
|
+
|
44
|
+
# Default options for Faraday::Connection
|
45
|
+
# @return [Hash]
|
46
|
+
def connection_options
|
47
|
+
{
|
48
|
+
:headers => {
|
49
|
+
:accept => default_media_type,
|
50
|
+
:user_agent => user_agent
|
51
|
+
}
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
# Default media type from ENV or {MEDIA_TYPE}
|
56
|
+
# @return [String]
|
57
|
+
def default_media_type
|
58
|
+
ENV['OHANAKAPA_DEFAULT_MEDIA_TYPE'] || MEDIA_TYPE
|
59
|
+
end
|
60
|
+
|
61
|
+
# Default middleware stack for Faraday::Connection
|
62
|
+
# from {MIDDLEWARE}
|
63
|
+
# @return [String]
|
64
|
+
def middleware
|
65
|
+
MIDDLEWARE
|
66
|
+
end
|
67
|
+
|
68
|
+
# Default proxy server URI for Faraday connection from ENV
|
69
|
+
# @return [String]
|
70
|
+
def proxy
|
71
|
+
ENV['OHANAKAPA_PROXY']
|
72
|
+
end
|
73
|
+
|
74
|
+
# Default api token for Ohana API
|
75
|
+
# @return [String]
|
76
|
+
def api_token
|
77
|
+
ENV['OHANAKAPA_API_TOKEN']
|
78
|
+
end
|
79
|
+
|
80
|
+
# Default User-Agent header string from ENV or {USER_AGENT}
|
81
|
+
# @return [String]
|
82
|
+
def user_agent
|
83
|
+
ENV['OHANAKAPA_USER_AGENT'] || USER_AGENT
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Ohanakapa
|
2
|
+
# Custom error class for rescuing from all Ohana API errors
|
3
|
+
class Error < StandardError
|
4
|
+
|
5
|
+
# Returns the appropriate Ohanakapa::Error subclass based
|
6
|
+
# on status and response message
|
7
|
+
#
|
8
|
+
# @param [Hash] response HTTP response
|
9
|
+
# @return [Ohanakapa::Error]
|
10
|
+
def self.from_response(response)
|
11
|
+
status = response[:status].to_i
|
12
|
+
body = response[:body].to_s
|
13
|
+
|
14
|
+
if klass = case status
|
15
|
+
when 400 then Ohanakapa::BadRequest
|
16
|
+
when 401 then Ohanakapa::Unauthorized
|
17
|
+
when 403
|
18
|
+
if body =~ /rate limit exceeded/i
|
19
|
+
Ohanakapa::TooManyRequests
|
20
|
+
elsif body =~ /login attempts exceeded/i
|
21
|
+
Ohanakapa::TooManyLoginAttempts
|
22
|
+
else
|
23
|
+
Ohanakapa::Forbidden
|
24
|
+
end
|
25
|
+
when 404 then Ohanakapa::NotFound
|
26
|
+
when 406 then Ohanakapa::NotAcceptable
|
27
|
+
#when 422 then Ohanakapa::UnprocessableEntity
|
28
|
+
when 500 then Ohanakapa::InternalServerError
|
29
|
+
when 501 then Ohanakapa::NotImplemented
|
30
|
+
when 502 then Ohanakapa::BadGateway
|
31
|
+
when 503 then Ohanakapa::ServiceUnavailable
|
32
|
+
end
|
33
|
+
klass.new(response)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(response=nil)
|
38
|
+
@response = response
|
39
|
+
super(build_error_message)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def data
|
45
|
+
@data ||=
|
46
|
+
if (body = @response[:body]) && !body.empty?
|
47
|
+
if body.is_a?(String) &&
|
48
|
+
@response[:response_headers] &&
|
49
|
+
@response[:response_headers][:content_type] =~ /json/
|
50
|
+
|
51
|
+
Sawyer::Agent.serializer.decode(body)
|
52
|
+
else
|
53
|
+
body
|
54
|
+
end
|
55
|
+
else
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def response_message
|
61
|
+
case data
|
62
|
+
when Hash
|
63
|
+
data[:message] || data[:description]
|
64
|
+
when String
|
65
|
+
data
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def response_error
|
70
|
+
"Error: #{data[:error]}" if data.is_a?(Hash) && data[:error]
|
71
|
+
end
|
72
|
+
|
73
|
+
def response_error_summary
|
74
|
+
return nil unless data.is_a?(Hash) && !Array(data[:errors]).empty?
|
75
|
+
|
76
|
+
summary = "\nError summary:\n"
|
77
|
+
summary << data[:errors].map do |hash|
|
78
|
+
hash.map { |k,v| " #{k}: #{v}" }
|
79
|
+
end.join("\n")
|
80
|
+
|
81
|
+
summary
|
82
|
+
end
|
83
|
+
|
84
|
+
def build_error_message
|
85
|
+
return nil if @response.nil?
|
86
|
+
|
87
|
+
message = "#{@response[:method].to_s.upcase} "
|
88
|
+
message << "#{@response[:url].to_s}: "
|
89
|
+
message << "#{@response[:status]} - "
|
90
|
+
message << "#{response_message}" unless response_message.nil?
|
91
|
+
message << "#{response_error}" unless response_error.nil?
|
92
|
+
message << "#{response_error_summary}" unless response_error_summary.nil?
|
93
|
+
message
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Raised when Ohana API returns a 400 HTTP status code
|
98
|
+
class BadRequest < Error; end
|
99
|
+
|
100
|
+
# Raised when Ohana API returns a 401 HTTP status code
|
101
|
+
class Unauthorized < Error; end
|
102
|
+
|
103
|
+
# Raised when Ohana API returns a 403 HTTP status code
|
104
|
+
class Forbidden < Error; end
|
105
|
+
|
106
|
+
# Raised when GitHub returns a 403 HTTP status code
|
107
|
+
# and body matches 'rate limit exceeded'
|
108
|
+
class TooManyRequests < Forbidden; end
|
109
|
+
|
110
|
+
# Raised when Ohana API returns a 404 HTTP status code
|
111
|
+
class NotFound < Error; end
|
112
|
+
|
113
|
+
# Raised when Ohana API returns a 406 HTTP status code
|
114
|
+
class NotAcceptable < Error; end
|
115
|
+
|
116
|
+
# Raised when Ohana API returns a 422 HTTP status code
|
117
|
+
class UnprocessableEntity < Error; end
|
118
|
+
|
119
|
+
# Raised when Ohana API returns a 500 HTTP status code
|
120
|
+
class InternalServerError < Error; end
|
121
|
+
|
122
|
+
# Raised when Ohana API returns a 501 HTTP status code
|
123
|
+
class NotImplemented < Error; end
|
124
|
+
|
125
|
+
# Raised when Ohana API returns a 502 HTTP status code
|
126
|
+
class BadGateway < Error; end
|
127
|
+
|
128
|
+
# Raised when Ohana API returns a 503 HTTP status code
|
129
|
+
class ServiceUnavailable < Error; end
|
130
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Ohanakapa
|
2
|
+
|
3
|
+
# Class for API Rate Limit info
|
4
|
+
#
|
5
|
+
# @!attribute [w] limit
|
6
|
+
# @return [Fixnum] Max tries per rate limit period
|
7
|
+
# @!attribute [w] remaining
|
8
|
+
# @return [Fixnum] Remaining tries per rate limit period
|
9
|
+
# @!attribute [w] resets_at
|
10
|
+
# @return [Time] Indicates when rate limit resets
|
11
|
+
# @!attribute [w] resets_in
|
12
|
+
# @return [Fixnum] Number of seconds when rate limit resets
|
13
|
+
#
|
14
|
+
# @see http://ohanapi.herokuapp.com/api/docs/v1/#rate-limiting
|
15
|
+
class RateLimit < Struct.new :limit, :remaining
|
16
|
+
|
17
|
+
# Get rate limit info from HTTP response
|
18
|
+
#
|
19
|
+
# @param response [#headers] HTTP response
|
20
|
+
# @return [RateLimit]
|
21
|
+
def self.from_response(response)
|
22
|
+
info = new
|
23
|
+
if response && !response.headers.nil?
|
24
|
+
info.limit = response.headers['X-RateLimit-Limit'].to_i
|
25
|
+
info.remaining = response.headers['X-RateLimit-Remaining'].to_i
|
26
|
+
end
|
27
|
+
|
28
|
+
info
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'ohanakapa/error'
|
3
|
+
|
4
|
+
module Ohanakapa
|
5
|
+
# Faraday response middleware
|
6
|
+
module Response
|
7
|
+
|
8
|
+
# This class raises an Ohanakapa-flavored exception based on
|
9
|
+
# HTTP status codes returned by the API
|
10
|
+
class RaiseError < Faraday::Response::Middleware
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def on_complete(response)
|
15
|
+
if error = Ohanakapa::Error.from_response(response)
|
16
|
+
raise error
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|