refactor: reshuffling and extracting Executor trait
This commit is contained in:
parent
3f0ecd5fd1
commit
be172b11a4
26 changed files with 599 additions and 355 deletions
|
@ -3,7 +3,7 @@ use kxio::{net::Response, print::Printer};
|
||||||
|
|
||||||
use crate::{e, s};
|
use crate::{e, s};
|
||||||
|
|
||||||
pub struct APIResult<T> {
|
pub(crate) struct APIResult<T> {
|
||||||
pub(crate) text: String,
|
pub(crate) text: String,
|
||||||
pub(crate) result: Result<T, kxio::net::Error>,
|
pub(crate) result: Result<T, kxio::net::Error>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,17 @@
|
||||||
//
|
//
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
|
|
||||||
use crate::{
|
use crate::{f, nextcloud::NextcloudConfig, s, trello::TrelloConfig, Ctx, NAME};
|
||||||
f,
|
|
||||||
nextcloud::model::{NextcloudBoardId, NextcloudHostname, NextcloudPassword, NextcloudUsername},
|
|
||||||
s,
|
|
||||||
trello::types::{
|
|
||||||
auth::{TrelloApiKey, TrelloApiSecret},
|
|
||||||
TrelloBoardName,
|
|
||||||
},
|
|
||||||
Ctx, NAME,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, derive_more::From, PartialEq, Eq, serde::Deserialize)]
|
|
||||||
pub(crate) struct TrelloConfig {
|
|
||||||
pub(crate) api_key: TrelloApiKey,
|
|
||||||
pub(crate) api_secret: TrelloApiSecret,
|
|
||||||
pub(crate) board_name: TrelloBoardName,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, derive_more::From, PartialEq, Eq, serde::Deserialize)]
|
|
||||||
pub(crate) struct NextcloudConfig {
|
|
||||||
pub(crate) hostname: NextcloudHostname,
|
|
||||||
pub(crate) username: NextcloudUsername,
|
|
||||||
pub(crate) password: NextcloudPassword,
|
|
||||||
pub(crate) board_id: NextcloudBoardId,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
Clone, Debug, derive_more::From, PartialEq, Eq, derive_more::AsRef, serde::Deserialize,
|
Clone, Debug, derive_more::From, PartialEq, Eq, derive_more::AsRef, serde::Deserialize,
|
||||||
)]
|
)]
|
||||||
pub struct AppConfig {
|
pub(crate) struct AppConfig {
|
||||||
pub(crate) trello: TrelloConfig,
|
pub(crate) trello: TrelloConfig,
|
||||||
pub(crate) nextcloud: NextcloudConfig,
|
pub(crate) nextcloud: NextcloudConfig,
|
||||||
}
|
}
|
||||||
impl AppConfig {
|
impl AppConfig {
|
||||||
pub fn load(ctx: &Ctx) -> Result<Self> {
|
pub(crate) fn load(ctx: &Ctx) -> Result<Self> {
|
||||||
let file = ctx.fs.base().join(f!("{NAME}.toml"));
|
let file = ctx.fs.base().join(f!("{NAME}.toml"));
|
||||||
let str = ctx.fs.file(&file).reader()?;
|
let str = ctx.fs.file(&file).reader()?;
|
||||||
Ok(toml::from_str(s!(str).as_str())?)
|
Ok(toml::from_str(s!(str).as_str())?)
|
||||||
|
|
21
src/execute.rs
Normal file
21
src/execute.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//
|
||||||
|
use color_eyre::eyre::eyre;
|
||||||
|
use color_eyre::Result;
|
||||||
|
|
||||||
|
use crate::FullCtx;
|
||||||
|
|
||||||
|
pub(crate) trait Execute {
|
||||||
|
async fn execute(self, ctx: FullCtx) -> Result<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Execute for crate::Command {
|
||||||
|
async fn execute(self, ctx: FullCtx) -> Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Init => Err(eyre!("Config file already exists. Not overwriting it.")),
|
||||||
|
Self::Check => crate::check::run(ctx).await,
|
||||||
|
Self::Import => todo!(), //crate::import::run(ctx).await,
|
||||||
|
Self::Trello(cmd) => cmd.execute(ctx).await,
|
||||||
|
Self::Nextcloud(cmd) => cmd.execute(ctx).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
172
src/lib.rs
172
src/lib.rs
|
@ -3,26 +3,30 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use color_eyre::eyre::eyre;
|
use color_eyre::eyre::eyre;
|
||||||
pub use config::AppConfig;
|
use config::AppConfig;
|
||||||
use kxio::{fs::FileSystem, net::Net, print::Printer};
|
use kxio::{fs::FileSystem, net::Net, print::Printer};
|
||||||
|
|
||||||
mod api_result;
|
mod api_result;
|
||||||
mod check;
|
mod check;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod execute;
|
||||||
mod init;
|
mod init;
|
||||||
mod macros;
|
mod macros;
|
||||||
pub mod nextcloud;
|
mod nextcloud;
|
||||||
mod template;
|
mod template;
|
||||||
mod trello;
|
mod trello;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
pub const NAME: &str = "trello-to-deck";
|
const NAME: &str = "trello-to-deck";
|
||||||
|
|
||||||
pub use kxio::kxeprintln as e;
|
use crate::nextcloud::client::DeckClient;
|
||||||
pub use kxio::kxprintln as p;
|
use crate::trello::client::TrelloClient;
|
||||||
use nextcloud::DeckClient;
|
use execute::Execute;
|
||||||
|
|
||||||
|
use kxio::kxeprintln as e;
|
||||||
|
use kxio::kxprintln as p;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[clap(version = clap::crate_version!(), author = clap::crate_authors!(), about = clap::crate_description!())]
|
#[clap(version = clap::crate_version!(), author = clap::crate_authors!(), about = clap::crate_description!())]
|
||||||
|
@ -34,77 +38,27 @@ struct Commands {
|
||||||
}
|
}
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
enum Command {
|
enum Command {
|
||||||
|
/// Initialize the configuration
|
||||||
|
#[command(about = "Initialize configuration")]
|
||||||
Init,
|
Init,
|
||||||
|
|
||||||
|
/// Check the configuration and connection
|
||||||
|
#[command(about = "Check configuration and connection")]
|
||||||
Check,
|
Check,
|
||||||
|
|
||||||
|
/// Import boards from Trello to Nextcloud Deck
|
||||||
|
#[command(about = "Import boards from Trello to Nextcloud Deck")]
|
||||||
Import,
|
Import,
|
||||||
#[clap(subcommand)]
|
|
||||||
Trello(TrelloCommand),
|
|
||||||
#[clap(subcommand)]
|
|
||||||
Nextcloud(NextcloudCommand),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
/// Trello-specific commands
|
||||||
enum NextcloudCommand {
|
#[command(about = "Trello-specific commands")]
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Board(NextcloudBoardCommand),
|
Trello(trello::TrelloCommand),
|
||||||
|
|
||||||
|
/// Nextcloud-specific commands
|
||||||
|
#[command(about = "Nextcloud-specific commands")]
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Stack(NextcloudStackCommand),
|
Nextcloud(nextcloud::NextcloudCommand),
|
||||||
#[clap(subcommand)]
|
|
||||||
Card(NextcloudCardCommand),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
enum NextcloudBoardCommand {
|
|
||||||
List {
|
|
||||||
#[clap(long, action = clap::ArgAction::SetTrue)]
|
|
||||||
dump: bool,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
enum NextcloudStackCommand {
|
|
||||||
List {
|
|
||||||
#[clap(long, action = clap::ArgAction::SetTrue)]
|
|
||||||
dump: bool,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
enum NextcloudCardCommand {
|
|
||||||
List {
|
|
||||||
#[clap(long, action = clap::ArgAction::SetTrue)]
|
|
||||||
dump: bool,
|
|
||||||
stack_id: i64,
|
|
||||||
},
|
|
||||||
Get {
|
|
||||||
#[clap(long, action = clap::ArgAction::SetTrue)]
|
|
||||||
dump: bool,
|
|
||||||
stack_id: i64,
|
|
||||||
card_id: i64,
|
|
||||||
},
|
|
||||||
Create {
|
|
||||||
#[clap(long, action = clap::ArgAction::SetTrue)]
|
|
||||||
dump: bool,
|
|
||||||
stack_id: i64,
|
|
||||||
#[clap(long)]
|
|
||||||
title: String,
|
|
||||||
#[clap(long)]
|
|
||||||
description: Option<String>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
enum TrelloCommand {
|
|
||||||
#[clap(subcommand)]
|
|
||||||
Board(TrelloBoardCommand),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
enum TrelloBoardCommand {
|
|
||||||
List {
|
|
||||||
#[clap(long, action = clap::ArgAction::SetTrue)]
|
|
||||||
dump: bool,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -124,16 +78,20 @@ impl Default for Ctx {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FullCtx {
|
pub(crate) struct FullCtx {
|
||||||
pub fs: FileSystem,
|
// pub fs: FileSystem,
|
||||||
pub net: Net,
|
pub net: Net,
|
||||||
pub prt: Printer,
|
pub prt: Printer,
|
||||||
pub cfg: AppConfig,
|
pub cfg: AppConfig,
|
||||||
}
|
}
|
||||||
impl FullCtx {
|
impl FullCtx {
|
||||||
pub fn deck_client(&self) -> DeckClient {
|
pub(crate) fn deck_client(&self) -> DeckClient {
|
||||||
DeckClient::new(self)
|
DeckClient::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn trello_client(&self) -> TrelloClient {
|
||||||
|
TrelloClient::new(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, mutants::skip)]
|
#[cfg_attr(test, mutants::skip)]
|
||||||
|
@ -160,63 +118,15 @@ pub async fn run(ctx: Ctx) -> color_eyre::Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(cfg) => {
|
Ok(cfg) => {
|
||||||
let ctx = FullCtx {
|
commands
|
||||||
fs: ctx.fs,
|
.command
|
||||||
net: ctx.net,
|
.execute(FullCtx {
|
||||||
prt: ctx.prt,
|
// fs: ctx.fs,
|
||||||
cfg,
|
net: ctx.net,
|
||||||
};
|
prt: ctx.prt,
|
||||||
match commands.command {
|
cfg,
|
||||||
Command::Init => Err(eyre!("Config file already exists. Not overwriting it.")),
|
})
|
||||||
Command::Check => check::run(ctx).await,
|
.await
|
||||||
Command::Import => todo!("import"),
|
|
||||||
Command::Trello(trello) => match trello {
|
|
||||||
TrelloCommand::Board(board) => match board {
|
|
||||||
TrelloBoardCommand::List { dump } => {
|
|
||||||
nextcloud::board::list(ctx, dump).await
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Command::Nextcloud(nextcloud) => match nextcloud {
|
|
||||||
NextcloudCommand::Board(board) => match board {
|
|
||||||
NextcloudBoardCommand::List { dump } => {
|
|
||||||
nextcloud::board::list(ctx, dump).await
|
|
||||||
}
|
|
||||||
},
|
|
||||||
NextcloudCommand::Stack(stack) => match stack {
|
|
||||||
NextcloudStackCommand::List { dump } => {
|
|
||||||
nextcloud::stack::list(ctx, dump).await
|
|
||||||
}
|
|
||||||
},
|
|
||||||
NextcloudCommand::Card(card) => match card {
|
|
||||||
NextcloudCardCommand::List { dump, stack_id } => {
|
|
||||||
nextcloud::card::list(ctx, dump, stack_id.into()).await
|
|
||||||
}
|
|
||||||
NextcloudCardCommand::Get {
|
|
||||||
dump,
|
|
||||||
stack_id,
|
|
||||||
card_id,
|
|
||||||
} => nextcloud::card::get(ctx, dump, stack_id.into(), card_id.into()).await,
|
|
||||||
NextcloudCardCommand::Create {
|
|
||||||
dump,
|
|
||||||
stack_id,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
} => {
|
|
||||||
nextcloud::card::create(
|
|
||||||
ctx,
|
|
||||||
nextcloud::card::Create {
|
|
||||||
dump,
|
|
||||||
stack_id,
|
|
||||||
title,
|
|
||||||
description,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,26 @@
|
||||||
//
|
//
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
use crate::execute::Execute;
|
||||||
use crate::{p, FullCtx};
|
use crate::{p, FullCtx};
|
||||||
|
|
||||||
pub async fn list(ctx: FullCtx, dump: bool) -> color_eyre::Result<()> {
|
#[derive(Parser, Debug)]
|
||||||
|
pub(crate) enum NextcloudBoardCommand {
|
||||||
|
List {
|
||||||
|
#[clap(long, action = clap::ArgAction::SetTrue)]
|
||||||
|
dump: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Execute for NextcloudBoardCommand {
|
||||||
|
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::List { dump } => list(&ctx, dump).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn list(ctx: &FullCtx, dump: bool) -> color_eyre::Result<()> {
|
||||||
let api_result = ctx.deck_client().get_boards().await;
|
let api_result = ctx.deck_client().get_boards().await;
|
||||||
if dump {
|
if dump {
|
||||||
p!(ctx.prt, "{}", api_result.text);
|
p!(ctx.prt, "{}", api_result.text);
|
||||||
|
|
|
@ -1,11 +1,78 @@
|
||||||
//
|
//
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
use crate::execute::Execute;
|
||||||
use crate::{
|
use crate::{
|
||||||
nextcloud::model::{NextcloudCardId, NextcloudStackId},
|
nextcloud::model::{NextcloudCardId, NextcloudStackId},
|
||||||
p, FullCtx,
|
p, FullCtx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
pub(crate) enum NextcloudCardCommand {
|
||||||
|
List {
|
||||||
|
#[clap(long, action = clap::ArgAction::SetTrue)]
|
||||||
|
dump: bool,
|
||||||
|
stack_id: i64,
|
||||||
|
},
|
||||||
|
Get {
|
||||||
|
#[clap(long, action = clap::ArgAction::SetTrue)]
|
||||||
|
dump: bool,
|
||||||
|
stack_id: i64,
|
||||||
|
card_id: i64,
|
||||||
|
},
|
||||||
|
Create {
|
||||||
|
#[clap(long, action = clap::ArgAction::SetTrue)]
|
||||||
|
dump: bool,
|
||||||
|
stack_id: i64,
|
||||||
|
#[clap(long)]
|
||||||
|
title: String,
|
||||||
|
#[clap(long)]
|
||||||
|
description: Option<String>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Execute for NextcloudCardCommand {
|
||||||
|
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::List { dump, stack_id } => {
|
||||||
|
list(&ctx, dump, NextcloudStackId::from(stack_id)).await
|
||||||
|
}
|
||||||
|
Self::Get {
|
||||||
|
dump,
|
||||||
|
stack_id,
|
||||||
|
card_id,
|
||||||
|
} => {
|
||||||
|
get(
|
||||||
|
&ctx,
|
||||||
|
dump,
|
||||||
|
NextcloudStackId::from(stack_id),
|
||||||
|
NextcloudCardId::from(card_id),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
Self::Create {
|
||||||
|
dump,
|
||||||
|
stack_id,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
} => {
|
||||||
|
create(
|
||||||
|
ctx,
|
||||||
|
Create {
|
||||||
|
dump,
|
||||||
|
stack_id,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn list(
|
pub(crate) async fn list(
|
||||||
ctx: FullCtx,
|
ctx: &FullCtx,
|
||||||
dump: bool,
|
dump: bool,
|
||||||
stack_id: NextcloudStackId,
|
stack_id: NextcloudStackId,
|
||||||
) -> color_eyre::Result<()> {
|
) -> color_eyre::Result<()> {
|
||||||
|
@ -26,7 +93,7 @@ pub(crate) async fn list(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get(
|
pub(crate) async fn get(
|
||||||
ctx: FullCtx,
|
ctx: &FullCtx,
|
||||||
dump: bool,
|
dump: bool,
|
||||||
stack_id: NextcloudStackId,
|
stack_id: NextcloudStackId,
|
||||||
card_id: NextcloudCardId,
|
card_id: NextcloudCardId,
|
||||||
|
@ -45,10 +112,10 @@ pub(crate) async fn get(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct Create {
|
pub(crate) struct Create {
|
||||||
pub dump: bool,
|
pub(crate) dump: bool,
|
||||||
pub stack_id: i64,
|
pub(crate) stack_id: i64,
|
||||||
pub title: String,
|
pub(crate) title: String,
|
||||||
pub description: Option<String>,
|
pub(crate) description: Option<String>,
|
||||||
}
|
}
|
||||||
pub(crate) async fn create(ctx: FullCtx, create: Create) -> color_eyre::Result<()> {
|
pub(crate) async fn create(ctx: FullCtx, create: Create) -> color_eyre::Result<()> {
|
||||||
let dc = ctx.deck_client();
|
let dc = ctx.deck_client();
|
||||||
|
|
143
src/nextcloud/client.rs
Normal file
143
src/nextcloud/client.rs
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
use crate::api_result::APIResult;
|
||||||
|
use crate::nextcloud::card::Create;
|
||||||
|
use crate::nextcloud::model::{
|
||||||
|
Board, Card, NextcloudBoardId, NextcloudCardId, NextcloudHostname, NextcloudPassword,
|
||||||
|
NextcloudStackId, NextcloudUsername, Stack,
|
||||||
|
};
|
||||||
|
use crate::{f, FullCtx};
|
||||||
|
use bytes::Bytes;
|
||||||
|
use kxio::net::{Net, ReqBuilder};
|
||||||
|
|
||||||
|
pub(crate) struct DeckClient<'ctx> {
|
||||||
|
ctx: &'ctx FullCtx,
|
||||||
|
hostname: &'ctx NextcloudHostname,
|
||||||
|
username: &'ctx NextcloudUsername,
|
||||||
|
password: &'ctx NextcloudPassword,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uses the API described here: https://deck.readthedocs.io/en/stable/API/#cards
|
||||||
|
impl<'ctx> DeckClient<'ctx> {
|
||||||
|
pub fn new(ctx: &'ctx FullCtx) -> Self {
|
||||||
|
Self {
|
||||||
|
ctx,
|
||||||
|
hostname: &ctx.cfg.nextcloud.hostname,
|
||||||
|
username: &ctx.cfg.nextcloud.username,
|
||||||
|
password: &ctx.cfg.nextcloud.password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn url(&self, path: impl Into<String>) -> String {
|
||||||
|
f!(
|
||||||
|
"https://{}/index.php/apps/deck/api/v1.0/{}",
|
||||||
|
self.hostname,
|
||||||
|
path.into()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn request<T: for<'a> serde::Deserialize<'a>>(
|
||||||
|
&self,
|
||||||
|
url: impl Into<String>,
|
||||||
|
custom: fn(&Net, String) -> ReqBuilder,
|
||||||
|
) -> APIResult<T> {
|
||||||
|
APIResult::new(
|
||||||
|
custom(&self.ctx.net, self.url(url))
|
||||||
|
.basic_auth(self.username.as_str(), Some(self.password.as_str()))
|
||||||
|
.header("accept", "application/json")
|
||||||
|
.header("content-type", "application/json")
|
||||||
|
.send()
|
||||||
|
.await,
|
||||||
|
&self.ctx.prt,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn request_with_body<T: for<'a> serde::Deserialize<'a>>(
|
||||||
|
&self,
|
||||||
|
url: impl Into<String>,
|
||||||
|
body: impl Into<Bytes>,
|
||||||
|
custom: fn(&Net, String) -> ReqBuilder,
|
||||||
|
) -> APIResult<T> {
|
||||||
|
APIResult::new(
|
||||||
|
custom(&self.ctx.net, self.url(url))
|
||||||
|
.basic_auth(self.username.as_str(), Some(self.password.as_str()))
|
||||||
|
.header("accept", "application/json")
|
||||||
|
.header("content-type", "application/json")
|
||||||
|
.body(body)
|
||||||
|
.send()
|
||||||
|
.await,
|
||||||
|
&self.ctx.prt,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn get_boards(&self) -> APIResult<Vec<Board>> {
|
||||||
|
self.request("boards", |net, url| net.get(url)).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn get_board(&self, board_id: NextcloudBoardId) -> APIResult<Board> {
|
||||||
|
self.request(f!("boards/{board_id}"), |net, url| net.get(url))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub(crate) async fn create_board(&self, title: &str, color: &str) -> APIResult<Board> {
|
||||||
|
// self.request_with_body(
|
||||||
|
// "boards",
|
||||||
|
// serde_json::json!({
|
||||||
|
// "title": title,
|
||||||
|
// "color": color
|
||||||
|
// })
|
||||||
|
// .to_string(),
|
||||||
|
// |net, url| net.post(url),
|
||||||
|
// )
|
||||||
|
// .await
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub(crate) async fn get_stacks(&self, board_id: NextcloudBoardId) -> APIResult<Vec<Stack>> {
|
||||||
|
self.request(f!("boards/{board_id}/stacks"), |net, url| net.get(url))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn get_stack(
|
||||||
|
&self,
|
||||||
|
board_id: NextcloudBoardId,
|
||||||
|
stack_id: NextcloudStackId,
|
||||||
|
) -> APIResult<Stack> {
|
||||||
|
self.request(f!("boards/{board_id}/stacks/{stack_id}"), |net, url| {
|
||||||
|
net.get(url)
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn create_card(&self, create: &Create) -> APIResult<Card> {
|
||||||
|
let mut body = serde_json::json!({
|
||||||
|
"title": create.title,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(desc) = &create.description {
|
||||||
|
body["description"] = serde_json::Value::String(desc.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.request_with_body(
|
||||||
|
format!(
|
||||||
|
"boards/{}/stacks/{}/cards",
|
||||||
|
self.ctx.cfg.nextcloud.board_id, create.stack_id
|
||||||
|
),
|
||||||
|
body.to_string(),
|
||||||
|
|net, url| net.post(url),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn get_card(
|
||||||
|
&self,
|
||||||
|
board_id: NextcloudBoardId,
|
||||||
|
stack_id: NextcloudStackId,
|
||||||
|
card_id: NextcloudCardId,
|
||||||
|
) -> APIResult<Card> {
|
||||||
|
self.request(
|
||||||
|
f!("boards/{board_id}/stacks/{stack_id}/cards/{card_id}"),
|
||||||
|
|net, url| net.get(url),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,30 @@
|
||||||
//
|
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
//
|
||||||
|
use clap::Parser;
|
||||||
use kxio::net::{Net, ReqBuilder};
|
use kxio::net::{Net, ReqBuilder};
|
||||||
|
|
||||||
use crate::{api_result::APIResult, f, FullCtx};
|
use crate::{
|
||||||
|
api_result::APIResult,
|
||||||
use card::Create;
|
execute::Execute,
|
||||||
use model::{
|
f,
|
||||||
Board, Card, NextcloudBoardId, NextcloudHostname, NextcloudPassword, NextcloudStackId,
|
nextcloud::{
|
||||||
NextcloudUsername, Stack,
|
board::NextcloudBoardCommand,
|
||||||
|
card::Create,
|
||||||
|
card::NextcloudCardCommand,
|
||||||
|
model::{
|
||||||
|
Board, Card, NextcloudBoardId, NextcloudHostname, NextcloudPassword, NextcloudStackId,
|
||||||
|
NextcloudUsername, Stack,
|
||||||
|
},
|
||||||
|
stack::NextcloudStackCommand,
|
||||||
|
},
|
||||||
|
FullCtx,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod board;
|
mod board;
|
||||||
pub mod card;
|
mod card;
|
||||||
pub mod model;
|
pub(crate) mod client;
|
||||||
pub mod stack;
|
pub(crate) mod model;
|
||||||
|
mod stack;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
@ -151,3 +162,30 @@ impl<'ctx> DeckClient<'ctx> {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
pub(crate) enum NextcloudCommand {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
Board(NextcloudBoardCommand),
|
||||||
|
#[clap(subcommand)]
|
||||||
|
Stack(NextcloudStackCommand),
|
||||||
|
#[clap(subcommand)]
|
||||||
|
Card(NextcloudCardCommand),
|
||||||
|
}
|
||||||
|
impl Execute for NextcloudCommand {
|
||||||
|
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
||||||
|
match self {
|
||||||
|
NextcloudCommand::Board(cmd) => cmd.execute(ctx).await,
|
||||||
|
NextcloudCommand::Stack(cmd) => cmd.execute(ctx).await,
|
||||||
|
NextcloudCommand::Card(cmd) => cmd.execute(ctx).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, derive_more::From, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize)]
|
||||||
|
pub struct NextcloudConfig {
|
||||||
|
pub(crate) hostname: NextcloudHostname,
|
||||||
|
pub(crate) username: NextcloudUsername,
|
||||||
|
pub(crate) password: NextcloudPassword,
|
||||||
|
pub(crate) board_id: NextcloudBoardId,
|
||||||
|
}
|
||||||
|
|
|
@ -113,64 +113,64 @@ newtype!(
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct NextcloudBoardOwner {
|
pub(crate) struct NextcloudBoardOwner {
|
||||||
#[serde(rename = "primaryKey")]
|
#[serde(rename = "primaryKey")]
|
||||||
pub primary_key: String,
|
pub(crate) primary_key: String,
|
||||||
pub uid: String,
|
pub(crate) uid: String,
|
||||||
#[serde(rename = "displayname")]
|
#[serde(rename = "displayname")]
|
||||||
pub display_name: String,
|
pub(crate) display_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Board {
|
pub(crate) struct Board {
|
||||||
pub id: NextcloudBoardId,
|
pub(crate) id: NextcloudBoardId,
|
||||||
pub title: NextcloudBoardTitle,
|
pub(crate) title: NextcloudBoardTitle,
|
||||||
pub owner: NextcloudBoardOwner,
|
pub(crate) owner: NextcloudBoardOwner,
|
||||||
pub color: NextcloudBoardColour,
|
pub(crate) color: NextcloudBoardColour,
|
||||||
pub archived: bool,
|
pub(crate) archived: bool,
|
||||||
pub labels: Vec<Label>,
|
pub(crate) labels: Vec<Label>,
|
||||||
pub acl: Vec<Acl>,
|
pub(crate) acl: Vec<Acl>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Stack {
|
pub(crate) struct Stack {
|
||||||
pub id: NextcloudStackId,
|
pub(crate) id: NextcloudStackId,
|
||||||
pub title: NextcloudStackTitle,
|
pub(crate) title: NextcloudStackTitle,
|
||||||
pub order: NextcloudOrder,
|
pub(crate) order: NextcloudOrder,
|
||||||
#[serde(rename = "boardId")]
|
#[serde(rename = "boardId")]
|
||||||
pub board_id: NextcloudBoardId,
|
pub(crate) board_id: NextcloudBoardId,
|
||||||
#[serde(rename = "ETag")]
|
#[serde(rename = "ETag")]
|
||||||
pub etag: NextcloudETag,
|
pub(crate) etag: NextcloudETag,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub cards: Vec<Card>,
|
pub(crate) cards: Vec<Card>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Card {
|
pub(crate) struct Card {
|
||||||
pub id: NextcloudCardId,
|
pub(crate) id: NextcloudCardId,
|
||||||
pub title: NextcloudCardTitle,
|
pub(crate) title: NextcloudCardTitle,
|
||||||
pub description: Option<String>,
|
pub(crate) description: Option<String>,
|
||||||
#[serde(rename = "stackId")]
|
#[serde(rename = "stackId")]
|
||||||
pub stack_id: NextcloudStackId,
|
pub(crate) stack_id: NextcloudStackId,
|
||||||
pub order: NextcloudOrder,
|
pub(crate) order: NextcloudOrder,
|
||||||
pub archived: bool,
|
pub(crate) archived: bool,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub due_date: Option<String>,
|
pub(crate) due_date: Option<String>,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub labels: Option<Vec<Label>>,
|
pub(crate) labels: Option<Vec<Label>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Label {
|
pub(crate) struct Label {
|
||||||
pub id: NextcloudLabelId,
|
pub(crate) id: NextcloudLabelId,
|
||||||
pub title: String,
|
pub(crate) title: String,
|
||||||
pub color: String,
|
pub(crate) color: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Acl {
|
pub(crate) struct Acl {
|
||||||
pub participant: String,
|
pub(crate) participant: String,
|
||||||
pub permission_edit: bool,
|
pub(crate) permission_edit: bool,
|
||||||
pub permission_share: bool,
|
pub(crate) permission_share: bool,
|
||||||
pub permission_manage: bool,
|
pub(crate) permission_manage: bool,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,26 @@
|
||||||
//
|
//
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
use crate::execute::Execute;
|
||||||
use crate::{p, FullCtx};
|
use crate::{p, FullCtx};
|
||||||
|
|
||||||
pub async fn list(ctx: FullCtx, dump: bool) -> color_eyre::Result<()> {
|
#[derive(Parser, Debug)]
|
||||||
|
pub(crate) enum NextcloudStackCommand {
|
||||||
|
List {
|
||||||
|
#[clap(long, action = clap::ArgAction::SetTrue)]
|
||||||
|
dump: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Execute for NextcloudStackCommand {
|
||||||
|
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::List { dump } => list(ctx, dump).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn list(ctx: FullCtx, dump: bool) -> color_eyre::Result<()> {
|
||||||
let api_result = ctx
|
let api_result = ctx
|
||||||
.deck_client()
|
.deck_client()
|
||||||
.get_stacks(ctx.cfg.nextcloud.board_id)
|
.get_stacks(ctx.cfg.nextcloud.board_id)
|
||||||
|
|
|
@ -1,26 +1,19 @@
|
||||||
//
|
//
|
||||||
use kxio::{
|
use kxio::net::StatusCode;
|
||||||
fs::TempFileSystem,
|
|
||||||
net::{MockNet, StatusCode},
|
|
||||||
print::Printer,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{NextcloudConfig, TrelloConfig},
|
nextcloud::model::{
|
||||||
nextcloud::{
|
Board, Card, NextcloudBoardColour, NextcloudBoardId, NextcloudBoardOwner,
|
||||||
model::{
|
NextcloudBoardTitle, NextcloudCardId, NextcloudCardTitle, NextcloudETag, NextcloudHostname,
|
||||||
Board, Card, NextcloudBoardColour, NextcloudBoardId, NextcloudBoardOwner,
|
NextcloudOrder, NextcloudPassword, NextcloudStackId, NextcloudStackTitle,
|
||||||
NextcloudBoardTitle, NextcloudCardId, NextcloudCardTitle, NextcloudETag,
|
NextcloudUsername, Stack,
|
||||||
NextcloudHostname, NextcloudOrder, NextcloudPassword, NextcloudStackId,
|
|
||||||
NextcloudStackTitle, NextcloudUsername, Stack,
|
|
||||||
},
|
|
||||||
DeckClient,
|
|
||||||
},
|
},
|
||||||
s, AppConfig, FullCtx,
|
s,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod config {
|
mod config {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::nextcloud::NextcloudConfig;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn config_hostname_returns_hostname() {
|
fn config_hostname_returns_hostname() {
|
||||||
|
@ -118,12 +111,16 @@ mod commands {
|
||||||
.body(include_str!("../tests/responses/nextcloud-board-list.json"))
|
.body(include_str!("../tests/responses/nextcloud-board-list.json"))
|
||||||
.expect("mock request");
|
.expect("mock request");
|
||||||
|
|
||||||
let fs = given::a_filesystem();
|
// let fs = given::a_filesystem();
|
||||||
let ctx = given::a_full_context(mock_net, fs);
|
let ctx = given::a_full_context(mock_net);
|
||||||
let deck_client = DeckClient::new(&ctx);
|
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = deck_client.get_boards().await.result.expect("get boards");
|
let result = ctx
|
||||||
|
.deck_client()
|
||||||
|
.get_boards()
|
||||||
|
.await
|
||||||
|
.result
|
||||||
|
.expect("get boards");
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
result.first(),
|
result.first(),
|
||||||
|
@ -162,12 +159,12 @@ mod commands {
|
||||||
))
|
))
|
||||||
.expect("mock request");
|
.expect("mock request");
|
||||||
|
|
||||||
let fs = given::a_filesystem();
|
// let fs = given::a_filesystem();
|
||||||
let ctx = given::a_full_context(mock_net, fs);
|
let ctx = given::a_full_context(mock_net);
|
||||||
let deck_client = DeckClient::new(&ctx);
|
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = deck_client
|
let result = ctx
|
||||||
|
.deck_client()
|
||||||
.get_stacks(ctx.cfg.nextcloud.board_id)
|
.get_stacks(ctx.cfg.nextcloud.board_id)
|
||||||
.await
|
.await
|
||||||
.result;
|
.result;
|
||||||
|
@ -187,12 +184,12 @@ mod commands {
|
||||||
.body(include_str!("../tests/responses/nextcloud-stack-list.json"))
|
.body(include_str!("../tests/responses/nextcloud-stack-list.json"))
|
||||||
.expect("mock request");
|
.expect("mock request");
|
||||||
|
|
||||||
let fs = given::a_filesystem();
|
// let fs = given::a_filesystem();
|
||||||
let ctx = given::a_full_context(mock_net, fs);
|
let ctx = given::a_full_context(mock_net);
|
||||||
let deck_client = DeckClient::new(&ctx);
|
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = deck_client
|
let result = ctx
|
||||||
|
.deck_client()
|
||||||
.get_stacks(ctx.cfg.nextcloud.board_id)
|
.get_stacks(ctx.cfg.nextcloud.board_id)
|
||||||
.await
|
.await
|
||||||
.result
|
.result
|
||||||
|
@ -305,12 +302,12 @@ mod commands {
|
||||||
.body(include_str!("../tests/responses/nextcloud-card-list.json"))
|
.body(include_str!("../tests/responses/nextcloud-card-list.json"))
|
||||||
.expect("mock request");
|
.expect("mock request");
|
||||||
|
|
||||||
let fs = given::a_filesystem();
|
// let fs = given::a_filesystem();
|
||||||
let ctx = given::a_full_context(mock_net, fs);
|
let ctx = given::a_full_context(mock_net);
|
||||||
let deck_client = DeckClient::new(&ctx);
|
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = deck_client
|
let result = ctx
|
||||||
|
.deck_client()
|
||||||
.get_stack(ctx.cfg.nextcloud.board_id, 1.into())
|
.get_stack(ctx.cfg.nextcloud.board_id, 1.into())
|
||||||
.await
|
.await
|
||||||
.result
|
.result
|
||||||
|
@ -351,12 +348,12 @@ mod commands {
|
||||||
.body(include_str!("../tests/responses/nextcloud-card-get.json"))
|
.body(include_str!("../tests/responses/nextcloud-card-get.json"))
|
||||||
.expect("mock request");
|
.expect("mock request");
|
||||||
|
|
||||||
let fs = given::a_filesystem();
|
// let fs = given::a_filesystem();
|
||||||
let ctx = given::a_full_context(mock_net, fs);
|
let ctx = given::a_full_context(mock_net);
|
||||||
let deck_client = DeckClient::new(&ctx);
|
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = deck_client
|
let result = ctx
|
||||||
|
.deck_client()
|
||||||
.get_card(ctx.cfg.nextcloud.board_id, 1.into(), 321.into())
|
.get_card(ctx.cfg.nextcloud.board_id, 1.into(), 321.into())
|
||||||
.await
|
.await
|
||||||
.result
|
.result
|
||||||
|
@ -405,12 +402,12 @@ mod commands {
|
||||||
))
|
))
|
||||||
.expect("mock request");
|
.expect("mock request");
|
||||||
|
|
||||||
let fs = given::a_filesystem();
|
// let fs = given::a_filesystem();
|
||||||
let ctx = given::a_full_context(mock_net, fs);
|
let ctx = given::a_full_context(mock_net);
|
||||||
let deck_client = DeckClient::new(&ctx);
|
|
||||||
|
|
||||||
//when
|
//when
|
||||||
let result = deck_client
|
let result = ctx
|
||||||
|
.deck_client()
|
||||||
.create_card(&Create {
|
.create_card(&Create {
|
||||||
dump: false,
|
dump: false,
|
||||||
stack_id: 1,
|
stack_id: 1,
|
||||||
|
@ -439,10 +436,16 @@ mod commands {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod given {
|
mod given {
|
||||||
|
use kxio::{net::MockNet, print::Printer};
|
||||||
|
|
||||||
use super::*;
|
use crate::nextcloud::model::{
|
||||||
|
NextcloudBoardId, NextcloudHostname, NextcloudPassword, NextcloudUsername,
|
||||||
|
};
|
||||||
|
use crate::nextcloud::NextcloudConfig;
|
||||||
|
use crate::trello::TrelloConfig;
|
||||||
|
use crate::{s, AppConfig, FullCtx};
|
||||||
|
|
||||||
pub fn a_nextcloud_config() -> NextcloudConfig {
|
pub(crate) fn a_nextcloud_config() -> NextcloudConfig {
|
||||||
let hostname = NextcloudHostname::new("host-name");
|
let hostname = NextcloudHostname::new("host-name");
|
||||||
let username = NextcloudUsername::new("username");
|
let username = NextcloudUsername::new("username");
|
||||||
let password = NextcloudPassword::new("password");
|
let password = NextcloudPassword::new("password");
|
||||||
|
@ -455,13 +458,13 @@ mod given {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_printer() -> Printer {
|
pub(crate) fn a_printer() -> Printer {
|
||||||
kxio::print::test()
|
kxio::print::test()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn a_filesystem() -> TempFileSystem {
|
// pub(crate) fn a_filesystem() -> TempFileSystem {
|
||||||
kxio::fs::temp().expect("temp fs")
|
// kxio::fs::temp().expect("temp fs")
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub(crate) fn a_trello_config() -> TrelloConfig {
|
pub(crate) fn a_trello_config() -> TrelloConfig {
|
||||||
TrelloConfig {
|
TrelloConfig {
|
||||||
|
@ -471,14 +474,17 @@ mod given {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn a_full_context(mock_net: MockNet, fs: TempFileSystem) -> FullCtx {
|
pub(crate) fn a_full_context(
|
||||||
|
mock_net: MockNet,
|
||||||
|
// fs: TempFileSystem
|
||||||
|
) -> FullCtx {
|
||||||
FullCtx {
|
FullCtx {
|
||||||
fs: fs.as_real(),
|
// fs: fs.as_real(),
|
||||||
net: mock_net.into(),
|
net: mock_net.into(),
|
||||||
prt: given::a_printer(),
|
prt: a_printer(),
|
||||||
cfg: AppConfig {
|
cfg: AppConfig {
|
||||||
trello: given::a_trello_config(),
|
trello: a_trello_config(),
|
||||||
nextcloud: given::a_nextcloud_config(),
|
nextcloud: a_nextcloud_config(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,13 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use assert2::let_assert;
|
use assert2::let_assert;
|
||||||
|
|
||||||
use crate::{config::AppConfig, f, init::run, Ctx, NAME};
|
use crate::{
|
||||||
|
config::AppConfig, f, init::run, nextcloud::NextcloudConfig, s, trello::TrelloConfig, Ctx, NAME,
|
||||||
|
};
|
||||||
|
|
||||||
mod config {
|
mod config {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::config::{NextcloudConfig, TrelloConfig};
|
|
||||||
use crate::s;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn load_config() {
|
fn load_config() {
|
||||||
//given
|
//given
|
||||||
|
@ -124,18 +123,18 @@ mod given {
|
||||||
print::Printer,
|
print::Printer,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn a_context(fs: FileSystem, net: Net, prt: Printer) -> Ctx {
|
pub(crate) fn a_context(fs: FileSystem, net: Net, prt: Printer) -> Ctx {
|
||||||
Ctx { fs, net, prt }
|
Ctx { fs, net, prt }
|
||||||
}
|
}
|
||||||
pub fn a_filesystem() -> TempFileSystem {
|
pub(crate) fn a_filesystem() -> TempFileSystem {
|
||||||
kxio::fs::temp().expect("temp fs")
|
kxio::fs::temp().expect("temp fs")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_network() -> MockNet {
|
pub(crate) fn a_network() -> MockNet {
|
||||||
kxio::net::mock()
|
kxio::net::mock()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn a_printer() -> Printer {
|
pub(crate) fn a_printer() -> Printer {
|
||||||
kxio::print::test()
|
kxio::print::test()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
// use color_eyre::Result;
|
|
||||||
// use kxio::net::Net;
|
|
||||||
|
|
||||||
use crate::trello::types::{board::TrelloBoard, TrelloBoardName};
|
use crate::trello::types::{board::TrelloBoard, TrelloBoardName};
|
||||||
|
|
||||||
// pub async fn get_board(
|
// pub(crate) async fn get_board(
|
||||||
// auth: &TrelloAuth,
|
// auth: &TrelloAuth,
|
||||||
// board_id: &TrelloBoardId,
|
// board_id: &TrelloBoardId,
|
||||||
// net: &Net,
|
// net: &Net,
|
||||||
|
@ -23,7 +19,7 @@ use crate::trello::types::{board::TrelloBoard, TrelloBoardName};
|
||||||
// Ok(board)
|
// Ok(board)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
pub trait TrelloBoards {
|
pub(crate) trait TrelloBoards {
|
||||||
fn find_by_name(&self, board_name: &TrelloBoardName) -> Option<&TrelloBoard>;
|
fn find_by_name(&self, board_name: &TrelloBoardName) -> Option<&TrelloBoard>;
|
||||||
}
|
}
|
||||||
impl TrelloBoards for Vec<TrelloBoard> {
|
impl TrelloBoards for Vec<TrelloBoard> {
|
||||||
|
|
|
@ -2,8 +2,3 @@ mod create;
|
||||||
mod delete;
|
mod delete;
|
||||||
mod get;
|
mod get;
|
||||||
mod update;
|
mod update;
|
||||||
|
|
||||||
pub use create::create_card;
|
|
||||||
pub use delete::delete_card;
|
|
||||||
pub use get::get_card;
|
|
||||||
pub use update::{update_card, TrelloCardUpdate};
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ use crate::{
|
||||||
/// 200 OK Success
|
/// 200 OK Success
|
||||||
///
|
///
|
||||||
/// application/json
|
/// application/json
|
||||||
pub async fn get_lists_cards(
|
pub(crate) async fn get_lists_cards(
|
||||||
auth: &TrelloAuth,
|
auth: &TrelloAuth,
|
||||||
list: &TrelloList,
|
list: &TrelloList,
|
||||||
net: &Net,
|
net: &Net,
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
//
|
//
|
||||||
use kxio::{net::Net, print::Printer};
|
use kxio::{net::Net, print::Printer};
|
||||||
|
|
||||||
use crate::api_result::APIResult;
|
use crate::trello::TrelloConfig;
|
||||||
use crate::config::TrelloConfig;
|
use crate::{
|
||||||
use crate::trello::{
|
api_result::APIResult,
|
||||||
types::{auth::TrelloAuth, board::TrelloBoard},
|
trello::{
|
||||||
url,
|
types::{auth::TrelloAuth, board::TrelloBoard},
|
||||||
|
url,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Get lists from named board that Member belongs to
|
/// Get lists from named board that Member belongs to
|
||||||
|
@ -37,12 +39,15 @@ use crate::trello::{
|
||||||
/// curl --request GET \
|
/// curl --request GET \
|
||||||
/// --url "https://api.trello.com/1/members/$TRELLO_USERNAME/boards?key=$TRELLO_KEY&token=$TRELLO_SECRET&lists=open" \
|
/// --url "https://api.trello.com/1/members/$TRELLO_USERNAME/boards?key=$TRELLO_KEY&token=$TRELLO_SECRET&lists=open" \
|
||||||
/// --header 'Accept: application/json'
|
/// --header 'Accept: application/json'
|
||||||
pub async fn get_boards_that_member_belongs_to(
|
pub(crate) async fn get_boards_that_member_belongs_to(
|
||||||
cfg: &TrelloConfig,
|
cfg: &TrelloConfig,
|
||||||
net: &Net,
|
net: &Net,
|
||||||
prt: &Printer,
|
prt: &Printer,
|
||||||
) -> APIResult<Vec<TrelloBoard>> {
|
) -> APIResult<Vec<TrelloBoard>> {
|
||||||
let auth = TrelloAuth::new(&cfg.api_key, &cfg.api_secret);
|
let auth = TrelloAuth {
|
||||||
|
api_key: &cfg.api_key,
|
||||||
|
api_secret: &cfg.api_secret,
|
||||||
|
};
|
||||||
APIResult::new(
|
APIResult::new(
|
||||||
net.get(url("/members/me/boards?lists=open"))
|
net.get(url("/members/me/boards?lists=open"))
|
||||||
.headers(auth.into())
|
.headers(auth.into())
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
pub mod boards;
|
pub(crate) mod boards;
|
||||||
// pub mod cards;
|
// pub(crate) mod cards;
|
||||||
// pub mod lists;
|
// pub(crate) mod lists;
|
||||||
pub mod members;
|
pub(crate) mod members;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
|
@ -5,7 +5,6 @@ use kxio::net::StatusCode;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::TrelloConfig,
|
|
||||||
s,
|
s,
|
||||||
trello::{api::members::get_boards_that_member_belongs_to, types::board::TrelloBoard},
|
trello::{api::members::get_boards_that_member_belongs_to, types::board::TrelloBoard},
|
||||||
};
|
};
|
||||||
|
@ -15,8 +14,8 @@ mod given;
|
||||||
type TestResult = color_eyre::Result<()>;
|
type TestResult = color_eyre::Result<()>;
|
||||||
|
|
||||||
mod members {
|
mod members {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::trello::TrelloConfig;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn get_member_boards() -> TestResult {
|
async fn get_member_boards() -> TestResult {
|
||||||
|
|
|
@ -1,18 +1,35 @@
|
||||||
//
|
//
|
||||||
// use crate::{p, FullCtx};
|
use clap::Parser;
|
||||||
//
|
|
||||||
// pub(crate) async fn list(ctx: FullCtx, dump: bool) -> color_eyre::Result<()> {
|
use crate::execute::Execute;
|
||||||
// let api_result =
|
use crate::{p, FullCtx};
|
||||||
// super::api::members::get_boards_that_member_belongs_to(&ctx.cfg.trello, &ctx.net, &ctx.prt)
|
|
||||||
// .await;
|
#[derive(Parser, Debug)]
|
||||||
// if dump {
|
pub(crate) enum TrelloBoardCommand {
|
||||||
// p!(ctx.prt, "{}", api_result.text);
|
List {
|
||||||
// } else {
|
#[clap(long, action = clap::ArgAction::SetTrue)]
|
||||||
// let mut boards = api_result.result?;
|
dump: bool,
|
||||||
// boards.sort_by(|a, b| a.name.cmp(&b.name));
|
},
|
||||||
// boards.into_iter().for_each(|board| {
|
}
|
||||||
// p!(ctx.prt, "{}:{}", board.id, board.name);
|
|
||||||
// });
|
impl Execute for TrelloBoardCommand {
|
||||||
// }
|
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
||||||
// Ok(())
|
match self {
|
||||||
// }
|
Self::List { dump } => list(ctx, dump).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn list(ctx: FullCtx, dump: bool) -> color_eyre::Result<()> {
|
||||||
|
let api_result = ctx.trello_client().boards(&ctx.cfg.trello).await;
|
||||||
|
if dump {
|
||||||
|
p!(ctx.prt, "{}", api_result.text);
|
||||||
|
} else {
|
||||||
|
let mut boards = api_result.result?;
|
||||||
|
boards.sort_by(|a, b| a.name.cmp(&b.name));
|
||||||
|
boards.into_iter().for_each(|board| {
|
||||||
|
p!(ctx.prt, "{}:{}", board.id, board.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
22
src/trello/client.rs
Normal file
22
src/trello/client.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
//
|
||||||
|
use crate::api_result::APIResult;
|
||||||
|
use crate::trello::types::board::TrelloBoard;
|
||||||
|
use crate::trello::TrelloConfig;
|
||||||
|
use crate::FullCtx;
|
||||||
|
|
||||||
|
pub(crate) struct TrelloClient<'ctx> {
|
||||||
|
ctx: &'ctx FullCtx,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> TrelloClient<'ctx> {
|
||||||
|
pub(crate) async fn boards(&self, cfg: &TrelloConfig) -> APIResult<Vec<TrelloBoard>> {
|
||||||
|
super::api::members::get_boards_that_member_belongs_to(cfg, &self.ctx.net, &self.ctx.prt)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TrelloClient<'_> {
|
||||||
|
pub(crate) fn new(ctx: &FullCtx) -> TrelloClient {
|
||||||
|
TrelloClient { ctx }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,16 +1,42 @@
|
||||||
//
|
//
|
||||||
// pub mod api;
|
pub(crate) mod api;
|
||||||
pub mod api;
|
pub(crate) mod boards;
|
||||||
pub mod boards;
|
pub(crate) mod client;
|
||||||
pub mod types;
|
pub(crate) mod types;
|
||||||
|
|
||||||
// #[cfg(test)]
|
// #[cfg(test)]
|
||||||
// mod tests;
|
// mod tests;
|
||||||
|
|
||||||
use crate::f;
|
use crate::execute::Execute;
|
||||||
|
use crate::trello::boards::TrelloBoardCommand;
|
||||||
|
use crate::trello::types::auth::{TrelloApiKey, TrelloApiSecret};
|
||||||
|
use crate::trello::types::TrelloBoardName;
|
||||||
|
use crate::{f, FullCtx};
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
pub fn url(path: impl Into<String>) -> String {
|
pub(crate) fn url(path: impl Into<String>) -> String {
|
||||||
let path = path.into();
|
let path = path.into();
|
||||||
assert!(path.starts_with("/"));
|
assert!(path.starts_with("/"));
|
||||||
f!("https://api.trello.com/1{path}")
|
f!("https://api.trello.com/1{path}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
pub(crate) enum TrelloCommand {
|
||||||
|
#[clap(subcommand)]
|
||||||
|
Board(TrelloBoardCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Execute for TrelloCommand {
|
||||||
|
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Board(cmd) => cmd.execute(ctx).await,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, derive_more::From, PartialEq, Eq, serde::Deserialize)]
|
||||||
|
pub struct TrelloConfig {
|
||||||
|
pub(crate) api_key: TrelloApiKey,
|
||||||
|
pub(crate) api_secret: TrelloApiSecret,
|
||||||
|
pub(crate) board_name: TrelloBoardName,
|
||||||
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
//
|
//
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{s, trello::types::auth::TrelloAuth};
|
use crate::{
|
||||||
use crate::trello::{
|
s,
|
||||||
api::boards::TrelloBoards as _,
|
trello::types::auth::TrelloAuth,
|
||||||
types::{
|
trello::{
|
||||||
TrelloBoard, TrelloBoardId, TrelloBoardName, TrelloList, TrelloListId,
|
api::boards::TrelloBoards as _,
|
||||||
TrelloListName,
|
types::{
|
||||||
|
TrelloBoard, TrelloBoardId, TrelloBoardName, TrelloList, TrelloListId, TrelloListName,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,6 +60,6 @@ fn trello_auth_into_hashmap() {
|
||||||
HashMap::from([(
|
HashMap::from([(
|
||||||
s!("Authorization"),
|
s!("Authorization"),
|
||||||
s!("OAuth oauth_consumer_key=\"key\", oauth_token=\"token\"")
|
s!("OAuth oauth_consumer_key=\"key\", oauth_token=\"token\"")
|
||||||
), ])
|
),])
|
||||||
);
|
);
|
||||||
}
|
}
|
|
@ -18,29 +18,13 @@ pub struct TrelloAuth<'cfg> {
|
||||||
pub(crate) api_key: &'cfg TrelloApiKey,
|
pub(crate) api_key: &'cfg TrelloApiKey,
|
||||||
pub(crate) api_secret: &'cfg TrelloApiSecret,
|
pub(crate) api_secret: &'cfg TrelloApiSecret,
|
||||||
}
|
}
|
||||||
impl<'cfg> TrelloAuth<'cfg> {
|
|
||||||
pub const fn new(api_key: &'cfg TrelloApiKey, api_secret: &'cfg TrelloApiSecret) -> Self {
|
|
||||||
Self {
|
|
||||||
api_key,
|
|
||||||
api_secret,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub const fn api_key(&self) -> &TrelloApiKey {
|
|
||||||
self.api_key
|
|
||||||
}
|
|
||||||
pub const fn api_token(&self) -> &TrelloApiSecret {
|
|
||||||
self.api_secret
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<TrelloAuth<'_>> for HashMap<String, String> {
|
impl From<TrelloAuth<'_>> for HashMap<String, String> {
|
||||||
fn from(value: TrelloAuth) -> Self {
|
fn from(value: TrelloAuth) -> Self {
|
||||||
HashMap::from([(
|
HashMap::from([(
|
||||||
"Authorization".into(),
|
"Authorization".into(),
|
||||||
format!(
|
format!(
|
||||||
r#"OAuth oauth_consumer_key="{}", oauth_token="{}""#,
|
r#"OAuth oauth_consumer_key="{}", oauth_token="{}""#,
|
||||||
value.api_key(),
|
value.api_key, value.api_secret
|
||||||
value.api_token()
|
|
||||||
),
|
),
|
||||||
)])
|
)])
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
use crate::trello::{api::cards::TrelloCardUpdate, TrelloCardId, TrelloCardName, TrelloListId};
|
use crate::trello::{api::cards::TrelloCardUpdate, TrelloCardId, TrelloCardName, TrelloListId};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||||
pub struct TrelloCard {
|
pub(crate) struct TrelloCard {
|
||||||
id: TrelloCardId,
|
id: TrelloCardId,
|
||||||
name: TrelloCardName,
|
name: TrelloCardName,
|
||||||
#[serde(rename = "idList")]
|
#[serde(rename = "idList")]
|
||||||
|
@ -10,11 +10,11 @@ pub struct TrelloCard {
|
||||||
}
|
}
|
||||||
impl TrelloCard {
|
impl TrelloCard {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub const fn new(id: TrelloCardId, name: TrelloCardName, id_list: TrelloListId) -> Self {
|
pub(crate) const fn new(id: TrelloCardId, name: TrelloCardName, id_list: TrelloListId) -> Self {
|
||||||
Self { id, name, id_list }
|
Self { id, name, id_list }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const fn list_id(&self) -> &TrelloListId {
|
pub(crate) const fn list_id(&self) -> &TrelloListId {
|
||||||
&self.id_list
|
&self.id_list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
pub(crate) mod auth;
|
pub(crate) mod auth;
|
||||||
pub(crate) mod board;
|
pub(crate) mod board;
|
||||||
// mod card;
|
// mod card;
|
||||||
mod list;
|
pub(crate) mod list;
|
||||||
// mod new_card;
|
// mod new_card;
|
||||||
|
|
||||||
use derive_more::derive::Display;
|
use derive_more::derive::Display;
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
use super::{TrelloCardName, TrelloListId};
|
use super::{TrelloCardName, TrelloListId};
|
||||||
|
|
||||||
pub struct NewTrelloCard {
|
pub(crate) struct NewTrelloCard {
|
||||||
name: TrelloCardName,
|
name: TrelloCardName,
|
||||||
list_id: TrelloListId,
|
list_id: TrelloListId,
|
||||||
}
|
}
|
||||||
impl NewTrelloCard {
|
impl NewTrelloCard {
|
||||||
pub fn new(name: impl Into<TrelloCardName>, list_id: impl Into<TrelloListId>) -> Self {
|
pub(crate) fn new(name: impl Into<TrelloCardName>, list_id: impl Into<TrelloListId>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
list_id: list_id.into(),
|
list_id: list_id.into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub const fn name(&self) -> &TrelloCardName {
|
pub(crate) const fn name(&self) -> &TrelloCardName {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
pub const fn list_id(&self) -> &TrelloListId {
|
pub(crate) const fn list_id(&self) -> &TrelloListId {
|
||||||
&self.list_id
|
&self.list_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue