This repository has been archived by the owner on Dec 23, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Massive refactor to significantly improve the code structure, remove …
…unnecessary modules, and provide cleaner interfaces
- Loading branch information
Showing
62 changed files
with
2,448 additions
and
1,987 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
[package] | ||
name = "scipio-airtable" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
anyhow = "1.0.89" | ||
chrono = { version = "0.4.38", features = ["serde"] } | ||
derive_builder = "0.20.2" | ||
dotenvy = "0.15.7" | ||
reqwest = { version = "0.12.8", features = ["json", "rustls-tls"] } | ||
reqwest-middleware = "0.3.3" | ||
reqwest-retry = "0.6.1" | ||
scipio-macros = { path = "../scipio-macros" } | ||
serde = { version = "1.0.210", features = ["derive"] } | ||
serde_json = "1.0.128" | ||
serde_with = "3.11.0" | ||
tokio = { version = "1.40.0", features = ["full"] } | ||
|
||
[dev-dependencies] | ||
rstest = "0.23.0" | ||
|
||
|
||
[features] | ||
default = [] | ||
integration = [] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
use anyhow::Result; | ||
|
||
use super::responses::{ListBasesResponse, SchemaResponse}; | ||
use crate::Airtable; | ||
|
||
impl Airtable { | ||
pub async fn list_bases(&self, offset: Option<String>) -> Result<ListBasesResponse> { | ||
let mut url = "https://api.airtable.com/v0/meta/bases".to_owned(); | ||
if let Some(offset) = offset { | ||
url.push_str(&format!("?offset={}", offset)); | ||
} | ||
let data = self.http.get(&url).send().await?.json::<ListBasesResponse>().await?; | ||
Ok(data) | ||
} | ||
|
||
pub async fn get_base_schema( | ||
&self, | ||
base_id: &str, | ||
include: Vec<String>, | ||
) -> Result<SchemaResponse> { | ||
let query = | ||
include.iter().map(|v| format!("include={}", v)).collect::<Vec<String>>().join("&"); | ||
|
||
let url = format!("https://api.airtable.com/v0/meta/bases/{base_id}/tables?{query}"); | ||
|
||
let data = self.http.get(url).send().await?.json::<SchemaResponse>().await?; | ||
|
||
Ok(data) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use serde::{Deserialize, Deserializer, Serialize}; | ||
use serde_json::Value; | ||
|
||
/// The permission level for a base. | ||
/// | ||
/// More information about this definition can be found | ||
/// [here](https://airtable.com/developers/web/api/list-bases). We have an `Other` variant to | ||
/// ensure forward compatibility with new permission levels if Airtable introduces them. | ||
#[derive(Debug, Serialize, Clone, Eq, PartialEq)] | ||
#[serde(rename_all = "lowercase")] | ||
pub enum PermissionLevel { | ||
None, | ||
Read, | ||
Comment, | ||
Edit, | ||
Create, | ||
Other(String), | ||
} | ||
|
||
impl<'de> Deserialize<'de> for PermissionLevel { | ||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> | ||
where | ||
D: Deserializer<'de>, | ||
{ | ||
let s: String = Deserialize::deserialize(deserializer)?; | ||
|
||
match s.as_ref() { | ||
"none" => Ok(PermissionLevel::None), | ||
"read" => Ok(PermissionLevel::Read), | ||
"comment" => Ok(PermissionLevel::Comment), | ||
"edit" => Ok(PermissionLevel::Edit), | ||
"create" => Ok(PermissionLevel::Create), | ||
_ => Ok(PermissionLevel::Other(s)), | ||
} | ||
} | ||
} | ||
|
||
/// A base in Airtable. | ||
/// | ||
/// * `id`: The ID of the base | ||
/// * `name`: The name of the base | ||
/// * `permission_level`: The permission level the API token used to fetch the base has on the | ||
/// base. | ||
/// | ||
/// More information about this definition can be found | ||
/// [here](https://airtable.com/developers/web/api/list-bases) | ||
#[derive(Debug, Serialize, Deserialize, Clone)] | ||
pub struct Base { | ||
pub id: String, | ||
pub name: String, | ||
#[serde(rename = "permissionLevel")] | ||
pub permission_level: PermissionLevel, | ||
} | ||
|
||
/// A field in an Airtable table. | ||
/// | ||
/// * `id`: The ID of the field | ||
/// * `_type`: The type of the field | ||
/// * `name`: The name of the field | ||
/// * `description`: The description of the field | ||
/// * `options`: Custom options for the field | ||
#[serde_with::skip_serializing_none] | ||
#[derive(Clone, Debug, Serialize, Deserialize)] | ||
pub struct Field { | ||
pub id: String, | ||
#[serde(rename = "type")] | ||
pub _type: Option<String>, | ||
pub name: String, | ||
pub description: Option<String>, | ||
pub options: Option<Value>, | ||
} | ||
|
||
/// A view in an Airtable table. | ||
/// | ||
/// * `id`: The ID of the view | ||
/// * `_type`: The type of the view | ||
/// * `name`: The name of the view | ||
/// * `visible_field_ids`: The IDs of the fields that are visible in the view | ||
#[serde_with::skip_serializing_none] | ||
#[derive(Clone, Debug, Serialize, Deserialize)] | ||
pub struct View { | ||
pub id: String, | ||
#[serde(rename = "type")] | ||
pub _type: String, | ||
pub name: String, | ||
pub visible_field_ids: Option<Vec<String>>, | ||
} | ||
|
||
/// A table in an Airtable base. | ||
/// | ||
/// * `id`: The ID of the table | ||
/// * `primary_field_id`: The ID of the primary field in the table | ||
/// * `name`: The name of the table | ||
/// * `description`: The description of the table | ||
/// * `fields`: The fields in the table | ||
/// * `views`: The views in the table | ||
#[serde_with::skip_serializing_none] | ||
#[derive(Clone, Debug, Serialize, Deserialize)] | ||
pub struct Table { | ||
pub id: String, | ||
#[serde(rename = "primaryFieldId")] | ||
pub primary_field_id: String, | ||
pub name: String, | ||
pub description: Option<String>, | ||
// #[serde(skip_serializing)] | ||
pub fields: Vec<Field>, | ||
pub views: Vec<View>, | ||
} | ||
|
||
/// A record in an Airtable table. | ||
/// | ||
/// * `id`: The ID of the record. | ||
/// * `fields`: The fields of the record. | ||
/// * `created_time`: When the record was created. | ||
#[derive(Clone, Debug, Serialize, Deserialize)] | ||
#[serde(rename_all = "camelCase")] | ||
pub struct Record<T> { | ||
pub id: String, | ||
pub fields: T, | ||
pub created_time: String, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
pub mod bases; | ||
pub mod entities; | ||
pub mod records; | ||
pub mod responses; | ||
|
||
#[cfg(test)] | ||
mod tests; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
use serde::{Deserialize, Serialize}; | ||
|
||
use super::entities::{Base, Record, Table}; | ||
|
||
/// Base response from the Airtable API. | ||
/// | ||
/// * `offset`: The offset to start listing bases from if we need to fetch more. | ||
/// * `bases`: The bases returned from the API. | ||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct ListBasesResponse { | ||
pub offset: Option<String>, | ||
pub bases: Vec<Base>, | ||
} | ||
|
||
#[derive(Clone, Debug, Serialize, Deserialize)] | ||
pub struct SchemaResponse { | ||
pub tables: Vec<Table>, | ||
} | ||
|
||
/// Response from the Airtable API for listing records. | ||
/// | ||
/// * `records`: The records returned from the API. | ||
/// * `offset`: The offset to start listing records from if we need to fetch more. (a pagination | ||
/// token). | ||
#[derive(Clone, Debug, Serialize, Deserialize)] | ||
pub struct ListRecordsResponse<T> { | ||
pub records: Vec<Record<T>>, | ||
pub offset: Option<String>, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
use rstest::rstest; | ||
|
||
use super::fixtures::airtable; | ||
use crate::Airtable; | ||
|
||
#[cfg(feature = "integration")] | ||
#[rstest] | ||
#[tokio::test] | ||
pub async fn test_list_bases(airtable: Airtable) { | ||
let bases_response = airtable.list_bases(None).await.unwrap(); | ||
dbg!(&bases_response); | ||
} | ||
|
||
// #[cfg(feature = "integration")] | ||
// #[rstest] | ||
// #[tokio::test] | ||
// pub async fn test_get_base_schema(airtable: Airtable) { | ||
// let bases_response = airtable.list_bases(None).await.unwrap(); | ||
// let base_id = | ||
// bases_response.bases.first().expect("there should be at least one base").id.as_str(); | ||
// let schema_response = airtable.get_base_schema(base_id, vec![]).await.unwrap(); | ||
// dbg!(&schema_response); | ||
// } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
use std::env; | ||
|
||
use rstest::fixture; | ||
|
||
use crate::Airtable; | ||
|
||
#[fixture] | ||
pub fn airtable() -> Airtable { | ||
dotenvy::dotenv().expect("error loading environment variables"); | ||
let api_token = env::var("AIRTABLE_API_TOKEN").expect("missing AIRTABLE_API_TOKEN variable"); | ||
Airtable::new(&api_token, 5).expect("error creating Airtable client") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
mod bases; | ||
mod fixtures; | ||
mod records; |
Oops, something went wrong.