smartcar 2.4.0 → 3.0.2

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: 4d9d54665fb1e1c644ec917708c8f702d6eec0afec4b7bc14e5504a717da92d9
4
- data.tar.gz: 31d22b73a4406d512a738c694bc250b9eeab7e60ba1e137fbd11c221d05206fa
3
+ metadata.gz: e822148047b9ffd85867b85f13eec9bbba7785424d7c98982a65657f24b02019
4
+ data.tar.gz: 1322fbc3a032a209bdd8b7101e0e3e8baf2113972586399edb292ca82879a967
5
5
  SHA512:
6
- metadata.gz: 950dccc0e0ae5e801bd24d8516a11258ef386bbc706e13b7ed323cbd032bf4e61d45f32ba7ec49136e022777d700611838548a73e7b339e7d668b01d3a64266a
7
- data.tar.gz: 979f5ee0f0bb62255aec3f5c11c844b787da1a9710f025f18d9f6d99d95dd80dd5d4ce4ae681743583d4a50ea8d251184ed31a4fdcd8073d056dc3d9fd654da6
6
+ metadata.gz: 5e99928f83946aa9a62b48a0f920c1571442545b5320d2b5d3c9e0195a6b0835748ef54d9335bf6f29f2ffa47b60dab6e24ffd576af885dd37d6b1bfa1c35214
7
+ data.tar.gz: 48df4fe316d3eb55ca38630345f35f00ebec59d984d63854e03810d0dbde2e12ae9768fda8d6ec5d24911d7c82fa2be7844427d0390f50927a6ce88aa61d4f79
data/.gitignore CHANGED
@@ -6,6 +6,7 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /.vscode/
9
10
 
10
11
  # rspec failure tracking
11
12
  .rspec_status
data/.rubocop.yml ADDED
@@ -0,0 +1,32 @@
1
+ # Prevent messages for libraries of rubocop
2
+ AllCops:
3
+ NewCops: enable
4
+ SuggestExtensions: false
5
+
6
+ # Disabling ABC size for now as refactoring few places seems pointless for now
7
+ Metrics/AbcSize:
8
+ Enabled: false
9
+
10
+ # Disabling this becuase we are using `set` and `get` prefixed methods to keep some commonality across SDKs
11
+ Naming/AccessorMethodName:
12
+ Enabled: false
13
+
14
+ Metrics/BlockLength:
15
+ Exclude:
16
+ - '**/*.gemspec'
17
+ - 'spec/**/*'
18
+
19
+ # Just ignoring test helper for headless auth.
20
+ Metrics/MethodLength:
21
+ Max: 20
22
+ Exclude:
23
+ - 'spec/smartcar/helpers/auth_helper.rb'
24
+
25
+ # Parameters in data from json API comes in as camelCase, ignoring those files to avoid snake_case enforcement
26
+ Naming/MethodName:
27
+ Exclude:
28
+ - 'lib/smartcar/battery.rb'
29
+ - 'lib/smartcar/charge.rb'
30
+ - 'lib/smartcar/engine_oil.rb'
31
+ - 'lib/smartcar/fuel.rb'
32
+ - 'lib/smartcar/tire_pressure.rb'
data/.travis.yml CHANGED
@@ -6,7 +6,6 @@ services:
6
6
  addons:
7
7
  firefox: latest
8
8
  rvm:
9
- - 2.5
10
9
  - 2.6
11
10
  - 2.7
12
11
  before_install:
@@ -25,5 +24,5 @@ deploy:
25
24
  on:
26
25
  tags: true
27
26
  branch: master
28
- rvm: 2.5
27
+ rvm: 2.6
29
28
  skip_cleanup: 'true'
data/Gemfile CHANGED
@@ -1,6 +1,8 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
6
 
5
7
  # Specify your gem's dependencies in smartcar.gemspec
6
8
  gemspec
data/Gemfile.lock CHANGED
@@ -1,21 +1,44 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smartcar (2.4.0)
4
+ smartcar (3.0.2)
5
5
  oauth2 (~> 1.4)
6
+ recursive-open-struct (~> 1.1.3)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
11
+ addressable (2.8.0)
12
+ public_suffix (>= 2.0.2, < 5.0)
13
+ ast (2.4.2)
14
+ backport (1.1.2)
10
15
  byebug (11.1.3)
11
16
  childprocess (3.0.0)
12
- diff-lcs (1.3)
13
- faraday (1.3.0)
17
+ codecov (0.5.2)
18
+ simplecov (>= 0.15, < 0.22)
19
+ crack (0.4.5)
20
+ rexml
21
+ diff-lcs (1.4.4)
22
+ docile (1.4.0)
23
+ faraday (1.5.1)
24
+ faraday-em_http (~> 1.0)
25
+ faraday-em_synchrony (~> 1.0)
26
+ faraday-excon (~> 1.1)
27
+ faraday-httpclient (~> 1.0.1)
14
28
  faraday-net_http (~> 1.0)
29
+ faraday-net_http_persistent (~> 1.1)
30
+ faraday-patron (~> 1.0)
15
31
  multipart-post (>= 1.2, < 3)
16
- ruby2_keywords
32
+ ruby2_keywords (>= 0.0.4)
33
+ faraday-em_http (1.0.0)
34
+ faraday-em_synchrony (1.0.0)
35
+ faraday-excon (1.1.0)
36
+ faraday-httpclient (1.0.1)
17
37
  faraday-net_http (1.0.1)
18
- jwt (2.2.2)
38
+ faraday-net_http_persistent (1.2.0)
39
+ faraday-patron (1.0.0)
40
+ hashdiff (1.0.1)
41
+ jwt (2.2.3)
19
42
  multi_json (1.15.0)
20
43
  multi_xml (0.6.0)
21
44
  multipart-post (2.1.1)
@@ -25,27 +48,62 @@ GEM
25
48
  multi_json (~> 1.3)
26
49
  multi_xml (~> 0.5)
27
50
  rack (>= 1.2, < 3)
51
+ parallel (1.20.1)
52
+ parser (3.0.1.1)
53
+ ast (~> 2.4.1)
54
+ public_suffix (4.0.6)
28
55
  rack (2.2.3)
56
+ rainbow (3.0.0)
29
57
  rake (12.3.3)
58
+ readapt (1.3.0)
59
+ backport (~> 1.1)
60
+ thor (~> 1.0)
61
+ recursive-open-struct (1.1.3)
30
62
  redcarpet (3.5.1)
31
- rspec (3.9.0)
32
- rspec-core (~> 3.9.0)
33
- rspec-expectations (~> 3.9.0)
34
- rspec-mocks (~> 3.9.0)
35
- rspec-core (3.9.2)
36
- rspec-support (~> 3.9.3)
37
- rspec-expectations (3.9.2)
63
+ regexp_parser (2.1.1)
64
+ rexml (3.2.5)
65
+ rspec (3.10.0)
66
+ rspec-core (~> 3.10.0)
67
+ rspec-expectations (~> 3.10.0)
68
+ rspec-mocks (~> 3.10.0)
69
+ rspec-core (3.10.1)
70
+ rspec-support (~> 3.10.0)
71
+ rspec-expectations (3.10.1)
38
72
  diff-lcs (>= 1.2.0, < 2.0)
39
- rspec-support (~> 3.9.0)
40
- rspec-mocks (3.9.1)
73
+ rspec-support (~> 3.10.0)
74
+ rspec-mocks (3.10.2)
41
75
  diff-lcs (>= 1.2.0, < 2.0)
42
- rspec-support (~> 3.9.0)
43
- rspec-support (3.9.3)
76
+ rspec-support (~> 3.10.0)
77
+ rspec-support (3.10.2)
78
+ rubocop (1.16.1)
79
+ parallel (~> 1.10)
80
+ parser (>= 3.0.0.0)
81
+ rainbow (>= 2.2.2, < 4.0)
82
+ regexp_parser (>= 1.8, < 3.0)
83
+ rexml
84
+ rubocop-ast (>= 1.7.0, < 2.0)
85
+ ruby-progressbar (~> 1.7)
86
+ unicode-display_width (>= 1.4.0, < 3.0)
87
+ rubocop-ast (1.7.0)
88
+ parser (>= 3.0.1.1)
89
+ ruby-progressbar (1.11.0)
44
90
  ruby2_keywords (0.0.4)
45
91
  rubyzip (2.3.0)
46
92
  selenium-webdriver (3.142.7)
47
93
  childprocess (>= 0.5, < 4.0)
48
94
  rubyzip (>= 1.2.2)
95
+ simplecov (0.21.2)
96
+ docile (~> 1.1)
97
+ simplecov-html (~> 0.11)
98
+ simplecov_json_formatter (~> 0.1)
99
+ simplecov-html (0.12.3)
100
+ simplecov_json_formatter (0.1.3)
101
+ thor (1.1.0)
102
+ unicode-display_width (2.0.0)
103
+ webmock (3.13.0)
104
+ addressable (>= 2.3.6)
105
+ crack (>= 0.3.2)
106
+ hashdiff (>= 0.4.0, < 2.0.0)
49
107
 
50
108
  PLATFORMS
51
109
  ruby
@@ -53,11 +111,15 @@ PLATFORMS
53
111
  DEPENDENCIES
54
112
  bundler (~> 2.0)
55
113
  byebug (~> 11.0)
114
+ codecov (~> 0.5.2)
56
115
  rake (~> 12.3, >= 12.3.3)
116
+ readapt (~> 1.3)
57
117
  redcarpet (~> 3.5.0)
58
118
  rspec (~> 3.0)
119
+ rubocop (~> 1.12)
59
120
  selenium-webdriver (~> 3.142)
60
121
  smartcar!
122
+ webmock (~> 3.13)
61
123
 
62
124
  BUNDLED WITH
63
125
  2.1.4
data/README.md CHANGED
@@ -29,28 +29,28 @@ not have access to the dashboard, please
29
29
 
30
30
  ### Flow
31
31
 
32
- - Create a new `AuthClient` object with your `clientId`, `clientSecret`,
33
- `redirectUri`, and required `scope`.
34
- - Redirect the user to Smartcar Connect using `getAuthUrl` or one
32
+ - Create a new `AuthClient` object with your `client_id`, `client_secret`,
33
+ `redirect_uri`.
34
+ - Redirect the user to Smartcar Connect using `get_auth_url` with required `scope` or with one
35
35
  of our frontend SDKs.
36
36
  - The user will login, and then accept or deny your `scope`'s permissions.
37
- - Handle the get request to `redirectUri`.
37
+ - Handle the get request to `redirect_uri`.
38
38
  - If the user accepted your permissions, `req.query.code` will contain an
39
39
  authorization code.
40
- - Use `exchangeCode` with this code to obtain an access object
40
+ - Use `exchange_code` with this code to obtain an access object
41
41
  containing an access token (lasting 2 hours) and a refresh token
42
42
  (lasting 60 days).
43
43
  - Save this access object.
44
44
  - If the user denied your permissions, `req.query.error` will be set
45
45
  to `"access_denied"`.
46
- - If you passed a state parameter to `getAuthUrl`, `req.query.state` will
46
+ - If you passed a state parameter to `get_auth_url`, `req.query.state` will
47
47
  contain the state value.
48
- - Get the user's vehicles with `getVehicleIds`.
49
- - Create a new `Vehicle` object using a `vehicleId` from the previous response,
48
+ - Get the user's vehicles with `getVehicles`.
49
+ - Create a new `Vehicle` object using a `vehicle_id` from the previous response,
50
50
  and the `access_token`.
51
51
  - Make requests to the Smartcar API.
52
- - Use `exchangeRefreshToken` on your saved `refreshToken` to retrieve a new token
53
- when your `accessToken` expires.
52
+ - Use `exchange_refresh_token` on your saved `refresh_token` to retrieve a new token
53
+ when your `access_token` expires.
54
54
 
55
55
  ## Installation
56
56
 
@@ -70,53 +70,47 @@ Or install it yourself as:
70
70
 
71
71
  ## Usage
72
72
 
73
- Setup the environment variables for CLIENT_ID and CLIENT_SECRET.
73
+ Setup the environment variables for SMARTCAR_CLIENT_ID, SMARTCAR_CLIENT_SECRET and SMARTCAR_REDIRECT_URI.
74
74
  ```bash
75
75
  # Get your API keys from https://dashboard.smartcar.com/signup
76
- export CLIENT_ID=<client id>
77
- export CLIENT_SECRET=<client secret>
76
+ export SMARTCAR_CLIENT_ID=<client id>
77
+ export SMARTCAR_CLIENT_SECRET=<client secret>
78
+ export SMARTCAR_REDIRECT_URI=<redirect URI>
78
79
  ```
79
80
 
80
81
  Example Usage for calling the reports API with oAuth token
81
82
  ```ruby
82
83
  2.5.7 :001 > require 'smartcar'
83
84
  => true
84
- 2.5.7 :003 > ids = Smartcar::Vehicle.all_vehicle_ids(token: token)
85
+ 2.5.7 :003 > ids = Smartcar.get_vehicles(token: token).vehicles
85
86
  => ["4bb777b2-bde7-4305-8952-25956f8c0868"]
86
87
  2.5.7 :004 > vehicle = Smartcar::Vehicle.new(token: token, id: ids.first)
87
- => #<Smartcar::Vehicle:0x00005564211a7c48 @token="5ae77cb0-7c1a-486a-ac20-00c76d2fd1aa", @id="4bb777b2-bde7-4305-8952-25956f8c0868", @unit_system="imperial">
88
+ => #<Smartcar::Vehicle:0x0000558dcd7ee608 @token="c900e00e-ee8e-403d-a7bf-f992bc0ad302", @id="e31c9de6-1332-472b-b648-5d74b05b7fda", @options={:unit_system=>"metric", :version=>"2.0"}, @unit_system="metric", @version="2.0", @service=#<Faraday::Connection:0x0000558dcd7d63f0 @parallel_manager=nil, @headers={"User-Agent"=>"Faraday v1.4.2"}, @params={}, @options=#<Faraday::RequestOptions timeout=310>, @ssl=#<Faraday::SSLOptions verify=true>, @default_parallel_manager=nil, @builder=#<Faraday::RackBuilder:0x0000558dcd7c1bf8 @adapter=Faraday::Adapter::NetHttp, @handlers=[Faraday::Request::UrlEncoded], @app=#<Faraday::Request::UrlEncoded:0x0000558dcd7af048 @app=#<Faraday::Adapter::NetHttp:0x0000558dcd7af390 @ssl_cert_store=#<OpenSSL::X509::Store:0x0000558dcd7a36a8 @verify_callback=nil, @error=nil, @error_string=nil, @chain=nil, @time=nil>, @app=#<Proc:0x0000558dcd7af278 /home/ashwinsubramanian/.rvm/gems/ruby-2.7.2/gems/faraday-1.4.2/lib/faraday/adapter.rb:37 (lambda)>, @connection_options={}, @config_block=nil>, @options={}>>, @url_prefix=#<URI::HTTPS https://api.smartcar.com/>, @proxy=nil, @manual_proxy=false>>
88
89
  2.5.7 :006 > vehicle.odometer
89
- => #<Smartcar::Odometer:0x00005564211330f0 @distance=17966.94802354251, @meta={"date"=>"Fri, 12 Jun 2020 06:04:32 GMT", "content-type"=>"application/json; charset=utf-8", "content-length"=>"30", "connection"=>"keep-alive", "access-control-allow-origin"=>"*", "sc-data-age"=>"2020-06-12T06:04:28.843Z", "sc-unit-system"=>"imperial", "sc-request-id"=>"3c447e9e-4cf7-43cb-b688-fba8db3d3582"}>
90
+ => #<OpenStruct distance=39685.33984375, meta=#<OpenStruct data_age=#<DateTime: 2021-06-24T22:28:39+00:00 ((2459390j,80919s,95000000n),+0s,2299161j)>, unit_system="metric", request_id="4962ba7f-5c94-48ab-9955-4e2b101c7b8a">>
90
91
  2.5.7 :007 > vehicle.battery
91
- => #<Smartcar::Battery:0x00005564210fcb18 @range=105.63, @percentRemaining=0.98, @meta={"date"=>"Fri, 12 Jun 2020 06:04:44 GMT", "content-type"=>"application/json; charset=utf-8", "content-length"=>"40", "connection"=>"keep-alive", "access-control-allow-origin"=>"*", "sc-data-age"=>"2020-06-12T06:04:28.843Z", "sc-unit-system"=>"imperial", "sc-request-id"=>"455ed4b0-b768-4961-86d7-436ad71cf0fa"}>
92
+ => #<OpenStruct range=208.82, percentRemaining=0.31, meta=#<OpenStruct data_age=#<DateTime: 2021-06-24T22:28:54+00:00 ((2459390j,80934s,855000000n),+0s,2299161j)>, unit_system="metric", request_id="a88b95ec-b10f-4fc8-979b-5d95fe40d925">, percentage_remaining=0.31>
92
93
  2.5.7 :009 > vehicle.lock!
93
- => true
94
- 2.5.7 :010 > vehicle.batch(["charge","battery"])
95
- => {:charge=>#<Smartcar::Charge:0x000055853d1fd7c8 @state="NOT_CHARGING", @isPluggedIn=false, @meta={"sc-data-age"=>"2020-06-12T06:18:50.581Z"}>, :battery=>#<Smartcar::Battery:0x000055853d1fd638 @range=105.63, @percentRemaining=0.98, @meta={"sc-data-age"=>"2020-06-12T06:18:50.581Z", "sc-unit-system"=>"imperial"}>}
96
- 2.5.7 :011 > vehicle.start_charge!
97
- Traceback (most recent call last):
98
- 5: from /usr/share/rvm/rubies/ruby-2.5.7/bin/irb:11:in `<main>'
99
- 4: from (irb):5
100
- 3: from /home/st-2vgpnn2/.rvm/gems/ruby-2.5.7/gems/smartcar-1.0.0/lib/smartcar/vehicle.rb:118:in `start_charge!'
101
- 2: from /home/st-2vgpnn2/.rvm/gems/ruby-2.5.7/gems/smartcar-1.0.0/lib/smartcar/vehicle.rb:290:in `start_or_stop_charge!'
102
- 1: from /home/st-2vgpnn2/.rvm/gems/ruby-2.5.7/gems/smartcar-1.0.0/lib/smartcar/base.rb:39:in `block (2 levels) in <class:Base>'
103
- Smartcar::ExternalServiceError (API error - {"error":"vehicle_state_error","message":"Charging plug is not connected to the vehicle.","code":"VS_004"})
104
-
94
+ => #<OpenStruct status="success", message="Successfully sent request to vehicle", meta=#<OpenStruct request_id="0c90918f-a9cc-405c-839f-7d9b70e249c4">>
95
+ 2.5.7 :010 > batch_response = vehicle.batch(["/charge","/battery"])
96
+ => #<OpenStruct>
97
+ 2.5.7 :011 > batch_response.charge()
98
+ => #<OpenStruct state="NOT_CHARGING", isPluggedIn=false, meta=#<OpenStruct data_age=#<DateTime: 2021-06-24T22:30:20+00:00 ((2459390j,81020s,892000000n),+0s,2299161j)>, request_id="29a66280-8685-4a57-9733-daa3dfb9970f">, is_plugged_in?=false>
105
99
  ```
106
100
 
107
101
  Example Usage for oAuth -
108
102
  ```ruby
109
103
  # To get the redirect URL :
110
- 2.5.5 :002 > options = {test_mode: true,scope: ["read_battery","read_charge","read_fuel","read_location","control_security","read_odometer","read_tires","read_vin","read_vehicle_info"],flags: ["country:DE"]}
104
+ 2.5.5 :002 > options = {test_mode: true}
111
105
  2.5.5 :003 > require 'smartcar'
112
- 2.5.5 :004 > client = Smartcar::Oauth.new(options)
113
- 2.5.5 :005 > url = client.authorization_url
106
+ 2.5.5 :004 > client = Smartcar::AuthClient.new(options)
107
+ 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"]})
114
108
  => "https://connect.smartcar.com/oauth/authorize?approval_prompt=auto&client_id=<client id>&mode=test&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Fcallback&response_type=code&scope=read_battery+read_charge+read_fuel+read_location+control_security+read_odometer+read_tires+read_vin+read_vehicle_info&flags=country%3ADE"
115
109
  # Redirect user to the above URL.
116
110
  # After authentication user control reaches the callback URL with code.
117
111
  # Use the code from the parameters and request a token
118
- 2.5.5 :006 > token_hash = client.get_token(code)
119
- => {"token_type"=>"Bearer", :access_token=>"56801a5e-6a0b-4d05-a43e-52a4d5e6648f", :refresh_token=>"4f46e7e4-28c5-47b3-ba8d-7dcef73d05dd", :expires_at=>1577875279}
112
+ 2.5.5 :006 > token_hash = client.exchange_code(code)
113
+ => #<OpenStruct token_type="Bearer", access_token="20e24b4a-3055-4cc8-9cf3-2b3c5afba3e6", refresh_token="cf89c62e-7b36-4e13-a9df-d9c2a5296280", expires_at=1624581588>
120
114
  # This access_token can be used to call the Smartcar APIs as given above.
121
115
  # Store this hash and if it expired refresh the token OR use the code again to
122
116
  # get a new token or use .
@@ -128,8 +122,10 @@ To install this gem onto your local machine, run `bundle exec rake install`.
128
122
 
129
123
  To run tests, make sure you have the env variables setup for client id and secret.
130
124
  ```shell
131
- export INTEGRATION_CLIENT_ID=<client id>
132
- export INTEGRATION_CLIENT_SECRET=<client secret>
125
+ export E2E_SMARTCAR_CLIENT_ID=<client id>
126
+ export E2E_SMARTCAR_CLIENT_SECRET=<client secret>
127
+ export E2E_SMARTCAR_AMT=<amt from dashboard for webhooks>
128
+ export E2E_SMARTCAR_WEBHOOK_ID=<webhook id to use for tests>
133
129
  ```
134
130
 
135
131
  Tests can be run using either default rake command OR specific rspec command.
@@ -164,3 +160,9 @@ To contribute, please:
164
160
 
165
161
  [gem-image]: https://badge.fury.io/rb/smartcar
166
162
  [gem-url]: https://badge.fury.io/rb/smartcar.svg
163
+
164
+ ## Supported Ruby Branches
165
+
166
+ Smartcar aims to support the SDK on all Ruby branches that have a status of "normal maintenance" or "security maintenance" as defined in the [Ruby Branches documentation](https://www.ruby-lang.org/en/downloads/branches/).
167
+
168
+ In accordance with the Semantic Versioning specification, the addition of support for new Ruby branches would result in a MINOR version bump and the removal of support for Ruby branches would result in a MAJOR version bump.
data/Rakefile CHANGED
@@ -1,6 +1,14 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new(:rubocop) do |t|
11
+ t.options = ['--display-cop-names']
12
+ end
13
+
14
+ task default: %i[rubocop spec]
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "smartcar"
4
+ require 'bundler/setup'
5
+ require 'smartcar'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +11,5 @@ require "smartcar"
10
11
  # require "pry"
11
12
  # Pry.start
12
13
 
13
- require "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
data/lib/smartcar.rb CHANGED
@@ -1,73 +1,185 @@
1
- require "smartcar/utils"
2
- require "smartcar/version"
3
- require "smartcar/base"
4
- require "smartcar/oauth"
5
- require "smartcar/permissions"
6
- require "smartcar/battery"
7
- require "smartcar/battery_capacity"
8
- require "smartcar/charge"
9
- require "smartcar/engine_oil"
10
- require "smartcar/fuel"
11
- require "smartcar/location"
12
- require "smartcar/odometer"
13
- require "smartcar/tire_pressure"
14
- require "smartcar/vin"
15
- require "smartcar/vehicle_attributes"
16
- require "smartcar/vehicle"
17
- require "smartcar/user"
1
+ # frozen_string_literal: true
18
2
 
3
+ require 'recursive_open_struct'
4
+ require 'smartcar_error'
5
+ require 'smartcar/utils'
6
+ require 'smartcar/version'
7
+ require 'smartcar/base'
8
+ require 'smartcar/auth_client'
9
+ require 'smartcar/vehicle'
19
10
 
20
11
  # Main Smartcar umbrella module
21
12
  module Smartcar
22
13
  # Error raised when a config is not found
23
14
  class ConfigNotFound < StandardError; end
24
- # Error raised when Smartcar returns non 400, 404, 401, 200 or 204 response
25
- class ExternalServiceError < StandardError; end
26
- # Error raised when Smartcar returns 404
27
- class ServiceUnavailableError < ExternalServiceError; end
28
- # Error raised when Smartcar returns Authentication Error with status 401
29
- class AuthenticationError < ExternalServiceError; end
30
- # Error raised when Smartcar returns 400 response
31
- class BadRequestError < ExternalServiceError; end
32
15
 
33
16
  # Host to connect to smartcar
34
- SITE = "https://api.smartcar.com/".freeze
17
+ API_ORIGIN = 'https://api.smartcar.com/'
18
+ PATHS = {
19
+ compatibility: '/compatibility',
20
+ user: '/user',
21
+ vehicles: '/vehicles'
22
+ }.freeze
35
23
 
36
24
  # Path for smartcar oauth
37
- OAUTH_PATH = "https://connect.smartcar.com/oauth/authorize".freeze
38
- %w(success code test live force auto metric imperial).each do |constant|
25
+ AUTH_ORIGIN = 'https://connect.smartcar.com'
26
+ %w[success code test live force auto metric imperial].each do |constant|
39
27
  # Constant to represent the value
40
28
  const_set(constant.upcase, constant.freeze)
41
29
  end
42
30
 
43
- # Lock value sent in request body
44
- LOCK = "LOCK".freeze
45
- # Unlock value sent in request body
46
- UNLOCK = "UNLOCK".freeze
47
- # Start charge value sent in request body
48
- START_CHARGE = "START".freeze
49
- # Stop charge value sent in request body
50
- STOP_CHARGE = "STOP".freeze
51
31
  # Constant for units
52
- UNITS = [IMPERIAL,METRIC]
53
-
54
- # Smartcar API version variable - defaulted to 1.0
55
- @api_version = "1.0"
56
-
57
- # Module method Used to set api version to be used.
58
- # This method can be used at the top to set the version and any
59
- # following request will use the version set here unless overridden
60
- # separately.
61
- # @param version [String] version to be set without 'v' prefix.
62
- def self.set_api_version(version)
63
- instance_variable_set('@api_version', version)
64
- end
32
+ UNITS = [IMPERIAL, METRIC].freeze
33
+
34
+ # Smartcar API version variable - defaulted to 2.0
35
+ @api_version = '2.0'
36
+
37
+ class << self
38
+ # Module method Used to set api version to be used.
39
+ # This method can be used at the top to set the version and any
40
+ # following request will use the version set here unless overridden
41
+ # separately.
42
+ # @param version [String] version to be set without 'v' prefix.
43
+ def set_api_version(version)
44
+ instance_variable_set('@api_version', version)
45
+ end
46
+
47
+ # Module method Used to get api version to be used.
48
+ # This is the getter for the class instance variable @api_version
49
+ #
50
+ # @return [String] api version number without 'v' prefix
51
+ def get_api_version
52
+ instance_variable_get('@api_version')
53
+ end
54
+
55
+ # Module method Used to check compatiblity for VIN and scope
56
+ #
57
+ # API Documentation - https://smartcar.com/docs/api#connect-compatibility
58
+ # @param vin [String] VIN of the vehicle to be checked
59
+ # @param scope [Array of Strings] - array of scopes
60
+ # @param country [String] An optional country code according to
61
+ # [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2).
62
+ # Defaults to US.
63
+ # @param options [Hash] Other optional parameters including overrides
64
+ # @option options [String] :client_id Client ID that overrides ENV
65
+ # @option options [String] :client_secret Client Secret that overrides ENV
66
+ # @option options [String] :version API version to use, defaults to what is globally set
67
+ # @option options [Hash] :flags A hash of flag name string as key and a string or boolean value.
68
+ # @option options [Boolean] :test_mode Wether to use test mode or not.
69
+ # @option options [String] :test_mode_compatibility_level this is required argument while using
70
+ # test mode with a real vin. For more information refer to docs.
71
+ #
72
+ # @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#connect-compatibility
73
+ # and a meta attribute with the relevant items from response headers.
74
+ def get_compatibility(vin:, scope:, country: 'US', options: {})
75
+ raise InvalidParameterValue.new, 'vin is a required field' if vin.nil?
76
+ raise InvalidParameterValue.new, 'scope is a required field' if scope.nil?
77
+
78
+ base_object = Base.new(
79
+ {
80
+ version: options[:version] || Smartcar.get_api_version,
81
+ auth_type: Base::BASIC
82
+ }
83
+ )
84
+
85
+ base_object.token = generate_basic_auth(options, base_object)
86
+
87
+ base_object.build_response(*base_object.fetch(
88
+ {
89
+ path: PATHS[:compatibility],
90
+ query_params: build_compatibility_params(vin, scope, country, options)
91
+ }
92
+ ))
93
+ end
94
+
95
+ # Module method Used to get user id
96
+ #
97
+ # API Documentation - https://smartcar.com/docs/api#get-user
98
+ # @param token [String] Access token
99
+ #
100
+ # @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-user
101
+ # and a meta attribute with the relevant items from response headers.
102
+ def get_user(token:, version: Smartcar.get_api_version)
103
+ base_object = Base.new(
104
+ {
105
+ token: token,
106
+ version: version
107
+ }
108
+ )
109
+ base_object.build_response(*base_object.fetch({ path: PATHS[:user] }))
110
+ end
111
+
112
+ # Module method Returns a paged list of all vehicles connected to the application for the current authorized user.
113
+ #
114
+ # API Documentation - https://smartcar.com/docs/api#get-all-vehicles
115
+ # @param token [String] - Access token
116
+ # @param paging [Hash] - Optional filter parameters (check documentation)
117
+ #
118
+ # @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-all-vehicles
119
+ # and a meta attribute with the relevant items from response headers.
120
+ def get_vehicles(token:, paging: {}, version: Smartcar.get_api_version)
121
+ base_object = Base.new(
122
+ {
123
+ token: token,
124
+ version: version
125
+ }
126
+ )
127
+ base_object.build_response(*base_object.fetch(
128
+ {
129
+ path: PATHS[:vehicles],
130
+ query_params: paging
131
+ }
132
+ ))
133
+ end
134
+
135
+ # Module method to generate hash challenge for webhooks. It does HMAC_SHA256(amt, challenge)
136
+ #
137
+ # @param amt [String] - Application Management Token
138
+ # @param challenge [String] - Challenge string
139
+ #
140
+ # @return [String] String representing the hex digest
141
+ def hash_challenge(amt, challenge)
142
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), amt, challenge)
143
+ end
144
+
145
+ # Module method used to verify webhook payload with AMT and signature.
146
+ #
147
+ # @param amt [String] - Application Management Token
148
+ # @param signature [String] - sc-signature header value
149
+ # @param body [Object] - webhook response body
150
+ #
151
+ # @return [true, false] - true if signature matches the hex digest of amt and body
152
+ def verify_payload(amt, signature, body)
153
+ hash_challenge(amt, body.to_json) == signature
154
+ end
155
+
156
+ private
157
+
158
+ def build_compatibility_params(vin, scope, country, options)
159
+ query_params = {
160
+ vin: vin,
161
+ scope: scope.join(' '),
162
+ country: country
163
+ }
164
+ query_params[:flags] = options[:flags].map { |key, value| "#{key}:#{value}" }.join(' ') if options[:flags]
165
+ query_params[:mode] = options[:test_mode].is_a?(TrueClass) ? 'test' : 'live' unless options[:test_mode].nil?
166
+
167
+ if options[:test_mode_compatibility_level]
168
+ query_params[:test_mode_compatibility_level] =
169
+ options[:test_mode_compatibility_level]
170
+ query_params[:mode] = 'test'
171
+ end
172
+
173
+ query_params
174
+ end
65
175
 
66
- # Module method Used to get api version to be used.
67
- # This is the getter for the class instance variable @api_version
68
- #
69
- # @return [String] api version number without 'v' prefix
70
- def self.get_api_version
71
- instance_variable_get('@api_version')
176
+ # returns auth token for Basic auth
177
+ #
178
+ # @return [String] Base64 encoding of CLIENT:SECRET
179
+ def generate_basic_auth(options, base_object)
180
+ client_id = options[:client_id] || base_object.get_config('SMARTCAR_CLIENT_ID')
181
+ client_secret = options[:client_secret] || base_object.get_config('SMARTCAR_CLIENT_SECRET')
182
+ Base64.strict_encode64("#{client_id}:#{client_secret}")
183
+ end
72
184
  end
73
185
  end