Skip to main content

aws_lite_rs/api/
dynamodb.rs

1//! Amazon DynamoDB API client.
2//!
3//! Thin wrapper over generated ops. All URL construction and HTTP methods
4//! are in `ops::dynamodb::DynamodbOps`. This layer adds:
5//! - Ergonomic method signatures
6
7use crate::{
8    AwsHttpClient, Result,
9    ops::dynamodb::DynamodbOps,
10    types::dynamodb::{
11        DeleteTableInput, DeleteTableOutput, DescribeTableInput, DescribeTableOutput,
12        ListTablesInput, ListTablesOutput, UpdateTableInput, UpdateTableOutput,
13    },
14};
15
16/// Client for the Amazon DynamoDB API
17pub struct DynamodbClient<'a> {
18    ops: DynamodbOps<'a>,
19}
20
21impl<'a> DynamodbClient<'a> {
22    /// Create a new Amazon DynamoDB API client
23    pub(crate) fn new(client: &'a AwsHttpClient) -> Self {
24        Self {
25            ops: DynamodbOps::new(client),
26        }
27    }
28
29    /// Returns an array of table names associated with the current account and endpoint.
30    pub async fn list_tables(&self, body: &ListTablesInput) -> Result<ListTablesOutput> {
31        self.ops.list_tables(body).await
32    }
33
34    /// Returns information about the table, including the current status of the table, when it was created, the primary key sch
35    pub async fn describe_table(&self, body: &DescribeTableInput) -> Result<DescribeTableOutput> {
36        self.ops.describe_table(body).await
37    }
38
39    /// Modifies the provisioned throughput settings, global secondary indexes, or DynamoDB Streams settings for a given table.
40    pub async fn update_table(&self, body: &UpdateTableInput) -> Result<UpdateTableOutput> {
41        self.ops.update_table(body).await
42    }
43
44    /// The DeleteTable operation deletes a table and all of its items.
45    pub async fn delete_table(&self, body: &DeleteTableInput) -> Result<DeleteTableOutput> {
46        self.ops.delete_table(body).await
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53    use crate::types::dynamodb::ProvisionedThroughput;
54
55    #[tokio::test]
56    async fn list_tables_returns_names() {
57        let mut mock = crate::MockClient::new();
58        mock.expect_post("/").returning_json(serde_json::json!({
59            "TableNames": ["orders", "users", "products"],
60            "LastEvaluatedTableName": "products"
61        }));
62        let client = crate::AwsHttpClient::from_mock(mock);
63        let result = client
64            .dynamodb()
65            .list_tables(&ListTablesInput::default())
66            .await
67            .unwrap();
68        assert_eq!(result.table_names, vec!["orders", "users", "products"]);
69        assert_eq!(
70            result.last_evaluated_table_name.as_deref(),
71            Some("products")
72        );
73    }
74
75    #[tokio::test]
76    async fn describe_table_returns_details() {
77        let mut mock = crate::MockClient::new();
78        mock.expect_post("/").returning_json(serde_json::json!({
79            "Table": {
80                "TableName": "orders",
81                "TableArn": "arn:aws:dynamodb:us-east-1:123456789012:table/orders",
82                "TableId": "abc-123",
83                "TableStatus": "ACTIVE",
84                "CreationDateTime": 1700000000.0,
85                "ItemCount": 42,
86                "TableSizeBytes": 8192,
87                "BillingModeSummary": {
88                    "BillingMode": "PAY_PER_REQUEST"
89                },
90                "KeySchema": [{
91                    "AttributeName": "pk",
92                    "KeyType": "HASH"
93                }],
94                "AttributeDefinitions": [{
95                    "AttributeName": "pk",
96                    "AttributeType": "S"
97                }]
98            }
99        }));
100        let client = crate::AwsHttpClient::from_mock(mock);
101        let result = client
102            .dynamodb()
103            .describe_table(&DescribeTableInput {
104                table_name: "orders".to_string(),
105            })
106            .await
107            .unwrap();
108        let table = result.table.unwrap();
109        assert_eq!(table.table_name.as_deref(), Some("orders"));
110        assert_eq!(table.table_status.as_deref(), Some("ACTIVE"));
111        assert_eq!(table.item_count, Some(42));
112        assert_eq!(table.key_schema[0].attribute_name, "pk");
113        assert_eq!(table.key_schema[0].key_type, "HASH");
114        assert_eq!(
115            table.billing_mode_summary.unwrap().billing_mode.as_deref(),
116            Some("PAY_PER_REQUEST"),
117        );
118    }
119
120    #[tokio::test]
121    async fn update_table_changes_billing_mode() {
122        let mut mock = crate::MockClient::new();
123        mock.expect_post("/").returning_json(serde_json::json!({
124            "TableDescription": {
125                "TableName": "orders",
126                "TableStatus": "UPDATING",
127                "ProvisionedThroughput": {
128                    "ReadCapacityUnits": 10,
129                    "WriteCapacityUnits": 5
130                }
131            }
132        }));
133        let client = crate::AwsHttpClient::from_mock(mock);
134        let result = client
135            .dynamodb()
136            .update_table(&UpdateTableInput {
137                table_name: "orders".to_string(),
138                billing_mode: Some("PROVISIONED".to_string()),
139                provisioned_throughput: Some(ProvisionedThroughput {
140                    read_capacity_units: 10,
141                    write_capacity_units: 5,
142                }),
143                ..Default::default()
144            })
145            .await
146            .unwrap();
147        let desc = result.table_description.unwrap();
148        assert_eq!(desc.table_name.as_deref(), Some("orders"));
149        assert_eq!(desc.table_status.as_deref(), Some("UPDATING"));
150        let tp = desc.provisioned_throughput.unwrap();
151        assert_eq!(tp.read_capacity_units, Some(10));
152        assert_eq!(tp.write_capacity_units, Some(5));
153    }
154
155    #[tokio::test]
156    async fn delete_table_returns_deleting_status() {
157        let mut mock = crate::MockClient::new();
158        mock.expect_post("/").returning_json(serde_json::json!({
159            "TableDescription": {
160                "TableName": "orders",
161                "TableStatus": "DELETING",
162                "TableArn": "arn:aws:dynamodb:us-east-1:123456789012:table/orders"
163            }
164        }));
165        let client = crate::AwsHttpClient::from_mock(mock);
166        let result = client
167            .dynamodb()
168            .delete_table(&DeleteTableInput {
169                table_name: "orders".to_string(),
170            })
171            .await
172            .unwrap();
173        let desc = result.table_description.unwrap();
174        assert_eq!(desc.table_name.as_deref(), Some("orders"));
175        assert_eq!(desc.table_status.as_deref(), Some("DELETING"));
176    }
177}