diff --git a/README.md b/README.md index b51c35c..7abe42f 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ As part of building the import server, the following commands exercise each oper - [x] trello board get - includes list of stacks - [x] trello stack get - includes list of cards - [x] trello card get - includes list of attachments -- [ ] trello attachment get +- [x] trello attachment get - includes download url - [ ] nextcloud deck get (was board list) - [ ] nextcloud board get (was stack list) - [ ] nextcloud stack get (was card list) diff --git a/src/tests/responses/trello-attachment-get.json b/src/tests/responses/trello-attachment-get.json new file mode 100644 index 0000000..8e74324 --- /dev/null +++ b/src/tests/responses/trello-attachment-get.json @@ -0,0 +1,78 @@ +{ + "id": "65ad94875aed24f70ecdd037", + "bytes": 184198, + "date": "2019-01-02T22:47:17.325Z", + "edgeColor": "#047cbc", + "idMember": "65ad94875aed24f70ecdd038", + "isUpload": true, + "mimeType": null, + "name": "Backlog.png", + "previews": [ + { + "id": "65ad94875aed24f70ecdd039", + "_id": "65ad94875aed24f70ecdd039", + "scaled": false, + "url": "https://trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037/previews/65ad94875aed24f70ecdd039/download/Backlog.png", + "bytes": 1064, + "height": 50, + "width": 70 + }, + { + "id": "65ad94875aed24f70ecdd03a", + "_id": "65ad94875aed24f70ecdd03a", + "scaled": false, + "url": "https://trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037/previews/65ad94875aed24f70ecdd03a/download/Backlog.png", + "bytes": 4859, + "height": 150, + "width": 250 + }, + { + "id": "65ad94875aed24f70ecdd03b", + "_id": "65ad94875aed24f70ecdd03b", + "scaled": true, + "url": "https://trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037/previews/65ad94875aed24f70ecdd03b/download/Backlog.png", + "bytes": 2721, + "height": 66, + "width": 150 + }, + { + "id": "65ad94875aed24f70ecdd03c", + "_id": "65ad94875aed24f70ecdd03c", + "scaled": true, + "url": "https://trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037/previews/65ad94875aed24f70ecdd03c/download/Backlog.png", + "bytes": 5874, + "height": 132, + "width": 300 + }, + { + "id": "65ad94875aed24f70ecdd03d", + "_id": "65ad94875aed24f70ecdd03d", + "scaled": true, + "url": "https://trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037/previews/65ad94875aed24f70ecdd03d/download/Backlog.png", + "bytes": 12946, + "height": 264, + "width": 600 + }, + { + "id": "65ad94875aed24f70ecdd03e", + "_id": "65ad94875aed24f70ecdd03e", + "scaled": true, + "url": "https://trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037/previews/65ad94875aed24f70ecdd03e/download/Backlog.png", + "bytes": 30062, + "height": 527, + "width": 1200 + }, + { + "id": "65ad94875aed24f70ecdd03f", + "_id": "65ad94875aed24f70ecdd03f", + "scaled": true, + "url": "https://trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037/previews/65ad94875aed24f70ecdd03f/download/Backlog.png", + "bytes": 184198, + "height": 3558, + "width": 8100 + } + ], + "url": "https://trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037/download/Backlog.png", + "pos": 49152, + "fileName": "Backlog.png" +} diff --git a/src/trello/attachment.rs b/src/trello/attachment.rs new file mode 100644 index 0000000..cbd661b --- /dev/null +++ b/src/trello/attachment.rs @@ -0,0 +1,45 @@ +// +use clap::Parser; +use color_eyre::Result; + +use crate::{execute::Execute, p, FullCtx}; + +use super::model::{TrelloAttachmentId, TrelloCardId}; + +#[derive(Parser, Debug)] +pub(crate) enum TrelloAttachmentCommand { + Get { + #[clap(long, action = clap::ArgAction::SetTrue)] + dump: bool, + + card_id: String, + attachment_id: String, + }, +} + +impl Execute for TrelloAttachmentCommand { + async fn execute(self, ctx: FullCtx) -> Result<()> { + match self { + Self::Get { + dump, + card_id, + attachment_id, + } => { + let api_result = ctx + .trello_client() + .card_attachment( + &TrelloCardId::new(card_id), + &TrelloAttachmentId::new(attachment_id), + ) + .await; + if dump { + p!(ctx.prt, "{}", api_result.text); + } else { + let attachment = api_result.result?; + p!(ctx.prt, "{}:{}", attachment.name, attachment.url); + } + Ok(()) + } + } + } +} diff --git a/src/trello/client.rs b/src/trello/client.rs index e200a00..d6f5573 100644 --- a/src/trello/client.rs +++ b/src/trello/client.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use kxio::net::{Net, ReqBuilder}; +use crate::trello::model::{TrelloAttachment, TrelloAttachmentId}; use crate::{ api_result::APIResult, f, s, @@ -84,18 +85,18 @@ impl<'ctx> TrelloClient<'ctx> { .await } - // // https://developer.atlassian.com/cloud/trello/rest/api-group-cards/#api-cards-id-attachments-idattachment-get - // pub(crate) async fn card_attachment( - // &self, - // card_id: &TrelloCardId, - // attachment_id: &TrelloAttachmentId, - // ) -> APIResult { - // self.request( - // f!("/cards/{card_id}/attachments/{attachment_id}"), - // |net, url| net.get(url), - // ) - // .await - // } + // https://developer.atlassian.com/cloud/trello/rest/api-group-cards/#api-cards-id-attachments-idattachment-get + pub(crate) async fn card_attachment( + &self, + card_id: &TrelloCardId, + attachment_id: &TrelloAttachmentId, + ) -> APIResult { + self.request( + f!("/cards/{card_id}/attachments/{attachment_id}"), + |net, url| net.get(url), + ) + .await + } } impl TrelloClient<'_> { diff --git a/src/trello/mod.rs b/src/trello/mod.rs index 677638e..0fe89ba 100644 --- a/src/trello/mod.rs +++ b/src/trello/mod.rs @@ -4,6 +4,7 @@ use clap::Parser; use crate::{ execute::Execute, trello::{ + attachment::TrelloAttachmentCommand, board::TrelloBoardCommand, card::TrelloCardCommand, member::TrelloMemberCommand, @@ -16,6 +17,7 @@ use crate::{ FullCtx, }; +pub(crate) mod attachment; pub(crate) mod board; pub(crate) mod card; pub(crate) mod client; @@ -39,6 +41,9 @@ pub(crate) enum TrelloCommand { #[clap(subcommand)] Card(TrelloCardCommand), + + #[clap(subcommand)] + Attachment(TrelloAttachmentCommand), } impl Execute for TrelloCommand { @@ -48,6 +53,7 @@ impl Execute for TrelloCommand { Self::Board(cmd) => cmd.execute(ctx).await, Self::Stack(cmd) => cmd.execute(ctx).await, Self::Card(cmd) => cmd.execute(ctx).await, + Self::Attachment(cmd) => cmd.execute(ctx).await, } } } diff --git a/src/trello/tests.rs b/src/trello/tests.rs index 7c70e02..bbbe2a2 100644 --- a/src/trello/tests.rs +++ b/src/trello/tests.rs @@ -1,12 +1,12 @@ // -use kxio::net::MockNet; -use kxio::net::StatusCode; +use kxio::net::{MockNet, StatusCode}; use crate::{ execute::Execute, s, tests::given, trello::{ + attachment::TrelloAttachmentCommand, card::TrelloCardCommand, model::{ board::{TrelloBoard, TrelloBoards as _}, @@ -178,4 +178,79 @@ mod commands { .expect("mock request"); } } + + mod attachments { + use super::*; + + mod get { + use super::*; + + #[tokio::test] + async fn get() { + //given + let mock_net = kxio::net::mock(); + + prep_mock_get(&mock_net); + + // let fs = given::a_filesystem(); + let ctx = given::a_full_context(mock_net); + let prt = ctx.prt.clone(); + let prt = prt.as_test().unwrap(); + let command = + Command::Trello(TrelloCommand::Attachment(TrelloAttachmentCommand::Get { + dump: false, + card_id: s!("65ad94865aed24f70ecdcebb"), + attachment_id: s!("65ad94875aed24f70ecdd037"), + })); + + //when + command.execute(ctx).await; + + //then + let output = prt.output(); + assert_eq!(output, s!("Backlog.png:https://trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037/download/Backlog.png\n")); + } + + #[tokio::test] + async fn get_dump() { + //given + let mock_net = kxio::net::mock(); + + prep_mock_get(&mock_net); + + // let fs = given::a_filesystem(); + let ctx = given::a_full_context(mock_net); + let prt = ctx.prt.clone(); + let prt = prt.as_test().unwrap(); + let command = + Command::Trello(TrelloCommand::Attachment(TrelloAttachmentCommand::Get { + dump: true, + card_id: s!("65ad94865aed24f70ecdcebb"), + attachment_id: s!("65ad94875aed24f70ecdd037"), + })); + + //when + command.execute(ctx).await; + + //then + let output = prt.output(); + assert_eq!( + output.trim(), + include_str!("../tests/responses/trello-attachment-get.json").trim() + ); + } + + fn prep_mock_get(mock_net: &MockNet) { + mock_net + .on() + .get("https://api.trello.com/1/cards/65ad94865aed24f70ecdcebb/attachments/65ad94875aed24f70ecdd037") + .header("authorization", "OAuth oauth_consumer_key=\"trello-api-key\", oauth_token=\"trello-api-secret\"") + .header("accept", "application/json") + .header("content-type", "application/json") + .respond(StatusCode::OK) + .body(include_str!("../tests/responses/trello-attachment-get.json")) + .expect("mock request"); + } + } + } }