Skip to main content

leash_sdk/integrations/
mod.rs

1//! Typed integration namespaces — Gmail, Calendar, Drive, Linear, plus the
2//! generic [`Provider`] escape hatch for providers without typed wrappers yet
3//! (Slack, GitHub, HubSpot, …).
4//!
5//! Mirrors `leash.integrations` in the TS / Python / Go SDKs. Every call
6//! routes through `crate::transport::Transport` which enforces the platform
7//! contract (X-API-Key + Cookie only, never `Authorization: Bearer`).
8
9mod calendar;
10mod drive;
11mod gmail;
12mod linear;
13pub mod types;
14
15pub use calendar::Calendar;
16pub use drive::Drive;
17pub use gmail::Gmail;
18pub use linear::Linear;
19
20use crate::errors::Result;
21use crate::transport::Transport;
22
23/// `leash.integrations()` — typed providers plus a generic escape hatch.
24#[derive(Debug, Clone)]
25pub struct Integrations {
26    transport: Transport,
27}
28
29impl Integrations {
30    pub(crate) fn new(transport: Transport) -> Self {
31        Self { transport }
32    }
33
34    /// Gmail provider.
35    pub fn gmail(&self) -> Gmail {
36        Gmail::new(self.transport.clone())
37    }
38
39    /// Google Calendar provider (canonical name).
40    pub fn calendar(&self) -> Calendar {
41        Calendar::new(self.transport.clone())
42    }
43
44    /// Alias of [`Self::calendar`] matching the TS / Python long-form name.
45    pub fn google_calendar(&self) -> Calendar {
46        self.calendar()
47    }
48
49    /// Google Drive provider (canonical name).
50    pub fn drive(&self) -> Drive {
51        Drive::new(self.transport.clone())
52    }
53
54    /// Alias of [`Self::drive`] matching the TS / Python long-form name.
55    pub fn google_drive(&self) -> Drive {
56        self.drive()
57    }
58
59    /// Linear provider.
60    pub fn linear(&self) -> Linear {
61        Linear::new(self.transport.clone())
62    }
63
64    /// Generic escape hatch — call any provider action by name.
65    ///
66    /// Use this for providers without typed wrappers yet (Slack, GitHub,
67    /// HubSpot, Jira, Gong, Slite, BigQuery, custom MCP servers, …).
68    ///
69    /// ```no_run
70    /// # async fn example(client: &leash_sdk::Leash) -> leash_sdk::Result<()> {
71    /// let res = client
72    ///     .integrations()
73    ///     .provider("slack")
74    ///     .call("post_message", serde_json::json!({ "channel": "#general", "text": "hi" }))
75    ///     .await?;
76    /// # let _ = res;
77    /// # Ok(()) }
78    /// ```
79    pub fn provider(&self, name: impl Into<String>) -> Provider {
80        Provider {
81            transport: self.transport.clone(),
82            name: name.into(),
83        }
84    }
85}
86
87/// Generic provider invoker — no typed shape, just a pass-through of action +
88/// body. Returned by [`Integrations::provider`].
89#[derive(Debug, Clone)]
90pub struct Provider {
91    transport: Transport,
92    name: String,
93}
94
95impl Provider {
96    /// Provider id this caller is bound to.
97    pub fn name(&self) -> &str {
98        &self.name
99    }
100
101    /// POST to `/api/integrations/{name}/{action}` with the given body and
102    /// return the raw JSON `data` field (or the raw response when there's no
103    /// envelope).
104    pub async fn call(&self, action: &str, body: serde_json::Value) -> Result<serde_json::Value> {
105        self.transport.integrations_call(&self.name, action, &body).await
106    }
107}