feat: migrate card position and due date
closes kemitix/trello-to-deck#10
This commit is contained in:
parent
738d7332d2
commit
61f4a36721
11 changed files with 61 additions and 13 deletions
|
@ -1,5 +1,6 @@
|
||||||
//
|
//
|
||||||
use crate::nextcloud::model::NextcloudStackTitle;
|
use crate::nextcloud::model::{NextcloudDueDate, NextcloudStackTitle};
|
||||||
|
use crate::trello::model::card::TrelloCardDue;
|
||||||
use crate::{
|
use crate::{
|
||||||
nextcloud::model::{NextcloudCardDescription, NextcloudCardTitle, NextcloudOrder},
|
nextcloud::model::{NextcloudCardDescription, NextcloudCardTitle, NextcloudOrder},
|
||||||
trello::model::{
|
trello::model::{
|
||||||
|
@ -37,3 +38,9 @@ impl From<&TrelloCardPosition> for NextcloudOrder {
|
||||||
Self::new(value.0)
|
Self::new(value.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&TrelloCardDue> for NextcloudDueDate {
|
||||||
|
fn from(value: &TrelloCardDue) -> Self {
|
||||||
|
Self::new(value.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use kameo::{
|
||||||
Actor,
|
Actor,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::nextcloud::model::{NextcloudDueDate, NextcloudOrder};
|
||||||
use crate::{
|
use crate::{
|
||||||
ask,
|
ask,
|
||||||
import::{
|
import::{
|
||||||
|
@ -69,10 +70,12 @@ impl Actor for ImportCardActor {
|
||||||
tracing::info!(name = %this.trello_card.name, "importing card");
|
tracing::info!(name = %this.trello_card.name, "importing card");
|
||||||
// - - create a nextcloud card
|
// - - create a nextcloud card
|
||||||
let title = NextcloudCardTitle::from(&this.trello_card.name);
|
let title = NextcloudCardTitle::from(&this.trello_card.name);
|
||||||
|
let order = NextcloudOrder::from(&this.trello_card.pos);
|
||||||
let desc: Option<NextcloudCardDescription> = match this.trello_card.desc.len() {
|
let desc: Option<NextcloudCardDescription> = match this.trello_card.desc.len() {
|
||||||
0 => None,
|
0 => None,
|
||||||
_ => Some(NextcloudCardDescription::from(&this.trello_card.desc)),
|
_ => Some(NextcloudCardDescription::from(&this.trello_card.desc)),
|
||||||
};
|
};
|
||||||
|
let due_date = this.trello_card.due.as_ref().map(NextcloudDueDate::from);
|
||||||
|
|
||||||
tracing::info!("Nextcloud: Create card");
|
tracing::info!("Nextcloud: Create card");
|
||||||
let nextcloud_card_id = match this
|
let nextcloud_card_id = match this
|
||||||
|
@ -82,7 +85,9 @@ impl Actor for ImportCardActor {
|
||||||
this.nextcloud_board_id,
|
this.nextcloud_board_id,
|
||||||
this.nextcloud_stack_id,
|
this.nextcloud_stack_id,
|
||||||
&title,
|
&title,
|
||||||
|
&order,
|
||||||
desc.as_ref(),
|
desc.as_ref(),
|
||||||
|
due_date.as_ref(),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.result
|
.result
|
||||||
|
|
|
@ -61,6 +61,7 @@ fn trello_card_without_description_or_label() -> TrelloShortCard {
|
||||||
desc: s!("").into(),
|
desc: s!("").into(),
|
||||||
labels: vec![],
|
labels: vec![],
|
||||||
pos: 333.into(),
|
pos: 333.into(),
|
||||||
|
due: Some(s!("2025-01-07T00:00:00.000Z").into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,8 +317,11 @@ fn given_nextcloud_create_card_with_description(mock_net: &MockNet) {
|
||||||
.body(
|
.body(
|
||||||
json!({
|
json!({
|
||||||
"description": "A list of the things we think we want to do, maybe not quite ready for work, but high likelihood of being worked on.\n\nThis is the staging area where specs should get fleshed out.\n\nNo limit on the list size, but we should reconsider if it gets long.",
|
"description": "A list of the things we think we want to do, maybe not quite ready for work, but high likelihood of being worked on.\n\nThis is the staging area where specs should get fleshed out.\n\nNo limit on the list size, but we should reconsider if it gets long.",
|
||||||
|
"duedate": "2025-01-07T00:00:00.000Z",
|
||||||
|
"order": 16384,
|
||||||
"title": "Backlog",
|
"title": "Backlog",
|
||||||
}) // TODO: (#10) include card 'order' from trello 'pos'
|
"type":"plain"
|
||||||
|
})
|
||||||
.to_string(),
|
.to_string(),
|
||||||
)
|
)
|
||||||
.respond(StatusCode::OK)
|
.respond(StatusCode::OK)
|
||||||
|
@ -333,7 +337,9 @@ fn given_nextcloud_create_card_without_description(mock_net: &MockNet) {
|
||||||
.body(
|
.body(
|
||||||
json!({
|
json!({
|
||||||
// "description": "trello-desc",
|
// "description": "trello-desc",
|
||||||
|
"order": 16384,
|
||||||
"title": "Backlog",
|
"title": "Backlog",
|
||||||
|
"type":"plain"
|
||||||
}) // TODO: (#10) include card 'order' from trello 'pos'
|
}) // TODO: (#10) include card 'order' from trello 'pos'
|
||||||
.to_string(),
|
.to_string(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,8 +3,13 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use crate::nextcloud::model::{NextcloudCardDescription, NextcloudCardTitle};
|
use crate::{
|
||||||
use crate::{execute::Execute, p, FullCtx};
|
execute::Execute,
|
||||||
|
nextcloud::model::{
|
||||||
|
NextcloudCardDescription, NextcloudCardTitle, NextcloudDueDate, NextcloudOrder,
|
||||||
|
},
|
||||||
|
p, FullCtx,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
pub enum NextcloudCardCommand {
|
pub enum NextcloudCardCommand {
|
||||||
|
@ -23,7 +28,11 @@ pub enum NextcloudCardCommand {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
title: String,
|
title: String,
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
|
order: i64,
|
||||||
|
#[clap(long)]
|
||||||
description: Option<String>,
|
description: Option<String>,
|
||||||
|
#[clap(long)]
|
||||||
|
due: Option<String>,
|
||||||
},
|
},
|
||||||
AddLabel {
|
AddLabel {
|
||||||
#[clap(long, action = clap::ArgAction::SetTrue)]
|
#[clap(long, action = clap::ArgAction::SetTrue)]
|
||||||
|
@ -76,7 +85,9 @@ impl Execute for NextcloudCardCommand {
|
||||||
board_id,
|
board_id,
|
||||||
stack_id,
|
stack_id,
|
||||||
title,
|
title,
|
||||||
|
order,
|
||||||
description,
|
description,
|
||||||
|
due,
|
||||||
} => {
|
} => {
|
||||||
let api_result = ctx
|
let api_result = ctx
|
||||||
.deck_client()
|
.deck_client()
|
||||||
|
@ -84,10 +95,12 @@ impl Execute for NextcloudCardCommand {
|
||||||
(*board_id).into(),
|
(*board_id).into(),
|
||||||
(*stack_id).into(),
|
(*stack_id).into(),
|
||||||
&NextcloudCardTitle::new(title),
|
&NextcloudCardTitle::new(title),
|
||||||
|
&NextcloudOrder::new(*order),
|
||||||
description
|
description
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(NextcloudCardDescription::new)
|
.map(NextcloudCardDescription::new)
|
||||||
.as_ref(),
|
.as_ref(),
|
||||||
|
due.as_ref().map(NextcloudDueDate::new).as_ref(),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
if *dump {
|
if *dump {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use reqwest::multipart;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use tracing::instrument;
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::nextcloud::model::NextcloudOrder;
|
use crate::nextcloud::model::{NextcloudDueDate, NextcloudOrder};
|
||||||
use crate::{
|
use crate::{
|
||||||
api_result::APIResult,
|
api_result::APIResult,
|
||||||
f,
|
f,
|
||||||
|
@ -149,16 +149,25 @@ impl<'ctx> DeckClient<'ctx> {
|
||||||
board_id: NextcloudBoardId,
|
board_id: NextcloudBoardId,
|
||||||
stack_id: NextcloudStackId,
|
stack_id: NextcloudStackId,
|
||||||
title: &NextcloudCardTitle,
|
title: &NextcloudCardTitle,
|
||||||
|
order: &NextcloudOrder,
|
||||||
description: Option<&NextcloudCardDescription>,
|
description: Option<&NextcloudCardDescription>,
|
||||||
|
due_date: Option<&NextcloudDueDate>,
|
||||||
) -> APIResult<Card> {
|
) -> APIResult<Card> {
|
||||||
let mut body = json!({
|
let mut body = json!({
|
||||||
"title": title,
|
"title": title,
|
||||||
|
"type": "plain",
|
||||||
|
"order": order,
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(desc) = &description {
|
if let Some(desc) = &description {
|
||||||
body["description"] = serde_json::Value::String(desc.to_string());
|
body["description"] = Value::String(desc.to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(due_date) = &due_date {
|
||||||
|
body["duedate"] = Value::String(due_date.to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
tracing::info!(body = %body.to_string());
|
||||||
self.request_with_body(
|
self.request_with_body(
|
||||||
f!("boards/{board_id}/stacks/{stack_id}/cards"),
|
f!("boards/{board_id}/stacks/{stack_id}/cards"),
|
||||||
body,
|
body,
|
||||||
|
|
|
@ -139,6 +139,7 @@ newtype!(
|
||||||
Ord,
|
Ord,
|
||||||
"Description of the Card"
|
"Description of the Card"
|
||||||
);
|
);
|
||||||
|
newtype!(NextcloudDueDate, String, "Due date of the Card");
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub(crate) struct Board {
|
pub(crate) struct Board {
|
||||||
|
|
|
@ -43,14 +43,15 @@ fn ctx() -> FullCtx {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest::rstest]
|
#[rstest::rstest]
|
||||||
#[case::no_desc(None)]
|
#[case::no_desc_or_due(None, None)]
|
||||||
#[case::desc(Some(s!("my new description")))]
|
#[case::desc_and_due(Some(s!("my new description")), Some(s!("2025-01-07T00:00:00.000Z")))]
|
||||||
#[test_log::test(tokio::test)]
|
#[test_log::test(tokio::test)]
|
||||||
async fn dump(
|
async fn dump(
|
||||||
ctx: FullCtx,
|
ctx: FullCtx,
|
||||||
board_id: NextcloudBoardId,
|
board_id: NextcloudBoardId,
|
||||||
stack_id: NextcloudStackId,
|
stack_id: NextcloudStackId,
|
||||||
#[case] description: Option<String>,
|
#[case] description: Option<String>,
|
||||||
|
#[case] due: Option<String>,
|
||||||
) {
|
) {
|
||||||
//given
|
//given
|
||||||
let prt = ctx.prt.clone();
|
let prt = ctx.prt.clone();
|
||||||
|
@ -62,7 +63,9 @@ async fn dump(
|
||||||
board_id: board_id.into(),
|
board_id: board_id.into(),
|
||||||
stack_id: stack_id.into(),
|
stack_id: stack_id.into(),
|
||||||
title: "my new card".to_string(),
|
title: "my new card".to_string(),
|
||||||
|
order: 42,
|
||||||
description,
|
description,
|
||||||
|
due,
|
||||||
}))
|
}))
|
||||||
.execute(&ctx)
|
.execute(&ctx)
|
||||||
.await
|
.await
|
||||||
|
@ -79,14 +82,15 @@ async fn dump(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest::rstest]
|
#[rstest::rstest]
|
||||||
#[case::no_desc(None)]
|
#[case::no_desc_or_due(None, None)]
|
||||||
#[case::desc(Some(s!("my new description")))]
|
#[case::desc_and_due(Some(s!("my new description")), Some(s!("2025-01-07T00:00:00.000Z")))]
|
||||||
#[test_log::test(tokio::test)]
|
#[test_log::test(tokio::test)]
|
||||||
async fn no_dump(
|
async fn no_dump(
|
||||||
ctx: FullCtx,
|
ctx: FullCtx,
|
||||||
board_id: NextcloudBoardId,
|
board_id: NextcloudBoardId,
|
||||||
stack_id: NextcloudStackId,
|
stack_id: NextcloudStackId,
|
||||||
#[case] description: Option<String>,
|
#[case] description: Option<String>,
|
||||||
|
#[case] due: Option<String>,
|
||||||
) {
|
) {
|
||||||
//given
|
//given
|
||||||
let prt = ctx.prt.clone();
|
let prt = ctx.prt.clone();
|
||||||
|
@ -98,7 +102,9 @@ async fn no_dump(
|
||||||
board_id: board_id.into(),
|
board_id: board_id.into(),
|
||||||
stack_id: stack_id.into(),
|
stack_id: stack_id.into(),
|
||||||
title: "my new card".to_string(),
|
title: "my new card".to_string(),
|
||||||
|
order: 42,
|
||||||
description,
|
description,
|
||||||
|
due,
|
||||||
}))
|
}))
|
||||||
.execute(&ctx)
|
.execute(&ctx)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
"order": 999,
|
"order": 999,
|
||||||
"archived": false,
|
"archived": false,
|
||||||
"done": null,
|
"done": null,
|
||||||
"duedate": null,
|
"duedate": "2025-01-07T00:00:00.000Z",
|
||||||
"notified": false,
|
"notified": false,
|
||||||
"deletedAt": 0,
|
"deletedAt": 0,
|
||||||
"commentsUnread": 0,
|
"commentsUnread": 0,
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
"dateLastActivity": "2024-12-25T08:40:39.500Z",
|
"dateLastActivity": "2024-12-25T08:40:39.500Z",
|
||||||
"desc": "A list of the things we think we want to do, maybe not quite ready for work, but high likelihood of being worked on.\n\nThis is the staging area where specs should get fleshed out.\n\nNo limit on the list size, but we should reconsider if it gets long.",
|
"desc": "A list of the things we think we want to do, maybe not quite ready for work, but high likelihood of being worked on.\n\nThis is the staging area where specs should get fleshed out.\n\nNo limit on the list size, but we should reconsider if it gets long.",
|
||||||
"descData": null,
|
"descData": null,
|
||||||
"due": null,
|
"due": "2025-01-07T00:00:00.000Z",
|
||||||
"dueReminder": null,
|
"dueReminder": null,
|
||||||
"email": null,
|
"email": null,
|
||||||
"idBoard": "65ad94865aed24f70ecdce4b",
|
"idBoard": "65ad94865aed24f70ecdce4b",
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
"dateLastActivity": "2024-01-21T22:02:47.582Z",
|
"dateLastActivity": "2024-01-21T22:02:47.582Z",
|
||||||
"desc": "A list of the things we think we want to do, maybe not quite ready for work, but high likelihood of being worked on.\n\nThis is the staging area where specs should get fleshed out.\n\nNo limit on the list size, but we should reconsider if it gets long.",
|
"desc": "A list of the things we think we want to do, maybe not quite ready for work, but high likelihood of being worked on.\n\nThis is the staging area where specs should get fleshed out.\n\nNo limit on the list size, but we should reconsider if it gets long.",
|
||||||
"descData": null,
|
"descData": null,
|
||||||
"due": null,
|
"due": "2025-01-07T00:00:00.000Z",
|
||||||
"dueReminder": null,
|
"dueReminder": null,
|
||||||
"email": null,
|
"email": null,
|
||||||
"idBoard": "65ad94865aed24f70ecdce4b",
|
"idBoard": "65ad94865aed24f70ecdce4b",
|
||||||
|
|
|
@ -39,6 +39,7 @@ pub(crate) struct TrelloShortCard {
|
||||||
pub(crate) desc: TrelloCardDescription,
|
pub(crate) desc: TrelloCardDescription,
|
||||||
pub(crate) labels: Vec<TrelloLabel>,
|
pub(crate) labels: Vec<TrelloLabel>,
|
||||||
pub(crate) pos: TrelloCardPosition,
|
pub(crate) pos: TrelloCardPosition,
|
||||||
|
pub(crate) due: Option<TrelloCardDue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
#[derive(Debug, PartialEq, Eq, Deserialize)]
|
||||||
|
|
Loading…
Reference in a new issue