ohanakapa 1.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/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
|