summaryrefslogtreecommitdiffstats
path: root/rust
diff options
context:
space:
mode:
authorBryan Newbold <bnewbold@robocracy.org>2018-06-30 18:40:27 -0700
committerBryan Newbold <bnewbold@robocracy.org>2018-06-30 18:40:27 -0700
commitc88af3a8a92329a598287b5dd3457030e3b4529f (patch)
tree12db7b14015ec028f02c1c329612552db7f8b74f /rust
parent08f7f1642eb8380c5b00f6a54e4b29e55713effd (diff)
downloadfatcat-c88af3a8a92329a598287b5dd3457030e3b4529f.tar.gz
fatcat-c88af3a8a92329a598287b5dd3457030e3b4529f.zip
generic changelog endpoints
Diffstat (limited to 'rust')
-rw-r--r--rust/fatcat-api/README.md4
-rw-r--r--rust/fatcat-api/api.yaml44
-rw-r--r--rust/fatcat-api/api/swagger.yaml225
-rw-r--r--rust/fatcat-api/examples/client.rs18
-rw-r--r--rust/fatcat-api/examples/server_lib/server.rs20
-rw-r--r--rust/fatcat-api/src/client.rs110
-rw-r--r--rust/fatcat-api/src/lib.rs34
-rw-r--r--rust/fatcat-api/src/mimetypes.rs20
-rw-r--r--rust/fatcat-api/src/models.rs5
-rw-r--r--rust/fatcat-api/src/server.rs144
-rw-r--r--rust/src/api_server.rs85
-rw-r--r--rust/src/database_models.rs1
-rw-r--r--rust/tests/test_api_server.rs21
13 files changed, 706 insertions, 25 deletions
diff --git a/rust/fatcat-api/README.md b/rust/fatcat-api/README.md
index 12673cfe..c693f229 100644
--- a/rust/fatcat-api/README.md
+++ b/rust/fatcat-api/README.md
@@ -13,7 +13,7 @@ To see how to make this your own, look here:
[README](https://github.com/swagger-api/swagger-codegen/blob/master/README.md)
- API version: 0.1.0
-- Build date: 2018-07-01T00:51:27.691Z
+- Build date: 2018-07-01T01:27:53.353Z
This autogenerated project defines an API crate `fatcat` which contains:
* An `Api` trait defining the API in Rust.
@@ -68,6 +68,8 @@ cargo run --example client CreateRelease
cargo run --example client CreateReleaseBatch
cargo run --example client CreateWork
cargo run --example client CreateWorkBatch
+cargo run --example client GetChangelog
+cargo run --example client GetChangelogEntry
cargo run --example client GetContainer
cargo run --example client GetContainerHistory
cargo run --example client GetCreator
diff --git a/rust/fatcat-api/api.yaml b/rust/fatcat-api/api.yaml
index 3d0690c6..e1c44063 100644
--- a/rust/fatcat-api/api.yaml
+++ b/rust/fatcat-api/api.yaml
@@ -286,6 +286,8 @@ definitions:
timestamp:
type: string
format: date-time
+ editgroup:
+ $ref: "#/definitions/editgroup"
release_ref:
type: object
properties:
@@ -911,6 +913,48 @@ paths:
description: Generic Error
schema:
$ref: "#/definitions/error_response"
+ /changelog:
+ parameters:
+ - name: limit
+ in: query
+ type: integer
+ format: int64
+ required: false
+ get:
+ operationId: "get_changelog"
+ responses:
+ 200:
+ description: Success
+ schema:
+ type: array
+ items:
+ $ref: "#/definitions/changelog_entry"
+ 500:
+ description: Generic Error
+ schema:
+ $ref: "#/definitions/error_response"
+ /changelog/{id}:
+ parameters:
+ - name: id
+ in: path
+ type: integer
+ format: int64
+ required: true
+ get:
+ operationId: "get_changelog_entry"
+ responses:
+ 200:
+ description: Found Changelog Entry
+ schema:
+ $ref: "#/definitions/changelog_entry"
+ 404:
+ description: Not Found
+ schema:
+ $ref: "#/definitions/error_response"
+ 500:
+ description: Generic Error
+ schema:
+ $ref: "#/definitions/error_response"
/stats:
get:
operationId: "get_stats"
diff --git a/rust/fatcat-api/api/swagger.yaml b/rust/fatcat-api/api/swagger.yaml
index 15ea229a..d376a078 100644
--- a/rust/fatcat-api/api/swagger.yaml
+++ b/rust/fatcat-api/api/swagger.yaml
@@ -1787,6 +1787,87 @@ paths:
path: "/editgroup/:id/accept"
HttpMethod: "Post"
httpmethod: "post"
+ /changelog:
+ get:
+ operationId: "get_changelog"
+ parameters:
+ - name: "limit"
+ in: "query"
+ required: false
+ type: "integer"
+ format: "int64"
+ formatString: "{:?}"
+ example: "Some(789)"
+ responses:
+ 200:
+ description: "Success"
+ schema:
+ type: "array"
+ items:
+ $ref: "#/definitions/changelog_entry"
+ x-responseId: "Success"
+ x-uppercaseResponseId: "SUCCESS"
+ uppercase_operation_id: "GET_CHANGELOG"
+ uppercase_data_type: "VEC<CHANGELOGENTRY>"
+ producesJson: true
+ 500:
+ description: "Generic Error"
+ schema:
+ $ref: "#/definitions/error_response"
+ x-responseId: "GenericError"
+ x-uppercaseResponseId: "GENERIC_ERROR"
+ uppercase_operation_id: "GET_CHANGELOG"
+ uppercase_data_type: "ERRORRESPONSE"
+ producesJson: true
+ operation_id: "get_changelog"
+ uppercase_operation_id: "GET_CHANGELOG"
+ path: "/changelog"
+ HttpMethod: "Get"
+ httpmethod: "get"
+ /changelog/{id}:
+ get:
+ operationId: "get_changelog_entry"
+ parameters:
+ - name: "id"
+ in: "path"
+ required: true
+ type: "integer"
+ format: "int64"
+ formatString: "{}"
+ example: "789"
+ responses:
+ 200:
+ description: "Found Changelog Entry"
+ schema:
+ $ref: "#/definitions/changelog_entry"
+ x-responseId: "FoundChangelogEntry"
+ x-uppercaseResponseId: "FOUND_CHANGELOG_ENTRY"
+ uppercase_operation_id: "GET_CHANGELOG_ENTRY"
+ uppercase_data_type: "CHANGELOGENTRY"
+ producesJson: true
+ 404:
+ description: "Not Found"
+ schema:
+ $ref: "#/definitions/error_response"
+ x-responseId: "NotFound"
+ x-uppercaseResponseId: "NOT_FOUND"
+ uppercase_operation_id: "GET_CHANGELOG_ENTRY"
+ uppercase_data_type: "ERRORRESPONSE"
+ producesJson: true
+ 500:
+ description: "Generic Error"
+ schema:
+ $ref: "#/definitions/error_response"
+ x-responseId: "GenericError"
+ x-uppercaseResponseId: "GENERIC_ERROR"
+ uppercase_operation_id: "GET_CHANGELOG_ENTRY"
+ uppercase_data_type: "ERRORRESPONSE"
+ producesJson: true
+ operation_id: "get_changelog_entry"
+ uppercase_operation_id: "GET_CHANGELOG_ENTRY"
+ path: "/changelog/:id"
+ HttpMethod: "Get"
+ httpmethod: "get"
/stats:
get:
operationId: "get_stats"
@@ -2260,6 +2341,77 @@ definitions:
editgroup_id: 16
revision: 42
changelog_entry:
+ editgroup:
+ extra: "{}"
+ edits:
+ works:
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ creators:
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ files:
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ containers:
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ releases:
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ description: "description"
+ editor_id: 6
+ id: 0
index: 1
editgroup_id: 5
timestamp: "2000-01-23T04:56:07.000+00:00"
@@ -2415,7 +2567,80 @@ definitions:
timestamp:
type: "string"
format: "date-time"
+ editgroup:
+ $ref: "#/definitions/editgroup"
example:
+ editgroup:
+ extra: "{}"
+ edits:
+ works:
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ creators:
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ files:
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ containers:
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ releases:
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ - ident: "00000000-0000-0000-adce-000000000001"
+ edit_id: 847
+ extra: "{}"
+ redirect_ident: "00000000-0000-0000-adce-000000000002"
+ editgroup_id: 16
+ revision: 42
+ description: "description"
+ editor_id: 6
+ id: 0
index: 1
editgroup_id: 5
timestamp: "2000-01-23T04:56:07.000+00:00"
diff --git a/rust/fatcat-api/examples/client.rs b/rust/fatcat-api/examples/client.rs
index eecb0c58..c79110d9 100644
--- a/rust/fatcat-api/examples/client.rs
+++ b/rust/fatcat-api/examples/client.rs
@@ -13,9 +13,9 @@ use clap::{App, Arg};
#[allow(unused_imports)]
use fatcat::{AcceptEditgroupResponse, ApiError, ApiNoContext, ContextWrapperExt, CreateContainerBatchResponse, CreateContainerResponse, CreateCreatorBatchResponse, CreateCreatorResponse,
CreateEditgroupResponse, CreateFileBatchResponse, CreateFileResponse, CreateReleaseBatchResponse, CreateReleaseResponse, CreateWorkBatchResponse, CreateWorkResponse,
- GetContainerHistoryResponse, GetContainerResponse, GetCreatorHistoryResponse, GetCreatorReleasesResponse, GetCreatorResponse, GetEditgroupResponse, GetEditorChangelogResponse,
- GetEditorResponse, GetFileHistoryResponse, GetFileResponse, GetReleaseFilesResponse, GetReleaseHistoryResponse, GetReleaseResponse, GetStatsResponse, GetWorkHistoryResponse,
- GetWorkReleasesResponse, GetWorkResponse, LookupContainerResponse, LookupCreatorResponse, LookupFileResponse, LookupReleaseResponse};
+ GetChangelogEntryResponse, GetChangelogResponse, GetContainerHistoryResponse, GetContainerResponse, GetCreatorHistoryResponse, GetCreatorReleasesResponse, GetCreatorResponse,
+ GetEditgroupResponse, GetEditorChangelogResponse, GetEditorResponse, GetFileHistoryResponse, GetFileResponse, GetReleaseFilesResponse, GetReleaseHistoryResponse, GetReleaseResponse,
+ GetStatsResponse, GetWorkHistoryResponse, GetWorkReleasesResponse, GetWorkResponse, LookupContainerResponse, LookupCreatorResponse, LookupFileResponse, LookupReleaseResponse};
#[allow(unused_imports)]
use futures::{future, stream, Future, Stream};
@@ -31,6 +31,8 @@ fn main() {
"CreateFileBatch",
"CreateReleaseBatch",
"CreateWorkBatch",
+ "GetChangelog",
+ "GetChangelogEntry",
"GetContainer",
"GetContainerHistory",
"GetCreator",
@@ -141,6 +143,16 @@ fn main() {
println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>")));
}
+ Some("GetChangelog") => {
+ let result = client.get_changelog(Some(789)).wait();
+ println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>")));
+ }
+
+ Some("GetChangelogEntry") => {
+ let result = client.get_changelog_entry(789).wait();
+ println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>")));
+ }
+
Some("GetContainer") => {
let result = client.get_container("id_example".to_string()).wait();
println!("{:?} (X-Span-ID: {:?})", result, client.context().x_span_id.clone().unwrap_or(String::from("<none>")));
diff --git a/rust/fatcat-api/examples/server_lib/server.rs b/rust/fatcat-api/examples/server_lib/server.rs
index 062fca02..76d7d13c 100644
--- a/rust/fatcat-api/examples/server_lib/server.rs
+++ b/rust/fatcat-api/examples/server_lib/server.rs
@@ -11,10 +11,10 @@ use swagger;
use fatcat::models;
use fatcat::{AcceptEditgroupResponse, Api, ApiError, Context, CreateContainerBatchResponse, CreateContainerResponse, CreateCreatorBatchResponse, CreateCreatorResponse, CreateEditgroupResponse,
- CreateFileBatchResponse, CreateFileResponse, CreateReleaseBatchResponse, CreateReleaseResponse, CreateWorkBatchResponse, CreateWorkResponse, GetContainerHistoryResponse,
- GetContainerResponse, GetCreatorHistoryResponse, GetCreatorReleasesResponse, GetCreatorResponse, GetEditgroupResponse, GetEditorChangelogResponse, GetEditorResponse,
- GetFileHistoryResponse, GetFileResponse, GetReleaseFilesResponse, GetReleaseHistoryResponse, GetReleaseResponse, GetStatsResponse, GetWorkHistoryResponse, GetWorkReleasesResponse,
- GetWorkResponse, LookupContainerResponse, LookupCreatorResponse, LookupFileResponse, LookupReleaseResponse};
+ CreateFileBatchResponse, CreateFileResponse, CreateReleaseBatchResponse, CreateReleaseResponse, CreateWorkBatchResponse, CreateWorkResponse, GetChangelogEntryResponse,
+ GetChangelogResponse, GetContainerHistoryResponse, GetContainerResponse, GetCreatorHistoryResponse, GetCreatorReleasesResponse, GetCreatorResponse, GetEditgroupResponse,
+ GetEditorChangelogResponse, GetEditorResponse, GetFileHistoryResponse, GetFileResponse, GetReleaseFilesResponse, GetReleaseHistoryResponse, GetReleaseResponse, GetStatsResponse,
+ GetWorkHistoryResponse, GetWorkReleasesResponse, GetWorkResponse, LookupContainerResponse, LookupCreatorResponse, LookupFileResponse, LookupReleaseResponse};
#[derive(Copy, Clone)]
pub struct Server;
@@ -96,6 +96,18 @@ impl Api for Server {
Box::new(futures::failed("Generic failure".into()))
}
+ fn get_changelog(&self, limit: Option<i64>, context: &Context) -> Box<Future<Item = GetChangelogResponse, Error = ApiError> + Send> {
+ let context = context.clone();
+ println!("get_changelog({:?}) - X-Span-ID: {:?}", limit, context.x_span_id.unwrap_or(String::from("<none>")).clone());
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
+ fn get_changelog_entry(&self, id: i64, context: &Context) -> Box<Future<Item = GetChangelogEntryResponse, Error = ApiError> + Send> {
+ let context = context.clone();
+ println!("get_changelog_entry({}) - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("<none>")).clone());
+ Box::new(futures::failed("Generic failure".into()))
+ }
+
fn get_container(&self, id: String, context: &Context) -> Box<Future<Item = GetContainerResponse, Error = ApiError> + Send> {
let context = context.clone();
println!("get_container(\"{}\") - X-Span-ID: {:?}", id, context.x_span_id.unwrap_or(String::from("<none>")).clone());
diff --git a/rust/fatcat-api/src/client.rs b/rust/fatcat-api/src/client.rs
index 54e8b86c..b68c43d2 100644
--- a/rust/fatcat-api/src/client.rs
+++ b/rust/fatcat-api/src/client.rs
@@ -35,10 +35,10 @@ use swagger::{ApiError, Context, XSpanId};
use models;
use {AcceptEditgroupResponse, Api, CreateContainerBatchResponse, CreateContainerResponse, CreateCreatorBatchResponse, CreateCreatorResponse, CreateEditgroupResponse, CreateFileBatchResponse,
- CreateFileResponse, CreateReleaseBatchResponse, CreateReleaseResponse, CreateWorkBatchResponse, CreateWorkResponse, GetContainerHistoryResponse, GetContainerResponse, GetCreatorHistoryResponse,
- GetCreatorReleasesResponse, GetCreatorResponse, GetEditgroupResponse, GetEditorChangelogResponse, GetEditorResponse, GetFileHistoryResponse, GetFileResponse, GetReleaseFilesResponse,
- GetReleaseHistoryResponse, GetReleaseResponse, GetStatsResponse, GetWorkHistoryResponse, GetWorkReleasesResponse, GetWorkResponse, LookupContainerResponse, LookupCreatorResponse,
- LookupFileResponse, LookupReleaseResponse};
+ CreateFileResponse, CreateReleaseBatchResponse, CreateReleaseResponse, CreateWorkBatchResponse, CreateWorkResponse, GetChangelogEntryResponse, GetChangelogResponse, GetContainerHistoryResponse,
+ GetContainerResponse, GetCreatorHistoryResponse, GetCreatorReleasesResponse, GetCreatorResponse, GetEditgroupResponse, GetEditorChangelogResponse, GetEditorResponse, GetFileHistoryResponse,
+ GetFileResponse, GetReleaseFilesResponse, GetReleaseHistoryResponse, GetReleaseResponse, GetStatsResponse, GetWorkHistoryResponse, 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> {
@@ -931,6 +931,108 @@ impl Api for Client {
Box::new(futures::done(result))
}
+ fn get_changelog(&self, param_limit: Option<i64>, context: &Context) -> Box<Future<Item = GetChangelogResponse, 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/changelog?{limit}", self.base_path, 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<GetChangelogResponse, 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::ChangelogEntry>>(&buf)?;
+
+ Ok(GetChangelogResponse::Success(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(GetChangelogResponse::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_changelog_entry(&self, param_id: i64, context: &Context) -> Box<Future<Item = GetChangelogEntryResponse, Error = ApiError> + Send> {
+ let url = format!("{}/v0/changelog/{id}", self.base_path, id = utf8_percent_encode(&param_id.to_string(), PATH_SEGMENT_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<GetChangelogEntryResponse, 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::<models::ChangelogEntry>(&buf)?;
+
+ Ok(GetChangelogEntryResponse::FoundChangelogEntry(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(GetChangelogEntryResponse::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(GetChangelogEntryResponse::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_container(&self, param_id: String, context: &Context) -> Box<Future<Item = GetContainerResponse, Error = ApiError> + Send> {
let url = format!("{}/v0/container/{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 28636d47..ce5674cd 100644
--- a/rust/fatcat-api/src/lib.rs
+++ b/rust/fatcat-api/src/lib.rs
@@ -175,6 +175,24 @@ pub enum CreateWorkBatchResponse {
}
#[derive(Debug, PartialEq)]
+pub enum GetChangelogResponse {
+ /// Success
+ Success(Vec<models::ChangelogEntry>),
+ /// Generic Error
+ GenericError(models::ErrorResponse),
+}
+
+#[derive(Debug, PartialEq)]
+pub enum GetChangelogEntryResponse {
+ /// Found Changelog Entry
+ FoundChangelogEntry(models::ChangelogEntry),
+ /// Not Found
+ NotFound(models::ErrorResponse),
+ /// Generic Error
+ GenericError(models::ErrorResponse),
+}
+
+#[derive(Debug, PartialEq)]
pub enum GetContainerResponse {
/// Found Entity
FoundEntity(models::ContainerEntity),
@@ -444,6 +462,10 @@ pub trait Api {
fn create_work_batch(&self, entity_list: &Vec<models::WorkEntity>, context: &Context) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send>;
+ fn get_changelog(&self, limit: Option<i64>, context: &Context) -> Box<Future<Item = GetChangelogResponse, Error = ApiError> + Send>;
+
+ fn get_changelog_entry(&self, id: i64, context: &Context) -> Box<Future<Item = GetChangelogEntryResponse, Error = ApiError> + Send>;
+
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>;
@@ -513,6 +535,10 @@ pub trait ApiNoContext {
fn create_work_batch(&self, entity_list: &Vec<models::WorkEntity>) -> Box<Future<Item = CreateWorkBatchResponse, Error = ApiError> + Send>;
+ fn get_changelog(&self, limit: Option<i64>) -> Box<Future<Item = GetChangelogResponse, Error = ApiError> + Send>;
+
+ fn get_changelog_entry(&self, id: i64) -> Box<Future<Item = GetChangelogEntryResponse, Error = ApiError> + Send>;
+
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>;
@@ -620,6 +646,14 @@ impl<'a, T: Api> ApiNoContext for ContextWrapper<'a, T> {
self.api().create_work_batch(entity_list, &self.context())
}
+ fn get_changelog(&self, limit: Option<i64>) -> Box<Future<Item = GetChangelogResponse, Error = ApiError> + Send> {
+ self.api().get_changelog(limit, &self.context())
+ }
+
+ fn get_changelog_entry(&self, id: i64) -> Box<Future<Item = GetChangelogEntryResponse, Error = ApiError> + Send> {
+ self.api().get_changelog_entry(id, &self.context())
+ }
+
fn get_container(&self, id: String) -> Box<Future<Item = GetContainerResponse, Error = ApiError> + Send> {
self.api().get_container(id, &self.context())
}
diff --git a/rust/fatcat-api/src/mimetypes.rs b/rust/fatcat-api/src/mimetypes.rs
index 8d67551a..53b582dc 100644
--- a/rust/fatcat-api/src/mimetypes.rs
+++ b/rust/fatcat-api/src/mimetypes.rs
@@ -192,6 +192,26 @@ pub mod responses {
lazy_static! {
pub static ref CREATE_WORK_BATCH_GENERIC_ERROR: Mime = mime!(Application / Json);
}
+ /// Create Mime objects for the response content types for GetChangelog
+ lazy_static! {
+ pub static ref GET_CHANGELOG_SUCCESS: Mime = mime!(Application / Json);
+ }
+ /// Create Mime objects for the response content types for GetChangelog
+ lazy_static! {
+ pub static ref GET_CHANGELOG_GENERIC_ERROR: Mime = mime!(Application / Json);
+ }
+ /// Create Mime objects for the response content types for GetChangelogEntry
+ lazy_static! {
+ pub static ref GET_CHANGELOG_ENTRY_FOUND_CHANGELOG_ENTRY: Mime = mime!(Application / Json);
+ }
+ /// Create Mime objects for the response content types for GetChangelogEntry
+ lazy_static! {
+ pub static ref GET_CHANGELOG_ENTRY_NOT_FOUND: Mime = mime!(Application / Json);
+ }
+ /// Create Mime objects for the response content types for GetChangelogEntry
+ lazy_static! {
+ pub static ref GET_CHANGELOG_ENTRY_GENERIC_ERROR: Mime = mime!(Application / Json);
+ }
/// Create Mime objects for the response content types for GetContainer
lazy_static! {
pub static ref GET_CONTAINER_FOUND_ENTITY: Mime = mime!(Application / Json);
diff --git a/rust/fatcat-api/src/models.rs b/rust/fatcat-api/src/models.rs
index 94edf5c0..cd18469c 100644
--- a/rust/fatcat-api/src/models.rs
+++ b/rust/fatcat-api/src/models.rs
@@ -19,6 +19,10 @@ pub struct ChangelogEntry {
#[serde(rename = "timestamp")]
pub timestamp: chrono::DateTime<chrono::Utc>,
+
+ #[serde(rename = "editgroup")]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub editgroup: Option<models::Editgroup>,
}
impl ChangelogEntry {
@@ -27,6 +31,7 @@ impl ChangelogEntry {
index: index,
editgroup_id: editgroup_id,
timestamp: timestamp,
+ editgroup: None,
}
}
}
diff --git a/rust/fatcat-api/src/server.rs b/rust/fatcat-api/src/server.rs
index e881b313..35b31691 100644
--- a/rust/fatcat-api/src/server.rs
+++ b/rust/fatcat-api/src/server.rs
@@ -37,10 +37,10 @@ use swagger::{ApiError, Context, XSpanId};
#[allow(unused_imports)]
use models;
use {AcceptEditgroupResponse, Api, CreateContainerBatchResponse, CreateContainerResponse, CreateCreatorBatchResponse, CreateCreatorResponse, CreateEditgroupResponse, CreateFileBatchResponse,
- CreateFileResponse, CreateReleaseBatchResponse, CreateReleaseResponse, CreateWorkBatchResponse, CreateWorkResponse, GetContainerHistoryResponse, GetContainerResponse, GetCreatorHistoryResponse,
- GetCreatorReleasesResponse, GetCreatorResponse, GetEditgroupResponse, GetEditorChangelogResponse, GetEditorResponse, GetFileHistoryResponse, GetFileResponse, GetReleaseFilesResponse,
- GetReleaseHistoryResponse, GetReleaseResponse, GetStatsResponse, GetWorkHistoryResponse, GetWorkReleasesResponse, GetWorkResponse, LookupContainerResponse, LookupCreatorResponse,
- LookupFileResponse, LookupReleaseResponse};
+ CreateFileResponse, CreateReleaseBatchResponse, CreateReleaseResponse, CreateWorkBatchResponse, CreateWorkResponse, GetChangelogEntryResponse, GetChangelogResponse, GetContainerHistoryResponse,
+ GetContainerResponse, GetCreatorHistoryResponse, GetCreatorReleasesResponse, GetCreatorResponse, GetEditgroupResponse, GetEditorChangelogResponse, GetEditorResponse, GetFileHistoryResponse,
+ GetFileResponse, GetReleaseFilesResponse, GetReleaseHistoryResponse, GetReleaseResponse, GetStatsResponse, GetWorkHistoryResponse, GetWorkReleasesResponse, GetWorkResponse,
+ LookupContainerResponse, LookupCreatorResponse, LookupFileResponse, LookupReleaseResponse};
header! { (Warning, "Warning") => [String] }
@@ -1330,6 +1330,142 @@ where
let api_clone = api.clone();
router.get(
+ "/v0/changelog",
+ 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>();
+
+ // 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_changelog(param_limit, context).wait() {
+ Ok(rsp) => match rsp {
+ GetChangelogResponse::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_CHANGELOG_SUCCESS.clone()));
+
+ context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
+
+ Ok(response)
+ }
+ GetChangelogResponse::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_CHANGELOG_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)
+ })
+ },
+ "GetChangelog",
+ );
+
+ let api_clone = api.clone();
+ router.get(
+ "/v0/changelog/:id",
+ 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))))?
+ };
+
+ match api.get_changelog_entry(param_id, context).wait() {
+ Ok(rsp) => match rsp {
+ GetChangelogEntryResponse::FoundChangelogEntry(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_CHANGELOG_ENTRY_FOUND_CHANGELOG_ENTRY.clone()));
+
+ context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
+
+ Ok(response)
+ }
+ GetChangelogEntryResponse::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_CHANGELOG_ENTRY_NOT_FOUND.clone()));
+
+ context.x_span_id.as_ref().map(|header| response.headers.set(XSpanId(header.clone())));
+
+ Ok(response)
+ }
+ GetChangelogEntryResponse::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_CHANGELOG_ENTRY_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)
+ })
+ },
+ "GetChangelogEntry",
+ );
+
+ let api_clone = api.clone();
+ router.get(
"/v0/container/:id",
move |req: &mut Request| {
let mut context = Context::default();
diff --git a/rust/src/api_server.rs b/rust/src/api_server.rs
index f895937e..fa767699 100644
--- a/rust/src/api_server.rs
+++ b/rust/src/api_server.rs
@@ -950,18 +950,50 @@ impl Server {
.load(&conn)?;
let entries = changes
- .iter()
- .map(|(row, _)| ChangelogEntry {
- index: row.id,
- editgroup_id: row.editgroup_id,
- timestamp: chrono::DateTime::from_utc(row.timestamp, chrono::Utc),
+ .into_iter()
+ .map(|(cl_row, eg_row)| ChangelogEntry {
+ index: cl_row.id,
+ editgroup: Some(eg_row.to_model_partial()),
+ editgroup_id: cl_row.editgroup_id,
+ timestamp: chrono::DateTime::from_utc(cl_row.timestamp, chrono::Utc),
})
.collect();
Ok(entries)
}
- /// "more" parameter isn't used, but could be to indicate that "expensive" database queries
- /// should be run
+ fn get_changelog_handler(&self, limit: Option<i64>) -> Result<Vec<ChangelogEntry>> {
+ let conn = self.db_pool.get().expect("db_pool error");
+ let limit = limit.unwrap_or(50);
+
+ let changes: Vec<(ChangelogRow, EditgroupRow)> = changelog::table
+ .inner_join(editgroup::table)
+ .order(changelog::id.desc())
+ .limit(limit)
+ .load(&conn)?;
+
+ let entries = changes
+ .into_iter()
+ .map(|(cl_row, eg_row)| ChangelogEntry {
+ index: cl_row.id,
+ editgroup: Some(eg_row.to_model_partial()),
+ editgroup_id: cl_row.editgroup_id,
+ timestamp: chrono::DateTime::from_utc(cl_row.timestamp, chrono::Utc),
+ })
+ .collect();
+ Ok(entries)
+ }
+
+ fn get_changelog_entry_handler(&self, id: i64) -> Result<ChangelogEntry> {
+ let conn = self.db_pool.get().expect("db_pool error");
+
+ let cl_row: ChangelogRow = changelog::table.find(id).first(&conn)?;
+ let editgroup = self.get_editgroup_handler(cl_row.editgroup_id)?;
+
+ let mut entry = cl_row.to_model();
+ entry.editgroup = Some(editgroup);
+ Ok(entry)
+ }
+
fn get_stats_handler(&self, more: Option<String>) -> Result<StatsResponse> {
let conn = self.db_pool.get().expect("db_pool error");
@@ -1304,6 +1336,45 @@ impl Api for Server {
Box::new(futures::done(Ok(ret)))
}
+ fn get_changelog(
+ &self,
+ limit: Option<i64>,
+ _context: &Context,
+ ) -> Box<Future<Item = GetChangelogResponse, Error = ApiError> + Send> {
+ let ret = match self.get_changelog_handler(limit) {
+ Ok(changelog) => GetChangelogResponse::Success(changelog),
+ Err(e) => {
+ error!("{}", e);
+ GetChangelogResponse::GenericError(ErrorResponse {
+ message: e.to_string(),
+ })
+ }
+ };
+ Box::new(futures::done(Ok(ret)))
+ }
+
+ fn get_changelog_entry(
+ &self,
+ id: i64,
+ _context: &Context,
+ ) -> Box<Future<Item = GetChangelogEntryResponse, Error = ApiError> + Send> {
+ let ret = match self.get_changelog_entry_handler(id) {
+ Ok(entry) => GetChangelogEntryResponse::FoundChangelogEntry(entry),
+ Err(Error(ErrorKind::Diesel(::diesel::result::Error::NotFound), _)) => {
+ GetChangelogEntryResponse::NotFound(ErrorResponse {
+ message: format!("No such changelog entry: {}", id),
+ })
+ }
+ Err(e) => {
+ error!("{}", e);
+ GetChangelogEntryResponse::GenericError(ErrorResponse {
+ message: e.to_string(),
+ })
+ }
+ };
+ Box::new(futures::done(Ok(ret)))
+ }
+
fn get_stats(
&self,
more: Option<String>,
diff --git a/rust/src/database_models.rs b/rust/src/database_models.rs
index 6fa9b7b7..8489b336 100644
--- a/rust/src/database_models.rs
+++ b/rust/src/database_models.rs
@@ -279,6 +279,7 @@ impl ChangelogRow {
ChangelogEntry {
index: self.id,
editgroup_id: self.editgroup_id,
+ editgroup: None,
timestamp: chrono::DateTime::from_utc(self.timestamp, chrono::Utc),
}
}
diff --git a/rust/tests/test_api_server.rs b/rust/tests/test_api_server.rs
index b46367e0..b00d22eb 100644
--- a/rust/tests/test_api_server.rs
+++ b/rust/tests/test_api_server.rs
@@ -501,16 +501,33 @@ fn test_accept_editgroup() {
}
#[test]
+fn test_changelog() {
+ let (headers, router, _conn) = setup();
+
+ check_response(
+ request::get("http://localhost:9411/v0/changelog", headers.clone(), &router),
+ status::Ok,
+ Some("editgroup_id"),
+ );
+
+ check_response(
+ request::get("http://localhost:9411/v0/changelog/1", headers.clone(), &router),
+ status::Ok,
+ Some("files"),
+ );
+}
+
+#[test]
fn test_stats() {
let (headers, router, _conn) = setup();
check_response(
- request::get("http://localhost:9411/v0/stats", headers, &router),
+ request::get("http://localhost:9411/v0/stats", headers.clone(), &router),
status::Ok,
Some("merged_editgroups"),
);
check_response(
- request::get("http://localhost:9411/v0/stats?more=yes", headers, &router),
+ request::get("http://localhost:9411/v0/stats?more=yes", headers.clone(), &router),
status::Ok,
Some("merged_editgroups"),
);