2019-09-02 04:29:36 +00:00
|
|
|
use super::{ApiError, ApiResult};
|
2019-11-25 04:48:24 +00:00
|
|
|
use crate::config::ApiEncodingFormat;
|
2019-09-02 04:29:36 +00:00
|
|
|
use http::header;
|
|
|
|
use hyper::{Body, Request, Response, StatusCode};
|
|
|
|
use serde::Serialize;
|
|
|
|
use ssz::Encode;
|
|
|
|
|
|
|
|
pub struct ResponseBuilder {
|
2019-11-25 04:48:24 +00:00
|
|
|
encoding: ApiEncodingFormat,
|
2019-09-02 04:29:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ResponseBuilder {
|
2019-09-13 10:42:56 +00:00
|
|
|
pub fn new(req: &Request<Body>) -> Result<Self, ApiError> {
|
2019-11-25 04:48:24 +00:00
|
|
|
let accept_header: String = req
|
2019-09-13 10:42:56 +00:00
|
|
|
.headers()
|
2019-11-25 04:48:24 +00:00
|
|
|
.get(header::ACCEPT)
|
2019-09-13 10:42:56 +00:00
|
|
|
.map_or(Ok(""), |h| h.to_str())
|
|
|
|
.map_err(|e| {
|
2019-09-13 10:52:12 +00:00
|
|
|
ApiError::BadRequest(format!(
|
2019-11-25 04:48:24 +00:00
|
|
|
"The Accept header contains invalid characters: {:?}",
|
2019-09-13 10:42:56 +00:00
|
|
|
e
|
|
|
|
))
|
|
|
|
})
|
2019-09-30 03:58:45 +00:00
|
|
|
.map(String::from)?;
|
2019-09-13 10:42:56 +00:00
|
|
|
|
2019-09-13 10:52:12 +00:00
|
|
|
// JSON is our default encoding, unless something else is requested.
|
2019-11-25 04:48:24 +00:00
|
|
|
let encoding = ApiEncodingFormat::from(accept_header.as_str());
|
2019-09-13 10:42:56 +00:00
|
|
|
Ok(Self { encoding })
|
2019-09-02 04:29:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn body<T: Serialize + Encode>(self, item: &T) -> ApiResult {
|
2019-09-13 10:42:56 +00:00
|
|
|
match self.encoding {
|
2019-11-25 04:48:24 +00:00
|
|
|
ApiEncodingFormat::SSZ => Response::builder()
|
2019-09-13 10:42:56 +00:00
|
|
|
.status(StatusCode::OK)
|
|
|
|
.header("content-type", "application/ssz")
|
|
|
|
.body(Body::from(item.as_ssz_bytes()))
|
|
|
|
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e))),
|
|
|
|
_ => self.body_no_ssz(item),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn body_no_ssz<T: Serialize>(self, item: &T) -> ApiResult {
|
2019-09-04 14:36:06 +00:00
|
|
|
let (body, content_type) = match self.encoding {
|
2019-11-25 04:48:24 +00:00
|
|
|
ApiEncodingFormat::JSON => (
|
2019-09-13 10:42:56 +00:00
|
|
|
Body::from(serde_json::to_string(&item).map_err(|e| {
|
|
|
|
ApiError::ServerError(format!(
|
|
|
|
"Unable to serialize response body as JSON: {:?}",
|
|
|
|
e
|
|
|
|
))
|
|
|
|
})?),
|
|
|
|
"application/json",
|
|
|
|
),
|
2019-11-25 04:48:24 +00:00
|
|
|
ApiEncodingFormat::SSZ => {
|
2019-09-13 10:42:56 +00:00
|
|
|
return Err(ApiError::UnsupportedType(
|
|
|
|
"Response cannot be encoded as SSZ.".into(),
|
|
|
|
));
|
2019-09-10 14:40:22 +00:00
|
|
|
}
|
2019-11-25 04:48:24 +00:00
|
|
|
ApiEncodingFormat::YAML => (
|
2019-09-04 14:36:06 +00:00
|
|
|
Body::from(serde_yaml::to_string(&item).map_err(|e| {
|
|
|
|
ApiError::ServerError(format!(
|
|
|
|
"Unable to serialize response body as YAML: {:?}",
|
|
|
|
e
|
|
|
|
))
|
|
|
|
})?),
|
2019-09-13 10:42:56 +00:00
|
|
|
"application/yaml",
|
2019-09-04 14:36:06 +00:00
|
|
|
),
|
2019-09-02 04:29:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Response::builder()
|
|
|
|
.status(StatusCode::OK)
|
2019-09-04 14:36:06 +00:00
|
|
|
.header("content-type", content_type)
|
2019-09-30 03:58:45 +00:00
|
|
|
.body(body)
|
2019-09-02 04:29:36 +00:00
|
|
|
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
|
|
|
}
|
2019-09-10 14:40:22 +00:00
|
|
|
|
2019-09-13 09:38:40 +00:00
|
|
|
pub fn body_text(self, text: String) -> ApiResult {
|
|
|
|
Response::builder()
|
|
|
|
.status(StatusCode::OK)
|
|
|
|
.header("content-type", "text/plain; charset=utf-8")
|
|
|
|
.body(Body::from(text))
|
|
|
|
.map_err(|e| ApiError::ServerError(format!("Failed to build response: {:?}", e)))
|
|
|
|
}
|
2019-09-02 04:29:36 +00:00
|
|
|
}
|