diff --git a/src/lib.rs b/src/lib.rs index d01af4b..39c6f61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,6 +82,15 @@ enum NextcloudCardCommand { 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, + }, } #[derive(Parser, Debug)] @@ -188,6 +197,23 @@ pub async fn run(ctx: Ctx) -> color_eyre::Result<()> { 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 + } }, }, } diff --git a/src/nextcloud/card.rs b/src/nextcloud/card.rs index 1d89fce..67e0365 100644 --- a/src/nextcloud/card.rs +++ b/src/nextcloud/card.rs @@ -43,3 +43,21 @@ pub(crate) async fn get( } Ok(()) } + +pub(crate) struct Create { + pub dump: bool, + pub stack_id: i64, + pub title: String, + pub description: Option, +} +pub(crate) async fn create(ctx: FullCtx, create: Create) -> color_eyre::Result<()> { + let dc = ctx.deck_client(); + let apiresult = dc.create_card(&create).await; + if create.dump { + p!(ctx.prt, "{}", apiresult.text); + } else { + let card = apiresult.result?; + p!(ctx.prt, "{}:{}", card.id, card.title); + } + Ok(()) +} diff --git a/src/nextcloud/mod.rs b/src/nextcloud/mod.rs index 4e83dda..24b62a5 100644 --- a/src/nextcloud/mod.rs +++ b/src/nextcloud/mod.rs @@ -2,14 +2,12 @@ use bytes::Bytes; use kxio::net::{Net, ReqBuilder}; -use crate::{ - api_result::APIResult, - f, - nextcloud::model::{ - Board, Card, NextcloudBoardId, NextcloudHostname, NextcloudPassword, NextcloudStackId, - NextcloudUsername, Stack, - }, - FullCtx, +use crate::{api_result::APIResult, f, FullCtx}; + +use card::Create; +use model::{ + Board, Card, NextcloudBoardId, NextcloudHostname, NextcloudPassword, NextcloudStackId, + NextcloudUsername, Stack, }; pub mod board; @@ -27,6 +25,7 @@ pub struct DeckClient<'ctx> { 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 { @@ -119,23 +118,20 @@ impl<'ctx> DeckClient<'ctx> { .await } - pub async fn create_card( - &self, - board_id: i64, - stack_id: i64, - title: &str, - description: Option<&str>, - ) -> APIResult { + pub(crate) async fn create_card(&self, create: &Create) -> APIResult { let mut body = serde_json::json!({ - "title": title, + "title": create.title, }); - if let Some(desc) = description { + if let Some(desc) = &create.description { body["description"] = serde_json::Value::String(desc.to_string()); } self.request_with_body( - format!("boards/{}/stacks/{}/cards", board_id, stack_id), + format!( + "boards/{}/stacks/{}/cards", + self.ctx.cfg.nextcloud.board_id, create.stack_id + ), body.to_string(), |net, url| net.post(url), ) diff --git a/src/nextcloud/tests.rs b/src/nextcloud/tests.rs index 0abfd63..711f453 100644 --- a/src/nextcloud/tests.rs +++ b/src/nextcloud/tests.rs @@ -292,8 +292,13 @@ mod commands { } mod card { + use serde_json::json; + use super::*; - use crate::nextcloud::model::{Label, NextcloudLabelId}; + use crate::nextcloud::{ + card::Create, + model::{Label, NextcloudLabelId}, + }; #[tokio::test] async fn list() { @@ -383,6 +388,61 @@ mod commands { } ); } + + #[tokio::test] + async fn create() { + //given + let mock_net = kxio::net::mock(); + + mock_net + .on() + .post("https://host-name/index.php/apps/deck/api/v1.0/boards/2/stacks/1/cards") + .basic_auth("username", Some("password")) + .header("accept", "application/json") + .header("content-type", "application/json") + .body( + json!({ + "title":"my new card", + "description":"my new description" + }) + .to_string(), + ) + .respond(StatusCode::OK) + .body(include_str!( + "../tests/responses/nextcloud-card-create.json" + )) + .expect("mock request"); + + let fs = given::a_filesystem(); + let ctx = given::a_full_context(mock_net, fs); + let deck_client = DeckClient::new(&ctx); + + //when + let result = deck_client + .create_card(&Create { + dump: false, + stack_id: 1, + title: s!("my new card"), + description: Some(s!("my new description")), + }) + .await + .result + .expect("get stacks"); + + assert_eq!( + result, + Card { + id: result.id, + title: NextcloudCardTitle::new("my new card"), + description: Some(s!("my new description")), + stack_id: NextcloudStackId::new(1), + order: NextcloudOrder::new(999), + archived: false, + due_date: None, + labels: None + } + ); + } } } diff --git a/src/tests/responses/nextcloud-card-create.json b/src/tests/responses/nextcloud-card-create.json new file mode 100644 index 0000000..26fa62f --- /dev/null +++ b/src/tests/responses/nextcloud-card-create.json @@ -0,0 +1,27 @@ +{ + "id": 331, + "title": "my new card", + "description": "my new description", + "descriptionPrev": null, + "stackId": 1, + "type": "plain", + "lastModified": 1733669509, + "lastEditor": null, + "createdAt": 1733669509, + "labels": null, + "assignedUsers": null, + "attachments": null, + "attachmentCount": null, + "owner": "pcampbell", + "order": 999, + "archived": false, + "done": null, + "duedate": null, + "notified": false, + "deletedAt": 0, + "commentsUnread": 0, + "commentsCount": 0, + "relatedStack": null, + "relatedBoard": null, + "ETag": "a3aee71091453c0e2055e4f2c31b611f" +}