smartcar 0.1.1 → 1.0.5

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.
@@ -1,12 +1,12 @@
1
1
  module Smartcar
2
2
  # class to represent Fuel info
3
- #
4
- # @author [ashwin]
5
- #
3
+ #@attr [Number] amountRemaining Amount of fuel remaining.
4
+ #@attr [Number] percentageRemaining Decimal value representing the remaining fuel percent.
5
+ #@attr [Number] range Remaining range of the vehicle.
6
6
  class Fuel < Base
7
- include Utils
7
+ # Path Proc for hitting fuel end point
8
8
  PATH = Proc.new{|id| "/vehicles/#{id}/fuel"}
9
- attr_accessor :amountRemaining, :percentRemaining, :range
9
+ attr_reader :amountRemaining, :percentRemaining, :range
10
10
 
11
11
  # just to have Ruby-esque method names
12
12
  alias_method :amount_remaining, :amountRemaining
@@ -1,11 +1,10 @@
1
1
  module Smartcar
2
2
  # class to represent Location info
3
- #
4
- # @author [ashwin]
5
- #
3
+ #@attr [Number] latitude Latitude of last recorded location.
4
+ #@attr [Number] longitude Longitude of last recorded location.
6
5
  class Location < Base
7
- include Utils
6
+ # Path Proc for hitting location end point
8
7
  PATH = Proc.new{|id| "/vehicles/#{id}/location"}
9
- attr_accessor :latitude, :longitude
8
+ attr_reader :latitude, :longitude
10
9
  end
11
10
  end
@@ -1,93 +1,91 @@
1
1
  module Smartcar
2
- # Oauth class to take care of the Oauth 2.0 with genomelink APIs
3
- #
4
- # @author [ashwin]
2
+ # Oauth class to take care of the Oauth 2.0 with Smartcar APIs
5
3
  #
6
4
  class Oauth < Base
7
- class << self
8
- # Generate the OAuth authorization URL.
9
- #
10
- # By default users are not shown the permission dialog if they have already
11
- # approved the set of scopes for this application. The application can elect
12
- # to always display the permissions dialog to the user by setting
13
- # approval_prompt to `force`.
14
- #
15
- # @param options [Hash]
16
- # @param options[:state] [String] - OAuth state parameter passed to the
17
- # redirect uri. This parameter may be used for identifying the user who
18
- # initiated the request.
19
- # @param options[:test_mode] [Boolean] - Setting this to 'true' runs it in test mode.
20
- # @param options[:force_prompt] [Boolean] - Setting `force_prompt` to
21
- # `true` will show the permissions approval screen on every authentication
22
- # attempt, even if the user has previously consented to the exact scope of
23
- # permissions.
24
- # @param options[:make] [String] - `make' is an optional parameter that allows
25
- # users to bypass the car brand selection screen.
26
- # For a complete list of supported makes, please see our
27
- # [API Reference](https://smartcar.com/docs/api#authorization) documentation.
28
- # @param options[:scope] [Array of Strings] - array of scopes that specify what the user can access
29
- # EXAMPLE : ['read_odometer', 'read_vehicle_info', 'required:read_location']
30
- # For further details refer to https://smartcar.com/docs/guides/scope/
31
- #
32
- # @return [String] URL where user needs to be redirected for authorization
33
- def authorization_url(options)
34
- parameters = {
35
- redirect_uri: get_config('REDIRECT_URI'),
36
- approval_prompt: options[:force_prompt] ? FORCE : AUTO,
37
- mode: options[:test_mode] ? TEST : LIVE,
38
- response_type: CODE
39
- }
40
- parameters[:scope] = options[:scope].join(' ') if options[:scope]
41
- %I(state make).each do |parameter|
42
- parameters[:parameter] = options[:parameter] unless options[:parameter].nil?
43
- end
44
-
45
- client.auth_code.authorize_url(parameters)
46
- end
5
+ extend Utils
6
+ # By default users are not shown the permission dialog if they have already
7
+ # approved the set of scopes for this application. The application can elect
8
+ # to always display the permissions dialog to the user by setting
9
+ # approval_prompt to `force`.
10
+ #
11
+ # @param options [Hash]
12
+ # @option options[:client_id] [String] - Client ID, if not passed fallsback to ENV['CLIENT_ID']
13
+ # @option options[:client_secret] [String] - Client Secret, if not passed fallsback to ENV['CLIENT_SECRET']
14
+ # @option options[:redirect_uri] [String] - Redirect URI, if not passed fallsback to ENV['REDIRECT_URI']
15
+ # @option options[:state] [String] - OAuth state parameter passed to the
16
+ # redirect uri. This parameter may be used for identifying the user who
17
+ # initiated the request.
18
+ # @option options[:test_mode] [Boolean] - Setting this to 'true' runs it in test mode.
19
+ # @option options[:force_prompt] [Boolean] - Setting `force_prompt` to
20
+ # `true` will show the permissions approval screen on every authentication
21
+ # attempt, even if the user has previously consented to the exact scope of
22
+ # permissions.
23
+ # @option options[:make] [String] - `make' is an optional parameter that allows
24
+ # users to bypass the car brand selection screen.
25
+ # For a complete list of supported makes, please see our
26
+ # [API Reference](https://smartcar.com/docs/api#authorization) documentation.
27
+ # @option options[:scope] [Array of Strings] - array of scopes that specify what the user can access
28
+ # EXAMPLE : ['read_odometer', 'read_vehicle_info', 'required:read_location']
29
+ # For further details refer to https://smartcar.com/docs/guides/scope/
30
+ #
31
+ # @return [Smartcar::Oauth] Returns a Smartcar::Oauth Object that has other methods
32
+ def initialize(options)
33
+ @redirect_uri = options[:redirect_uri] || get_config('REDIRECT_URI')
34
+ @client_id = options[:client_id] || get_config('CLIENT_ID')
35
+ @client_secret = options[:client_secret] || get_config('CLIENT_SECRET')
47
36
 
48
- # [get_token description]
49
- # @param auth_code [String] This is the code that is returned after use
50
- # visits and authorizes on the authorization URL.
51
- #
52
- # @return [Hash] Hash of token, refresh token, expiry info and token type
53
- def get_token(auth_code)
54
- client.auth_code
55
- .get_token(
56
- auth_code,
57
- redirect_uri: get_config('REDIRECT_URI')
58
- ).to_hash
37
+ @auth_parameters = {
38
+ redirect_uri: @redirect_uri,
39
+ approval_prompt: options[:force_prompt] ? FORCE : AUTO,
40
+ mode: options[:test_mode] ? TEST : LIVE,
41
+ response_type: CODE
42
+ }
43
+ @auth_parameters[:scope] = options[:scope].join(' ') if options[:scope]
44
+ %I(state make).each do |parameter|
45
+ parameters[:parameter] = options[:parameter] unless options[:parameter].nil?
59
46
  end
47
+ end
60
48
 
61
- # [refresh_token description]
62
- # @param token_hash [Hash] This is the hash that is returned with the
63
- # get_token method
64
- #
65
- # @return [Hash] Hash of token, refresh token, expiry info and token type
66
- def refresh_token(token_hash)
67
- token_object = OAuth2::AccessToken.from_hash(client, token_hash)
68
- token_object = token_object.refresh!
69
- token_object.to_hash
70
- end
49
+ # Generate the OAuth authorization URL.
50
+ #
51
+ # @return [String] Authorization URL string
52
+ def authorization_url
53
+ client.auth_code.authorize_url(@auth_parameters)
54
+ end
71
55
 
72
- private
73
- # gets the Oauth Client object
74
- #
75
- # @return [OAuth2::Client] A Oauth Client object.
76
- def client
77
- @client ||= OAuth2::Client.new( get_config('CLIENT_ID'),
78
- get_config('CLIENT_SECRET'),
79
- :site => OAUTH_PATH
80
- )
81
- end
56
+ # Generates the tokens hash using the code returned in oauth process.
57
+ # @param auth_code [String] This is the code that is returned after user
58
+ # visits and authorizes on the authorization URL.
59
+ #
60
+ # @return [Hash] Hash of token, refresh token, expiry info and token type
61
+ def get_token(auth_code)
62
+ client.auth_code
63
+ .get_token(
64
+ auth_code,
65
+ redirect_uri: @redirect_uri
66
+ ).to_hash
67
+ end
82
68
 
83
- # gets a given env variable, checks for existence and throws exception if not present
84
- # @param config_name [String] key of the env variable
85
- #
86
- # @return [String] value of the env variable
87
- def get_config(config_name)
88
- raise ConfigNotFound, "Environment variable #{config_name} not found !" unless ENV[config_name]
89
- ENV[config_name]
90
- end
69
+ # Refreshing the access token
70
+ # @param token_hash [Hash] This is the hash that is returned with the
71
+ # get_token method
72
+ #
73
+ # @return [Hash] Hash of token, refresh token, expiry info and token type
74
+ def refresh_token(token_hash)
75
+ token_object = OAuth2::AccessToken.from_hash(client, token_hash)
76
+ token_object = token_object.refresh!
77
+ token_object.to_hash
78
+ end
79
+
80
+ private
81
+ # gets the Oauth Client object
82
+ #
83
+ # @return [OAuth2::Client] A Oauth Client object.
84
+ def client
85
+ @client ||= OAuth2::Client.new( @client_id,
86
+ @client_secret,
87
+ :site => OAUTH_PATH
88
+ )
91
89
  end
92
90
  end
93
91
  end
@@ -1,11 +1,9 @@
1
1
  module Smartcar
2
- # class to get Charge info
3
- #
4
- # @author [ashwin]
5
- #
2
+ # class to represent Odometer
3
+ #@attr [Number] distanceLast recorded odometer reading.
6
4
  class Odometer < Base
7
- include Utils
5
+ # Path Proc for hitting odometer end point
8
6
  PATH = Proc.new{|id| "/vehicles/#{id}/odometer"}
9
- attr_accessor :distance
7
+ attr_reader :distance
10
8
  end
11
9
  end
@@ -1,11 +1,9 @@
1
1
  module Smartcar
2
- # class to get Charge info
3
- #
4
- # @author [ashwin]
5
- #
6
- class Permissions
7
- include Utils
2
+ # class to represent permissions response
3
+ #@attr [Array] permissions Array of permissions granted on the vehicle.
4
+ class Permissions < Base
5
+ # Path Proc for hitting permissions end point
8
6
  PATH = Proc.new{|id| "/vehicles/#{id}/permissions"}
9
- attr_accessor :permissions
7
+ attr_reader :permissions
10
8
  end
11
9
  end
@@ -1,12 +1,14 @@
1
1
  module Smartcar
2
- # class to represent Engine oil life
3
- #
4
- # @author [ashwin]
5
- #
2
+ # class to represent Tire Pressure response
3
+ #@attr [Number] back_left Last recorded tire pressure of the back left tire.
4
+ #@attr [Number] back_right Last recorded tire pressure of the back right tire.
5
+ #@attr [Number] front_left Last recorded tire pressure of the front left tire.
6
+ #@attr [Number] front_right Last recorded tire pressure of the front right tire.
7
+
6
8
  class TirePressure < Base
7
- include Utils
9
+ # Path Proc for hitting tire pressure end point
8
10
  PATH = Proc.new{|id| "/vehicles/#{id}/tires/pressure"}
9
- attr_accessor :backLeft, :backRight, :frontLeft, :frontRight
11
+ attr_reader :backLeft, :backRight, :frontLeft, :frontRight
10
12
 
11
13
  # just to have Ruby-esque method names
12
14
  alias_method :back_left, :backLeft
@@ -1,11 +1,11 @@
1
1
  module Smartcar
2
2
  # Class to get to user API.
3
- #
4
- # @author [ashwin]
5
- #
3
+ #@attr [String] id Smartcar user id.
4
+ #@attr [String] token Access token used to connect to Smartcar API.
6
5
  class User < Base
6
+ # Path for hitting user end point
7
7
  USER_PATH = '/user'.freeze
8
- attr_accessor :id, :token
8
+ attr_reader :id, :token
9
9
 
10
10
  def initialize(token:)
11
11
  raise InvalidParameterValue.new, "Access Token(token) is a required field" if token.nil?
@@ -21,6 +21,6 @@ module Smartcar
21
21
  def self.user_id(token:)
22
22
  new(token: token).get(USER_PATH)['id']
23
23
  end
24
-
24
+
25
25
  end
26
26
  end
@@ -1,11 +1,35 @@
1
- module Utils
2
- # A constructor to take a hash and assign it to the instance variables
3
- # @param options = {} [Hash] Could by any class's hash, but the first level keys should be defined in the class
4
- #
5
- # @return [Subclass os Base] Returns object of any subclass like Report
6
- def initialize(options = {})
7
- options.each do |attribute, value|
8
- instance_variable_set("@#{attribute}", value)
9
- end
1
+ # Utils module , provides utility methods to underlying classes
2
+ module Utils
3
+ # A constructor to take a hash and assign it to the instance variables
4
+ # @param options = {} [Hash] Could by any class's hash, but the first level keys should be defined in the class
5
+ #
6
+ # @return [Subclass os Base] Returns object of any subclass like Report
7
+ def initialize(options = {})
8
+ options.each do |attribute, value|
9
+ instance_variable_set("@#{attribute}", value)
10
10
  end
11
- end
11
+ end
12
+
13
+ # gets a given env variable, checks for existence and throws exception if not present
14
+ # @param config_name [String] key of the env variable
15
+ #
16
+ # @return [String] value of the env variable
17
+ def get_config(config_name)
18
+ config_name = "INTEGRATION_#{config_name}" if ENV['MODE'] == 'test'
19
+ raise Smartcar::ConfigNotFound, "Environment variable #{config_name} not found !" unless ENV[config_name]
20
+ ENV[config_name]
21
+ end
22
+
23
+ # Given the response from smartcar API, returns an error object if needed
24
+ # @param response [Object] response Object with status and body
25
+ #
26
+ # @return [Object] nil OR Error object
27
+ def get_error(response)
28
+ status = response.status
29
+ return nil if [200,204].include?(status)
30
+ return Smartcar::ServiceUnavailableError.new("Service Unavailable - #{response.body}") if status == 404
31
+ return Smartcar::BadRequestError.new("Bad Request - #{response.body}") if status == 400
32
+ return Smartcar::AuthenticationError.new("Authentication error") if status == 401
33
+ return Smartcar::ExternalServiceError.new("API error - #{response.body}")
34
+ end
35
+ end
@@ -4,14 +4,21 @@ module Smartcar
4
4
  # For Ex. Vehicle object will be treate as an entity and doing vehicle_object.
5
5
  # Battery should return Battery object.
6
6
  #
7
- # @author [ashwin]
8
- #
7
+ #@attr [String] token Access token used to connect to Smartcar API.
8
+ #@attr [String] id Smartcar vehicle ID.
9
+ #@attr [String] unit_system unit system to represent the data in.
9
10
  class Vehicle < Base
11
+ include Utils
12
+
10
13
 
14
+ # Path for hitting compatibility end point
11
15
  COMPATIBLITY_PATH = '/compatibility'.freeze
16
+
17
+ # Path for hitting vehicle ids end point
12
18
  PATH = Proc.new{|id| "/vehicles/#{id}"}
13
- attr_accessor :token, :id, :unit_system
14
19
 
20
+ attr_reader :id
21
+ attr_accessor :token, :unit_system
15
22
 
16
23
  def initialize(token:, id:, unit_system: IMPERIAL)
17
24
  raise InvalidParameterValue.new, "Invalid Units provided : #{unit_system}" unless UNITS.include?(unit_system)
@@ -22,13 +29,6 @@ module Smartcar
22
29
  @unit_system = unit_system
23
30
  end
24
31
 
25
- # Accessor method for vehicle attributes.
26
- %I(make model year).each do |method_name|
27
- define_method method_name do
28
- vehicle_attributes.send(method_name)
29
- end
30
- end
31
-
32
32
  # Class method Used to get all the vehicles in the app. This only returns
33
33
  # API - https://smartcar.com/docs/api#get-all-vehicles
34
34
  # @param token [String] - Access token
@@ -36,10 +36,11 @@ module Smartcar
36
36
  #
37
37
  # @return [Array] of vehicle IDs(Strings)
38
38
  def self.all_vehicle_ids(token:, options: {})
39
- new(token: token, id: 'none').fetch(
39
+ response, meta = new(token: token, id: 'none').fetch(
40
40
  path: PATH.call(''),
41
41
  options: options
42
- )['vehicles']
42
+ )
43
+ response['vehicles']
43
44
  end
44
45
 
45
46
  # Class method Used to check compatiblity for VIN and scope
@@ -52,13 +53,26 @@ module Smartcar
52
53
  raise InvalidParameterValue.new, "vin is a required field" if vin.nil?
53
54
  raise InvalidParameterValue.new, "scope is a required field" if scope.nil?
54
55
 
55
- new(token: 'none', id: 'none').fetch(path: COMPATIBLITY_PATH,
56
+ response, meta = new(token: 'none', id: 'none').fetch(path: COMPATIBLITY_PATH,
56
57
  options: {
57
58
  vin: vin,
58
59
  scope: scope.join(' ')
59
60
  },
60
61
  auth: BASIC
61
- )['compatible']
62
+ )
63
+ response['compatible']
64
+ end
65
+
66
+ # Method to get batch requests
67
+ # API - https://smartcar.com/docs/api#post-batch-request
68
+ # @param attributes [Array] Array of strings or symbols of attributes to be fetched together
69
+ #
70
+ # @return [Hash] Hash wth key as requested attribute(symbol) and value as Error OR Object of the requested attribute
71
+ def batch(attributes = [])
72
+ raise InvalidParameterValue.new, "vin is a required field" if attributes.nil?
73
+ request_body = get_batch_request_body(attributes)
74
+ response, _meta = post(PATH.call(id) + "/batch", request_body)
75
+ process_batch_response(response)
62
76
  end
63
77
 
64
78
  # Fetch the list of permissions that this application has been granted for
@@ -68,10 +82,7 @@ module Smartcar
68
82
  #
69
83
  # @return [Array] of permissions (Strings)
70
84
  def permissions(options: {})
71
- Permissions.new(fetch(
72
- path: Permissions::PATH.call(id),
73
- options: options
74
- )).permissions
85
+ get_attribute(Permissions)
75
86
  end
76
87
 
77
88
  # Method Used toRevoke access for the current requesting application
@@ -83,104 +94,201 @@ module Smartcar
83
94
  response['status'] == SUCCESS
84
95
  end
85
96
 
86
- # Methods Used lock or unlock car
97
+ # Methods Used to lock car
87
98
  # API - https://smartcar.com/docs/api#post-security
88
99
  #
89
100
  # @return [Boolean] true if success
90
- %w(lock unlock).each do |method_name|
91
- define_method "#{method_name}!" do
92
- lock_or_unlock!(action: Smartcar.const_get(method_name.upcase))
93
- end
101
+ def lock!
102
+ lock_or_unlock!(action: Smartcar::LOCK)
94
103
  end
95
104
 
96
- # Following section defined methods using meta programing to fetch various
97
- # details of a vehicle. The key is the method name, and value is Class that
98
- # wraps the data.
99
- {
100
- # Returns the state of charge (SOC) and remaining range of an electric or
101
- # plug-in hybrid vehicle's battery.
102
- # API - https://smartcar.com/docs/api#get-ev-battery
103
- #
104
- # @return [Battery] object
105
- battery: Battery,
106
- # Returns the current charge status of the vehicle.
107
- # API - https://smartcar.com/docs/api#get-ev-battery
108
- #
109
- # @return [Charge] object
110
- charge: Charge,
111
- # Returns the remaining life span of a vehicle's engine oil
112
- # API - https://smartcar.com/docs/api#get-engine-oil-life
113
- #
114
- # @return [EngineOil] object
115
- engine_oil: EngineOil,
116
- # Returns the status of the fuel remaining in the vehicle's gas tank.
117
- # API - https://smartcar.com/docs/api#get-fuel-tank
118
- #
119
- # @return [Fuel] object
120
- fuel: Fuel,
121
- # Returns the last known location of the vehicle in geographic coordinates.
122
- # API - https://smartcar.com/docs/api#get-location
123
- #
124
- # @return [Location] object
125
- location: Location,
126
- # Returns the vehicle's last known odometer reading.
127
- # API - https://smartcar.com/docs/api#get-odometer
128
- #
129
- # @return [Odometer] object
130
- odometer: Odometer,
131
- # Returns the air pressure of each of the vehicle's tires.
132
- # API - https://smartcar.com/docs/api#get-tire-pressure
133
- #
134
- # @return [TirePressure] object
135
- tire_pressure: TirePressure,
136
- }.each do |method_name, klass|
137
- define_method method_name do
138
- klass.new(
139
- fetch(
140
- path: klass::PATH.call(id)
141
- )
142
- )
143
- end
105
+ # Methods Used to unlock car
106
+ # API - https://smartcar.com/docs/api#post-security
107
+ #
108
+ # @return [Boolean] true if success
109
+ def unlock!
110
+ lock_or_unlock!(action: Smartcar::UNLOCK)
144
111
  end
145
112
 
146
- #NOTE : The following two also could be defined by metaprogramming,
147
- # But these are items that dont change and hence can be cached in the
148
- # vehicle object.
113
+ # Method used to start charging a car
114
+ #
115
+ #
116
+ # @return [Boolean] true if success
117
+ def start_charge!
118
+ start_or_stop_charge!(action: Smartcar::START_CHARGE)
119
+ end
149
120
 
150
- # Returns the vehicle's manufacturer identifier.
151
- # API - https://smartcar.com/docs/api#get-vin
121
+ # Method used to stop charging a car
152
122
  #
153
- # @return [Vin] object
154
- def vin
155
- @vin ||= Vin.new(
156
- fetch(
157
- path: Vin::PATH.call(id)
158
- )
159
- ).vin
123
+ #
124
+ # @return [Boolean] true if success
125
+ def stop_charge!
126
+ start_or_stop_charge!(action: Smartcar::STOP_CHARGE)
160
127
  end
161
128
 
162
- # Returns the vehicle's model make and year.
163
- # API - https://smartcar.com/docs/api#get-vehicle-attributes
129
+ # Returns make model year and id of the vehicle
130
+ # API - https://smartcar.com/api#get-vehicle-attributes
164
131
  #
165
132
  # @return [VehicleAttributes] object
166
133
  def vehicle_attributes
167
- @vehicle_attributes ||= VehicleAttributes.new(
168
- fetch(
169
- path: PATH.call(id)
170
- )
171
- )
134
+ get_attribute(VehicleAttributes)
135
+ end
136
+
137
+ # Returns the state of charge (SOC) and remaining range of an electric or
138
+ # plug-in hybrid vehicle's battery.
139
+ # API - https://smartcar.com/docs/api#get-ev-battery
140
+ #
141
+ # @return [Battery] object
142
+ def battery
143
+ get_attribute(Battery)
144
+ end
145
+
146
+ # Returns the current charge status of the vehicle.
147
+ # API - https://smartcar.com/docs/api#get-ev-battery
148
+ #
149
+ # @return [Charge] object
150
+ def charge
151
+ get_attribute(Charge)
152
+ end
153
+
154
+ # Returns the remaining life span of a vehicle's engine oil
155
+ # API - https://smartcar.com/docs/api#get-engine-oil-life
156
+ #
157
+ # @return [EngineOil] object
158
+ def engine_oil
159
+ get_attribute(EngineOil)
160
+ end
161
+
162
+ # Returns the status of the fuel remaining in the vehicle's gas tank.
163
+ # API - https://smartcar.com/docs/api#get-fuel-tank
164
+ #
165
+ # @return [Fuel] object
166
+ def fuel
167
+ get_attribute(Fuel)
168
+ end
169
+
170
+ # Returns the last known location of the vehicle in geographic coordinates.
171
+ # API - https://smartcar.com/docs/api#get-location
172
+ #
173
+ # @return [Location] object
174
+ def location
175
+ get_attribute(Location)
176
+ end
177
+
178
+ # Returns the vehicle's last known odometer reading.
179
+ # API - https://smartcar.com/docs/api#get-odometer
180
+ #
181
+ # @return [Odometer] object
182
+ def odometer
183
+ get_attribute(Odometer)
184
+ end
185
+
186
+ # Returns the air pressure of each of the vehicle's tires.
187
+ # API - https://smartcar.com/docs/api#get-tire-pressure
188
+ #
189
+ # @return [TirePressure] object
190
+ def tire_pressure
191
+ get_attribute(TirePressure)
192
+ end
193
+
194
+ # Returns the vehicle's manufacturer identifier (VIN).
195
+ # API - https://smartcar.com/docs/api#get-vin
196
+ #
197
+ # @return [String] Vin of the vehicle.
198
+ def vin
199
+ _object = get_attribute(Vin)
200
+ @vin ||= _object.vin
172
201
  end
173
202
 
174
203
  private
175
204
 
205
+ def allowed_attributes
206
+ @allowed_attributes ||= {
207
+ battery: get_path(Battery),
208
+ charge: get_path(Charge),
209
+ engine_oil: get_path(EngineOil),
210
+ fuel: get_path(Fuel),
211
+ location: get_path(Location),
212
+ odometer: get_path(Odometer),
213
+ permissions: get_path(Permissions),
214
+ tire_pressure: get_path(TirePressure),
215
+ vin: get_path(Vin),
216
+ }
217
+ end
218
+
219
+ def path_to_class
220
+ @path_to_class ||= {
221
+ get_path(Battery) => Battery,
222
+ get_path(Charge) => Charge,
223
+ get_path(EngineOil) => EngineOil,
224
+ get_path(Fuel) => Fuel,
225
+ get_path(Location) => Location,
226
+ get_path(Odometer) => Odometer,
227
+ get_path(Permissions) => Permissions,
228
+ get_path(TirePressure) => TirePressure,
229
+ get_path(Vin) => Vin,
230
+ }
231
+ end
232
+
233
+ # @private
234
+ BatchItemResponse = Struct.new(:body, :status, :headers) do
235
+ def body_with_meta
236
+ body.merge(meta: headers)
237
+ end
238
+ end
239
+
240
+ def get_batch_request_body(attributes)
241
+ attributes = validated_attributes(attributes)
242
+ requests = attributes.each_with_object([]) do |item, requests|
243
+ requests << { path: allowed_attributes[item] }
244
+ end
245
+ { requests: requests }
246
+ end
247
+
248
+ def process_batch_response(responses)
249
+ inverted_map = allowed_attributes.invert
250
+ responses["responses"].each_with_object({}) do |response, result|
251
+ item_response = BatchItemResponse.new(response["body"], response["code"], response["headers"])
252
+ error = get_error(item_response)
253
+ path = response["path"]
254
+ result[inverted_map[path]] = error || get_object(path_to_class[path], item_response.body_with_meta)
255
+ end
256
+ end
257
+
258
+ def validated_attributes(attributes)
259
+ attributes.map!(&:to_sym)
260
+ unsupported_attributes = (attributes - allowed_attributes.keys) || []
261
+ unless unsupported_attributes.empty?
262
+ message = "Unsupported attribute(s) requested in batch - #{unsupported_attributes.join(',')}"
263
+ raise InvalidParameterValue.new, message
264
+ end
265
+ attributes
266
+ end
267
+
268
+ def get_attribute(klass)
269
+ body, meta = fetch(
270
+ path: klass::PATH.call(id)
271
+ )
272
+ get_object(klass, body.merge(meta: meta))
273
+ end
274
+
275
+ def get_object(klass, data)
276
+ klass.new(data)
277
+ end
278
+
279
+ def get_path(klass)
280
+ path = klass::PATH.call(id)
281
+ path.split("/vehicles/#{id}").last
282
+ end
283
+
176
284
  def lock_or_unlock!(action:)
177
- response = post(PATH.call(id) + "/security", {action: action}.to_json)
285
+ response, meta = post(PATH.call(id) + "/security", { action: action })
178
286
  response['status'] == SUCCESS
179
287
  end
180
288
 
181
- class VehicleAttributes
182
- include Utils
183
- attr_accessor :id, :make, :model, :year
289
+ def start_or_stop_charge!(action:)
290
+ response, meta = post(PATH.call(id) + "/charge", { action: action })
291
+ response['status'] == SUCCESS
184
292
  end
185
293
  end
186
294
  end