trello-to-deck/src/nextcloud/client.rs
Paul Campbell 61323484a2
Some checks failed
Test / build (map[name:stable]) (push) Successful in 1m51s
Test / build (map[name:nightly]) (push) Successful in 2m26s
Release Please / Release-plz (push) Failing after 26s
refactor: split import into smaller functions
2024-12-22 14:27:41 +00:00

256 lines
7.4 KiB
Rust

//
use bytes::Bytes;
use kxio::{
fs::FileHandle,
net::{Net, ReqBuilder},
};
use reqwest::multipart;
use serde::de::DeserializeOwned;
use serde_json::json;
use crate::nextcloud::model::{
Label, NextcloudCardDescription, NextcloudCardTitle, NextcloudLabelColour, NextcloudLabelTitle,
NextcloudStackTitle,
};
use crate::{
api_result::APIResult,
f,
nextcloud::model::{
Attachment, Board, Card, NextcloudBoardId, NextcloudCardId, NextcloudHostname,
NextcloudLabelId, NextcloudPassword, NextcloudStackId, NextcloudUsername, Stack,
},
FullCtx,
};
pub(crate) struct DeckClient<'ctx> {
ctx: &'ctx FullCtx,
hostname: &'ctx NextcloudHostname,
username: &'ctx NextcloudUsername,
password: &'ctx NextcloudPassword,
}
// Uses the API described here: https://deck.readthedocs.io/en/stable/API/#cards
impl<'ctx> DeckClient<'ctx> {
pub const fn new(ctx: &'ctx FullCtx) -> Self {
Self {
ctx,
hostname: &ctx.cfg.nextcloud.hostname,
username: &ctx.cfg.nextcloud.username,
password: &ctx.cfg.nextcloud.password,
}
}
fn url(&self, path: impl Into<String>) -> String {
f!(
"{}/index.php/apps/deck/api/v1.0/{}",
self.hostname,
path.into()
)
}
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))
.basic_auth(self.username.as_str(), Some(self.password.as_str()))
.header("accept", "application/json")
.header("content-type", "application/json")
.send()
.await,
&self.ctx.prt,
)
.await
}
async fn request_with_body<T: for<'a> serde::Deserialize<'a>>(
&self,
url: impl Into<String>,
body: impl Into<Bytes>,
custom: fn(&Net, String) -> ReqBuilder,
) -> APIResult<T> {
APIResult::new(
custom(&self.ctx.net, self.url(url))
.basic_auth(self.username.as_str(), Some(self.password.as_str()))
.header("accept", "application/json")
.header("content-type", "application/json")
.body(body)
.send()
.await,
&self.ctx.prt,
)
.await
}
async fn request_with_form<T>(
&self,
path: String,
// form_data: HashMap<String, String>,
form: multipart::Form,
) -> APIResult<T>
where
T: DeserializeOwned,
{
// let form: multipart::Form = multipart::Form::new();
// let full_path = self.ctx.fs.base().join(form_data.get("file").expect("file"));
// e!(self.ctx.prt, "Uploading file: {}", full_path.display());
// let form = form.file("file", Path::new(&full_path))
// .await
// .expect("read file");
let request_builder = self
.ctx
.net
.client()
.post(self.url(&path))
.basic_auth(self.username.as_str(), Some(self.password.as_str()))
.header("accept", "application/json")
// .form(&form_data);
.multipart(form);
// let data = request_builder.multipart();
APIResult::new(
match self.ctx.net.send(request_builder).await {
Ok(response) => Ok(response),
Err(err) => Err(err),
},
&self.ctx.prt,
)
.await
}
pub(crate) async fn get_boards(&self) -> APIResult<Vec<Board>> {
self.request("boards", |net, url| net.get(url)).await
}
pub(crate) async fn get_board(&self, board_id: NextcloudBoardId) -> APIResult<Board> {
self.request(f!("boards/{board_id}"), |net, url| net.get(url))
.await
}
pub(crate) async fn get_stacks(&self, board_id: NextcloudBoardId) -> APIResult<Vec<Stack>> {
self.request(f!("boards/{board_id}/stacks"), |net, url| net.get(url))
.await
}
pub(crate) async fn get_stack(
&self,
board_id: NextcloudBoardId,
stack_id: NextcloudStackId,
) -> APIResult<Stack> {
self.request(f!("boards/{board_id}/stacks/{stack_id}"), |net, url| {
net.get(url)
})
.await
}
pub(crate) async fn create_stack(
&self,
board_id: NextcloudBoardId,
stack_title: &NextcloudStackTitle,
) -> APIResult<Stack> {
self.request_with_body(
f!("boards/{board_id}/stacks"),
json!({
"title": stack_title,
"order": 999,
})
.to_string(),
|net, url| net.post(url),
)
.await
}
pub(crate) async fn create_card(
&self,
board_id: NextcloudBoardId,
stack_id: NextcloudStackId,
title: &NextcloudCardTitle,
description: Option<&NextcloudCardDescription>,
) -> APIResult<Card> {
let mut body = json!({
"title": title,
});
if let Some(desc) = &description {
body["description"] = serde_json::Value::String(desc.to_string());
}
self.request_with_body(
f!("boards/{board_id}/stacks/{stack_id}/cards"),
body.to_string(),
|net, url| net.post(url),
)
.await
}
pub(crate) async fn create_label(
&self,
board_id: NextcloudBoardId,
name: &NextcloudLabelTitle,
colour: &NextcloudLabelColour,
) -> APIResult<Label> {
self.request_with_body(
f!("boards/{board_id}/labels"),
json!({
"title": name,
"color": colour,
})
.to_string(),
|net, url| net.post(url),
)
.await
}
pub(crate) async fn get_card(
&self,
board_id: NextcloudBoardId,
stack_id: NextcloudStackId,
card_id: NextcloudCardId,
) -> APIResult<Card> {
self.request(
f!("boards/{board_id}/stacks/{stack_id}/cards/{card_id}"),
|net, url| net.get(url),
)
.await
}
pub(crate) async fn add_label_to_card(
&self,
board_id: NextcloudBoardId,
stack_id: NextcloudStackId,
card_id: NextcloudCardId,
label_id: NextcloudLabelId,
) -> APIResult<()> {
self.request_with_body(
f!("boards/{board_id}/stacks/{stack_id}/cards/{card_id}/assignLabel"),
json!({
"labelId": label_id
})
.to_string(),
|net, url| net.put(url),
)
.await
}
pub(crate) async fn add_attachment_to_card(
&self,
board_id: NextcloudBoardId,
stack_id: NextcloudStackId,
card_id: NextcloudCardId,
file: &FileHandle,
) -> APIResult<Attachment> {
let form: multipart::Form = multipart::Form::new();
let form = form.text("type", "file");
let form = form
.file("file", file.as_pathbuf())
.await
.expect("read file");
self.request_with_form(
// f!("apps/deck/cards/{card_id}/attachment"),
f!("boards/{board_id}/stacks/{stack_id}/cards/{card_id}/attachments"),
form,
)
.await
}
}