From d2f50808ef9b96afc36d864adec74f10c9cea9af Mon Sep 17 00:00:00 2001 From: Bryan Newbold Date: Tue, 19 Jun 2018 18:31:55 -0700 Subject: implement (most) of stats endpoint --- rust/fatcat-api/src/client.rs | 51 +++++++++++++++++++++++++++++++++- rust/fatcat-api/src/lib.rs | 16 +++++++++++ rust/fatcat-api/src/mimetypes.rs | 8 ++++++ rust/fatcat-api/src/models.rs | 13 +++++++++ rust/fatcat-api/src/server.rs | 60 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 146 insertions(+), 2 deletions(-) (limited to 'rust/fatcat-api/src') diff --git a/rust/fatcat-api/src/client.rs b/rust/fatcat-api/src/client.rs index c5c4a320..809fd1d6 100644 --- a/rust/fatcat-api/src/client.rs +++ b/rust/fatcat-api/src/client.rs @@ -36,7 +36,7 @@ use swagger::{ApiError, Context, XSpanId}; use models; use {AcceptEditgroupResponse, Api, CreateContainerBatchResponse, CreateContainerResponse, CreateCreatorBatchResponse, CreateCreatorResponse, CreateEditgroupResponse, CreateFileBatchResponse, CreateFileResponse, CreateReleaseBatchResponse, CreateReleaseResponse, CreateWorkBatchResponse, CreateWorkResponse, GetContainerResponse, GetCreatorReleasesResponse, GetCreatorResponse, - GetEditgroupResponse, GetEditorChangelogResponse, GetEditorResponse, GetFileResponse, GetReleaseFilesResponse, GetReleaseResponse, GetWorkReleasesResponse, GetWorkResponse, + GetEditgroupResponse, GetEditorChangelogResponse, GetEditorResponse, GetFileResponse, GetReleaseFilesResponse, GetReleaseResponse, GetStatsResponse, GetWorkReleasesResponse, GetWorkResponse, LookupContainerResponse, LookupCreatorResponse, LookupFileResponse, LookupReleaseResponse}; /// Convert input into a base path, e.g. "http://example:123". Also checks the scheme as it goes. @@ -1464,6 +1464,55 @@ impl Api for Client { Box::new(futures::done(result)) } + fn get_stats(&self, param_more: Option, context: &Context) -> Box + Send> { + // Query parameters + let query_more = param_more.map_or_else(String::new, |query| format!("more={more}&", more = query.to_string())); + + let url = format!("{}/v0/stats?{more}", self.base_path, more = utf8_percent_encode(&query_more, QUERY_ENCODE_SET)); + + let hyper_client = (self.hyper_client)(); + let request = hyper_client.request(hyper::method::Method::Get, &url); + let mut custom_headers = hyper::header::Headers::new(); + + context.x_span_id.as_ref().map(|header| custom_headers.set(XSpanId(header.clone()))); + + let request = request.headers(custom_headers); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn parse_response(mut response: hyper::client::response::Response) -> Result { + match response.status.to_u16() { + 200 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::(&buf)?; + + Ok(GetStatsResponse::Success(body)) + } + 0 => { + let mut buf = String::new(); + response.read_to_string(&mut buf).map_err(|e| ApiError(format!("Response was not valid UTF8: {}", e)))?; + let body = serde_json::from_str::(&buf)?; + + Ok(GetStatsResponse::GenericError(body)) + } + code => { + let mut buf = [0; 100]; + let debug_body = match response.read(&mut buf) { + Ok(len) => match str::from_utf8(&buf[..len]) { + Ok(body) => Cow::from(body), + Err(_) => Cow::from(format!("", &buf[..len].to_vec())), + }, + Err(e) => Cow::from(format!("", e)), + }; + Err(ApiError(format!("Unexpected response code {}:\n{:?}\n\n{}", code, response.headers, debug_body))) + } + } + } + + let result = request.send().map_err(|e| ApiError(format!("No response received: {}", e))).and_then(parse_response); + Box::new(futures::done(result)) + } + fn get_work(&self, param_id: String, context: &Context) -> Box + Send> { let url = format!("{}/v0/work/{id}", self.base_path, id = utf8_percent_encode(¶m_id.to_string(), PATH_SEGMENT_ENCODE_SET)); diff --git a/rust/fatcat-api/src/lib.rs b/rust/fatcat-api/src/lib.rs index c926966e..fd0cfe54 100644 --- a/rust/fatcat-api/src/lib.rs +++ b/rust/fatcat-api/src/lib.rs @@ -278,6 +278,14 @@ pub enum GetReleaseFilesResponse { GenericError(models::ErrorResponse), } +#[derive(Debug, PartialEq)] +pub enum GetStatsResponse { + /// Success + Success(models::StatsResponse), + /// Generic Error + GenericError(models::ErrorResponse), +} + #[derive(Debug, PartialEq)] pub enum GetWorkResponse { /// Found Entity @@ -394,6 +402,8 @@ pub trait Api { fn get_release_files(&self, id: String, context: &Context) -> Box + Send>; + fn get_stats(&self, more: Option, context: &Context) -> Box + Send>; + fn get_work(&self, id: String, context: &Context) -> Box + Send>; fn get_work_releases(&self, id: String, context: &Context) -> Box + Send>; @@ -451,6 +461,8 @@ pub trait ApiNoContext { fn get_release_files(&self, id: String) -> Box + Send>; + fn get_stats(&self, more: Option) -> Box + Send>; + fn get_work(&self, id: String) -> Box + Send>; fn get_work_releases(&self, id: String) -> Box + Send>; @@ -564,6 +576,10 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> { self.api().get_release_files(id, &self.context()) } + fn get_stats(&self, more: Option) -> Box + Send> { + self.api().get_stats(more, &self.context()) + } + fn get_work(&self, id: String) -> Box + Send> { self.api().get_work(id, &self.context()) } diff --git a/rust/fatcat-api/src/mimetypes.rs b/rust/fatcat-api/src/mimetypes.rs index 68e5ec04..be7e792f 100644 --- a/rust/fatcat-api/src/mimetypes.rs +++ b/rust/fatcat-api/src/mimetypes.rs @@ -328,6 +328,14 @@ pub mod responses { lazy_static! { pub static ref GET_RELEASE_FILES_GENERIC_ERROR: Mime = mime!(Application / Json); } + /// Create Mime objects for the response content types for GetStats + lazy_static! { + pub static ref GET_STATS_SUCCESS: Mime = mime!(Application / Json); + } + /// Create Mime objects for the response content types for GetStats + lazy_static! { + pub static ref GET_STATS_GENERIC_ERROR: Mime = mime!(Application / Json); + } /// Create Mime objects for the response content types for GetWork lazy_static! { pub static ref GET_WORK_FOUND_ENTITY: Mime = mime!(Application / Json); diff --git a/rust/fatcat-api/src/models.rs b/rust/fatcat-api/src/models.rs index 140e0c2e..15247ce6 100644 --- a/rust/fatcat-api/src/models.rs +++ b/rust/fatcat-api/src/models.rs @@ -617,6 +617,19 @@ impl ReleaseRef { } } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct StatsResponse { + #[serde(rename = "extra")] + #[serde(skip_serializing_if = "Option::is_none")] + pub extra: Option, +} + +impl StatsResponse { + pub fn new() -> StatsResponse { + StatsResponse { extra: None } + } +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Success { #[serde(rename = "message")] diff --git a/rust/fatcat-api/src/server.rs b/rust/fatcat-api/src/server.rs index 2716dfc5..7eb73122 100644 --- a/rust/fatcat-api/src/server.rs +++ b/rust/fatcat-api/src/server.rs @@ -38,7 +38,7 @@ use swagger::{ApiError, Context, XSpanId}; use models; use {AcceptEditgroupResponse, Api, CreateContainerBatchResponse, CreateContainerResponse, CreateCreatorBatchResponse, CreateCreatorResponse, CreateEditgroupResponse, CreateFileBatchResponse, CreateFileResponse, CreateReleaseBatchResponse, CreateReleaseResponse, CreateWorkBatchResponse, CreateWorkResponse, GetContainerResponse, GetCreatorReleasesResponse, GetCreatorResponse, - GetEditgroupResponse, GetEditorChangelogResponse, GetEditorResponse, GetFileResponse, GetReleaseFilesResponse, GetReleaseResponse, GetWorkReleasesResponse, GetWorkResponse, + GetEditgroupResponse, GetEditorChangelogResponse, GetEditorResponse, GetFileResponse, GetReleaseFilesResponse, GetReleaseResponse, GetStatsResponse, GetWorkReleasesResponse, GetWorkResponse, LookupContainerResponse, LookupCreatorResponse, LookupFileResponse, LookupReleaseResponse}; header! { (Warning, "Warning") => [String] } @@ -2099,6 +2099,64 @@ where "GetReleaseFiles", ); + let api_clone = api.clone(); + router.get( + "/v0/stats", + move |req: &mut Request| { + let mut context = Context::default(); + + // Helper function to provide a code block to use `?` in (to be replaced by the `catch` block when it exists). + fn handle_request(req: &mut Request, api: &T, context: &mut Context) -> Result + where + T: Api, + { + context.x_span_id = Some(req.headers.get::().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string())); + context.auth_data = req.extensions.remove::(); + context.authorization = req.extensions.remove::(); + + // Query parameters (note that non-required or collection query parameters will ignore garbage values, rather than causing a 400 response) + let query_params = req.get::().unwrap_or_default(); + let param_more = query_params.get("more").and_then(|list| list.first()).and_then(|x| x.parse::().ok()); + + match api.get_stats(param_more, context).wait() { + Ok(rsp) => match rsp { + GetStatsResponse::Success(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(200), body_string)); + response.headers.set(ContentType(mimetypes::responses::GET_STATS_SUCCESS.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + + Ok(response) + } + GetStatsResponse::GenericError(body) => { + let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize"); + + let mut response = Response::with((status::Status::from_u16(0), body_string)); + response.headers.set(ContentType(mimetypes::responses::GET_STATS_GENERIC_ERROR.clone())); + + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + + Ok(response) + } + }, + Err(_) => { + // Application code returned an error. This should not happen, as the implementation should + // return a valid response. + Err(Response::with((status::InternalServerError, "An internal error occurred".to_string()))) + } + } + } + + handle_request(req, &api_clone, &mut context).or_else(|mut response| { + context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone()))); + Ok(response) + }) + }, + "GetStats", + ); + let api_clone = api.clone(); router.get( "/v0/work/:id", -- cgit v1.2.3