4个版本 (2个稳定版)

1.1.0 2023年10月5日
1.0.0 2023年6月8日
0.1.9 2023年6月8日
0.1.8 2022年9月16日

#183认证

Download history 9/week @ 2024-07-02 172/week @ 2024-07-30

每月 172 次下载

MIT 许可证

72KB
1.5K SLoC

Smartcar Rust SDK

Crate Documentation Tests

用于向Smartcar API发送请求的Rust库crate

概述

Smartcar API 允许您通过HTTP请求读取车辆数据并向车辆发送命令(锁定、解锁)。

要从Web或移动应用程序向车辆发送请求,最终用户必须使用 Smartcar Connect 将其车辆连接。此流程遵循OAuth规范,并将返回一个 code,可用于从Smartcar获取访问令牌。

Smartcar Rust SDK提供以下方法:

  1. 生成重定向到Connect的链接。
  2. 使用从Connect获得的 code 向Smartcar发出请求以获取访问令牌和刷新令牌
  3. 使用在第2步中获得的访问令牌向Smartcar API发出请求以读取车辆数据并向车辆发送命令。

在集成Smartcar SDK之前,您需要在 Smartcar开发者门户 中注册一个应用程序。如果您无法访问仪表板,请 请求访问

请注意,Rust SDK仅支持Smartcar API的2.0版本。

安装

将以下内容添加到您的 Cargo.toml

[dependencies]
smartcar = "1.0.0"

流程

  1. 使用您的 client_idclient_secretredirect_uri 创建一个新的 AuthClient 结构。

  2. 使用 <AuthClient>.get_auth_url 并带有所需的 scope 将用户重定向到Smartcar Connect。

  3. 用户将登录,然后接受或拒绝您的 scope 的权限。

  4. 如果用户接受了您的权限

    a. 处理对您的 redirect_uri 的GET请求。它将有一个查询 code,表示用户的同意。

    b. 使用以下代码通过<AuthClient>.exchange_code来获取一个Access结构体。这个结构体包含你的令牌:access_token(有效期3小时)和refresh_token(有效期60天)*。

  5. 使用get_vehicles获取一个包含车主所有车辆ID的Vehicles结构体。

  6. 使用前一步响应中的id和第4步中的access_token创建一个新的Vehicle(单数)结构体。

  7. 开始向Smartcar API发送请求!


* 为了进行后续请求,您需要将令牌保存到Access结构体中某个地方。

** 当您的访问令牌过期时,使用<AuthClient>.exchange_refresh_token和您的refresh_token来获取一组新的令牌。

入门指南

让我们看看使用axum web框架smartcar的基本用法。在这个例子中,我们将设置一个简单的服务器,它运行在localhost 3000上,以运行上述流程,以获取车辆的制造商、型号和年份。

请参阅./example/getting-started.rs中的代码。

对于不包含Web框架集成的更简单的示例,请查看./example/getting-started-cli.rs

要求

如何运行此示例

  1. 克隆此仓库,然后cd进入目录。
  2. 在Smartcar仪表板中设置一个新的重定向URI。
    • 添加https://127.0.0.1:3000/callback
  3. ./example/getting-started.rs中找到get_auth_client,并用您的仪表板中的实际客户端凭据替换假凭据。
    • 假凭据以前缀"REPLACE_WITH_YOUR_"开头。
  4. 使用带有示例标志的cargo run运行示例
cargo run --example=getting-started
  1. 在浏览器中,访问http://locahost:3000/login以查看Smartcar Connect流程。此示例在测试模式下运行,使用随机数据和假车辆。

    • 通常,您的用户将完成此流程。在此示例中,您将亲自完成此流程。
    • 选择任何制造商并输入假电子邮件/密码
    • 例如:用户名:"[email protected]",密码:"blah"
  2. 登录并批准权限后,您应该会收到一个包含车辆制造商、型号、年份和ID的JSON响应。

跟随您终端中的打印语句来查看步骤!


** example/getting-started.rs中的打印语句对应于上面提到的7步流程。为了减少噪音,下面的代码不包括打印语句。

入门指南(使用axum web框架)

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))
}

依赖关系

~7–20MB
~296K SLoC