Skip to content

Commit 17ef2a2

Browse files
authored
lambda_http: Add convenience methods to get references to data in the request (#602)
* Fixed some types in docs and tests * Add convenience methods to get references to data in the request There are existing methods to get owned clones of various pieces of data in the `Request`. This adds methods to get references where owned data is not needed.
1 parent fcb0b8f commit 17ef2a2

File tree

2 files changed

+100
-25
lines changed

2 files changed

+100
-25
lines changed

lambda-http/src/ext.rs

Lines changed: 99 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ impl Error for PayloadError {
5555
}
5656
}
5757

58-
/// Extentions for `lambda_http::Request` structs that
58+
/// Extensions for `lambda_http::Request` structs that
5959
/// provide access to [API gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format)
6060
/// and [ALB](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html)
6161
/// features.
@@ -109,6 +109,9 @@ pub trait RequestExt {
109109
/// Return the raw http path for a request without any stage information.
110110
fn raw_http_path(&self) -> String;
111111

112+
/// Return the raw http path for a request without any stage information.
113+
fn raw_http_path_str(&self) -> &str;
114+
112115
/// Configures instance with the raw http path.
113116
fn with_raw_http_path(self, path: &str) -> Self;
114117

@@ -118,12 +121,24 @@ pub trait RequestExt {
118121
///
119122
/// The yielded value represents both single and multi-valued
120123
/// parameters alike. When multiple query string parameters with the same
121-
/// name are expected, `query_string_parameters().get_all("many")` to retrieve them all.
124+
/// name are expected, `query_string_parameters().all("many")` to retrieve them all.
122125
///
123-
/// No query parameters
124-
/// will yield an empty `QueryMap`.
126+
/// No query parameters will yield an empty `QueryMap`.
125127
fn query_string_parameters(&self) -> QueryMap;
126128

129+
/// Return pre-parsed http query string parameters, parameters
130+
/// provided after the `?` portion of a url,
131+
/// associated with the API gateway request.
132+
///
133+
/// The yielded value represents both single and multi-valued
134+
/// parameters alike. When multiple query string parameters with the same
135+
/// name are expected,
136+
/// `query_string_parameters_ref().and_then(|params| params.all("many"))` to
137+
/// retrieve them all.
138+
///
139+
/// No query parameters will yield `None`.
140+
fn query_string_parameters_ref(&self) -> Option<&QueryMap>;
141+
127142
/// Configures instance with query string parameters
128143
///
129144
/// This is intended for use in mock testing contexts.
@@ -139,6 +154,14 @@ pub trait RequestExt {
139154
/// These will always be empty for ALB triggered requests
140155
fn path_parameters(&self) -> QueryMap;
141156

157+
/// Return pre-extracted path parameters, parameter provided in url placeholders
158+
/// `/foo/{bar}/baz/{boom}`,
159+
/// associated with the API gateway request. No path parameters
160+
/// will yield `None`
161+
///
162+
/// These will always be `None` for ALB triggered requests
163+
fn path_parameters_ref(&self) -> Option<&QueryMap>;
164+
142165
/// Configures instance with path parameters
143166
///
144167
/// This is intended for use in mock testing contexts.
@@ -153,6 +176,13 @@ pub trait RequestExt {
153176
/// These will always be empty for ALB triggered requests
154177
fn stage_variables(&self) -> QueryMap;
155178

179+
/// Return [stage variables](https://docs.aws.amazon.com/apigateway/latest/developerguide/stage-variables.html)
180+
/// associated with the API gateway request. No stage parameters
181+
/// will yield `None`
182+
///
183+
/// These will always be `None` for ALB triggered requests
184+
fn stage_variables_ref(&self) -> Option<&QueryMap>;
185+
156186
/// Configures instance with stage variables under #[cfg(test)] configurations
157187
///
158188
/// This is intended for use in mock testing contexts.
@@ -164,6 +194,10 @@ pub trait RequestExt {
164194
/// Return request context data associated with the ALB or API gateway request
165195
fn request_context(&self) -> RequestContext;
166196

197+
/// Return a reference to the request context data associated with the ALB or
198+
/// API gateway request
199+
fn request_context_ref(&self) -> Option<&RequestContext>;
200+
167201
/// Configures instance with request context
168202
///
169203
/// This is intended for use in mock testing contexts.
@@ -185,15 +219,23 @@ pub trait RequestExt {
185219
/// Return the Lambda function context associated with the request
186220
fn lambda_context(&self) -> Context;
187221

222+
/// Return a reference to the the Lambda function context associated with the
223+
/// request
224+
fn lambda_context_ref(&self) -> Option<&Context>;
225+
188226
/// Configures instance with lambda context
189227
fn with_lambda_context(self, context: Context) -> Self;
190228
}
191229

192230
impl RequestExt for http::Request<Body> {
193231
fn raw_http_path(&self) -> String {
232+
self.raw_http_path_str().to_string()
233+
}
234+
235+
fn raw_http_path_str(&self) -> &str {
194236
self.extensions()
195237
.get::<RawHttpPath>()
196-
.map(|ext| ext.0.clone())
238+
.map(|RawHttpPath(path)| path.as_str())
197239
.unwrap_or_default()
198240
}
199241

@@ -204,10 +246,19 @@ impl RequestExt for http::Request<Body> {
204246
}
205247

206248
fn query_string_parameters(&self) -> QueryMap {
207-
self.extensions()
208-
.get::<QueryStringParameters>()
209-
.map(|ext| ext.0.clone())
210-
.unwrap_or_default()
249+
self.query_string_parameters_ref().cloned().unwrap_or_default()
250+
}
251+
252+
fn query_string_parameters_ref(&self) -> Option<&QueryMap> {
253+
self.extensions().get::<QueryStringParameters>().and_then(
254+
|QueryStringParameters(params)| {
255+
if params.is_empty() {
256+
None
257+
} else {
258+
Some(params)
259+
}
260+
},
261+
)
211262
}
212263

213264
fn with_query_string_parameters<Q>(self, parameters: Q) -> Self
@@ -220,10 +271,19 @@ impl RequestExt for http::Request<Body> {
220271
}
221272

222273
fn path_parameters(&self) -> QueryMap {
223-
self.extensions()
224-
.get::<PathParameters>()
225-
.map(|ext| ext.0.clone())
226-
.unwrap_or_default()
274+
self.path_parameters_ref().cloned().unwrap_or_default()
275+
}
276+
277+
fn path_parameters_ref(&self) -> Option<&QueryMap> {
278+
self.extensions().get::<PathParameters>().and_then(
279+
|PathParameters(params)| {
280+
if params.is_empty() {
281+
None
282+
} else {
283+
Some(params)
284+
}
285+
},
286+
)
227287
}
228288

229289
fn with_path_parameters<P>(self, parameters: P) -> Self
@@ -236,10 +296,19 @@ impl RequestExt for http::Request<Body> {
236296
}
237297

238298
fn stage_variables(&self) -> QueryMap {
239-
self.extensions()
240-
.get::<StageVariables>()
241-
.map(|ext| ext.0.clone())
242-
.unwrap_or_default()
299+
self.stage_variables_ref().cloned().unwrap_or_default()
300+
}
301+
302+
fn stage_variables_ref(&self) -> Option<&QueryMap> {
303+
self.extensions().get::<StageVariables>().and_then(
304+
|StageVariables(vars)| {
305+
if vars.is_empty() {
306+
None
307+
} else {
308+
Some(vars)
309+
}
310+
},
311+
)
243312
}
244313

245314
#[cfg(test)]
@@ -253,25 +322,31 @@ impl RequestExt for http::Request<Body> {
253322
}
254323

255324
fn request_context(&self) -> RequestContext {
256-
self.extensions()
257-
.get::<RequestContext>()
325+
self.request_context_ref()
258326
.cloned()
259327
.expect("Request did not contain a request context")
260328
}
261329

330+
fn request_context_ref(&self) -> Option<&RequestContext> {
331+
self.extensions().get::<RequestContext>()
332+
}
333+
262334
fn with_request_context(self, context: RequestContext) -> Self {
263335
let mut s = self;
264336
s.extensions_mut().insert(context);
265337
s
266338
}
267339

268340
fn lambda_context(&self) -> Context {
269-
self.extensions()
270-
.get::<Context>()
341+
self.lambda_context_ref()
271342
.cloned()
272343
.expect("Request did not contain a lambda context")
273344
}
274345

346+
fn lambda_context_ref(&self) -> Option<&Context> {
347+
self.extensions().get::<Context>()
348+
}
349+
275350
fn with_lambda_context(self, context: Context) -> Self {
276351
let mut s = self;
277352
s.extensions_mut().insert(context);
@@ -358,7 +433,7 @@ mod tests {
358433
}
359434

360435
#[test]
361-
fn requests_have_json_parseable_payloads() {
436+
fn requests_have_json_parsable_payloads() {
362437
#[derive(Deserialize, Eq, PartialEq, Debug)]
363438
struct Payload {
364439
foo: String,
@@ -421,15 +496,15 @@ mod tests {
421496
}
422497

423498
#[test]
424-
fn requests_omiting_content_types_do_not_support_parseable_payloads() {
499+
fn requests_omitting_content_types_do_not_support_parsable_payloads() {
425500
#[derive(Deserialize, Eq, PartialEq, Debug)]
426501
struct Payload {
427502
foo: String,
428503
baz: usize,
429504
}
430505
let request = http::Request::builder()
431506
.body(Body::from(r#"{"foo":"bar", "baz": 2}"#))
432-
.expect("failed to bulid request");
507+
.expect("failed to build request");
433508
let payload: Option<Payload> = request.payload().unwrap_or_default();
434509
assert_eq!(payload, None);
435510
}

lambda-http/src/request.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ fn into_websocket_request(ag: ApiGatewayWebsocketProxyRequest) -> http::Request<
298298
.extension(RequestContext::WebSocket(ag.request_context));
299299

300300
// merge headers into multi_value_headers and make
301-
// multi-value_headers our cannoncial source of request headers
301+
// multi-value_headers our canonical source of request headers
302302
let mut headers = ag.multi_value_headers;
303303
headers.extend(ag.headers);
304304
update_xray_trace_id_header(&mut headers);

0 commit comments

Comments
 (0)