I am new to Rust and this question may come off as silly. I am trying to develop a lambda that reads a single item from dynamoDB given a key. The returned item needs to be shared back as result to the calling lambda.
I want the response to be in JSON.
Here is what I have:
The Input Struct
#[derive(Deserialize, Clone)]
struct CustomEvent {
#[serde(rename = "user_id")]
user_id: String,
}
The Output Struct
#[derive(Serialize, Clone)]
struct CustomOutput {
user_name: String,
user_email: String,
}
The Main fn
#[tokio::main]
async fn main() -> std::result::Result<(), Error> {
let func = handler_fn(get_user_details);
lambda_runtime::run(func).await?;
Ok(())
}
The logic to query
async fn get_user_details(
e: CustomEvent,
_c: Context,
) -> std::result::Result<CustomOutput, Error> {
if e.user_id == "" {
error!("User Id must be specified as user_id in the request");
}
let region_provider =
RegionProviderChain::first_try(Region::new("ap-south-1")).or_default_provider();
let shared_config = aws_config::from_env().region(region_provider).load().await;
let client: Client = Client::new(&shared_config);
let resp: () = query_user(&client, &e.user_id).await?;
println!("{:?}", resp);
Ok(CustomOutput {
// Does not work
// user_name: resp[0].user_name,
// user_email: resp[0].user_email,
// Works because it is hardcoded
user_name: "hello".to_string(),
user_email: "[email protected]".to_string()
})
}
async fn query_user(
client: &Client,
user_id: &str,
) -> Result<(), Error> {
let user_id_av = AttributeValue::S(user_id.to_string());
let resp = client
.query()
.table_name("users")
.key_condition_expression("#key = :value".to_string())
.expression_attribute_names("#key".to_string(), "id".to_string())
.expression_attribute_values(":value".to_string(), user_id_av)
.projection_expression("user_email")
.send()
.await?;
println!("{:?}", resp.items.unwrap_or_default()[0]);
return Ok(resp.items.unwrap_or_default().pop().as_ref());
}
My TOML
[dependencies]
lambda_runtime = "^0.4"
serde = "^1"
serde_json = "^1"
serde_derive = "^1"
http = "0.2.5"
rand = "0.8.3"
tokio-stream = "0.1.8"
structopt = "0.3"
aws-config = "0.12.0"
aws-sdk-dynamodb = "0.12.0"
log = "^0.4"
simple_logger = "^1"
tokio = { version = "1.5.0", features = ["full"] }
I am unable to unwrap and send the response back to the called lambda. From query_user function, I want to be able to return a constructed CustomOutput struct to this
Ok(CustomOutput {
// user_name: resp[0].user_name,
// user_email: resp[0].user_email,
})
block in get_user_details. Any help or references would help a lot. Thank you.
After several attempts, here is what I learnt: The match keyword can be used instead of collecting the result in a variable. I did this:
match client
.query()
.table_name("users")
.key_condition_expression("#key = :value".to_string())
.expression_attribute_names("#key".to_string(), "id".to_string())
.expression_attribute_values(":value".to_string(), user_id_av)
.projection_expression("user_email")
.send()
.await
{
Ok(resp) => Ok(resp.items),
Err(e) => Err(e),
}
When a response comes back from the DB, it will have to have an items key-value inside it.
so, this line:
Ok(resp) => Ok(resp.items)
will ensure that the items array is returned to the calling function.
Next, to get the values one by one out of the Hashmap and load them into CustomOutput, this is what I did:
let resp: std::option::Option<Vec<HashMap<std::string::String, AttributeValue>>> = query_user(&client, &e.user_id).await?;
Once I have the resp, I can burrow down to the first element if I need like this:
x[0]
.get("user_name")
.unwrap()
.as_s()
.unwrap()
.to_string()
and for Number types maybe something like "battery_voltage":
x[0]
.get("battery_voltage")
.unwrap()
.as_n()
.unwrap()
.to_string()
.parse::<f32>()
.unwrap(),
Finally, use a match block to determine the Some or None for the data:
match _val {
Some(x) => {
// pattern
if x.len() > 0 {
return Ok(json!(CustomOutput {
user_name: x[0].get("user_name").unwrap().as_s().unwrap().to_string(),
user_email: x[0].get("user_email").unwrap().as_s().unwrap().to_string(),
}));
} else {
return Ok(json!({
"code": "404".to_string(),
"message": "Not found.".to_string(),
}));
}
}
None => {
// other pattern
println!("Got nothing");
return Ok(json!({
"code": "404".to_string(),
"message": "Not found.".to_string(),
}));
}
Hope this helps someone else!