feat: add command 'nextcloud board get'
This commit is contained in:
parent
3552faf7a9
commit
aac5ff3f5f
8 changed files with 419 additions and 1 deletions
|
@ -75,7 +75,7 @@ As part of building the import server, the following commands exercise each oper
|
||||||
- [x] trello attachment get - includes download url
|
- [x] trello attachment get - includes download url
|
||||||
- [x] trello attachment save - saves to disk
|
- [x] trello attachment save - saves to disk
|
||||||
- [x] nextcloud deck get - includes list of boards
|
- [x] nextcloud deck get - includes list of boards
|
||||||
- [ ] nextcloud board get (was stack list)
|
- [x] nextcloud board get - includes list of stacks
|
||||||
- [ ] nextcloud stack get (was card list)
|
- [ ] nextcloud stack get (was card list)
|
||||||
- [x] nextcloud card create
|
- [x] nextcloud card create
|
||||||
- [x] nextcloud card add-label
|
- [x] nextcloud card add-label
|
||||||
|
|
38
src/nextcloud/board.rs
Normal file
38
src/nextcloud/board.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
//
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
use crate::execute::Execute;
|
||||||
|
use crate::nextcloud::model::NextcloudBoardId;
|
||||||
|
use crate::{p, FullCtx};
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
pub enum NextcloudBoardCommand {
|
||||||
|
Get {
|
||||||
|
#[clap(long, action = clap::ArgAction::SetTrue)]
|
||||||
|
dump: bool,
|
||||||
|
board_id: i64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Execute for NextcloudBoardCommand {
|
||||||
|
async fn execute(&self, ctx: &FullCtx) -> color_eyre::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Get { dump, board_id } => {
|
||||||
|
let api_result = ctx
|
||||||
|
.deck_client()
|
||||||
|
.get_stacks(NextcloudBoardId::new(*board_id))
|
||||||
|
.await;
|
||||||
|
if *dump {
|
||||||
|
p!(ctx.prt, "{}", api_result.text);
|
||||||
|
} else {
|
||||||
|
let mut stacks = api_result.result?;
|
||||||
|
stacks.sort_by(|a, b| a.title.cmp(&b.title));
|
||||||
|
stacks.into_iter().for_each(|stack| {
|
||||||
|
p!(ctx.prt, "{}:{}", stack.id, stack.title);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,8 @@ pub(crate) struct DeckClient<'ctx> {
|
||||||
password: &'ctx NextcloudPassword,
|
password: &'ctx NextcloudPassword,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl DeckClient<'_> {}
|
||||||
|
|
||||||
// Uses the API described here: https://deck.readthedocs.io/en/stable/API/#cards
|
// Uses the API described here: https://deck.readthedocs.io/en/stable/API/#cards
|
||||||
impl<'ctx> DeckClient<'ctx> {
|
impl<'ctx> DeckClient<'ctx> {
|
||||||
pub fn new(ctx: &'ctx FullCtx) -> Self {
|
pub fn new(ctx: &'ctx FullCtx) -> Self {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//
|
//
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
|
use crate::nextcloud::board::NextcloudBoardCommand;
|
||||||
use crate::{
|
use crate::{
|
||||||
execute::Execute,
|
execute::Execute,
|
||||||
nextcloud::{
|
nextcloud::{
|
||||||
|
@ -12,6 +13,7 @@ use crate::{
|
||||||
FullCtx,
|
FullCtx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub(crate) mod board;
|
||||||
pub(crate) mod card;
|
pub(crate) mod card;
|
||||||
pub(crate) mod client;
|
pub(crate) mod client;
|
||||||
pub(crate) mod deck;
|
pub(crate) mod deck;
|
||||||
|
@ -33,6 +35,8 @@ pub enum NextcloudCommand {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Deck(NextcloudDeckCommand),
|
Deck(NextcloudDeckCommand),
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
|
Board(NextcloudBoardCommand),
|
||||||
|
#[clap(subcommand)]
|
||||||
Stack(NextcloudStackCommand),
|
Stack(NextcloudStackCommand),
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Card(NextcloudCardCommand),
|
Card(NextcloudCardCommand),
|
||||||
|
@ -41,6 +45,7 @@ impl Execute for NextcloudCommand {
|
||||||
async fn execute(&self, ctx: &FullCtx) -> color_eyre::Result<()> {
|
async fn execute(&self, ctx: &FullCtx) -> color_eyre::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
NextcloudCommand::Deck(cmd) => cmd.execute(ctx).await,
|
NextcloudCommand::Deck(cmd) => cmd.execute(ctx).await,
|
||||||
|
NextcloudCommand::Board(cmd) => cmd.execute(ctx).await,
|
||||||
NextcloudCommand::Stack(cmd) => cmd.execute(ctx).await,
|
NextcloudCommand::Stack(cmd) => cmd.execute(ctx).await,
|
||||||
NextcloudCommand::Card(cmd) => cmd.execute(ctx).await,
|
NextcloudCommand::Card(cmd) => cmd.execute(ctx).await,
|
||||||
}
|
}
|
||||||
|
|
78
src/nextcloud/tests/board/get.rs
Normal file
78
src/nextcloud/tests/board/get.rs
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
use crate::execute::Execute;
|
||||||
|
use crate::nextcloud::board::NextcloudBoardCommand;
|
||||||
|
use crate::nextcloud::card::NextcloudCardCommand;
|
||||||
|
use crate::nextcloud::stack::NextcloudStackCommand;
|
||||||
|
use crate::nextcloud::NextcloudCommand;
|
||||||
|
use crate::Command;
|
||||||
|
//
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[rstest::fixture]
|
||||||
|
fn ctx() -> FullCtx {
|
||||||
|
let fs = given::a_filesystem();
|
||||||
|
|
||||||
|
let nextcloud_config = given::a_nextcloud_config();
|
||||||
|
|
||||||
|
let hostname = &nextcloud_config.hostname;
|
||||||
|
let board_id = nextcloud_config.board_id;
|
||||||
|
|
||||||
|
let mock_net = given::a_network();
|
||||||
|
mock_net
|
||||||
|
.on()
|
||||||
|
.get(crate::f!(
|
||||||
|
"https://{hostname}/index.php/apps/deck/api/v1.0/boards/{board_id}/stacks",
|
||||||
|
))
|
||||||
|
.respond(StatusCode::OK)
|
||||||
|
.body(include_str!(
|
||||||
|
"../../../tests/responses/nextcloud-board-get.json"
|
||||||
|
))
|
||||||
|
.expect("mock request");
|
||||||
|
|
||||||
|
given::a_full_context(fs, mock_net)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest::rstest]
|
||||||
|
#[test_log::test(tokio::test)]
|
||||||
|
async fn dump(ctx: FullCtx) {
|
||||||
|
//given
|
||||||
|
let prt = ctx.prt.clone();
|
||||||
|
let prt = prt.as_test().unwrap();
|
||||||
|
let board_id = ctx.cfg.nextcloud.board_id;
|
||||||
|
|
||||||
|
//when
|
||||||
|
Command::Nextcloud(NextcloudCommand::Board(NextcloudBoardCommand::Get {
|
||||||
|
dump: true,
|
||||||
|
board_id: board_id.into(),
|
||||||
|
}))
|
||||||
|
.execute(&ctx)
|
||||||
|
.await
|
||||||
|
.expect("execute");
|
||||||
|
|
||||||
|
//then
|
||||||
|
let output = prt.output();
|
||||||
|
assert_eq!(
|
||||||
|
output.trim(),
|
||||||
|
include_str!("../../../tests/responses/nextcloud-board-get.json").trim()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest::rstest]
|
||||||
|
#[test_log::test(tokio::test)]
|
||||||
|
async fn no_dump(ctx: FullCtx) {
|
||||||
|
//given
|
||||||
|
let prt = ctx.prt.clone();
|
||||||
|
let prt = prt.as_test().unwrap();
|
||||||
|
|
||||||
|
//when
|
||||||
|
Command::Nextcloud(NextcloudCommand::Board(NextcloudBoardCommand::Get {
|
||||||
|
dump: false,
|
||||||
|
board_id: ctx.cfg.nextcloud.board_id.into(),
|
||||||
|
}))
|
||||||
|
.execute(&ctx)
|
||||||
|
.await
|
||||||
|
.expect("execute");
|
||||||
|
|
||||||
|
//then
|
||||||
|
let output = prt.output();
|
||||||
|
assert_eq!(output.trim(), ["2:Doing\n3:Done\n1:To do"].join("\n"));
|
||||||
|
}
|
4
src/nextcloud/tests/board/mod.rs
Normal file
4
src/nextcloud/tests/board/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
//
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
mod get;
|
|
@ -29,6 +29,7 @@ use crate::{
|
||||||
AppConfig, FullCtx,
|
AppConfig, FullCtx,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mod board;
|
||||||
mod card;
|
mod card;
|
||||||
mod deck;
|
mod deck;
|
||||||
mod stack;
|
mod stack;
|
||||||
|
|
290
src/tests/responses/nextcloud-board-get.json
Normal file
290
src/tests/responses/nextcloud-board-get.json
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"title": "Done",
|
||||||
|
"boardId": 1,
|
||||||
|
"deletedAt": 0,
|
||||||
|
"lastModified": 1733515991,
|
||||||
|
"cards": [
|
||||||
|
{
|
||||||
|
"id": 322,
|
||||||
|
"title": "Lunch: Soup & Toast",
|
||||||
|
"description": "",
|
||||||
|
"stackId": 3,
|
||||||
|
"type": "plain",
|
||||||
|
"lastModified": 1733515991,
|
||||||
|
"lastEditor": null,
|
||||||
|
"createdAt": 1733043472,
|
||||||
|
"labels": [
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"title": "Later",
|
||||||
|
"color": "F1DB50",
|
||||||
|
"boardId": 1,
|
||||||
|
"cardId": 322,
|
||||||
|
"lastModified": 1670965629,
|
||||||
|
"ETag": "983f87848dc9c18d0aee63e7ee0fc83f"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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": "dda386b3b247d7b4bd8917e19d38c01b",
|
||||||
|
"overdue": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"order": 2,
|
||||||
|
"ETag": "dda386b3b247d7b4bd8917e19d38c01b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"title": "Doing",
|
||||||
|
"boardId": 1,
|
||||||
|
"deletedAt": 0,
|
||||||
|
"lastModified": 1733695323,
|
||||||
|
"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": 1733695323,
|
||||||
|
"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": "9de45fc7d68507f1eaef462e90e6414c",
|
||||||
|
"overdue": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"order": 1,
|
||||||
|
"ETag": "9de45fc7d68507f1eaef462e90e6414c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "To do",
|
||||||
|
"boardId": 1,
|
||||||
|
"deletedAt": 0,
|
||||||
|
"lastModified": 1734115321,
|
||||||
|
"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": 1734115321,
|
||||||
|
"lastEditor": null,
|
||||||
|
"createdAt": 1733043461,
|
||||||
|
"labels": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "Finished",
|
||||||
|
"color": "31CC7C",
|
||||||
|
"boardId": 1,
|
||||||
|
"cardId": 321,
|
||||||
|
"lastModified": 1670965629,
|
||||||
|
"ETag": "983f87848dc9c18d0aee63e7ee0fc83f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "Finished",
|
||||||
|
"color": "31CC7C",
|
||||||
|
"boardId": 1,
|
||||||
|
"cardId": 321,
|
||||||
|
"lastModified": 1670965629,
|
||||||
|
"ETag": "983f87848dc9c18d0aee63e7ee0fc83f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"title": "Finished",
|
||||||
|
"color": "31CC7C",
|
||||||
|
"boardId": 1,
|
||||||
|
"cardId": 321,
|
||||||
|
"lastModified": 1670965629,
|
||||||
|
"ETag": "983f87848dc9c18d0aee63e7ee0fc83f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"title": "To review",
|
||||||
|
"color": "317CCC",
|
||||||
|
"boardId": 1,
|
||||||
|
"cardId": 321,
|
||||||
|
"lastModified": 1670965629,
|
||||||
|
"ETag": "983f87848dc9c18d0aee63e7ee0fc83f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"title": "To review",
|
||||||
|
"color": "317CCC",
|
||||||
|
"boardId": 1,
|
||||||
|
"cardId": 321,
|
||||||
|
"lastModified": 1670965629,
|
||||||
|
"ETag": "983f87848dc9c18d0aee63e7ee0fc83f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"title": "Action needed",
|
||||||
|
"color": "FF7A66",
|
||||||
|
"boardId": 1,
|
||||||
|
"cardId": 321,
|
||||||
|
"lastModified": 1670965629,
|
||||||
|
"ETag": "983f87848dc9c18d0aee63e7ee0fc83f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"title": "Later",
|
||||||
|
"color": "F1DB50",
|
||||||
|
"boardId": 1,
|
||||||
|
"cardId": 321,
|
||||||
|
"lastModified": 1670965629,
|
||||||
|
"ETag": "983f87848dc9c18d0aee63e7ee0fc83f"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"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": "9acdd42135c0347968891d2a073b87be",
|
||||||
|
"overdue": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"order": 0,
|
||||||
|
"ETag": "9acdd42135c0347968891d2a073b87be"
|
||||||
|
}
|
||||||
|
]
|
Loading…
Reference in a new issue