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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/lib/ohanakapa.rb +33 -0
  3. data/lib/ohanakapa/arguments.rb +14 -0
  4. data/lib/ohanakapa/authentication.rb +25 -0
  5. data/lib/ohanakapa/client.rb +226 -0
  6. data/lib/ohanakapa/client/categories.rb +42 -0
  7. data/lib/ohanakapa/client/keywords.rb +24 -0
  8. data/lib/ohanakapa/client/locations.rb +67 -0
  9. data/lib/ohanakapa/client/organizations.rb +43 -0
  10. data/lib/ohanakapa/client/rate_limit.rb +34 -0
  11. data/lib/ohanakapa/client/search.rb +23 -0
  12. data/lib/ohanakapa/configurable.rb +78 -0
  13. data/lib/ohanakapa/default.rb +88 -0
  14. data/lib/ohanakapa/error.rb +130 -0
  15. data/lib/ohanakapa/rate_limit.rb +31 -0
  16. data/lib/ohanakapa/response/raise_error.rb +21 -0
  17. data/lib/ohanakapa/version.rb +6 -0
  18. data/spec/cassettes/Ohanakapa_Client/_get/handles_query_params.json +1 -0
  19. data/spec/cassettes/Ohanakapa_Client/_head/handles_query_params.json +1 -0
  20. data/spec/cassettes/Ohanakapa_Client/_last_response/caches_the_last_agent_response.json +1 -0
  21. data/spec/cassettes/Ohanakapa_Client/auto_pagination/fetches_all_the_pages.json +1 -0
  22. data/spec/cassettes/Ohanakapa_Client_Categories/_add_keywords_to_a_service/adds_keywords_to_a_given_service.json +1 -0
  23. data/spec/cassettes/Ohanakapa_Client_Categories/_categories/returns_all_categories.json +1 -0
  24. data/spec/cassettes/Ohanakapa_Client_Categories/_replace_all_categories/replaces_all_categories_for_a_service.json +1 -0
  25. data/spec/cassettes/Ohanakapa_Client_Locations/_location/returns_an_location.json +1 -0
  26. data/spec/cassettes/Ohanakapa_Client_Locations/_locations/returns_all_locations.json +1 -0
  27. data/spec/cassettes/Ohanakapa_Client_Locations/_nearby/returns_locations_near_the_queried_location.json +1 -0
  28. data/spec/cassettes/Ohanakapa_Client_Locations/_update_location/updates_a_location_s_attributes.json +1 -0
  29. data/spec/cassettes/Ohanakapa_Client_Organizations/_organization/returns_an_organization.json +1 -0
  30. data/spec/cassettes/Ohanakapa_Client_Organizations/_organizations/returns_all_organizations.json +1 -0
  31. data/spec/cassettes/Ohanakapa_Client_Search/_search/searches_for_keyword_food_and_language_Spanish_.json +1 -0
  32. data/spec/cassettes/Ohanakapa_Client_Search/_search/searches_for_keyword_market_.json +1 -0
  33. data/spec/cassettes/Ohanakapa_Client_Search/_search/searches_for_location_san_mateo_ca_.json +1 -0
  34. data/spec/cassettes/rate_limit.json +1 -0
  35. data/spec/cassettes/root.json +1 -0
  36. data/spec/ohanakapa/client/categories_spec.rb +27 -0
  37. data/spec/ohanakapa/client/keywords_spec.rb +20 -0
  38. data/spec/ohanakapa/client/locations_spec.rb +41 -0
  39. data/spec/ohanakapa/client/organizations_spec.rb +23 -0
  40. data/spec/ohanakapa/client/rate_limit_spec.rb +40 -0
  41. data/spec/ohanakapa/client/search_spec.rb +38 -0
  42. data/spec/ohanakapa/client_spec.rb +363 -0
  43. data/spec/ohanakapa/rate_limit_spec.rb +25 -0
  44. data/spec/ohanakapa_spec.rb +46 -0
  45. data/spec/spec_helper.rb +88 -0
  46. 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