rag-module 0.6.7

Enterprise RAG module with chat context storage, vector search, session management, and model downloading. Rust implementation with Node.js compatibility.
//! Lambda Service Parser
//! 
//! Parses AWS Lambda service data including functions, layers, and aliases.

use async_trait::async_trait;
use anyhow::{Result, anyhow};
use serde_json::{json, Value};
use std::collections::HashMap;
use indexmap::IndexMap;
use tracing::{debug, warn};

use crate::types::Document;
use crate::services::aws_estate_service::AwsServiceParser;

/// Parser for AWS Lambda service
pub struct LambdaParser;

impl LambdaParser {
    pub fn new() -> Self {
        Self
    }
    
    /// Generate AWS ARN
    fn generate_arn(
        service: &str,
        region: &str,
        account_id: &str,
        resource_type: &str,
        resource_id: &str,
    ) -> String {
        format!("arn:aws:{}:{}:{}:{}:{}", service, region, account_id, resource_type, resource_id)
    }
    
    /// Create base metadata for AWS resources
    fn create_base_metadata(
        account_id: &str,
        service: &str,
        resource_type: &str,
        region: Option<&str>,
    ) -> IndexMap<String, Value> {
        let mut metadata = IndexMap::new();
        metadata.insert("account_id".to_string(), json!(account_id));
        metadata.insert("service".to_string(), json!(service));
        metadata.insert("resource_type".to_string(), json!(resource_type));
        metadata.insert("cloud_provider".to_string(), json!("aws"));
        metadata.insert("document_type".to_string(), json!("aws_estate"));
        metadata.insert("last_synced".to_string(), json!(chrono::Utc::now().timestamp()));
        
        if let Some(region_val) = region {
            metadata.insert("region".to_string(), json!(region_val));
        }
        
        metadata
    }
    
    /// Parse Lambda functions from the service data
    fn parse_lambda_functions(&self, account_id: &str, functions: &[Value]) -> Result<Vec<Document>> {
        let mut documents = Vec::new();
        
        for function in functions {
            let function_name = function["name"]
                .as_str()
                .or_else(|| function["function_name"].as_str())
                .or_else(|| function["FunctionName"].as_str())
                .unwrap_or("unknown");
            let region = function["region"]
                .as_str()
                .unwrap_or("us-east-1");
            let runtime = function["runtime"]
                .as_str()
                .unwrap_or("unknown");
            let timeout = function["timeout"]
                .as_u64()
                .unwrap_or(0);
            let memory_size = function["memory_size"]
                .as_u64()
                .unwrap_or(0);
            let handler = function["handler"]
                .as_str()
                .unwrap_or("unknown");
            let last_modified = function["last_modified"]
                .as_str()
                .unwrap_or("unknown");
            
            // Generate ARN for the Lambda function
            let doc_id = Self::generate_arn(
                "lambda",
                region,
                account_id,
                "function",
                function_name,
            );
            
            // Create searchable content
            let content = format!(
                "Lambda Function {} in region {} - Runtime: {} - Memory: {}MB - Timeout: {}s - Handler: {}",
                function_name, region, runtime, memory_size, timeout, handler
            );
            
            // Create base metadata
            let mut metadata = Self::create_base_metadata(
                account_id,
                "lambda",
                "lambda-function",
                Some(region),
            );
            
            // Add Lambda-specific metadata
            metadata.insert("function_name".to_string(), json!(function_name));
            metadata.insert("runtime".to_string(), json!(runtime));
            metadata.insert("timeout".to_string(), json!(timeout));
            metadata.insert("memory_size".to_string(), json!(memory_size));
            metadata.insert("handler".to_string(), json!(handler));
            metadata.insert("last_modified".to_string(), json!(last_modified));
            
            // Add optional fields
            if let Some(code_size) = function.get("code_size").and_then(|v| v.as_u64()) {
                metadata.insert("code_size".to_string(), json!(code_size));
            }
            
            if let Some(role) = function.get("role").and_then(|v| v.as_str()) {
                metadata.insert("role".to_string(), json!(role));
            }
            
            if let Some(environment) = function.get("environment") {
                metadata.insert("environment".to_string(), environment.clone());
            }
            
            if let Some(layers) = function.get("layers") {
                metadata.insert("layers".to_string(), layers.clone());
            }
            
            // Add IAM permissions if available
            if let Some(permissions) = function.get("permissions") {
                metadata.insert("iam_permissions".to_string(), permissions.clone());
            }
            
            metadata.insert("tags".to_string(), json!({
                "FunctionName": function_name,
                "Runtime": runtime
            }));
            
            let mut doc = Document::new(doc_id, content);
            doc.metadata = metadata;
            documents.push(doc);
        }
        
        Ok(documents)
    }
    
    /// Parse Lambda layers summary
    fn parse_layers_summary(&self, account_id: &str, service_data: &Value) -> Result<Option<Document>> {
        if let Some(layers_count) = service_data.get("layers_count").and_then(|v| v.as_u64()) {
            if layers_count > 0 {
                let doc_id = format!("arn:aws:lambda:global:{}:layers-summary", account_id);
                
                let content = format!(
                    "Lambda Layers Summary: {} total layers available for reuse | Cloud Provider: aws",
                    layers_count
                );
                
                let mut metadata = Self::create_base_metadata(
                    account_id,
                    "lambda",
                    "lambda-layers-summary",
                    Some("global"),
                );
                
                metadata.insert("layers_count".to_string(), json!(layers_count));
                
                let mut doc = Document::new(doc_id, content);
                doc.metadata = metadata;
                return Ok(Some(doc));
            }
        }
        
        Ok(None)
    }
    
    /// Parse Lambda event source mappings summary
    fn parse_event_sources_summary(&self, account_id: &str, service_data: &Value) -> Result<Option<Document>> {
        if let Some(event_sources_count) = service_data.get("event_sources_count").and_then(|v| v.as_u64()) {
            if event_sources_count > 0 {
                let doc_id = format!("arn:aws:lambda:global:{}:event-sources-summary", account_id);
                
                let content = format!(
                    "Lambda Event Sources Summary: {} event source mappings configured | Cloud Provider: aws",
                    event_sources_count
                );
                
                let mut metadata = Self::create_base_metadata(
                    account_id,
                    "lambda",
                    "lambda-event-sources-summary",
                    Some("global"),
                );
                
                metadata.insert("event_sources_count".to_string(), json!(event_sources_count));
                
                let mut doc = Document::new(doc_id, content);
                doc.metadata = metadata;
                return Ok(Some(doc));
            }
        }
        
        Ok(None)
    }
}

#[async_trait]
impl AwsServiceParser for LambdaParser {
    fn service_name(&self) -> &str {
        "lambda"
    }
    
    fn can_parse(&self, service_data: &Value) -> bool {
        // Check if this looks like Lambda data
        service_data.is_object() && (
            service_data.get("functions").is_some() ||
            service_data.get("layers_count").is_some() ||
            service_data.get("event_sources_count").is_some()
        )
    }
    
    async fn parse(&self, account_id: &str, service_data: &Value) -> Result<Vec<Document>> {
        debug!("🔍 Lambda parser processing data for account: {}", account_id);
        
        let mut documents = Vec::new();
        
        // Parse Lambda functions
        if let Some(functions) = service_data.get("functions").and_then(|v| v.as_array()) {
            if !functions.is_empty() {
                let mut function_docs = self.parse_lambda_functions(account_id, functions)?;
                documents.append(&mut function_docs);
                debug!("✅ Parsed {} Lambda functions", functions.len());
            }
        }
        
        // Parse layers summary
        if let Some(layers_doc) = self.parse_layers_summary(account_id, service_data)? {
            documents.push(layers_doc);
            debug!("✅ Parsed Lambda layers summary");
        }
        
        // Parse event sources summary
        if let Some(event_sources_doc) = self.parse_event_sources_summary(account_id, service_data)? {
            documents.push(event_sources_doc);
            debug!("✅ Parsed Lambda event sources summary");
        }
        
        if documents.is_empty() {
            warn!("🟡 Lambda parser found no parseable data");
        } else {
            debug!("🎉 Lambda parser generated {} documents", documents.len());
        }
        
        Ok(documents)
    }
    
    fn get_data_schema(&self) -> Option<Value> {
        Some(json!({
            "type": "object",
            "properties": {
                "functions": {
                    "type": "array",
                    "items": {
                        "type": "object",
                        "required": ["function_name", "region", "runtime", "handler"],
                        "properties": {
                            "function_name": {"type": "string"},
                            "region": {"type": "string"},
                            "runtime": {"type": "string"},
                            "timeout": {"type": "number"},
                            "memory_size": {"type": "number"},
                            "handler": {"type": "string"},
                            "last_modified": {"type": "string"},
                            "code_size": {"type": "number"},
                            "role": {"type": "string"},
                            "environment": {"type": "object"},
                            "layers": {"type": "array"},
                            "permissions": {"type": "array"}
                        }
                    }
                },
                "layers_count": {"type": "number"},
                "event_sources_count": {"type": "number"}
            }
        }))
    }
}