diff --git a/Cargo.lock b/Cargo.lock index a3ce9d8..0f7637a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -948,6 +948,7 @@ dependencies = [ "aws-sdk-dynamodb", "aws-sdk-ec2", "aws-smithy-runtime-api", + "aws-smithy-types", "aws-types", "backon", "base64 0.22.0", diff --git a/Cargo.toml b/Cargo.toml index e55fa2b..14831f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ aws-config = "1.4.0" aws-sdk-dynamodb = "1.28.0" aws-sdk-ec2 = "1.42.0" aws-smithy-runtime-api = "1.6.0" +aws-smithy-types = { version = "1.1.9", features=["serde-serialize"] } aws-types = "1.2.1" chrono = "0.4" clap = { version = "4.5.4", features = ["derive"] } diff --git a/src/app.rs b/src/app.rs index eb2a4c4..9141a0b 100644 --- a/src/app.rs +++ b/src/app.rs @@ -594,8 +594,7 @@ pub async fn table_schema(cx: &Context) -> TableSchema { Some(table_name) => { // TODO: reduce # of DescribeTable API calls. table_schema function is called every time you do something. let desc: TableDescription = control::describe_table_api( - cx, - table_name, /* should be equal to 'cx.effective_table_name()' */ + cx, table_name, /* should be equal to 'cx.effective_table_name()' */ ) .await; @@ -663,7 +662,7 @@ pub fn index_schemas(desc: &TableDescription) -> Option> { } } -pub fn bye(code: i32, msg: &str) { +pub fn bye(code: i32, msg: &str) -> ! { println!("{}", msg); std::process::exit(code); } @@ -757,7 +756,6 @@ mod tests { use super::*; use std::convert::TryInto; use std::error::Error; - use std::str::FromStr; // to utilize Region::from_str // for unit tests #[test] fn test_context_functions() -> Result<(), Box> { @@ -790,29 +788,35 @@ mod tests { should_strict_for_query: None, retry: Some(RetryConfig::default().try_into()?), }; - assert_eq!(cx2.effective_region(), Region::from_str("ap-northeast-1")?); + assert_eq!( + cx2.effective_region(), + Region::from_static("ap-northeast-1") + ); assert_eq!(cx2.effective_table_name(), String::from("cfgtbl")); let cx3 = Context { - overwritten_region: Some(Region::from_str("us-east-1")?), // --region us-east-1 - overwritten_table_name: Some(String::from("argtbl")), // --table argtbl + overwritten_region: Some(Region::from_static("us-east-1")), // --region us-east-1 + overwritten_table_name: Some(String::from("argtbl")), // --table argtbl ..cx2.clone() }; - assert_eq!(cx3.effective_region(), Region::from_str("us-east-1")?); + assert_eq!(cx3.effective_region(), Region::from_static("us-east-1")); assert_eq!(cx3.effective_table_name(), String::from("argtbl")); let cx4 = Context { - overwritten_region: Some(Region::from_str("us-east-1")?), // --region us-east-1 + overwritten_region: Some(Region::from_static("us-east-1")), // --region us-east-1 ..cx2.clone() }; - assert_eq!(cx4.effective_region(), Region::from_str("us-east-1")?); + assert_eq!(cx4.effective_region(), Region::from_static("us-east-1")); assert_eq!(cx4.effective_table_name(), String::from("cfgtbl")); let cx5 = Context { overwritten_table_name: Some(String::from("argtbl")), // --table argtbl ..cx2.clone() }; - assert_eq!(cx5.effective_region(), Region::from_str("ap-northeast-1")?); + assert_eq!( + cx5.effective_region(), + Region::from_static("ap-northeast-1") + ); assert_eq!(cx5.effective_table_name(), String::from("argtbl")); Ok(()) diff --git a/src/batch.rs b/src/batch.rs index 34a4f8a..60b8bcd 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -158,7 +158,9 @@ pub fn build_batch_request_items_from_json( ddbjson_attributes_to_attrvals(raw_item); write_requests.push( WriteRequest::builder() - .put_request(PutRequest::builder().set_item(Some(item)).build().unwrap()) + .put_request( + PutRequest::builder().set_item(Some(item)).build().unwrap(), + ) .build(), ); } else { @@ -191,7 +193,9 @@ pub fn build_batch_request_items_from_json( ddbjson_attributes_to_attrvals(raw_key); write_requests.push( WriteRequest::builder() - .delete_request(DeleteRequest::builder().set_key(Some(key)).build().unwrap()) + .delete_request( + DeleteRequest::builder().set_key(Some(key)).build().unwrap(), + ) .build(), ); } else { @@ -353,7 +357,12 @@ pub async fn batch_write_item( validate_item_keys(&attrs, &ts)?; write_requests.push( WriteRequest::builder() - .delete_request(DeleteRequest::builder().set_key(Some(attrs)).build().unwrap()) + .delete_request( + DeleteRequest::builder() + .set_key(Some(attrs)) + .build() + .unwrap(), + ) .build(), ); } @@ -554,7 +563,9 @@ fn ddbjson_val_to_attrval(ddb_jsonval: &JsonValue) -> Option { } else if let Some(x) = ddb_jsonval.get("N") { Some(AttributeValue::N(x.as_str().unwrap().to_string())) } else if let Some(x) = ddb_jsonval.get("B") { - Some(AttributeValue::B(aws_sdk_dynamodb::primitives::Blob::new(json_binary_val_to_bytes(x)))) + Some(AttributeValue::B(aws_sdk_dynamodb::primitives::Blob::new( + json_binary_val_to_bytes(x), + ))) } else if let Some(x) = ddb_jsonval.get("BOOL") { Some(AttributeValue::Bool(x.as_bool().unwrap())) } else if let Some(x) = ddb_jsonval.get("SS") { diff --git a/src/bootstrap.rs b/src/bootstrap.rs index 3992bca..4b8748b 100644 --- a/src/bootstrap.rs +++ b/src/bootstrap.rs @@ -200,7 +200,12 @@ see https://github.com/awslabs/dynein#working-with-dynamodb-items for detail .collect(); write_requests.push( WriteRequest::builder() - .put_request(PutRequest::builder().set_item(Some(item_attrval)).build().unwrap()) + .put_request( + PutRequest::builder() + .set_item(Some(item_attrval)) + .build() + .unwrap(), + ) .build(), ); if write_requests.len() == 25 { diff --git a/src/control.rs b/src/control.rs index 94c0dd4..737ac81 100644 --- a/src/control.rs +++ b/src/control.rs @@ -123,11 +123,8 @@ pub async fn describe_table(cx: app::Context, target_table_to_desc: Option util::print_table_description(new_context.effective_region().as_ref(), desc), + None | Some("yaml") => { + util::print_table_description(new_context.effective_region().as_ref(), desc) + } // Some("raw") => println!("{:#?}", desc), Some(_) => { println!("ERROR: unsupported output type."); @@ -156,10 +155,7 @@ pub async fn describe_table(cx: app::Context, target_table_to_desc: Option TableDescription { +pub async fn describe_table_api(cx: &app::Context, table_name: String) -> TableDescription { let region = cx.effective_region(); let config = cx.effective_sdk_config_with_region(region.as_ref()).await; let ddb = DynamoDbSdkClient::new(&config); @@ -190,7 +186,7 @@ pub async fn create_table(cx: app::Context, name: String, given_keys: Vec util::print_table_description(cx.effective_region().as_ref(), desc), Err(e) => { debug!("CreateTable API call got an error -- {:#?}", e); - error!("{}", e.to_string()); + error!("{}", e.into_service_error()); std::process::exit(1); } } @@ -253,7 +249,8 @@ pub async fn create_index(cx: app::Context, index_name: String, given_keys: Vec< .build(), ) .set_provisioned_throughput(None) // TODO: assign default rcu/wcu if base table is Provisioned mode. currently it works only for OnDemand talbe. - .build().unwrap(); + .build() + .unwrap(); let gsi_update = GlobalSecondaryIndexUpdate::builder() .create(create_gsi_action) @@ -274,7 +271,10 @@ pub async fn create_index(cx: app::Context, index_name: String, given_keys: Vec< } Ok(res) => { debug!("Returned result: {:#?}", res); - util::print_table_description(cx.effective_region().as_ref(), res.table_description.unwrap()); + util::print_table_description( + cx.effective_region().as_ref(), + res.table_description.unwrap(), + ); } } } @@ -287,8 +287,7 @@ pub async fn update_table( rcu: Option, ) { // Retrieve TableDescription of the table to update, current (before update) status. - let desc: TableDescription = - describe_table_api(&cx, table_name_to_update.clone()).await; + let desc: TableDescription = describe_table_api(&cx, table_name_to_update.clone()).await; // Map given string into "Mode" enum. Note that in cmd.rs clap already limits acceptable values. let switching_to_mode: Option = match mode_string { @@ -330,7 +329,8 @@ pub async fn update_table( .write_capacity_units .unwrap() })) - .build().unwrap(), + .build() + .unwrap(), ), } } @@ -348,7 +348,8 @@ pub async fn update_table( ProvisionedThroughput::builder() .read_capacity_units(rcu.unwrap_or(5)) .write_capacity_units(wcu.unwrap_or(5)) - .build().unwrap(), + .build() + .unwrap(), ), }, }; @@ -427,7 +428,7 @@ pub async fn delete_table(cx: app::Context, name: String, skip_confirmation: boo match ddb.delete_table().table_name(name).send().await { Err(e) => { debug!("DeleteTable API call got an error -- {:#?}", e); - error!("{}", e.to_string()); + error!("{}", e.into_service_error()); std::process::exit(1); } Ok(res) => { @@ -469,7 +470,7 @@ pub async fn backup(cx: app::Context, all_tables: bool) { match req.send().await { Err(e) => { debug!("CreateBackup API call got an error -- {:#?}", e); - app::bye(1, &e.to_string()); + app::bye(1, &e.into_service_error().to_string()); } Ok(res) => { debug!("Returned result: {:#?}", res); @@ -589,6 +590,7 @@ pub async fn restore(cx: app::Context, backup_name: Option, restore_name { Err(e) => { debug!("RestoreTableFromBackup API call got an error -- {:#?}", e); + app::bye(1, &e.into_service_error().to_string()); /* e.g. ... Possibly see "BackupInUse" error: [2020-08-14T13:16:07Z DEBUG dy::control] RestoreTableFromBackup API call got an error -- Service( BackupInUse( "Backup is being used to restore another table: arn:aws:dynamodb:us-west-2:111111111111:table/Music/backup/01527492829107-81b9b3dd",)) */ @@ -636,9 +638,7 @@ async fn list_backups_api(cx: &app::Context, all_tables: bool) -> Vec { debug!("ListBackups API call got an error -- {:#?}", e); - // app::bye(1, &e.to_string()) // it doesn't meet return value requirement. - println!("{}", &e.to_string()); - std::process::exit(1); + app::bye(1, &e.into_service_error().to_string()); } Ok(res) => res .backup_summaries diff --git a/src/data.rs b/src/data.rs index d3d0b55..5913335 100644 --- a/src/data.rs +++ b/src/data.rs @@ -30,6 +30,7 @@ use aws_sdk_dynamodb::{ Client as DynamoDbSdkClient, }; use log::{debug, error}; +use serde::{ser::SerializeStruct, Serialize, Serializer}; use serde_json::Value as JsonValue; use tabwriter::TabWriter; // use bytes::Bytes; @@ -717,6 +718,46 @@ pub fn dispatch_jsonvalue_to_attrval(jv: &JsonValue, enable_set_inference: bool) } } +struct AttributeValueWrapper(AttributeValue); + +impl Serialize for AttributeValueWrapper { + fn serialize(&self, serializer: S) -> Result { + let mut state = serializer.serialize_struct("Object", 1)?; + + match &self.0 { + AttributeValue::S(v) => state.serialize_field("S", v)?, + AttributeValue::N(v) => state.serialize_field("N", v)?, + AttributeValue::Bool(v) => state.serialize_field("BOOL", v)?, + AttributeValue::Null(_) => state.serialize_field("NULL", &true)?, + AttributeValue::Ss(v) => state.serialize_field("SS", v)?, + AttributeValue::Ns(v) => state.serialize_field("NS", v)?, + AttributeValue::B(v) => { + state.serialize_field("B", &aws_smithy_types::base64::encode(v))? + } + AttributeValue::Bs(v) => state.serialize_field( + "BS", + &v.iter() + .map(aws_smithy_types::base64::encode) + .collect::>(), + )?, + AttributeValue::M(_) => { + // noop, M is extracted by strip_item + } + AttributeValue::L(v) => state.serialize_field( + "L", + &v.iter() + .map(|item| AttributeValueWrapper(item.clone())) + .collect::>(), + )?, + _ => panic!( + "DynamoDB AttributeValue is not in valid status: {:#?}", + &self.0 + ), + }; + state.end() + } +} + /// `strip_items` calls `strip_item` for each item. fn strip_items( items: &[HashMap], @@ -742,16 +783,25 @@ fn strip_items( /// to something like this: /// /// { "pkA": { "S": "e0a170d9-5ce3-443b-bbce-d0d49c71d151" } -/// -/// by utilizing Serialize derive of the struct: -/// https://docs.rs/rusoto_dynamodb/0.42.0/src/rusoto_dynamodb/generated.rs.html#38 -/// https://docs.rs/rusoto_dynamodb/0.42.0/rusoto_dynamodb/struct.AttributeValue.html fn strip_item(item: &HashMap) -> HashMap { item.iter() - .map(|attr| - // Serialization: `serde_json::to_value(sth: rusoto_dynamodb::AttributeValue)` - // TODO: fix attr.1 - (attr.0.to_string(), serde_json::to_value("attr.1").unwrap())) + .map(|attr| { + if let Ok(m) = attr.1.as_m() { + ( + attr.0.to_string(), + serde_json::to_value(HashMap::from([( + "M", + serde_json::to_value(strip_item(m)).unwrap(), + )])) + .unwrap(), + ) + } else { + ( + attr.0.to_string(), + serde_json::to_value(AttributeValueWrapper(attr.1.to_owned())).unwrap(), + ) + } + }) .collect() } @@ -1356,10 +1406,7 @@ mod tests { actual.vals, Some(HashMap::from([( ":DYNEIN_ATTRVAL0".to_owned(), - AttributeValue { - s: Some("2020-02-24T22:22:22Z".to_owned()), - ..Default::default() - }, + AttributeValue::S("2020-02-24T22:22:22Z".to_owned()), )])) ); } @@ -1389,7 +1436,7 @@ mod tests { Some(HashMap::from([ ( ":DYNEIN_ATTRVAL0".to_owned(), - AttributeValue::S("0".to_owned()) + AttributeValue::N("0".to_owned()) ), ( ":DYNEIN_ATTRVAL1".to_owned(), @@ -1418,10 +1465,7 @@ mod tests { actual.vals, Some(HashMap::from([( ":DYNEIN_ATTRVAL0".to_owned(), - AttributeValue { - s: Some("value".to_owned()), - ..Default::default() - }, + AttributeValue::S("value".to_owned()), )])) ); } @@ -1446,10 +1490,7 @@ mod tests { actual.vals, Some(HashMap::from([( ":DYNEIN_ATTRVAL0".to_owned(), - AttributeValue { - s: Some("item1".to_owned()), - ..Default::default() - } + AttributeValue::S("item1".to_owned()), )])) ); } @@ -1476,17 +1517,11 @@ mod tests { Some(HashMap::from([ ( ":DYNEIN_ATTRVAL0".to_owned(), - AttributeValue { - n: Some("7".to_owned()), - ..Default::default() - } + AttributeValue::N("7".to_owned()), ), ( ":DYNEIN_ATTRVAL1".to_owned(), - AttributeValue { - n: Some("3".to_owned()), - ..Default::default() - } + AttributeValue::N("3".to_owned()), ), ])) ) @@ -1516,13 +1551,7 @@ mod tests { actual.vals, Some(HashMap::from([( ":DYNEIN_ATTRVAL0".to_owned(), - AttributeValue { - l: Some(vec![AttributeValue { - s: Some("item2".to_owned()), - ..Default::default() - }]), - ..Default::default() - } + AttributeValue::L(vec![AttributeValue::S("item2".to_owned())]), )])) ); } @@ -1551,13 +1580,7 @@ mod tests { actual.vals, Some(HashMap::from([( ":DYNEIN_ATTRVAL0".to_owned(), - AttributeValue { - l: Some(vec![AttributeValue { - s: Some("item2".to_owned()), - ..Default::default() - }]), - ..Default::default() - } + AttributeValue::L(vec![AttributeValue::S("item2".to_owned())]) )])) ); } @@ -1585,10 +1608,7 @@ mod tests { actual.vals, Some(HashMap::from([( ":DYNEIN_ATTRVAL0".to_owned(), - AttributeValue { - n: Some("123".to_owned()), - ..Default::default() - } + AttributeValue::N("123".to_owned()), ),])) ) } @@ -1644,27 +1664,15 @@ mod tests { let actual = dispatch_jsonvalue_to_attrval(&string_list, false); assert_eq!( actual, - AttributeValue { - l: Some(vec!( - AttributeValue { - s: Some("+44 1234567".to_owned()), - ..Default::default() - }, - AttributeValue { - s: Some("+44 2345678".to_owned()), - ..Default::default() - } - )), - ..Default::default() - } + AttributeValue::L(vec![ + AttributeValue::S("+44 1234567".to_owned()), + AttributeValue::S("+44 2345678".to_owned()), + ]), ); let actual = dispatch_jsonvalue_to_attrval(&string_list, true); assert_eq!( actual, - AttributeValue { - ss: Some(vec!("+44 1234567".to_owned(), "+44 2345678".to_owned())), - ..Default::default() - } + AttributeValue::Ss(vec!("+44 1234567".to_owned(), "+44 2345678".to_owned())), ); let number_list = r#" @@ -1676,27 +1684,15 @@ mod tests { let actual = dispatch_jsonvalue_to_attrval(&number_list, false); assert_eq!( actual, - AttributeValue { - l: Some(vec!( - AttributeValue { - n: Some("12345".to_owned()), - ..Default::default() - }, - AttributeValue { - n: Some("67890".to_owned()), - ..Default::default() - } - )), - ..Default::default() - } + AttributeValue::L(vec![ + AttributeValue::N("12345".to_owned()), + AttributeValue::N("67890".to_owned()), + ]) ); let actual = dispatch_jsonvalue_to_attrval(&number_list, true); assert_eq!( actual, - AttributeValue { - ns: Some(vec!["12345".to_owned(), "67890".to_owned()]), - ..Default::default() - } + AttributeValue::Ns(vec!["12345".to_owned(), "67890".to_owned()]), ); let mix_list = r#" @@ -1709,19 +1705,10 @@ mod tests { let actual = dispatch_jsonvalue_to_attrval(&mix_list, flag); assert_eq!( actual, - AttributeValue { - l: Some(vec!( - AttributeValue { - s: Some("text".to_owned()), - ..Default::default() - }, - AttributeValue { - n: Some("1234".to_owned()), - ..Default::default() - } - )), - ..Default::default() - } + AttributeValue::L(vec![ + AttributeValue::S("text".to_owned()), + AttributeValue::N("1234".to_owned()), + ]) ); } } diff --git a/src/key.rs b/src/key.rs index 96a9350..5c907d0 100644 --- a/src/key.rs +++ b/src/key.rs @@ -117,11 +117,12 @@ pub fn typed_key_for_schema( name: key.clone().attribute_name, // kind should be one of S/N/B, Which can be retrieved from AttributeDefinition's attribute_type. kind: KeyType::from_str( - &attrs + attrs .iter() .find(|at| at.attribute_name == key.attribute_name) .expect("primary key should be in AttributeDefinition.") - .attribute_type.as_str(), + .attribute_type + .as_str(), ) .unwrap(), }) diff --git a/src/parser.rs b/src/parser.rs index e4f21f5..febd82f 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -15,7 +15,7 @@ */ use crate::pest::Parser; -use aws_sdk_dynamodb::types::AttributeValue; +use aws_sdk_dynamodb::{primitives::Blob, types::AttributeValue}; use base64::engine::{general_purpose, DecodePaddingMode, GeneralPurpose, GeneralPurposeConfig}; use base64::{DecodeError, Engine}; use bytes::Bytes; @@ -456,7 +456,7 @@ impl AttrVal { AttrVal::S(str) => AttributeValue::S(str), AttrVal::Bool(boolean) => AttributeValue::Bool(boolean), AttrVal::Null(isnull) => AttributeValue::Null(isnull), - AttrVal::B(binary) => AttributeValue::B(aws_sdk_dynamodb::primitives::Blob::new(binary)), + AttrVal::B(binary) => AttributeValue::B(Blob::new(binary)), AttrVal::L(list) => AttributeValue::L( list.into_iter() .map(|x| x.convert_attribute_value()) @@ -469,7 +469,7 @@ impl AttrVal { ), AttrVal::NS(list) => AttributeValue::Ns(list), AttrVal::SS(list) => AttributeValue::Ss(list), - AttrVal::BS(list) => AttributeValue::Bs(list.into_iter().map(aws_sdk_dynamodb::primitives::Blob::new).collect()), + AttrVal::BS(list) => AttributeValue::Bs(list.into_iter().map(Blob::new).collect()), } } } @@ -1444,7 +1444,11 @@ impl DyneinParser { let result = GeneratedParser::parse(Rule::map_literal, exp); match result { Ok(mut pair) => { - let item = parse_literal(pair.next().unwrap())?.convert_attribute_value().as_m().unwrap().to_owned(); + let item = parse_literal(pair.next().unwrap())? + .convert_attribute_value() + .as_m() + .unwrap() + .to_owned(); // content must be map literal let mut image = match initial_item { Some(init_item) => init_item, @@ -2384,92 +2388,44 @@ mod tests { do_test!( AttrVal::N("123".to_owned()), - AttributeValue { - n: Some("123".to_owned()), - ..Default::default() - } + AttributeValue::N("123".to_owned()) ); do_test!( AttrVal::S("string".to_owned()), - AttributeValue { - s: Some("string".to_owned()), - ..Default::default() - } - ); - do_test!( - AttrVal::Bool(true), - AttributeValue { - bool: Some(true), - ..Default::default() - } - ); - do_test!( - AttrVal::Bool(false), - AttributeValue { - bool: Some(false), - ..Default::default() - } - ); - do_test!( - AttrVal::Null(true), - AttributeValue { - null: Some(true), - ..Default::default() - } + AttributeValue::S("string".to_owned()) ); + do_test!(AttrVal::Bool(true), AttributeValue::Bool(true)); + do_test!(AttrVal::Bool(false), AttributeValue::Bool(false)); + do_test!(AttrVal::Null(true), AttributeValue::Null(true)); do_test!( AttrVal::B(Bytes::from_static(b"123")), - AttributeValue { - b: Some(Bytes::from_static(b"123")), - ..Default::default() - } + AttributeValue::B(Blob::new(Bytes::from_static(b"123"))) ); do_test!( AttrVal::L(vec![AttrVal::N("123".to_owned())]), - AttributeValue { - l: Some(vec![AttributeValue { - n: Some("123".to_owned()), - ..Default::default() - }]), - ..Default::default() - } + AttributeValue::L(vec![AttributeValue::N("123".to_owned())]) ); do_test!( AttrVal::M(HashMap::from([( "m".to_owned(), AttrVal::N("123".to_owned()), )])), - AttributeValue { - m: Some(HashMap::from([( - "m".to_owned(), - AttributeValue { - n: Some("123".to_owned()), - ..Default::default() - } - )])), - ..Default::default() - } + AttributeValue::M(HashMap::from([( + "m".to_owned(), + AttributeValue::N("123".to_owned()) + )])) ); do_test!( AttrVal::NS(vec!["123".to_owned()]), - AttributeValue { - ns: Some(vec!["123".to_owned()]), - ..Default::default() - } + AttributeValue::Ns(vec!["123".to_owned()]) ); do_test!( AttrVal::SS(vec!["123".to_owned()]), - AttributeValue { - ss: Some(vec!["123".to_owned()]), - ..Default::default() - } + AttributeValue::Ss(vec!["123".to_owned()]) ); do_test!( AttrVal::BS(vec![Bytes::from_static(b"123")]), - AttributeValue { - bs: Some(vec![Bytes::from_static(b"123")]), - ..Default::default() - } + AttributeValue::Bs(vec![Blob::new(Bytes::from_static(b"123"))]) ); } @@ -2488,13 +2444,7 @@ mod tests { ExpressionResult { exp: format!("{}={}", attr_name_ref(0), attr_val_ref(0)), names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), - values: HashMap::from([( - attr_val_ref(0), - AttributeValue { - n: Some("1".to_owned()), - ..Default::default() - } - )]), + values: HashMap::from([(attr_val_ref(0), AttributeValue::N("1".to_owned()))]), } ); @@ -2510,13 +2460,7 @@ mod tests { ExpressionResult { exp: format!("{}={}", attr_name_ref(0), attr_val_ref(0)), names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), - values: HashMap::from([( - attr_val_ref(0), - AttributeValue { - s: Some("1".to_owned()), - ..Default::default() - } - )]), + values: HashMap::from([(attr_val_ref(0), AttributeValue::S("1".to_owned()))]), } ); @@ -2532,13 +2476,7 @@ mod tests { ExpressionResult { exp: format!("{}>{}", attr_name_ref(0), attr_val_ref(0)), names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), - values: HashMap::from([( - attr_val_ref(0), - AttributeValue { - s: Some("1".to_owned()), - ..Default::default() - } - )]), + values: HashMap::from([(attr_val_ref(0), AttributeValue::S("1".to_owned()))]), } ); @@ -2554,13 +2492,7 @@ mod tests { ExpressionResult { exp: format!("{}>={}", attr_name_ref(0), attr_val_ref(0)), names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), - values: HashMap::from([( - attr_val_ref(0), - AttributeValue { - n: Some("1".to_owned()), - ..Default::default() - } - )]), + values: HashMap::from([(attr_val_ref(0), AttributeValue::N("1".to_owned()))]), } ); @@ -2576,13 +2508,7 @@ mod tests { ExpressionResult { exp: format!("{}<{}", attr_name_ref(0), attr_val_ref(0)), names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), - values: HashMap::from([( - attr_val_ref(0), - AttributeValue { - s: Some("1 2".to_owned()), - ..Default::default() - } - )]), + values: HashMap::from([(attr_val_ref(0), AttributeValue::S("1 2".to_owned()))]), } ); @@ -2598,13 +2524,7 @@ mod tests { ExpressionResult { exp: format!("{}<={}", attr_name_ref(0), attr_val_ref(0)), names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), - values: HashMap::from([( - attr_val_ref(0), - AttributeValue { - n: Some("-1e5".to_owned()), - ..Default::default() - } - )]), + values: HashMap::from([(attr_val_ref(0), AttributeValue::N("-1e5".to_owned()))]), } ); @@ -2628,17 +2548,11 @@ mod tests { values: HashMap::from([ ( attr_val_ref(0), - AttributeValue { - b: Some(Bytes::from_static(b"1")), - ..Default::default() - } + AttributeValue::B(Blob::new(Bytes::from_static(b"1"))) ), ( attr_val_ref(1), - AttributeValue { - b: Some(Bytes::from_static(b"2")), - ..Default::default() - } + AttributeValue::B(Blob::new(Bytes::from_static(b"2"))) ) ]), } @@ -2658,10 +2572,7 @@ mod tests { names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), values: HashMap::from([( attr_val_ref(0), - AttributeValue { - s: Some("id1234#e1234".to_owned()), - ..Default::default() - } + AttributeValue::S("id1234#e1234".to_owned()) )]), } ); @@ -2684,10 +2595,7 @@ mod tests { names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), values: HashMap::from([( attr_val_ref(0), - AttributeValue { - s: Some(expected_val[i].to_owned()), - ..Default::default() - } + AttributeValue::S(expected_val[i].to_owned()) )]), } ); @@ -2738,20 +2646,8 @@ mod tests { ), names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), values: HashMap::from([ - ( - attr_val_ref(0), - AttributeValue { - s: Some("1".to_owned()), - ..Default::default() - } - ), - ( - attr_val_ref(1), - AttributeValue { - s: Some("2".to_owned()), - ..Default::default() - } - ) + (attr_val_ref(0), AttributeValue::S("1".to_owned())), + (attr_val_ref(1), AttributeValue::S("2".to_owned())) ]), } ); @@ -2770,10 +2666,7 @@ mod tests { names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), values: HashMap::from([( attr_val_ref(0), - AttributeValue { - s: Some("id12#i-12@i-12/i-12".to_owned()), - ..Default::default() - } + AttributeValue::S("id12#i-12@i-12/i-12".to_owned()) )]), } ); @@ -2790,13 +2683,7 @@ mod tests { ExpressionResult { exp: format!("{}={}", attr_name_ref(0), attr_val_ref(0),), names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), - values: HashMap::from([( - attr_val_ref(0), - AttributeValue { - s: Some("123".to_owned()), - ..Default::default() - } - )]), + values: HashMap::from([(attr_val_ref(0), AttributeValue::S("123".to_owned()))]), } ); @@ -2812,13 +2699,7 @@ mod tests { ExpressionResult { exp: format!("{}={}", attr_name_ref(0), attr_val_ref(0),), names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), - values: HashMap::from([( - attr_val_ref(0), - AttributeValue { - n: Some("123".to_owned()), - ..Default::default() - } - )]), + values: HashMap::from([(attr_val_ref(0), AttributeValue::N("123".to_owned()))]), } ); } @@ -2845,90 +2726,42 @@ mod tests { ) .unwrap(), HashMap::from([ - ( - "k0".to_owned(), - AttributeValue { - null: Some(true), - ..Default::default() - } - ), + ("k0".to_owned(), AttributeValue::Null(true)), ( "k1".to_owned(), - AttributeValue { - l: Some(Vec::from([ - AttributeValue { - n: Some("1".to_owned()), - ..Default::default() - }, - AttributeValue { - n: Some("2".to_owned()), - ..Default::default() - }, - AttributeValue { - n: Some("3".to_owned()), - ..Default::default() - }, - AttributeValue { - s: Some("str".to_owned()), - ..Default::default() - }, - ])), - ..Default::default() - } - ), - ( - "k2".to_owned(), - AttributeValue { - s: Some("str".to_owned()), - ..Default::default() - } + AttributeValue::L(vec![ + AttributeValue::N("1".to_owned()), + AttributeValue::N("2".to_owned()), + AttributeValue::N("3".to_owned()), + AttributeValue::S("str".to_owned()), + ]) ), + ("k2".to_owned(), AttributeValue::S("str".to_owned())), ( "k3".to_owned(), - AttributeValue { - m: Some(HashMap::from([ - ( - "l0".to_owned(), - AttributeValue { - ns: Some(vec!["1".to_owned(), "2".to_owned()]), - ..Default::default() - } - ), - ( - "l1".to_owned(), - AttributeValue { - ss: Some(vec!["str1".to_owned(), "str2".to_owned()]), - ..Default::default() - } - ), - ( - "l2".to_owned(), - AttributeValue { - bool: Some(true), - ..Default::default() - } - ) - ])), - ..Default::default() - } + AttributeValue::M(HashMap::from([ + ( + "l0".to_owned(), + AttributeValue::Ns(vec!["1".to_owned(), "2".to_owned()]) + ), + ( + "l1".to_owned(), + AttributeValue::Ss(vec!["str1".to_owned(), "str2".to_owned()]) + ), + ("l2".to_owned(), AttributeValue::Bool(true)) + ])) ), ( "k4".to_owned(), - AttributeValue { - b: Some(Bytes::from_static(b"\x20")), - ..Default::default() - } + AttributeValue::B(Blob::new(Bytes::from_static(b"\x20"))) ), ( "k5".to_owned(), - AttributeValue { - bs: Some(vec!( - Bytes::from_static(b"This"), - Bytes::from_static(b"bin"), - Bytes::from_static(b"file"), - )), - ..Default::default() - } + AttributeValue::Bs(vec![ + Blob::new(Bytes::from_static(b"This")), + Blob::new(Bytes::from_static(b"bin")), + Blob::new(Bytes::from_static(b"file")) + ]) ) ]) ) @@ -2942,13 +2775,7 @@ mod tests { ExpressionResult { exp: format!("{}={}", attr_name_ref(0), attr_val_ref(0)), names: HashMap::from([(attr_name_ref(0), "id".to_owned())]), - values: HashMap::from([( - attr_val_ref(0), - AttributeValue { - s: Some("string".to_owned()), - ..Default::default() - } - )]), + values: HashMap::from([(attr_val_ref(0), AttributeValue::S("string".to_owned()))]), } ); } @@ -2984,13 +2811,7 @@ mod tests { fn test_set_and_remove_action() { let mut parser = DyneinParser::new(); let names = HashMap::from([(attr_name_ref(0), "p0".to_owned())]); - let values = HashMap::from([( - attr_val_ref(0), - AttributeValue { - s: Some("string".to_owned()), - ..Default::default() - }, - )]); + let values = HashMap::from([(attr_val_ref(0), AttributeValue::S("string".to_owned()))]); assert_eq!( parser.parse_set_action("p0 = \"string\"").unwrap(), ExpressionResult { diff --git a/src/util.rs b/src/util.rs index af86e03..9cb1cdc 100644 --- a/src/util.rs +++ b/src/util.rs @@ -57,9 +57,9 @@ pub enum Mode { OnDemand, } -impl Into for Mode { - fn into(self) -> BillingMode { - match self { +impl From for BillingMode { + fn from(mode: Mode) -> Self { + match mode { Mode::Provisioned => BillingMode::Provisioned, Mode::OnDemand => BillingMode::PayPerRequest, } @@ -142,7 +142,8 @@ pub fn generate_essential_key_definitions( } else { KeyType::Range }) - .build().unwrap(), + .build() + .unwrap(), ); // If data type of key is omitted, dynein assumes it as String (S). @@ -154,7 +155,8 @@ pub fn generate_essential_key_definitions( } else { ScalarAttributeType::S }) - .build().unwrap(), + .build() + .unwrap(), ) } (key_schema, attribute_definitions) diff --git a/tests/backup.rs b/tests/backup.rs index 148ac36..d056683 100644 --- a/tests/backup.rs +++ b/tests/backup.rs @@ -38,7 +38,7 @@ async fn test_backup() -> Result<(), Box> { .failure() .stdout(predicate::str::contains( // This error message only happens on DynamoDB Local which does not support backup feature. - "com.amazonaws.dynamodb.v20120810#UnknownOperationException", + "unhandled error (UnknownOperationException)", )); Ok(()) diff --git a/tests/export.rs b/tests/export.rs index 8ea97e5..1f39fa7 100644 --- a/tests/export.rs +++ b/tests/export.rs @@ -62,7 +62,7 @@ async fn test_export_empty_table() -> Result<(), Box> { // TODO: this behavior should be fixed by the issue // https://github.com/awslabs/dynein/issues/152 cmd.assert().failure().stderr(predicate::str::contains( - "thread 'main' panicked at src/transfer.rs:478:20:\nattempt to subtract with overflow", + "thread 'main' panicked at src/transfer.rs:481:20:\nattempt to subtract with overflow", )); Ok(()) } diff --git a/tests/restore.rs b/tests/restore.rs index e73fa85..3cef50a 100644 --- a/tests/restore.rs +++ b/tests/restore.rs @@ -38,7 +38,7 @@ async fn test_restore() -> Result<(), Box> { .failure() .stdout(predicate::str::contains( // This error message only happens on DynamoDB Local which does not support backup feature. - "com.amazonaws.dynamodb.v20120810#UnknownOperationException", + "unhandled error (UnknownOperationException)", )); Ok(()) diff --git a/tests/util/mod.rs b/tests/util/mod.rs index 76efa3c..f3a1db4 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -14,16 +14,17 @@ * limitations under the License. */ -use assert_cmd::prelude::*; // Add methods on commands +use assert_cmd::prelude::*; use std::env; use std::process::Command; // Run programs // use assert_cmd::cmd::Command; // Run programs - it seems to be equal to "use assert_cmd::prelude::* + use std::process::Command" +use aws_config::SdkConfig; +use aws_sdk_dynamodb::Client as DynamoDbSdkClient; +use aws_types::region::Region; use once_cell::sync::Lazy; use rand::{distributions::Alphanumeric, Rng}; use regex::bytes::Regex; -use rusoto_core::Region; -use rusoto_dynamodb::{DynamoDb, DynamoDbClient}; use serde_json::Value; use std::io::{self, Write}; // Used when check results by printing to stdout use std::path::{Path, PathBuf}; @@ -339,14 +340,15 @@ async fn setup_container(port: i32) -> Result<(), Box> { // Wait dynamodb-local let health_check_url = format!("http://localhost:{}", port); - let ddb = DynamoDbClient::new(Region::Custom { - name: "local".to_owned(), - endpoint: health_check_url, - }); + // let ddb = DynamoDbClient::new(Region::Custom { + // name: "local".to_owned(), + // endpoint: health_check_url, + // }); + let ddb = DynamoDbSdkClient::new(&SdkConfig::builder().region(Region::new("local")).build()); let max_retries = 5; let mut attempts = 0; loop { - match ddb.list_tables(Default::default()).await { + match ddb.list_tables().send().await { Ok(_result) => { println!("ListTables API succeeded."); break;