uber-sdk 0.1.1

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.
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ module Uber
3
+ class ApiRequest
4
+ attr_accessor :client, :request_method, :path, :options
5
+ alias_method :verb, :request_method
6
+
7
+ # @param client [Uber::Client]
8
+ # @param request_method [String, Symbol]
9
+ # @param path [String]
10
+ # @param options [Hash]
11
+ # @return [Uber::ApiRequest]
12
+ def initialize(client, request_method, path, options = {})
13
+ @client = client
14
+ @request_method = request_method.to_sym
15
+ @path = path
16
+ @options = options
17
+ end
18
+
19
+ # @return [Hash]
20
+ def perform
21
+ @client.send(@request_method, @path, @options).body
22
+ end
23
+
24
+ # @param klass [Class]
25
+ # @param request [Uber::ApiRequest]
26
+ # @return [Object]
27
+ def perform_with_object(klass)
28
+ klass.new(perform)
29
+ end
30
+
31
+ # @param klass [Class]
32
+ # @return [Array]
33
+ def perform_with_objects(klass)
34
+ perform.values.flatten.map do |element|
35
+ klass.new(element)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ module Uber
3
+ class Arguments < Array
4
+ attr_reader :options
5
+
6
+ # Initializes a new Arguments object
7
+ #
8
+ # @return [Uber::Arguments]
9
+ def initialize(args)
10
+ @options = args.last.is_a?(::Hash) ? args.pop : {}
11
+ super(args.flatten)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module Uber
3
+ class Base
4
+ attr_reader :attrs
5
+ alias_method :to_h, :attrs
6
+
7
+ # Initializes a new object
8
+ #
9
+ # @param attrs [Hash]
10
+ # @return [Uber::Base]
11
+ def initialize(attrs = {})
12
+ return if attrs.nil? || attrs.empty?
13
+ attrs.each do |key, value|
14
+ if respond_to?(:"#{key}=")
15
+ send(:"#{key}=", value)
16
+ end
17
+ end
18
+ end
19
+
20
+ # Fetches an attribute of an object using hash notation
21
+ #
22
+ # @param method [String, Symbol] Message to send to the object
23
+ def [](method)
24
+ send(method.to_sym)
25
+ rescue NoMethodError
26
+ nil
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,194 @@
1
+ # frozen_string_literal: true
2
+ require "uber/version"
3
+ require "uber/error"
4
+ require "base64"
5
+ require "faraday"
6
+ require "faraday/request/multipart"
7
+ require "uber/parse_json"
8
+
9
+ module Uber
10
+ class Client
11
+ include Uber::API
12
+
13
+ attr_accessor :server_token, :client_id, :client_secret
14
+ attr_accessor :bearer_token
15
+ attr_accessor :sandbox
16
+
17
+ attr_writer :connection_options, :middleware
18
+ ENDPOINT = "https://api.uber.com"
19
+ SANDBOX_ENDPOINT = "https://sandbox-api.uber.com"
20
+
21
+ def initialize(options = {})
22
+ options.each do |key, value|
23
+ send(:"#{key}=", value)
24
+ end
25
+ yield(self) if block_given?
26
+ validate_credential_type!
27
+ end
28
+
29
+ def bearer_token=(token)
30
+ @bearer_token = Token.new(
31
+ access_token: token,
32
+ token_type: Token::BEARER_TYPE
33
+ )
34
+ end
35
+
36
+ def connection_options
37
+ @connection_options ||= {
38
+ builder: middleware,
39
+ headers: {
40
+ accept: "application/json",
41
+ user_agent: user_agent,
42
+ },
43
+ request: {
44
+ open_timeout: 10,
45
+ timeout: 30,
46
+ }
47
+ }
48
+ end
49
+
50
+ # @return [Boolean]
51
+ def user_token?
52
+ !!(client_id && client_secret)
53
+ end
54
+
55
+ # @return [String]
56
+ def user_agent
57
+ @user_agent ||= "Uber Ruby Gem #{Uber::Version}"
58
+ end
59
+
60
+ def middleware
61
+ @middleware ||= Faraday::RackBuilder.new do |faraday|
62
+ # Encodes as "application/x-www-form-urlencoded" if not already encoded
63
+ faraday.request :url_encoded
64
+ # Parse JSON response bodies
65
+ faraday.response :parse_json
66
+ # Use instrumentation if available
67
+ if defined?(FaradayMiddleware::Instrumentation)
68
+ faraday.use :instrumentation
69
+ end
70
+ # Set default HTTP adapter
71
+ faraday.adapter Faraday.default_adapter
72
+ end
73
+ end
74
+
75
+ # Perform an HTTP GET request
76
+ def get(path, params = {})
77
+ headers = request_headers(:get, path, params)
78
+ request(:get, path, params, headers)
79
+ end
80
+
81
+ # Perform an HTTP POST request
82
+ def post(path, params = {})
83
+ respond = params.values.any? { |value| value.respond_to?(:to_io) }
84
+ response = if respond
85
+ request_headers(:post, path, params, {})
86
+ else
87
+ request_headers(:post, path, params)
88
+ end
89
+ headers = response
90
+ request(:post, path, params.to_json, headers)
91
+ end
92
+
93
+ # Perform an HTTP PUT request
94
+ def put(path, params = {})
95
+ respond = params.values.any? { |value| value.respond_to?(:to_io) }
96
+ response = if respond
97
+ request_headers(:post, path, params, {})
98
+ else
99
+ request_headers(:put, path, params)
100
+ end
101
+ headers = response
102
+ request(:put, path, params.to_json, headers)
103
+ end
104
+
105
+ # Perform an HTTP DELETE request
106
+ def delete(path, params = {})
107
+ headers = request_headers(:delete, path, params)
108
+ request(:delete, path, params, headers)
109
+ end
110
+
111
+ # @return [Boolean]
112
+ def bearer_token?
113
+ !!bearer_token
114
+ end
115
+
116
+ # @return [Hash]
117
+ def credentials
118
+ {
119
+ server_token: server_token,
120
+ client_id: client_id,
121
+ client_secret: client_secret
122
+ }
123
+ end
124
+
125
+ # @return [Boolean]
126
+ def credentials?
127
+ credentials.values.all?
128
+ end
129
+
130
+ private
131
+
132
+ # Ensures that all credentials set during configuration are
133
+ # of a valid type. Valid types are String and Symbol.
134
+ #
135
+ # @raise [Uber::Error::ConfigurationError] Error is raised when
136
+ # supplied uber credentials are not a String or Symbol.
137
+ def validate_credential_type!
138
+ credentials.each do |credential, value|
139
+ next if value.nil?
140
+
141
+ unless value.is_a?(String) || value.is_a?(Symbol) # rubocop:disable Style/Next, Metrics/LineLength
142
+ msg = "Invalid #{credential} specified: #{value.inspect}
143
+ must be a string or symbol."
144
+ fail(Uber::Error::ConfigurationError.new(msg))
145
+ end
146
+ end
147
+ end
148
+
149
+ # Returns a Faraday::Connection object
150
+ #
151
+ # @return [Faraday::Connection]
152
+ def connection
153
+ @connection ||= Faraday.new(
154
+ sandbox ? SANDBOX_ENDPOINT : ENDPOINT,
155
+ connection_options
156
+ )
157
+ end
158
+
159
+ def request(method, path, params = {}, headers = {})
160
+ connection.send(method.to_sym, path, params) do |request|
161
+ request.headers.update(headers)
162
+ end.env
163
+ rescue Faraday::Error::TimeoutError, Timeout::Error => error
164
+ raise(Uber::Error::RequestTimeout.new(error))
165
+ rescue Faraday::Error::ClientError, JSON::ParserError => error
166
+ fail(Uber::Error.new(error))
167
+ end
168
+
169
+ def request_headers(_method, _path, params = {}, _signature_params = params)
170
+ headers = {}
171
+ headers[:accept] = "*/*"
172
+ headers[:content_type] = "application/json; charset=UTF-8"
173
+ headers[:authorization] = if bearer_token?
174
+ bearer_auth_header
175
+ else
176
+ server_auth_header
177
+ end
178
+ headers
179
+ end
180
+
181
+ def bearer_auth_header
182
+ token = if bearer_token.is_a?(Uber::Token) && bearer_token.bearer?
183
+ bearer_token.access_token
184
+ else
185
+ bearer_token
186
+ end
187
+ "Bearer #{token}"
188
+ end
189
+
190
+ def server_auth_header
191
+ "Token #{@server_token}"
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+ require "uber/rate_limit"
3
+
4
+ module Uber
5
+ # Custom error class for rescuing from all Uber errors
6
+ class Error < StandardError
7
+ attr_reader :code, :rate_limit
8
+
9
+ module Code
10
+ AUTHENTICATION_PROBLEM = 32
11
+ MALFORMED_REQUEST = 400
12
+ UNAUTHORIZED_REQUEST = 401
13
+ REQUEST_FORBIDDEN = 403
14
+ RESOURCE_NOT_FOUND = 404
15
+ UNACCEPTABLE_CONTENT_TYPE = 406
16
+ INVALID_REQUEST = 422
17
+ RATE_LIMIT_EXCEEDED = 429
18
+ INTERVAL_ERROR = 500
19
+ end
20
+
21
+ Codes = Code
22
+
23
+ class << self
24
+ # Create a new error from an HTTP response
25
+ #
26
+ # @param response [Faraday::Response]
27
+ # @return [Uber::Error]
28
+ def from_response(response)
29
+ message, code = parse_error(response.body)
30
+ new(message, response.response_headers, code)
31
+ end
32
+
33
+ # @return [Hash]
34
+ def errors
35
+ @errors ||= {
36
+ 400 => Uber::Error::BadRequest,
37
+ 401 => Uber::Error::Unauthorized,
38
+ 403 => Uber::Error::Forbidden,
39
+ 404 => Uber::Error::NotFound,
40
+ 406 => Uber::Error::NotAcceptable,
41
+ 422 => Uber::Error::UnprocessableEntity,
42
+ 429 => Uber::Error::RateLimited,
43
+ 500 => Uber::Error::InternalServerError
44
+ }
45
+ end
46
+
47
+ private
48
+
49
+ def parse_error(body)
50
+ if body.nil?
51
+ ["", nil]
52
+ elsif body[:error]
53
+ [body[:error], nil]
54
+ elsif body[:errors]
55
+ extract_message_from_errors(body)
56
+ end
57
+ end
58
+
59
+ def extract_message_from_errors(body)
60
+ first = Array(body[:errors]).first
61
+ if first.is_a?(Hash)
62
+ [first[:message].chomp, first[:code]]
63
+ else
64
+ [first.chomp, nil]
65
+ end
66
+ end
67
+ end
68
+
69
+ # Initializes a new Error object
70
+ #
71
+ # @param exception [Exception, String]
72
+ # @param rate_limit [Hash]
73
+ # @param code [Integer]
74
+ # @return [Uber::Error]
75
+ def initialize(message = "", rate_limit = {}, code = nil)
76
+ super(message)
77
+ @rate_limit = Uber::RateLimit.new(rate_limit)
78
+ @code = code
79
+ end
80
+
81
+ class ClientError < self; end
82
+
83
+ class ConfigurationError < ::ArgumentError; end
84
+
85
+ # Raised when Uber returns the HTTP status code 400
86
+ class BadRequest < ClientError; end
87
+
88
+ # Raised when Uber returns the HTTP status code 401
89
+ class Unauthorized < ClientError; end
90
+
91
+ # Raised when Uber returns the HTTP status code 403
92
+ class Forbidden < ClientError; end
93
+
94
+ # Raised when Uber returns the HTTP status code 404
95
+ class NotFound < ClientError; end
96
+
97
+ # Raised when Uber returns the HTTP status code 406
98
+ class NotAcceptable < ClientError; end
99
+
100
+ # Raised when Uber returns the HTTP status code 422
101
+ class UnprocessableEntity < ClientError; end
102
+
103
+ # Raised when Uber returns the HTTP status code 429
104
+ class RateLimited < ClientError; end
105
+
106
+ # Raised when Uber returns a 5xx HTTP status code
107
+ class ServerError < self; end
108
+
109
+ # Raised when Uber returns the HTTP status code 500
110
+ class InternalServerError < ServerError; end
111
+ end
112
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ module Uber
3
+ class Activity < Base
4
+ attr_accessor :offset, :limit, :count, :histories
5
+
6
+ def history=(values)
7
+ @histories = values.map { |value| History.new(value) }
8
+ end
9
+ end
10
+
11
+ class History < Base
12
+ attr_accessor :request_time,
13
+ :product_id,
14
+ :status,
15
+ :distance,
16
+ :start_time,
17
+ :end_time,
18
+ :start_city,
19
+ :request_id
20
+
21
+ alias_method :uuid, :request_id
22
+
23
+ def request_time=(value)
24
+ @request_time = ::Time.at(value)
25
+ end
26
+
27
+ def start_time=(value)
28
+ @start_time = ::Time.at(value)
29
+ end
30
+
31
+ def end_time=(value)
32
+ @end_time = ::Time.at(value)
33
+ end
34
+
35
+ def start_city=(value)
36
+ @start_city = City.new(value)
37
+ end
38
+ end
39
+
40
+ class Location < Base
41
+ attr_accessor :address, :latitude, :longitude
42
+ end
43
+
44
+ class City < Base
45
+ attr_accessor :display_name, :latitude, :longitude
46
+ end
47
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+ module Uber
3
+ class Estimate < Base
4
+ attr_accessor :pickup_estimate, :price, :trip, :errors, :code, :message
5
+
6
+ def price=(value)
7
+ @price = value.nil? ? nil : Price.new(value)
8
+ end
9
+
10
+ def trip=(value)
11
+ @trip = value.nil? ? nil : Trip.new(value)
12
+ end
13
+
14
+ def errors=(values)
15
+ @errors = values.map { |v| RequestError.new(v) }
16
+ end
17
+
18
+ def errors?
19
+ multi_errors = @errors && @errors.size >= 1
20
+ single_error = @code && !@code.empty? && @message && !@message.empty?
21
+ multi_errors || single_error
22
+ end
23
+
24
+ def humanized_estimate
25
+ unless pickup_estimate.nil?
26
+ if pickup_estimate.to_i == 1
27
+ "#{pickup_estimate} minute"
28
+ else
29
+ "#{pickup_estimate} minutes"
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ class RequestError < Base
36
+ attr_accessor :status, :code, :title
37
+ end
38
+
39
+ class Price < Base
40
+ attr_accessor :surge_confirmation_href,
41
+ :surge_confirmation_id,
42
+ :high_estimate,
43
+ :low_estimate,
44
+ :minimum,
45
+ :surge_multiplier,
46
+ :display,
47
+ :currency_code
48
+ end
49
+
50
+ class Trip < Base
51
+ attr_accessor :distance_unit, :duration_estimate, :distance_estimate
52
+ end
53
+ end