From 5d62b7edd0258063f9c3cc02b6000e4f7da8a0c1 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sun, 22 Dec 2024 14:14:53 +0000 Subject: [PATCH] feat(nextcloud): support exponential backoff with jitter --- src/nextcloud/client.rs | 117 ++++++++++++++++++---------------------- src/trello/client.rs | 1 - 2 files changed, 51 insertions(+), 67 deletions(-) diff --git a/src/nextcloud/client.rs b/src/nextcloud/client.rs index 3332883..01ca09d 100644 --- a/src/nextcloud/client.rs +++ b/src/nextcloud/client.rs @@ -5,21 +5,18 @@ use kxio::{ 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, + Attachment, Board, Card, Label, NextcloudBoardId, NextcloudCardDescription, + NextcloudCardId, NextcloudCardTitle, NextcloudHostname, NextcloudLabelColour, + NextcloudLabelId, NextcloudLabelTitle, NextcloudPassword, NextcloudStackId, + NextcloudStackTitle, NextcloudUsername, Stack, }, - FullCtx, + with_exponential_backoff, FullCtx, }; pub(crate) struct DeckClient<'ctx> { @@ -53,13 +50,17 @@ impl<'ctx> DeckClient<'ctx> { url: impl Into, custom: fn(&Net, String) -> ReqBuilder, ) -> APIResult { + let url = url.into(); 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, + with_exponential_backoff!( + &self.ctx, + custom(&self.ctx.net, self.url(url.clone())) + .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 @@ -71,49 +72,19 @@ impl<'ctx> DeckClient<'ctx> { body: impl Into, custom: fn(&Net, String) -> ReqBuilder, ) -> APIResult { + let url = self.url(url.into()); + let body = body.into(); 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( - &self, - path: String, - // form_data: HashMap, - form: multipart::Form, - ) -> APIResult - 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), - }, + with_exponential_backoff!( + &self.ctx, + custom(&self.ctx.net, url.clone()) + .basic_auth(self.username.as_str(), Some(self.password.as_str())) + .header("accept", "application/json") + .header("content-type", "application/json") + .body(body.clone()) + .send() + .await + ), &self.ctx.prt, ) .await @@ -240,16 +211,30 @@ impl<'ctx> DeckClient<'ctx> { card_id: NextcloudCardId, file: &FileHandle, ) -> APIResult { - 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, + APIResult::new( + with_exponential_backoff!(&self.ctx, { + 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.ctx + .net + .send( + self.ctx + .net + .client() + .post(self.url(f!( + "boards/{board_id}/stacks/{stack_id}/cards/{card_id}/attachments" + ))) + .basic_auth(self.username.as_str(), Some(self.password.as_str())) + .header("accept", "application/json") + .multipart(form), + ) + .await + }), + &self.ctx.prt, ) .await } diff --git a/src/trello/client.rs b/src/trello/client.rs index 43f10ed..c674a4a 100644 --- a/src/trello/client.rs +++ b/src/trello/client.rs @@ -18,7 +18,6 @@ use crate::{ pub(crate) struct TrelloClient<'ctx> { ctx: &'ctx FullCtx, - // 300 requests per 10 seconds for each API key and 100 requests per 10 second interval for each token } impl<'ctx> TrelloClient<'ctx> {