Skip to content

Commit d02b0e4

Browse files
softpropsdavidbarsky
authored andcommitted
Expose Functions for deserializing HTTP types from raw event sources (#93)
1 parent d739445 commit d02b0e4

File tree

2 files changed

+69
-20
lines changed

2 files changed

+69
-20
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# next (unreleased)
22

3-
- **New**: `lambda_http::RequestExt` crate now exposes mock helper methods under `cfg(test)` builds to facilitate straightforward unit testability of handlers
3+
- **New**: The `lambda_http` crate now exposes mock helper methods for `RequestExt` under `cfg(test)` builds to facilitate straight forward unit testability of handlers.
4+
- **New**: The `lambda_http` crate now exposes two new functions for deserializing requests from text and raw IO: `lambda_http::request::{from_str,from_reader}`.
45

56
# 0.2.0
67

lambda-http/src/request.rs

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
1-
//! ALB andAPI Gateway request types.
1+
//! ALB and API Gateway request types.
22
//!
33
//! Typically these are exposed via the `request_context`
44
//! request extension method provided by [lambda_http::RequestExt](../trait.RequestExt.html)
55
//!
6-
use std::{borrow::Cow, collections::HashMap, fmt, mem};
7-
86
use http::{
97
self,
108
header::{HeaderName, HeaderValue, HOST},
119
HeaderMap, Method, Request as HttpRequest,
1210
};
1311
use serde::de::{Deserialize, Deserializer, Error as DeError, MapAccess, Visitor};
1412
use serde_derive::Deserialize;
15-
use serde_json::Value;
13+
use serde_json::{error::Error as JsonError, Value};
14+
use std::{borrow::Cow, collections::HashMap, fmt, io::Read, mem};
1615

1716
use crate::{
1817
body::Body,
@@ -322,12 +321,55 @@ impl<'a> From<LambdaRequest<'a>> for HttpRequest<Body> {
322321
}
323322
}
324323

324+
/// Deserializes a Request from an IO stream of JSON.
325+
///
326+
/// # Example
327+
///
328+
/// ```rust,no_run
329+
/// use lambda_http::request::from_reader;
330+
/// use std::fs::File;
331+
/// use std::error::Error;
332+
///
333+
/// fn main() -> Result<(), Box<dyn Error>> {
334+
/// let request = from_reader(
335+
/// File::open("path/to/request.json")?
336+
/// )?;
337+
/// Ok(println!("{:#?}", request))
338+
/// }
339+
/// ```
340+
pub fn from_reader<R>(rdr: R) -> Result<crate::Request, JsonError>
341+
where
342+
R: Read,
343+
{
344+
serde_json::from_reader(rdr).map(LambdaRequest::into)
345+
}
346+
347+
/// Deserializes a Request from a string of JSON text.
348+
///
349+
/// # Example
350+
///
351+
/// ```rust,no_run
352+
/// use lambda_http::request::from_str;
353+
/// use std::fs::File;
354+
/// use std::error::Error;
355+
///
356+
/// fn main() -> Result<(), Box<dyn Error>> {
357+
/// let request = from_str(
358+
/// r#"{ ...raw json here... }"#
359+
/// )?;
360+
/// Ok(println!("{:#?}", request))
361+
/// }
362+
/// ```
363+
pub fn from_str(s: &str) -> Result<crate::Request, JsonError> {
364+
serde_json::from_str(s).map(LambdaRequest::into)
365+
}
366+
325367
#[cfg(test)]
326368
mod tests {
327369
use super::*;
328370
use crate::RequestExt;
329371
use serde_json;
330-
use std::collections::HashMap;
372+
use std::{collections::HashMap, fs::File};
331373

332374
#[test]
333375
fn requests_convert() {
@@ -345,12 +387,21 @@ mod tests {
345387
assert_eq!(expected.method(), actual.method());
346388
}
347389

390+
#[test]
391+
fn deserializes_apigw_request_events_from_readables() {
392+
// from the docs
393+
// https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-api-gateway-request
394+
// note: file paths are relative to the directory of the crate at runtime
395+
let result = from_reader(File::open("tests/data/apigw_proxy_request.json").expect("expected file"));
396+
assert!(result.is_ok(), format!("event was not parsed as expected {:?}", result));
397+
}
398+
348399
#[test]
349400
fn deserializes_apigw_request_events() {
350401
// from the docs
351402
// https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-api-gateway-request
352403
let input = include_str!("../tests/data/apigw_proxy_request.json");
353-
let result = serde_json::from_str::<LambdaRequest<'_>>(&input);
404+
let result = from_str(input);
354405
assert!(result.is_ok(), format!("event was not parsed as expected {:?}", result));
355406
}
356407

@@ -359,7 +410,7 @@ mod tests {
359410
// from the docs
360411
// https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#multi-value-headers
361412
let input = include_str!("../tests/data/alb_request.json");
362-
let result = serde_json::from_str::<LambdaRequest<'_>>(&input);
413+
let result = from_str(input);
363414
assert!(result.is_ok(), format!("event was not parsed as expected {:?}", result));
364415
}
365416

@@ -368,19 +419,18 @@ mod tests {
368419
// from docs
369420
// https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
370421
let input = include_str!("../tests/data/apigw_multi_value_proxy_request.json");
371-
let result = serde_json::from_str::<LambdaRequest<'_>>(&input);
422+
let result = from_str(input);
372423
assert!(
373424
result.is_ok(),
374425
format!("event is was not parsed as expected {:?}", result)
375426
);
376-
let apigw = result.unwrap();
377-
assert!(!apigw.query_string_parameters.is_empty());
378-
assert!(!apigw.multi_value_query_string_parameters.is_empty());
379-
let actual = HttpRequest::from(apigw);
427+
let unwrapped = result.unwrap();
428+
429+
assert!(!unwrapped.query_string_parameters().is_empty());
380430

381431
// test RequestExt#query_string_parameters does the right thing
382432
assert_eq!(
383-
actual.query_string_parameters().get_all("multivalueName"),
433+
unwrapped.query_string_parameters().get_all("multivalueName"),
384434
Some(vec!["you", "me"])
385435
);
386436
}
@@ -390,19 +440,17 @@ mod tests {
390440
// from docs
391441
// https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format
392442
let input = include_str!("../tests/data/alb_multi_value_request.json");
393-
let result = serde_json::from_str::<LambdaRequest<'_>>(&input);
443+
let result = from_str(input);
394444
assert!(
395445
result.is_ok(),
396446
format!("event is was not parsed as expected {:?}", result)
397447
);
398-
let apigw = result.unwrap();
399-
assert!(!apigw.query_string_parameters.is_empty());
400-
assert!(!apigw.multi_value_query_string_parameters.is_empty());
401-
let actual = HttpRequest::from(apigw);
448+
let unwrapped = result.unwrap();
449+
assert!(!unwrapped.query_string_parameters().is_empty());
402450

403451
// test RequestExt#query_string_parameters does the right thing
404452
assert_eq!(
405-
actual.query_string_parameters().get_all("myKey"),
453+
unwrapped.query_string_parameters().get_all("myKey"),
406454
Some(vec!["val1", "val2"])
407455
);
408456
}

0 commit comments

Comments
 (0)