Allows to define DID resolver test sans I/O. Forge resolver TBD.
3OWFX33CH6MBXWI5QCIOO5AYKTW7Y4S2VAIRTW2W6JEOT66KW63QC
F4U537ERR3UINFAH3KAG32R4YXITF2BERC7AWXHJU662L5MZYMMAC
TCHBIEEALVTB67SZZT5RI7DPOCIXF25JXFXLTILFQ5K3ZB3PB7JQC
PHZUP2AONLUTA5FYRFTBGGZ23JWRADCREFLOT6IXGBY4S2XI7QIAC
QZ5HE3YCM4QPKQDF2XUHI7IASJ5PYGMKTF4JVWM3TLRIWGEIM5QQC
H3V4EM4IBB7YSJV63CXX33B4FJFXNGH2QYQSBKDC7HLMDPBQ2H5QC
HGDC5V5PNOXYZ4NM66GEQ2AQZ7FHQ4FYWTSFV3Z5Y3MTFVKAJXGQC
U63IONGETCB4NUGTSWYUJQMZNR5O6ZZKIPPBYJPBGVLAFXSFDNOAC
LSM3P7VKAWMCE4VP5F6MU72N6JSSBUX5W5ITE54Z7WSQRUVU2SRQC
7AUCC3IQYJ2XOWFH7S66JOW2KKRMRS4IGBDGPNTSUBXBU3LNEWEAC
Platform::Github => Github::new(username.as_ref(), client).resolve().await?,
Platform::Gitlab => Gitlab::new(username.as_ref(), client).resolve().await?,
Platform::Github => Github::new(username.as_ref()).resolve(client).await?,
Platform::Gitlab => Gitlab::new(username.as_ref()).resolve(client).await?,
async fn resolve(&self) -> Result<Keys, Self::Error> {
tokio::try_join!(self.resolve_ssh(), self.resolve_gpg()).map(|(ssh, gpg)| Keys { ssh, gpg })
async fn resolve<C: http::Client + Send>(&self, client: C) -> Result<Keys, Self::Error> {
tokio::try_join!(self.resolve_ssh(&client), self.resolve_gpg(&client))
.map(|(ssh, gpg)| Keys { ssh, gpg })
}
#[async_trait]
pub trait ResponseExt: Sized {
async fn succeed(self) -> anyhow::Result<Self>;
}
#[async_trait]
impl ResponseExt for Response {
async fn succeed(self) -> anyhow::Result<Self> {
debug!("{}", self.status());
if !self.status().is_success() {
debug!("{:#?}", self.headers());
let body = self.text().await?;
bail!(body)
}
Ok(self)
}
let doc = super::create(did.clone(), ed25519::KeyPair::generate().pk);
let client = http::mock_client(|url| {
assert_eq!(url, Url::parse("https://eagain.io/kim/did.json").unwrap());
let body = serde_json::to_string(&doc)
.map(reqwest::Body::from)
.unwrap();
::http::Response::builder()
.status(http::StatusCode::OK)
.header("Content-Type", "application/json")
.body(body)
.unwrap()
});
// Copyright © 2023 Kim Altintop <kim@eagain.io>
// SPDX-License-Identifier: GPL-2.0-only
pub mod http;
// Copyright © 2023 Kim Altintop <kim@eagain.io>
// SPDX-License-Identifier: GPL-2.0-only
use anyhow::bail;
use async_trait::async_trait;
use serde::de::DeserializeOwned;
use tracing::debug;
use url::Url;
pub use http::{
HeaderMap,
StatusCode,
};
pub type Result<T> = anyhow::Result<T>;
pub trait IntoUrl: Send {
fn into_url(self) -> Result<Url>;
}
impl IntoUrl for Url {
fn into_url(self) -> Result<Url> {
Ok(self)
}
}
impl<'a> IntoUrl for &'a str {
fn into_url(self) -> Result<Url> {
Ok(Url::parse(self)?)
}
}
impl IntoUrl for String {
fn into_url(self) -> Result<Url> {
Ok(Url::parse(self.as_str())?)
}
}
#[async_trait]
pub trait Client: Send + Sync {
type Response: Response;
async fn get<U: IntoUrl>(&self, url: U) -> Result<Self::Response>;
}
#[async_trait]
pub trait Response: Send + Sync {
async fn text(self) -> Result<String>;
async fn json<T: DeserializeOwned>(self) -> Result<T>;
fn status(&self) -> StatusCode;
fn content_length(&self) -> Option<u64>;
fn headers(&self) -> &HeaderMap;
}
#[async_trait]
impl Client for reqwest::Client {
type Response = reqwest::Response;
async fn get<U: IntoUrl>(&self, url: U) -> Result<Self::Response> {
let resp = self.get(url.into_url()?).send().await?;
let status = resp.status();
debug!("{status}");
if !status.is_success() {
debug!("{:#?}", resp.headers());
let body = resp.text().await?;
bail!(body);
}
Ok(resp)
}
}
#[async_trait]
impl Response for reqwest::Response {
async fn text(self) -> Result<String> {
Ok(self.text().await?)
}
async fn json<T: DeserializeOwned>(self) -> Result<T> {
Ok(self.json().await?)
}
fn status(&self) -> StatusCode {
self.status()
}
fn content_length(&self) -> Option<u64> {
self.content_length()
}
fn headers(&self) -> &HeaderMap {
self.headers()
}
}
/// Create an HTTP client with default configuration.
pub fn client() -> impl Client {
static UA: &str = concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION"));
reqwest::Client::builder().user_agent(UA).build().unwrap()
}
#[cfg(any(test, feature = "testing"))]
pub fn mock_client<F>(f: F) -> impl Client
where
F: Fn(Url) -> ::http::Response<reqwest::Body> + Send + Sync,
{
struct C<F> {
respond: F,
}
#[async_trait]
impl<F> Client for C<F>
where
F: Fn(Url) -> ::http::Response<reqwest::Body> + Send + Sync,
{
type Response = reqwest::Response;
async fn get<U: IntoUrl>(&self, url: U) -> Result<Self::Response> {
let url = url.into_url()?;
Ok(Self::Response::from((self.respond)(url)))
}
}
C { respond: f }
}
[package]
name = "yapma-common"
version = "0.1.0"
authors.workspace = true
license.workspace = true
rust-version.workspace = true
edition.workspace = true
[features]
testing = []
[dependencies]
anyhow.workspace = true
async-trait.workspace = true
http.workspace = true
reqwest.workspace = true
serde.workspace = true
tracing.workspace = true
url.workspace = true
.git
.DS_Store
# Added by cargo
/target
/Cargo.lock