diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 789825d..a8829c7 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1275,6 +1275,7 @@ dependencies = [ "async-openai", "aws-config", "aws-sdk-dynamodb", + "base64 0.22.1", "futures", "http 0.2.12", "log", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 6b1b0f4..24e0ee9 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -30,6 +30,7 @@ log = "0.4.22" futures = "0.3.30" aws-config = "1.6.1" aws-sdk-dynamodb = "1.71.2" +base64 = "0.22.1" [features] # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! custom-protocol = ["tauri/custom-protocol"] diff --git a/src-tauri/src/dynamo_client.rs b/src-tauri/src/dynamo_client.rs index aa0a602..be36a0b 100644 --- a/src-tauri/src/dynamo_client.rs +++ b/src-tauri/src/dynamo_client.rs @@ -3,6 +3,7 @@ use aws_sdk_dynamodb::{Client, config::Credentials, types::AttributeValue}; use aws_config::Region; use serde::{Deserialize, Serialize}; use serde_json::json; +use base64; #[derive(Debug, Deserialize)] pub struct DynamoCredentials { @@ -25,6 +26,23 @@ struct ApiResponse { data: Option, } +fn convert_json_to_attr_value(value: &serde_json::Value) -> Option { + match value { + serde_json::Value::String(s) => Some(AttributeValue::S(s.clone())), + serde_json::Value::Number(n) => Some(AttributeValue::N(n.to_string())), + serde_json::Value::Bool(b) => Some(AttributeValue::Bool(*b)), + serde_json::Value::Null => Some(AttributeValue::Null(true)), + serde_json::Value::Array(arr) => Some(AttributeValue::L( + arr.iter().filter_map(|v| convert_json_to_attr_value(v)).collect() + )), + serde_json::Value::Object(map) => Some(AttributeValue::M( + map.iter().filter_map(|(k, v)| { + convert_json_to_attr_value(v).map(|av| (k.clone(), av)) + }).collect() + )), + } +} + #[tauri::command] pub async fn dynamo_api( window: tauri::Window, @@ -65,7 +83,8 @@ pub async fn dynamo_api( let table_info = json!({ "id": response.table().and_then(|t| t.table_id()), "name": response.table().map(|t| t.table_name()), - "status": response.table().and_then(|t| t.table_status().map(|s| s.as_str().to_string())), "itemCount": response.table().and_then(|t| t.item_count()), + "status": response.table().and_then(|t| t.table_status().map(|s| s.as_str().to_string())), + "itemCount": response.table().and_then(|t| t.item_count()), "sizeBytes": response.table().and_then(|t| t.table_size_bytes()), "keySchema": response.table().and_then(|t| { Some(t.key_schema().iter().map(|k| { @@ -146,18 +165,79 @@ pub async fn dynamo_api( }) } }, - "put_item" => { - if let Some(_payload) = &options.payload { - // Implementation for put_item would go here - Ok(ApiResponse { - status: 200, - message: "Item put successfully".to_string(), - data: None, - }) + "CREATE_ITEM" => { + if let Some(payload) = &options.payload { + // Expecting payload to have an "attributes" array + if let Some(attributes) = payload.get("attributes").and_then(|v| v.as_array()) { + let mut put_item = client.put_item().table_name(&options.table_name); + + for attr in attributes { + if let (Some(key), Some(value), Some(attr_type)) = ( + attr.get("key").and_then(|v| v.as_str()), + attr.get("value"), + attr.get("type").and_then(|v| v.as_str()), + ) { + let attr_value = match attr_type { + "S" => value.as_str().map(|s| AttributeValue::S(s.to_string())), + "N" => value.as_f64().map(|n| AttributeValue::N(n.to_string())), + "B" => value.as_str().map(|s| AttributeValue::B( + aws_sdk_dynamodb::primitives::Blob::new(base64::decode(s).unwrap_or_default()) + )), + "BOOL" => value.as_bool().map(AttributeValue::Bool), + "NULL" => Some(AttributeValue::Null(true)), + "SS" => value.as_array().map(|arr| { + AttributeValue::Ss(arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect()) + }), + "NS" => value.as_array().map(|arr| { + AttributeValue::Ns(arr.iter().filter_map(|v| v.as_f64().map(|n| n.to_string())).collect()) + }), + "BS" => value.as_array().map(|arr| { + AttributeValue::Bs(arr.iter().filter_map(|v| v.as_str().map(|s| { + aws_sdk_dynamodb::primitives::Blob::new(base64::decode(s).unwrap_or_default()) + })).collect()) + }), + "L" => value.as_array().map(|arr| { + AttributeValue::L(arr.iter().filter_map(|v| { + // Recursively convert each element + convert_json_to_attr_value(v) + }).collect()) + }), + "M" => value.as_object().map(|map| { + AttributeValue::M(map.iter().filter_map(|(k, v)| { + convert_json_to_attr_value(v).map(|av| (k.clone(), av)) + }).collect()) + }), + _ => None, + }; + if let Some(av) = attr_value { + put_item = put_item.item(key, av); + } + } + } + + match put_item.send().await { + Ok(_) => Ok(ApiResponse { + status: 200, + message: "Item created successfully".to_string(), + data: None, + }), + Err(e) => Ok(ApiResponse { + status: 500, + message: format!("Failed to create item: {}", e), + data: None, + }), + } + } else { + Ok(ApiResponse { + status: 400, + message: "Attributes array is required".to_string(), + data: None, + }) + } } else { Ok(ApiResponse { status: 400, - message: "Item is required for put_item operation".to_string(), + message: "Item payload is required".to_string(), data: None, }) } diff --git a/src/components/tool-bar.vue b/src/components/tool-bar.vue index 710a8f3..522b274 100644 --- a/src/components/tool-bar.vue +++ b/src/components/tool-bar.vue @@ -61,7 +61,7 @@ {{ $t('editor.loadDefault') }} - +