athena_rs 3.3.0

Database gateway API
Documentation
//! Update gateway helpers that timestamp and validate insert payloads for `/gateway/insert`.
use actix_web::{
    HttpRequest, HttpResponse,
    http::StatusCode,
    put,
    web::{Data, Json},
};
use serde_json::{Value, json};
use std::time::Instant;
use uuid::Uuid;

use crate::AppState;
use crate::api::gateway::update::table_id_map::get_resource_id_key;
use crate::api::headers::x_athena_client::x_athena_client;
use crate::api::headers::x_company_id::get_x_company_id;
use crate::api::headers::x_organization_id::get_x_organization_id;
use crate::api::headers::x_user_id::get_x_user_id;

use crate::utils::request_logging::{LoggedRequest, log_operation_event, log_request};

#[derive(Debug, serde::Deserialize)]
/// Represents the payload accepted by the `/gateway/insert` update helper.
struct UpdateRequest {
    /// Target table for the inserted payload.
    table_name: String,
    /// Main insert payload to be enriched.
    insert_body: Value,
    /// Optional update diff attached to the request.
    #[serde(default)]
    #[allow(unused)]
    update_body: Option<Value>,
}

#[put("/gateway/insert")]
/// Placeholder for `/gateway/insert` that validates headers, annotates the payload, and returns metadata.
///
/// # Parameters
/// - `req`: Request that must include the Athena headers.
/// - `body`: Payload with `table_name`, `insert_body`, and optional `update_body`.
///
/// # Returns
/// JSON describing the augmented payload, newly generated UUID, and table name, or `400` if headers are missing.
///
/// # Example
/// ```http
/// PUT /gateway/insert
/// X-Athena-Client: supabase
/// X-Company-Id: comp-1
/// X-Organization-Id: org-1
/// Content-Type: application/json
///
/// {
///   "table_name": "users",
///   "insert_body": { "name": "Alice" }
/// }
/// ```
async fn insert_data(
    req: HttpRequest,
    body: Json<UpdateRequest>,
    app_state: Data<AppState>,
) -> HttpResponse {
    let logged_request: LoggedRequest =
        log_request(req.clone(), Some(app_state.get_ref()), None, None);
    let operation_start: Instant = Instant::now();

    #[allow(unused)]
    let client_name: String = x_athena_client(&req);
    #[allow(unused)]
    let _user_id: Option<String> = get_x_user_id(&req);

    let _company_id: String = match get_x_company_id(&req) {
        Some(id) => id,
        None => {
            return HttpResponse::BadRequest()
                .json(json!({"error": "X-Company-Id header not found in the request"}));
        }
    };

    let _organization_id: String = match get_x_organization_id(&req) {
        Some(id) => id,
        None => {
            return HttpResponse::BadRequest()
                .json(json!({"error": "X-Organization-Id header not found in the request"}));
        }
    };

    let table_name: String = body.table_name.clone();
    let mut insert_body: Value = body.insert_body.clone();

    // Generate a UUID and add it to the insert_body under the mapped id key
    let id_key: String = get_resource_id_key(&table_name).await;
    let new_uuid: String = Uuid::new_v4().to_string();
    if let Some(obj) = insert_body.as_object_mut() {
        obj.insert(id_key.to_string(), Value::String(new_uuid.clone()));
    }

    log_operation_event(
        Some(app_state.get_ref()),
        &logged_request,
        "insert_update_stub",
        Some(&table_name),
        operation_start.elapsed().as_millis(),
        StatusCode::OK,
        Some(json!({
            "resource_id": new_uuid,
        })),
    );

    HttpResponse::Ok().json(json!({
        "status": "success",
        "success": true,
        "message": "Data inserted successfully",
        "data": insert_body,
        "resource_id": new_uuid,
        "table_name": table_name,
    }))
}

pub mod table_id_map;