smartcar 2.1.0 → 3.0.0

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: 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