feat(nextcloud): add basics of nextcloud config model client
This commit is contained in:
parent
c8f9780226
commit
327e33a7e3
11 changed files with 884 additions and 14 deletions
|
@ -4,7 +4,6 @@ use kxio::net::Response;
|
||||||
use crate::{e, s};
|
use crate::{e, s};
|
||||||
|
|
||||||
pub struct APIResult<T> {
|
pub struct APIResult<T> {
|
||||||
pub text: String,
|
|
||||||
pub result: Result<T, kxio::net::Error>,
|
pub result: Result<T, kxio::net::Error>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,12 +20,9 @@ impl<T: for<'a> serde::Deserialize<'a>> APIResult<T> {
|
||||||
e
|
e
|
||||||
})
|
})
|
||||||
.map_err(kxio::net::Error::from);
|
.map_err(kxio::net::Error::from);
|
||||||
Self { text, result }
|
Self { result }
|
||||||
}
|
}
|
||||||
Err(e) => Self {
|
Err(e) => Self { result: Err(e) },
|
||||||
text: s!(""),
|
|
||||||
result: Err(e),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,35 @@
|
||||||
//
|
//
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
|
|
||||||
use crate::{f, s, Ctx, NAME};
|
use crate::{
|
||||||
|
f,
|
||||||
|
nextcloud::model::{NextcloudBoardId, NextcloudHostname, NextcloudPassword, NextcloudUsername},
|
||||||
|
s, Ctx, NAME,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, derive_more::From, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize)]
|
#[derive(Clone, Debug, derive_more::From, PartialEq, Eq, PartialOrd, Ord, serde::Deserialize)]
|
||||||
pub struct TrelloConfig {
|
pub(crate) struct TrelloConfig {
|
||||||
pub api_key: String,
|
pub(crate) api_key: String,
|
||||||
pub api_secret: String,
|
pub(crate) api_secret: String,
|
||||||
pub board_name: String,
|
pub(crate) board_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Clone,
|
||||||
|
Debug,
|
||||||
|
derive_more::From,
|
||||||
|
PartialEq,
|
||||||
|
Eq,
|
||||||
|
PartialOrd,
|
||||||
|
Ord,
|
||||||
|
serde::Deserialize,
|
||||||
|
derive_more::Constructor,
|
||||||
|
)]
|
||||||
|
pub(crate) struct NextcloudConfig {
|
||||||
|
pub(crate) hostname: NextcloudHostname,
|
||||||
|
pub(crate) username: NextcloudUsername,
|
||||||
|
pub(crate) password: NextcloudPassword,
|
||||||
|
pub(crate) board_id: NextcloudBoardId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(
|
#[derive(
|
||||||
|
@ -22,7 +44,8 @@ pub struct TrelloConfig {
|
||||||
serde::Deserialize,
|
serde::Deserialize,
|
||||||
)]
|
)]
|
||||||
pub struct AppConfig {
|
pub struct AppConfig {
|
||||||
pub trello: TrelloConfig,
|
pub(crate) trello: TrelloConfig,
|
||||||
|
pub(crate) nextcloud: NextcloudConfig,
|
||||||
}
|
}
|
||||||
impl AppConfig {
|
impl AppConfig {
|
||||||
pub fn load(ctx: &Ctx) -> Result<Self> {
|
pub fn load(ctx: &Ctx) -> Result<Self> {
|
||||||
|
|
|
@ -2,12 +2,14 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
pub use config::AppConfig;
|
||||||
use kxio::{fs::FileSystem, net::Net};
|
use kxio::{fs::FileSystem, net::Net};
|
||||||
|
|
||||||
// mod api_result;
|
mod api_result;
|
||||||
mod config;
|
mod config;
|
||||||
mod init;
|
mod init;
|
||||||
mod macros;
|
mod macros;
|
||||||
|
pub mod nextcloud;
|
||||||
mod template;
|
mod template;
|
||||||
// mod trello;
|
// mod trello;
|
||||||
|
|
||||||
|
|
22
src/nextcloud/board.rs
Normal file
22
src/nextcloud/board.rs
Normal file
|
@ -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(())
|
||||||
|
}
|
113
src/nextcloud/mod.rs
Normal file
113
src/nextcloud/mod.rs
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
//
|
||||||
|
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(crate) struct DeckClient {
|
||||||
|
net: Net,
|
||||||
|
hostname: String,
|
||||||
|
username: String,
|
||||||
|
password: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeckClient {
|
||||||
|
pub fn new(cfg: &NextcloudConfig, net: Net) -> Self {
|
||||||
|
Self {
|
||||||
|
net,
|
||||||
|
hostname: cfg.hostname.to_string(),
|
||||||
|
username: cfg.username.to_string(),
|
||||||
|
password: cfg.password.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn url(&self, path: impl Into<String>) -> String {
|
||||||
|
f!(
|
||||||
|
"https://{}/index.php/apps/deck/api/v1.0/{}",
|
||||||
|
self.hostname,
|
||||||
|
path.into()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_boards(&self) -> APIResult<Vec<Board>> {
|
||||||
|
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 create_board(&self, title: &str, color: &str) -> APIResult<Board> {
|
||||||
|
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<Vec<Stack>> {
|
||||||
|
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<Card> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
168
src/nextcloud/model.rs
Normal file
168
src/nextcloud/model.rs
Normal file
|
@ -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<Label>,
|
||||||
|
pub acl: Vec<Acl>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Stack {
|
||||||
|
pub id: NextcloudStackId,
|
||||||
|
pub title: NextcloudStackTitle,
|
||||||
|
pub order: NextcloudOrder,
|
||||||
|
#[serde(rename = "boardId")]
|
||||||
|
pub board_id: NextcloudBoardId,
|
||||||
|
#[serde(rename = "ETag")]
|
||||||
|
pub etag: NextcloudETag,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Card {
|
||||||
|
pub id: NextcloudCardId,
|
||||||
|
pub title: NextcloudCardTitle,
|
||||||
|
pub description: Option<String>,
|
||||||
|
#[serde(rename = "stackId")]
|
||||||
|
pub stack_id: NextcloudStackId,
|
||||||
|
pub order: NextcloudOrder,
|
||||||
|
pub archived: bool,
|
||||||
|
pub due_date: Option<String>,
|
||||||
|
pub labels: Vec<NextcloudLabelId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Label {
|
||||||
|
pub id: NextcloudLabelId,
|
||||||
|
pub title: String,
|
||||||
|
pub color: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct Acl {
|
||||||
|
pub participant: String,
|
||||||
|
pub permission_edit: bool,
|
||||||
|
pub permission_share: bool,
|
||||||
|
pub permission_manage: bool,
|
||||||
|
}
|
19
src/nextcloud/stack.rs
Normal file
19
src/nextcloud/stack.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
//
|
||||||
|
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_stacks(ctx.cfg.nextcloud.board_id()).await;
|
||||||
|
if dump {
|
||||||
|
p!("{}", apiresult.text);
|
||||||
|
} else {
|
||||||
|
let mut stacks = apiresult.result?;
|
||||||
|
stacks.sort_by_key(|stack| stack.order);
|
||||||
|
stacks
|
||||||
|
.iter()
|
||||||
|
.for_each(|stack| p!("{}:{}", stack.id, stack.title));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
213
src/nextcloud/tests.rs
Normal file
213
src/nextcloud/tests.rs
Normal file
|
@ -0,0 +1,213 @@
|
||||||
|
//
|
||||||
|
use crate::{
|
||||||
|
config::NextcloudConfig,
|
||||||
|
nextcloud::{
|
||||||
|
model::{NextcloudBoardId, NextcloudHostname, NextcloudPassword, NextcloudUsername},
|
||||||
|
DeckClient,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mod config {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_hostname_returns_hostname() {
|
||||||
|
//given
|
||||||
|
let hostname = NextcloudHostname::new("host-name");
|
||||||
|
let username = NextcloudUsername::new("username");
|
||||||
|
let password = NextcloudPassword::new("password");
|
||||||
|
let board_id = NextcloudBoardId::new(2);
|
||||||
|
let cfg = NextcloudConfig::new(hostname.clone(), username, password, board_id);
|
||||||
|
|
||||||
|
//when
|
||||||
|
//then
|
||||||
|
assert_eq!(cfg.hostname, hostname);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_username_returns_username() {
|
||||||
|
//given
|
||||||
|
let hostname = NextcloudHostname::new("host-name");
|
||||||
|
let username = NextcloudUsername::new("username");
|
||||||
|
let password = NextcloudPassword::new("password");
|
||||||
|
let board_id = NextcloudBoardId::new(2);
|
||||||
|
let cfg = NextcloudConfig::new(hostname, username.clone(), password, board_id);
|
||||||
|
|
||||||
|
//when
|
||||||
|
//then
|
||||||
|
assert_eq!(cfg.username, username);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_password_returns_password() {
|
||||||
|
//given
|
||||||
|
let hostname = NextcloudHostname::new("host-name");
|
||||||
|
let username = NextcloudUsername::new("username");
|
||||||
|
let password = NextcloudPassword::new("password");
|
||||||
|
let board_id = NextcloudBoardId::new(2);
|
||||||
|
let cfg = NextcloudConfig::new(hostname, username, password.clone(), board_id);
|
||||||
|
|
||||||
|
//when
|
||||||
|
//then
|
||||||
|
assert_eq!(cfg.password, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_board_id_returns_board_id() {
|
||||||
|
//given
|
||||||
|
let hostname = NextcloudHostname::new("host-name");
|
||||||
|
let username = NextcloudUsername::new("username");
|
||||||
|
let password = NextcloudPassword::new("password");
|
||||||
|
let board_id = NextcloudBoardId::new(2);
|
||||||
|
let cfg = NextcloudConfig::new(hostname, username, password, board_id);
|
||||||
|
|
||||||
|
//when
|
||||||
|
//then
|
||||||
|
assert_eq!(cfg.board_id, board_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod client {
|
||||||
|
use kxio::net::StatusCode;
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
nextcloud::model::{
|
||||||
|
Acl, Board, Label, NextcloudBoardColour, NextcloudBoardOwner, NextcloudBoardTitle,
|
||||||
|
NextcloudETag, NextcloudLabelId, NextcloudOrder, NextcloudStackId, NextcloudStackTitle,
|
||||||
|
Stack,
|
||||||
|
},
|
||||||
|
s,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_boards() {
|
||||||
|
//given
|
||||||
|
let mock_net = kxio::net::mock();
|
||||||
|
|
||||||
|
mock_net
|
||||||
|
.on()
|
||||||
|
.get("https://host-name/index.php/apps/deck/api/v1.0/boards")
|
||||||
|
.basic_auth("username", Some("password"))
|
||||||
|
.respond(StatusCode::OK)
|
||||||
|
.body(
|
||||||
|
json!([{
|
||||||
|
"id":2,
|
||||||
|
"title":"board-title",
|
||||||
|
"owner":"owner",
|
||||||
|
"color":"red",
|
||||||
|
"archived":false,
|
||||||
|
"labels":[
|
||||||
|
{
|
||||||
|
"id":2,
|
||||||
|
"title":"label-title",
|
||||||
|
"color":"blue"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"acl":[
|
||||||
|
{
|
||||||
|
"participant":"participant",
|
||||||
|
"permission_edit":true,
|
||||||
|
"permission_share":false,
|
||||||
|
"permission_manage":true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}])
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
.expect("mock request");
|
||||||
|
|
||||||
|
let cfg = given::a_nextcloud_config();
|
||||||
|
let deck_client = DeckClient::new(&cfg, mock_net.into());
|
||||||
|
|
||||||
|
//when
|
||||||
|
let result = deck_client.get_boards().await.result.expect("get boards");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
vec![Board {
|
||||||
|
id: NextcloudBoardId::new(2),
|
||||||
|
title: NextcloudBoardTitle::new("board-title"),
|
||||||
|
owner: NextcloudBoardOwner::new("owner"),
|
||||||
|
color: NextcloudBoardColour::new("red"),
|
||||||
|
archived: false,
|
||||||
|
labels: vec![Label {
|
||||||
|
id: NextcloudLabelId::new(2),
|
||||||
|
title: s!("label-title"),
|
||||||
|
color: s!("blue")
|
||||||
|
}],
|
||||||
|
acl: vec![Acl {
|
||||||
|
participant: s!("participant"),
|
||||||
|
permission_edit: true,
|
||||||
|
permission_share: false,
|
||||||
|
permission_manage: true
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn get_stacks() {
|
||||||
|
//given
|
||||||
|
let mock_net = kxio::net::mock();
|
||||||
|
|
||||||
|
mock_net
|
||||||
|
.on()
|
||||||
|
.get("https://host-name/index.php/apps/deck/api/v1.0/boards/2/stacks")
|
||||||
|
.basic_auth("username", Some("password"))
|
||||||
|
.respond(StatusCode::OK)
|
||||||
|
.body(include_str!("../tests/responses/nextcloud-stack-list.json"))
|
||||||
|
.expect("mock request");
|
||||||
|
|
||||||
|
let cfg = given::a_nextcloud_config();
|
||||||
|
let deck_client = DeckClient::new(&cfg, mock_net.into());
|
||||||
|
|
||||||
|
//when
|
||||||
|
let result = deck_client
|
||||||
|
.get_stacks(cfg.board_id)
|
||||||
|
.await
|
||||||
|
.result
|
||||||
|
.expect("get stacks");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
vec![
|
||||||
|
Stack {
|
||||||
|
id: NextcloudStackId::new(3),
|
||||||
|
title: NextcloudStackTitle::new("Done"),
|
||||||
|
order: NextcloudOrder::new(2),
|
||||||
|
board_id: NextcloudBoardId::new(1),
|
||||||
|
etag: NextcloudETag::new("97592874d17017ef4f620c9c2a490086")
|
||||||
|
},
|
||||||
|
Stack {
|
||||||
|
id: NextcloudStackId::new(2),
|
||||||
|
title: NextcloudStackTitle::new("Doing"),
|
||||||
|
order: NextcloudOrder::new(1),
|
||||||
|
board_id: NextcloudBoardId::new(1),
|
||||||
|
etag: NextcloudETag::new("3da05f904903c88450b79e4f8f6e2160")
|
||||||
|
},
|
||||||
|
Stack {
|
||||||
|
id: NextcloudStackId::new(1),
|
||||||
|
title: NextcloudStackTitle::new("To do"),
|
||||||
|
order: NextcloudOrder::new(0),
|
||||||
|
board_id: NextcloudBoardId::new(1),
|
||||||
|
etag: NextcloudETag::new("b567d287210fa4d9b108ac68d5b087c1")
|
||||||
|
}
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod given {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub fn a_nextcloud_config() -> NextcloudConfig {
|
||||||
|
let hostname = NextcloudHostname::new("host-name");
|
||||||
|
let username = NextcloudUsername::new("username");
|
||||||
|
let password = NextcloudPassword::new("password");
|
||||||
|
let board_id = NextcloudBoardId::new(2);
|
||||||
|
NextcloudConfig::new(hostname, username, password, board_id)
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,7 +9,7 @@ use crate::{config::AppConfig, f, NAME};
|
||||||
mod config {
|
mod config {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::config::TrelloConfig;
|
use crate::config::{NextcloudConfig, TrelloConfig};
|
||||||
use crate::s;
|
use crate::s;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -24,6 +24,12 @@ mod config {
|
||||||
"api_key = \"trello-api-key\"",
|
"api_key = \"trello-api-key\"",
|
||||||
"api_secret = \"trello-api-secret\"",
|
"api_secret = \"trello-api-secret\"",
|
||||||
"board_name = \"trello-board-name\"",
|
"board_name = \"trello-board-name\"",
|
||||||
|
"",
|
||||||
|
"[nextcloud]",
|
||||||
|
"hostname = \"nextcloud-hostname\"",
|
||||||
|
"username = \"nextcloud-username\"",
|
||||||
|
"password = \"nextcloud-password\"",
|
||||||
|
"board_id = 22",
|
||||||
]
|
]
|
||||||
.join("\n"),
|
.join("\n"),
|
||||||
)
|
)
|
||||||
|
@ -41,6 +47,12 @@ mod config {
|
||||||
api_key: s!("trello-api-key"),
|
api_key: s!("trello-api-key"),
|
||||||
api_secret: s!("trello-api-secret"),
|
api_secret: s!("trello-api-secret"),
|
||||||
board_name: s!("trello-board-name"),
|
board_name: s!("trello-board-name"),
|
||||||
|
},
|
||||||
|
nextcloud: NextcloudConfig {
|
||||||
|
hostname: s!("nextcloud-hostname").into(),
|
||||||
|
username: s!("nextcloud-username").into(),
|
||||||
|
password: s!("nextcloud-password").into(),
|
||||||
|
board_id: 22.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
86
src/tests/responses/nextcloud-boards.json
Normal file
86
src/tests/responses/nextcloud-boards.json
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "Personal Board",
|
||||||
|
"owner": {
|
||||||
|
"primaryKey": "pcampbell",
|
||||||
|
"uid": "pcampbell",
|
||||||
|
"displayname": "Paul Campbell",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
"color": "0087C5",
|
||||||
|
"archived": false,
|
||||||
|
"labels": [],
|
||||||
|
"acl": [],
|
||||||
|
"permissions": {
|
||||||
|
"PERMISSION_READ": true,
|
||||||
|
"PERMISSION_EDIT": true,
|
||||||
|
"PERMISSION_MANAGE": true,
|
||||||
|
"PERMISSION_SHARE": true
|
||||||
|
},
|
||||||
|
"users": [],
|
||||||
|
"shared": 0,
|
||||||
|
"stacks": [],
|
||||||
|
"activeSessions": [],
|
||||||
|
"deletedAt": 0,
|
||||||
|
"lastModified": 1733337423,
|
||||||
|
"settings": [],
|
||||||
|
"ETag": "b567d287210fa4d9b108ac68d5b087c1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"title": "4 Published: Cossmass Infinities",
|
||||||
|
"owner": {
|
||||||
|
"primaryKey": "pcampbell",
|
||||||
|
"uid": "pcampbell",
|
||||||
|
"displayname": "Paul Campbell",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
"color": "ff0000",
|
||||||
|
"archived": true,
|
||||||
|
"labels": [],
|
||||||
|
"acl": [],
|
||||||
|
"permissions": {
|
||||||
|
"PERMISSION_READ": true,
|
||||||
|
"PERMISSION_EDIT": true,
|
||||||
|
"PERMISSION_MANAGE": true,
|
||||||
|
"PERMISSION_SHARE": true
|
||||||
|
},
|
||||||
|
"users": [],
|
||||||
|
"shared": 0,
|
||||||
|
"stacks": [],
|
||||||
|
"activeSessions": [],
|
||||||
|
"deletedAt": 0,
|
||||||
|
"lastModified": 1699798570,
|
||||||
|
"settings": [],
|
||||||
|
"ETag": "5e0fe035f3b95672da3cba633086be37"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"title": "Fulfilment: Cossmass Infinities",
|
||||||
|
"owner": {
|
||||||
|
"primaryKey": "pcampbell",
|
||||||
|
"uid": "pcampbell",
|
||||||
|
"displayname": "Paul Campbell",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
"color": "ff0000",
|
||||||
|
"archived": true,
|
||||||
|
"labels": [],
|
||||||
|
"acl": [],
|
||||||
|
"permissions": {
|
||||||
|
"PERMISSION_READ": true,
|
||||||
|
"PERMISSION_EDIT": true,
|
||||||
|
"PERMISSION_MANAGE": true,
|
||||||
|
"PERMISSION_SHARE": true
|
||||||
|
},
|
||||||
|
"users": [],
|
||||||
|
"shared": 0,
|
||||||
|
"stacks": [],
|
||||||
|
"activeSessions": [],
|
||||||
|
"deletedAt": 0,
|
||||||
|
"lastModified": 1699798567,
|
||||||
|
"settings": [],
|
||||||
|
"ETag": "90e2f9d53c5f6ec83088425d4486e54d"
|
||||||
|
}
|
||||||
|
]
|
216
src/tests/responses/nextcloud-stack-list.json
Normal file
216
src/tests/responses/nextcloud-stack-list.json
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"title": "Done",
|
||||||
|
"boardId": 1,
|
||||||
|
"deletedAt": 0,
|
||||||
|
"lastModified": 1733337412,
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"id": 322,
|
||||||
|
"title": "Lunch: Soup & Toast",
|
||||||
|
"description": "",
|
||||||
|
"stackId": 3,
|
||||||
|
"type": "plain",
|
||||||
|
"lastModified": 1733337412,
|
||||||
|
"lastEditor": null,
|
||||||
|
"createdAt": 1733043472,
|
||||||
|
"labels": [],
|
||||||
|
"assignedUsers": [
|
||||||
|
{
|
||||||
|
"id": 25,
|
||||||
|
"participant": {
|
||||||
|
"primaryKey": "pcampbell",
|
||||||
|
"uid": "pcampbell",
|
||||||
|
"displayname": "Paul Campbell",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
"cardId": 322,
|
||||||
|
"type": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attachments": null,
|
||||||
|
"attachmentCount": 0,
|
||||||
|
"owner": {
|
||||||
|
"primaryKey": "pcampbell",
|
||||||
|
"uid": "pcampbell",
|
||||||
|
"displayname": "Paul Campbell",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
"order": 0,
|
||||||
|
"archived": false,
|
||||||
|
"done": null,
|
||||||
|
"duedate": null,
|
||||||
|
"deletedAt": 0,
|
||||||
|
"commentsUnread": 0,
|
||||||
|
"commentsCount": 0,
|
||||||
|
"ETag": "97592874d17017ef4f620c9c2a490086",
|
||||||
|
"overdue": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"order": 2,
|
||||||
|
"ETag": "97592874d17017ef4f620c9c2a490086"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"title": "Doing",
|
||||||
|
"boardId": 1,
|
||||||
|
"deletedAt": 0,
|
||||||
|
"lastModified": 1733337420,
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"id": 319,
|
||||||
|
"title": "That",
|
||||||
|
"description": "",
|
||||||
|
"stackId": 2,
|
||||||
|
"type": "plain",
|
||||||
|
"lastModified": 1733335979,
|
||||||
|
"lastEditor": null,
|
||||||
|
"createdAt": 1732610551,
|
||||||
|
"labels": [],
|
||||||
|
"assignedUsers": [],
|
||||||
|
"attachments": null,
|
||||||
|
"attachmentCount": 1,
|
||||||
|
"owner": {
|
||||||
|
"primaryKey": "pcampbell",
|
||||||
|
"uid": "pcampbell",
|
||||||
|
"displayname": "Paul Campbell",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
"order": 0,
|
||||||
|
"archived": false,
|
||||||
|
"done": null,
|
||||||
|
"duedate": null,
|
||||||
|
"deletedAt": 0,
|
||||||
|
"commentsUnread": 0,
|
||||||
|
"commentsCount": 0,
|
||||||
|
"ETag": "79aeb703494e67736cc66b35053d258d",
|
||||||
|
"overdue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 323,
|
||||||
|
"title": "Second lunch: Poached Egg & Toasted Muffin",
|
||||||
|
"description": "",
|
||||||
|
"stackId": 2,
|
||||||
|
"type": "plain",
|
||||||
|
"lastModified": 1733337420,
|
||||||
|
"lastEditor": null,
|
||||||
|
"createdAt": 1733043481,
|
||||||
|
"labels": [],
|
||||||
|
"assignedUsers": [
|
||||||
|
{
|
||||||
|
"id": 26,
|
||||||
|
"participant": {
|
||||||
|
"primaryKey": "pcampbell",
|
||||||
|
"uid": "pcampbell",
|
||||||
|
"displayname": "Paul Campbell",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
"cardId": 323,
|
||||||
|
"type": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attachments": null,
|
||||||
|
"attachmentCount": 0,
|
||||||
|
"owner": {
|
||||||
|
"primaryKey": "pcampbell",
|
||||||
|
"uid": "pcampbell",
|
||||||
|
"displayname": "Paul Campbell",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
"order": 1,
|
||||||
|
"archived": false,
|
||||||
|
"done": null,
|
||||||
|
"duedate": null,
|
||||||
|
"deletedAt": 0,
|
||||||
|
"commentsUnread": 0,
|
||||||
|
"commentsCount": 0,
|
||||||
|
"ETag": "3da05f904903c88450b79e4f8f6e2160",
|
||||||
|
"overdue": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"order": 1,
|
||||||
|
"ETag": "3da05f904903c88450b79e4f8f6e2160"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "To do",
|
||||||
|
"boardId": 1,
|
||||||
|
"deletedAt": 0,
|
||||||
|
"lastModified": 1733337423,
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"id": 318,
|
||||||
|
"title": "This",
|
||||||
|
"description": "",
|
||||||
|
"stackId": 1,
|
||||||
|
"type": "plain",
|
||||||
|
"lastModified": 1733049748,
|
||||||
|
"lastEditor": null,
|
||||||
|
"createdAt": 1732610548,
|
||||||
|
"labels": [],
|
||||||
|
"assignedUsers": [],
|
||||||
|
"attachments": null,
|
||||||
|
"attachmentCount": 1,
|
||||||
|
"owner": {
|
||||||
|
"primaryKey": "pcampbell",
|
||||||
|
"uid": "pcampbell",
|
||||||
|
"displayname": "Paul Campbell",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
"order": 0,
|
||||||
|
"archived": false,
|
||||||
|
"done": null,
|
||||||
|
"duedate": null,
|
||||||
|
"deletedAt": 0,
|
||||||
|
"commentsUnread": 0,
|
||||||
|
"commentsCount": 0,
|
||||||
|
"ETag": "e5007451d88799e3e3d3581cbcb30210",
|
||||||
|
"overdue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 321,
|
||||||
|
"title": "Breakfast: Cereal",
|
||||||
|
"description": "",
|
||||||
|
"stackId": 1,
|
||||||
|
"type": "plain",
|
||||||
|
"lastModified": 1733337423,
|
||||||
|
"lastEditor": null,
|
||||||
|
"createdAt": 1733043461,
|
||||||
|
"labels": [],
|
||||||
|
"assignedUsers": [
|
||||||
|
{
|
||||||
|
"id": 24,
|
||||||
|
"participant": {
|
||||||
|
"primaryKey": "pcampbell",
|
||||||
|
"uid": "pcampbell",
|
||||||
|
"displayname": "Paul Campbell",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
"cardId": 321,
|
||||||
|
"type": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attachments": null,
|
||||||
|
"attachmentCount": 0,
|
||||||
|
"owner": {
|
||||||
|
"primaryKey": "pcampbell",
|
||||||
|
"uid": "pcampbell",
|
||||||
|
"displayname": "Paul Campbell",
|
||||||
|
"type": 0
|
||||||
|
},
|
||||||
|
"order": 1,
|
||||||
|
"archived": false,
|
||||||
|
"done": null,
|
||||||
|
"duedate": null,
|
||||||
|
"deletedAt": 0,
|
||||||
|
"commentsUnread": 0,
|
||||||
|
"commentsCount": 0,
|
||||||
|
"ETag": "b567d287210fa4d9b108ac68d5b087c1",
|
||||||
|
"overdue": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"order": 0,
|
||||||
|
"ETag": "b567d287210fa4d9b108ac68d5b087c1"
|
||||||
|
}
|
||||||
|
]
|
Loading…
Reference in a new issue