Skip to content

Commit 2a91c03

Browse files
committed
feat/response-lib
1 parent 332796d commit 2a91c03

6 files changed

Lines changed: 117 additions & 13 deletions

File tree

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edition = "2021"
55
repository = "https://github.com/MadebyAe/lambda-utils"
66
description = "Lambda Utils for AWS Rust Lambda"
77
authors = ["Ae <[email protected]>"]
8-
keywords = ["aws", "lambda", "serverless", "rust"]
8+
keywords = ["aws", "lambda", "serverless", "rust", "macros", "mongodb", "sqs"]
99
categories = ["utils"]
1010
license = "MIT"
1111

@@ -14,6 +14,7 @@ aws-config = { version = "1.1.2", features = ["behavior-version-latest"], option
1414
aws-sdk-sqs = { version = "1.9.0", optional = true }
1515
aws-types = { version = "1.1.2", optional = true }
1616
aws_lambda_events = { version = "0.15.0", optional = true }
17+
chrono = { version = "0.4", optional = true }
1718
lambda_http = { version = "0.11.1", optional = true }
1819
mongodb = { version = "2.3.1", optional = true }
1920
once_cell = { version = "1.17.0", optional = true }
@@ -26,4 +27,5 @@ tokio = { version = "1.17.0", features = ["full", "test-util"] }
2627
headers = ["lambda_http", "serde_json"]
2728
mongodb = ["dep:mongodb", "once_cell"]
2829
network = ["aws_lambda_events", "lambda_http"]
30+
response = ["chrono", "lambda_http", "serde_json"]
2931
sqs = ["aws-sdk-sqs", "aws-config", "serde_json"]

src/headers.rs

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use lambda_http::Request;
2-
use serde_json::to_string;
2+
use serde_json::{json, Value};
33
use std::collections::HashMap;
44

55
pub fn get_header_value(request: &Request, value: String) -> String {
@@ -18,7 +18,7 @@ pub fn get_header_user_agent(request: &Request) -> String {
1818
return get_header_value(request, "User-Agent".to_string());
1919
}
2020

21-
pub fn get_header_cookies(request: &Request) -> String {
21+
pub fn get_header_cookies(request: &Request) -> Value {
2222
let mut cookies = HashMap::new();
2323

2424
if let Some(cookie_header) = request.headers().get("Cookie") {
@@ -40,22 +40,31 @@ pub fn get_header_cookies(request: &Request) -> String {
4040
}
4141
}
4242

43-
return to_string(&cookies).unwrap();
43+
return json!(&cookies);
4444
}
4545

4646
#[cfg(test)]
47-
mod tests {
47+
mod headers_tests {
4848
use super::*;
49-
use lambda_http::http::header::{HeaderMap, HeaderValue};
49+
use lambda_http::http::header::{HeaderName, HeaderValue};
5050
use lambda_http::{Body, Request};
5151

5252
fn mock_request(headers: Vec<(&str, &str)>) -> Request {
53-
let mut header_map = HeaderMap::new();
53+
let mut request = Request::new(Body::Empty); // Correct use of Request::new with Body::Empty for lambda_http
54+
5455
for (key, value) in headers {
55-
header_map.insert(key, HeaderValue::from_static(value));
56+
match key.parse::<HeaderName>() {
57+
Ok(parsed_key) => match HeaderValue::from_str(value) {
58+
Ok(header_value) => {
59+
request.headers_mut().insert(parsed_key, header_value);
60+
}
61+
Err(e) => eprintln!("Failed to create HeaderValue: {}", e),
62+
},
63+
Err(e) => eprintln!("Failed to parse HeaderName: {}", e),
64+
}
5665
}
5766

58-
Request::new(Body::Empty).with_headers(header_map)
67+
return request;
5968
}
6069

6170
#[test]
@@ -84,10 +93,10 @@ mod tests {
8493

8594
#[test]
8695
fn test_get_header_cookies() {
87-
let request = mock_request(vec![("Cookie", "username=user; session=token")]);
96+
let request = mock_request(vec![("Cookie", "username=username; session=session;")]);
8897
assert_eq!(
8998
get_header_cookies(&request),
90-
r#"{"session":"token","username":"user"}"#
99+
json!({ "session":"session","username":"username" })
91100
);
92101
}
93102
}

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@ pub mod mongodb;
77
#[cfg(feature = "network")]
88
pub mod network;
99

10+
#[cfg(feature = "response")]
11+
pub mod response;
12+
1013
#[cfg(feature = "sqs")]
1114
pub mod sqs;

src/mongodb.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ pub fn get_mongodb_client() -> Result<&'static Client, Box<dyn Error + Send + Sy
3939
}
4040

4141
#[cfg(test)]
42-
mod tests {
42+
mod mongodb_tests {
43+
use super::*;
44+
use std::env;
45+
4346
#[tokio::test]
4447
async fn test_create_mongodb_client() {
4548
env::set_var(

src/response.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
#[macro_export]
2+
macro_rules! json_error {
3+
($status_code:expr, $message:expr) => {{
4+
use chrono::Utc;
5+
use lambda_http::Body;
6+
use lambda_http::http::{Response, StatusCode};
7+
use serde_json::json;
8+
9+
fn error_code_to_type(status: StatusCode) -> &'static str {
10+
match status {
11+
StatusCode::BAD_REQUEST => "BadRequest",
12+
StatusCode::FORBIDDEN => "Forbidden",
13+
StatusCode::INTERNAL_SERVER_ERROR => "InternalServerError",
14+
StatusCode::UNAUTHORIZED => "Unauthorized",
15+
_ => "Unknown",
16+
}
17+
}
18+
19+
let now = Utc::now();
20+
let body = json!({
21+
"code": $status_code.as_u16(),
22+
"message": $message,
23+
"timestamp": now.timestamp_millis(),
24+
"type": error_code_to_type($status_code),
25+
}).to_string();
26+
27+
Response::builder()
28+
.status($status_code)
29+
.header("Content-Type", "application/json")
30+
.body(Body::from(body))
31+
.expect("Failed to construct error response")
32+
}};
33+
}
34+
pub use json_error;
35+
36+
#[macro_export]
37+
macro_rules! json_ok {
38+
($data:tt) => {{
39+
use lambda_http::http::Response;
40+
use lambda_http::Body;
41+
use serde_json::json;
42+
43+
let body = json!($data).to_string();
44+
45+
Response::builder()
46+
.status(StatusCode::OK)
47+
.header("Content-Type", "application/json")
48+
.body(Body::from(body))
49+
.expect("Failed to construct success response")
50+
}};
51+
}
52+
pub use json_ok;
53+
54+
#[cfg(test)]
55+
mod response_tests {
56+
use super::*;
57+
use lambda_http::http::StatusCode;
58+
use lambda_http::{Body, Response};
59+
use serde_json::{from_slice, Value};
60+
61+
fn extract_body(response: Response<Body>) -> Value {
62+
let body = response.into_body();
63+
64+
return from_slice(&body).unwrap_or_default();
65+
}
66+
67+
#[test]
68+
fn json_error_macro_test() {
69+
let response = json_error!(StatusCode::FORBIDDEN, "Forbidden access");
70+
assert_eq!(response.status(), StatusCode::FORBIDDEN);
71+
72+
let body = extract_body(response);
73+
assert_eq!(body["code"], 403);
74+
assert_eq!(body["message"], "Forbidden access");
75+
assert_eq!(body["type"], "Forbidden");
76+
}
77+
78+
#[test]
79+
fn json_ok_macro_test() {
80+
let response = json_ok!({ "data": "Success" });
81+
assert_eq!(response.status(), StatusCode::OK);
82+
83+
let body = extract_body(response);
84+
assert_eq!(body["data"], "Success");
85+
}
86+
}

src/sqs.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ pub async fn receive_from_sqs() -> Result<ReceiveMessageOutput, SqsError> {
5858
}
5959

6060
#[cfg(test)]
61-
mod tests {
61+
mod sqs_tests {
62+
use super::*;
6263
use serde_json::{json, Value};
6364

6465
fn create_mock_object() -> Value {

0 commit comments

Comments
 (0)