dji_log_parser/keychain/
api.rs1use serde::{Deserialize, Serialize};
2#[cfg(target_arch = "wasm32")]
3use tsify_next::Tsify;
4
5use crate::Result;
6
7use super::{EncodedKeychainFeaturePoint, KeychainFeaturePoint};
8
9const DEFAULT_ENDPOINT: &str = "https://dev.dji.com/openapi/v1/flight-records/keychains";
10
11#[derive(Debug, Default, Serialize, Clone)]
13#[cfg_attr(target_arch = "wasm32", derive(Tsify))]
14pub struct KeychainsRequest {
15 pub version: u16,
16 pub department: u8,
17 #[serde(rename = "keychainsArray")]
18 pub keychains: Vec<Vec<EncodedKeychainFeaturePoint>>,
19}
20
21#[derive(Debug, Deserialize)]
23pub struct KeychainsResponse {
24 pub data: Option<Vec<Vec<KeychainFeaturePoint>>>,
25 pub result: KeychainResponseResult,
26}
27
28#[derive(Debug, Deserialize)]
29#[serde(rename_all = "camelCase")]
30pub struct KeychainResponseResult {
31 pub code: u8,
32 pub msg: String,
33}
34
35impl KeychainsRequest {
36 #[cfg(not(target_arch = "wasm32"))]
50 pub fn fetch(
51 &self,
52 api_key: &str,
53 endpoint: Option<&str>,
54 ) -> Result<Vec<Vec<KeychainFeaturePoint>>> {
55 let endpoint = endpoint.unwrap_or(DEFAULT_ENDPOINT);
56 native::fetch(api_key, endpoint, self)
57 }
58
59 #[cfg(any(target_arch = "wasm32", feature = "native-async"))]
73 pub async fn fetch_async(
74 &self,
75 api_key: &str,
76 endpoint: Option<&str>,
77 ) -> Result<Vec<Vec<KeychainFeaturePoint>>> {
78 let endpoint = endpoint.unwrap_or(DEFAULT_ENDPOINT);
79
80 #[cfg(not(target_arch = "wasm32"))]
81 return native::fetch_async(api_key, endpoint, self).await;
82 #[cfg(target_arch = "wasm32")]
83 return wasm::fetch_async(api_key, endpoint, self).await;
84 }
85}
86
87#[cfg(not(target_arch = "wasm32"))]
88pub(crate) mod native {
89 use std::time::Duration;
90
91 use crate::keychain::KeychainFeaturePoint;
92 use crate::{Error, Result};
93
94 use super::{KeychainsRequest, KeychainsResponse};
95
96 pub fn fetch(
97 api_key: &str,
98 endpoint: &str,
99 request: &KeychainsRequest,
100 ) -> Result<Vec<Vec<KeychainFeaturePoint>>> {
101 let body = serde_json::to_string(&request)?;
102
103 let response = ureq::post(endpoint)
104 .set("Content-Type", "application/json")
105 .set("Api-Key", api_key)
106 .timeout(Duration::from_secs(30))
107 .send_string(&body)
108 .map_err(|e| match e {
109 ureq::Error::Status(403, _) => Error::ApiKeyError,
110 ureq::Error::Status(status, _) => Error::NetworkRequestStatus(status),
111 _ => Error::NetworkConnection,
112 })?;
113
114 let keychains_response: KeychainsResponse = response.into_json()?;
115
116 if keychains_response.result.code != 0 {
117 Err(Error::ApiError(keychains_response.result.msg))
118 } else {
119 match keychains_response.data {
120 Some(data) => Ok(data),
121 None => Err(Error::ApiError("Missing keychain data".to_owned())),
122 }
123 }
124 }
125
126 #[cfg(feature = "native-async")]
127 pub async fn fetch_async(
128 api_key: &str,
129 endpoint: &str,
130 request: &KeychainsRequest,
131 ) -> Result<Vec<Vec<KeychainFeaturePoint>>> {
132 let api_key = api_key.to_string();
133 let endpoint = endpoint.to_string();
134 let request = request.clone();
135
136 let (tx, rx) = async_channel::bounded(1);
137
138 std::thread::spawn(move || {
139 let response = fetch(&api_key, &endpoint, &request);
140 tx.send(response);
141 });
142
143 rx.recv().await.map_err(|_| Error::NetworkConnection)?
144 }
145}
146
147#[cfg(target_arch = "wasm32")]
148pub(crate) mod wasm {
149 use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
150 use wasm_bindgen_futures::js_sys::Promise;
151 use wasm_bindgen_futures::JsFuture;
152 use web_sys::{Request, RequestInit, Response};
153
154 use crate::keychain::KeychainFeaturePoint;
155 use crate::{Error, Result};
156
157 use super::{KeychainsRequest, KeychainsResponse};
158
159 #[wasm_bindgen]
160 extern "C" {
161 #[wasm_bindgen(js_name = fetch)]
162 fn js_fetch(input: &Request) -> Promise;
163 }
164
165 pub async fn fetch_async(
166 api_key: &str,
167 endpoint: &str,
168 request: &KeychainsRequest,
169 ) -> Result<Vec<Vec<KeychainFeaturePoint>>> {
170 let body = serde_json::to_string(&request)?;
171
172 let mut init = RequestInit::new();
173 init.method("POST");
174 init.body(Some(&JsValue::from_str(&body)));
175
176 let request = Request::new_with_str_and_init(endpoint, &init)
177 .map_err(|_| Error::ApiError("Unable to init request".to_owned()))?;
178
179 request
180 .headers()
181 .set("Content-Type", "application/json")
182 .map_err(|_| Error::ApiError("Unable to set Content-Type header".to_owned()))?;
183 request
184 .headers()
185 .set("Api-Key", &api_key)
186 .map_err(|_| Error::ApiError("Unable to set Api-Key header".to_owned()))?;
187
188 let response: Response = JsFuture::from(js_fetch(&request))
189 .await
190 .map_err(|_| Error::ApiError("Unable to fetch request".to_owned()))?
191 .unchecked_into();
192
193 if !response.ok() {
194 return Err(Error::ApiKeyError);
195 }
196
197 let json = JsFuture::from(
198 response
199 .json()
200 .map_err(|_| Error::ApiError("Unable get response".to_owned()))?,
201 )
202 .await
203 .map_err(|_| Error::ApiError("Unable to get response".to_owned()))?;
204
205 let keychains_response: KeychainsResponse = serde_wasm_bindgen::from_value(json)
206 .map_err(|_| Error::ApiError("Unable to parse response".to_owned()))?;
207
208 if keychains_response.result.code != 0 {
209 Err(Error::ApiError(keychains_response.result.msg))
210 } else {
211 match keychains_response.data {
212 Some(data) => Ok(data),
213 None => Err(Error::ApiError("Missing keychain data".to_owned())),
214 }
215 }
216 }
217}