Skip to main content

openapp_sdk_core/resources/
integrations.rs

1//! `Integrations` resource group.
2
3use std::sync::Arc;
4
5use openapp_sdk_common::NameMatch;
6use reqwest::Method;
7
8use super::JsonValue;
9use crate::{
10    error::SdkError,
11    resolve::{
12        fetch_all_paginated, items_from_list_response, resolve_unique_sdk, use_server_q_prefilter,
13    },
14    transport::{RequestSpec, Transport},
15};
16
17/// Optional filters for [`IntegrationsClient::list`].
18#[derive(Debug, Clone, Default)]
19pub struct ListIntegrationsParams {
20    pub provider_type: Option<String>,
21    pub q: Option<String>,
22    pub limit: Option<u32>,
23    pub offset: Option<u32>,
24}
25
26#[derive(Debug, Clone)]
27pub struct IntegrationsClient {
28    transport: Arc<Transport>,
29}
30
31impl IntegrationsClient {
32    pub(crate) fn new(transport: Arc<Transport>) -> Self {
33        Self { transport }
34    }
35
36    pub async fn list(&self) -> Result<JsonValue, SdkError> {
37        self.list_with(ListIntegrationsParams::default()).await
38    }
39
40    pub async fn list_with(&self, params: ListIntegrationsParams) -> Result<JsonValue, SdkError> {
41        let query = build_list_query(&params);
42        self.transport
43            .request_json::<(), JsonValue>(RequestSpec {
44                method: Method::GET,
45                path: "/integrations",
46                query: &query,
47                ..Default::default()
48            })
49            .await
50    }
51
52    /// Resolve a single integration by localized display name.
53    pub async fn get_by_name(
54        &self,
55        name: &str,
56        mode: NameMatch,
57        provider_type: Option<&str>,
58    ) -> Result<JsonValue, SdkError> {
59        let q = use_server_q_prefilter(mode, name).then(|| name.to_owned());
60        let provider = provider_type.map(str::to_owned);
61        let client = self.clone();
62        let items = fetch_all_paginated(move |limit, offset| {
63            let client = client.clone();
64            let params = ListIntegrationsParams {
65                provider_type: provider.clone(),
66                q: q.clone(),
67                limit: Some(limit),
68                offset: Some(offset),
69            };
70            async move { client.list_with(params).await }
71        })
72        .await?;
73        resolve_unique_sdk(&items, "integration", "name", name, mode)
74    }
75
76    /// Resolve a single access portal under an integration by localized display name.
77    pub async fn get_access_portal_by_name(
78        &self,
79        integration_id: &str,
80        name: &str,
81        mode: NameMatch,
82    ) -> Result<JsonValue, SdkError> {
83        let path = format!("/integrations/{integration_id}/access-portals");
84        let response = self
85            .transport
86            .request_json::<(), JsonValue>(RequestSpec {
87                method: Method::GET,
88                path: &path,
89                ..Default::default()
90            })
91            .await?;
92        let items = items_from_list_response(&response, Some("portals"));
93        resolve_unique_sdk(&items, "portal", "name", name, mode)
94    }
95
96    pub async fn create(&self, body: &JsonValue) -> Result<JsonValue, SdkError> {
97        self.transport
98            .request_json::<JsonValue, JsonValue>(RequestSpec {
99                method: Method::POST,
100                path: "/integrations",
101                body: Some(body),
102                ..Default::default()
103            })
104            .await
105    }
106
107    pub async fn provider_types(&self) -> Result<Vec<JsonValue>, SdkError> {
108        self.transport
109            .request_json::<(), Vec<JsonValue>>(RequestSpec {
110                method: Method::GET,
111                path: "/integrations/provider-types",
112                ..Default::default()
113            })
114            .await
115    }
116
117    pub async fn provider_definition(&self, provider_type: &str) -> Result<JsonValue, SdkError> {
118        let path = format!("/integrations/provider-types/{provider_type}/definition");
119        self.transport
120            .request_json::<(), JsonValue>(RequestSpec {
121                method: Method::GET,
122                path: &path,
123                ..Default::default()
124            })
125            .await
126    }
127
128    pub async fn get(&self, id: &str) -> Result<JsonValue, SdkError> {
129        let path = format!("/integrations/{id}");
130        self.transport
131            .request_json::<(), JsonValue>(RequestSpec {
132                method: Method::GET,
133                path: &path,
134                ..Default::default()
135            })
136            .await
137    }
138
139    pub async fn update(&self, id: &str, body: &JsonValue) -> Result<JsonValue, SdkError> {
140        let path = format!("/integrations/{id}");
141        self.transport
142            .request_json::<JsonValue, JsonValue>(RequestSpec {
143                method: Method::PUT,
144                path: &path,
145                body: Some(body),
146                ..Default::default()
147            })
148            .await
149    }
150
151    pub async fn purge(&self, id: &str) -> Result<(), SdkError> {
152        let path = format!("/integrations/{id}/purge");
153        self.transport
154            .request_json::<(), ()>(RequestSpec {
155                method: Method::DELETE,
156                path: &path,
157                ..Default::default()
158            })
159            .await
160    }
161
162    pub async fn restore(&self, id: &str) -> Result<JsonValue, SdkError> {
163        let path = format!("/integrations/{id}/restore");
164        self.transport
165            .request_json::<(), JsonValue>(RequestSpec {
166                method: Method::POST,
167                path: &path,
168                ..Default::default()
169            })
170            .await
171    }
172
173    pub async fn device_metadata_schema(&self, id: &str) -> Result<JsonValue, SdkError> {
174        let path = format!("/integrations/{id}/device-metadata-schema");
175        self.transport
176            .request_json::<(), JsonValue>(RequestSpec {
177                method: Method::GET,
178                path: &path,
179                ..Default::default()
180            })
181            .await
182    }
183
184    pub async fn discovered_devices(&self, id: &str) -> Result<Vec<JsonValue>, SdkError> {
185        let path = format!("/integrations/{id}/discovered-devices");
186        self.transport
187            .request_json::<(), Vec<JsonValue>>(RequestSpec {
188                method: Method::GET,
189                path: &path,
190                ..Default::default()
191            })
192            .await
193    }
194
195    pub async fn entities(&self, id: &str) -> Result<Vec<JsonValue>, SdkError> {
196        let path = format!("/integrations/{id}/entities");
197        self.transport
198            .request_json::<(), Vec<JsonValue>>(RequestSpec {
199                method: Method::GET,
200                path: &path,
201                ..Default::default()
202            })
203            .await
204    }
205
206    pub async fn ops(&self, id: &str) -> Result<Vec<JsonValue>, SdkError> {
207        let path = format!("/integrations/{id}/ops");
208        self.transport
209            .request_json::<(), Vec<JsonValue>>(RequestSpec {
210                method: Method::GET,
211                path: &path,
212                ..Default::default()
213            })
214            .await
215    }
216
217    pub async fn run_op(
218        &self,
219        id: &str,
220        op_id: &str,
221        body: &JsonValue,
222    ) -> Result<JsonValue, SdkError> {
223        let path = format!("/integrations/{id}/ops/{op_id}");
224        self.transport
225            .request_json::<JsonValue, JsonValue>(RequestSpec {
226                method: Method::POST,
227                path: &path,
228                body: Some(body),
229                ..Default::default()
230            })
231            .await
232    }
233
234    // -- Access portals / invites ------------------------------------------------
235
236    pub async fn list_access_portals(&self, id: &str) -> Result<JsonValue, SdkError> {
237        let path = format!("/integrations/{id}/access-portals");
238        self.transport
239            .request_json::<(), JsonValue>(RequestSpec {
240                method: Method::GET,
241                path: &path,
242                ..Default::default()
243            })
244            .await
245    }
246
247    pub async fn create_access_portal(
248        &self,
249        id: &str,
250        body: &JsonValue,
251    ) -> Result<JsonValue, SdkError> {
252        let path = format!("/integrations/{id}/access-portals");
253        self.transport
254            .request_json::<JsonValue, JsonValue>(RequestSpec {
255                method: Method::POST,
256                path: &path,
257                body: Some(body),
258                ..Default::default()
259            })
260            .await
261    }
262
263    pub async fn get_access_portal(&self, portal_id: &str) -> Result<JsonValue, SdkError> {
264        let path = format!("/integrations/access-portals/{portal_id}");
265        self.transport
266            .request_json::<(), JsonValue>(RequestSpec {
267                method: Method::GET,
268                path: &path,
269                ..Default::default()
270            })
271            .await
272    }
273
274    pub async fn update_access_portal(
275        &self,
276        id: &str,
277        portal_id: &str,
278        body: &JsonValue,
279    ) -> Result<JsonValue, SdkError> {
280        let path = format!("/integrations/{id}/access-portals/{portal_id}");
281        self.transport
282            .request_json::<JsonValue, JsonValue>(RequestSpec {
283                method: Method::PUT,
284                path: &path,
285                body: Some(body),
286                ..Default::default()
287            })
288            .await
289    }
290
291    pub async fn delete_access_portal(&self, id: &str, portal_id: &str) -> Result<(), SdkError> {
292        let path = format!("/integrations/{id}/access-portals/{portal_id}");
293        self.transport
294            .request_json::<(), ()>(RequestSpec {
295                method: Method::DELETE,
296                path: &path,
297                ..Default::default()
298            })
299            .await
300    }
301
302    pub async fn create_access_invite(
303        &self,
304        id: &str,
305        body: &JsonValue,
306    ) -> Result<JsonValue, SdkError> {
307        let path = format!("/integrations/{id}/access-invites");
308        self.transport
309            .request_json::<JsonValue, JsonValue>(RequestSpec {
310                method: Method::POST,
311                path: &path,
312                body: Some(body),
313                ..Default::default()
314            })
315            .await
316    }
317
318    pub async fn update_access_invite(
319        &self,
320        id: &str,
321        invite_link_id: &str,
322        body: &JsonValue,
323    ) -> Result<JsonValue, SdkError> {
324        let path = format!("/integrations/{id}/access-invites/{invite_link_id}");
325        self.transport
326            .request_json::<JsonValue, JsonValue>(RequestSpec {
327                method: Method::PUT,
328                path: &path,
329                body: Some(body),
330                ..Default::default()
331            })
332            .await
333    }
334
335    pub async fn delete_access_invite(
336        &self,
337        id: &str,
338        invite_link_id: &str,
339    ) -> Result<(), SdkError> {
340        let path = format!("/integrations/{id}/access-invites/{invite_link_id}");
341        self.transport
342            .request_json::<(), ()>(RequestSpec {
343                method: Method::DELETE,
344                path: &path,
345                ..Default::default()
346            })
347            .await
348    }
349
350    pub async fn restore_access_invite(
351        &self,
352        id: &str,
353        invite_link_id: &str,
354    ) -> Result<JsonValue, SdkError> {
355        let path = format!("/integrations/{id}/access-invites/{invite_link_id}/restore");
356        self.transport
357            .request_json::<(), JsonValue>(RequestSpec {
358                method: Method::POST,
359                path: &path,
360                ..Default::default()
361            })
362            .await
363    }
364}
365
366fn build_list_query(params: &ListIntegrationsParams) -> Vec<(&str, Option<String>)> {
367    vec![
368        (
369            "provider_type",
370            params
371                .provider_type
372                .as_ref()
373                .map(std::borrow::ToOwned::to_owned),
374        ),
375        ("q", params.q.as_ref().map(std::borrow::ToOwned::to_owned)),
376        ("limit", params.limit.map(|n| n.to_string())),
377        ("offset", params.offset.map(|n| n.to_string())),
378    ]
379}