feat(trello): add basics of trello config model
Some checks failed
Test / build (map[name:nightly]) (push) Successful in 2m14s
Test / build (map[name:stable]) (push) Successful in 2m57s
Release Please / Release-plz (push) Failing after 21s

This commit is contained in:
Paul Campbell 2024-11-29 19:19:36 +00:00
parent de94004be3
commit c8f9780226
8 changed files with 190 additions and 155 deletions

View file

@ -1,32 +1,32 @@
//
// use kxio::net::Response;
//
// use crate::{e, s};
use kxio::net::Response;
// pub struct APIResult<T> {
// pub text: String,
// pub result: Result<T, kxio::net::Error>,
// }
use crate::{e, s};
// 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),
// },
// }
// }
// }
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

@ -3,6 +3,13 @@ use color_eyre::Result;
use crate::{f, s, Ctx, NAME};
#[derive(Clone, Debug, derive_more::From, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize)]
pub struct TrelloConfig {
pub api_key: String,
pub api_secret: String,
pub board_name: String,
}
#[derive(
Clone,
Debug,
@ -14,7 +21,9 @@ use crate::{f, s, Ctx, NAME};
derive_more::AsRef,
serde::Deserialize,
)]
pub struct AppConfig {}
pub struct AppConfig {
pub trello: TrelloConfig,
}
impl AppConfig {
pub fn load(ctx: &Ctx) -> Result<Self> {
let file = ctx.fs.base().join(f!("{NAME}.toml"));

View file

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

View file

@ -9,19 +9,41 @@ use crate::{config::AppConfig, f, NAME};
mod config {
use super::*;
use crate::config::TrelloConfig;
use crate::s;
#[test]
fn load_config() {
//given
let fs = given::a_filesystem();
let file = fs.base().join(f!("{}.toml", NAME));
fs.file(&file).write("").expect("write file");
fs.file(&file)
.write(
[
"[trello]",
"api_key = \"trello-api-key\"",
"api_secret = \"trello-api-secret\"",
"board_name = \"trello-board-name\"",
]
.join("\n"),
)
.expect("write file");
let ctx = given::a_context(fs.as_real(), given::a_network().into());
//when
let_assert!(Ok(config) = AppConfig::load(&ctx));
//then
assert_eq!(config, AppConfig {});
assert_eq!(
config,
AppConfig {
trello: TrelloConfig {
api_key: s!("trello-api-key"),
api_secret: s!("trello-api-secret"),
board_name: s!("trello-board-name"),
}
}
);
}
}

View file

@ -1,54 +1,54 @@
//
// use kxio::net::Net;
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
// }
use crate::api_result::APIResult;
use crate::{
f,
trello::{
types::{auth::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
}

View file

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

View file

@ -1,59 +1,62 @@
// use super::*;
use super::*;
mod given;
// type TestResult = color_eyre::Result<()>;
type TestResult = color_eyre::Result<()>;
mod members {
// use std::collections::HashMap;
use std::collections::HashMap;
// use kxio::net::StatusCode;
// use serde_json::json;
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 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(())
// }
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
.result?;
assert_eq!(
result,
vec![TrelloBoard::new(
TrelloBoardId::new("1"),
TrelloBoardName::new("board-1"),
vec![]
)]
);
//then
Ok(())
}
}

View file

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