1use serde_json::Value;
2
3use crate::error::{map_manager_err, ManagerError};
4use crate::gen::manager::apis::configuration::Configuration;
5use crate::gen::manager::apis::{application_api as api, local_automation_api, manager_api};
6use crate::gen::manager::models;
7use crate::http::{collect_all, fetch_page, Page};
8use crate::resources::raw::{get_json, post_json, post_json_body, put_json};
9use crate::retry::{with_retry, RetryPolicy};
10use crate::token::SharedCfg;
11
12#[derive(Debug, Clone, PartialEq)]
19#[non_exhaustive]
20pub enum ApplicationItem {
21 Typed(models::Application),
23 Unknown {
25 module: String,
27 raw: serde_json::Value,
29 },
30}
31
32impl ApplicationItem {
33 pub fn as_typed(&self) -> Option<&models::Application> {
35 match self {
36 ApplicationItem::Typed(app) => Some(app),
37 ApplicationItem::Unknown { .. } => None,
38 }
39 }
40
41 pub fn module(&self) -> &str {
43 use models::Application as App;
44 match self {
45 ApplicationItem::Typed(app) => match app {
46 App::AgentQueue(_) => "agentQueue",
47 App::Agentic(_) => "agentic",
48 App::AudioPlayer(_) => "audioPlayer",
49 App::ConsumerQueue(_) => "consumerQueue",
50 App::InputReader(_) => "inputReader",
51 App::InputReaderV2(_) => "inputReader.v2",
52 App::PromptPlayer(_) => "promptPlayer",
53 App::Recording(_) => "recording",
54 App::SimpleMenu(_) => "simpleMenu",
55 App::SpeechToText(_) => "speechToText",
56 App::SwitchNode(_) => "switchNode",
57 App::TextToSpeech(_) => "textToSpeech",
58 App::Transfer(_) => "transfer",
59 },
60 ApplicationItem::Unknown { module, .. } => module,
61 }
62 }
63}
64
65fn decode_application(v: Value) -> Result<ApplicationItem, ManagerError> {
75 use models::Application as App;
76 fn var<T: serde::de::DeserializeOwned>(v: Value) -> Result<Box<T>, ManagerError> {
77 serde_json::from_value(v)
78 .map(Box::new)
79 .map_err(|e| ManagerError::Decode(e.to_string()))
80 }
81 if !v.is_object() {
82 return Err(ManagerError::Decode(
83 "application item is not a JSON object".into(),
84 ));
85 }
86 let module = v
87 .get("module")
88 .and_then(Value::as_str)
89 .unwrap_or_default()
90 .to_string();
91 Ok(ApplicationItem::Typed(match module.as_str() {
92 "agentQueue" => App::AgentQueue(var(v)?),
93 "agentic" => App::Agentic(var(v)?),
94 "audioPlayer" => App::AudioPlayer(var(v)?),
95 "consumerQueue" => App::ConsumerQueue(var(v)?),
96 "inputReader" => App::InputReader(var(v)?),
97 "inputReader.v2" => App::InputReaderV2(var(v)?),
98 "promptPlayer" => App::PromptPlayer(var(v)?),
99 "recording" => App::Recording(var(v)?),
100 "simpleMenu" => App::SimpleMenu(var(v)?),
101 "speechToText" => App::SpeechToText(var(v)?),
102 "switchNode" => App::SwitchNode(var(v)?),
103 "textToSpeech" => App::TextToSpeech(var(v)?),
104 "transfer" => App::Transfer(var(v)?),
105 _ => return Ok(ApplicationItem::Unknown { module, raw: v }),
106 }))
107}
108
109fn decode_item_envelope(v: Value) -> Result<ApplicationItem, ManagerError> {
112 let item = v
113 .get("item")
114 .cloned()
115 .ok_or_else(|| ManagerError::Decode("response had no `item`".into()))?;
116 decode_application(item)
117}
118
119async fn applications_page(
125 cfg: &Configuration,
126 page: i32,
127 per_page: Option<i32>,
128) -> Result<(Vec<ApplicationItem>, i32, i32, Option<i32>), ManagerError> {
129 let mut path = format!("/api/v2/applications?page={page}");
130 if let Some(max) = per_page {
131 path.push_str(&format!("&max={max}"));
132 }
133 let v = get_json(cfg, &path).await?;
134 let items = v
135 .get("items")
136 .and_then(Value::as_array)
137 .cloned()
138 .unwrap_or_default()
139 .into_iter()
140 .map(decode_application)
141 .collect::<Result<Vec<_>, _>>()?;
142 let p = v.get("pagination").cloned().unwrap_or(Value::Null);
143 let num = |k: &str| p.get(k).and_then(Value::as_i64).map(|n| n as i32);
144 Ok((
145 items,
146 num("pages").unwrap_or(1),
147 num("current").unwrap_or(page),
148 num("total"),
149 ))
150}
151
152pub struct ApplicationsResource {
154 pub(crate) cfg: SharedCfg<Configuration>,
155 pub(crate) retry: RetryPolicy,
156 pub local_automations: LocalAutomationsResource,
158}
159
160impl ApplicationsResource {
161 pub async fn list_all(&self) -> Result<Vec<ApplicationItem>, ManagerError> {
163 let cfg = self.cfg.get().await?;
164 let cfg = cfg.as_ref();
165 collect_all(
166 &self.retry,
167 |e| e,
168 |page| async move {
169 applications_page(cfg, page, None)
170 .await
171 .map(|(items, pages, current, _)| (items, pages, current))
172 },
173 )
174 .await
175 }
176
177 pub async fn list_page(
179 &self,
180 page: i32,
181 per_page: Option<i32>,
182 ) -> Result<Page<ApplicationItem>, ManagerError> {
183 let cfg = self.cfg.get().await?;
184 let cfg = cfg.as_ref();
185 fetch_page(
186 &self.retry,
187 |e| e,
188 || async move { applications_page(cfg, page, per_page).await },
189 )
190 .await
191 }
192
193 pub async fn all_local_automations(
195 &self,
196 ) -> Result<Vec<models::LocalAutomation>, ManagerError> {
197 let cfg = self.cfg.get().await?;
198 let cfg = cfg.as_ref();
199 collect_all(&self.retry, map_manager_err, |page| async move {
200 let r = local_automation_api::list_all_local_automations(cfg, Some(page), None).await?;
201 Ok((
202 r.items,
203 r.pagination.pages.unwrap_or(1),
204 r.pagination.current.unwrap_or(1),
205 ))
206 })
207 .await
208 }
209
210 pub async fn create(
217 &self,
218 body: models::ApplicationCreateBody,
219 ) -> Result<ApplicationItem, ManagerError> {
220 let body = serde_json::to_value(&body)
221 .map_err(|e| ManagerError::InvalidArgument(e.to_string()))?;
222 decode_item_envelope(self.create_raw(body).await?)
223 }
224
225 pub async fn create_raw(&self, body: Value) -> Result<Value, ManagerError> {
228 let cfg = self.cfg.get().await?;
229 let cfg = cfg.as_ref();
230 with_retry(&self.retry, false, || {
232 post_json_body(cfg, "/api/v2/applications", &body)
233 })
234 .await
235 }
236
237 pub async fn get(&self, id: &str) -> Result<ApplicationItem, ManagerError> {
243 let cfg = self.cfg.get().await?;
244 let cfg = cfg.as_ref();
245 let path = format!(
246 "/api/v2/applications/{}",
247 crate::gen::manager::apis::urlencode(id)
248 );
249 with_retry(&self.retry, true, || async {
250 decode_item_envelope(get_json(cfg, &path).await?)
251 })
252 .await
253 }
254
255 pub async fn update(
260 &self,
261 id: &str,
262 body: models::ApplicationUpdateBody,
263 ) -> Result<ApplicationItem, ManagerError> {
264 let body = serde_json::to_value(&body)
265 .map_err(|e| ManagerError::InvalidArgument(e.to_string()))?;
266 decode_item_envelope(self.update_raw(id, body).await?)
267 }
268
269 pub async fn update_raw(&self, id: &str, body: Value) -> Result<Value, ManagerError> {
272 let cfg = self.cfg.get().await?;
273 let cfg = cfg.as_ref();
274 let path = format!(
275 "/api/v2/applications/{}",
276 crate::gen::manager::apis::urlencode(id)
277 );
278 with_retry(&self.retry, false, || put_json(cfg, &path, &body)).await
280 }
281
282 pub async fn delete(&self, id: &str) -> Result<(), ManagerError> {
284 let cfg = self.cfg.get().await?;
285 let cfg = cfg.as_ref();
286 with_retry(&self.retry, false, || api::delete_application(cfg, id))
287 .await
288 .map_err(map_manager_err)?;
289 Ok(())
290 }
291
292 pub async fn delete_many(
294 &self,
295 ids: Vec<String>,
296 ) -> Result<models::DefaultV2MessageResponse, ManagerError> {
297 let cfg = self.cfg.get().await?;
298 let cfg = cfg.as_ref();
299 let body = models::DeleteManyApplicationsRequest { ids };
300 with_retry(&self.retry, false, || {
301 api::delete_many_applications(cfg, body.clone())
302 })
303 .await
304 .map_err(map_manager_err)
305 }
306
307 pub async fn list_modules(&self) -> Result<models::ListModulesResponse, ManagerError> {
309 let cfg = self.cfg.get().await?;
310 let cfg = cfg.as_ref();
311 with_retry(&self.retry, true, || api::list_modules(cfg))
312 .await
313 .map_err(map_manager_err)
314 }
315
316 pub async fn dispatch(
319 &self,
320 id: &str,
321 position: &str,
322 is_async: bool,
323 body: models::LocalAutomationDispatch,
324 ) -> Result<models::DispatchLocalAutomationsResponse, ManagerError> {
325 let cfg = self.cfg.get().await?;
326 let cfg = cfg.as_ref();
327 with_retry(&self.retry, false, || {
328 manager_api::dispatch_local_automations(cfg, id, position, is_async, Some(body.clone()))
329 })
330 .await
331 .map_err(map_manager_err)
332 }
333
334 pub async fn clone(&self, id: &str) -> Result<ApplicationItem, ManagerError> {
339 decode_item_envelope(self.clone_raw(id).await?)
340 }
341
342 pub async fn clone_raw(&self, id: &str) -> Result<Value, ManagerError> {
345 let cfg = self.cfg.get().await?;
346 let cfg = cfg.as_ref();
347 let path = format!(
348 "/api/v2/applications/{}/clone",
349 crate::gen::manager::apis::urlencode(id)
350 );
351 with_retry(&self.retry, false, || post_json(cfg, &path)).await
353 }
354
355 pub async fn bulk_update(
357 &self,
358 body: models::BulkUpdateApplicationsRequest,
359 ) -> Result<models::DefaultV2MessageResponse, ManagerError> {
360 let cfg = self.cfg.get().await?;
361 let cfg = cfg.as_ref();
362 with_retry(&self.retry, false, || {
363 api::bulk_update_applications(cfg, body.clone())
364 })
365 .await
366 .map_err(map_manager_err)
367 }
368
369 pub async fn list_app_actions(&self) -> Result<models::ObjectListResponse, ManagerError> {
371 let cfg = self.cfg.get().await?;
372 let cfg = cfg.as_ref();
373 with_retry(&self.retry, true, || {
374 api::list_application_actions(cfg, None, None)
375 })
376 .await
377 .map_err(map_manager_err)
378 }
379
380 pub async fn list_errors(&self) -> Result<models::ObjectListResponse, ManagerError> {
382 let cfg = self.cfg.get().await?;
383 let cfg = cfg.as_ref();
384 with_retry(&self.retry, true, || api::list_application_errors(cfg))
385 .await
386 .map_err(map_manager_err)
387 }
388}
389
390pub struct LocalAutomationsResource {
392 pub(crate) cfg: SharedCfg<Configuration>,
393 pub(crate) retry: RetryPolicy,
394}
395
396impl LocalAutomationsResource {
397 pub async fn list_all(
399 &self,
400 application_id: &str,
401 ) -> Result<Vec<models::LocalAutomation>, ManagerError> {
402 let cfg = self.cfg.get().await?;
403 let cfg = cfg.as_ref();
404 collect_all(&self.retry, map_manager_err, |page| async move {
405 let r =
406 local_automation_api::list_local_automations(cfg, application_id, Some(page), None)
407 .await?;
408 Ok((
409 r.items,
410 r.pagination.pages.unwrap_or(1),
411 r.pagination.current.unwrap_or(1),
412 ))
413 })
414 .await
415 }
416
417 pub async fn create(
419 &self,
420 application_id: &str,
421 body: models::RestCreateLocalAutomation,
422 ) -> Result<models::LocalAutomationItemResponse, ManagerError> {
423 let cfg = self.cfg.get().await?;
424 let cfg = cfg.as_ref();
425 with_retry(&self.retry, false, || {
426 local_automation_api::create_local_automation(cfg, application_id, body.clone())
427 })
428 .await
429 .map_err(map_manager_err)
430 }
431
432 pub async fn get(
434 &self,
435 application_id: &str,
436 id: &str,
437 ) -> Result<models::LocalAutomationItemResponse, ManagerError> {
438 let cfg = self.cfg.get().await?;
439 let cfg = cfg.as_ref();
440 with_retry(&self.retry, true, || {
441 local_automation_api::get_local_automation(cfg, application_id, id)
442 })
443 .await
444 .map_err(map_manager_err)
445 }
446
447 pub async fn update(
449 &self,
450 application_id: &str,
451 id: &str,
452 body: models::RestUpdateLocalAutomation,
453 ) -> Result<models::LocalAutomationItemResponse, ManagerError> {
454 let cfg = self.cfg.get().await?;
455 let cfg = cfg.as_ref();
456 with_retry(&self.retry, false, || {
457 local_automation_api::update_local_automation(cfg, application_id, id, body.clone())
458 })
459 .await
460 .map_err(map_manager_err)
461 }
462
463 pub async fn delete(&self, application_id: &str, id: &str) -> Result<(), ManagerError> {
465 let cfg = self.cfg.get().await?;
466 let cfg = cfg.as_ref();
467 with_retry(&self.retry, false, || {
468 local_automation_api::delete_local_automation(cfg, application_id, id)
469 })
470 .await
471 .map_err(map_manager_err)?;
472 Ok(())
473 }
474}