smartcar 3.2.0 → 3.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: af85a8f04bb5ecfd37182ae539ef60e5ca4e9487edf5db544109417df43d82d0
4
- data.tar.gz: 5f2ccac9657e5e5b122b32943fa48845aa0acd5319dccc8836fd6b3cbf063ced
3
+ metadata.gz: fe729c83b0d759f2d787090e3f815ec9d97cd0b283585f8775ebe16e61b16e56
4
+ data.tar.gz: '029284bb31e8363b2cd0fe751705749e19365fb5692cbc3054325d648440117e'
5
5
  SHA512:
6
- metadata.gz: 47abd5b611ddc7089be88614c2b79a4d2c33db498f1254eea55f2ed1426b795752b47b7e08e9ac8825086790313738f16affdf536ebed450963b36fd0d1305e3
7
- data.tar.gz: 3ffef0a6f8f06da4c9f70a6798a9f701521831cdc30bbc183e613515b2b9520a492bd2d9e686a5c4cca0421d42def98bc0ccb6c4239cabd1d7b250ba5416f31d
6
+ metadata.gz: 6e469fcb92c39256e4a779476217fdb7cd5cce276af14d61ac9a51054e5d5e485f70e8f7886fb1852d2fb9d8128edd9b85164303f7c36fdca9d919f50bb0f4cb
7
+ data.tar.gz: faf78f8b977e3548e9f44fa45a59ce3b26cc89de53e6d85f3a8bc5391ced3031bdb2801696cb237cc2bb8c801bf85f7d2dd5bb6f15ccf2904430c65549c8ac86
data/.rubocop.yml CHANGED
@@ -12,7 +12,7 @@ Naming/AccessorMethodName:
12
12
  Enabled: false
13
13
 
14
14
  # Disabling this until we figure out a better way than using openstruct
15
- # Currently we use open struct because this gives us an object representing the JSON
15
+ # Currently we use open struct because this gives us an object representing the JSON
16
16
  # with accessor style methods.
17
17
  Style/OpenStructUse:
18
18
  Enabled: false
@@ -30,3 +30,9 @@ Metrics/MethodLength:
30
30
 
31
31
  Metrics/ClassLength:
32
32
  Max: 200
33
+
34
+ Metrics/CyclomaticComplexity:
35
+ Max: 10
36
+
37
+ Metrics/PerceivedComplexity:
38
+ Max: 10
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smartcar (3.2.0)
4
+ smartcar (3.3.1)
5
5
  oauth2 (~> 1.4)
6
6
  recursive-open-struct (~> 1.1.3)
7
7
 
data/README.md CHANGED
@@ -31,6 +31,7 @@ not have access to the dashboard, please
31
31
 
32
32
  - Create a new `AuthClient` object with your `client_id`, `client_secret`,
33
33
  `redirect_uri`.
34
+ -
34
35
  - Redirect the user to Smartcar Connect using `get_auth_url` with required `scope` or with one
35
36
  of our frontend SDKs.
36
37
  - The user will login, and then accept or deny your `scope`'s permissions.
@@ -76,6 +77,9 @@ Setup the environment variables for SMARTCAR_CLIENT_ID, SMARTCAR_CLIENT_SECRET a
76
77
  export SMARTCAR_CLIENT_ID=<client id>
77
78
  export SMARTCAR_CLIENT_SECRET=<client secret>
78
79
  export SMARTCAR_REDIRECT_URI=<redirect URI>
80
+ # Optional ENV variables
81
+ export SMARTCAR_CONNECT_ORIGIN=(default_value: connect.smartcar.com): Used as the host for the URL that starts the Connect/OAuth2 flow
82
+ export SMARTCAR_AUTH_ORIGIN=(default_value: auth.smartcar.com): Used as the host for the token exchange requests
79
83
  ```
80
84
 
81
85
  Example Usage for calling the reports API with oAuth token
@@ -101,7 +105,7 @@ Example Usage for calling the reports API with oAuth token
101
105
  Example Usage for oAuth -
102
106
  ```ruby
103
107
  # To get the redirect URL :
104
- 2.5.5 :002 > options = {test_mode: true}
108
+ 2.5.5 :002 > options = {mode: 'test'}
105
109
  2.5.5 :003 > require 'smartcar'
106
110
  2.5.5 :004 > client = Smartcar::AuthClient.new(options)
107
111
  2.5.5 :005 > url = client.get_auth_url(["read_battery","read_charge","read_fuel","read_location","control_security","read_odometer","read_tires","read_vin","read_vehicle_info"], {flags: ["country:DE"]})
@@ -6,7 +6,7 @@ module Smartcar
6
6
  class AuthClient
7
7
  include Smartcar::Utils
8
8
 
9
- attr_reader :redirect_uri, :client_id, :client_secret, :scope, :mode, :flags, :origin
9
+ attr_reader :redirect_uri, :client_id, :client_secret, :scope, :mode, :flags, :auth_origin, :connect_origin
10
10
 
11
11
  # Constructor for a client object
12
12
  #
@@ -14,15 +14,18 @@ module Smartcar
14
14
  # @option options[:client_id] [String] - Client ID, if not passed fallsback to ENV['SMARTCAR_CLIENT_ID']
15
15
  # @option options[:client_secret] [String] - Client Secret, if not passed fallsback to ENV['SMARTCAR_CLIENT_SECRET']
16
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
- #
17
+ # @option options[:test_mode] [Boolean] - [DEPRECATED], please use `mode` instead.
18
+ # Launch Smartcar Connect in [test mode](https://smartcar.com/docs/guides/testing/).
19
+ # @option options[:mode] [String] - Determine what mode Smartcar Connect should be launched in.
20
+ # Should be one of test, live or simulated.
19
21
  # @return [Smartcar::AuthClient] Returns a Smartcar::AuthClient Object that has other methods
20
22
  def initialize(options)
21
23
  options[:redirect_uri] ||= get_config('SMARTCAR_REDIRECT_URI')
22
24
  options[:client_id] ||= get_config('SMARTCAR_CLIENT_ID')
23
25
  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
+ options[:auth_origin] = ENV['SMARTCAR_AUTH_ORIGIN'] || AUTH_ORIGIN
27
+ options[:connect_origin] = ENV['SMARTCAR_CONNECT_ORIGIN'] || CONNECT_ORIGIN
28
+ options[:mode] = determine_mode(options[:test_mode], options[:mode]) || 'live'
26
29
  super
27
30
  end
28
31
 
@@ -55,7 +58,7 @@ module Smartcar
55
58
  def get_auth_url(scope, options = {})
56
59
  initialize_auth_parameters(scope, options)
57
60
  add_single_select_options(options[:single_select])
58
- client.auth_code.authorize_url(@auth_parameters)
61
+ connect_client.auth_code.authorize_url(@auth_parameters)
59
62
  end
60
63
 
61
64
  # Generates the tokens hash using the code returned in oauth process.
@@ -68,9 +71,9 @@ module Smartcar
68
71
  def exchange_code(code, options = {})
69
72
  set_token_url(options[:flags])
70
73
 
71
- token_hash = client.auth_code
72
- .get_token(code, redirect_uri: redirect_uri)
73
- .to_hash
74
+ token_hash = auth_client.auth_code
75
+ .get_token(code, redirect_uri: redirect_uri)
76
+ .to_hash
74
77
 
75
78
  json_to_ostruct(token_hash)
76
79
  rescue OAuth2::Error => e
@@ -86,7 +89,7 @@ module Smartcar
86
89
  def exchange_refresh_token(token, options = {})
87
90
  set_token_url(options[:flags])
88
91
 
89
- token_object = OAuth2::AccessToken.from_hash(client, { refresh_token: token })
92
+ token_object = OAuth2::AccessToken.from_hash(auth_client, { refresh_token: token })
90
93
  token_object = token_object.refresh!
91
94
 
92
95
  json_to_ostruct(token_object.to_hash)
@@ -99,7 +102,7 @@ module Smartcar
99
102
  #
100
103
  # @return [Boolean]
101
104
  def expired?(expires_at)
102
- OAuth2::AccessToken.from_hash(client, { expires_at: expires_at }).expired?
105
+ OAuth2::AccessToken.from_hash(auth_client, { expires_at: expires_at }).expired?
103
106
  end
104
107
 
105
108
  private
@@ -115,7 +118,7 @@ module Smartcar
115
118
  params[:flags] = build_flags(flags) if flags
116
119
  # Note - The inbuild interface to get the token does not allow any way to pass additional
117
120
  # 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
121
+ auth_client.options[:token_url] = auth_client.connection.build_url('/oauth/token', params).request_uri
119
122
  end
120
123
 
121
124
  def initialize_auth_parameters(scope, options)
@@ -142,13 +145,22 @@ module Smartcar
142
145
  end
143
146
  end
144
147
 
145
- # gets the Oauth Client object
148
+ # gets the Oauth Client object configured with auth.connect.smartcar.com
149
+ #
150
+ # @return [OAuth2::Client] A Oauth Client object.
151
+ def auth_client
152
+ @auth_client ||= OAuth2::Client.new(client_id,
153
+ client_secret,
154
+ site: auth_origin)
155
+ end
156
+
157
+ # gets the Oauth Client object configured with connect.smartcar.com
146
158
  #
147
159
  # @return [OAuth2::Client] A Oauth Client object.
148
- def client
149
- @client ||= OAuth2::Client.new(client_id,
150
- client_secret,
151
- site: origin)
160
+ def connect_client
161
+ @connect_client ||= OAuth2::Client.new(client_id,
162
+ client_secret,
163
+ site: connect_origin)
152
164
  end
153
165
  end
154
166
  end
data/lib/smartcar/base.rb CHANGED
@@ -22,7 +22,7 @@ module Smartcar
22
22
  # @param data [Hash] request body if needed.
23
23
  #
24
24
  # @return [Hash] The response Json parsed as a hash.
25
- define_method verb do |path, data = nil, headers = {}|
25
+ define_method verb do |path, query_params = {}, data = nil, headers = {}|
26
26
  response = service.send(verb) do |request|
27
27
  request_headers = {}
28
28
  request_headers['Authorization'] = auth_type == BASIC ? "Basic #{token}" : "Bearer #{token}"
@@ -32,6 +32,7 @@ module Smartcar
32
32
  "Smartcar/#{VERSION} (#{RbConfig::CONFIG['host_os']}; #{RbConfig::CONFIG['arch']}) Ruby v#{RUBY_VERSION}"
33
33
  request.headers = request_headers.merge(headers)
34
34
  complete_path = "/v#{version}#{path}"
35
+ complete_path += "?#{URI.encode_www_form(query_params.compact)}" unless query_params.empty?
35
36
  if verb == :get
36
37
  request.url complete_path, data
37
38
  else
@@ -46,17 +47,6 @@ module Smartcar
46
47
  end
47
48
  end
48
49
 
49
- # This requires a proc 'PATH' to be defined in the class
50
- # @param path [String] resource path
51
- # @param query_params [Hash] query params
52
- # @param auth [String] type of auth
53
- #
54
- # @return [Object]
55
- def fetch(path:, query_params: {})
56
- path += "?#{URI.encode_www_form(query_params)}" unless query_params.empty?
57
- get(path)
58
- end
59
-
60
50
  private
61
51
 
62
52
  # gets a smartcar API service/client
@@ -127,5 +127,25 @@ module Smartcar
127
127
 
128
128
  path.split('/').reject(&:empty?).join('_').to_sym
129
129
  end
130
+
131
+ # takes query parameters and returns them as a string
132
+ # EX - {'country': 'DE', 'flags': true} -> "county:DE flags:true"
133
+ def stringify_params(query_params)
134
+ query_params&.map { |key, value| "#{key}:#{value}" }&.join(' ')
135
+ end
136
+
137
+ def determine_mode(test_mode, mode)
138
+ unless mode.nil?
139
+ unless %w[test live simulated].include? mode
140
+ raise 'The "mode" parameter MUST be one of the following: \'test\', \'live\', \'simulated\''
141
+ end
142
+
143
+ return mode
144
+ end
145
+ return if test_mode.nil?
146
+
147
+ warn '[DEPRECATION] The "test_mode" parameter is deprecated, please use the "mode" parameter instead.'
148
+ test_mode.is_a?(TrueClass) ? 'test' : 'live'
149
+ end
130
150
  end
131
151
  end
@@ -11,6 +11,7 @@ module Smartcar
11
11
  # @attr [Hash] options
12
12
  # @attr unit_system [String] Unit system to represent the data in, defaults to Imperial
13
13
  # @attr version [String] API version to be used.
14
+ # @attr flags [Hash] Object of flags where key is the name of the flag and value is string or boolean value.
14
15
  # @attr service [Faraday::Connection] An optional connection object to be used for requests.
15
16
  class Vehicle < Base
16
17
  attr_reader :id
@@ -77,6 +78,7 @@ module Smartcar
77
78
  @unit_system = options[:unit_system] || METRIC
78
79
  @version = options[:version] || Smartcar.get_api_version
79
80
  @service = options[:service]
81
+ @query_params = { flags: stringify_params(options[:flags]) }
80
82
 
81
83
  raise InvalidParameterValue.new, "Invalid Units provided : #{@unit_system}" unless UNITS.include?(@unit_system)
82
84
  raise InvalidParameterValue.new, 'Vehicle ID (id) is a required field' if id.nil?
@@ -177,11 +179,11 @@ module Smartcar
177
179
  define_method method do
178
180
  body, headers = case item[:type]
179
181
  when :post
180
- post(item[:path].call(id), item[:body])
182
+ post(item[:path].call(id), @query_params, item[:body])
181
183
  when :delete
182
- delete(item[:path].call(id))
184
+ delete(item[:path].call(id), @query_params)
183
185
  else
184
- fetch(path: item[:path].call(id))
186
+ get(item[:path].call(id), @query_params)
185
187
  end
186
188
  build_aliases(build_response(body, headers), item[:aliases])
187
189
  end
@@ -195,7 +197,7 @@ module Smartcar
195
197
  # @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-application-permissions
196
198
  # and a meta attribute with the relevant items from response headers.
197
199
  def permissions(paging = {})
198
- response, headers = fetch(path: METHODS.dig(:permissions, :path).call(id), query_params: paging)
200
+ response, headers = get(METHODS.dig(:permissions, :path).call(id), @query_params.merge(paging))
199
201
  build_response(response, headers)
200
202
  end
201
203
 
@@ -206,7 +208,7 @@ module Smartcar
206
208
  # @return [OpenStruct] An object representing the JSON response and a meta attribute
207
209
  # with the relevant items from response headers.
208
210
  def subscribe!(webhook_id)
209
- response, headers = post(METHODS.dig(:subscribe!, :path).call(id, webhook_id), {})
211
+ response, headers = post(METHODS.dig(:subscribe!, :path).call(id, webhook_id), @query_params)
210
212
  build_aliases(build_response(response, headers), METHODS.dig(:subscribe!, :aliases))
211
213
  end
212
214
 
@@ -220,7 +222,8 @@ module Smartcar
220
222
  # swapping off the token with amt for unsubscribe.
221
223
  access_token = token
222
224
  self.token = amt
223
- response, headers = delete(METHODS.dig(:unsubscribe!, :path).call(id, webhook_id))
225
+ response, headers = delete(METHODS.dig(:unsubscribe!, :path).call(id, webhook_id),
226
+ @query_params)
224
227
  self.token = access_token
225
228
  build_response(response, headers)
226
229
  end
@@ -233,7 +236,7 @@ module Smartcar
233
236
  # an OpenStruct object of the requested attribute or taises if it is an error.
234
237
  def batch(paths)
235
238
  request_body = { requests: paths.map { |path| { path: path } } }
236
- response, headers = post("/vehicles/#{id}/batch", request_body)
239
+ response, headers = post("/vehicles/#{id}/batch", @query_params, request_body)
237
240
  process_batch_response(response, headers)
238
241
  end
239
242
 
@@ -249,7 +252,7 @@ module Smartcar
249
252
  # response body and a "meta" attribute with the relevant items from response headers.
250
253
  def request(method, path, body = {}, headers = {})
251
254
  path = "/vehicles/#{id}/#{path}"
252
- raw_response, headers = send(method.downcase, path, body, headers)
255
+ raw_response, headers = send(method.downcase, path, @query_params, body, headers)
253
256
  meta = build_meta(headers)
254
257
  json_to_ostruct({ body: raw_response, meta: meta })
255
258
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Smartcar
4
4
  # Gem current version number
5
- VERSION = '3.2.0'
5
+ VERSION = '3.3.1'
6
6
  end
data/lib/smartcar.rb CHANGED
@@ -22,7 +22,8 @@ module Smartcar
22
22
  }.freeze
23
23
 
24
24
  # Path for smartcar oauth
25
- AUTH_ORIGIN = 'https://connect.smartcar.com'
25
+ CONNECT_ORIGIN = 'https://connect.smartcar.com'
26
+ AUTH_ORIGIN = 'https://auth.smartcar.com'
26
27
  %w[success code test live force auto metric imperial].each do |constant|
27
28
  # Constant to represent the value
28
29
  const_set(constant.upcase, constant.freeze)
@@ -38,6 +39,7 @@ module Smartcar
38
39
  @api_version = '2.0'
39
40
 
40
41
  class << self
42
+ include Smartcar::Utils
41
43
  # Module method Used to set api version to be used.
42
44
  # This method can be used at the top to set the version and any
43
45
  # following request will use the version set here unless overridden
@@ -67,7 +69,10 @@ module Smartcar
67
69
  # @option options [String] :client_secret Client Secret that overrides ENV
68
70
  # @option options [String] :version API version to use, defaults to what is globally set
69
71
  # @option options [Hash] :flags A hash of flag name string as key and a string or boolean value.
70
- # @option options [Boolean] :test_mode Whether to use test mode or not.
72
+ # @option options[Boolean] :test_mode [DEPRECATED], please use `mode` instead.
73
+ # Launch Smartcar Connect in test mode(https://smartcar.com/docs/guides/testing/).
74
+ # @option options [String] :mode Determine what mode Smartcar Connect should be launched in.
75
+ # Should be one of test, live or simulated.
71
76
  # @option options [String] :test_mode_compatibility_level this is required argument while using
72
77
  # test mode with a real vin. For more information refer to docs.
73
78
  # @option options [Faraday::Connection] :service Optional connection object to be used for requests
@@ -88,9 +93,9 @@ module Smartcar
88
93
 
89
94
  base_object.token = generate_basic_auth(options, base_object)
90
95
 
91
- base_object.build_response(*base_object.fetch(
92
- path: PATHS[:compatibility],
93
- query_params: build_compatibility_params(vin, scope, country, options)
96
+ base_object.build_response(*base_object.get(
97
+ PATHS[:compatibility],
98
+ build_compatibility_params(vin, scope, country, options)
94
99
  ))
95
100
  end
96
101
 
@@ -112,7 +117,7 @@ module Smartcar
112
117
  service: options[:service]
113
118
  }
114
119
  )
115
- base_object.build_response(*base_object.fetch(path: PATHS[:user]))
120
+ base_object.build_response(*base_object.get(PATHS[:user]))
116
121
  end
117
122
 
118
123
  # Module method Returns a paged list of all vehicles connected to the application for the current authorized user.
@@ -134,9 +139,9 @@ module Smartcar
134
139
  service: options[:service]
135
140
  }
136
141
  )
137
- base_object.build_response(*base_object.fetch(
138
- path: PATHS[:vehicles],
139
- query_params: paging
142
+ base_object.build_response(*base_object.get(
143
+ PATHS[:vehicles],
144
+ paging
140
145
  ))
141
146
  end
142
147
 
@@ -169,15 +174,15 @@ module Smartcar
169
174
  scope: scope.join(' '),
170
175
  country: country
171
176
  }
172
- query_params[:flags] = options[:flags].map { |key, value| "#{key}:#{value}" }.join(' ') if options[:flags]
173
- query_params[:mode] = options[:test_mode].is_a?(TrueClass) ? 'test' : 'live' unless options[:test_mode].nil?
177
+ query_params[:flags] = stringify_params(options[:flags])
174
178
 
175
- if options[:test_mode_compatibility_level]
176
- query_params[:test_mode_compatibility_level] =
177
- options[:test_mode_compatibility_level]
178
- query_params[:mode] = 'test'
179
- end
179
+ mode = determine_mode(options[:test_mode], options[:mode])
180
180
 
181
+ unless options[:test_mode_compatibility_level].nil?
182
+ query_params[:test_mode_compatibility_level] = options[:test_mode_compatibility_level]
183
+ mode = 'test'
184
+ end
185
+ query_params[:mode] = mode unless mode.nil?
181
186
  query_params
182
187
  end
183
188
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smartcar
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ashwin Subramanian
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-05-03 00:00:00.000000000 Z
11
+ date: 2023-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -235,7 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
235
235
  - !ruby/object:Gem::Version
236
236
  version: '0'
237
237
  requirements: []
238
- rubygems_version: 3.0.8
238
+ rubygems_version: 3.1.6
239
239
  signing_key:
240
240
  specification_version: 4
241
241
  summary: Ruby Gem to access smartcar APIs (https://smartcar.com/docs/)