redis-cloud 0.10.0

Redis Cloud REST API client library
Documentation
//! AWS Transit Gateway (TGW) attachment operations.
//!
//! Manages AWS Transit Gateway attachments and CIDR allow-lists so a Redis
//! Cloud subscription can ride a hub-and-spoke topology you already
//! operate, rather than terminating its own VPC peering connection.
//!
//! # When to use this module
//!
//! - You already run a Transit Gateway and want Redis Cloud subscriptions
//!   to attach to it for centralized routing and segmentation.
//! - You need to extend or update the CIDRs a subscription is allowed to
//!   reach through its TGW attachment.
//!
//! For direct point-to-point AWS peering see
//! [`crate::connectivity::vpc_peering`]; for endpoint-style PrivateLink
//! connectivity see [`crate::connectivity::private_link`].
//!
//! # Endpoint surface
//!
//! Standard subscriptions:
//!
//! - `GET    /subscriptions/{subscriptionId}/transitGateways`
//! - `POST   /subscriptions/{subscriptionId}/transitGateways/{tgwId}`
//! - `DELETE /subscriptions/{subscriptionId}/transitGateways/{tgwId}`
//! - `PUT    /subscriptions/{subscriptionId}/transitGateways/{tgwId}/attachment`
//!
//! Active-Active subscriptions expose the same surface under
//! `/subscriptions/{subscriptionId}/regions/{regionId}/...`.
//!
//! # Errors
//!
//! All operations return [`crate::Result`]; transport, auth, and 4xx/5xx
//! responses surface as the corresponding [`crate::CloudError`] variant.

use crate::{CloudClient, Result};
use serde::{Deserialize, Serialize};

/// CIDR block definition
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Cidr {
    /// CIDR notation address (e.g. `"10.0.0.0/16"`).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cidr_address: Option<String>,
}

/// Transit Gateway CIDRs update request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TgwUpdateCidrsRequest {
    /// Optional. List of transit gateway attachment CIDRs.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cidrs: Option<Vec<Cidr>>,

    /// Read-only on the response; populated by the server with the
    /// operation type (e.g. `"UPDATE_TGW_CIDRS"`).
    #[serde(skip_serializing_if = "Option::is_none")]
    pub command_type: Option<String>,
}

/// Transit Gateway attachment request
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TgwAttachmentRequest {
    /// AWS account ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub aws_account_id: Option<String>,

    /// Transit Gateway ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub tgw_id: Option<String>,

    /// CIDR blocks to route through the TGW
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cidrs: Option<Vec<String>>,
}

/// Task state update response
pub use crate::types::TaskStateUpdate;

/// Transit Gateway attachment information
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransitGatewayAttachment {
    /// Attachment ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<i32>,

    /// AWS Transit Gateway UID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub aws_tgw_uid: Option<String>,

    /// AWS attachment UID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attachment_uid: Option<String>,

    /// Attachment status
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<String>,

    /// AWS attachment status
    #[serde(skip_serializing_if = "Option::is_none")]
    pub attachment_status: Option<String>,

    /// AWS account ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub aws_account_id: Option<String>,

    /// CIDR blocks
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cidrs: Option<Vec<CidrStatus>>,
}

/// CIDR block with status
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CidrStatus {
    /// CIDR address
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cidr_address: Option<String>,

    /// CIDR status
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<String>,
}

/// Transit Gateway resource share invitation
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TransitGatewayInvitation {
    /// Invitation ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub id: Option<i32>,

    /// Invitation name
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,

    /// AWS Resource share UID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub resource_share_uid: Option<String>,

    /// AWS account ID
    #[serde(skip_serializing_if = "Option::is_none")]
    pub aws_account_id: Option<String>,

    /// Invitation status
    #[serde(skip_serializing_if = "Option::is_none")]
    pub status: Option<String>,

    /// Date the resource was shared
    #[serde(skip_serializing_if = "Option::is_none")]
    pub shared_date: Option<String>,
}

/// Transit Gateway handler
pub struct TransitGatewayHandler {
    client: CloudClient,
}

impl TransitGatewayHandler {
    /// Create a new Transit Gateway handler
    #[must_use]
    pub fn new(client: CloudClient) -> Self {
        Self { client }
    }

    // ========================================================================
    // Standard Transit Gateway Operations
    // ========================================================================

    /// Get Transit Gateway attachments
    pub async fn get_attachments(&self, subscription_id: i32) -> Result<TaskStateUpdate> {
        self.client
            .get(&format!("/subscriptions/{subscription_id}/transitGateways"))
            .await
    }

    /// Get Transit Gateway shared invitations
    pub async fn get_shared_invitations(&self, subscription_id: i32) -> Result<TaskStateUpdate> {
        self.client
            .get(&format!(
                "/subscriptions/{subscription_id}/transitGateways/invitations"
            ))
            .await
    }

    /// Accept Transit Gateway resource share
    pub async fn accept_resource_share(
        &self,
        subscription_id: i32,
        invitation_id: String,
    ) -> Result<TaskStateUpdate> {
        self.client
            .put(
                &format!(
                    "/subscriptions/{subscription_id}/transitGateways/invitations/{invitation_id}/accept"
                ),
                &serde_json::json!({}),
            )
            .await
    }

    /// Reject Transit Gateway resource share
    pub async fn reject_resource_share(
        &self,
        subscription_id: i32,
        invitation_id: String,
    ) -> Result<TaskStateUpdate> {
        self.client
            .put(
                &format!(
                    "/subscriptions/{subscription_id}/transitGateways/invitations/{invitation_id}/reject"
                ),
                &serde_json::json!({}),
            )
            .await
    }

    /// Delete Transit Gateway attachment
    pub async fn delete_attachment(
        &self,
        subscription_id: i32,
        attachment_id: String,
    ) -> Result<TaskStateUpdate> {
        self.client
            .delete_typed(&format!(
                "/subscriptions/{subscription_id}/transitGateways/{attachment_id}/attachment"
            ))
            .await
    }

    /// Create Transit Gateway attachment with `tgw_id` in path
    pub async fn create_attachment_with_id(
        &self,
        subscription_id: i32,
        tgw_id: &str,
    ) -> Result<TaskStateUpdate> {
        let request = TgwAttachmentRequest {
            tgw_id: Some(tgw_id.to_string()),
            aws_account_id: None,
            cidrs: None,
        };

        self.client
            .post(
                &format!("/subscriptions/{subscription_id}/transitGateways/{tgw_id}/attachment"),
                &request,
            )
            .await
    }

    /// Update Transit Gateway attachment CIDRs
    pub async fn update_attachment_cidrs(
        &self,
        subscription_id: i32,
        attachment_id: String,
        request: &TgwAttachmentRequest,
    ) -> Result<TaskStateUpdate> {
        self.client
            .put(
                &format!(
                    "/subscriptions/{subscription_id}/transitGateways/{attachment_id}/attachment"
                ),
                request,
            )
            .await
    }

    // ========================================================================
    // Active-Active Transit Gateway Operations
    // ========================================================================

    /// Get Active-Active Transit Gateway attachments for a region
    pub async fn get_attachments_active_active(
        &self,
        subscription_id: i32,
        region_id: i32,
    ) -> Result<TaskStateUpdate> {
        self.client
            .get(&format!(
                "/subscriptions/{subscription_id}/regions/{region_id}/transitGateways"
            ))
            .await
    }

    /// Get Active-Active Transit Gateway shared invitations for a region
    pub async fn get_shared_invitations_active_active(
        &self,
        subscription_id: i32,
        region_id: i32,
    ) -> Result<TaskStateUpdate> {
        self.client
            .get(&format!(
                "/subscriptions/{subscription_id}/regions/{region_id}/transitGateways/invitations"
            ))
            .await
    }

    /// Accept Active-Active Transit Gateway resource share
    pub async fn accept_resource_share_active_active(
        &self,
        subscription_id: i32,
        region_id: i32,
        invitation_id: String,
    ) -> Result<TaskStateUpdate> {
        self.client
            .put(
                &format!(
                    "/subscriptions/{subscription_id}/regions/{region_id}/transitGateways/invitations/{invitation_id}/accept"
                ),
                &serde_json::json!({}),
            )
            .await
    }

    /// Reject Active-Active Transit Gateway resource share
    pub async fn reject_resource_share_active_active(
        &self,
        subscription_id: i32,
        region_id: i32,
        invitation_id: String,
    ) -> Result<TaskStateUpdate> {
        self.client
            .put(
                &format!(
                    "/subscriptions/{subscription_id}/regions/{region_id}/transitGateways/invitations/{invitation_id}/reject"
                ),
                &serde_json::json!({}),
            )
            .await
    }

    /// Delete Active-Active Transit Gateway attachment
    pub async fn delete_attachment_active_active(
        &self,
        subscription_id: i32,
        region_id: i32,
        tgw_id: String,
    ) -> Result<TaskStateUpdate> {
        self.client.delete_typed(&format!(
                "/subscriptions/{subscription_id}/regions/{region_id}/transitGateways/{tgw_id}/attachment"
            )).await
    }

    /// Create Active-Active Transit Gateway attachment
    pub async fn create_attachment_active_active(
        &self,
        subscription_id: i32,
        region_id: i32,
        tgw_id: &str,
        request: &TgwAttachmentRequest,
    ) -> Result<TaskStateUpdate> {
        self.client
            .post(
                &format!(
                    "/subscriptions/{subscription_id}/regions/{region_id}/transitGateways/{tgw_id}/attachment"
                ),
                request,
            )
            .await
    }

    /// Update Active-Active Transit Gateway attachment CIDRs
    pub async fn update_attachment_cidrs_active_active(
        &self,
        subscription_id: i32,
        region_id: i32,
        tgw_id: String,
        request: &TgwAttachmentRequest,
    ) -> Result<TaskStateUpdate> {
        self.client
            .put(
                &format!(
                    "/subscriptions/{subscription_id}/regions/{region_id}/transitGateways/{tgw_id}/attachment"
                ),
                request,
            )
            .await
    }
}