Skip to main content

videosdk/resources/
connectors.rs

1//! Telephony connectors: per-provider webhook ingress that bridges inbound calls
2//! into a room.
3
4use reqwest::Method;
5use serde::{Deserialize, Serialize};
6use serde_json::{Map, Value};
7
8use crate::client::{CallOptions, Client};
9use crate::common::{string_enum, MessageResponse};
10use crate::error::Result;
11use crate::pagination::{static_page, Page};
12use crate::resources::escape;
13
14const PATH: &str = "/v2/connectors";
15
16string_enum! {
17    /// A telephony provider.
18    ConnectorProvider {
19        /// Twilio.
20        TWILIO => "twilio",
21        /// Plivo.
22        PLIVO => "plivo",
23        /// Telnyx.
24        TELNYX => "telnyx",
25    }
26}
27
28/// A per-provider telephony webhook ingress.
29#[derive(Debug, Clone, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct Connector {
32    /// The connector id.
33    pub id: String,
34    /// The telephony provider.
35    pub provider: Option<ConnectorProvider>,
36    /// The connector's display name.
37    pub name: Option<String>,
38    /// The public webhook URL to configure at the provider.
39    pub webhook_url: Option<String>,
40    /// The fallback room, used when no routing rule resolves.
41    pub default_room_id: Option<String>,
42    /// The routing rule applied by default.
43    pub default_rule_id: Option<String>,
44    /// The region call ingestion is pinned to.
45    pub region: Option<String>,
46    /// When the connector was created.
47    pub created_at: Option<String>,
48    /// Any fields the server returned that this SDK does not model yet.
49    #[serde(flatten)]
50    pub extra: Map<String, Value>,
51}
52
53/// The parameters for [`ConnectorsResource::create`].
54#[derive(Debug, Clone, Default)]
55pub struct CreateConnectorParams {
56    /// The telephony provider. Required.
57    pub provider: Option<ConnectorProvider>,
58    /// The connector's display name.
59    pub name: Option<String>,
60    /// The fallback room, used when no routing rule resolves.
61    pub room_id: Option<String>,
62    /// The routing rule to apply by default.
63    pub default_rule_id: Option<String>,
64    /// Pins call ingestion to a region.
65    pub region: Option<String>,
66}
67
68/// `room_id` becomes `defaultRoomId`.
69#[derive(Debug, Serialize)]
70#[serde(rename_all = "camelCase")]
71struct ConnectorWire {
72    #[serde(skip_serializing_if = "Option::is_none")]
73    provider: Option<ConnectorProvider>,
74    #[serde(skip_serializing_if = "Option::is_none")]
75    name: Option<String>,
76    #[serde(skip_serializing_if = "Option::is_none")]
77    default_room_id: Option<String>,
78    #[serde(skip_serializing_if = "Option::is_none")]
79    default_rule_id: Option<String>,
80    #[serde(skip_serializing_if = "Option::is_none")]
81    region: Option<String>,
82}
83
84impl From<CreateConnectorParams> for ConnectorWire {
85    fn from(params: CreateConnectorParams) -> Self {
86        Self {
87            provider: params.provider,
88            name: params.name,
89            default_room_id: params.room_id,
90            default_rule_id: params.default_rule_id,
91            region: params.region,
92        }
93    }
94}
95
96/// Telephony connectors. Reached via [`Client::connectors`].
97#[derive(Debug, Clone, Copy)]
98pub struct ConnectorsResource<'a> {
99    client: &'a Client,
100}
101
102impl<'a> ConnectorsResource<'a> {
103    pub(crate) fn new(client: &'a Client) -> Self {
104        Self { client }
105    }
106
107    /// Creates a connector.
108    pub async fn create(&self, params: CreateConnectorParams) -> Result<Connector> {
109        let body = ConnectorWire::from(params);
110        self.client
111            .data(Method::POST, PATH, CallOptions::json(&body)?)
112            .await
113    }
114
115    /// Lists every connector.
116    ///
117    /// This endpoint answers with a flat array rather than a paginated envelope,
118    /// so the returned page is always complete.
119    pub async fn list(&self) -> Result<Page<Connector>> {
120        let connectors: Vec<Connector> = self
121            .client
122            .data(Method::GET, PATH, CallOptions::new())
123            .await?;
124        Ok(static_page(connectors))
125    }
126
127    /// Fetches a connector by id.
128    pub async fn get(&self, connector_id: &str) -> Result<Connector> {
129        let path = format!("{PATH}/{}", escape(connector_id));
130        self.client
131            .data(Method::GET, &path, CallOptions::new())
132            .await
133    }
134
135    /// Rotates the connector's webhook key, invalidating the old webhook URL.
136    pub async fn rotate_key(&self, connector_id: &str) -> Result<Connector> {
137        let path = format!("{PATH}/{}/rotate-key", escape(connector_id));
138        self.client
139            .data(Method::POST, &path, CallOptions::new())
140            .await
141    }
142
143    /// Deletes a connector.
144    pub async fn delete(&self, connector_id: &str) -> Result<MessageResponse> {
145        let path = format!("{PATH}/{}", escape(connector_id));
146        self.client
147            .json(Method::DELETE, &path, CallOptions::new())
148            .await
149    }
150}