Skip to content

Commit cc2840f

Browse files
committed
feat: add an option to pass the request id in the API client
Now you can send a header `x-request-id` to the API. ```rust let request_id = Uuid::new_v4(); let response = Client::new(env.get_connection_info()) .get_request_with_query( "stats", Query::params([QueryParam::new("token", "")].to_vec()), Some(headers_with_request_id(request_id)), ) .await; ``` That ID is recorded in logs. So you can use it to track the request.
1 parent 88d3d49 commit cc2840f

File tree

3 files changed

+50
-23
lines changed

3 files changed

+50
-23
lines changed

tests/servers/api/v1/client.rs

+27-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
use hyper::HeaderMap;
12
use reqwest::Response;
23
use serde::Serialize;
4+
use uuid::Uuid;
35

46
use crate::common::http::{Query, QueryParam, ReqwestQuery};
57
use crate::servers::api::connection_info::ConnectionInfo;
@@ -65,7 +67,7 @@ impl Client {
6567
query.add_param(QueryParam::new("token", token));
6668
};
6769

68-
self.get_request_with_query(path, query).await
70+
self.get_request_with_query(path, query, None).await
6971
}
7072

7173
pub async fn post_empty(&self, path: &str) -> Response {
@@ -96,12 +98,12 @@ impl Client {
9698
.unwrap()
9799
}
98100

99-
pub async fn get_request_with_query(&self, path: &str, params: Query) -> Response {
100-
get(&self.base_url(path), Some(params)).await
101+
pub async fn get_request_with_query(&self, path: &str, params: Query, headers: Option<HeaderMap>) -> Response {
102+
get(&self.base_url(path), Some(params), headers).await
101103
}
102104

103105
pub async fn get_request(&self, path: &str) -> Response {
104-
get(&self.base_url(path), None).await
106+
get(&self.base_url(path), None, None).await
105107
}
106108

107109
fn query_with_token(&self) -> Query {
@@ -116,18 +118,27 @@ impl Client {
116118
}
117119
}
118120

119-
pub async fn get(path: &str, query: Option<Query>) -> Response {
120-
match query {
121-
Some(params) => reqwest::Client::builder()
122-
.build()
123-
.unwrap()
124-
.get(path)
125-
.query(&ReqwestQuery::from(params))
126-
.send()
127-
.await
128-
.unwrap(),
129-
None => reqwest::Client::builder().build().unwrap().get(path).send().await.unwrap(),
130-
}
121+
pub async fn get(path: &str, query: Option<Query>, headers: Option<HeaderMap>) -> Response {
122+
let builder = reqwest::Client::builder().build().unwrap();
123+
124+
let builder = match query {
125+
Some(params) => builder.get(path).query(&ReqwestQuery::from(params)),
126+
None => builder.get(path),
127+
};
128+
129+
let builder = match headers {
130+
Some(headers) => builder.headers(headers),
131+
None => builder,
132+
};
133+
134+
builder.send().await.unwrap()
135+
}
136+
137+
/// Returns a `HeaderMap` with a request id header
138+
pub fn headers_with_request_id(request_id: Uuid) -> HeaderMap {
139+
let mut headers = HeaderMap::new();
140+
headers.insert("x-request-id", request_id.to_string().parse().unwrap());
141+
headers
131142
}
132143

133144
#[derive(Serialize, Debug)]

tests/servers/api/v1/contract/authentication.rs

+22-6
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use torrust_tracker_test_helpers::configuration;
2+
use uuid::Uuid;
23

34
use crate::common::http::{Query, QueryParam};
4-
use crate::common::logging::{self};
5+
use crate::common::logging::{self, logs_contains_a_line_with};
56
use crate::servers::api::v1::asserts::{assert_token_not_valid, assert_unauthorized};
6-
use crate::servers::api::v1::client::Client;
7+
use crate::servers::api::v1::client::{headers_with_request_id, Client};
78
use crate::servers::api::Started;
89

910
#[tokio::test]
@@ -15,7 +16,7 @@ async fn should_authenticate_requests_by_using_a_token_query_param() {
1516
let token = env.get_connection_info().api_token.unwrap();
1617

1718
let response = Client::new(env.get_connection_info())
18-
.get_request_with_query("stats", Query::params([QueryParam::new("token", &token)].to_vec()))
19+
.get_request_with_query("stats", Query::params([QueryParam::new("token", &token)].to_vec()), None)
1920
.await;
2021

2122
assert_eq!(response.status(), 200);
@@ -30,7 +31,7 @@ async fn should_not_authenticate_requests_when_the_token_is_missing() {
3031
let env = Started::new(&configuration::ephemeral().into()).await;
3132

3233
let response = Client::new(env.get_connection_info())
33-
.get_request_with_query("stats", Query::default())
34+
.get_request_with_query("stats", Query::default(), None)
3435
.await;
3536

3637
assert_unauthorized(response).await;
@@ -44,13 +45,24 @@ async fn should_not_authenticate_requests_when_the_token_is_empty() {
4445

4546
let env = Started::new(&configuration::ephemeral().into()).await;
4647

48+
let request_id = Uuid::new_v4();
49+
4750
let response = Client::new(env.get_connection_info())
48-
.get_request_with_query("stats", Query::params([QueryParam::new("token", "")].to_vec()))
51+
.get_request_with_query(
52+
"stats",
53+
Query::params([QueryParam::new("token", "")].to_vec()),
54+
Some(headers_with_request_id(request_id)),
55+
)
4956
.await;
5057

5158
assert_token_not_valid(response).await;
5259

5360
env.stop().await;
61+
62+
assert!(
63+
logs_contains_a_line_with(&["ERROR", "API", &format!("{request_id}")]),
64+
"Expected logs to contain: ERROR ... API ... request_id={request_id}"
65+
);
5466
}
5567

5668
#[tokio::test]
@@ -60,7 +72,11 @@ async fn should_not_authenticate_requests_when_the_token_is_invalid() {
6072
let env = Started::new(&configuration::ephemeral().into()).await;
6173

6274
let response = Client::new(env.get_connection_info())
63-
.get_request_with_query("stats", Query::params([QueryParam::new("token", "INVALID TOKEN")].to_vec()))
75+
.get_request_with_query(
76+
"stats",
77+
Query::params([QueryParam::new("token", "INVALID TOKEN")].to_vec()),
78+
None,
79+
)
6480
.await;
6581

6682
assert_token_not_valid(response).await;

tests/servers/api/v1/contract/context/health_check.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ async fn health_check_endpoint_should_return_status_ok_if_api_is_running() {
1313

1414
let url = format!("http://{}/api/health_check", env.get_connection_info().bind_address);
1515

16-
let response = get(&url, None).await;
16+
let response = get(&url, None, None).await;
1717

1818
assert_eq!(response.status(), 200);
1919
assert_eq!(response.headers().get("content-type").unwrap(), "application/json");

0 commit comments

Comments
 (0)