From 8cfe326d353d802dac2417c55776c2c1f3e47ce5 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 30 Nov 2024 11:30:36 +0000 Subject: [PATCH] WIP(TODO): define nextcloud config model client --- src/config.rs | 50 +++- src/lib.rs | 4 +- src/nextcloud/board.rs | 22 ++ src/nextcloud/mod.rs | 129 ++++++++++ src/nextcloud/model.rs | 168 +++++++++++++ src/nextcloud/stack.rs | 19 ++ src/nextcloud/tests.rs | 221 ++++++++++++++++++ src/tests/responses/nextcloud-boards.json | 86 +++++++ src/tests/responses/nextcloud-stack-list.json | 216 +++++++++++++++++ 9 files changed, 904 insertions(+), 11 deletions(-) create mode 100644 src/nextcloud/board.rs create mode 100644 src/nextcloud/mod.rs create mode 100644 src/nextcloud/model.rs create mode 100644 src/nextcloud/stack.rs create mode 100644 src/nextcloud/tests.rs create mode 100644 src/tests/responses/nextcloud-boards.json create mode 100644 src/tests/responses/nextcloud-stack-list.json diff --git a/src/config.rs b/src/config.rs index 398d434..0f3a9b7 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,16 +1,13 @@ // // use color_eyre::Result; -// use crate::{ -// f, s, -// trello::{ -// types::{ -// auth::{TrelloApiKey, TrelloApiSecret, TrelloAuth, TrelloUser}, -// TrelloBoardName, -// } -// }, -// Ctx, NAME, -// }; +use crate::{ + // f, + nextcloud::model::{NextcloudBoardId, NextcloudHostname, NextcloudPassword, NextcloudUsername}, + // s, + // trello::types::{TrelloApiKey, TrelloApiSecret, TrelloAuth, TrelloBoardName, TrelloUser}, + // Ctx, NAME, +}; #[derive( Clone, @@ -48,6 +45,38 @@ pub struct TrelloConfig { // } // } +#[derive( + Clone, + Debug, + derive_more::From, + PartialEq, + Eq, + PartialOrd, + Ord, + serde::Deserialize, + derive_more::Constructor, +)] +pub struct NextcloudConfig { + hostname: NextcloudHostname, + username: NextcloudUsername, + password: NextcloudPassword, + board_id: NextcloudBoardId, +} +impl NextcloudConfig { + pub fn hostname(&self) -> &NextcloudHostname { + &self.hostname + } + pub fn username(&self) -> &NextcloudUsername { + &self.username + } + pub fn password(&self) -> &NextcloudPassword { + &self.password + } + pub fn board_id(&self) -> NextcloudBoardId { + self.board_id + } +} + #[derive( Clone, Debug, @@ -62,6 +91,7 @@ pub struct TrelloConfig { )] pub struct AppConfig { pub trello: TrelloConfig, + pub nextcloud: NextcloudConfig, } // impl AppConfig { // pub fn load(ctx: &Ctx) -> Result { diff --git a/src/lib.rs b/src/lib.rs index be0077d..fe20602 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,12 +2,14 @@ use std::path::PathBuf; use clap::Parser; +pub use config::AppConfig; use kxio::{fs::FileSystem, net::Net}; -// mod api_result; +mod api_result; mod config; mod init; mod macros; +pub mod nextcloud; mod template; // mod trello; diff --git a/src/nextcloud/board.rs b/src/nextcloud/board.rs new file mode 100644 index 0000000..de3db0b --- /dev/null +++ b/src/nextcloud/board.rs @@ -0,0 +1,22 @@ +// +use kxio::net::Net; + +use crate::{p, AppConfig, Ctx}; +use crate::{p, FullCtx}; + +use super::DeckClient; + +pub async fn list(ctx: FullCtx, dump: bool) -> color_eyre::Result<()> { + let dc = DeckClient::new(&ctx.cfg.nextcloud, ctx.net); + let apiresult = dc.get_boards().await; + if dump { + p!("{}", apiresult.text); + } else { + let mut boards = apiresult.result?; + boards.sort_by_key(|stack| stack.title.clone()); + boards + .iter() + .for_each(|stack| p!("{}:{}", stack.id, stack.title)); + } + Ok(()) + } diff --git a/src/nextcloud/mod.rs b/src/nextcloud/mod.rs new file mode 100644 index 0000000..0a01925 --- /dev/null +++ b/src/nextcloud/mod.rs @@ -0,0 +1,129 @@ +// +use kxio::net::Net; + +use crate::api_result::APIResult; +use crate::{config::NextcloudConfig, f}; + +use model::{Board, Card, NextcloudBoardId, Stack}; + +pub mod model; + +#[cfg(test)] +mod tests; + +pub struct DeckClient { + net: Net, + hostname: String, + username: String, + password: String, +} + +impl DeckClient { + pub fn new(cfg: &NextcloudConfig, net: Net) -> Self { + let hostname = cfg.hostname().to_string(); + let username = cfg.username().to_string(); + let password = cfg.password().to_string(); + + Self { + net, + hostname, + username, + password, + } + } + + fn url(&self, path: impl Into) -> String { + f!( + "https://{}/index.php/apps/deck/api/v1.0/{}", + self.hostname, + path.into() + ) + } + + pub async fn get_boards(&self) -> APIResult> { + APIResult::new( + self.net + .get(self.url("boards")) + .basic_auth(&self.username, Some(&self.password)) + .header("accept", "application/json") + .send() + .await, + ) + .await + } + + pub async fn get_board(&self, board_id: NextcloudBoardId) -> APIResult { + APIResult::new( + self.net + .get(self.url(f!("boards/{board_id}"))) + .basic_auth(&self.username, Some(&self.password)) + .header("accept", "application/json") + .send() + .await, + ) + .await + } + + pub async fn create_board(&self, title: &str, color: &str) -> APIResult { + APIResult::new( + self.net + .post(self.url("boards")) + .basic_auth(&self.username, Some(&self.password)) + .header("accept", "application/json") + .body( + serde_json::json!({ + "title": title, + "color": color + }) + .to_string(), + ) + .send() + .await, + ) + .await + } + + pub async fn get_stacks(&self, board_id: NextcloudBoardId) -> APIResult> { + APIResult::new( + self.net + .get(self.url(f!("boards/{board_id}/stacks"))) + .basic_auth(&self.username, Some(&self.password)) + .header("accept", "application/json") + .send() + .await, + ) + .await + } + + pub async fn create_card( + &self, + board_id: i64, + stack_id: i64, + title: &str, + description: Option<&str>, + ) -> APIResult { + let url = format!( + "https://{}/index.php/apps/deck/api/v1.0/boards/{}/stacks/{}/cards", + self.hostname, board_id, stack_id + ); + + let mut json = serde_json::json!({ + "title": title, + }); + + if let Some(desc) = description { + json["description"] = serde_json::Value::String(desc.to_string()); + } + + APIResult::new( + self.net + .post(&url) + .basic_auth(&self.username, Some(&self.password)) + .header("accept", "application/json") + .body(json.to_string()) + .send() + .await, + ) + .await + } +} diff --git a/src/nextcloud/model.rs b/src/nextcloud/model.rs new file mode 100644 index 0000000..94fc87f --- /dev/null +++ b/src/nextcloud/model.rs @@ -0,0 +1,168 @@ +use derive_more::derive::Display; +// +use serde::{Deserialize, Serialize}; + +use crate::newtype; + +newtype!( + NextcloudHostname, + String, + Display, + PartialOrd, + Ord, + "Hostname of the Nextcloud server" +); +newtype!( + NextcloudUsername, + String, + Display, + PartialOrd, + Ord, + "Username to authenticate as" +); +newtype!( + NextcloudPassword, + String, + PartialOrd, + Ord, + "Password to authenticate with" +); + +newtype!( + NextcloudBoardId, + i64, + Copy, + Display, + PartialOrd, + Ord, + "ID of a Nextcloud Board" +); +newtype!( + NextcloudStackId, + i64, + Copy, + Display, + PartialOrd, + Ord, + "ID of a Nextcloud Stack" +); +newtype!( + NextcloudCardId, + i64, + Copy, + Display, + PartialOrd, + Ord, + "ID of a Nextcloud Card" +); +newtype!( + NextcloudLabelId, + i64, + Copy, + Display, + PartialOrd, + Ord, + "ID of a Nextcloud Label" +); +newtype!( + NextcloudOrder, + i64, + Copy, + Display, + PartialOrd, + Ord, + "Relative position of the item amongst its peers" +); +newtype!( + NextcloudETag, + String, + PartialOrd, + Ord, + "ETag for a resource" +); +newtype!( + NextcloudBoardTitle, + String, + PartialOrd, + Ord, + "Title of the Board" +); +newtype!( + NextcloudBoardOwner, + String, + PartialOrd, + Ord, + "Owner of the Board" +); +newtype!( + NextcloudBoardColour, + String, + PartialOrd, + Ord, + "Colour of the Board" +); + +newtype!( + NextcloudStackTitle, + String, + PartialOrd, + Ord, + "Title of the Stack" +); +newtype!( + NextcloudCardTitle, + String, + PartialOrd, + Ord, + "Title of the Card" +); + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] +pub struct Board { + pub id: NextcloudBoardId, + pub title: NextcloudBoardTitle, + pub owner: NextcloudBoardOwner, + pub color: NextcloudBoardColour, + pub archived: bool, + pub labels: Vec