videosdk-server-sdk 0.1.0

Rust server SDK for the VideoSDK v2 REST APIs
Documentation
//! Telephony connectors: per-provider webhook ingress that bridges inbound calls
//! into a room.

use reqwest::Method;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};

use crate::client::{CallOptions, Client};
use crate::common::{string_enum, MessageResponse};
use crate::error::Result;
use crate::pagination::{static_page, Page};
use crate::resources::escape;

const PATH: &str = "/v2/connectors";

string_enum! {
    /// A telephony provider.
    ConnectorProvider {
        /// Twilio.
        TWILIO => "twilio",
        /// Plivo.
        PLIVO => "plivo",
        /// Telnyx.
        TELNYX => "telnyx",
    }
}

/// A per-provider telephony webhook ingress.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Connector {
    /// The connector id.
    pub id: String,
    /// The telephony provider.
    pub provider: Option<ConnectorProvider>,
    /// The connector's display name.
    pub name: Option<String>,
    /// The public webhook URL to configure at the provider.
    pub webhook_url: Option<String>,
    /// The fallback room, used when no routing rule resolves.
    pub default_room_id: Option<String>,
    /// The routing rule applied by default.
    pub default_rule_id: Option<String>,
    /// The region call ingestion is pinned to.
    pub region: Option<String>,
    /// When the connector was created.
    pub created_at: Option<String>,
    /// Any fields the server returned that this SDK does not model yet.
    #[serde(flatten)]
    pub extra: Map<String, Value>,
}

/// The parameters for [`ConnectorsResource::create`].
#[derive(Debug, Clone, Default)]
pub struct CreateConnectorParams {
    /// The telephony provider. Required.
    pub provider: Option<ConnectorProvider>,
    /// The connector's display name.
    pub name: Option<String>,
    /// The fallback room, used when no routing rule resolves.
    pub room_id: Option<String>,
    /// The routing rule to apply by default.
    pub default_rule_id: Option<String>,
    /// Pins call ingestion to a region.
    pub region: Option<String>,
}

/// `room_id` becomes `defaultRoomId`.
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct ConnectorWire {
    #[serde(skip_serializing_if = "Option::is_none")]
    provider: Option<ConnectorProvider>,
    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    default_room_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    default_rule_id: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    region: Option<String>,
}

impl From<CreateConnectorParams> for ConnectorWire {
    fn from(params: CreateConnectorParams) -> Self {
        Self {
            provider: params.provider,
            name: params.name,
            default_room_id: params.room_id,
            default_rule_id: params.default_rule_id,
            region: params.region,
        }
    }
}

/// Telephony connectors. Reached via [`Client::connectors`].
#[derive(Debug, Clone, Copy)]
pub struct ConnectorsResource<'a> {
    client: &'a Client,
}

impl<'a> ConnectorsResource<'a> {
    pub(crate) fn new(client: &'a Client) -> Self {
        Self { client }
    }

    /// Creates a connector.
    pub async fn create(&self, params: CreateConnectorParams) -> Result<Connector> {
        let body = ConnectorWire::from(params);
        self.client
            .data(Method::POST, PATH, CallOptions::json(&body)?)
            .await
    }

    /// Lists every connector.
    ///
    /// This endpoint answers with a flat array rather than a paginated envelope,
    /// so the returned page is always complete.
    pub async fn list(&self) -> Result<Page<Connector>> {
        let connectors: Vec<Connector> = self
            .client
            .data(Method::GET, PATH, CallOptions::new())
            .await?;
        Ok(static_page(connectors))
    }

    /// Fetches a connector by id.
    pub async fn get(&self, connector_id: &str) -> Result<Connector> {
        let path = format!("{PATH}/{}", escape(connector_id));
        self.client
            .data(Method::GET, &path, CallOptions::new())
            .await
    }

    /// Rotates the connector's webhook key, invalidating the old webhook URL.
    pub async fn rotate_key(&self, connector_id: &str) -> Result<Connector> {
        let path = format!("{PATH}/{}/rotate-key", escape(connector_id));
        self.client
            .data(Method::POST, &path, CallOptions::new())
            .await
    }

    /// Deletes a connector.
    pub async fn delete(&self, connector_id: &str) -> Result<MessageResponse> {
        let path = format!("{PATH}/{}", escape(connector_id));
        self.client
            .json(Method::DELETE, &path, CallOptions::new())
            .await
    }
}