use std::{ops::Deref, sync::Arc};
use endhost_api::routes::{
ENDHOST_API_V1, LIST_SEGMENTS, LIST_UNDERLAYS, SEGMENTS_SERVICE, UNDERLAY_SERVICE,
};
use endhost_api_models::underlays::Underlays;
use endhost_api_protobuf::v1::{
ListSegmentsRequest, ListSegmentsResponse, ListUnderlaysRequest, ListUnderlaysResponse,
};
use scion_sdk_reqwest_connect_rpc::{
client::{CrpcClient, CrpcClientError},
token_source::TokenSource,
};
use sciparse::{
identifier::isd_asn::IsdAsn,
segment::{SegmentsPage, rpc::InvalidSegmentError},
};
#[async_trait::async_trait]
pub trait EndhostApiClient: Send + Sync {
async fn list_underlays(&self, isd_as: IsdAsn) -> Result<Underlays, CrpcClientError>;
async fn list_segments(
&self,
src: IsdAsn,
dst: IsdAsn,
page_size: i32,
page_token: String,
) -> Result<SegmentsPage, CrpcClientError>;
}
pub struct CrpcEndhostApiClient {
client: CrpcClient,
}
impl Deref for CrpcEndhostApiClient {
type Target = CrpcClient;
fn deref(&self) -> &Self::Target {
&self.client
}
}
impl CrpcEndhostApiClient {
pub fn new(base_url: &url::Url) -> anyhow::Result<Self> {
Ok(CrpcEndhostApiClient {
client: CrpcClient::new(base_url)?,
})
}
pub fn new_with_client(base_url: &url::Url, client: reqwest::Client) -> anyhow::Result<Self> {
Ok(CrpcEndhostApiClient {
client: CrpcClient::new_with_client(base_url, client)?,
})
}
pub fn use_token_source(&mut self, token_source: Arc<dyn TokenSource>) -> &mut Self {
self.client.use_token_source(token_source);
self
}
}
#[async_trait::async_trait]
impl EndhostApiClient for CrpcEndhostApiClient {
async fn list_underlays(&self, isd_as: IsdAsn) -> Result<Underlays, CrpcClientError> {
self.client
.unary_request::<ListUnderlaysRequest, ListUnderlaysResponse>(
&format!("{ENDHOST_API_V1}.{UNDERLAY_SERVICE}{LIST_UNDERLAYS}"),
ListUnderlaysRequest {
isd_as: Some(isd_as.into()),
},
)
.await?
.try_into()
.map_err(|e: url::ParseError| {
CrpcClientError::DecodeError {
context: "parsing underlay address as URL".into(),
source: Some(e.into()),
body: None,
}
})
.inspect(|resp| {
tracing::debug!(%resp, "Listed underlays");
})
}
async fn list_segments(
&self,
src: IsdAsn,
dst: IsdAsn,
page_size: i32,
page_token: String,
) -> Result<SegmentsPage, CrpcClientError> {
self.client
.unary_request::<ListSegmentsRequest, ListSegmentsResponse>(
&format!("{ENDHOST_API_V1}.{SEGMENTS_SERVICE}{LIST_SEGMENTS}"),
ListSegmentsRequest {
src_isd_as: src.0,
dst_isd_as: dst.0,
page_size,
page_token,
},
)
.await?
.try_into()
.map_err(|e: InvalidSegmentError| {
CrpcClientError::DecodeError {
context: "decoding segments".into(),
source: Some(e.into()),
body: None,
}
})
.inspect(|resp: &SegmentsPage| {
tracing::debug!(
core=?resp.segments.core_segments.len(),
down=?resp.segments.down_segments.len(),
up=?resp.segments.up_segments.len(),
"Listed segments"
);
})
}
}