Merge pull request #107 from mozilla-services/feat/issue-99

feat: return newlines reply when get accept header indicates it
This commit is contained in:
Philip Jenvey 2018-11-30 14:07:32 -08:00 committed by GitHub
commit d392e95e06
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 8 deletions

View File

@ -12,6 +12,7 @@ use web::extractors::RequestErrorLocation;
/// Legacy Sync 1.1 error codes, which Sync 1.5 also returns by replacing the descriptive JSON
/// information and replacing it with one of these error codes.
#[allow(dead_code)]
#[derive(Serialize)]
enum WeaveError {
/// Unknown error

View File

@ -106,10 +106,13 @@ impl From<Context<ValidationErrorKind>> for ValidationError {
fn from(inner: Context<ValidationErrorKind>) -> Self {
let status = match inner.get_context() {
ValidationErrorKind::FromDetails(ref _description, ref location, Some(ref name))
if *location == RequestErrorLocation::Header
&& name.eq_ignore_ascii_case("Content-Type") =>
if *location == RequestErrorLocation::Header =>
{
StatusCode::UNSUPPORTED_MEDIA_TYPE
match name.to_ascii_lowercase().as_str() {
"accept" => StatusCode::NOT_ACCEPTABLE,
"content-type" => StatusCode::UNSUPPORTED_MEDIA_TYPE,
_ => StatusCode::BAD_REQUEST,
}
}
_ => StatusCode::BAD_REQUEST,
};

View File

@ -5,7 +5,7 @@
use std::collections::HashMap;
use std::str::FromStr;
use actix_web::http::header::{HeaderValue, CONTENT_TYPE};
use actix_web::http::header::{HeaderValue, ACCEPT, CONTENT_TYPE};
use actix_web::{
dev::{JsonConfig, PayloadConfig},
error::ErrorInternalServerError,
@ -391,6 +391,13 @@ impl FromRequest<ServerState> for MetaRequest {
}
}
/// Desired reply format for a Collection Get request
#[derive(Copy, Clone, Debug)]
pub enum ReplyFormat {
Json,
Newlines,
}
/// Collection Request Delete/Get extractor
///
/// Extracts/validates information needed for collection delete/get requests.
@ -399,6 +406,7 @@ pub struct CollectionRequest {
pub db: Box<dyn Db>,
pub user_id: HawkIdentifier,
pub query: BsoQueryParams,
pub reply: ReplyFormat,
}
impl FromRequest<ServerState> for CollectionRequest {
@ -410,12 +418,25 @@ impl FromRequest<ServerState> for CollectionRequest {
let db = <Box<dyn Db>>::from_request(req, settings)?;
let query = BsoQueryParams::from_request(req, settings)?;
let collection = CollectionParam::from_request(req, settings)?.collection;
let reply = match req.headers().get(ACCEPT) {
Some(v) if v.as_bytes() == b"application/newlines" => ReplyFormat::Newlines,
Some(v) if v.as_bytes() == b"application/json" => ReplyFormat::Json,
Some(_) => {
return Err(ValidationErrorKind::FromDetails(
"Invalid accept".to_string(),
RequestErrorLocation::Header,
Some("accept".to_string()),
).into());
}
None => ReplyFormat::Json,
};
Ok(CollectionRequest {
collection,
db,
user_id,
query,
reply,
})
}
}

View File

@ -10,7 +10,7 @@ use error::ApiError;
use server::ServerState;
use web::extractors::{
BsoPutRequest, BsoRequest, CollectionPostRequest, CollectionRequest, HawkIdentifier,
MetaRequest,
MetaRequest, ReplyFormat,
};
pub const ONE_KB: f64 = 1024.0;
@ -128,6 +128,7 @@ where
F: Future<Item = Paginated<T>, Error = ApiError> + 'static,
T: Serialize + Default + 'static,
{
let reply_format = coll.reply;
Box::new(
fut.or_else(move |e| {
if e.is_colllection_not_found() {
@ -143,13 +144,29 @@ where
.extract_resource(coll.user_id, Some(coll.collection), None)
.map_err(From::from)
.map(move |ts| (result, ts))
}).map(|(result, ts)| {
HttpResponse::build(StatusCode::OK)
}).map(move |(result, ts)| {
let mut builder = HttpResponse::build(StatusCode::OK);
let resp = builder
.header("X-Last-Modified", ts.as_header())
.header("X-Weave-Records", result.items.len().to_string())
.if_some(result.offset, |offset, resp| {
resp.header("X-Weave-Next-Offset", offset.to_string());
}).json(result.items)
});
match reply_format {
ReplyFormat::Json => resp.json(result.items),
ReplyFormat::Newlines => {
let items: String = result
.items
.into_iter()
.map(|v| serde_json::to_string(&v).unwrap_or("".to_string()))
.filter(|v| !v.is_empty())
.map(|v| v.replace("\n", "\\u000a") + "\n")
.collect();
resp.header("Content-Type", "application/newlines")
.header("Content-Length", format!("{}", items.len()))
.body(items)
}
}
}),
)
}