smartcar 2.5.0 → 3.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 +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +4 -0
- data/.travis.yml +1 -2
- data/Gemfile.lock +56 -21
- data/README.md +38 -36
- data/lib/open_struct_extensions.rb +21 -0
- data/lib/smartcar.rb +158 -57
- data/lib/smartcar/auth_client.rb +154 -0
- data/lib/smartcar/base.rb +12 -22
- data/lib/smartcar/utils.rb +82 -21
- data/lib/smartcar/vehicle.rb +211 -54
- data/lib/smartcar/version.rb +1 -1
- data/lib/smartcar_error.rb +49 -0
- data/ruby-sdk.gemspec +3 -0
- metadata +47 -18
- data/lib/smartcar/battery.rb +0 -15
- data/lib/smartcar/battery_capacity.rb +0 -11
- data/lib/smartcar/charge.rb +0 -15
- data/lib/smartcar/engine_oil.rb +0 -14
- data/lib/smartcar/fuel.rb +0 -17
- data/lib/smartcar/location.rb +0 -12
- data/lib/smartcar/oauth.rb +0 -127
- data/lib/smartcar/odometer.rb +0 -11
- data/lib/smartcar/permissions.rb +0 -11
- data/lib/smartcar/tire_pressure.rb +0 -20
- data/lib/smartcar/user.rb +0 -38
- data/lib/smartcar/vehicle_attributes.rb +0 -14
- data/lib/smartcar/vehicle_utils/actions.rb +0 -61
- data/lib/smartcar/vehicle_utils/batch.rb +0 -83
- data/lib/smartcar/vehicle_utils/data.rb +0 -110
- data/lib/smartcar/vin.rb +0 -12
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Smartcar
|
4
|
+
# AuthClient class to take care of the Oauth 2.0 with Smartcar APIs
|
5
|
+
#
|
6
|
+
class AuthClient
|
7
|
+
include Smartcar::Utils
|
8
|
+
|
9
|
+
attr_reader :redirect_uri, :client_id, :client_secret, :scope, :mode, :flags, :origin
|
10
|
+
|
11
|
+
# Constructor for a client object
|
12
|
+
#
|
13
|
+
# @param [Hash] options
|
14
|
+
# @option options[:client_id] [String] - Client ID, if not passed fallsback to ENV['SMARTCAR_CLIENT_ID']
|
15
|
+
# @option options[:client_secret] [String] - Client Secret, if not passed fallsback to ENV['SMARTCAR_CLIENT_SECRET']
|
16
|
+
# @option options[:redirect_uri] [String] - Redirect URI, if not passed fallsback to ENV['SMARTCAR_REDIRECT_URI']
|
17
|
+
# @option options[:test_mode] [Boolean] - Setting this to 'true' runs it in test mode.
|
18
|
+
#
|
19
|
+
# @return [Smartcar::AuthClient] Returns a Smartcar::AuthClient Object that has other methods
|
20
|
+
def initialize(options)
|
21
|
+
options[:redirect_uri] ||= get_config('SMARTCAR_REDIRECT_URI')
|
22
|
+
options[:client_id] ||= get_config('SMARTCAR_CLIENT_ID')
|
23
|
+
options[:client_secret] ||= get_config('SMARTCAR_CLIENT_SECRET')
|
24
|
+
options[:mode] = options[:test_mode].is_a?(TrueClass) ? TEST : LIVE
|
25
|
+
options[:origin] = ENV['SMARTCAR_AUTH_ORIGIN'] || AUTH_ORIGIN
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
# Generate the OAuth authorization URL.
|
30
|
+
# @param scope [Array<String>] Array of permissions that specify what the user can access
|
31
|
+
# EXAMPLE : ['read_odometer', 'read_vehicle_info', 'required:read_location']
|
32
|
+
# For further details refer to https://smartcar.com/docs/guides/scope/
|
33
|
+
# @param [Hash] options
|
34
|
+
# @option options[:force_prompt] [Boolean] - Setting `force_prompt` to
|
35
|
+
# `true` will show the permissions approval screen on every authentication
|
36
|
+
# attempt, even if the user has previously consented to the exact scope of
|
37
|
+
# permissions.
|
38
|
+
# @option options[:single_select] [Hash] - An optional object that sets the
|
39
|
+
# behavior of the grant dialog displayed to the user. Object can contain two keys :
|
40
|
+
# - enabled - Boolean value, if set to `true`,
|
41
|
+
# `single_select` limits the user to selecting only one vehicle.
|
42
|
+
# - vin - String vin, if set, Smartcar will only authorize the vehicle
|
43
|
+
# with the specified VIN.
|
44
|
+
# See the [Single Select guide](https://smartcar.com/docs/guides/single-select/) for more information.
|
45
|
+
# @option options[:state] [String] - OAuth state parameter passed to the
|
46
|
+
# redirect uri. This parameter may be used for identifying the user who
|
47
|
+
# initiated the request.
|
48
|
+
# @option options[:make_bypass] [String] - `make_bypass' is an optional parameter that allows
|
49
|
+
# users to bypass the car brand selection screen.
|
50
|
+
# For a complete list of supported makes, please see our
|
51
|
+
# [API Reference](https://smartcar.com/docs/api#authorization) documentation.
|
52
|
+
# @option options[:flags] [Hash] - A hash of flag name string as key and a string or boolean value.
|
53
|
+
#
|
54
|
+
# @return [String] Authorization URL string
|
55
|
+
def get_auth_url(scope, options = {})
|
56
|
+
initialize_auth_parameters(scope, options)
|
57
|
+
add_single_select_options(options[:single_select])
|
58
|
+
client.auth_code.authorize_url(@auth_parameters)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Generates the tokens hash using the code returned in oauth process.
|
62
|
+
# @param code [String] This is the code that is returned after user
|
63
|
+
# visits and authorizes on the authorization URL.
|
64
|
+
# @param [Hash] options
|
65
|
+
# @option options[:flags] [Hash] - A hash of flag name string as key and a string or boolean value.
|
66
|
+
#
|
67
|
+
# @return [Hash] Hash of token, refresh token, expiry info and token type
|
68
|
+
def exchange_code(code, options = {})
|
69
|
+
set_token_url(options[:flags])
|
70
|
+
|
71
|
+
token_hash = client.auth_code
|
72
|
+
.get_token(code, redirect_uri: redirect_uri)
|
73
|
+
.to_hash
|
74
|
+
|
75
|
+
JSON.parse(token_hash.to_json, object_class: OpenStruct)
|
76
|
+
rescue OAuth2::Error => e
|
77
|
+
raise build_error(e.response.status, e.response.body, e.response.headers)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Refreshing the access token
|
81
|
+
# @param token [String] refresh_token received during token exchange
|
82
|
+
# @param [Hash] options
|
83
|
+
# @option options[:flags] [Hash] - A hash of flag name string as key and a string or boolean value.
|
84
|
+
#
|
85
|
+
# @return [Hash] Hash of token, refresh token, expiry info and token type
|
86
|
+
def exchange_refresh_token(token, options = {})
|
87
|
+
set_token_url(options[:flags])
|
88
|
+
|
89
|
+
token_object = OAuth2::AccessToken.from_hash(client, { refresh_token: token })
|
90
|
+
token_object = token_object.refresh!
|
91
|
+
|
92
|
+
JSON.parse(token_object.to_hash.to_json, object_class: OpenStruct)
|
93
|
+
rescue OAuth2::Error => e
|
94
|
+
raise build_error(e.response.status, e.response.body, e.response.headers)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Checks if token is expired using Oauth2 classes
|
98
|
+
# @param expires_at [Number] expires_at as time since epoch
|
99
|
+
#
|
100
|
+
# @return [Boolean]
|
101
|
+
def expired?(expires_at)
|
102
|
+
OAuth2::AccessToken.from_hash(client, { expires_at: expires_at }).expired?
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def build_flags(flags)
|
108
|
+
return unless flags
|
109
|
+
|
110
|
+
flags.map { |key, value| "#{key}:#{value}" }.join(' ')
|
111
|
+
end
|
112
|
+
|
113
|
+
def set_token_url(flags)
|
114
|
+
params = {}
|
115
|
+
params[:flags] = build_flags(flags) if flags
|
116
|
+
# Note - The inbuild interface to get the token does not allow any way to pass additional
|
117
|
+
# URL params. Hence building the token URL with the flags and setting it in client.
|
118
|
+
client.options[:token_url] = client.connection.build_url('/oauth/token', params).request_uri
|
119
|
+
end
|
120
|
+
|
121
|
+
def initialize_auth_parameters(scope, options)
|
122
|
+
@auth_parameters = {
|
123
|
+
response_type: CODE,
|
124
|
+
redirect_uri: redirect_uri,
|
125
|
+
mode: mode,
|
126
|
+
scope: scope.join(' ')
|
127
|
+
}
|
128
|
+
@auth_parameters[:approval_prompt] = options[:force_prompt] ? FORCE : AUTO unless options[:force_prompt].nil?
|
129
|
+
@auth_parameters[:state] = options[:state] if options[:state]
|
130
|
+
@auth_parameters[:make] = options[:make_bypass] if options[:make_bypass]
|
131
|
+
@auth_parameters[:flags] = build_flags(options[:flags]) if options[:flags]
|
132
|
+
end
|
133
|
+
|
134
|
+
def add_single_select_options(single_select)
|
135
|
+
return unless single_select
|
136
|
+
|
137
|
+
if single_select[:vin]
|
138
|
+
@auth_parameters[:single_select_vin] = single_select[:vin]
|
139
|
+
@auth_parameters[:single_select] = true
|
140
|
+
elsif !single_select[:enabled].nil?
|
141
|
+
@auth_parameters[:single_select] = single_select[:enabled]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# gets the Oauth Client object
|
146
|
+
#
|
147
|
+
# @return [OAuth2::Client] A Oauth Client object.
|
148
|
+
def client
|
149
|
+
@client ||= OAuth2::Client.new(client_id,
|
150
|
+
client_secret,
|
151
|
+
site: origin)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
data/lib/smartcar/base.rb
CHANGED
@@ -10,14 +10,12 @@ module Smartcar
|
|
10
10
|
|
11
11
|
# Error raised when an invalid parameter is passed.
|
12
12
|
class InvalidParameterValue < StandardError; end
|
13
|
-
# Constant for Bearer auth type
|
14
|
-
BEARER = 'BEARER'
|
15
13
|
# Constant for Basic auth type
|
16
|
-
BASIC = '
|
14
|
+
BASIC = 'Basic'
|
17
15
|
# Number of seconds to wait for response
|
18
16
|
REQUEST_TIMEOUT = 310
|
19
17
|
|
20
|
-
attr_accessor :token, :error, :
|
18
|
+
attr_accessor :token, :error, :unit_system, :version, :auth_type
|
21
19
|
|
22
20
|
%i[get post patch put delete].each do |verb|
|
23
21
|
# meta programming and define all Restful methods.
|
@@ -27,8 +25,7 @@ module Smartcar
|
|
27
25
|
# @return [Hash] The response Json parsed as a hash.
|
28
26
|
define_method verb do |path, data = nil|
|
29
27
|
response = service.send(verb) do |request|
|
30
|
-
request.headers['Authorization'] = "
|
31
|
-
request.headers['Authorization'] = "BASIC #{generate_basic_auth}" if data && data[:auth] == BASIC
|
28
|
+
request.headers['Authorization'] = auth_type == BASIC ? "Basic #{token}" : "Bearer #{token}"
|
32
29
|
request.headers['sc-unit-system'] = unit_system if unit_system
|
33
30
|
request.headers['Content-Type'] = 'application/json'
|
34
31
|
complete_path = "/v#{version}#{path}"
|
@@ -39,38 +36,31 @@ module Smartcar
|
|
39
36
|
request.body = data.to_json if data
|
40
37
|
end
|
41
38
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
[JSON.parse(
|
39
|
+
handle_error(response)
|
40
|
+
# required to handle unsubscribe response
|
41
|
+
body = response.body.empty? ? '{}' : response.body
|
42
|
+
[JSON.parse(body), response.headers]
|
46
43
|
end
|
47
44
|
end
|
48
45
|
|
49
46
|
# This requires a proc 'PATH' to be defined in the class
|
50
47
|
# @param path [String] resource path
|
51
|
-
# @param
|
48
|
+
# @param query_params [Hash] query params
|
52
49
|
# @param auth [String] type of auth
|
53
50
|
#
|
54
51
|
# @return [Object]
|
55
|
-
def fetch(path:,
|
56
|
-
path += "?#{URI.encode_www_form(
|
57
|
-
get(path
|
52
|
+
def fetch(path:, query_params: {})
|
53
|
+
path += "?#{URI.encode_www_form(query_params)}" unless query_params.empty?
|
54
|
+
get(path)
|
58
55
|
end
|
59
56
|
|
60
57
|
private
|
61
58
|
|
62
|
-
# returns auth token for BASIC auth
|
63
|
-
#
|
64
|
-
# @return [String] Base64 encoding of CLIENT:SECRET
|
65
|
-
def generate_basic_auth
|
66
|
-
Base64.strict_encode64("#{get_config('CLIENT_ID')}:#{get_config('CLIENT_SECRET')}")
|
67
|
-
end
|
68
|
-
|
69
59
|
# gets a smartcar API service/client
|
70
60
|
#
|
71
61
|
# @return [OAuth2::AccessToken] An initialized AccessToken instance that acts as service client
|
72
62
|
def service
|
73
|
-
@service ||= Faraday.new(url:
|
63
|
+
@service ||= Faraday.new(url: ENV['SMARTCAR_API_ORIGIN'] || API_ORIGIN, request: { timeout: REQUEST_TIMEOUT })
|
74
64
|
end
|
75
65
|
end
|
76
66
|
end
|
data/lib/smartcar/utils.rb
CHANGED
@@ -13,43 +13,104 @@ module Smartcar
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
# Utility method to return a hash of the isntance variables
|
17
|
-
#
|
18
|
-
# @return [Hash] hash of all instance variables
|
19
|
-
def to_hash
|
20
|
-
instance_variables.each_with_object({}) do |attribute, hash|
|
21
|
-
hash[attribute.to_s.delete('@').to_sym] = instance_variable_get(attribute)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
16
|
# gets a given env variable, checks for existence and throws exception if not present
|
26
17
|
# @param config_name [String] key of the env variable
|
27
18
|
#
|
28
19
|
# @return [String] value of the env variable
|
29
20
|
def get_config(config_name)
|
30
|
-
|
21
|
+
# ENV.MODE is set to test by e2e tests.
|
22
|
+
config_name = "E2E_#{config_name}" if ENV['MODE'] == 'test'
|
31
23
|
raise Smartcar::ConfigNotFound, "Environment variable #{config_name} not found !" unless ENV[config_name]
|
32
24
|
|
33
25
|
ENV[config_name]
|
34
26
|
end
|
35
27
|
|
36
|
-
|
28
|
+
def build_meta(headers)
|
29
|
+
meta_hash = {
|
30
|
+
'sc-data-age' => :data_age,
|
31
|
+
'sc-unit-system' => :unit_system,
|
32
|
+
'sc-request-id' => :request_id
|
33
|
+
}.each_with_object({}) do |(header_name, key), meta|
|
34
|
+
meta[key] = headers[header_name] if headers[header_name]
|
35
|
+
end
|
36
|
+
meta = JSON.parse(meta_hash.to_json, object_class: OpenStruct)
|
37
|
+
meta.data_age &&= DateTime.parse(meta.data_age)
|
38
|
+
|
39
|
+
meta
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_response(body, headers)
|
43
|
+
response = JSON.parse(body.to_json, object_class: OpenStruct)
|
44
|
+
response.meta = build_meta(headers)
|
45
|
+
response
|
46
|
+
end
|
47
|
+
|
48
|
+
def build_aliases(response, aliases)
|
49
|
+
(aliases || []).each do |original_name, alias_name|
|
50
|
+
response.send("#{alias_name}=".to_sym, response.send(original_name.to_sym))
|
51
|
+
end
|
52
|
+
|
53
|
+
response
|
54
|
+
end
|
55
|
+
|
56
|
+
def build_error(status, body_string, headers)
|
57
|
+
content_type = headers['content-type'] || ''
|
58
|
+
return SmartcarError.new(status, body_string, headers) unless content_type.include?('application/json')
|
59
|
+
|
60
|
+
begin
|
61
|
+
parsed_body = JSON.parse(body_string, { symbolize_names: true })
|
62
|
+
rescue StandardError => e
|
63
|
+
return SmartcarError.new(
|
64
|
+
status,
|
65
|
+
{
|
66
|
+
message: e.message,
|
67
|
+
type: 'SDK_ERROR'
|
68
|
+
},
|
69
|
+
headers
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
return SmartcarError.new(status, parsed_body, headers) if parsed_body[:error] || parsed_body[:type]
|
74
|
+
|
75
|
+
SmartcarError.new(status, parsed_body.merge({ type: 'SDK_ERROR' }), headers)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Given the response from smartcar API, throws an error if needed
|
37
79
|
# @param response [Object] response Object with status and body
|
38
|
-
|
39
|
-
# @return [Object] nil OR Error object
|
40
|
-
def get_error(response)
|
80
|
+
def handle_error(response)
|
41
81
|
status = response.status
|
42
82
|
return nil if [200, 204].include?(status)
|
43
|
-
return Smartcar::ServiceUnavailableError.new("Service Unavailable - #{response.body}") if status == 404
|
44
|
-
return Smartcar::BadRequestError.new("Bad Request - #{response.body}") if status == 400
|
45
|
-
return Smartcar::AuthenticationError.new('Authentication error') if status == 401
|
46
83
|
|
47
|
-
|
84
|
+
raise build_error(response.status, response.body, response.headers)
|
85
|
+
end
|
86
|
+
|
87
|
+
def process_batch_response(response_body, response_headers)
|
88
|
+
response_object = OpenStruct.new
|
89
|
+
response_body['responses'].each do |item|
|
90
|
+
attribute_name = convert_path_to_attribute(item['path'])
|
91
|
+
aliases = Vehicle::METHODS[attribute_name.to_sym][:aliases]
|
92
|
+
# merging the top level request headers and separate headers for each item of batch
|
93
|
+
headers = response_headers.merge(item['headers'])
|
94
|
+
response = if [200, 204].include?(item['code'])
|
95
|
+
build_aliases(build_response(item['body'], headers), aliases)
|
96
|
+
else
|
97
|
+
build_error(item['code'], item['body'].to_json, headers)
|
98
|
+
end
|
99
|
+
response_object.define_singleton_method attribute_name do
|
100
|
+
raise response if response.is_a?(SmartcarError)
|
101
|
+
|
102
|
+
response
|
103
|
+
end
|
104
|
+
end
|
105
|
+
response_object
|
48
106
|
end
|
49
107
|
|
50
|
-
|
51
|
-
|
52
|
-
|
108
|
+
# takes a path and converts it to the keys we use.
|
109
|
+
# EX - '/charge' -> :charge, '/battery/capacity' -> :battery_capacity
|
110
|
+
def convert_path_to_attribute(path)
|
111
|
+
return :attributes if path == '/'
|
112
|
+
|
113
|
+
path.split('/').reject(&:empty?).join('_').to_sym
|
53
114
|
end
|
54
115
|
end
|
55
116
|
end
|
data/lib/smartcar/vehicle.rb
CHANGED
@@ -8,74 +8,231 @@ module Smartcar
|
|
8
8
|
#
|
9
9
|
# @attr [String] token Access token used to connect to Smartcar API.
|
10
10
|
# @attr [String] id Smartcar vehicle ID.
|
11
|
-
# @attr [
|
11
|
+
# @attr [Hash] options
|
12
|
+
# @attr unit_system [String] Unit system to represent the data in, defaults to Imperial
|
13
|
+
# @attr version [String] API version to be used.
|
12
14
|
class Vehicle < Base
|
13
|
-
include VehicleUtils::Data
|
14
|
-
include VehicleUtils::Actions
|
15
|
-
include VehicleUtils::Batch
|
16
|
-
# Path for hitting compatibility end point
|
17
|
-
COMPATIBLITY_PATH = '/compatibility'
|
18
|
-
|
19
|
-
# Path for hitting vehicle ids end point
|
20
|
-
PATH = proc { |id| "/vehicles/#{id}" }
|
21
|
-
|
22
15
|
attr_reader :id
|
23
16
|
|
24
|
-
|
17
|
+
# @private
|
18
|
+
METHODS = {
|
19
|
+
permissions: { path: proc { |id| "/vehicles/#{id}/permissions" }, skip: true },
|
20
|
+
attributes: { path: proc { |id| "/vehicles/#{id}" } },
|
21
|
+
battery: {
|
22
|
+
path: proc { |id| "/vehicles/#{id}/battery" },
|
23
|
+
aliases: { 'percentRemaining' => 'percentage_remaining' }
|
24
|
+
},
|
25
|
+
battery_capacity: { path: proc { |id| "/vehicles/#{id}/battery/capacity" } },
|
26
|
+
charge: {
|
27
|
+
path: proc { |id| "/vehicles/#{id}/charge" },
|
28
|
+
aliases: { 'isPluggedIn' => 'is_plugged_in?' }
|
29
|
+
},
|
30
|
+
engine_oil: {
|
31
|
+
path: proc { |id| "/vehicles/#{id}/engine/oil" },
|
32
|
+
aliases: { 'lifeRemaining' => 'life_remaining' }
|
33
|
+
},
|
34
|
+
fuel: {
|
35
|
+
path: proc { |id| "/vehicles/#{id}/fuel" },
|
36
|
+
aliases: {
|
37
|
+
'amountRemaining' => 'amount_remaining',
|
38
|
+
'percentRemaining' => 'percent_remaining'
|
39
|
+
}
|
40
|
+
},
|
41
|
+
location: { path: proc { |id| "/vehicles/#{id}/location" } },
|
42
|
+
odometer: { path: proc { |id| "/vehicles/#{id}/odometer" } },
|
43
|
+
tire_pressure: {
|
44
|
+
path: proc { |id| "/vehicles/#{id}/tires/pressure" },
|
45
|
+
aliases: {
|
46
|
+
'backLeft' => 'back_left',
|
47
|
+
'backRight' => 'back_right',
|
48
|
+
'frontLeft' => 'front_left',
|
49
|
+
'frontRight' => 'front_right'
|
50
|
+
}
|
51
|
+
},
|
52
|
+
vin: { path: proc { |id| "/vehicles/#{id}/vin" } },
|
53
|
+
disconnect!: { type: :delete, path: proc { |id| "/vehicles/#{id}/application" } },
|
54
|
+
lock!: { type: :post, path: proc { |id| "/vehicles/#{id}/security" }, body: { action: 'LOCK' } },
|
55
|
+
unlock!: { type: :post, path: proc { |id| "/vehicles/#{id}/security" }, body: { action: 'UNLOCK' } },
|
56
|
+
start_charge!: { type: :post, path: proc { |id| "/vehicles/#{id}/charge" }, body: { action: 'START' } },
|
57
|
+
stop_charge!: { type: :post, path: proc { |id| "/vehicles/#{id}/charge" }, body: { action: 'STOP' } },
|
58
|
+
subscribe!: {
|
59
|
+
type: :post,
|
60
|
+
path: proc { |id, webhook_id| "/vehicles/#{id}/webhooks/#{webhook_id}" },
|
61
|
+
aliases: {
|
62
|
+
'webhookId' => 'webhook_id',
|
63
|
+
'vehicleId' => 'vehicle_id'
|
64
|
+
},
|
65
|
+
skip: true
|
66
|
+
},
|
67
|
+
unsubscribe!: { type: :post, path: proc { |id, webhook_id|
|
68
|
+
"/vehicles/#{id}/webhooks/#{webhook_id}"
|
69
|
+
}, skip: true }
|
70
|
+
}.freeze
|
71
|
+
|
72
|
+
def initialize(token:, id:, options: { unit_system: METRIC, version: Smartcar.get_api_version })
|
25
73
|
super
|
26
|
-
|
74
|
+
@token = token
|
75
|
+
@id = id
|
76
|
+
@unit_system = options[:unit_system]
|
77
|
+
@version = options[:version]
|
78
|
+
|
79
|
+
raise InvalidParameterValue.new, "Invalid Units provided : #{@unit_system}" unless UNITS.include?(@unit_system)
|
27
80
|
raise InvalidParameterValue.new, 'Vehicle ID (id) is a required field' if id.nil?
|
28
81
|
raise InvalidParameterValue.new, 'Access Token(token) is a required field' if token.nil?
|
82
|
+
end
|
29
83
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
84
|
+
# @!method attributes()
|
85
|
+
# Returns make model year and id of the vehicle
|
86
|
+
#
|
87
|
+
# API Documentation - https://smartcar.com/api#get-vehicle-attributes
|
88
|
+
#
|
89
|
+
# @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/api#get-vehicle-attributes
|
90
|
+
# and a meta attribute with the relevant items from response headers.
|
91
|
+
|
92
|
+
# @!method battery()
|
93
|
+
# Returns the state of charge (SOC) and remaining range of an electric or plug-in hybrid vehicle's battery.
|
94
|
+
#
|
95
|
+
# API Documentation https://smartcar.com/docs/api#get-ev-battery
|
96
|
+
#
|
97
|
+
# @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-ev-battery
|
98
|
+
# and a meta attribute with the relevant items from response headers.
|
99
|
+
|
100
|
+
# @!method battery_capacity()
|
101
|
+
# Returns the capacity of an electric or plug-in hybrid vehicle's battery.
|
102
|
+
#
|
103
|
+
# API Documentation https://smartcar.com/docs/api#get-ev-battery-capacity
|
104
|
+
#
|
105
|
+
# @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-ev-battery-capacity
|
106
|
+
# and a meta attribute with the relevant items from response headers.
|
107
|
+
|
108
|
+
# @!method charge()
|
109
|
+
# Returns the current charge status of the vehicle.
|
110
|
+
#
|
111
|
+
# API Documentation https://smartcar.com/docs/api#get-ev-battery
|
112
|
+
#
|
113
|
+
# @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-ev-battery
|
114
|
+
# and a meta attribute with the relevant items from response headers.
|
115
|
+
|
116
|
+
# @!method engine_oil()
|
117
|
+
# Returns the remaining life span of a vehicle's engine oil
|
118
|
+
#
|
119
|
+
# API Documentation https://smartcar.com/docs/api#get-engine-oil-life
|
120
|
+
#
|
121
|
+
# @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-engine-oil-life
|
122
|
+
# and a meta attribute with the relevant items from response headers.
|
123
|
+
|
124
|
+
# @!method fuel()
|
125
|
+
# Returns the status of the fuel remaining in the vehicle's gas tank.
|
126
|
+
#
|
127
|
+
# API Documentation https://smartcar.com/docs/api#get-fuel-tank
|
128
|
+
#
|
129
|
+
# @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-fuel-tank
|
130
|
+
# and a meta attribute with the relevant items from response headers.
|
131
|
+
|
132
|
+
# @!method location()
|
133
|
+
# Returns the last known location of the vehicle in geographic coordinates.
|
134
|
+
#
|
135
|
+
# API Documentation https://smartcar.com/docs/api#get-location
|
136
|
+
#
|
137
|
+
# @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-location
|
138
|
+
# and a meta attribute with the relevant items from response headers.
|
139
|
+
|
140
|
+
# @!method odometer()
|
141
|
+
# Returns the vehicle's last known odometer reading.
|
142
|
+
#
|
143
|
+
# API Documentation https://smartcar.com/docs/api#get-odometer
|
144
|
+
#
|
145
|
+
# @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-odometer
|
146
|
+
# and a meta attribute with the relevant items from response headers.
|
147
|
+
|
148
|
+
# @!method tire_pressure()
|
149
|
+
# Returns the air pressure of each of the vehicle's tires.
|
150
|
+
#
|
151
|
+
# API Documentation https://smartcar.com/docs/api#get-tire-pressure
|
152
|
+
#
|
153
|
+
# @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-tire-pressure
|
154
|
+
# and a meta attribute with the relevant items from response headers.
|
155
|
+
|
156
|
+
# @!method vin()
|
157
|
+
# Returns the vehicle's manufacturer identifier (VIN).
|
158
|
+
#
|
159
|
+
# API Documentation https://smartcar.com/docs/api#get-vin
|
160
|
+
#
|
161
|
+
# @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-vin
|
162
|
+
# and a meta attribute with the relevant items from response headers.
|
163
|
+
|
164
|
+
# NOTES :
|
165
|
+
# - We only generate the methods where there is no query string or additional options considering thats
|
166
|
+
# the majority, for all the ones that require parameters, write them separately.
|
167
|
+
# Ex. permissions, subscribe, unsubscribe
|
168
|
+
# - The following snippet generates methods dynamically , but if we are adding a new item,
|
169
|
+
# make sure we also add the doc for it.
|
170
|
+
METHODS.each do |method, item|
|
171
|
+
# We add these to the METHODS object to keep it in one place, but mark them to be skipped
|
172
|
+
# for dynamic generation
|
173
|
+
next if item[:skip]
|
174
|
+
|
175
|
+
define_method method do
|
176
|
+
body, headers = case item[:type]
|
177
|
+
when :post
|
178
|
+
post(item[:path].call(id), item[:body])
|
179
|
+
when :delete
|
180
|
+
delete(item[:path].call(id))
|
181
|
+
else
|
182
|
+
fetch(path: item[:path].call(id))
|
183
|
+
end
|
184
|
+
build_aliases(build_response(body, headers), item[:aliases])
|
185
|
+
end
|
34
186
|
end
|
35
187
|
|
36
|
-
#
|
37
|
-
# API - https://smartcar.com/docs/api#get-
|
38
|
-
#
|
39
|
-
# @param
|
40
|
-
#
|
41
|
-
# @return [
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
)
|
47
|
-
response['vehicles']
|
188
|
+
# Method to fetch the list of permissions that this application has been granted for this vehicle.
|
189
|
+
# API - https://smartcar.com/docs/api#get-application-permissions
|
190
|
+
#
|
191
|
+
# @param paging [Hash] Optional filter parameters (check documentation)
|
192
|
+
#
|
193
|
+
# @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-application-permissions
|
194
|
+
# and a meta attribute with the relevant items from response headers.
|
195
|
+
def permissions(paging = {})
|
196
|
+
response, headers = fetch(path: METHODS.dig(:permissions, :path).call(id), query_params: paging)
|
197
|
+
build_response(response, headers)
|
48
198
|
end
|
49
199
|
|
50
|
-
#
|
51
|
-
#
|
52
|
-
# @param
|
53
|
-
#
|
54
|
-
# @
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
def self.compatible?(vin:, scope:, country: 'US', version: Smartcar.get_api_version)
|
60
|
-
raise InvalidParameterValue.new, 'vin is a required field' if vin.nil?
|
61
|
-
raise InvalidParameterValue.new, 'scope is a required field' if scope.nil?
|
62
|
-
|
63
|
-
response, = new(token: 'none', id: 'none', version: version).fetch(
|
64
|
-
path: COMPATIBLITY_PATH,
|
65
|
-
options: {
|
66
|
-
vin: vin,
|
67
|
-
scope: scope.join(' '),
|
68
|
-
country: country
|
69
|
-
},
|
70
|
-
auth: BASIC
|
71
|
-
)
|
72
|
-
response['compatible']
|
200
|
+
# Subscribe the vehicle to given webhook Id.
|
201
|
+
#
|
202
|
+
# @param webhook_id [String] Webhook id to subscribe to
|
203
|
+
#
|
204
|
+
# @return [OpenStruct] And object representing the JSON response and a meta attribute
|
205
|
+
# with the relevant items from response headers.
|
206
|
+
def subscribe!(webhook_id)
|
207
|
+
response, headers = post(METHODS.dig(:subscribe!, :path).call(id, webhook_id), {})
|
208
|
+
build_aliases(build_response(response, headers), METHODS.dig(:subscribe!, :aliases))
|
73
209
|
end
|
74
210
|
|
75
|
-
|
211
|
+
# Unubscribe the vehicle from given webhook Id.
|
212
|
+
#
|
213
|
+
# @param amt [String] Application management token
|
214
|
+
# @param webhook_id [String] Webhook id to subscribe to
|
215
|
+
#
|
216
|
+
# @return [OpenStruct] Meta attribute with the relevant items from response headers.
|
217
|
+
def unsubscribe!(amt, webhook_id)
|
218
|
+
# swapping off the token with amt for unsubscribe.
|
219
|
+
access_token = token
|
220
|
+
self.token = amt
|
221
|
+
response, headers = delete(METHODS.dig(:unsubscribe!, :path).call(id, webhook_id))
|
222
|
+
self.token = access_token
|
223
|
+
build_response(response, headers)
|
224
|
+
end
|
76
225
|
|
77
|
-
|
78
|
-
|
226
|
+
# Method to get batch requests.
|
227
|
+
# API - https://smartcar.com/docs/api#post-batch-request
|
228
|
+
# @param paths [Array] Array of paths as strings. Ex ['/battery', '/odometer']
|
229
|
+
#
|
230
|
+
# @return [OpenStruct] Object with one attribute per requested path that returns
|
231
|
+
# an OpenStruct object of the requested attribute or taises if it is an error.
|
232
|
+
def batch(paths)
|
233
|
+
request_body = { requests: paths.map { |path| { path: path } } }
|
234
|
+
response, headers = post("/vehicles/#{id}/batch", request_body)
|
235
|
+
process_batch_response(response, headers)
|
79
236
|
end
|
80
237
|
end
|
81
238
|
end
|