awsim-lambda 0.4.0

AWSim Lambda service emulator
Documentation
use std::time::{SystemTime, UNIX_EPOCH};

use awsim_core::{AwsError, RequestContext};
use serde_json::{Value, json};

use crate::{
    error::resource_not_found,
    state::{EventInvokeConfig, LambdaState},
    util::require_str,
};

fn now_epoch() -> f64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|d| d.as_secs_f64())
        .unwrap_or(0.0)
}

fn key(function_name: &str, qualifier: Option<&str>) -> String {
    match qualifier {
        Some(q) => format!("{function_name}:{q}"),
        None => function_name.to_string(),
    }
}

fn qualifier_of(input: &Value) -> Option<&str> {
    input.get("Qualifier").and_then(|v| v.as_str())
}

fn destination_of(input: &Value, field: &str) -> Option<String> {
    input
        .get("DestinationConfig")
        .and_then(|d| d.get(field))
        .and_then(|d| d.get("Destination"))
        .and_then(|d| d.as_str())
        .map(|s| s.to_string())
}

fn config_to_json(cfg: &EventInvokeConfig) -> Value {
    let mut destination_config = serde_json::Map::new();
    if let Some(d) = &cfg.destination_on_success {
        destination_config.insert("OnSuccess".to_string(), json!({ "Destination": d }));
    }
    if let Some(d) = &cfg.destination_on_failure {
        destination_config.insert("OnFailure".to_string(), json!({ "Destination": d }));
    }

    json!({
        "FunctionArn": cfg.function_arn,
        "MaximumRetryAttempts": cfg.maximum_retry_attempts,
        "MaximumEventAgeInSeconds": cfg.maximum_event_age_in_seconds,
        "DestinationConfig": Value::Object(destination_config),
        "LastModified": cfg.last_modified,
    })
}

pub fn put_function_event_invoke_config(
    state: &LambdaState,
    input: &Value,
    _ctx: &RequestContext,
) -> Result<Value, AwsError> {
    let name = require_str(input, "FunctionName")?;
    let func = state
        .functions
        .get(name)
        .ok_or_else(|| resource_not_found("function", name))?;

    let k = key(name, qualifier_of(input));
    let cfg = EventInvokeConfig {
        function_arn: func.arn.clone(),
        maximum_retry_attempts: input
            .get("MaximumRetryAttempts")
            .and_then(|v| v.as_i64())
            .map(|n| n as i32),
        maximum_event_age_in_seconds: input
            .get("MaximumEventAgeInSeconds")
            .and_then(|v| v.as_i64())
            .map(|n| n as i32),
        destination_on_success: destination_of(input, "OnSuccess"),
        destination_on_failure: destination_of(input, "OnFailure"),
        last_modified: now_epoch(),
    };

    let result = config_to_json(&cfg);
    state.event_invoke_configs.insert(k, cfg);
    Ok(result)
}

pub fn get_function_event_invoke_config(
    state: &LambdaState,
    input: &Value,
    _ctx: &RequestContext,
) -> Result<Value, AwsError> {
    let name = require_str(input, "FunctionName")?;
    let k = key(name, qualifier_of(input));
    let cfg = state
        .event_invoke_configs
        .get(&k)
        .ok_or_else(|| resource_not_found("event-invoke-config", &k))?;
    Ok(config_to_json(&cfg))
}

pub fn update_function_event_invoke_config(
    state: &LambdaState,
    input: &Value,
    ctx: &RequestContext,
) -> Result<Value, AwsError> {
    put_function_event_invoke_config(state, input, ctx)
}

pub fn delete_function_event_invoke_config(
    state: &LambdaState,
    input: &Value,
    _ctx: &RequestContext,
) -> Result<Value, AwsError> {
    let name = require_str(input, "FunctionName")?;
    let k = key(name, qualifier_of(input));
    state.event_invoke_configs.remove(&k);
    Ok(json!({}))
}

pub fn list_function_event_invoke_configs(
    state: &LambdaState,
    input: &Value,
    _ctx: &RequestContext,
) -> Result<Value, AwsError> {
    let name = require_str(input, "FunctionName")?;
    let prefix = name.to_string();
    let configs: Vec<Value> = state
        .event_invoke_configs
        .iter()
        .filter(|e| e.key() == &prefix || e.key().starts_with(&format!("{prefix}:")))
        .map(|e| config_to_json(e.value()))
        .collect();
    Ok(json!({ "FunctionEventInvokeConfigs": configs }))
}