videosdk-server-sdk 0.1.0

Rust server SDK for the VideoSDK v2 REST APIs
Documentation
//! The SIP / telephony API.
//!
//! To route inbound calls to a room, provision numbers with
//! [`PhoneNumbersResource`] and bind them with a
//! [routing rule](SipRoutingRulesResource::create).

pub mod calls;
pub mod phone_numbers;
pub mod routing_rules;
pub mod trunks;
pub mod webhooks;

use serde::{Deserialize, Serialize};

use crate::client::Client;
use crate::common::string_enum;

pub use calls::{
    CreateSipCallParams, SipCall, SipCallListParams, SipCallsResource, SwitchRoomParams,
    TransferSipCallParams,
};
pub use phone_numbers::{
    CreatePhoneNumbersParams, PhoneNumberInboundConfig, PhoneNumberInfo, PhoneNumberListParams,
    PhoneNumberOutboundConfig, PhoneNumberWithGateways, PhoneNumbersResource,
    UpdatePhoneNumberGatewayParams,
};
pub use routing_rules::{
    CreateRoutingRuleParams, RoutingRuleTarget, SipRoomConfig, SipRoutingRule,
    SipRoutingRuleListParams, SipRoutingRulesResource, UpdateRoutingRuleParams,
};
pub use trunks::{
    CreateInboundTrunkParams, CreateOutboundTrunkParams, InboundTrunkResource,
    OutboundTrunkResource, SipTrunk, SipTrunkListParams, SipTrunksResource,
    UpdateInboundTrunkParams, UpdateOutboundTrunkParams,
};
pub use webhooks::{CreateSipWebhookParams, SipWebhook, SipWebhookListParams, SipWebhooksResource};

string_enum! {
    /// A SIP transport protocol.
    SipTransport {
        /// UDP.
        UDP => "udp",
        /// TCP.
        TCP => "tcp",
        /// TLS. The default for outbound trunks.
        TLS => "tls",
    }
}

string_enum! {
    /// Whether media is encrypted.
    SipMediaEncryption {
        /// Encrypt media.
        ENABLE => "enable",
        /// Do not encrypt media.
        DISABLE => "disable",
    }
}

string_enum! {
    /// Which SIP headers to forward.
    SipIncludeHeaders {
        /// Forward every header.
        ALL => "ALL",
        /// Forward only `X-` headers.
        SIP_X_HEADERS => "SIP_X_HEADERS",
        /// Forward no headers.
        NONE => "NONE",
    }
}

string_enum! {
    /// A routing-rule or call direction.
    SipDirection {
        /// Calls arriving from the provider.
        INBOUND => "inbound",
        /// Calls placed to the provider.
        OUTBOUND => "outbound",
    }
}

string_enum! {
    /// How a routing rule allocates its destination room.
    SipRoomType {
        /// Every call lands in one fixed room.
        STATIC => "static",
        /// Each call gets a fresh room.
        DYNAMIC => "dynamic",
    }
}

string_enum! {
    /// How a per-call room id is generated. A strategy, not a literal prefix.
    SipRoomPrefix {
        /// No prefix.
        BLANK => "blank",
        /// Prefix with the participant's number.
        PARTICIPANT_NUMBER => "participantNumber",
        /// Prefix with the SIP number.
        SIP_NUMBER => "sipNumber",
        /// Prefix randomly.
        RANDOM => "random",
    }
}

string_enum! {
    /// A SIP-capable region.
    SipRegion {
        /// Pick the closest region automatically.
        AUTO => "auto",
        /// `us002`
        US002 => "us002",
        /// `in002`
        IN002 => "in002",
    }
}

string_enum! {
    /// A call's lifecycle status.
    SipCallStatus {
        /// The call has been created.
        INITIATED => "initiated",
        /// The destination is ringing.
        RINGING => "ringing",
        /// Nobody answered in time.
        MISSED => "missed",
        /// The call was not answered.
        NOT_ANSWERED => "not-answered",
        /// The call failed.
        FAILED => "failed",
        /// An inbound call is arriving.
        INCOMING => "incoming",
        /// The caller was verified.
        VERIFIED => "verified",
        /// The call was rejected.
        REJECTED => "rejected",
        /// The caller hung up before it was answered.
        ABANDONED => "abandoned",
        /// The call was answered.
        ANSWERED => "answered",
        /// The call has ended.
        ENDED => "ended",
        /// A transfer has started.
        TRANSFER_INITIATED => "transfer-initiated",
        /// The transfer was accepted.
        TRANSFER_ACCEPTED => "transfer-accepted",
        /// The transfer is in progress.
        TRANSFERRING => "transferring",
        /// The transfer destination is ringing.
        TRANSFER_RINGING => "transfer-ringing",
        /// The call was transferred.
        TRANSFERRED => "transferred",
        /// The transfer failed.
        TRANSFER_FAILED => "transfer-failed",
        /// A room switch has started.
        SWITCH_ROOM_INITIATED => "switch-room-initiated",
        /// The room switch completed.
        SWITCH_ROOM_COMPLETED => "switch-room-completed",
        /// The room switch failed.
        SWITCH_ROOM_FAILED => "switch-room-failed",
    }
}

string_enum! {
    /// A SIP call-lifecycle webhook event.
    SipWebhookEvent {
        /// A call started.
        CALL_STARTED => "call-started",
        /// A call was answered.
        CALL_ANSWERED => "call-answered",
        /// A call hung up.
        CALL_HANGUP => "call-hangup",
        /// A call was transferred.
        CALL_TRANSFERRED => "call-transferred",
        /// A call was missed.
        CALL_MISSED => "call-missed",
        /// A call was updated.
        CALL_UPDATE => "call-update",
        /// A call is ringing.
        CALL_RINGING => "call-ringing",
        /// A transfer has started.
        CALL_TRANSFER_INITIATED => "call-transfer-initiated",
        /// A transfer was accepted.
        CALL_TRANSFER_ACCEPTED => "call-transfer-accepted",
        /// A transfer is in progress.
        CALL_TRANSFERRING => "call-transferring",
        /// A transfer destination is ringing.
        CALL_TRANSFER_RINGING => "call-transfer-ringing",
        /// A transfer failed.
        CALL_TRANSFER_FAILED => "call-transfer-failed",
        /// A room switch has started.
        SWITCH_ROOM_INITIATED => "switch-room-initiated",
        /// A room switch completed.
        SWITCH_ROOM_COMPLETED => "switch-room-completed",
        /// A room switch failed.
        SWITCH_ROOM_FAILED => "switch-room-failed",
    }
}

/// SIP credentials.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SipAuth {
    /// The SIP username.
    pub username: String,
    /// The SIP password.
    pub password: String,
}

/// The SIP / telephony API. Reached via [`Client::sip`].
#[derive(Debug, Clone, Copy)]
pub struct SipResource<'a> {
    client: &'a Client,
}

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

    /// SIP trunks: the inbound and outbound connections to your provider.
    pub fn trunks(&self) -> SipTrunksResource<'a> {
        SipTrunksResource::new(self.client)
    }

    /// SIP routing rules, which bind provisioned numbers to a dispatch target.
    pub fn routing_rules(&self) -> SipRoutingRulesResource<'a> {
        SipRoutingRulesResource::new(self.client)
    }

    /// Provisioned phone numbers, and the gateways attached to them.
    pub fn phone_numbers(&self) -> PhoneNumbersResource<'a> {
        PhoneNumbersResource::new(self.client)
    }

    /// Places and manages SIP calls.
    pub fn calls(&self) -> SipCallsResource<'a> {
        SipCallsResource::new(self.client)
    }

    /// Webhooks delivering SIP call-event notifications.
    ///
    /// This manages webhook *configuration*. It is unrelated to verifying the
    /// signature of an inbound webhook.
    pub fn webhooks(&self) -> SipWebhooksResource<'a> {
        SipWebhooksResource::new(self.client)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn string_enums_round_trip_their_wire_values() {
        assert_eq!(SipTransport::TLS.as_str(), "tls");
        assert_eq!(SipIncludeHeaders::SIP_X_HEADERS.as_str(), "SIP_X_HEADERS");
        assert_eq!(
            SipRoomPrefix::PARTICIPANT_NUMBER.as_str(),
            "participantNumber"
        );
        assert_eq!(SipCallStatus::NOT_ANSWERED.as_str(), "not-answered");
        assert_eq!(
            SipWebhookEvent::SWITCH_ROOM_COMPLETED.as_str(),
            "switch-room-completed"
        );
        assert_eq!(
            serde_json::to_value(SipDirection::INBOUND).unwrap(),
            json!("inbound")
        );
    }

    /// A closed enum would fail here, taking the whole response with it.
    #[test]
    fn an_unknown_value_still_deserializes() {
        let status: SipCallStatus = serde_json::from_value(json!("some-future-status")).unwrap();
        assert_eq!(status.as_str(), "some-future-status");
        assert_eq!(
            serde_json::to_value(&status).unwrap(),
            json!("some-future-status")
        );
    }
}