feat: add APIResult
Some checks failed
Test / build (map[name:stable]) (push) Failing after 56s
Test / build (map[name:nightly]) (push) Failing after 1m12s

This is a generic type that contains the raw JSON response and the parsed value as a Result.

fixup: APIResult

FIXUP: APIResutl can handle missing response bodies
This commit is contained in:
Paul Campbell 2024-12-06 08:31:34 +00:00
parent 70aa002048
commit 16618f02d6
32 changed files with 19719 additions and 2 deletions

View file

@ -10,7 +10,7 @@ color-eyre = "0.6"
derive_more = { version = "1.0", features = [ derive_more = { version = "1.0", features = [
"as_ref", "as_ref",
"constructor", "constructor",
# "deref", "deref",
# "display", # "display",
# "from", # "from",
#] } #] }
@ -20,7 +20,7 @@ derive_more = { version = "1.0", features = [
# kxio = {path = "../kxio/"} # kxio = {path = "../kxio/"}
kxio = "3.2" kxio = "3.2"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
#serde_json = "1.0" serde_json = "1.0"
tokio = { version = "1.41", features = ["full"] } tokio = { version = "1.41", features = ["full"] }
toml = "0.8" toml = "0.8"
#tracing= "0.1" #tracing= "0.1"

32
src/api_result.rs Normal file
View file

@ -0,0 +1,32 @@
//
// use kxio::net::Response;
//
// use crate::{e, s};
// pub struct APIResult<T> {
// pub text: String,
// pub result: Result<T, kxio::net::Error>,
// }
// impl<T: for<'a> serde::Deserialize<'a>> APIResult<T> {
// pub async fn new(response: kxio::net::Result<Response>) -> Self {
// match response {
// Ok(response) => {
// let text = response.text().await.unwrap_or_default();
// let text = if text.is_empty() { s!("null") } else { text };
// let result = serde_json::from_str::<T>(&text)
// .map_err(|e| e.to_string())
// .map_err(|e| {
// e!("{e}: {text}");
// e
// })
// .map_err(kxio::net::Error::from);
// Self { text, result }
// }
// Err(e) => Self {
// text: s!(""),
// result: Err(e),
// },
// }
// }
// }

View file

@ -4,10 +4,12 @@ use std::path::PathBuf;
use clap::Parser; use clap::Parser;
use kxio::{fs::FileSystem, net::Net}; use kxio::{fs::FileSystem, net::Net};
mod api_result;
mod config; mod config;
mod init; mod init;
mod macros; mod macros;
mod template; mod template;
mod trello;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;

41
src/trello/api/boards.rs Normal file
View file

@ -0,0 +1,41 @@
//
// use crate::trello::types::board::TrelloBoard;
// use color_eyre::Result;
// use kxio::net::Net;
//
// use crate::{
// f,
// trello::{
// types::{TrelloAuth, TrelloBoardId},
// url,
// },
// };
// use crate::trello::types::TrelloBoardName;
// pub async fn get_board(
// auth: &TrelloAuth,
// board_id: &TrelloBoardId,
// net: &Net,
// ) -> Result<TrelloBoard> {
// let board = net
// .get(url(f!(
// "/boards/{}/?fields=name&lists=all&list_fields=all&cards=all&card_fields=all",
// **board_id
// )))
// .headers(auth.into())
// .send()
// .await?
// .json()
// .await?;
// Ok(board)
// }
// pub trait TrelloBoards {
// fn find_by_name(&self, board_name: &TrelloBoardName) -> Option<&TrelloBoard>;
// }
// impl TrelloBoards for Vec<TrelloBoard> {
// fn find_by_name(&self, board_name: &TrelloBoardName) -> Option<&TrelloBoard> {
// self.iter().find(|b| &b.name == board_name)
// }
// }

View file

@ -0,0 +1,61 @@
//
use color_eyre::Result;
use kxio::net::Net;
use serde_json::json;
use crate::trello::{
types::{NewTrelloCard, TrelloAuth, TrelloCard},
url,
};
///
/// POST /cards
///
/// Query Pparameters
///
/// - name string
/// The name for the card
/// - desc string
/// The description of the card
/// - pos string
/// The position of the card. top, bottom, or a positive number.
/// - due string
/// A due date for the card. A date
/// - start string
/// A start date for the card. A date, or null
/// - dueComplete bool
/// If the due date has been marked complete
/// - idList string REQUIRED
/// The ID of the list the card should be added to
/// - idMembers string
/// A comma-separated list of memberIds to add to the card
/// - idLabels string
/// A comma-separated list of labelIds to add to the card
pub async fn create_card(
auth: &TrelloAuth,
new_card: NewTrelloCard,
net: &Net,
) -> Result<TrelloCard> {
let card = net
.post(url("/cards"))
.headers(auth.into())
.body(
json!({
"idList": new_card.list_id(),
"name": new_card.name(),
"pos": "bottom",
})
.to_string(),
)
.send()
.await?
.json()
.await?;
Ok(card)
}
// #[derive(Debug, serde::Serialize)]
// pub struct CreateCardRequest {
// name: String,
// pos: String,
// }

View file

@ -0,0 +1,21 @@
//
use color_eyre::Result;
use kxio::net::Net;
use crate::{
f,
trello::{types::TrelloAuth, url, TrelloCardId},
};
pub async fn delete_card(
auth: &TrelloAuth,
remote_task_id: &TrelloCardId,
net: &Net,
) -> Result<()> {
net.delete(url(f!("/cards/{remote_task_id}")))
.headers(auth.into())
.send()
.await?;
Ok(())
}

View file

@ -0,0 +1,96 @@
//
use color_eyre::Result;
use kxio::net::Net;
use crate::{
f,
trello::{
types::{TrelloAuth, TrelloCard},
url, TrelloCardId,
},
};
/// Get a Card
///
/// https://developer.atlassian.com/cloud/trello/rest/api-group-cards/#api-cards-id-get
///
/// GET /cards/{id}
///
/// Get a card by its ID
///
/// Request
///
/// Path parameters
///
/// id TrelloID REQUIRED
///
/// Query parameters
///
/// - fields string
/// Which fields to return.
/// Default: `all`
/// `all` or a comma-separated list of fields.
/// Defaults: `badges, checkItemStates, closed, dateLastActivity, desc, descData,
/// due, start, email, idBoard, idChecklists, idLabels, idList, idMembers, idShort, idAttachmentCover,
/// manualCoverAttachment, labels, name, pos, shortUrl, url`
///
/// - actions string
/// See the [Actions Nested Resource](https://developer.atlassian.com/cloud/trello/guides/rest-api/nested-resources/#actions-nested-resource)
///
/// - attachments oneOf [string, boolean]
/// true, false, or cover
/// Default: false
///
/// - attachment_fields string
/// `all` or a comma-separated list of attachment fields
/// Default: `all`
///
/// - members boolean
/// Whether to return member objects for members on the card
/// Default: false
///
/// - member_fields string
/// `all` or a comma-separated list of member fields. Defaults: `avatarHash, fullName, initials, username`
///
/// - checkItemStates boolean
/// Whether to return checkItemState objects for checklists on the card
/// Default: false
///
/// - checklists string
/// Whether to return the checklists on the card. all or none
/// Default: none
///
/// - checklist_fields string
/// `all` or a comma-separated list of idBoard,idCard,name,pos
/// Default: `all`
///
/// - board boolean
/// Whether to return the board object the card is on
/// Default: false
///
/// - board_fields string
/// `all` or a comma-separated list of board fields. Defaults: `name, desc, descData, closed, idOrganization, pinned, url, prefs`
///
/// - list boolean
/// See the [Lists Nested Resource](https://developer.atlassian.com/cloud/trello/guides/rest-api/nested-resources/#lists-nested-resource)
///
/// Responses
///
/// 200 OK Success
///
/// application/json
pub async fn get_card(
auth: &TrelloAuth,
trello_card_id: &TrelloCardId,
net: &Net,
) -> Result<TrelloCard> {
let card = net
.get(url(f!("/cards/{trello_card_id}")))
.headers(auth.into())
.send()
.await?
.json()
.await?;
Ok(card)
}

View file

@ -0,0 +1,9 @@
mod create;
mod delete;
mod get;
mod update;
pub use create::create_card;
pub use delete::delete_card;
pub use get::get_card;
pub use update::{update_card, TrelloCardUpdate};

View file

@ -0,0 +1,49 @@
//
use color_eyre::Result;
use kxio::net::Net;
use serde_json::json;
use crate::{
f,
trello::{
types::{TrelloAuth, TrelloCard},
url, TrelloCardId, TrelloCardName, TrelloListId,
},
};
pub async fn update_card(
auth: &TrelloAuth,
card_update: TrelloCardUpdate,
net: &Net,
) -> Result<TrelloCard> {
let net_url = url(f!("/cards/{}", card_update.id));
let card = net
.put(net_url)
.headers(auth.into())
.body(
json!({
"idList": card_update.id_list,
"name": card_update.name,
})
.to_string(),
)
.send()
.await?
.json()
.await?;
Ok(card)
}
#[derive(Debug, PartialEq, Eq, serde::Serialize)]
pub struct TrelloCardUpdate {
id: TrelloCardId,
name: TrelloCardName,
#[serde(rename = "idList")]
id_list: TrelloListId,
}
impl TrelloCardUpdate {
pub const fn new(id: TrelloCardId, name: TrelloCardName, id_list: TrelloListId) -> Self {
Self { id, name, id_list }
}
}

47
src/trello/api/lists.rs Normal file
View file

@ -0,0 +1,47 @@
//
use color_eyre::Result;
use kxio::net::Net;
use crate::{
f,
trello::{
types::{TrelloAuth, TrelloCard, TrelloList},
url,
},
};
/// Get Cards in a List
///
/// https://developer.atlassian.com/cloud/trello/rest/api-group-lists/#api-lists-id-cards-get
///
/// GET /lists/{id}/cards
///
/// List the cards in a list
///
/// Request
///
/// Path parameters
///
/// id TrelloID REQUIRED
///
/// Responses
///
/// 200 OK Success
///
/// application/json
pub async fn get_lists_cards(
auth: &TrelloAuth,
list: &TrelloList,
net: &Net,
) -> Result<Vec<TrelloCard>> {
let net_url = url(f!("/lists/{}/cards", **list.id()));
let cards = net
.get(net_url)
.headers(auth.into())
.send()
.await?
.json()
.await?;
Ok(cards)
}

54
src/trello/api/members.rs Normal file
View file

@ -0,0 +1,54 @@
//
// use kxio::net::Net;
// use crate::api_result::APIResult;
// use crate::{
// f,
// trello::{
// types::{TrelloAuth, board::TrelloBoard},
// url,
// },
// };
//
// /// Get lists from named board that Member belongs to
// ///
// /// Get Boards that Member belongs to
// /// https://developer.atlassian.com/cloud/trello/rest/api-group-members/#api-members-id-boards-get
// /// /members/{id}/boards
// ///
// /// Lists the boards that the user is a member of.
// ///
// /// Request
// ///
// /// Path parameters
// ///
// /// - id TrelloID REQUIRED
// ///
// ///
// /// Query parameters
// ///
// /// - fields string
// /// Default: all
// /// Valid values: id, name, desc, descData, closed, idMemberCreator, idOrganization, pinned, url, shortUrl, prefs, labelNames, starred, limits, memberships, enterpriseOwned
// ///
// /// - lists string
// /// Which lists to include with the boards. One of: all, closed, none, open
// /// Default: none
// /// Valid values: all, closed, none, open
// ///
// /// curl --request GET \
// /// --url "https://api.trello.com/1/members/$TRELLO_USERNAME/boards?key=$TRELLO_KEY&token=$TRELLO_SECRET&lists=open" \
// /// --header 'Accept: application/json'
// pub async fn get_boards_that_member_belongs_to(
// auth: &TrelloAuth,
// net: &Net,
// ) -> APIResult<Vec<TrelloBoard>> {
// APIResult::new(
// net.get(url(f!("/members/{}/boards?lists=open", **auth.user())))
// .headers(auth.into())
// .header("Accept", "application/json")
// .send()
// .await,
// )
// .await
// }

9
src/trello/api/mod.rs Normal file
View file

@ -0,0 +1,9 @@
//
pub mod boards;
// pub mod cards;
// pub mod lists;
pub mod members;
#[cfg(test)]
mod tests;

View file

@ -0,0 +1,15 @@
// use kxio::net::MockNet;
// use crate::trello::types::auth::{TrelloApiKey, TrelloApiSecret, TrelloAuth, TrelloUser};
// pub(crate) fn a_network() -> MockNet {
// kxio::net::mock()
// }
// pub(crate) fn an_auth() -> TrelloAuth {
// TrelloAuth::new(
// TrelloApiKey::new("foo"),
// TrelloApiSecret::new("bar"),
// TrelloUser::new("baz"),
// )
// }

View file

@ -0,0 +1,59 @@
// use super::*;
mod given;
// type TestResult = color_eyre::Result<()>;
mod members {
// use std::collections::HashMap;
// use kxio::net::StatusCode;
// use serde_json::json;
// use crate::{
// s,
// trello::{
// // api::members::get_boards_that_member_belongs_to,
// types::{board::TrelloBoard, TrelloBoardId, TrelloBoardName},
// },
// };
// use super::*;
//
// #[tokio::test]
// async fn get_member_boards() -> TestResult {
// //given
// let net = given::a_network();
// let auth = given::an_auth();
//
// net.on()
// .get("https://api.trello.com/1/members/baz/boards?lists=open")
// .headers(HashMap::from([
// (
// s!("authorization"),
// s!("OAuth oauth_consumer_key=\"foo\", oauth_token=\"bar\""),
// ),
// (s!("accept"), s!("application/json")),
// ]))
// .respond(StatusCode::OK)
// .body(s!(json!([
// {"id": "1", "name": "board-1", "lists":[]}
// ])))?;
//
// //when
// let result = get_boards_that_member_belongs_to(&auth, &net.into()).await;
//
// assert_eq!(
// result.result?,
// vec![TrelloBoard::new(
// TrelloBoardId::new("1"),
// TrelloBoardName::new("board-1"),
// vec![]
// )]
// );
//
// //then
// Ok(())
// }
}

14
src/trello/mod.rs Normal file
View file

@ -0,0 +1,14 @@
//
pub mod api;
pub mod types;
#[cfg(test)]
mod tests;
// use crate::f;
// pub fn url(path: impl Into<String>) -> String {
// let path = path.into();
// assert!(path.starts_with("/"));
// f!("https://api.trello.com/1{path}")
// }

View file

@ -0,0 +1 @@
[]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,88 @@
{
"id": "list-1-card-1-5abbe4b7ddc1b351ef961414",
"address": "<string>",
"badges": {
"attachmentsByType": {
"trello": {
"board": 2154,
"card": 2154
}
},
"location": true,
"votes": 2154,
"viewingMemberVoted": false,
"subscribed": false,
"fogbugz": "<string>",
"checkItems": 0,
"checkItemsChecked": 0,
"comments": 0,
"attachments": 0,
"description": true,
"due": "<string>",
"start": "<string>",
"dueComplete": true
},
"checkItemStates": [
"<string>"
],
"closed": true,
"coordinates": "<string>",
"creationMethod": "<string>",
"dateLastActivity": "2019-09-16T16:19:17.156Z",
"desc": "👋Hey there,\n\nTrello's Platform team uses this board to keep developers up-to-date.",
"descData": {
"emoji": {}
},
"due": "<string>",
"dueReminder": "<string>",
"email": "bentleycook+2kea95u7kchsvqnxkwe+2q0byi6qv4pt9uc7q5m+25qyyohtzg@boards.trello.com",
"idBoard": "5abbe4b7ddc1b351ef961414",
"idChecklists": [
{
"id": "5abbe4b7ddc1b351ef961414"
}
],
"idLabels": [
{
"id": "5abbe4b7ddc1b351ef961414",
"idBoard": "5abbe4b7ddc1b351ef961414",
"name": "Overdue",
"color": "yellow"
}
],
"idList": "id-for-inbox-5dbd5a88b09f840da3174607",
"idMembers": [
"5abbe4b7ddc1b351ef961414"
],
"idMembersVoted": [
"5abbe4b7ddc1b351ef961414"
],
"idShort": 2154,
"labels": [
"5abbe4b7ddc1b351ef961414"
],
"limits": {
"attachments": {
"perBoard": {
"status": "ok",
"disableAt": 36000,
"warnAt": 32400
}
}
},
"locationName": "<string>",
"manualCoverAttachment": false,
"name": "👋 What? Why? How?",
"pos": 65535,
"shortLink": "H0TZyzbK",
"shortUrl": "https://trello.com/c/H0TZyzbK",
"subscribed": false,
"url": "https://trello.com/c/H0TZyzbK/4-%F0%9F%91%8B-what-why-how",
"cover": {
"color": "yellow",
"idUploadedBackground": true,
"size": "normal",
"brightness": "light",
"isTemplate": false
}
}

View file

@ -0,0 +1,86 @@
[
{
"id": "5abbe4b7ddc1b351ef961414",
"address": "<string>",
"badges": {
"attachmentsByType": {
"trello": {
"board": 2154,
"card": 2154
}
},
"location": true,
"votes": 2154,
"viewingMemberVoted": false,
"subscribed": false,
"fogbugz": "<string>",
"checkItems": 0,
"checkItemsChecked": 0,
"comments": 0,
"attachments": 0,
"description": true,
"due": "<string>",
"start": "<string>",
"dueComplete": true
},
"checkItemStates": [
"<string>"
],
"closed": true,
"coordinates": "<string>",
"creationMethod": "<string>",
"dateLastActivity": "2019-09-16T16:19:17.156Z",
"desc": "👋Hey there,\n\nTrello's Platform team uses this board to keep developers up-to-date.",
"descData": {
"emoji": {}
},
"due": "<string>",
"dueReminder": "<string>",
"email": "bentleycook+2kea95u7kchsvqnxkwe+2q0byi6qv4pt9uc7q5m+25qyyohtzg@boards.trello.com",
"idBoard": "5abbe4b7ddc1b351ef961414",
"idChecklists": [
{
"id": "5abbe4b7ddc1b351ef961414"
}
],
"idLabels": [
{
"id": "5abbe4b7ddc1b351ef961414",
"idBoard": "5abbe4b7ddc1b351ef961414",
"name": "Overdue",
"color": "yellow"
}
],
"idList": "5abbe4b7ddc1b351ef961414",
"idMembers": [
"5abbe4b7ddc1b351ef961414"
],
"idMembersVoted": [
"5abbe4b7ddc1b351ef961414"
],
"idShort": 2154,
"labels": [
"5abbe4b7ddc1b351ef961414"
],
"limits": {
"attachments": {
"perBoard": {}
}
},
"locationName": "<string>",
"manualCoverAttachment": false,
"name": "👋 What? Why? How?",
"pos": 65535,
"shortLink": "H0TZyzbK",
"shortUrl": "https://trello.com/c/H0TZyzbK",
"subscribed": false,
"url": "https://trello.com/c/H0TZyzbK/4-%F0%9F%91%8B-what-why-how",
"cover": {
"color": "yellow",
"idUploadedBackground": true,
"size": "normal",
"brightness": "light",
"isTemplate": false
}
}
]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,86 @@
[
{
"id": "5abbe4b7ddc1b351ef961414",
"address": "<string>",
"badges": {
"attachmentsByType": {
"trello": {
"board": 2154,
"card": 2154
}
},
"location": true,
"votes": 2154,
"viewingMemberVoted": false,
"subscribed": false,
"fogbugz": "<string>",
"checkItems": 0,
"checkItemsChecked": 0,
"comments": 0,
"attachments": 0,
"description": true,
"due": "<string>",
"start": "<string>",
"dueComplete": true
},
"checkItemStates": [
"<string>"
],
"closed": true,
"coordinates": "<string>",
"creationMethod": "<string>",
"dateLastActivity": "2019-09-16T16:19:17.156Z",
"desc": "👋Hey there,\n\nTrello's Platform team uses this board to keep developers up-to-date.",
"descData": {
"emoji": {}
},
"due": "<string>",
"dueReminder": "<string>",
"email": "bentleycook+2kea95u7kchsvqnxkwe+2q0byi6qv4pt9uc7q5m+25qyyohtzg@boards.trello.com",
"idBoard": "5abbe4b7ddc1b351ef961414",
"idChecklists": [
{
"id": "5abbe4b7ddc1b351ef961414"
}
],
"idLabels": [
{
"id": "5abbe4b7ddc1b351ef961414",
"idBoard": "5abbe4b7ddc1b351ef961414",
"name": "Overdue",
"color": "yellow"
}
],
"idList": "list-1-5abbe4b7ddc1b351ef961414",
"idMembers": [
"5abbe4b7ddc1b351ef961414"
],
"idMembersVoted": [
"5abbe4b7ddc1b351ef961414"
],
"idShort": 2154,
"labels": [
"5abbe4b7ddc1b351ef961414"
],
"limits": {
"attachments": {
"perBoard": {}
}
},
"locationName": "<string>",
"manualCoverAttachment": false,
"name": "👋 What? Why? How?",
"pos": 65535,
"shortLink": "H0TZyzbK",
"shortUrl": "https://trello.com/c/H0TZyzbK",
"subscribed": false,
"url": "https://trello.com/c/H0TZyzbK/4-%F0%9F%91%8B-what-why-how",
"cover": {
"color": "yellow",
"idUploadedBackground": true,
"size": "normal",
"brightness": "light",
"isTemplate": false
}
}
]

View file

@ -0,0 +1,86 @@
[
{
"id": "5abbe4b7ddc1b351ef061414",
"address": "<string>",
"badges": {
"attachmentsByType": {
"trello": {
"board": 2154,
"card": 2154
}
},
"location": true,
"votes": 2154,
"viewingMemberVoted": false,
"subscribed": false,
"fogbugz": "<string>",
"checkItems": 0,
"checkItemsChecked": 0,
"comments": 0,
"attachments": 0,
"description": true,
"due": "<string>",
"start": "<string>",
"dueComplete": true
},
"checkItemStates": [
"<string>"
],
"closed": true,
"coordinates": "<string>",
"creationMethod": "<string>",
"dateLastActivity": "2019-09-16T16:19:17.156Z",
"desc": "👋Hey there,\n\nTrello's Platform team uses this board to keep developers up-to-date.",
"descData": {
"emoji": {}
},
"due": "<string>",
"dueReminder": "<string>",
"email": "bentleycook+2kea95u7kchsvqnxkwe+2q0byi6qv4pt9uc7q5m+25qyyohtzg@boards.trello.com",
"idBoard": "5abbe4b7ddc1b351ef961414",
"idChecklists": [
{
"id": "5abbe4b7ddc1b351ef961414"
}
],
"idLabels": [
{
"id": "5abbe4b7ddc1b351ef961414",
"idBoard": "5abbe4b7ddc1b351ef961414",
"name": "Overdue",
"color": "yellow"
}
],
"idList": "list-2-5list-2-abbe4b7ddc1b351ef961414",
"idMembers": [
"5abbe4b7ddc1b351ef961414"
],
"idMembersVoted": [
"5abbe4b7ddc1b351ef961414"
],
"idShort": 2154,
"labels": [
"5abbe4b7ddc1b351ef961414"
],
"limits": {
"attachments": {
"perBoard": {}
}
},
"locationName": "<string>",
"manualCoverAttachment": false,
"name": "Card from list 2",
"pos": 65535,
"shortLink": "H0TZyzbK",
"shortUrl": "https://trello.com/c/H0TZyzbK",
"subscribed": false,
"url": "https://trello.com/c/H0TZyzbK/4-%F0%9F%91%8B-what-why-how",
"cover": {
"color": "yellow",
"idUploadedBackground": true,
"size": "normal",
"brightness": "light",
"isTemplate": false
}
}
]

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,170 @@
[
{
"id": "list-1-card-1-5abbe4b7ddc1b351ef961414",
"address": "<string>",
"badges": {
"attachmentsByType": {
"trello": {
"board": 2154,
"card": 2154
}
},
"location": true,
"votes": 2154,
"viewingMemberVoted": false,
"subscribed": false,
"fogbugz": "<string>",
"checkItems": 0,
"checkItemsChecked": 0,
"comments": 0,
"attachments": 0,
"description": true,
"due": "<string>",
"start": "<string>",
"dueComplete": true
},
"checkItemStates": [
"<string>"
],
"closed": true,
"coordinates": "<string>",
"creationMethod": "<string>",
"dateLastActivity": "2019-09-16T16:19:17.156Z",
"desc": "👋Hey there,\n\nTrello's Platform team uses this board to keep developers up-to-date.",
"descData": {
"emoji": {}
},
"due": "<string>",
"dueReminder": "<string>",
"email": "bentleycook+2kea95u7kchsvqnxkwe+2q0byi6qv4pt9uc7q5m+25qyyohtzg@boards.trello.com",
"idBoard": "5abbe4b7ddc1b351ef961414",
"idChecklists": [
{
"id": "5abbe4b7ddc1b351ef961414"
}
],
"idLabels": [
{
"id": "5abbe4b7ddc1b351ef961414",
"idBoard": "5abbe4b7ddc1b351ef961414",
"name": "Overdue",
"color": "yellow"
}
],
"idList": "list-1-5abbe4b7ddc1b351ef961414",
"idMembers": [
"5abbe4b7ddc1b351ef961414"
],
"idMembersVoted": [
"5abbe4b7ddc1b351ef961414"
],
"idShort": 2154,
"labels": [
"5abbe4b7ddc1b351ef961414"
],
"limits": {
"attachments": {
"perBoard": {}
}
},
"locationName": "<string>",
"manualCoverAttachment": false,
"name": "List 1 Card 1",
"pos": 65535,
"shortLink": "H0TZyzbK",
"shortUrl": "https://trello.com/c/H0TZyzbK",
"subscribed": false,
"url": "https://trello.com/c/H0TZyzbK/4-%F0%9F%91%8B-what-why-how",
"cover": {
"color": "yellow",
"idUploadedBackground": true,
"size": "normal",
"brightness": "light",
"isTemplate": false
}
}, {
"id": "list-1-card-2-5abbe4b7ddc1b351ef961414",
"address": "<string>",
"badges": {
"attachmentsByType": {
"trello": {
"board": 2154,
"card": 2154
}
},
"location": true,
"votes": 2154,
"viewingMemberVoted": false,
"subscribed": false,
"fogbugz": "<string>",
"checkItems": 0,
"checkItemsChecked": 0,
"comments": 0,
"attachments": 0,
"description": true,
"due": "<string>",
"start": "<string>",
"dueComplete": true
},
"checkItemStates": [
"<string>"
],
"closed": true,
"coordinates": "<string>",
"creationMethod": "<string>",
"dateLastActivity": "2019-09-16T16:19:17.156Z",
"desc": "👋Hey there,\n\nTrello's Platform team uses this board to keep developers up-to-date.",
"descData": {
"emoji": {}
},
"due": "<string>",
"dueReminder": "<string>",
"email": "bentleycook+2kea95u7kchsvqnxkwe+2q0byi6qv4pt9uc7q5m+25qyyohtzg@boards.trello.com",
"idBoard": "5abbe4b7ddc1b351ef961414",
"idChecklists": [
{
"id": "5abbe4b7ddc1b351ef961414"
}
],
"idLabels": [
{
"id": "5abbe4b7ddc1b351ef961414",
"idBoard": "5abbe4b7ddc1b351ef961414",
"name": "Overdue",
"color": "yellow"
}
],
"idList": "list-1-5abbe4b7ddc1b351ef961414",
"idMembers": [
"5abbe4b7ddc1b351ef961414"
],
"idMembersVoted": [
"5abbe4b7ddc1b351ef961414"
],
"idShort": 2154,
"labels": [
"5abbe4b7ddc1b351ef961414"
],
"limits": {
"attachments": {
"perBoard": {}
}
},
"locationName": "<string>",
"manualCoverAttachment": false,
"name": "List 1 Card 2",
"pos": 65535,
"shortLink": "H0TZyzbK",
"shortUrl": "https://trello.com/c/H0TZyzbK",
"subscribed": false,
"url": "https://trello.com/c/H0TZyzbK/4-%F0%9F%91%8B-what-why-how",
"cover": {
"color": "yellow",
"idUploadedBackground": true,
"size": "normal",
"brightness": "light",
"isTemplate": false
}
}
]

View file

@ -0,0 +1,170 @@
[
{
"id": "list-2-card-1-5abbe4b7ddc1b351ef961414",
"address": "<string>",
"badges": {
"attachmentsByType": {
"trello": {
"board": 2154,
"card": 2154
}
},
"location": true,
"votes": 2154,
"viewingMemberVoted": false,
"subscribed": false,
"fogbugz": "<string>",
"checkItems": 0,
"checkItemsChecked": 0,
"comments": 0,
"attachments": 0,
"description": true,
"due": "<string>",
"start": "<string>",
"dueComplete": true
},
"checkItemStates": [
"<string>"
],
"closed": true,
"coordinates": "<string>",
"creationMethod": "<string>",
"dateLastActivity": "2019-09-16T16:19:17.156Z",
"desc": "👋Hey there,\n\nTrello's Platform team uses this board to keep developers up-to-date.",
"descData": {
"emoji": {}
},
"due": "<string>",
"dueReminder": "<string>",
"email": "bentleycook+2kea95u7kchsvqnxkwe+2q0byi6qv4pt9uc7q5m+25qyyohtzg@boards.trello.com",
"idBoard": "5abbe4b7ddc1b351ef961414",
"idChecklists": [
{
"id": "5abbe4b7ddc1b351ef961414"
}
],
"idLabels": [
{
"id": "5abbe4b7ddc1b351ef961414",
"idBoard": "5abbe4b7ddc1b351ef961414",
"name": "Overdue",
"color": "yellow"
}
],
"idList": "list-2-5abbe4b7ddc1b351ef961414",
"idMembers": [
"5abbe4b7ddc1b351ef961414"
],
"idMembersVoted": [
"5abbe4b7ddc1b351ef961414"
],
"idShort": 2154,
"labels": [
"5abbe4b7ddc1b351ef961414"
],
"limits": {
"attachments": {
"perBoard": {}
}
},
"locationName": "<string>",
"manualCoverAttachment": false,
"name": "List 2 Card 1",
"pos": 65535,
"shortLink": "H0TZyzbK",
"shortUrl": "https://trello.com/c/H0TZyzbK",
"subscribed": false,
"url": "https://trello.com/c/H0TZyzbK/4-%F0%9F%91%8B-what-why-how",
"cover": {
"color": "yellow",
"idUploadedBackground": true,
"size": "normal",
"brightness": "light",
"isTemplate": false
}
}, {
"id": "list-2-card-2-5abbe4b7ddc1b351ef961414",
"address": "<string>",
"badges": {
"attachmentsByType": {
"trello": {
"board": 2154,
"card": 2154
}
},
"location": true,
"votes": 2154,
"viewingMemberVoted": false,
"subscribed": false,
"fogbugz": "<string>",
"checkItems": 0,
"checkItemsChecked": 0,
"comments": 0,
"attachments": 0,
"description": true,
"due": "<string>",
"start": "<string>",
"dueComplete": true
},
"checkItemStates": [
"<string>"
],
"closed": true,
"coordinates": "<string>",
"creationMethod": "<string>",
"dateLastActivity": "2019-09-16T16:19:17.156Z",
"desc": "👋Hey there,\n\nTrello's Platform team uses this board to keep developers up-to-date.",
"descData": {
"emoji": {}
},
"due": "<string>",
"dueReminder": "<string>",
"email": "bentleycook+2kea95u7kchsvqnxkwe+2q0byi6qv4pt9uc7q5m+25qyyohtzg@boards.trello.com",
"idBoard": "5abbe4b7ddc1b351ef961414",
"idChecklists": [
{
"id": "5abbe4b7ddc1b351ef961414"
}
],
"idLabels": [
{
"id": "5abbe4b7ddc1b351ef961414",
"idBoard": "5abbe4b7ddc1b351ef961414",
"name": "Overdue",
"color": "yellow"
}
],
"idList": "list-2-5abbe4b7ddc1b351ef961414",
"idMembers": [
"5abbe4b7ddc1b351ef961414"
],
"idMembersVoted": [
"5abbe4b7ddc1b351ef961414"
],
"idShort": 2154,
"labels": [
"5abbe4b7ddc1b351ef961414"
],
"limits": {
"attachments": {
"perBoard": {}
}
},
"locationName": "<string>",
"manualCoverAttachment": false,
"name": "List 2 Card 2",
"pos": 65535,
"shortLink": "H0TZyzbK",
"shortUrl": "https://trello.com/c/H0TZyzbK",
"subscribed": false,
"url": "https://trello.com/c/H0TZyzbK/4-%F0%9F%91%8B-what-why-how",
"cover": {
"color": "yellow",
"idUploadedBackground": true,
"size": "normal",
"brightness": "light",
"isTemplate": false
}
}
]

37
src/trello/tests.rs Normal file
View file

@ -0,0 +1,37 @@
mod board {
// use crate::trello::{
// // api::boards::TrelloBoards as _,
// types::{
// board::TrelloBoard, TrelloBoardId, TrelloBoardName, TrelloListId, TrelloListName,
// },
// };
// #[test]
// fn list_of_boards_find_by_name_returns_board() {
// //given
// let board = TrelloBoard::new(
// TrelloBoardId::new("2"),
// TrelloBoardName::new("beta"),
// vec![],
// );
// let boards = vec![
// TrelloBoard::new(
// TrelloBoardId::new("1"),
// TrelloBoardName::new("alpha"),
// vec![],
// ),
// board.clone(),
// TrelloBoard::new(
// TrelloBoardId::new("3"),
// TrelloBoardName::new("gamma"),
// vec![],
// ),
// ];
//
// //when
// let result = boards.find_by_name(board.name());
//
// //then
// assert_eq!(result, Some(&board));
// }
}

53
src/trello/types/auth.rs Normal file
View file

@ -0,0 +1,53 @@
use std::collections::HashMap;
//
use derive_more::derive::Display;
use crate::newtype;
newtype!(TrelloUser, String, Display, "User");
newtype!(TrelloApiKey, String, Display, "API Key");
newtype!(
TrelloApiSecret,
String,
Display,
"API Secret token for Trello"
);
#[derive(Debug, Clone)]
pub struct TrelloAuth {
api_key: TrelloApiKey,
api_secret: TrelloApiSecret,
user: TrelloUser,
}
impl TrelloAuth {
pub const fn new(api_key: TrelloApiKey, api_secret: TrelloApiSecret, user: TrelloUser) -> Self {
Self {
api_key,
api_secret,
user,
}
}
pub const fn api_key(&self) -> &TrelloApiKey {
&self.api_key
}
pub const fn api_token(&self) -> &TrelloApiSecret {
&self.api_secret
}
pub const fn user(&self) -> &TrelloUser {
&self.user
}
}
impl From<&TrelloAuth> for HashMap<String, String> {
fn from(value: &TrelloAuth) -> Self {
HashMap::from([(
"Authorization".into(),
format!(
r#"OAuth oauth_consumer_key="{}", oauth_token="{}""#,
value.api_key(),
value.api_token()
),
)])
}
}

13
src/trello/types/board.rs Normal file
View file

@ -0,0 +1,13 @@
//
use derive_more::derive::Constructor;
use crate::trello::types::list::TrelloList;
use super::{TrelloBoardId, TrelloBoardName};
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, Constructor)]
pub(crate) struct TrelloBoard {
pub(crate) id: TrelloBoardId,
pub(crate) name: TrelloBoardName,
pub(crate) lists: Vec<TrelloList>,
}

20
src/trello/types/card.rs Normal file
View file

@ -0,0 +1,20 @@
//
use crate::trello::{api::cards::TrelloCardUpdate, TrelloCardId, TrelloCardName, TrelloListId};
#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
pub struct TrelloCard {
id: TrelloCardId,
name: TrelloCardName,
#[serde(rename = "idList")]
id_list: TrelloListId,
}
impl TrelloCard {
#[cfg(test)]
pub const fn new(id: TrelloCardId, name: TrelloCardName, id_list: TrelloListId) -> Self {
Self { id, name, id_list }
}
pub const fn list_id(&self) -> &TrelloListId {
&self.id_list
}
}

9
src/trello/types/list.rs Normal file
View file

@ -0,0 +1,9 @@
use derive_more::derive::Constructor;
use super::{TrelloListId, TrelloListName};
#[derive(Debug, Clone, PartialEq, Eq, serde::Deserialize, Constructor)]
pub(crate) struct TrelloList {
pub(crate) id: TrelloListId,
pub(crate) name: TrelloListName,
}

23
src/trello/types/mod.rs Normal file
View file

@ -0,0 +1,23 @@
pub(crate) mod auth;
pub(crate) mod board;
// mod card;
mod list;
// mod new_card;
use derive_more::derive::Display;
use crate::newtype;
newtype!(TrelloBoardId, String, Display, "Board ID");
newtype!(TrelloBoardName, String, Display, "Board Name");
newtype!(TrelloListId, String, "List ID");
newtype!(
TrelloListName,
String,
Display,
PartialOrd,
Ord,
"List Name"
);
newtype!(TrelloCardId, String, Display, "Card ID");
newtype!(TrelloCardName, String, Display, "Card Name");

View file

@ -0,0 +1,20 @@
use super::{TrelloCardName, TrelloListId};
pub struct NewTrelloCard {
name: TrelloCardName,
list_id: TrelloListId,
}
impl NewTrelloCard {
pub fn new(name: impl Into<TrelloCardName>, list_id: impl Into<TrelloListId>) -> Self {
Self {
name: name.into(),
list_id: list_id.into(),
}
}
pub const fn name(&self) -> &TrelloCardName {
&self.name
}
pub const fn list_id(&self) -> &TrelloListId {
&self.list_id
}
}