Crates.io | smartcar |
lib.rs | smartcar |
version | 1.1.0 |
source | src |
created_at | 2022-09-06 18:10:03.990624 |
updated_at | 2023-10-05 21:58:39.004271 |
description | The Rust SDK for Smartcar API |
homepage | https://github.com/nbry/smartcar-rust-sdk |
repository | https://github.com/nbry/smartcar-rust-sdk |
max_upload_size | |
id | 659661 |
size | 139,007 |
Rust library crate for sending requests to Smartcar API
Smartcar API lets you read vehicle data and send commands to vehicles (lock, unlock) using HTTP requests.
To make requests to a vehicle from a web or mobile application, the end user must connect their vehicle using Smartcar Connect. This flow follows the OAuth spec and will return a code
which can be used to obtain an access token from Smartcar.
The Smartcar Rust SDK provides methods to:
code
obtained from Connect to obtain an access and refresh tokenBefore integrating with Smartcar's SDK, you'll need to register an application in the Smartcar Developer portal. If you do not have access to the dashboard, please request access.
Note that the Rust SDK only supports version 2.0 of Smartcar API.
Add this to your Cargo.toml
:
[dependencies]
smartcar = "1.0.0"
Create a new AuthClient
struct with your client_id
, client_secret
, and redirect_uri
.
Redirect the user to Smartcar Connect using <AuthClient>.get_auth_url
with required scope
.
The user will login and then accept or deny your scope
's permissions.
If the user accepted your permissions:
a. Handle the get request to your redirect_uri
. It will have a query code
, which represents the user's consent.
b. Use <AuthClient>.exchange_code
with this code to obtain an Access
struct. This struct contains your tokens: access_token
(lasting 3 hours) and refresh_token
(lasting 60 days) *.
Use get_vehicles
to get a Vehicles
struct that has all the ids of the owner's vehicles.
Create a new Vehicle
(singular) struct using an id
from the previous response and the access_token
from Step 4.
Start making requests to the Smartcar API!
* In order to make subsequent requests, you will need to save this the tokens in the Access struct somewhere.
** When your access token expires, use <AuthClient>.exchange_refresh_token
on your refresh_token
to get a fresh set.
Let's see a basic use case of smartcar
using the axum web framework. In this example, we will set up a simple server running on localhost 3000 to run through the flow described above, in order to get the make, model, and year of a vehicle.
See the code in ./example/getting-started.rs.
For a much simpler example without a web framework integration, check out ./example/getting-started-cli.rs.
cd
into the directory.http://localhost:3000/callback
get_auth_client
in ./example/getting-started.rs and replace the fake credentials with your actual client credentials from your dashboard.
"REPLACE_WITH_YOUR_"
.cargo run --example=getting-started
In a browser, go http://locahost:3000/login
to see the Smartcar Connect flow. This example runs connect in Test Mode, which uses randomized data and fake cars.
"blah@blah.com"
, password: "blah"
After logging in and approving permissions, you should get a JSON response with the vehicle's make, model, year, and id.
Follow along with the print statements in your terminal to see the steps!
** example/getting-started.rs has print statements that correspond to the 7-step Flow above. To minimize noise, the code below does not include the print statements.
use axum::extract::Query;
use axum::http::StatusCode;
use axum::response::{IntoResponse, Redirect};
use axum::Json;
use axum::{routing::get, Router};
use serde::Deserialize;
use serde_json::json;
use smartcar::*;
use auth_client::{AuthClient, AuthUrlOptionsBuilder};
use response::{Meta, VehicleAttributes};
use vehicle::Vehicle;
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/login", get(login))
.route("/callback", get(callback));
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
/// Helper for creating an Auth Client instance with your credentials
fn get_auth_client() -> AuthClient {
AuthClient::new(
"REPLACE_WITH_YOUR_SMARTCAR_CLIENT_ID",
"REPLACE_WITH_YOUR_SMARTCAR_CLIENT_SECRET",
"REPLACE_WITH_YOUR_SMARTCAR_REDIRECT_URI.COM",
true,
)
}
/// Smartcar Connect Flow
async fn login() -> Redirect {
// Flow - Step 1
let auth_client = get_auth_client();
let scope = ScopeBuilder::new().add_permission(Permission::ReadVehicleInfo);
let auth_url_options = AuthUrlOptionsBuilder::new().set_force_prompt(true);
// Flow - Step 2
let auth_url = auth_client.get_auth_url(&scope, Some(&auth_url_options));
Redirect::to(&auth_url)
}
#[derive(Deserialize)]
struct Callback {
code: Option<String>,
error: Option<String>,
}
async fn callback(q: Query<Callback>) -> impl IntoResponse {
// Flow - Step 3 completed, starting 4a
// If user denies you access, you'll see this
if let Some(_) = &q.error {
return (
StatusCode::EXPECTATION_FAILED,
Json(json!("User delined during Smartcar Connect")),
);
};
// This is the code that represents the user’s consent to grant you permission
// to read their vehicle's attributes. This code must be exchanged for an
// access token to start making requests to the vehicle.
let code = &q.code.to_owned().unwrap();
match get_attributes_handler(&code).await {
Err(_) => {
return (
StatusCode::EXPECTATION_FAILED,
Json(json!("attributes request failed")),
)
}
Ok((attributes, _)) => {
(
StatusCode::OK,
Json(json!(attributes)),
)
}
}
}
async fn get_attributes_handler(
auth_code: &str,
) -> Result<(VehicleAttributes, Meta), smartcar::error::Error> {
let client = get_auth_client();
// Flow - Step 4b
let (access, _) = client.exchange_code(auth_code).await?;
// Flow - Step 5
let (vehicle_ids, _) = smartcar::get_vehicles(&access, None, None).await?;
// Flow - Step 6
let vehicle = Vehicle::new(&vehicle_ids.vehicles[0], &access.access_token);
// Flow - Step 7
let (attributes, meta) = vehicle.attributes().await?;
Ok((attributes, meta))
}