use prost::Message;
use reqwest;
use reqwest::header::CONTENT_TYPE;
use reqwest::Client;
use std::default::Default;
use crate::error::VssError;
use crate::types::{
DeleteObjectRequest, DeleteObjectResponse, GetObjectRequest, GetObjectResponse, ListKeyVersionsRequest,
ListKeyVersionsResponse, PutObjectRequest, PutObjectResponse,
};
use crate::util::retry::{retry, RetryPolicy};
const APPLICATION_OCTET_STREAM: &str = "application/octet-stream";
#[derive(Clone)]
pub struct VssClient<R>
where
R: RetryPolicy<E = VssError>,
{
base_url: String,
client: Client,
retry_policy: R,
}
impl<R: RetryPolicy<E = VssError>> VssClient<R> {
pub fn new(base_url: &str, retry_policy: R) -> Self {
let client = Client::new();
Self::from_client(base_url, client, retry_policy)
}
pub fn from_client(base_url: &str, client: Client, retry_policy: R) -> Self {
Self { base_url: String::from(base_url), client, retry_policy }
}
pub fn base_url(&self) -> &str {
&self.base_url
}
pub async fn get_object(&self, request: &GetObjectRequest) -> Result<GetObjectResponse, VssError> {
retry(
|| async {
let url = format!("{}/getObject", self.base_url);
self.post_request(request, &url).await.and_then(|response: GetObjectResponse| {
if response.value.is_none() {
Err(VssError::InternalServerError(
"VSS Server API Violation, expected value in GetObjectResponse but found none".to_string(),
))
} else {
Ok(response)
}
})
},
&self.retry_policy,
)
.await
}
pub async fn put_object(&self, request: &PutObjectRequest) -> Result<PutObjectResponse, VssError> {
retry(
|| async {
let url = format!("{}/putObjects", self.base_url);
self.post_request(request, &url).await
},
&self.retry_policy,
)
.await
}
pub async fn delete_object(&self, request: &DeleteObjectRequest) -> Result<DeleteObjectResponse, VssError> {
retry(
|| async {
let url = format!("{}/deleteObject", self.base_url);
self.post_request(request, &url).await
},
&self.retry_policy,
)
.await
}
pub async fn list_key_versions(
&self, request: &ListKeyVersionsRequest,
) -> Result<ListKeyVersionsResponse, VssError> {
retry(
|| async {
let url = format!("{}/listKeyVersions", self.base_url);
self.post_request(request, &url).await
},
&self.retry_policy,
)
.await
}
async fn post_request<Rq: Message, Rs: Message + Default>(&self, request: &Rq, url: &str) -> Result<Rs, VssError> {
let request_body = request.encode_to_vec();
let response_raw = self
.client
.post(url)
.header(CONTENT_TYPE, APPLICATION_OCTET_STREAM)
.body(request_body)
.send()
.await?;
let status = response_raw.status();
let payload = response_raw.bytes().await?;
if status.is_success() {
let response = Rs::decode(&payload[..])?;
Ok(response)
} else {
Err(VssError::new(status, payload))
}
}
}