8 个版本 (5 个重大变更)

0.6.2 2023 年 2 月 6 日
0.6.1 2022 年 4 月 5 日
0.5.0 2022 年 2 月 27 日
0.4.0 2022 年 2 月 25 日
0.1.0 2022 年 2 月 7 日

#49 in #api-wrapper

24 个月下载量

MIT 许可证

150KB
2.5K SLoC

smartsheet-rs

github crates.io docs.rs build status

smartsheet-rs 是一个 rust crate,它提供了一个异步包装器 API,使您能够轻松地与 Smartsheet API 2.0 交互。

这是一个为了学习 Rust 而制作的非官方 SDK,但我希望您也能从中获得乐趣——我知道我在为这个 crate 编写实现时确实有很多乐趣。

目录

入门指南

使用 smartsheet-rs 库入门很简单

  1. 在您的环境中设置 SMARTSHEET_ACCESS_TOKEN;您还可以使用 SmartsheetApi::from_token 构造函数显式设置令牌值。有关 Smartsheet API 文档中的 身份验证和访问令牌 的更多信息。

  2. 将这些依赖项添加到您的 Cargo.toml

    [dependencies]
    smartsheet-rs = "0.6.2"
    tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
    
  3. 将一些用法添加到您的应用程序中

    use smartsheet_rs::SmartsheetApi;
    
    #[tokio::main]
    async fn main() -> std::result::Result<(), Box<dyn std::error::Error + Send + Sync>> {
        let smart = SmartsheetApi::from_env()?;
        println!("Created a Smartsheet API client");
    
        let sheets = smart.list_sheets().await?;
    
        println!("Printing sheet names:");
        for sheet in sheets.data {
            println!("  - {}", sheet.name);
        }
    
        Ok(())
    }
    

实现的方法

目前已经实现了以下来自 官方文档 的 API 方法

您可以在 GitHub 上的项目仓库中的 examples/ 文件夹中查看这些 API 方法的示例用法。

更大的示例

在 SmartSheet API 中处理行和单元格时,一个突出的问题是 API 故意通过 ID 而不是标题或 列名 来标识列。

然而,作为人类,在处理数据时引用 列名 要自然和方便得多。为此,smartsheet-rs crate 提供了如 ColumnMapperCellGetterRowGetter 等辅助 struct 实现,以简化与 Smartsheet API 的交互。

单元格

检索单元格

要按关联的列ID从行中检索单个单元格,您可以使用Row::get_cell_by_id

如果要按列名检索单个Cell,您首先可以使用ColumnMapper构建一个列名到ID的映射,然后与CellGetter配对以从行中检索单元格。

以下是一个快速示例,说明这是如何工作的

use smartsheet_rs::{CellGetter, ColumnMapper, SmartsheetApi};

// TODO update these values as needed
const SHEET_ID: u64 = 1234567890;
const COLUMN_NAME: &str = "My Column";

// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

#[tokio::main]
async fn main() -> Result<()> {
    let smart = SmartsheetApi::from_env()?;

    let sheet = smart.get_sheet(SHEET_ID).await?;

    // Create interchangeable name <-> id mappings for columns in the row
    let cols = ColumnMapper::new(&sheet.columns);

    // Create a `CellGetter` helper to find cells in a row by *Column Name*
    let get_cell = CellGetter::new(&cols);

    // Get the first row in the sheet. We could also access
    // a row by index, like `&sheet.rows[i]` for example.
    let first_row = sheet.rows.first().unwrap();
   
    // Try to find a cell in the row by it's column name
    match get_cell.by_name(first_row, COLUMN_NAME) {
        Ok(cell) => println!("Here's the cell: {:#?}", *cell),
        Err(e) => println!("Error: {}", e),
    }

    Ok(())
}

CellGetter::by_name方法通过遍历行中的每个单元格,然后返回第一个匹配指定列名的单元格的列IDCell

如果需要通过列名从Row中检索多个Cell对象,最好首先为该列的每个列名构建一个映射到行中相应Cell对象的映射。可以使用以下示例中的CellGetter::name_to_cell方法来执行此操作。

let column_name_to_cell = get_cell.name_to_cell(row);

println!("{:#?}", column_name_to_cell);
// Prints:
// {
//     "Column 1": Cell {...},
//     "Column 2": Cell {...},
//      ...

多联系人单元格

当处理更复杂的对象,例如MULTI_CONTACT列类型的单元格时,可以使用辅助方法Cell::contacts从单元格中提取联系信息。请注意,要检索每个联系人的电子邮件,需要传递包含include=objectValue查询参数的include参数以及相应的level参数,以便收集完整的多联系人详细信息。

以下是代码的相关部分,演示了处理给定行中MULTI_CONTACT单元格数据的理想方式

// Retrieve the sheet with `MULTI_CONTACT` info included, such as emails.
let sheet = smart.get_sheet_with_multi_contact_info(sheet_id).await?;

// Let's assume we retrieve the cell for the specified column from the first row.
let cell = get_cell.by_name(&sheet.rows[0], "My Multi-Contact Column")?;

// Now we create a list of `Contact` objects from the cell details.
let contacts = cell.contacts()?;

// Get the contact emails, as a comma-delimited string in the format
// *[email protected], [email protected]*
let emails = contacts.addrs_str();

// Get a list of contact name addresses, where each one as indicated
// in the RFC will be in the format `[display-name] angle-addr` --
// that is, for example, *John Doe <[email protected]>*
let names = contacts.name_addrs();

要查看完整的代码,请参阅项目存储库中的示例cell_multi_contactcell_multi_contact

检索行

要按关联的行ID从工作表检索单个行,您可以使用Sheet::get_row_by_id

如果目标是找到与指定条件匹配的一个或多个行,则可以使用RowGetter辅助程序使任务更加方便。

以下是一个简单示例,用于查找具有特定值的列中Cell的第一个Row,以及具有指定值的列中所有Row

use serde_json::to_string_pretty;
use smartsheet_rs::{ColumnMapper, RowGetter, SmartsheetApi};

// TODO update these values as needed
const SHEET_ID: u64 = 1234567890;

// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

#[tokio::main]
async fn main() -> Result<()> {
    let smart = SmartsheetApi::from_env()?;

    let sheet = smart.get_sheet(SHEET_ID).await?;
    let cols = ColumnMapper::from(&sheet);

    // Create a `RowGetter` helper to find rows in a sheet by a condition
    // based on a *Column Name* and *Column Value*.
    let get_row = RowGetter::new(&sheet.rows, &cols);

    let row = get_row
        // Note: "My Value" can be either a String, Number, or Boolean.
        .where_eq("Column 1", "My Value")?
        // Only want to get the first row which matches the condition.
        .first()?;

    let rows = get_row
        // Retrieve *all* rows that *do not* match the specified cell value.
        .where_ne("Column 2", 123.45)?
        .find_all()?;

    // Print the match for the first query
    println!("Here's the first result: {:#?}", *row);

    // Print the list of rows that match the second query
    println!("Found {} Rows that match the second condition:", rows.len());
    println!("{}", to_string_pretty(&rows)?);

    Ok(())
}

类似于从Row中检索多个Cell对象的示例,可以使用Sheet::id_to_row方法构建每个行ID与其相关Row对象的映射。这在使用行ID值搜索多个Row对象时很有用。

创建行

要添加或更新行,需要构建一个单元格列表以更新其值,然后将单元格添加到行中。可以使用辅助结构体CellFactory来构建要添加到Row中的Cell对象。

请注意,要添加行,需要传递一个位置指定符属性。要更新行,我们只需要设置每个行的行ID

以下是一个将新的Row添加到工作表的示例。在这里,我们将位置指定符to_top设置为将新行发送到工作表的顶部。

use serde_json::to_string_pretty;
use smartsheet_rs::models::{Decision, LightPicker, Row, RowLocationSpecifier};
use smartsheet_rs::{CellFactory, ColumnMapper, SmartsheetApi};

// TODO update these values as needed
const SHEET_ID: u64 = 1234567890;

// A simple type alias so as to DRY.
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

#[tokio::main]
async fn main() -> Result<()> {
    let smart = SmartsheetApi::from_env()?;

    let index_result = smart.list_columns(SHEET_ID).await?;
    let cols = ColumnMapper::from(&index_result);

    // Create a `CellFactory` helper to build out a list of cells to create
    // a `Row` from.
    let make = CellFactory::new(&cols);

    // Create the `Cell` objects to add here.
    let cells = [
        make.cell("Text/Number Column", 123.45)?,
        make.cell("Symbol Column #1", LightPicker::Yellow)?,
        make.cell("Symbol Column #2", Decision::Hold)?,
        make.cell("Checkbox Column", true)?,
        make.contact_cell("Assigned To", "[email protected]")?,
        make.url_hyperlink_cell("Link to Page", "Rust Homepage", "https://rust-lang.net.cn")?,
        make.multi_picklist_cell(
            "Multi Dropdown Column",
            &["Hello, world!", "Testing", "1 2 3"],
        )?,
    ];

    // Create a new `Row` from the list of `Cell` objects.
    let row_to_add = Row::from(&cells);
    println!("Input Object: {}", to_string_pretty(&row_to_add)?);
    
    // Add the Rows to the Sheet
    let _ = smart.add_rows(SHEET_ID, [row_to_add].to_top(true)).await?;

    Ok(())
}

依赖项和功能

该库只使用所需的最小依赖项,以保持整体大小较小。此crate内部使用hyperhyper-rustls,用于向Smartsheet API发起HTTPS请求。

虽然选择hyper-rustls作为默认的TLS实现,因为它在为x86_64-unknown-linux-musl目标交叉编译时没有问题,这对于AWS Lambda部署来说是常见的,但仍可以选择使用依赖OpenSSL的本地hyper-tls实现。

为此,请禁用默认的"rust-tls"功能并启用"native-tls"功能

[dependencies]
smartsheet-rs = { version = "0.6.2", default-features = false, features = ["native-tls", "logging", "serde-std"] }

贡献

欢迎贡献!打开一个pull request来修复错误,或者打开一个issue来讨论新功能或变更。

有关更多信息,请查看文档中的贡献部分。

许可证

本项目自豪地采用MIT许可协议(LICENSEhttp://opensource.org/licenses/MIT)。

smartsheet-rs可根据MIT许可协议进行分发。贡献将在同一许可下接受。

作者

依赖项

~5–19MB
~276K SLoC