You've already forked AstralRinth
forked from didirus/AstralRinth
Fix Mural payout status syncing (#4853)
* Fix Mural payout status syncing * Make Mural payout code more resilient * prepare sqlx * fix test
This commit is contained in:
122
packages/muralpay/src/client/mod.rs
Normal file
122
packages/muralpay/src/client/mod.rs
Normal file
@@ -0,0 +1,122 @@
|
||||
mod error;
|
||||
pub use error::*;
|
||||
use {
|
||||
reqwest::{IntoUrl, RequestBuilder},
|
||||
secrecy::{ExposeSecret, SecretString},
|
||||
};
|
||||
|
||||
#[cfg(feature = "mock")]
|
||||
mod mock;
|
||||
#[cfg(feature = "mock")]
|
||||
pub use mock::MuralPayMock;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Client {
|
||||
pub http: reqwest::Client,
|
||||
pub api_url: String,
|
||||
pub api_key: SecretString,
|
||||
pub transfer_api_key: SecretString,
|
||||
#[cfg(feature = "mock")]
|
||||
pub mock: std::sync::Arc<arc_swap::ArcSwapOption<mock::MuralPayMock>>,
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(
|
||||
api_url: impl Into<String>,
|
||||
api_key: impl Into<SecretString>,
|
||||
transfer_api_key: impl Into<SecretString>,
|
||||
) -> Self {
|
||||
Self {
|
||||
http: reqwest::Client::new(),
|
||||
api_url: api_url.into(),
|
||||
api_key: api_key.into(),
|
||||
transfer_api_key: transfer_api_key.into(),
|
||||
#[cfg(feature = "mock")]
|
||||
mock: std::sync::Arc::new(arc_swap::ArcSwapOption::empty()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a client which mocks responses.
|
||||
#[cfg(feature = "mock")]
|
||||
#[must_use]
|
||||
pub fn from_mock(mock: mock::MuralPayMock) -> Self {
|
||||
Self {
|
||||
http: reqwest::Client::new(),
|
||||
api_url: String::new(),
|
||||
api_key: SecretString::from(String::new()),
|
||||
transfer_api_key: SecretString::from(String::new()),
|
||||
mock: std::sync::Arc::new(arc_swap::ArcSwapOption::from_pointee(mock)),
|
||||
}
|
||||
}
|
||||
|
||||
fn http_req(&self, make_req: impl FnOnce() -> RequestBuilder) -> RequestBuilder {
|
||||
make_req()
|
||||
.bearer_auth(self.api_key.expose_secret())
|
||||
.header("accept", "application/json")
|
||||
.header("content-type", "application/json")
|
||||
}
|
||||
|
||||
pub(crate) fn http_get<U: IntoUrl>(&self, make_url: impl FnOnce(&str) -> U) -> RequestBuilder {
|
||||
self.http_req(|| self.http.get(make_url(&self.api_url)))
|
||||
}
|
||||
|
||||
pub(crate) fn http_post<U: IntoUrl>(&self, make_url: impl FnOnce(&str) -> U) -> RequestBuilder {
|
||||
self.http_req(|| self.http.post(make_url(&self.api_url)))
|
||||
}
|
||||
|
||||
pub(crate) fn http_put<U: IntoUrl>(&self, make_url: impl FnOnce(&str) -> U) -> RequestBuilder {
|
||||
self.http_req(|| self.http.put(make_url(&self.api_url)))
|
||||
}
|
||||
|
||||
pub(crate) fn http_delete<U: IntoUrl>(
|
||||
&self,
|
||||
make_url: impl FnOnce(&str) -> U,
|
||||
) -> RequestBuilder {
|
||||
self.http_req(|| self.http.delete(make_url(&self.api_url)))
|
||||
}
|
||||
|
||||
pub async fn health(&self) -> reqwest::Result<()> {
|
||||
self.http_get(|base| format!("{base}/api/health"))
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RequestExt: Sized {
|
||||
#[must_use]
|
||||
fn transfer_auth(self, client: &Client) -> Self;
|
||||
|
||||
fn send_mural<T: DeserializeOwned>(
|
||||
self,
|
||||
) -> impl Future<Output = crate::Result<T>> + Send + Sync;
|
||||
}
|
||||
|
||||
const HEADER_TRANSFER_API_KEY: &str = "transfer-api-key";
|
||||
|
||||
impl RequestExt for reqwest::RequestBuilder {
|
||||
fn transfer_auth(self, client: &Client) -> Self {
|
||||
self.header(
|
||||
HEADER_TRANSFER_API_KEY,
|
||||
client.transfer_api_key.expose_secret(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn send_mural<T: DeserializeOwned>(self) -> crate::Result<T> {
|
||||
let resp = self.send().await?;
|
||||
let status = resp.status();
|
||||
if status.is_client_error() || status.is_server_error() {
|
||||
let json = resp.bytes().await?;
|
||||
let err = serde_json::from_slice::<ApiError>(&json)
|
||||
.map_err(|source| MuralError::DecodeError { source, json })?;
|
||||
Err(MuralError::Api(err))
|
||||
} else {
|
||||
let json = resp.bytes().await?;
|
||||
let t = serde_json::from_slice::<T>(&json)
|
||||
.map_err(|source| MuralError::Decode { source, json })?;
|
||||
Ok(t)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user