Skip to main content

openapp_sdk_core/resources/
devices.rs

1//! `Devices` resource group.
2
3use std::sync::Arc;
4
5use openapp_sdk_common::NameMatch;
6use reqwest::Method;
7
8use super::JsonValue;
9use super::types;
10use crate::{
11    error::SdkError,
12    resolve::{fetch_all_paginated, resolve_unique_sdk, use_server_q_prefilter},
13    transport::{RequestSpec, Transport},
14};
15
16/// Optional filters for [`DevicesClient::list`].
17#[derive(Debug, Clone, Default)]
18pub struct ListDevicesParams {
19    pub integration_id: Option<String>,
20    pub zone_id: 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 DevicesClient {
28    transport: Arc<Transport>,
29}
30
31impl DevicesClient {
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(ListDevicesParams::default()).await
38    }
39
40    pub async fn list_with(&self, params: ListDevicesParams) -> Result<JsonValue, SdkError> {
41        let query = build_list_query(&params);
42        self.transport
43            .request_json::<(), JsonValue>(RequestSpec {
44                method: Method::GET,
45                path: "/devices",
46                query: &query,
47                ..Default::default()
48            })
49            .await
50    }
51
52    /// Resolve a single device by localized display name.
53    pub async fn get_by_name(
54        &self,
55        name: &str,
56        mode: NameMatch,
57        integration_id: Option<&str>,
58    ) -> Result<JsonValue, SdkError> {
59        let q = use_server_q_prefilter(mode, name).then(|| name.to_owned());
60        let integration = integration_id.map(str::to_owned);
61        let client = self.clone();
62        let all_items = fetch_all_paginated(move |limit, offset| {
63            let client = client.clone();
64            let params = ListDevicesParams {
65                integration_id: integration.clone(),
66                q: q.clone(),
67                limit: Some(limit),
68                offset: Some(offset),
69                ..Default::default()
70            };
71            async move {
72                let page = client.list_with(params).await?;
73                Ok(normalize_devices_page(page))
74            }
75        })
76        .await?;
77        resolve_unique_sdk(&all_items, "device", "name", name, mode)
78    }
79
80    pub async fn create(
81        &self,
82        body: &types::CreateDeviceRequest,
83    ) -> Result<types::DeviceResponse, SdkError> {
84        self.transport
85            .request_json::<types::CreateDeviceRequest, types::DeviceResponse>(RequestSpec {
86                method: Method::POST,
87                path: "/devices",
88                body: Some(body),
89                ..Default::default()
90            })
91            .await
92    }
93
94    pub async fn get(&self, id: &str) -> Result<types::DeviceResponse, SdkError> {
95        let path = format!("/devices/{id}");
96        self.transport
97            .request_json::<(), types::DeviceResponse>(RequestSpec {
98                method: Method::GET,
99                path: &path,
100                ..Default::default()
101            })
102            .await
103    }
104
105    pub async fn update(
106        &self,
107        id: &str,
108        body: &types::UpdateDeviceRequest,
109    ) -> Result<types::DeviceResponse, SdkError> {
110        let path = format!("/devices/{id}");
111        self.transport
112            .request_json::<types::UpdateDeviceRequest, types::DeviceResponse>(RequestSpec {
113                method: Method::PUT,
114                path: &path,
115                body: Some(body),
116                ..Default::default()
117            })
118            .await
119    }
120
121    pub async fn delete(&self, id: &str) -> Result<(), SdkError> {
122        let path = format!("/devices/{id}");
123        self.transport
124            .request_json::<(), ()>(RequestSpec {
125                method: Method::DELETE,
126                path: &path,
127                ..Default::default()
128            })
129            .await
130    }
131
132    pub async fn purge(&self, id: &str) -> Result<(), SdkError> {
133        let path = format!("/devices/{id}/purge");
134        self.transport
135            .request_json::<(), ()>(RequestSpec {
136                method: Method::DELETE,
137                path: &path,
138                ..Default::default()
139            })
140            .await
141    }
142
143    pub async fn restore(&self, id: &str) -> Result<types::DeviceResponse, SdkError> {
144        let path = format!("/devices/{id}/restore");
145        self.transport
146            .request_json::<(), types::DeviceResponse>(RequestSpec {
147                method: Method::POST,
148                path: &path,
149                ..Default::default()
150            })
151            .await
152    }
153
154    pub async fn door_restrictions(
155        &self,
156        id: &str,
157    ) -> Result<types::DoorRestrictionsResponse, SdkError> {
158        let path = format!("/devices/{id}/door-restrictions");
159        self.transport
160            .request_json::<(), types::DoorRestrictionsResponse>(RequestSpec {
161                method: Method::GET,
162                path: &path,
163                ..Default::default()
164            })
165            .await
166    }
167
168    pub async fn metadata_definition(
169        &self,
170        id: &str,
171    ) -> Result<types::DeviceMetadataDefinitionResponse, SdkError> {
172        let path = format!("/devices/{id}/metadata-definition");
173        self.transport
174            .request_json::<(), types::DeviceMetadataDefinitionResponse>(RequestSpec {
175                method: Method::GET,
176                path: &path,
177                ..Default::default()
178            })
179            .await
180    }
181}
182
183fn build_list_query(params: &ListDevicesParams) -> Vec<(&str, Option<String>)> {
184    vec![
185        (
186            "integration_id",
187            params
188                .integration_id
189                .as_ref()
190                .map(std::borrow::ToOwned::to_owned),
191        ),
192        (
193            "zone_id",
194            params.zone_id.as_ref().map(std::borrow::ToOwned::to_owned),
195        ),
196        ("q", params.q.as_ref().map(std::borrow::ToOwned::to_owned)),
197        ("limit", params.limit.map(|n| n.to_string())),
198        ("offset", params.offset.map(|n| n.to_string())),
199    ]
200}
201
202fn normalize_devices_page(page: JsonValue) -> JsonValue {
203    if page.is_array() {
204        let len = page.as_array().map_or(0, std::vec::Vec::len);
205        serde_json::json!({ "items": page, "total": len })
206    } else {
207        page
208    }
209}