Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/rnag/smartsheet-rs
Async Smartsheet API implementation in Rust
https://github.com/rnag/smartsheet-rs
async hyper-rustls rust smartsheet smartsheet-api
Last synced: about 1 month ago
JSON representation
Async Smartsheet API implementation in Rust
- Host: GitHub
- URL: https://github.com/rnag/smartsheet-rs
- Owner: rnag
- License: mit
- Created: 2022-02-07T00:06:07.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2023-02-06T03:16:58.000Z (almost 2 years ago)
- Last Synced: 2024-09-17T20:10:57.692Z (2 months ago)
- Topics: async, hyper-rustls, rust, smartsheet, smartsheet-api
- Language: Rust
- Homepage:
- Size: 151 KB
- Stars: 4
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# smartsheet-rs
[](https://github.com/rnag/smartsheet-rs)
[](https://crates.io/crates/smartsheet-rs)
[](https://docs.rs/smartsheet-rs)
[](https://github.com/rnag/smartsheet-rs/actions?query=branch%3Amain)**smartsheet-rs** is a *rust crate* which provides an `async` wrapper API that lets you easily interact
with the [Smartsheet API 2.0](https://smartsheet-platform.github.io/api-docs/).This is an *unofficial* SDK I have made to learn Rust a little, but I hope you have fun with it --
I know that I certainly had quite a bit of fun in writing out the implementation for
this crate.## Table of Contents
* [Getting Started](#getting-started)
* [Implemented Methods](#implemented-methods)
* [A Larger Example](#a-larger-example)
* [Cells](#cells)
* [Retrieve Cells](#retrieve-cells)
* [Multi-Contact Cells](#multi-contact-cells)
* [Rows](#rows)
* [Retrieve Rows](#retrieve-rows)
* [Create Rows](#create-rows)
* [Dependencies and Features](#dependencies-and-features)
* [Contributing](#contributing)
* [License](#license)
* [Authors](#authors)## Getting Started
Getting started with the `smartsheet-rs` library is easy:
1. Set **SMARTSHEET_ACCESS_TOKEN** in your environment; you can
also use the `SmartsheetApi::from_token` constructor
to explicitly set the token value.
Find out more about [Authentication and Access Tokens](https://smartsheet-platform.github.io/api-docs/#authentication-and-access-tokens)
in the Smartsheet API Documentation.3. Add these dependencies to your `Cargo.toml`:
```toml
[dependencies]
smartsheet-rs = "0.6.2"
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
```3. Add some usage to your application:
```rust
use smartsheet_rs::SmartsheetApi;#[tokio::main]
async fn main() -> std::result::Result<(), Box> {
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(())
}
```## Implemented Methods
The following API methods from the [official documentation](https://smartsheet-platform.github.io/api-docs)
have been implemented currently:- [List Sheets](https://smartsheet-platform.github.io/api-docs/#list-sheets)
- [List Columns](https://smartsheet-platform.github.io/api-docs/#list-columns)
- [List Attachments](https://smartsheet-platform.github.io/api-docs/#list-attachments)
- [Get Sheet](https://smartsheet-platform.github.io/api-docs/#get-sheet)
- [Get Column](https://smartsheet-platform.github.io/api-docs/#get-column)
- [Get Attachment](https://smartsheet-platform.github.io/api-docs/#get-attachment)
- [Get Row](https://smartsheet-platform.github.io/api-docs/#get-row)
- [Add Rows](https://smartsheet-platform.github.io/api-docs/#add-rows)
- [Update Rows](https://smartsheet-platform.github.io/api-docs/#update-rows)
- [Delete Rows](https://smartsheet-platform.github.io/api-docs/#delete-rows)You can check out sample usage of these API methods in the [examples/](https://github.com/rnag/smartsheet-rs/tree/main/examples)
folder in the project repo on GitHub.## A Larger Example
When working with rows and cells in the SmartSheet API, one thing that
stands out is that the API purposefully identifies columns by their *ID*,
rather than their title or *column name*.However, as humans it's much more natural and convenient to refer to *column names*
when working with the data.
Towards that end, the **smartsheet-rs** crate provides helper *struct* implementations
such as the `ColumnMapper`, `CellGetter`, and `RowGetter` in order to simplify interaction
with the Smartsheet API.### Cells
#### Retrieve Cells
To retrieve an individual cell from a row by its associated *column id*, you can simply use `Row::get_cell_by_id`.
To instead retrieve a single `Cell` by its *column name*, you can first
build out a mapping of *Column Name to Id* with a `ColumnMapper`, and then
pair that with `CellGetter` in order to retrieve a Cell from a Row.Here's a quick example of how that would work:
```rust
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 = std::result::Result>;#[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(())
}
```The `CellGetter::by_name` method works by iterating over each cell in the row,
and then returning the first `Cell` where the *Column Id* for the cell
matches the specified *column name*.If the need arises to retrieve *multiple* `Cell` objects from a `Row` by their column names,
it might be a better idea to first build out a mapping of each *column name* to the
`Cell` object in the row for that column. The method `CellGetter::name_to_cell` can be used
for this purpose, as shown below.```rust
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 Cells
When working with more [complex objects] such as cells for a `MULTI_CONTACT`
column type, the helper method `Cell::contacts` can be used to extract the
contact info from the cell. Note that to retrieve the emails for each contact,
it's necessary to pass the `include=objectValue` query parameter, along with the
corresponding `level` parameter, in order to gather the full *Multi-contact* details.[complex objects]: https://smartsheet.redoc.ly/#section/API-Basics/Multi-contact-or-Multi-picklist:-Working-with-Complex-Objects
Here is the relevant part of the code which demonstrates the ideal way of processing
`MULTI_CONTACT` cell data for a given row:```rust
// 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 *
let names = contacts.name_addrs();
```For the full code, check out the [`cell_multi_contact`] example in the project repo.
[`cell_multi_contact`]: https://github.com/rnag/smartsheet-rs/blob/main/examples/cell_multi_contact.rs
### Rows
#### Retrieve Rows
To retrieve an individual row from a sheet by its associated *row id*, you can simply use `Sheet::get_row_by_id`.
If the goal is to find one or more rows that match a specified condition from a list of rows, you can use the `RowGetter` helper
to make the task much more convenient.Here's a simple example to find the **first** `Row` where a `Cell` from a column has a particular value, and find
**all** `Row`s where a `Cell` from a column does *not* have a specified value.```rust
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 = std::result::Result>;#[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(())
}
```Similar to the example of retrieving *multiple* `Cell` objects from a `Row`,
the `Sheet::id_to_row` method can be used to build out a mapping of each *row id* to
its associated `Row` object. This can be useful when searching for multiple `Row` objects
by their *row id* value.#### Create Rows
To add or update rows, it's necessary to build out a list of cells to update the
values for, and then add the cells to the row.
The helper *struct* `CellFactory` can be used to construct `Cell` objects to add to a `Row`.Note that to *add* rows, we need to pass in a [location-specifier](https://smartsheet.redoc.ly/#section/Specify-Row-Location) attribute.
To *update* rows, we only need to set the *Row Id* for each row.An example of *adding* a new `Row` to a sheet is shown below. Here we set the location specifier
`to_top` to send the new row to the top of the sheet.```rust
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 = std::result::Result>;#[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.org")?,
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(())
}
```## Dependencies and Features
This library uses only the minimum required dependencies, in order
to keep the overall size small. This crate uses [`hyper`][] and [`hyper-rustls`][]
internally, to make HTTPS requests to the Smartsheet API.While `hyper-rustls` was chosen as the default TLS implementation
because it works without issue when cross-compiling for the
**x86_64-unknown-linux-musl** target as is common for [AWS Lambda][]
deployments, it is still possible to instead use the native [`hyper-tls`][]
implementation, which relies on OpenSSL.To do this, disable the default "rust-tls" feature and enable the "native-tls" feature:
```toml
[dependencies]
smartsheet-rs = { version = "0.6.2", default-features = false, features = ["native-tls", "logging", "serde-std"] }
```[`hyper`]: https://docs.rs/hyper
[`hyper-rustls`]: https://docs.rs/hyper-rustls
[`hyper-tls`]: https://docs.rs/hyper-tls
[AWS Lambda]: https://docs.aws.amazon.com/sdk-for-rust/latest/dg/lambda.html## Contributing
Contributions are welcome! Open a pull request to fix a bug, or [open an issue][]
to discuss a new feature or change.Check out the [Contributing][] section in the docs for more info.
[Contributing]: CONTRIBUTING.md
[open an issue]: https://github.com/rnag/smartsheet-rs/issues## License
This project is proudly licensed under the MIT license ([LICENSE](LICENSE)
or http://opensource.org/licenses/MIT).`smartsheet-rs` can be distributed according to the MIT license. Contributions
will be accepted under the same license.## Authors
* [Ritvik Nag](https://github.com/rnag)