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.
- checksums.yaml +15 -0
- data/LICENSE.txt +23 -0
- data/README.md +189 -0
- data/Rakefile +7 -0
- data/lib/uber.rb +8 -0
- data/lib/uber/api.rb +21 -0
- data/lib/uber/api/activities.rb +15 -0
- data/lib/uber/api/me.rb +15 -0
- data/lib/uber/api/price_estimates.rb +20 -0
- data/lib/uber/api/products.rb +33 -0
- data/lib/uber/api/promotions.rb +20 -0
- data/lib/uber/api/requests.rb +52 -0
- data/lib/uber/api/time_estimates.rb +20 -0
- data/lib/uber/api_request.rb +39 -0
- data/lib/uber/arguments.rb +14 -0
- data/lib/uber/base.rb +29 -0
- data/lib/uber/client.rb +194 -0
- data/lib/uber/error.rb +112 -0
- data/lib/uber/models/activity.rb +47 -0
- data/lib/uber/models/estimate.rb +53 -0
- data/lib/uber/models/map.rb +6 -0
- data/lib/uber/models/price.rb +14 -0
- data/lib/uber/models/product.rb +6 -0
- data/lib/uber/models/promotion.rb +6 -0
- data/lib/uber/models/request.rb +67 -0
- data/lib/uber/models/time.rb +6 -0
- data/lib/uber/models/user.rb +6 -0
- data/lib/uber/parse_json.rb +33 -0
- data/lib/uber/rate_limit.rb +34 -0
- data/lib/uber/token.rb +16 -0
- data/lib/uber/utils.rb +25 -0
- data/lib/uber/version.rb +15 -0
- data/uber-sdk.gemspec +33 -0
- metadata +176 -0
@@ -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
|
data/lib/uber/base.rb
ADDED
@@ -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
|
data/lib/uber/client.rb
ADDED
@@ -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
|
data/lib/uber/error.rb
ADDED
@@ -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
|