summaryrefslogtreecommitdiffstats
path: root/rust/fatcat-api/src
diff options
context:
space:
mode:
authorBryan Newbold <bnewbold@robocracy.org>2018-06-30 17:34:22 -0700
committerBryan Newbold <bnewbold@robocracy.org>2018-06-30 17:34:22 -0700
commitf4064c19ec140987e15c64e80a3bf0c70025b31b (patch)
treee89548d5d5f350f65e9c294a34c71a2225a14099 /rust/fatcat-api/src
parent3ed7db573438d3620d295813a81237acb91155cb (diff)
downloadfatcat-f4064c19ec140987e15c64e80a3bf0c70025b31b.tar.gz
fatcat-f4064c19ec140987e15c64e80a3bf0c70025b31b.zip
history for container entities
Diffstat (limited to 'rust/fatcat-api/src')
-rw-r--r--rust/fatcat-api/src/client.rs74
-rw-r--r--rust/fatcat-api/src/lib.rs20
-rw-r--r--rust/fatcat-api/src/mimetypes.rs16
-rw-r--r--rust/fatcat-api/src/models.rs69
-rw-r--r--rust/fatcat-api/src/server.rs98
5 files changed, 206 insertions, 71 deletions
diff --git a/rust/fatcat-api/src/client.rs b/rust/fatcat-api/src/client.rs
index 8c7e4ef3..02f0e155 100644
--- a/rust/fatcat-api/src/client.rs
+++ b/rust/fatcat-api/src/client.rs
@@ -35,9 +35,9 @@ 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, GetStatsResponse, GetWorkReleasesResponse, GetWorkResponse,
- LookupContainerResponse, LookupCreatorResponse, LookupFileResponse, LookupReleaseResponse};
+ CreateFileResponse, CreateReleaseBatchResponse, CreateReleaseResponse, CreateWorkBatchResponse, CreateWorkResponse, GetContainerHistoryResponse, GetContainerResponse,
+ GetCreatorReleasesResponse, GetCreatorResponse, 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.
fn into_base_path<T: IntoUrl>(input: T, correct_scheme: Option<&'static str>) -> Result<String, ClientInitError> {
@@ -990,6 +990,74 @@ impl Api for Client {
Box::new(futures::done(result))
}
+ fn get_container_history(&self, param_id: String, param_limit: Option<i64>, context: &Context) -> Box<Future<Item = GetContainerHistoryResponse, Error = ApiError> + Send> {
+ // Query parameters
+ let query_limit = param_limit.map_or_else(String::new, |query| format!("limit={limit}&", limit = query.to_string()));
+
+ let url = format!(
+ "{}/v0/container/{id}/history?{limit}",
+ self.base_path,
+ id = utf8_percent_encode(&param_id.to_string(), PATH_SEGMENT_ENCODE_SET),
+ limit = utf8_percent_encode(&query_limit, 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<GetContainerHistoryResponse, ApiError> {
+ 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::<Vec<models::EntityHistoryEntry>>(&buf)?;
+
+ Ok(GetContainerHistoryResponse::FoundEntityHistory(body))
+ }
+ 400 => {
+ 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::<models::ErrorResponse>(&buf)?;
+
+ Ok(GetContainerHistoryResponse::BadRequest(body))
+ }
+ 404 => {
+ 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::<models::ErrorResponse>(&buf)?;
+
+ Ok(GetContainerHistoryResponse::NotFound(body))
+ }
+ 500 => {
+ 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::<models::ErrorResponse>(&buf)?;
+
+ Ok(GetContainerHistoryResponse::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!("<Body was not UTF8: {:?}>", &buf[..len].to_vec())),
+ },
+ Err(e) => Cow::from(format!("<Failed to read body: {}>", 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_creator(&self, param_id: String, context: &Context) -> Box<Future<Item = GetCreatorResponse, Error = ApiError> + Send> {
let url = format!("{}/v0/creator/{id}", self.base_path, id = utf8_percent_encode(&param_id.to_string(), PATH_SEGMENT_ENCODE_SET));
diff --git a/rust/fatcat-api/src/lib.rs b/rust/fatcat-api/src/lib.rs
index f45e113e..291dcaf4 100644
--- a/rust/fatcat-api/src/lib.rs
+++ b/rust/fatcat-api/src/lib.rs
@@ -187,6 +187,18 @@ pub enum GetContainerResponse {
}
#[derive(Debug, PartialEq)]
+pub enum GetContainerHistoryResponse {
+ /// Found Entity History
+ FoundEntityHistory(Vec<models::EntityHistoryEntry>),
+ /// Bad Request
+ BadRequest(models::ErrorResponse),
+ /// Not Found
+ NotFound(models::ErrorResponse),
+ /// Generic Error
+ GenericError(models::ErrorResponse),
+}
+
+#[derive(Debug, PartialEq)]
pub enum GetCreatorResponse {
/// Found Entity
FoundEntity(models::CreatorEntity),
@@ -386,6 +398,8 @@ pub trait Api {
fn get_container(&self, id: String, context: &Context) -> Box<Future<Item = GetContainerResponse, Error = ApiError> + Send>;
+ fn get_container_history(&self, id: String, limit: Option<i64>, context: &Context) -> Box<Future<Item = GetContainerHistoryResponse, Error = ApiError> + Send>;
+
fn get_creator(&self, id: String, context: &Context) -> Box<Future<Item = GetCreatorResponse, Error = ApiError> + Send>;
fn get_creator_releases(&self, id: String, context: &Context) -> Box<Future<Item = GetCreatorReleasesResponse, Error = ApiError> + Send>;
@@ -445,6 +459,8 @@ pub trait ApiNoContext {
fn get_container(&self, id: String) -> Box<Future<Item = GetContainerResponse, Error = ApiError> + Send>;
+ fn get_container_history(&self, id: String, limit: Option<i64>) -> Box<Future<Item = GetContainerHistoryResponse, Error = ApiError> + Send>;
+
fn get_creator(&self, id: String) -> Box<Future<Item = GetCreatorResponse, Error = ApiError> + Send>;
fn get_creator_releases(&self, id: String) -> Box<Future<Item = GetCreatorReleasesResponse, Error = ApiError> + Send>;
@@ -544,6 +560,10 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> {
self.api().get_container(id, &self.context())
}
+ fn get_container_history(&self, id: String, limit: Option<i64>) -> Box<Future<Item = GetContainerHistoryResponse, Error = ApiError> + Send> {
+ self.api().get_container_history(id, limit, &self.context())
+ }
+
fn get_creator(&self, id: String) -> Box<Future<Item = GetCreatorResponse, Error = ApiError> + Send> {
self.api().get_creator(id, &self.context())
}
diff --git a/rust/fatcat-api/src/mimetypes.rs b/rust/fatcat-api/src/mimetypes.rs
index be7e792f..0384b319 100644
--- a/rust/fatcat-api/src/mimetypes.rs
+++ b/rust/fatcat-api/src/mimetypes.rs
@@ -208,6 +208,22 @@ pub mod responses {
lazy_static! {
pub static ref GET_CONTAINER_GENERIC_ERROR: Mime = mime!(Application / Json);
}
+ /// Create Mime objects for the response content types for GetContainerHistory
+ lazy_static! {
+ pub static ref GET_CONTAINER_HISTORY_FOUND_ENTITY_HISTORY: Mime = mime!(Application / Json);
+ }
+ /// Create Mime objects for the response content types for GetContainerHistory
+ lazy_static! {
+ pub static ref GET_CONTAINER_HISTORY_BAD_REQUEST: Mime = mime!(Application / Json);
+ }
+ /// Create Mime objects for the response content types for GetContainerHistory
+ lazy_static! {
+ pub static ref GET_CONTAINER_HISTORY_NOT_FOUND: Mime = mime!(Application / Json);
+ }
+ /// Create Mime objects for the response content types for GetContainerHistory
+ lazy_static! {
+ pub static ref GET_CONTAINER_HISTORY_GENERIC_ERROR: Mime = mime!(Application / Json);
+ }
/// Create Mime objects for the response content types for GetCreator
lazy_static! {
pub static ref GET_CREATOR_FOUND_ENTITY: Mime = mime!(Application / Json);
diff --git a/rust/fatcat-api/src/models.rs b/rust/fatcat-api/src/models.rs
index 7ff39789..f33ed9db 100644
--- a/rust/fatcat-api/src/models.rs
+++ b/rust/fatcat-api/src/models.rs
@@ -275,68 +275,7 @@ impl EntityEdit {
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct EntityHistory(Vec<EntityHistoryInner>);
-
-impl ::std::convert::From<Vec<EntityHistoryInner>> for EntityHistory {
- fn from(x: Vec<EntityHistoryInner>) -> Self {
- EntityHistory(x)
- }
-}
-
-impl ::std::convert::From<EntityHistory> for Vec<EntityHistoryInner> {
- fn from(x: EntityHistory) -> Self {
- x.0
- }
-}
-
-impl ::std::iter::FromIterator<EntityHistoryInner> for EntityHistory {
- fn from_iter<U: IntoIterator<Item = EntityHistoryInner>>(u: U) -> Self {
- EntityHistory(Vec::<EntityHistoryInner>::from_iter(u))
- }
-}
-
-impl ::std::iter::IntoIterator for EntityHistory {
- type Item = EntityHistoryInner;
- type IntoIter = ::std::vec::IntoIter<EntityHistoryInner>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.0.into_iter()
- }
-}
-
-impl<'a> ::std::iter::IntoIterator for &'a EntityHistory {
- type Item = &'a EntityHistoryInner;
- type IntoIter = ::std::slice::Iter<'a, EntityHistoryInner>;
-
- fn into_iter(self) -> Self::IntoIter {
- (&self.0).into_iter()
- }
-}
-
-impl<'a> ::std::iter::IntoIterator for &'a mut EntityHistory {
- type Item = &'a mut EntityHistoryInner;
- type IntoIter = ::std::slice::IterMut<'a, EntityHistoryInner>;
-
- fn into_iter(self) -> Self::IntoIter {
- (&mut self.0).into_iter()
- }
-}
-
-impl ::std::ops::Deref for EntityHistory {
- type Target = Vec<EntityHistoryInner>;
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl ::std::ops::DerefMut for EntityHistory {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.0
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct EntityHistoryInner {
+pub struct EntityHistoryEntry {
#[serde(rename = "edit")]
pub edit: models::EntityEdit,
@@ -347,9 +286,9 @@ pub struct EntityHistoryInner {
pub changelog: models::ChangelogEntry,
}
-impl EntityHistoryInner {
- pub fn new(edit: models::EntityEdit, editgroup: models::Editgroup, changelog: models::ChangelogEntry) -> EntityHistoryInner {
- EntityHistoryInner {
+impl EntityHistoryEntry {
+ pub fn new(edit: models::EntityEdit, editgroup: models::Editgroup, changelog: models::ChangelogEntry) -> EntityHistoryEntry {
+ EntityHistoryEntry {
edit: edit,
editgroup: editgroup,
changelog: changelog,
diff --git a/rust/fatcat-api/src/server.rs b/rust/fatcat-api/src/server.rs
index faa9e293..f01dde1b 100644
--- a/rust/fatcat-api/src/server.rs
+++ b/rust/fatcat-api/src/server.rs
@@ -37,9 +37,9 @@ use swagger::{ApiError, Context, XSpanId};
#[allow(unused_imports)]
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, GetStatsResponse, GetWorkReleasesResponse, GetWorkResponse,
- LookupContainerResponse, LookupCreatorResponse, LookupFileResponse, LookupReleaseResponse};
+ CreateFileResponse, CreateReleaseBatchResponse, CreateReleaseResponse, CreateWorkBatchResponse, CreateWorkResponse, GetContainerHistoryResponse, GetContainerResponse,
+ GetCreatorReleasesResponse, GetCreatorResponse, GetEditgroupResponse, GetEditorChangelogResponse, GetEditorResponse, GetFileResponse, GetReleaseFilesResponse, GetReleaseResponse,
+ GetStatsResponse, GetWorkReleasesResponse, GetWorkResponse, LookupContainerResponse, LookupCreatorResponse, LookupFileResponse, LookupReleaseResponse};
header! { (Warning, "Warning") => [String] }
@@ -1417,6 +1417,98 @@ where
let api_clone = api.clone();
router.get(
+ "/v0/container/:id/history",
+ 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<T>(req: &mut Request, api: &T, context: &mut Context) -> Result<Response, Response>
+ where
+ T: Api,
+ {
+ context.x_span_id = Some(req.headers.get::<XSpanId>().map(XSpanId::to_string).unwrap_or_else(|| self::uuid::Uuid::new_v4().to_string()));
+ context.auth_data = req.extensions.remove::<AuthData>();
+ context.authorization = req.extensions.remove::<Authorization>();
+
+ // Path parameters
+ let param_id = {
+ let param = req.extensions
+ .get::<Router>()
+ .ok_or_else(|| Response::with((status::InternalServerError, "An internal error occurred".to_string())))?
+ .find("id")
+ .ok_or_else(|| Response::with((status::BadRequest, "Missing path parameter id".to_string())))?;
+ percent_decode(param.as_bytes())
+ .decode_utf8()
+ .map_err(|_| Response::with((status::BadRequest, format!("Couldn't percent-decode path parameter as UTF-8: {}", param))))?
+ .parse()
+ .map_err(|e| Response::with((status::BadRequest, format!("Couldn't parse path parameter id: {}", e))))?
+ };
+
+ // 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::<UrlEncodedQuery>().unwrap_or_default();
+ let param_limit = query_params.get("limit").and_then(|list| list.first()).and_then(|x| x.parse::<i64>().ok());
+
+ match api.get_container_history(param_id, param_limit, context).wait() {
+ Ok(rsp) => match rsp {
+ GetContainerHistoryResponse::FoundEntityHistory(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_CONTAINER_HISTORY_FOUND_ENTITY_HISTORY.clone()));
+
+ context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
+
+ Ok(response)
+ }
+ GetContainerHistoryResponse::BadRequest(body) => {
+ let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ let mut response = Response::with((status::Status::from_u16(400), body_string));
+ response.headers.set(ContentType(mimetypes::responses::GET_CONTAINER_HISTORY_BAD_REQUEST.clone()));
+
+ context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
+
+ Ok(response)
+ }
+ GetContainerHistoryResponse::NotFound(body) => {
+ let body_string = serde_json::to_string(&body).expect("impossible to fail to serialize");
+
+ let mut response = Response::with((status::Status::from_u16(404), body_string));
+ response.headers.set(ContentType(mimetypes::responses::GET_CONTAINER_HISTORY_NOT_FOUND.clone()));
+
+ context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
+
+ Ok(response)
+ }
+ GetContainerHistoryResponse::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(500), body_string));
+ response.headers.set(ContentType(mimetypes::responses::GET_CONTAINER_HISTORY_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)
+ })
+ },
+ "GetContainerHistory",
+ );
+
+ let api_clone = api.clone();
+ router.get(
"/v0/creator/:id",
move |req: &mut Request| {
let mut context = Context::default();