Skip to main content

aws_lite_rs/api/
cloudtrail.rs

1//! AWS CloudTrail API client.
2//!
3//! Thin wrapper over generated ops. All URL construction and HTTP methods
4//! are in `ops::cloudtrail::CloudtrailOps`. This layer adds:
5//! - Ergonomic method signatures
6
7use crate::{
8    AwsHttpClient, Result,
9    ops::cloudtrail::CloudtrailOps,
10    types::cloudtrail::{
11        DeleteTrailRequest, DeleteTrailResponse, DescribeTrailsRequest, DescribeTrailsResponse,
12        GetEventSelectorsRequest, GetEventSelectorsResponse, GetTrailStatusRequest,
13        GetTrailStatusResponse, UpdateTrailRequest, UpdateTrailResponse,
14    },
15};
16
17/// Client for the AWS CloudTrail API
18pub struct CloudtrailClient<'a> {
19    ops: CloudtrailOps<'a>,
20}
21
22impl<'a> CloudtrailClient<'a> {
23    /// Create a new AWS CloudTrail API client
24    pub(crate) fn new(client: &'a AwsHttpClient) -> Self {
25        Self {
26            ops: CloudtrailOps::new(client),
27        }
28    }
29
30    /// Retrieves settings for one or more trails associated with the current region.
31    pub async fn describe_trails(
32        &self,
33        body: &DescribeTrailsRequest,
34    ) -> Result<DescribeTrailsResponse> {
35        self.ops.describe_trails(body).await
36    }
37
38    /// Returns a JSON-formatted list of information about the specified trail.
39    pub async fn get_trail_status(
40        &self,
41        body: &GetTrailStatusRequest,
42    ) -> Result<GetTrailStatusResponse> {
43        self.ops.get_trail_status(body).await
44    }
45
46    /// Describes the settings for the event selectors configured for your trail.
47    pub async fn get_event_selectors(
48        &self,
49        body: &GetEventSelectorsRequest,
50    ) -> Result<GetEventSelectorsResponse> {
51        self.ops.get_event_selectors(body).await
52    }
53
54    /// Deletes a trail.
55    pub async fn delete_trail(&self, body: &DeleteTrailRequest) -> Result<DeleteTrailResponse> {
56        self.ops.delete_trail(body).await
57    }
58
59    /// Updates trail settings that control what events you are logging.
60    pub async fn update_trail(&self, body: &UpdateTrailRequest) -> Result<UpdateTrailResponse> {
61        self.ops.update_trail(body).await
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68
69    #[tokio::test]
70    async fn describe_trails_returns_trail_list() {
71        let mut mock = crate::MockClient::new();
72        mock.expect_post("/").returning_json(serde_json::json!({
73            "trailList": [
74                {
75                    "Name": "my-trail",
76                    "TrailARN": "arn:aws:cloudtrail:us-east-1:123456789012:trail/my-trail",
77                    "HomeRegion": "us-east-1",
78                    "S3BucketName": "my-trail-logs",
79                    "IsMultiRegionTrail": true,
80                    "IsOrganizationTrail": false,
81                    "HasCustomEventSelectors": false,
82                    "HasInsightSelectors": false
83                }
84            ]
85        }));
86        let client = crate::AwsHttpClient::from_mock(mock);
87        let result = client
88            .cloudtrail()
89            .describe_trails(&DescribeTrailsRequest::default())
90            .await
91            .unwrap();
92        assert_eq!(result.trail_list.len(), 1);
93        let trail = &result.trail_list[0];
94        assert_eq!(trail.name.as_deref(), Some("my-trail"));
95        assert_eq!(
96            trail.trail_arn.as_deref(),
97            Some("arn:aws:cloudtrail:us-east-1:123456789012:trail/my-trail")
98        );
99        assert_eq!(trail.home_region.as_deref(), Some("us-east-1"));
100        assert_eq!(trail.s3_bucket_name.as_deref(), Some("my-trail-logs"));
101        assert_eq!(trail.is_multi_region_trail, Some(true));
102    }
103
104    #[tokio::test]
105    async fn get_trail_status_returns_logging_state() {
106        let mut mock = crate::MockClient::new();
107        mock.expect_post("/").returning_json(serde_json::json!({
108            "IsLogging": true,
109            "StartLoggingTime": 1700000000.0,
110            "LatestDeliveryTime": 1700001000.0
111        }));
112        let client = crate::AwsHttpClient::from_mock(mock);
113        let result = client
114            .cloudtrail()
115            .get_trail_status(&GetTrailStatusRequest {
116                name: "my-trail".to_string(),
117            })
118            .await
119            .unwrap();
120        assert_eq!(result.is_logging, Some(true));
121        assert_eq!(result.start_logging_time, Some(1700000000.0));
122        assert_eq!(result.latest_delivery_time, Some(1700001000.0));
123    }
124
125    #[tokio::test]
126    async fn update_trail_returns_updated_config() {
127        let mut mock = crate::MockClient::new();
128        mock.expect_post("/").returning_json(serde_json::json!({
129            "Name": "my-trail",
130            "TrailARN": "arn:aws:cloudtrail:us-east-1:123456789012:trail/my-trail",
131            "S3BucketName": "my-trail-logs",
132            "IncludeGlobalServiceEvents": false,
133            "IsMultiRegionTrail": false,
134            "LogFileValidationEnabled": true
135        }));
136        let client = crate::AwsHttpClient::from_mock(mock);
137        let result = client
138            .cloudtrail()
139            .update_trail(&UpdateTrailRequest {
140                name: "my-trail".to_string(),
141                include_global_service_events: Some(false),
142                ..Default::default()
143            })
144            .await
145            .unwrap();
146        assert_eq!(result.name.as_deref(), Some("my-trail"));
147        assert_eq!(
148            result.trail_arn.as_deref(),
149            Some("arn:aws:cloudtrail:us-east-1:123456789012:trail/my-trail")
150        );
151        assert_eq!(result.include_global_service_events, Some(false));
152        assert_eq!(result.log_file_validation_enabled, Some(true));
153    }
154
155    #[tokio::test]
156    async fn delete_trail_returns_empty_response() {
157        let mut mock = crate::MockClient::new();
158        mock.expect_post("/").returning_json(serde_json::json!({}));
159        let client = crate::AwsHttpClient::from_mock(mock);
160        let result = client
161            .cloudtrail()
162            .delete_trail(&DeleteTrailRequest {
163                name: "my-trail".to_string(),
164            })
165            .await;
166        assert!(result.is_ok(), "DeleteTrail should succeed with empty body");
167    }
168
169    #[tokio::test]
170    async fn get_event_selectors_returns_selectors() {
171        let mut mock = crate::MockClient::new();
172        mock.expect_post("/").returning_json(serde_json::json!({
173            "TrailARN": "arn:aws:cloudtrail:us-east-1:123456789012:trail/my-trail",
174            "EventSelectors": [
175                {
176                    "ReadWriteType": "All",
177                    "IncludeManagementEvents": true,
178                    "DataResources": []
179                }
180            ],
181            "AdvancedEventSelectors": []
182        }));
183        let client = crate::AwsHttpClient::from_mock(mock);
184        let result = client
185            .cloudtrail()
186            .get_event_selectors(&GetEventSelectorsRequest {
187                trail_name: "my-trail".to_string(),
188            })
189            .await
190            .unwrap();
191        assert_eq!(
192            result.trail_arn.as_deref(),
193            Some("arn:aws:cloudtrail:us-east-1:123456789012:trail/my-trail")
194        );
195        assert_eq!(result.event_selectors.len(), 1);
196        assert_eq!(
197            result.event_selectors[0].read_write_type.as_deref(),
198            Some("All")
199        );
200        assert_eq!(
201            result.event_selectors[0].include_management_events,
202            Some(true)
203        );
204        assert!(result.advanced_event_selectors.is_empty());
205    }
206}