refactor(trello): rewrite trello module and rename commands
This commit is contained in:
parent
f71f8c5467
commit
90912509f4
26 changed files with 455 additions and 625 deletions
|
@ -60,10 +60,9 @@ trello-to-deck check
|
||||||
|
|
||||||
As part of building the import server, I'm including the following commands the exercise each operation invovled.
|
As part of building the import server, I'm including the following commands the exercise each operation invovled.
|
||||||
|
|
||||||
- [x] trello member get
|
- [x] trello member get - includes list of boards
|
||||||
- [ ] trello board get (was list/stack list)
|
- [x] trello board get - includes list of stacks
|
||||||
- [ ] trello stack get (was card list)
|
- [x] trello stack get - includes list of cards
|
||||||
- [ ] trello card get
|
|
||||||
- [ ] trello attachment get
|
- [ ] trello attachment get
|
||||||
- [ ] nextcloud deck get (was board list)
|
- [ ] nextcloud deck get (was board list)
|
||||||
- [ ] nextcloud board get (was stack list)
|
- [ ] nextcloud board get (was stack list)
|
||||||
|
|
10
src/check.rs
10
src/check.rs
|
@ -1,18 +1,12 @@
|
||||||
//
|
//
|
||||||
use color_eyre::eyre::{OptionExt as _, Result};
|
use color_eyre::eyre::{OptionExt as _, Result};
|
||||||
|
|
||||||
use crate::{f, p, trello::api::boards::TrelloBoards as _, FullCtx};
|
use crate::{f, p, trello::model::board::TrelloBoards as _, FullCtx};
|
||||||
|
|
||||||
pub(crate) async fn run(ctx: FullCtx) -> Result<()> {
|
pub(crate) async fn run(ctx: FullCtx) -> Result<()> {
|
||||||
// test trello by getting a list of the boards for the user
|
// test trello by getting a list of the boards for the user
|
||||||
p!(ctx.prt, ">> Testing Trello details...");
|
p!(ctx.prt, ">> Testing Trello details...");
|
||||||
let boards = crate::trello::api::members::get_boards_that_member_belongs_to(
|
let boards = ctx.trello_client().boards().await.result?;
|
||||||
&ctx.cfg.trello,
|
|
||||||
&ctx.net,
|
|
||||||
&ctx.prt,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.result?;
|
|
||||||
p!(ctx.prt, "<<< Trello Credentials: OKAY");
|
p!(ctx.prt, "<<< Trello Credentials: OKAY");
|
||||||
let board_name = &ctx.cfg.trello.board_name;
|
let board_name = &ctx.cfg.trello.board_name;
|
||||||
p!(ctx.prt, ">> Trello Board: {board_name}");
|
p!(ctx.prt, ">> Trello Board: {board_name}");
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
[trello]
|
[trello]
|
||||||
api_key = ""
|
api_key = ""
|
||||||
api_secret = ""
|
api_secret = ""
|
||||||
user = ""
|
|
||||||
board_name = ""
|
board_name = ""
|
||||||
|
|
||||||
[nextcloud]
|
[nextcloud]
|
||||||
|
|
193
src/tests/responses/trello-board-get.json
Normal file
193
src/tests/responses/trello-board-get.json
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
{
|
||||||
|
"id": "65ad94865aed24f70ecdce4b",
|
||||||
|
"name": "Tasyn Kanban",
|
||||||
|
"desc": "Use this simple Kanban template to keep the engineering team on the same page and moving through work fluidly. \n\n1. Break down the roadmap by adding tasks as cards to the **Backlog** list. \n\n2. Move the cards one-by-one through **Design** as they becomes more fleshed out. *Pro tip:* You can enable Power-ups for your favorite design tools like [Figma](https://trello.com/power-ups/59b2e7611e6ece0b35eac16a/figma) or [Invision](https://trello.com/power-ups/596f2cb2d279152540b2bb31), in order to easily link and view designs without switching context.\n\n3. When a card is fully specced out and designs are attached, move it to **To Do** for engineers to pick up. \n\n4. Engineers move cards to **Doing** and assign themselves to the cards, so the whole team stays informed of who is working on what.\n\n5. Cards then move through **Code Review** when they're ready for a second set of eyes. The team can set a **List Limit** (with the List Limit Power-up) on the number of cards in Code Review, as a visual indicator for when the team needs to prioritize reviews rather than picking up new work. \n\n6. Once cards move through **Testing** and eventually ship to production, move them to **Done** and celebrate!\n",
|
||||||
|
"descData": null,
|
||||||
|
"closed": false,
|
||||||
|
"idOrganization": "60ae034415aa230ab2ef596d",
|
||||||
|
"idEnterprise": null,
|
||||||
|
"pinned": false,
|
||||||
|
"url": "https://trello.com/b/pKSkfnfK/tasyn-kanban",
|
||||||
|
"shortUrl": "https://trello.com/b/pKSkfnfK",
|
||||||
|
"prefs": {
|
||||||
|
"permissionLevel": "org",
|
||||||
|
"hideVotes": false,
|
||||||
|
"voting": "disabled",
|
||||||
|
"comments": "members",
|
||||||
|
"invitations": "members",
|
||||||
|
"selfJoin": false,
|
||||||
|
"cardCovers": true,
|
||||||
|
"cardCounts": false,
|
||||||
|
"isTemplate": false,
|
||||||
|
"cardAging": "regular",
|
||||||
|
"calendarFeedEnabled": false,
|
||||||
|
"hiddenPluginBoardButtons": [],
|
||||||
|
"switcherViews": [
|
||||||
|
{
|
||||||
|
"viewType": "Board",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"viewType": "Table",
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"viewType": "Calendar",
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"viewType": "Dashboard",
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"viewType": "Timeline",
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"viewType": "Map",
|
||||||
|
"enabled": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background": "5dfa855f31b76a80318febaf",
|
||||||
|
"backgroundColor": null,
|
||||||
|
"backgroundImage": "https://trello-backgrounds.s3.amazonaws.com/SharedBackground/original/d71b9370b4cba91634ae4ffe331fb59a/photo-1576502200916-3808e07386a5",
|
||||||
|
"backgroundTile": false,
|
||||||
|
"backgroundBrightness": "light",
|
||||||
|
"sharedSourceUrl": "https://images.unsplash.com/photo-1576502200916-3808e07386a5?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjcwNjZ9&w=2560&h=2048&q=90",
|
||||||
|
"backgroundImageScaled": [
|
||||||
|
{
|
||||||
|
"width": 140,
|
||||||
|
"height": 94,
|
||||||
|
"url": "https://trello-backgrounds.s3.amazonaws.com/SharedBackground/140x94/47f09f0e3910259568294477d0bdedac/photo-1576502200916-3808e07386a5.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 256,
|
||||||
|
"height": 172,
|
||||||
|
"url": "https://trello-backgrounds.s3.amazonaws.com/SharedBackground/256x172/47f09f0e3910259568294477d0bdedac/photo-1576502200916-3808e07386a5.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 480,
|
||||||
|
"height": 322,
|
||||||
|
"url": "https://trello-backgrounds.s3.amazonaws.com/SharedBackground/480x322/47f09f0e3910259568294477d0bdedac/photo-1576502200916-3808e07386a5.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 960,
|
||||||
|
"height": 644,
|
||||||
|
"url": "https://trello-backgrounds.s3.amazonaws.com/SharedBackground/960x644/47f09f0e3910259568294477d0bdedac/photo-1576502200916-3808e07386a5.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 1024,
|
||||||
|
"height": 687,
|
||||||
|
"url": "https://trello-backgrounds.s3.amazonaws.com/SharedBackground/1024x687/47f09f0e3910259568294477d0bdedac/photo-1576502200916-3808e07386a5.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 1280,
|
||||||
|
"height": 859,
|
||||||
|
"url": "https://trello-backgrounds.s3.amazonaws.com/SharedBackground/1280x859/47f09f0e3910259568294477d0bdedac/photo-1576502200916-3808e07386a5.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 1920,
|
||||||
|
"height": 1288,
|
||||||
|
"url": "https://trello-backgrounds.s3.amazonaws.com/SharedBackground/1920x1288/47f09f0e3910259568294477d0bdedac/photo-1576502200916-3808e07386a5.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 2048,
|
||||||
|
"height": 1374,
|
||||||
|
"url": "https://trello-backgrounds.s3.amazonaws.com/SharedBackground/2048x1374/47f09f0e3910259568294477d0bdedac/photo-1576502200916-3808e07386a5.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 2386,
|
||||||
|
"height": 1600,
|
||||||
|
"url": "https://trello-backgrounds.s3.amazonaws.com/SharedBackground/2386x1600/47f09f0e3910259568294477d0bdedac/photo-1576502200916-3808e07386a5.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"width": 2560,
|
||||||
|
"height": 1717,
|
||||||
|
"url": "https://trello-backgrounds.s3.amazonaws.com/SharedBackground/original/d71b9370b4cba91634ae4ffe331fb59a/photo-1576502200916-3808e07386a5"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"backgroundBottomColor": "#068faa",
|
||||||
|
"backgroundTopColor": "#ddaba7",
|
||||||
|
"canBePublic": true,
|
||||||
|
"canBeEnterprise": true,
|
||||||
|
"canBeOrg": true,
|
||||||
|
"canBePrivate": true,
|
||||||
|
"canInvite": true
|
||||||
|
},
|
||||||
|
"labelNames": {
|
||||||
|
"green": "",
|
||||||
|
"yellow": "",
|
||||||
|
"orange": "",
|
||||||
|
"red": "",
|
||||||
|
"purple": "",
|
||||||
|
"blue": "",
|
||||||
|
"sky": "",
|
||||||
|
"lime": "",
|
||||||
|
"pink": "",
|
||||||
|
"black": "",
|
||||||
|
"green_dark": "",
|
||||||
|
"yellow_dark": "",
|
||||||
|
"orange_dark": "",
|
||||||
|
"red_dark": "",
|
||||||
|
"purple_dark": "",
|
||||||
|
"blue_dark": "",
|
||||||
|
"sky_dark": "",
|
||||||
|
"lime_dark": "",
|
||||||
|
"pink_dark": "",
|
||||||
|
"black_dark": "",
|
||||||
|
"green_light": "",
|
||||||
|
"yellow_light": "",
|
||||||
|
"orange_light": "",
|
||||||
|
"red_light": "",
|
||||||
|
"purple_light": "",
|
||||||
|
"blue_light": "",
|
||||||
|
"sky_light": "",
|
||||||
|
"lime_light": "",
|
||||||
|
"pink_light": "",
|
||||||
|
"black_light": ""
|
||||||
|
},
|
||||||
|
"lists": [
|
||||||
|
{
|
||||||
|
"id": "65ad94865aed24f70ecdce4c",
|
||||||
|
"name": "Backlog",
|
||||||
|
"closed": false,
|
||||||
|
"color": null,
|
||||||
|
"idBoard": "65ad94865aed24f70ecdce4b",
|
||||||
|
"pos": 65535,
|
||||||
|
"subscribed": false,
|
||||||
|
"softLimit": null,
|
||||||
|
"type": null,
|
||||||
|
"datasource": {
|
||||||
|
"filter": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "65ad94865aed24f70ecdce4e",
|
||||||
|
"name": "To Do",
|
||||||
|
"closed": false,
|
||||||
|
"color": null,
|
||||||
|
"idBoard": "65ad94865aed24f70ecdce4b",
|
||||||
|
"pos": 196607,
|
||||||
|
"subscribed": false,
|
||||||
|
"softLimit": null,
|
||||||
|
"type": null,
|
||||||
|
"datasource": {
|
||||||
|
"filter": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "65ad94865aed24f70ecdce52",
|
||||||
|
"name": "Done 🎉",
|
||||||
|
"closed": false,
|
||||||
|
"color": null,
|
||||||
|
"idBoard": "65ad94865aed24f70ecdce4b",
|
||||||
|
"pos": 393215,
|
||||||
|
"subscribed": false,
|
||||||
|
"softLimit": null,
|
||||||
|
"type": null,
|
||||||
|
"datasource": {
|
||||||
|
"filter": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -1,40 +0,0 @@
|
||||||
//
|
|
||||||
use crate::trello::model::board::TrelloBoard;
|
|
||||||
// use color_eyre::Result;
|
|
||||||
// use kxio::net::Net;
|
|
||||||
//
|
|
||||||
// use crate::{
|
|
||||||
// f,
|
|
||||||
// trello::{
|
|
||||||
// model::{TrelloAuth, TrelloBoardId},
|
|
||||||
// url,
|
|
||||||
// },
|
|
||||||
// };
|
|
||||||
use crate::trello::model::TrelloBoardName;
|
|
||||||
|
|
||||||
// pub(crate) async fn get_board(
|
|
||||||
// auth: &TrelloAuth,
|
|
||||||
// board_id: &TrelloBoardId,
|
|
||||||
// net: &Net,
|
|
||||||
// ) -> Result<TrelloBoard> {
|
|
||||||
// let board = net
|
|
||||||
// .get(url(f!(
|
|
||||||
// "/boards/{}/?fields=name&lists=all&list_fields=all&cards=all&card_fields=all",
|
|
||||||
// **board_id
|
|
||||||
// )))
|
|
||||||
// .headers(auth.into())
|
|
||||||
// .send()
|
|
||||||
// .await?
|
|
||||||
// .json()
|
|
||||||
// .await?;
|
|
||||||
// Ok(board)
|
|
||||||
// }
|
|
||||||
|
|
||||||
pub(crate) trait TrelloBoards {
|
|
||||||
fn find_by_name(&self, board_name: &TrelloBoardName) -> Option<&TrelloBoard>;
|
|
||||||
}
|
|
||||||
impl TrelloBoards for Vec<TrelloBoard> {
|
|
||||||
fn find_by_name(&self, board_name: &TrelloBoardName) -> Option<&TrelloBoard> {
|
|
||||||
self.iter().find(|b| &b.name == board_name)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
//
|
|
||||||
|
|
||||||
use color_eyre::Result;
|
|
||||||
use kxio::net::Net;
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use crate::trello::{
|
|
||||||
types::{NewTrelloCard, TrelloAuth, TrelloCard},
|
|
||||||
url,
|
|
||||||
};
|
|
||||||
|
|
||||||
///
|
|
||||||
/// POST /cards
|
|
||||||
///
|
|
||||||
/// Query Pparameters
|
|
||||||
///
|
|
||||||
/// - name string
|
|
||||||
/// The name for the card
|
|
||||||
/// - desc string
|
|
||||||
/// The description of the card
|
|
||||||
/// - pos string
|
|
||||||
/// The position of the card. top, bottom, or a positive number.
|
|
||||||
/// - due string
|
|
||||||
/// A due date for the card. A date
|
|
||||||
/// - start string
|
|
||||||
/// A start date for the card. A date, or null
|
|
||||||
/// - dueComplete bool
|
|
||||||
/// If the due date has been marked complete
|
|
||||||
/// - idList string REQUIRED
|
|
||||||
/// The ID of the list the card should be added to
|
|
||||||
/// - idMembers string
|
|
||||||
/// A comma-separated list of memberIds to add to the card
|
|
||||||
/// - idLabels string
|
|
||||||
/// A comma-separated list of labelIds to add to the card
|
|
||||||
pub async fn create_card(
|
|
||||||
auth: &TrelloAuth,
|
|
||||||
new_card: NewTrelloCard,
|
|
||||||
net: &Net,
|
|
||||||
) -> Result<TrelloCard> {
|
|
||||||
let card = net
|
|
||||||
.post(url("/cards"))
|
|
||||||
.headers(auth.into())
|
|
||||||
.body(
|
|
||||||
json!({
|
|
||||||
"idList": new_card.list_id(),
|
|
||||||
"name": new_card.name(),
|
|
||||||
"pos": "bottom",
|
|
||||||
})
|
|
||||||
.to_string(),
|
|
||||||
)
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
.json()
|
|
||||||
.await?;
|
|
||||||
Ok(card)
|
|
||||||
}
|
|
||||||
// #[derive(Debug, serde::Serialize)]
|
|
||||||
// pub struct CreateCardRequest {
|
|
||||||
// name: String,
|
|
||||||
// pos: String,
|
|
||||||
// }
|
|
|
@ -1,21 +0,0 @@
|
||||||
//
|
|
||||||
|
|
||||||
use color_eyre::Result;
|
|
||||||
use kxio::net::Net;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
f,
|
|
||||||
trello::{types::TrelloAuth, url, TrelloCardId},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub async fn delete_card(
|
|
||||||
auth: &TrelloAuth,
|
|
||||||
remote_task_id: &TrelloCardId,
|
|
||||||
net: &Net,
|
|
||||||
) -> Result<()> {
|
|
||||||
net.delete(url(f!("/cards/{remote_task_id}")))
|
|
||||||
.headers(auth.into())
|
|
||||||
.send()
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,96 +0,0 @@
|
||||||
//
|
|
||||||
|
|
||||||
use color_eyre::Result;
|
|
||||||
use kxio::net::Net;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
f,
|
|
||||||
trello::{
|
|
||||||
types::{TrelloAuth, TrelloCard},
|
|
||||||
url, TrelloCardId,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Get a Card
|
|
||||||
///
|
|
||||||
/// https://developer.atlassian.com/cloud/trello/rest/api-group-cards/#api-cards-id-get
|
|
||||||
///
|
|
||||||
/// GET /cards/{id}
|
|
||||||
///
|
|
||||||
/// Get a card by its ID
|
|
||||||
///
|
|
||||||
/// Request
|
|
||||||
///
|
|
||||||
/// Path parameters
|
|
||||||
///
|
|
||||||
/// id TrelloID REQUIRED
|
|
||||||
///
|
|
||||||
/// Query parameters
|
|
||||||
///
|
|
||||||
/// - fields string
|
|
||||||
/// Which fields to return.
|
|
||||||
/// Default: `all`
|
|
||||||
/// `all` or a comma-separated list of fields.
|
|
||||||
/// Defaults: `badges, checkItemStates, closed, dateLastActivity, desc, descData,
|
|
||||||
/// due, start, email, idBoard, idChecklists, idLabels, idList, idMembers, idShort, idAttachmentCover,
|
|
||||||
/// manualCoverAttachment, labels, name, pos, shortUrl, url`
|
|
||||||
///
|
|
||||||
/// - actions string
|
|
||||||
/// See the [Actions Nested Resource](https://developer.atlassian.com/cloud/trello/guides/rest-api/nested-resources/#actions-nested-resource)
|
|
||||||
///
|
|
||||||
/// - attachments oneOf [string, boolean]
|
|
||||||
/// true, false, or cover
|
|
||||||
/// Default: false
|
|
||||||
///
|
|
||||||
/// - attachment_fields string
|
|
||||||
/// `all` or a comma-separated list of attachment fields
|
|
||||||
/// Default: `all`
|
|
||||||
///
|
|
||||||
/// - members boolean
|
|
||||||
/// Whether to return member objects for members on the card
|
|
||||||
/// Default: false
|
|
||||||
///
|
|
||||||
/// - member_fields string
|
|
||||||
/// `all` or a comma-separated list of member fields. Defaults: `avatarHash, fullName, initials, username`
|
|
||||||
///
|
|
||||||
/// - checkItemStates boolean
|
|
||||||
/// Whether to return checkItemState objects for checklists on the card
|
|
||||||
/// Default: false
|
|
||||||
///
|
|
||||||
/// - checklists string
|
|
||||||
/// Whether to return the checklists on the card. all or none
|
|
||||||
/// Default: none
|
|
||||||
///
|
|
||||||
/// - checklist_fields string
|
|
||||||
/// `all` or a comma-separated list of idBoard,idCard,name,pos
|
|
||||||
/// Default: `all`
|
|
||||||
///
|
|
||||||
/// - board boolean
|
|
||||||
/// Whether to return the board object the card is on
|
|
||||||
/// Default: false
|
|
||||||
///
|
|
||||||
/// - board_fields string
|
|
||||||
/// `all` or a comma-separated list of board fields. Defaults: `name, desc, descData, closed, idOrganization, pinned, url, prefs`
|
|
||||||
///
|
|
||||||
/// - list boolean
|
|
||||||
/// See the [Lists Nested Resource](https://developer.atlassian.com/cloud/trello/guides/rest-api/nested-resources/#lists-nested-resource)
|
|
||||||
///
|
|
||||||
/// Responses
|
|
||||||
///
|
|
||||||
/// 200 OK Success
|
|
||||||
///
|
|
||||||
/// application/json
|
|
||||||
pub async fn get_card(
|
|
||||||
auth: &TrelloAuth,
|
|
||||||
trello_card_id: &TrelloCardId,
|
|
||||||
net: &Net,
|
|
||||||
) -> Result<TrelloCard> {
|
|
||||||
let card = net
|
|
||||||
.get(url(f!("/cards/{trello_card_id}")))
|
|
||||||
.headers(auth.into())
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
.json()
|
|
||||||
.await?;
|
|
||||||
Ok(card)
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
mod create;
|
|
||||||
mod delete;
|
|
||||||
mod get;
|
|
||||||
mod update;
|
|
|
@ -1,49 +0,0 @@
|
||||||
//
|
|
||||||
|
|
||||||
use color_eyre::Result;
|
|
||||||
use kxio::net::Net;
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
f,
|
|
||||||
trello::{
|
|
||||||
types::{TrelloAuth, TrelloCard},
|
|
||||||
url, TrelloCardId, TrelloCardName, TrelloListId,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub async fn update_card(
|
|
||||||
auth: &TrelloAuth,
|
|
||||||
card_update: TrelloCardUpdate,
|
|
||||||
net: &Net,
|
|
||||||
) -> Result<TrelloCard> {
|
|
||||||
let net_url = url(f!("/cards/{}", card_update.id));
|
|
||||||
let card = net
|
|
||||||
.put(net_url)
|
|
||||||
.headers(auth.into())
|
|
||||||
.body(
|
|
||||||
json!({
|
|
||||||
"idList": card_update.id_list,
|
|
||||||
"name": card_update.name,
|
|
||||||
})
|
|
||||||
.to_string(),
|
|
||||||
)
|
|
||||||
.send()
|
|
||||||
.await?
|
|
||||||
.json()
|
|
||||||
.await?;
|
|
||||||
Ok(card)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, serde::Serialize)]
|
|
||||||
pub struct TrelloCardUpdate {
|
|
||||||
id: TrelloCardId,
|
|
||||||
name: TrelloCardName,
|
|
||||||
#[serde(rename = "idList")]
|
|
||||||
id_list: TrelloListId,
|
|
||||||
}
|
|
||||||
impl TrelloCardUpdate {
|
|
||||||
pub const fn new(id: TrelloCardId, name: TrelloCardName, id_list: TrelloListId) -> Self {
|
|
||||||
Self { id, name, id_list }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,72 +0,0 @@
|
||||||
//
|
|
||||||
|
|
||||||
use kxio::{net::Net, print::Printer};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
api_result::APIResult,
|
|
||||||
f,
|
|
||||||
trello::{
|
|
||||||
model::{auth::TrelloAuth, list::TrelloList, TrelloBoardId},
|
|
||||||
url, TrelloConfig,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Get Lists in a Board
|
|
||||||
///
|
|
||||||
/// GET /boards/{id}/lists
|
|
||||||
///
|
|
||||||
/// List all lists in a board
|
|
||||||
pub(crate) async fn get_board_lists(
|
|
||||||
cfg: &TrelloConfig,
|
|
||||||
board_id: &TrelloBoardId,
|
|
||||||
net: &Net,
|
|
||||||
prt: &Printer,
|
|
||||||
) -> APIResult<Vec<TrelloList>> {
|
|
||||||
APIResult::new(
|
|
||||||
net.get(url(f!("/boards/{}/lists", board_id)))
|
|
||||||
.headers(TrelloAuth::from(cfg).into())
|
|
||||||
.header("accept", "application/json")
|
|
||||||
.header("content-type", "application/json")
|
|
||||||
.send()
|
|
||||||
.await,
|
|
||||||
prt,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
// /// Get Cards in a List
|
|
||||||
// ///
|
|
||||||
// /// https://developer.atlassian.com/cloud/trello/rest/api-group-lists/#api-lists-id-cards-get
|
|
||||||
// ///
|
|
||||||
// /// GET /lists/{id}/cards
|
|
||||||
// ///
|
|
||||||
// /// List the cards in a list
|
|
||||||
// ///
|
|
||||||
// /// Request
|
|
||||||
// ///
|
|
||||||
// /// Path parameters
|
|
||||||
// ///
|
|
||||||
// /// id TrelloID REQUIRED
|
|
||||||
// ///
|
|
||||||
// /// Responses
|
|
||||||
// ///
|
|
||||||
// /// 200 OK Success
|
|
||||||
// ///
|
|
||||||
// /// application/json
|
|
||||||
// pub(crate) async fn get_lists_cards<'cfg>(
|
|
||||||
// cfg: &TrelloConfig,
|
|
||||||
// list: &TrelloList,
|
|
||||||
// net: &Net,
|
|
||||||
// prt: &Printer,
|
|
||||||
// ) -> APIResult<Vec<TrelloCard>> {
|
|
||||||
// APIResult::new(
|
|
||||||
// net.get(url(f!("/lists/{}/cards", list.id)))
|
|
||||||
// .headers(TrelloAuth::from(cfg).into())
|
|
||||||
// .header("accept", "application/json")
|
|
||||||
// .header("content-type", "application/json")
|
|
||||||
// .send()
|
|
||||||
// .await,
|
|
||||||
// prt,
|
|
||||||
// )
|
|
||||||
// .await
|
|
||||||
// }
|
|
|
@ -1,55 +0,0 @@
|
||||||
//
|
|
||||||
use kxio::{net::Net, print::Printer};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
api_result::APIResult,
|
|
||||||
trello::{
|
|
||||||
model::{auth::TrelloAuth, board::TrelloBoard},
|
|
||||||
url, TrelloConfig,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Get lists from named board that Member belongs to
|
|
||||||
///
|
|
||||||
/// Get Boards that Member belongs to
|
|
||||||
/// https://developer.atlassian.com/cloud/trello/rest/api-group-members/#api-members-id-boards-get
|
|
||||||
/// /members/{id}/boards
|
|
||||||
///
|
|
||||||
/// Lists the boards that the user is a member of.
|
|
||||||
///
|
|
||||||
/// Request
|
|
||||||
///
|
|
||||||
/// Path parameters
|
|
||||||
///
|
|
||||||
/// - id TrelloID REQUIRED
|
|
||||||
///
|
|
||||||
///
|
|
||||||
/// Query parameters
|
|
||||||
///
|
|
||||||
/// - fields string
|
|
||||||
/// Default: all
|
|
||||||
/// Valid values: id, name, desc, descData, closed, idMemberCreator, idOrganization, pinned, url, shortUrl, prefs, labelNames, starred, limits, memberships, enterpriseOwned
|
|
||||||
///
|
|
||||||
/// - lists string
|
|
||||||
/// Which lists to include with the boards. One of: all, closed, none, open
|
|
||||||
/// Default: none
|
|
||||||
/// Valid values: all, closed, none, open
|
|
||||||
///
|
|
||||||
/// curl --request GET \
|
|
||||||
/// --url "https://api.trello.com/1/members/$TRELLO_USERNAME/boards?key=$TRELLO_KEY&token=$TRELLO_SECRET&lists=open" \
|
|
||||||
/// --header 'Accept: application/json'
|
|
||||||
pub(crate) async fn get_boards_that_member_belongs_to(
|
|
||||||
cfg: &TrelloConfig,
|
|
||||||
net: &Net,
|
|
||||||
prt: &Printer,
|
|
||||||
) -> APIResult<Vec<TrelloBoard>> {
|
|
||||||
APIResult::new(
|
|
||||||
net.get(url("/members/me/boards?lists=open"))
|
|
||||||
.headers(TrelloAuth::from(cfg).into())
|
|
||||||
.header("Accept", "application/json")
|
|
||||||
.send()
|
|
||||||
.await,
|
|
||||||
prt,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
//
|
|
||||||
|
|
||||||
pub(crate) mod boards;
|
|
||||||
// pub(crate) mod cards;
|
|
||||||
pub(crate) mod lists;
|
|
||||||
pub(crate) mod members;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests;
|
|
|
@ -1,17 +0,0 @@
|
||||||
//
|
|
||||||
use kxio::{net::MockNet, print::Printer};
|
|
||||||
|
|
||||||
pub(crate) fn a_network() -> MockNet {
|
|
||||||
kxio::net::mock()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn a_printer() -> Printer {
|
|
||||||
kxio::print::test()
|
|
||||||
}
|
|
||||||
|
|
||||||
// pub(crate) fn an_auth<'cfg>(cfg: &'cfg TrelloConfig) -> TrelloAuth<'cfg> {
|
|
||||||
// TrelloAuth {
|
|
||||||
// api_key: &cfg.api_key,
|
|
||||||
// api_secret: &cfg.api_secret,
|
|
||||||
// }
|
|
||||||
// }
|
|
|
@ -1,66 +0,0 @@
|
||||||
//
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
use kxio::net::StatusCode;
|
|
||||||
use serde_json::json;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
s,
|
|
||||||
trello::{
|
|
||||||
api::members::get_boards_that_member_belongs_to, model::board::TrelloBoard, TrelloConfig,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
mod given;
|
|
||||||
|
|
||||||
type TestResult = color_eyre::Result<()>;
|
|
||||||
|
|
||||||
mod members {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn get_member_boards() -> TestResult {
|
|
||||||
//given
|
|
||||||
let net = given::a_network();
|
|
||||||
let prt = given::a_printer();
|
|
||||||
let trello_config = TrelloConfig {
|
|
||||||
api_key: s!("foo").into(),
|
|
||||||
api_secret: s!("bar").into(),
|
|
||||||
board_name: s!("board-name").into(),
|
|
||||||
};
|
|
||||||
|
|
||||||
net.on()
|
|
||||||
.get("https://api.trello.com/1/members/me/boards?lists=open")
|
|
||||||
.headers(HashMap::from([
|
|
||||||
(
|
|
||||||
s!("authorization"),
|
|
||||||
s!("OAuth oauth_consumer_key=\"foo\", oauth_token=\"bar\""),
|
|
||||||
),
|
|
||||||
(s!("accept"), s!("application/json")),
|
|
||||||
]))
|
|
||||||
.respond(StatusCode::OK)
|
|
||||||
.body(s!(json!([
|
|
||||||
{"id": "1", "name": "board-name", "lists":[]}
|
|
||||||
])))?;
|
|
||||||
|
|
||||||
//when
|
|
||||||
let result = get_boards_that_member_belongs_to(&trello_config, &net.into(), &prt)
|
|
||||||
.await
|
|
||||||
.result?;
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
result,
|
|
||||||
vec![TrelloBoard {
|
|
||||||
id: s!("1").into(),
|
|
||||||
name: s!("board-name").into(),
|
|
||||||
lists: vec![]
|
|
||||||
}]
|
|
||||||
);
|
|
||||||
|
|
||||||
//then
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: boards
|
|
||||||
// TODO: lists
|
|
||||||
// TODO: cards
|
|
40
src/trello/board.rs
Normal file
40
src/trello/board.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
//
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
use crate::execute::Execute;
|
||||||
|
use crate::{p, FullCtx};
|
||||||
|
|
||||||
|
use super::model::TrelloBoardId;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug)]
|
||||||
|
pub(crate) enum TrelloBoardCommand {
|
||||||
|
Get {
|
||||||
|
#[clap(long, action = clap::ArgAction::SetTrue)]
|
||||||
|
dump: bool,
|
||||||
|
|
||||||
|
board_id: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Execute for TrelloBoardCommand {
|
||||||
|
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
||||||
|
match self {
|
||||||
|
Self::Get { dump, board_id } => {
|
||||||
|
let api_result = ctx
|
||||||
|
.trello_client()
|
||||||
|
.board(&TrelloBoardId::new(board_id))
|
||||||
|
.await;
|
||||||
|
if dump {
|
||||||
|
p!(ctx.prt, "{}", api_result.text);
|
||||||
|
} else {
|
||||||
|
let mut lists = api_result.result?.lists;
|
||||||
|
lists.sort_by(|a, b| a.name.cmp(&b.name));
|
||||||
|
lists.into_iter().for_each(|list| {
|
||||||
|
p!(ctx.prt, "{}:{}", list.id, list.name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,35 +0,0 @@
|
||||||
//
|
|
||||||
use clap::Parser;
|
|
||||||
|
|
||||||
use crate::execute::Execute;
|
|
||||||
use crate::{p, FullCtx};
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
|
||||||
pub(crate) enum TrelloBoardCommand {
|
|
||||||
List {
|
|
||||||
#[clap(long, action = clap::ArgAction::SetTrue)]
|
|
||||||
dump: bool,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Execute for TrelloBoardCommand {
|
|
||||||
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
|
||||||
match self {
|
|
||||||
Self::List { dump } => list(ctx, dump).await,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn list(ctx: FullCtx, dump: bool) -> color_eyre::Result<()> {
|
|
||||||
let api_result = ctx.trello_client().boards(&ctx.cfg.trello).await;
|
|
||||||
if dump {
|
|
||||||
p!(ctx.prt, "{}", api_result.text);
|
|
||||||
} else {
|
|
||||||
let mut boards = api_result.result?;
|
|
||||||
boards.sort_by(|a, b| a.name.cmp(&b.name));
|
|
||||||
boards.into_iter().for_each(|board| {
|
|
||||||
p!(ctx.prt, "{}:{}", board.id, board.name);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
|
@ -1,25 +1,92 @@
|
||||||
//
|
//
|
||||||
use crate::api_result::APIResult;
|
use std::collections::HashMap;
|
||||||
use crate::trello::model::list::TrelloList;
|
|
||||||
use crate::trello::TrelloConfig;
|
|
||||||
use crate::trello::{api::lists, model::board::TrelloBoard};
|
|
||||||
use crate::FullCtx;
|
|
||||||
|
|
||||||
use super::model::TrelloBoardId;
|
use kxio::net::{Net, ReqBuilder};
|
||||||
|
|
||||||
|
use super::model::{TrelloBoardId, TrelloListId};
|
||||||
|
use crate::trello::model::card::TrelloShortCard;
|
||||||
|
use crate::{api_result::APIResult, f, s, trello::model::board::TrelloBoard, FullCtx};
|
||||||
|
|
||||||
pub(crate) struct TrelloClient<'ctx> {
|
pub(crate) struct TrelloClient<'ctx> {
|
||||||
ctx: &'ctx FullCtx,
|
ctx: &'ctx FullCtx,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> TrelloClient<'ctx> {
|
impl<'ctx> TrelloClient<'ctx> {
|
||||||
pub(crate) async fn boards(&self, cfg: &TrelloConfig) -> APIResult<Vec<TrelloBoard>> {
|
fn url(&self, path: impl Into<String>) -> String {
|
||||||
super::api::members::get_boards_that_member_belongs_to(cfg, &self.ctx.net, &self.ctx.prt)
|
let path = path.into();
|
||||||
|
assert!(path.starts_with("/"));
|
||||||
|
f!("https://api.trello.com/1{path}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn common_headers(&self) -> HashMap<String, String> {
|
||||||
|
let api_key = &self.ctx.cfg.trello.api_key;
|
||||||
|
let api_secret = &self.ctx.cfg.trello.api_secret;
|
||||||
|
HashMap::from([
|
||||||
|
(s!("accept"), s!("application/json")),
|
||||||
|
(s!("content-type"), s!("application/json")),
|
||||||
|
(
|
||||||
|
s!("Authorization"),
|
||||||
|
f!(r#"OAuth oauth_consumer_key="{api_key}", oauth_token="{api_secret}""#,),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn request<T: for<'a> serde::Deserialize<'a>>(
|
||||||
|
&self,
|
||||||
|
url: impl Into<String>,
|
||||||
|
custom: fn(&Net, String) -> ReqBuilder,
|
||||||
|
) -> APIResult<T> {
|
||||||
|
APIResult::new(
|
||||||
|
custom(&self.ctx.net, self.url(url))
|
||||||
|
.headers(self.common_headers())
|
||||||
|
.send()
|
||||||
|
.await,
|
||||||
|
&self.ctx.prt,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'ctx> TrelloClient<'ctx> {
|
||||||
|
// https://developer.atlassian.com/cloud/trello/rest/api-group-members/#api-members-id-boards-get
|
||||||
|
pub(crate) async fn boards(&self) -> APIResult<Vec<TrelloBoard>> {
|
||||||
|
self.request("/members/me/boards?lists=open", |net, url| net.get(url))
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn lists(&self, board_id: &TrelloBoardId) -> APIResult<Vec<TrelloList>> {
|
// https://developer.atlassian.com/cloud/trello/rest/api-group-boards/#api-boards-id-get
|
||||||
lists::get_board_lists(&self.ctx.cfg.trello, board_id, &self.ctx.net, &self.ctx.prt).await
|
pub(crate) async fn board(&self, board_id: &TrelloBoardId) -> APIResult<TrelloBoard> {
|
||||||
|
self.request(f!("/boards/{board_id}?lists=open"), |net, url| net.get(url))
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://developer.atlassian.com/cloud/trello/rest/api-group-lists/#api-lists-id-cards-get
|
||||||
|
pub(crate) async fn list_cards(
|
||||||
|
&self,
|
||||||
|
list_id: &TrelloListId,
|
||||||
|
) -> APIResult<Vec<TrelloShortCard>> {
|
||||||
|
self.request(f!("/lists/{list_id}/cards"), |net, url| net.get(url))
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
// // https://developer.atlassian.com/cloud/trello/rest/api-group-cards/#api-cards-id-get
|
||||||
|
// pub(crate) async fn card(&self, card_id: &TrelloCardId) -> APIResult<TrelloShortCard>{
|
||||||
|
// self.request(f!("/cards/{card_id}?attachments=true"), |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<TrelloAttachment> {
|
||||||
|
// self.request(
|
||||||
|
// f!("/cards/{card_id}/attachments/{attachment_id}"),
|
||||||
|
// |net, url| net.get(url),
|
||||||
|
// )
|
||||||
|
// .await
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TrelloClient<'_> {
|
impl TrelloClient<'_> {
|
||||||
|
|
|
@ -16,7 +16,7 @@ impl Execute for TrelloMemberCommand {
|
||||||
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::Get { dump } => {
|
Self::Get { dump } => {
|
||||||
let api_result = ctx.trello_client().boards(&ctx.cfg.trello).await;
|
let api_result = ctx.trello_client().boards().await;
|
||||||
if dump {
|
if dump {
|
||||||
p!(ctx.prt, "{}", api_result.text);
|
p!(ctx.prt, "{}", api_result.text);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,6 +1,21 @@
|
||||||
//
|
//
|
||||||
pub(crate) mod api;
|
use clap::Parser;
|
||||||
pub(crate) mod boards;
|
|
||||||
|
use crate::{
|
||||||
|
execute::Execute,
|
||||||
|
trello::{
|
||||||
|
board::TrelloBoardCommand,
|
||||||
|
member::TrelloMemberCommand,
|
||||||
|
model::{
|
||||||
|
auth::{TrelloApiKey, TrelloApiSecret},
|
||||||
|
TrelloBoardName,
|
||||||
|
},
|
||||||
|
stack::TrelloStackCommand,
|
||||||
|
},
|
||||||
|
FullCtx,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(crate) mod board;
|
||||||
pub(crate) mod client;
|
pub(crate) mod client;
|
||||||
pub(crate) mod member;
|
pub(crate) mod member;
|
||||||
pub(crate) mod model;
|
pub(crate) mod model;
|
||||||
|
@ -9,26 +24,14 @@ pub(crate) mod stack;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
use crate::execute::Execute;
|
|
||||||
use crate::trello::member::TrelloMemberCommand;
|
|
||||||
use crate::trello::model::auth::{TrelloApiKey, TrelloApiSecret};
|
|
||||||
use crate::trello::model::TrelloBoardName;
|
|
||||||
use crate::trello::stack::TrelloStackCommand;
|
|
||||||
use crate::{f, FullCtx};
|
|
||||||
|
|
||||||
use clap::Parser;
|
|
||||||
|
|
||||||
pub(crate) fn url(path: impl Into<String>) -> String {
|
|
||||||
let path = path.into();
|
|
||||||
assert!(path.starts_with("/"));
|
|
||||||
f!("https://api.trello.com/1{path}")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
pub(crate) enum TrelloCommand {
|
pub(crate) enum TrelloCommand {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Member(TrelloMemberCommand),
|
Member(TrelloMemberCommand),
|
||||||
|
|
||||||
|
#[clap(subcommand)]
|
||||||
|
Board(TrelloBoardCommand),
|
||||||
|
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
Stack(TrelloStackCommand),
|
Stack(TrelloStackCommand),
|
||||||
}
|
}
|
||||||
|
@ -37,6 +40,7 @@ impl Execute for TrelloCommand {
|
||||||
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
async fn execute(self, ctx: FullCtx) -> color_eyre::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::Member(cmd) => cmd.execute(ctx).await,
|
Self::Member(cmd) => cmd.execute(ctx).await,
|
||||||
|
Self::Board(cmd) => cmd.execute(ctx).await,
|
||||||
Self::Stack(cmd) => cmd.execute(ctx).await,
|
Self::Stack(cmd) => cmd.execute(ctx).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
//
|
//
|
||||||
use derive_more::derive::Display;
|
use derive_more::derive::Display;
|
||||||
|
|
||||||
|
|
|
@ -9,3 +9,12 @@ pub(crate) struct TrelloBoard {
|
||||||
pub(crate) name: TrelloBoardName,
|
pub(crate) name: TrelloBoardName,
|
||||||
pub(crate) lists: Vec<TrelloList>,
|
pub(crate) lists: Vec<TrelloList>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) trait TrelloBoards {
|
||||||
|
fn find_by_name(&self, board_name: &TrelloBoardName) -> Option<&TrelloBoard>;
|
||||||
|
}
|
||||||
|
impl TrelloBoards for Vec<TrelloBoard> {
|
||||||
|
fn find_by_name(&self, board_name: &TrelloBoardName) -> Option<&TrelloBoard> {
|
||||||
|
self.iter().find(|b| &b.name == board_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
//
|
//
|
||||||
use super::{TrelloCardId, TrelloCardName, TrelloListId};
|
use super::{
|
||||||
|
TrelloAttachmentId, TrelloCardDescription, TrelloCardDue, TrelloCardId, TrelloCardName,
|
||||||
|
TrelloCardPosition, TrelloLabelId, TrelloListId,
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
|
||||||
pub(crate) struct TrelloCard {
|
pub(crate) struct TrelloCard {
|
||||||
|
@ -8,3 +11,27 @@ pub(crate) struct TrelloCard {
|
||||||
#[serde(rename = "idList")]
|
#[serde(rename = "idList")]
|
||||||
pub(crate) id_list: TrelloListId,
|
pub(crate) id_list: TrelloListId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub(crate) struct TrelloShortCard {
|
||||||
|
pub(crate) id: TrelloCardId,
|
||||||
|
pub(crate) name: TrelloCardName,
|
||||||
|
pub(crate) desc: TrelloCardDescription,
|
||||||
|
pub(crate) due: Option<TrelloCardDue>, // format date
|
||||||
|
#[serde(rename = "idAttachmentCover")]
|
||||||
|
pub(crate) id_attachment_cover: Option<TrelloAttachmentId>,
|
||||||
|
pub(crate) labels: Vec<TrelloLabelId>,
|
||||||
|
pub(crate) pos: TrelloCardPosition,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
|
||||||
|
pub(crate) struct TrelloLongCard {
|
||||||
|
pub(crate) id: TrelloCardId,
|
||||||
|
pub(crate) name: TrelloCardName,
|
||||||
|
pub(crate) desc: TrelloCardDescription,
|
||||||
|
pub(crate) due: Option<TrelloCardDue>, // format date
|
||||||
|
#[serde(rename = "idAttachmentCover")]
|
||||||
|
pub(crate) id_attachment_cover: Option<TrelloAttachmentId>,
|
||||||
|
pub(crate) labels: Vec<TrelloLabelId>,
|
||||||
|
pub(crate) pos: TrelloCardPosition,
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ pub(crate) mod list;
|
||||||
// mod new_card;
|
// mod new_card;
|
||||||
|
|
||||||
use derive_more::derive::Display;
|
use derive_more::derive::Display;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::newtype;
|
use crate::newtype;
|
||||||
|
|
||||||
|
@ -21,3 +22,27 @@ newtype!(
|
||||||
);
|
);
|
||||||
newtype!(TrelloCardId, String, Display, "Card ID");
|
newtype!(TrelloCardId, String, Display, "Card ID");
|
||||||
newtype!(TrelloCardName, String, Display, "Card Name");
|
newtype!(TrelloCardName, String, Display, "Card Name");
|
||||||
|
newtype!(TrelloCardDescription, String, Display, "Card Description");
|
||||||
|
newtype!(TrelloCardDue, String, Display, "Card Due");
|
||||||
|
newtype!(TrelloCardPosition, i64, Display, "Card Position");
|
||||||
|
newtype!(TrelloAttachmentId, String, Display, "Card Attachment ID");
|
||||||
|
newtype!(TrelloLabelId, String, Display, "Label ID");
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub(crate) struct TrelloAttachment {
|
||||||
|
pub(crate) id: String, // "5abbe4b7ddc1b351ef961414",
|
||||||
|
pub(crate) bytes: String,
|
||||||
|
date: String, //"2018-10-17T19:10:14.808Z",
|
||||||
|
#[serde(rename = "edgeColor")]
|
||||||
|
edge_color: String, //"yellow",
|
||||||
|
#[serde(rename = "idMember")]
|
||||||
|
id_member: String, //"5abbe4b7ddc1b351ef961414",
|
||||||
|
#[serde(rename = "isUpload")]
|
||||||
|
is_upload: bool, //false,
|
||||||
|
#[serde(rename = "mimeType")]
|
||||||
|
mime_type: String, //"",
|
||||||
|
pub(crate) name: String, //"Deprecation Extension Notice",
|
||||||
|
previews: Vec<String>, //[],
|
||||||
|
url: String, //"https://admin.typeform.com/form/RzExEM/share#/link",
|
||||||
|
pos: i64, //1638
|
||||||
|
}
|
||||||
|
|
|
@ -4,41 +4,36 @@ use color_eyre::Result;
|
||||||
|
|
||||||
use crate::{execute::Execute, p, FullCtx};
|
use crate::{execute::Execute, p, FullCtx};
|
||||||
|
|
||||||
|
use super::model::TrelloListId;
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
pub(crate) enum TrelloStackCommand {
|
pub(crate) enum TrelloStackCommand {
|
||||||
/// List all stacks (lists) in the board
|
Get {
|
||||||
List {
|
|
||||||
#[clap(long, action = clap::ArgAction::SetTrue)]
|
#[clap(long, action = clap::ArgAction::SetTrue)]
|
||||||
dump: bool,
|
dump: bool,
|
||||||
|
|
||||||
|
list_id: String,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Execute for TrelloStackCommand {
|
impl Execute for TrelloStackCommand {
|
||||||
async fn execute(self, ctx: FullCtx) -> Result<()> {
|
async fn execute(self, ctx: FullCtx) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
Self::List { dump } => list(ctx, dump).await,
|
Self::Get { dump, list_id } => {
|
||||||
|
let api_result = ctx
|
||||||
|
.trello_client()
|
||||||
|
.list_cards(&TrelloListId::new(list_id))
|
||||||
|
.await;
|
||||||
|
if dump {
|
||||||
|
p!(ctx.prt, "{}", api_result.text);
|
||||||
|
} else {
|
||||||
|
let cards = api_result.result?;
|
||||||
|
for card in cards {
|
||||||
|
p!(ctx.prt, "{}:{}", card.id, card.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn list(ctx: FullCtx, dump: bool) -> std::result::Result<(), color_eyre::eyre::Error> {
|
|
||||||
let client = ctx.trello_client();
|
|
||||||
let cfg = &ctx.cfg.trello;
|
|
||||||
let board = client
|
|
||||||
.boards(cfg)
|
|
||||||
.await
|
|
||||||
.result?
|
|
||||||
.into_iter()
|
|
||||||
.find(|b| b.name == cfg.board_name)
|
|
||||||
.ok_or_else(|| color_eyre::eyre::eyre!("Board not found"))?;
|
|
||||||
let api_result = client.lists(&board.id).await;
|
|
||||||
if dump {
|
|
||||||
p!(ctx.prt, "{}", api_result.text);
|
|
||||||
} else {
|
|
||||||
let lists = api_result.result?;
|
|
||||||
for list in lists {
|
|
||||||
p!(ctx.prt, "{}", list.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
use kxio::net::StatusCode;
|
use kxio::net::StatusCode;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
s,
|
||||||
tests::given,
|
tests::given,
|
||||||
trello::{
|
trello::{
|
||||||
api::boards::TrelloBoards as _,
|
model::board::TrelloBoards as _,
|
||||||
model::{
|
model::{
|
||||||
board::TrelloBoard, list::TrelloList, TrelloBoardId, TrelloBoardName, TrelloListId,
|
board::TrelloBoard, list::TrelloList, TrelloBoardId, TrelloBoardName, TrelloListId,
|
||||||
TrelloListName,
|
TrelloListName,
|
||||||
|
@ -48,39 +49,42 @@ mod commands {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod stack {
|
#[tokio::test]
|
||||||
|
async fn get() {
|
||||||
|
//given
|
||||||
|
let mock_net = kxio::net::mock();
|
||||||
|
|
||||||
use super::*;
|
mock_net
|
||||||
|
.on()
|
||||||
|
.get("https://api.trello.com/1/boards/65ad94865aed24f70ecdce4b")
|
||||||
|
.query("lists", "open")
|
||||||
|
.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-board-get.json"))
|
||||||
|
.expect("mock request");
|
||||||
|
|
||||||
#[tokio::test]
|
// let fs = given::a_filesystem();
|
||||||
async fn list() {
|
let ctx = given::a_full_context(mock_net);
|
||||||
//given
|
|
||||||
let mock_net = kxio::net::mock();
|
|
||||||
|
|
||||||
mock_net
|
//when
|
||||||
.on()
|
let result = ctx
|
||||||
.get("https://api.trello.com/1/boards/123/lists")
|
.trello_client()
|
||||||
.header("authorization", "OAuth oauth_consumer_key=\"trello-api-key\", oauth_token=\"trello-api-secret\"")
|
.board(&TrelloBoardId::new("65ad94865aed24f70ecdce4b"))
|
||||||
.header("accept", "application/json")
|
.await
|
||||||
.header("content-type", "application/json")
|
.result
|
||||||
.respond(StatusCode::OK)
|
.expect("board");
|
||||||
.body(include_str!("../tests/responses/trello-stack-list.json"))
|
|
||||||
.expect("mock request");
|
|
||||||
|
|
||||||
// let fs = given::a_filesystem();
|
assert_eq!(
|
||||||
let ctx = given::a_full_context(mock_net);
|
result,
|
||||||
|
TrelloBoard {
|
||||||
//when
|
id: TrelloBoardId::new(crate::s!("65ad94865aed24f70ecdce4b")),
|
||||||
let result = ctx
|
name: TrelloBoardName::new(s!("Tasyn Kanban")),
|
||||||
.trello_client()
|
lists: vec![
|
||||||
.lists(&TrelloBoardId::new("123"))
|
|
||||||
.await
|
|
||||||
.result
|
|
||||||
.expect("get stacks");
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
result,
|
|
||||||
vec![
|
|
||||||
TrelloList {
|
TrelloList {
|
||||||
id: TrelloListId::new("65ad94865aed24f70ecdce4c"),
|
id: TrelloListId::new("65ad94865aed24f70ecdce4c"),
|
||||||
name: TrelloListName::new("Backlog")
|
name: TrelloListName::new("Backlog")
|
||||||
|
@ -94,7 +98,7 @@ mod commands {
|
||||||
name: TrelloListName::new("Done 🎉")
|
name: TrelloListName::new("Done 🎉")
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
);
|
}
|
||||||
}
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue