feat: add command 'nextcloud board get'
Some checks failed
Test / build (map[name:nightly]) (push) Successful in 7m33s
Test / build (map[name:stable]) (push) Successful in 2m4s
Release Please / Release-plz (push) Failing after 17s

This commit is contained in:
Paul Campbell 2024-12-16 22:17:16 +00:00
parent 3552faf7a9
commit aac5ff3f5f
8 changed files with 419 additions and 1 deletions

View file

@ -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
View 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(())
}
}
}
}

View file

@ -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 {

View file

@ -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,
} }

View 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"));
}

View file

@ -0,0 +1,4 @@
//
use super::*;
mod get;

View file

@ -29,6 +29,7 @@ use crate::{
AppConfig, FullCtx, AppConfig, FullCtx,
}; };
mod board;
mod card; mod card;
mod deck; mod deck;
mod stack; mod stack;

View 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"
}
]