use apolloconfig_sig::AuthSignature;
use http_api_client_endpoint::{
http::{
header::{ACCEPT, USER_AGENT},
Method, StatusCode,
},
Body, Endpoint, Request, Response, MIME_APPLICATION_JSON,
};
use serde_json::{Map, Value};
use url::Url;
use crate::{
endpoints::{
common::{EndpointError, EndpointRet},
CLUSTER_NAME_DEFAULT, NAMESPACE_NAME_DEFAULT,
},
objects::{
configurations::{Configurations, ConfigurationsFromMapError},
ResponseBodyErrJson,
},
types::NamespaceFormat,
};
pub type ConfigfilesJsonContents = Vec<u8>;
#[derive(Debug, Clone)]
pub struct ConfigfilesJson {
pub config_server_url: Box<str>,
pub access_key_secret: Box<str>,
pub app_id: Box<str>,
pub cluster_name: Option<Box<str>>,
pub namespace_name: Option<Box<str>>,
pub client_ip: Option<Box<str>>,
}
impl ConfigfilesJson {
pub fn new(
config_server_url: impl AsRef<str>,
access_key_secret: impl AsRef<str>,
app_id: impl AsRef<str>,
) -> Self {
Self {
config_server_url: config_server_url.as_ref().into(),
access_key_secret: access_key_secret.as_ref().into(),
app_id: app_id.as_ref().into(),
cluster_name: None,
namespace_name: None,
client_ip: None,
}
}
pub fn cluster_name(mut self, value: impl AsRef<str>) -> Self {
self.cluster_name = Some(value.as_ref().into());
self
}
pub fn namespace_name(mut self, value: impl AsRef<str>) -> Self {
self.namespace_name = Some(value.as_ref().into());
self
}
pub fn client_ip(mut self, value: impl AsRef<str>) -> Self {
self.client_ip = Some(value.as_ref().into());
self
}
}
impl Endpoint for ConfigfilesJson {
type RenderRequestError = EndpointError;
type ParseResponseOutput =
EndpointRet<(ConfigfilesJsonConfigurations, ConfigfilesJsonContents)>;
type ParseResponseError = EndpointError;
fn render_request(&self) -> Result<Request<Body>, Self::RenderRequestError> {
let mut url =
Url::parse(&self.config_server_url).map_err(EndpointError::MakeRequestUrlFailed)?;
let path = format!(
"/configfiles/json/{}/{}/{}",
self.app_id,
self.cluster_name.as_deref().unwrap_or(CLUSTER_NAME_DEFAULT),
self.namespace_name
.as_deref()
.unwrap_or(NAMESPACE_NAME_DEFAULT)
);
url.set_path(path.as_str());
if let Some(client_ip) = &self.client_ip {
url.query_pairs_mut()
.append_pair("ip", client_ip.to_string().as_str());
}
let auth_headers = AuthSignature
.http_headers(url.as_str(), &self.app_id, &self.access_key_secret)
.map_err(EndpointError::MakeSignatureFailed)?;
let request_builder = Request::builder()
.method(Method::GET)
.uri(url.as_str())
.header(USER_AGENT, "apolloconfig-rs")
.header(ACCEPT, MIME_APPLICATION_JSON);
let request_builder = auth_headers
.into_iter()
.flat_map(|(k, v)| k.map(|k| (k, v)))
.fold(request_builder, |request_builder, (k, v)| {
request_builder.header(k, v)
});
let request = request_builder
.body(vec![])
.map_err(EndpointError::MakeRequestFailed)?;
Ok(request)
}
fn parse_response(
&self,
response: Response<Body>,
) -> Result<Self::ParseResponseOutput, Self::ParseResponseError> {
let status = response.status();
match status {
StatusCode::OK => Ok(EndpointRet::Ok((
ConfigfilesJsonConfigurations::from_contents(
response.body(),
self.namespace_name.as_deref(),
)
.map_err(EndpointError::DeResponseBodyOkJsonFailed)?,
response.body().to_owned(),
))),
status => match serde_json::from_slice::<ResponseBodyErrJson>(response.body()) {
Ok(err_json) => Ok(EndpointRet::Other((status, Ok(err_json)))),
Err(_) => Ok(EndpointRet::Other((
status,
Err(response.body().to_owned()),
))),
},
}
}
}
#[derive(Debug, Clone)]
pub struct ConfigfilesJsonConfigurations {
pub namespace_name: Box<str>,
configurations: Map<String, Value>,
}
impl ConfigfilesJsonConfigurations {
pub fn configurations(&self) -> Result<Configurations, ConfigurationsFromMapError> {
Configurations::from_map(
&self.configurations,
NamespaceFormat::from_namespace_name(Some(&self.namespace_name)),
)
}
pub fn from_contents(
contents: impl AsRef<[u8]>,
namespace_name: Option<&str>,
) -> Result<Self, serde_json::Error> {
let contents = contents.as_ref();
serde_json::from_slice(contents).map(|x| Self {
namespace_name: namespace_name.unwrap_or(NAMESPACE_NAME_DEFAULT).into(),
configurations: x,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_configurations_from_contents() {
let content = include_str!(
"../../tests/response_body_json_files/configfiles_json_application_ok.json"
);
match ConfigfilesJsonConfigurations::from_contents(content.as_bytes(), None) {
Ok(c) => {
let c = c.configurations().unwrap();
match &c {
Configurations::Properties(m) => {
assert_eq!(m.get("content").unwrap().as_str().unwrap(), "content");
}
_ => panic!("{:?}", c),
}
}
Err(err) => panic!("{}", err),
}
let content = include_str!(
"../../tests/response_body_json_files/configfiles_json_namespace_json_ok.json"
);
match ConfigfilesJsonConfigurations::from_contents(
content.as_bytes(),
Some("my_namespace.json"),
) {
Ok(c) => {
let c = c.configurations().unwrap();
match &c {
Configurations::Json(v) => {
assert_eq!(
v.as_object()
.unwrap()
.get("firstName")
.unwrap()
.as_str()
.unwrap(),
"John"
);
}
_ => panic!("{:?}", c),
}
}
Err(err) => panic!("{}", err),
}
}
}