smartcar 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42d14a5db8893d4b26b07e273924b854c1c5970aee1a072e5d748a95a5934201
4
- data.tar.gz: 8b5c2a8c57127a1a494dc6448c84a251d8c6f2eaf0f6773255258006dcac7296
3
+ metadata.gz: 3f96ead76c474cc7bd2f579c79ae5457437b7826ea5738fc939e78e7fd15ad9a
4
+ data.tar.gz: 4ca101d8945328f524a4c4fe525eef8c0fa9ed5828a6d7fc1e7d96bccc299015
5
5
  SHA512:
6
- metadata.gz: 3b143db051f1009607453d86e2f034b3ad3a6a32949a0aee77ea88a6ef4db34e9d0f65d2b3e8da361eb4aee7da4cad4bcfe873aeb8079647287d488206f6fddb
7
- data.tar.gz: 47061d5c08c3db932e6d8c0b2cea7e0064c60348122ac70fb537fbf512b19b545593d9448cbc91766299c8c31ae79029088d10dab56fc5a445d47de501d635ef
6
+ metadata.gz: c944b360628c97f3e78e6d1f7287e91c136a33cd8c696c0e8246c18839f97d790aea5457dc402ace57eda565f4937dbc72a51e5c66e70b12d24a1850674f6bcf
7
+ data.tar.gz: 73646b1545692c60714bd7581abef94f6271907587dc1ed2564dbae30adafb63b3c3d546b46f2188933fd2f4035a1eb8922fa4ef0c7e09a4fbd48a5d69c1cb11
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,49 +1,103 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smartcar (2.1.0)
4
+ smartcar (3.0.0)
5
5
  oauth2 (~> 1.4)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
+ addressable (2.7.0)
11
+ public_suffix (>= 2.0.2, < 5.0)
12
+ ast (2.4.2)
13
+ backport (1.1.2)
10
14
  byebug (11.1.3)
11
15
  childprocess (3.0.0)
12
- diff-lcs (1.3)
13
- faraday (1.2.0)
16
+ codecov (0.5.2)
17
+ simplecov (>= 0.15, < 0.22)
18
+ crack (0.4.5)
19
+ rexml
20
+ diff-lcs (1.4.4)
21
+ docile (1.4.0)
22
+ faraday (1.4.2)
23
+ faraday-em_http (~> 1.0)
24
+ faraday-em_synchrony (~> 1.0)
25
+ faraday-excon (~> 1.1)
26
+ faraday-net_http (~> 1.0)
27
+ faraday-net_http_persistent (~> 1.1)
14
28
  multipart-post (>= 1.2, < 3)
15
- ruby2_keywords
16
- jwt (2.2.2)
29
+ ruby2_keywords (>= 0.0.4)
30
+ faraday-em_http (1.0.0)
31
+ faraday-em_synchrony (1.0.0)
32
+ faraday-excon (1.1.0)
33
+ faraday-net_http (1.0.1)
34
+ faraday-net_http_persistent (1.1.0)
35
+ hashdiff (1.0.1)
36
+ jwt (2.2.3)
17
37
  multi_json (1.15.0)
18
38
  multi_xml (0.6.0)
19
39
  multipart-post (2.1.1)
20
- oauth2 (1.4.4)
40
+ oauth2 (1.4.7)
21
41
  faraday (>= 0.8, < 2.0)
22
42
  jwt (>= 1.0, < 3.0)
23
43
  multi_json (~> 1.3)
24
44
  multi_xml (~> 0.5)
25
45
  rack (>= 1.2, < 3)
46
+ parallel (1.20.1)
47
+ parser (3.0.1.1)
48
+ ast (~> 2.4.1)
49
+ public_suffix (4.0.6)
26
50
  rack (2.2.3)
51
+ rainbow (3.0.0)
27
52
  rake (12.3.3)
28
- redcarpet (3.5.0)
29
- rspec (3.9.0)
30
- rspec-core (~> 3.9.0)
31
- rspec-expectations (~> 3.9.0)
32
- rspec-mocks (~> 3.9.0)
33
- rspec-core (3.9.2)
34
- rspec-support (~> 3.9.3)
35
- rspec-expectations (3.9.2)
53
+ readapt (1.3.0)
54
+ backport (~> 1.1)
55
+ thor (~> 1.0)
56
+ redcarpet (3.5.1)
57
+ regexp_parser (2.1.1)
58
+ rexml (3.2.5)
59
+ rspec (3.10.0)
60
+ rspec-core (~> 3.10.0)
61
+ rspec-expectations (~> 3.10.0)
62
+ rspec-mocks (~> 3.10.0)
63
+ rspec-core (3.10.1)
64
+ rspec-support (~> 3.10.0)
65
+ rspec-expectations (3.10.1)
36
66
  diff-lcs (>= 1.2.0, < 2.0)
37
- rspec-support (~> 3.9.0)
38
- rspec-mocks (3.9.1)
67
+ rspec-support (~> 3.10.0)
68
+ rspec-mocks (3.10.2)
39
69
  diff-lcs (>= 1.2.0, < 2.0)
40
- rspec-support (~> 3.9.0)
41
- rspec-support (3.9.3)
42
- ruby2_keywords (0.0.2)
70
+ rspec-support (~> 3.10.0)
71
+ rspec-support (3.10.2)
72
+ rubocop (1.16.1)
73
+ parallel (~> 1.10)
74
+ parser (>= 3.0.0.0)
75
+ rainbow (>= 2.2.2, < 4.0)
76
+ regexp_parser (>= 1.8, < 3.0)
77
+ rexml
78
+ rubocop-ast (>= 1.7.0, < 2.0)
79
+ ruby-progressbar (~> 1.7)
80
+ unicode-display_width (>= 1.4.0, < 3.0)
81
+ rubocop-ast (1.7.0)
82
+ parser (>= 3.0.1.1)
83
+ ruby-progressbar (1.11.0)
84
+ ruby2_keywords (0.0.4)
43
85
  rubyzip (2.3.0)
44
86
  selenium-webdriver (3.142.7)
45
87
  childprocess (>= 0.5, < 4.0)
46
88
  rubyzip (>= 1.2.2)
89
+ simplecov (0.21.2)
90
+ docile (~> 1.1)
91
+ simplecov-html (~> 0.11)
92
+ simplecov_json_formatter (~> 0.1)
93
+ simplecov-html (0.12.3)
94
+ simplecov_json_formatter (0.1.3)
95
+ thor (1.1.0)
96
+ unicode-display_width (2.0.0)
97
+ webmock (3.13.0)
98
+ addressable (>= 2.3.6)
99
+ crack (>= 0.3.2)
100
+ hashdiff (>= 0.4.0, < 2.0.0)
47
101
 
48
102
  PLATFORMS
49
103
  ruby
@@ -51,11 +105,15 @@ PLATFORMS
51
105
  DEPENDENCIES
52
106
  bundler (~> 2.0)
53
107
  byebug (~> 11.0)
108
+ codecov (~> 0.5.2)
54
109
  rake (~> 12.3, >= 12.3.3)
110
+ readapt (~> 1.3)
55
111
  redcarpet (~> 3.5.0)
56
112
  rspec (~> 3.0)
113
+ rubocop (~> 1.12)
57
114
  selenium-webdriver (~> 3.142)
58
115
  smartcar!
116
+ webmock (~> 3.13)
59
117
 
60
118
  BUNDLED WITH
61
- 2.1.2
119
+ 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::Vehicle.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__)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Extension to OpenStruct to convert a nested OpenStruct object to a hash.
4
+ # Using this method any of the API response can be converted back to hash
5
+ # or JSON (from hash) for convenience.
6
+ # Example Usage :
7
+ # response = {a: { b: {c: "test", d: [{x: 1}, {y: 3}]}}}
8
+ class OpenStruct
9
+ def deep_to_h
10
+ to_h.transform_values do |value|
11
+ case value
12
+ when is_a?(OpenStruct)
13
+ value.deep_to_h
14
+ when is_a?(Array)
15
+ value.map { |item| item.is_a?(OpenStruct) ? item.deep_to_h : item }
16
+ else
17
+ value
18
+ end
19
+ end
20
+ end
21
+ end
data/lib/smartcar.rb CHANGED
@@ -1,54 +1,184 @@
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"
18
-
19
-
20
- # Main Smartcar umbrella module
21
- module Smartcar
1
+ # frozen_string_literal: true
2
+
3
+ require 'smartcar_error'
4
+ require 'smartcar/utils'
5
+ require 'smartcar/version'
6
+ require 'smartcar/base'
7
+ require 'smartcar/auth_client'
8
+ require 'smartcar/vehicle'
9
+
10
+ # Main Smartcar umbrella module
11
+ module Smartcar
22
12
  # Error raised when a config is not found
23
13
  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
- # Smartcar API version being used
33
- API_VERSION = "v1.0".freeze
14
+
34
15
  # Host to connect to smartcar
35
- SITE = "https://api.smartcar.com/".freeze
16
+ API_ORIGIN = 'https://api.smartcar.com/'
17
+ PATHS = {
18
+ compatibility: '/compatibility',
19
+ user: '/user',
20
+ vehicles: '/vehicles'
21
+ }.freeze
36
22
 
37
23
  # Path for smartcar oauth
38
- OAUTH_PATH = "https://connect.smartcar.com/oauth/authorize".freeze
39
- %w(success code test live force auto metric imperial).each do |constant|
24
+ AUTH_ORIGIN = 'https://connect.smartcar.com'
25
+ %w[success code test live force auto metric imperial].each do |constant|
40
26
  # Constant to represent the value
41
27
  const_set(constant.upcase, constant.freeze)
42
28
  end
43
29
 
44
- # Lock value sent in request body
45
- LOCK = "LOCK".freeze
46
- # Unlock value sent in request body
47
- UNLOCK = "UNLOCK".freeze
48
- # Start charge value sent in request body
49
- START_CHARGE = "START".freeze
50
- # Stop charge value sent in request body
51
- STOP_CHARGE = "STOP".freeze
52
30
  # Constant for units
53
- UNITS = [IMPERIAL,METRIC]
31
+ UNITS = [IMPERIAL, METRIC].freeze
32
+
33
+ # Smartcar API version variable - defaulted to 2.0
34
+ @api_version = '2.0'
35
+
36
+ class << self
37
+ # Module method Used to set api version to be used.
38
+ # This method can be used at the top to set the version and any
39
+ # following request will use the version set here unless overridden
40
+ # separately.
41
+ # @param version [String] version to be set without 'v' prefix.
42
+ def set_api_version(version)
43
+ instance_variable_set('@api_version', version)
44
+ end
45
+
46
+ # Module method Used to get api version to be used.
47
+ # This is the getter for the class instance variable @api_version
48
+ #
49
+ # @return [String] api version number without 'v' prefix
50
+ def get_api_version
51
+ instance_variable_get('@api_version')
52
+ end
53
+
54
+ # Module method Used to check compatiblity for VIN and scope
55
+ #
56
+ # API Documentation - https://smartcar.com/docs/api#connect-compatibility
57
+ # @param vin [String] VIN of the vehicle to be checked
58
+ # @param scope [Array of Strings] - array of scopes
59
+ # @param country [String] An optional country code according to
60
+ # [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2).
61
+ # Defaults to US.
62
+ # @param options [Hash] Other optional parameters including overrides
63
+ # @option options [String] :client_id Client ID that overrides ENV
64
+ # @option options [String] :client_secret Client Secret that overrides ENV
65
+ # @option options [String] :version API version to use, defaults to what is globally set
66
+ # @option options [Hash] :flags A hash of flag name string as key and a string or boolean value.
67
+ # @option options [Boolean] :test_mode Wether to use test mode or not.
68
+ # @option options [String] :test_mode_compatibility_level this is required argument while using
69
+ # test mode with a real vin. For more information refer to docs.
70
+ #
71
+ # @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#connect-compatibility
72
+ # and a meta attribute with the relevant items from response headers.
73
+ def get_compatibility(vin:, scope:, country: 'US', options: {})
74
+ raise InvalidParameterValue.new, 'vin is a required field' if vin.nil?
75
+ raise InvalidParameterValue.new, 'scope is a required field' if scope.nil?
76
+
77
+ base_object = Base.new(
78
+ {
79
+ version: options[:version] || Smartcar.get_api_version,
80
+ auth_type: Base::BASIC
81
+ }
82
+ )
83
+
84
+ base_object.token = generate_basic_auth(options, base_object)
85
+
86
+ base_object.build_response(*base_object.fetch(
87
+ {
88
+ path: PATHS[:compatibility],
89
+ query_params: build_compatibility_params(vin, scope, country, options)
90
+ }
91
+ ))
92
+ end
93
+
94
+ # Module method Used to get user id
95
+ #
96
+ # API Documentation - https://smartcar.com/docs/api#get-user
97
+ # @param token [String] Access token
98
+ #
99
+ # @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-user
100
+ # and a meta attribute with the relevant items from response headers.
101
+ def get_user(token:, version: Smartcar.get_api_version)
102
+ base_object = Base.new(
103
+ {
104
+ token: token,
105
+ version: version
106
+ }
107
+ )
108
+ base_object.build_response(*base_object.fetch({ path: PATHS[:user] }))
109
+ end
110
+
111
+ # Module method Returns a paged list of all vehicles connected to the application for the current authorized user.
112
+ #
113
+ # API Documentation - https://smartcar.com/docs/api#get-all-vehicles
114
+ # @param token [String] - Access token
115
+ # @param paging [Hash] - Optional filter parameters (check documentation)
116
+ #
117
+ # @return [OpenStruct] And object representing the JSON response mentioned in https://smartcar.com/docs/api#get-all-vehicles
118
+ # and a meta attribute with the relevant items from response headers.
119
+ def get_vehicles(token:, paging: {}, version: Smartcar.get_api_version)
120
+ base_object = Base.new(
121
+ {
122
+ token: token,
123
+ version: version
124
+ }
125
+ )
126
+ base_object.build_response(*base_object.fetch(
127
+ {
128
+ path: PATHS[:vehicles],
129
+ query_params: paging
130
+ }
131
+ ))
132
+ end
133
+
134
+ # Module method to generate hash challenge for webhooks. It does HMAC_SHA256(amt, challenge)
135
+ #
136
+ # @param amt [String] - Application Management Token
137
+ # @param challenge [String] - Challenge string
138
+ #
139
+ # @return [String] String representing the hex digest
140
+ def hash_challenge(amt, challenge)
141
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), amt, challenge)
142
+ end
143
+
144
+ # Module method used to verify webhook payload with AMT and signature.
145
+ #
146
+ # @param amt [String] - Application Management Token
147
+ # @param signature [String] - sc-signature header value
148
+ # @param body [Object] - webhook response body
149
+ #
150
+ # @return [true, false] - true if signature matches the hex digest of amt and body
151
+ def verify_payload(amt, signature, body)
152
+ hash_challenge(amt, body.to_json) == signature
153
+ end
154
+
155
+ private
156
+
157
+ def build_compatibility_params(vin, scope, country, options)
158
+ query_params = {
159
+ vin: vin,
160
+ scope: scope.join(' '),
161
+ country: country
162
+ }
163
+ query_params[:flags] = options[:flags].map { |key, value| "#{key}:#{value}" }.join(' ') if options[:flags]
164
+ query_params[:mode] = options[:test_mode].is_a?(TrueClass) ? 'test' : 'live' unless options[:test_mode].nil?
165
+
166
+ if options[:test_mode_compatibility_level]
167
+ query_params[:test_mode_compatibility_level] =
168
+ options[:test_mode_compatibility_level]
169
+ query_params[:mode] = 'test'
170
+ end
171
+
172
+ query_params
173
+ end
174
+
175
+ # returns auth token for Basic auth
176
+ #
177
+ # @return [String] Base64 encoding of CLIENT:SECRET
178
+ def generate_basic_auth(options, base_object)
179
+ client_id = options[:client_id] || base_object.get_config('SMARTCAR_CLIENT_ID')
180
+ client_secret = options[:client_secret] || base_object.get_config('SMARTCAR_CLIENT_SECRET')
181
+ Base64.strict_encode64("#{client_id}:#{client_secret}")
182
+ end
183
+ end
54
184
  end