diff --git a/src/api.rs b/src/api.rs index a23df0a..b6d6c83 100644 --- a/src/api.rs +++ b/src/api.rs @@ -128,8 +128,8 @@ pub enum Endpoint { impl Endpoint { pub fn as_str(&self) -> &str { match self { - Endpoint::ApiKeyAccessSecret => "/api-key-access/secret/", - Endpoint::ApiKeyInspect => "/api-key-access/inspect/", + Endpoint::ApiKeyAccessSecret => "api-key-access/secret/", + Endpoint::ApiKeyInspect => "api-key-access/inspect/", } } } @@ -274,16 +274,32 @@ pub fn make_request( Ok(vec) } +fn build_endpoint_url(server_url: &Url, endpoint: &Endpoint) -> Result { + let mut endpoint_url = server_url.clone(); + + endpoint_url.set_path(&server_url.path().trim_end_matches('/')); + + for segment in endpoint.as_str().split("/") { + endpoint_url + .path_segments_mut() + .map_err(|_| anyhow!("cannot create endpoint url from server_url and endpoint path"))? + .pop_if_empty() + .push(segment.trim_start_matches("/")); + } + + Ok(endpoint_url) +} + fn call_route(server_url: &Url, http_options: &HttpOptions, route: Route) -> Result> where T: Serialize, { - let url = format!("{}/{}", server_url, route.endpoint.as_str()); - let url_parsed = Url::parse(&url).context("url parsing error")?; + let endpoint_url = + build_endpoint_url(server_url, &route.endpoint).context("building endpoint url failed")?; let body = serde_json::to_string(&route.body)?; - let response_raw: Vec = make_request(http_options, url_parsed, route.method, Some(body)) + let response_raw: Vec = make_request(http_options, endpoint_url, route.method, Some(body)) .context("make request failed")?; Ok(response_raw) @@ -964,6 +980,8 @@ pub fn api_key_get_secrets(config: &Config) -> Result> { #[cfg(test)] mod tests { + use std::str::FromStr; + use lazy_static::lazy_static; use super::*; @@ -1057,6 +1075,62 @@ mod tests { ); } + #[test] + #[allow(non_snake_case)] + fn build_url_simple_server_build_valid() { + let expected = Url::from_str("https://psono.pw/api-key-access/secret/").unwrap(); + + let server_url = Url::from_str("https://psono.pw").unwrap(); + + let endpoint_url = build_endpoint_url(&server_url, &Endpoint::ApiKeyAccessSecret); + + assert!(endpoint_url.is_ok()); + + assert_eq!(endpoint_url.unwrap(), expected); + } + + #[test] + #[allow(non_snake_case)] + fn build_url_no_trailing_slash_build_valid() { + let expected = Url::from_str("https://psono.pw/backend/api-key-access/secret/").unwrap(); + + let server_url = Url::from_str("https://psono.pw/backend").unwrap(); + + let endpoint_url = build_endpoint_url(&server_url, &Endpoint::ApiKeyAccessSecret); + + assert!(endpoint_url.is_ok()); + + assert_eq!(endpoint_url.unwrap(), expected); + } + + #[test] + #[allow(non_snake_case)] + fn build_url_one_trailing_slash_build_valid() { + let expected = Url::from_str("https://psono.pw/backend/api-key-access/secret/").unwrap(); + + let server_url = Url::from_str("https://psono.pw/backend/").unwrap(); + + let endpoint_url = build_endpoint_url(&server_url, &Endpoint::ApiKeyAccessSecret); + + assert!(endpoint_url.is_ok()); + + assert_eq!(endpoint_url.unwrap(), expected); + } + + #[test] + #[allow(non_snake_case)] + fn build_url_many_trailing_slashes_build_valid() { + let expected = Url::from_str("https://psono.pw/backend/api-key-access/secret/").unwrap(); + + let server_url = Url::from_str("https://psono.pw/backend//").unwrap(); + + let endpoint_url = build_endpoint_url(&server_url, &Endpoint::ApiKeyAccessSecret); + + assert!(endpoint_url.is_ok()); + + assert_eq!(endpoint_url.unwrap(), expected); + } + #[test] #[allow(non_snake_case)] fn load_root_certificate__pem_success() {