Skip to main content

rustack_logs_http/
router.rs

1//! CloudWatch Logs request router.
2//!
3//! CloudWatch Logs uses the `awsJson1_1` protocol where all requests are `POST /`
4//! with the operation specified in the `X-Amz-Target` header:
5//!
6//! ```text
7//! X-Amz-Target: Logs_20140328.CreateLogGroup
8//! ```
9
10use rustack_logs_model::{error::LogsError, operations::LogsOperation};
11
12/// The expected prefix for the `X-Amz-Target` header value.
13const TARGET_PREFIX: &str = "Logs_20140328.";
14
15/// Resolve a CloudWatch Logs operation from an HTTP request.
16///
17/// Extracts the operation from the `X-Amz-Target` header, validates the
18/// format, and maps it to a [`LogsOperation`] enum variant.
19pub fn resolve_operation(headers: &http::HeaderMap) -> Result<LogsOperation, LogsError> {
20    let target = headers
21        .get("x-amz-target")
22        .ok_or_else(LogsError::missing_action)?;
23
24    let target_str = target.to_str().map_err(|_| LogsError::missing_action())?;
25
26    let operation_name = target_str
27        .strip_prefix(TARGET_PREFIX)
28        .ok_or_else(|| LogsError::unknown_operation(target_str))?;
29
30    LogsOperation::from_name(operation_name).ok_or_else(|| LogsError::unknown_operation(target_str))
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    fn headers_with_target(target: &str) -> http::HeaderMap {
38        let mut map = http::HeaderMap::new();
39        map.insert(
40            "x-amz-target",
41            http::HeaderValue::from_str(target).expect("valid header value"),
42        );
43        map
44    }
45
46    #[test]
47    fn test_should_resolve_create_log_group() {
48        let headers = headers_with_target("Logs_20140328.CreateLogGroup");
49        let op = resolve_operation(&headers).expect("should resolve");
50        assert_eq!(op, LogsOperation::CreateLogGroup);
51    }
52
53    #[test]
54    #[allow(clippy::too_many_lines)]
55    fn test_should_resolve_all_operations() {
56        let ops = [
57            (
58                "Logs_20140328.CreateLogGroup",
59                LogsOperation::CreateLogGroup,
60            ),
61            (
62                "Logs_20140328.DeleteLogGroup",
63                LogsOperation::DeleteLogGroup,
64            ),
65            (
66                "Logs_20140328.DescribeLogGroups",
67                LogsOperation::DescribeLogGroups,
68            ),
69            (
70                "Logs_20140328.CreateLogStream",
71                LogsOperation::CreateLogStream,
72            ),
73            (
74                "Logs_20140328.DeleteLogStream",
75                LogsOperation::DeleteLogStream,
76            ),
77            (
78                "Logs_20140328.DescribeLogStreams",
79                LogsOperation::DescribeLogStreams,
80            ),
81            ("Logs_20140328.PutLogEvents", LogsOperation::PutLogEvents),
82            ("Logs_20140328.GetLogEvents", LogsOperation::GetLogEvents),
83            (
84                "Logs_20140328.FilterLogEvents",
85                LogsOperation::FilterLogEvents,
86            ),
87            (
88                "Logs_20140328.PutRetentionPolicy",
89                LogsOperation::PutRetentionPolicy,
90            ),
91            (
92                "Logs_20140328.DeleteRetentionPolicy",
93                LogsOperation::DeleteRetentionPolicy,
94            ),
95            (
96                "Logs_20140328.PutMetricFilter",
97                LogsOperation::PutMetricFilter,
98            ),
99            (
100                "Logs_20140328.DeleteMetricFilter",
101                LogsOperation::DeleteMetricFilter,
102            ),
103            (
104                "Logs_20140328.DescribeMetricFilters",
105                LogsOperation::DescribeMetricFilters,
106            ),
107            (
108                "Logs_20140328.PutSubscriptionFilter",
109                LogsOperation::PutSubscriptionFilter,
110            ),
111            (
112                "Logs_20140328.DeleteSubscriptionFilter",
113                LogsOperation::DeleteSubscriptionFilter,
114            ),
115            (
116                "Logs_20140328.DescribeSubscriptionFilters",
117                LogsOperation::DescribeSubscriptionFilters,
118            ),
119            ("Logs_20140328.TagResource", LogsOperation::TagResource),
120            ("Logs_20140328.UntagResource", LogsOperation::UntagResource),
121            (
122                "Logs_20140328.ListTagsForResource",
123                LogsOperation::ListTagsForResource,
124            ),
125            ("Logs_20140328.TagLogGroup", LogsOperation::TagLogGroup),
126            ("Logs_20140328.UntagLogGroup", LogsOperation::UntagLogGroup),
127            (
128                "Logs_20140328.ListTagsLogGroup",
129                LogsOperation::ListTagsLogGroup,
130            ),
131            (
132                "Logs_20140328.PutDestination",
133                LogsOperation::PutDestination,
134            ),
135            (
136                "Logs_20140328.PutDestinationPolicy",
137                LogsOperation::PutDestinationPolicy,
138            ),
139            (
140                "Logs_20140328.DeleteDestination",
141                LogsOperation::DeleteDestination,
142            ),
143            (
144                "Logs_20140328.DescribeDestinations",
145                LogsOperation::DescribeDestinations,
146            ),
147            (
148                "Logs_20140328.AssociateKmsKey",
149                LogsOperation::AssociateKmsKey,
150            ),
151            (
152                "Logs_20140328.DisassociateKmsKey",
153                LogsOperation::DisassociateKmsKey,
154            ),
155            ("Logs_20140328.StartQuery", LogsOperation::StartQuery),
156            ("Logs_20140328.StopQuery", LogsOperation::StopQuery),
157            (
158                "Logs_20140328.GetQueryResults",
159                LogsOperation::GetQueryResults,
160            ),
161            (
162                "Logs_20140328.DescribeQueries",
163                LogsOperation::DescribeQueries,
164            ),
165            (
166                "Logs_20140328.PutQueryDefinition",
167                LogsOperation::PutQueryDefinition,
168            ),
169            (
170                "Logs_20140328.DeleteQueryDefinition",
171                LogsOperation::DeleteQueryDefinition,
172            ),
173            (
174                "Logs_20140328.DescribeQueryDefinitions",
175                LogsOperation::DescribeQueryDefinitions,
176            ),
177            (
178                "Logs_20140328.PutResourcePolicy",
179                LogsOperation::PutResourcePolicy,
180            ),
181            (
182                "Logs_20140328.DeleteResourcePolicy",
183                LogsOperation::DeleteResourcePolicy,
184            ),
185            (
186                "Logs_20140328.DescribeResourcePolicies",
187                LogsOperation::DescribeResourcePolicies,
188            ),
189            (
190                "Logs_20140328.TestMetricFilter",
191                LogsOperation::TestMetricFilter,
192            ),
193        ];
194        for (target, expected) in ops {
195            let headers = headers_with_target(target);
196            let op = resolve_operation(&headers).expect("should resolve");
197            assert_eq!(op, expected, "failed for target: {target}");
198        }
199    }
200
201    #[test]
202    fn test_should_error_on_missing_target() {
203        let headers = http::HeaderMap::new();
204        let err = resolve_operation(&headers).unwrap_err();
205        assert_eq!(
206            err.code,
207            rustack_logs_model::error::LogsErrorCode::MissingAction
208        );
209    }
210
211    #[test]
212    fn test_should_error_on_wrong_prefix() {
213        let headers = headers_with_target("WrongService.CreateLogGroup");
214        let err = resolve_operation(&headers).unwrap_err();
215        assert_eq!(
216            err.code,
217            rustack_logs_model::error::LogsErrorCode::InvalidAction,
218        );
219    }
220
221    #[test]
222    fn test_should_error_on_unknown_operation() {
223        let headers = headers_with_target("Logs_20140328.NonExistent");
224        let err = resolve_operation(&headers).unwrap_err();
225        assert_eq!(
226            err.code,
227            rustack_logs_model::error::LogsErrorCode::InvalidAction,
228        );
229    }
230}