diff --git a/docs/content.en/docs/release-notes/_index.md b/docs/content.en/docs/release-notes/_index.md index afb0f93e..5c31aab6 100644 --- a/docs/content.en/docs/release-notes/_index.md +++ b/docs/content.en/docs/release-notes/_index.md @@ -36,7 +36,8 @@ Information about release notes of Coco Server is provided here. - refactor: refactoring login callback, receive access_token from coco-server - chore: adjust web component styles #362 - style: modify the style #370 -- style: search list details display #378 #380 +- style: search list details display #378 +- refactor: refactoring api error handling #382 ## 0.3.0 (2025-03-31) diff --git a/src-tauri/src/assistant/mod.rs b/src-tauri/src/assistant/mod.rs index 38533006..14b8a662 100644 --- a/src-tauri/src/assistant/mod.rs +++ b/src-tauri/src/assistant/mod.rs @@ -1,7 +1,7 @@ +use crate::common; use crate::common::assistant::ChatRequestMessage; use crate::common::http::GetResponse; use crate::server::http_client::HttpClient; -use reqwest::Response; use serde_json::Value; use std::collections::HashMap; use tauri::{AppHandle, Runtime}; @@ -24,8 +24,8 @@ pub async fn chat_history( if let Some(query) = query { if !query.is_empty() { - query_params.insert("query".to_string(), query.into()); - } + query_params.insert("query".to_string(), query.into()); + } } let response = HttpClient::get(&server_id, "/chat/_history", Some(query_params)) @@ -35,21 +35,8 @@ pub async fn chat_history( format!("Error get history: {}", e) })?; - handle_raw_response(response).await? -} -async fn handle_raw_response(response: Response) -> Result, String> { - Ok( - if response.status().as_u16() < 200 || response.status().as_u16() >= 400 { - Err("Failed to send message".to_string()) - } else { - let body = response - .text() - .await - .map_err(|e| format!("Failed to parse response JSON: {}", e))?; - Ok(body) - }, - ) + common::http::get_response_body_text(response).await } #[tauri::command] @@ -74,7 +61,7 @@ pub async fn session_chat_history( .await .map_err(|e| format!("Error get session message: {}", e))?; - handle_raw_response(response).await? + common::http::get_response_body_text(response).await } #[tauri::command] @@ -90,7 +77,7 @@ pub async fn open_session_chat( .await .map_err(|e| format!("Error open session: {}", e))?; - handle_raw_response(response).await? + common::http::get_response_body_text(response).await } #[tauri::command] @@ -106,7 +93,7 @@ pub async fn close_session_chat( .await .map_err(|e| format!("Error close session: {}", e))?; - handle_raw_response(response).await? + common::http::get_response_body_text(response).await } #[tauri::command] pub async fn cancel_session_chat( @@ -121,7 +108,7 @@ pub async fn cancel_session_chat( .await .map_err(|e| format!("Error cancel session: {}", e))?; - handle_raw_response(response).await? + common::http::get_response_body_text(response).await } #[tauri::command] @@ -130,14 +117,17 @@ pub async fn new_chat( server_id: String, websocket_id: String, message: String, - query_params: Option>, //search,deep_thinking + query_params: Option>, ) -> Result { let body = if !message.is_empty() { let message = ChatRequestMessage { message: Some(message), }; - let body = reqwest::Body::from(serde_json::to_string(&message).unwrap()); - Some(body) + Some( + serde_json::to_string(&message) + .map_err(|e| format!("Failed to serialize message: {}", e))? + .into(), + ) } else { None }; @@ -145,21 +135,18 @@ pub async fn new_chat( let mut headers = HashMap::new(); headers.insert("WEBSOCKET-SESSION-ID".to_string(), websocket_id.into()); - let response = - HttpClient::advanced_post(&server_id, "/chat/_new", Some(headers), query_params, body) - .await - .map_err(|e| format!("Error sending message: {}", e))?; - - if response.status().as_u16() < 200 || response.status().as_u16() >= 400 { - return Err("Failed to send message".to_string()); - } - - let chat_response: GetResponse = response - .json() + let response = HttpClient::advanced_post(&server_id, "/chat/_new", Some(headers), query_params, body) .await + .map_err(|e| format!("Error sending message: {}", e))?; + + let text = response + .text() + .await + .map_err(|e| format!("Failed to read response body: {}", e))?; + + let chat_response: GetResponse = serde_json::from_str(&text) .map_err(|e| format!("Failed to parse response JSON: {}", e))?; - // Check the result and status fields if chat_response.result != "created" { return Err(format!("Unexpected result: {}", chat_response.result)); } @@ -192,10 +179,10 @@ pub async fn send_message( query_params, Some(body), ) - .await - .map_err(|e| format!("Error cancel session: {}", e))?; + .await + .map_err(|e| format!("Error cancel session: {}", e))?; - handle_raw_response(response).await? + common::http::get_response_body_text(response).await } #[tauri::command] @@ -235,8 +222,8 @@ pub async fn update_session_chat( None, Some(reqwest::Body::from(serde_json::to_string(&body).unwrap())), ) - .await - .map_err(|e| format!("Error updating session: {}", e))?; + .await + .map_err(|e| format!("Error updating session: {}", e))?; Ok(response.status().is_success()) } diff --git a/src-tauri/src/common/error.rs b/src-tauri/src/common/error.rs new file mode 100644 index 00000000..0347c806 --- /dev/null +++ b/src-tauri/src/common/error.rs @@ -0,0 +1,45 @@ +use serde::{Deserialize, Serialize}; +use thiserror::Error; + +#[derive(Debug, Deserialize)] +pub struct ErrorDetail { + pub reason: String, + pub status: u16, +} + +#[derive(Debug, Deserialize)] +pub struct ErrorResponse { + pub error: ErrorDetail, +} + +#[derive(Debug, Error, Serialize)] +pub enum SearchError { + #[error("HTTP request failed: {0}")] + HttpError(String), + + #[error("Invalid response format: {0}")] + ParseError(String), + + #[error("Timeout occurred")] + Timeout, + + #[error("Unknown error: {0}")] + #[allow(dead_code)] + Unknown(String), + + #[error("InternalError error: {0}")] + #[allow(dead_code)] + InternalError(String), +} + +impl From for SearchError { + fn from(err: reqwest::Error) -> Self { + if err.is_timeout() { + SearchError::Timeout + } else if err.is_decode() { + SearchError::ParseError(err.to_string()) + } else { + SearchError::HttpError(err.to_string()) + } + } +} \ No newline at end of file diff --git a/src-tauri/src/common/http.rs b/src-tauri/src/common/http.rs index 6a58145f..bbdefc0c 100644 --- a/src-tauri/src/common/http.rs +++ b/src-tauri/src/common/http.rs @@ -1,3 +1,5 @@ +use crate::common; +use reqwest::Response; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -15,4 +17,34 @@ pub struct Source { pub created: String, pub updated: String, pub status: String, +} + +pub async fn get_response_body_text(response: Response) -> Result { + let status = response.status().as_u16(); + let body = response + .text() + .await + .map_err(|e| format!("Failed to read response body: {}", e))?; + + if status < 200 || status >= 400 { + // Try to parse the error body + let fallback_error = "Failed to send message".to_string(); + + if body.trim().is_empty() { + return Err(fallback_error); + } + + match serde_json::from_str::(&body) { + Ok(parsed_error) => { + dbg!(&parsed_error); + Err(format!( + "Server error ({}): {}", + parsed_error.error.status, parsed_error.error.reason + )) + } + Err(_) => Err(fallback_error), + } + } else { + Ok(body) + } } \ No newline at end of file diff --git a/src-tauri/src/common/mod.rs b/src-tauri/src/common/mod.rs index 7eba4ca2..0366dee1 100644 --- a/src-tauri/src/common/mod.rs +++ b/src-tauri/src/common/mod.rs @@ -10,6 +10,7 @@ pub mod traits; pub mod register; pub mod assistant; pub mod http; +pub mod error; pub static MAIN_WINDOW_LABEL: &str = "main"; pub static SETTINGS_WINDOW_LABEL: &str = "settings"; diff --git a/src-tauri/src/common/search.rs b/src-tauri/src/common/search.rs index f6511f80..0e90a127 100644 --- a/src-tauri/src/common/search.rs +++ b/src-tauri/src/common/search.rs @@ -1,9 +1,10 @@ use crate::common::document::Document; +use crate::common::http::get_response_body_text; use reqwest::Response; use serde::{Deserialize, Serialize}; -use serde_json::Value; use std::collections::HashMap; use std::error::Error; + #[derive(Debug, Serialize, Deserialize)] pub struct SearchResponse { pub took: u64, @@ -47,14 +48,11 @@ pub async fn parse_search_response( where T: for<'de> Deserialize<'de> + std::fmt::Debug, { - let body = response - .json::() - .await - .map_err(|e| format!("Failed to parse JSON: {}", e))?; + let body_text = get_response_body_text(response).await?; - // dbg!(&body); + dbg!(&body_text); - let search_response: SearchResponse = serde_json::from_value(body) + let search_response: SearchResponse = serde_json::from_str(&body_text) .map_err(|e| format!("Failed to deserialize search response: {}", e))?; Ok(search_response) diff --git a/src-tauri/src/common/traits.rs b/src-tauri/src/common/traits.rs index b9314ac5..a4a1cca2 100644 --- a/src-tauri/src/common/traits.rs +++ b/src-tauri/src/common/traits.rs @@ -1,10 +1,8 @@ -use crate::common::search::{QueryResponse, QuerySource}; -use thiserror::Error; - -use async_trait::async_trait; +use crate::common::error::SearchError; // use std::{future::Future, pin::Pin}; use crate::common::search::SearchQuery; -use serde::Serialize; +use crate::common::search::{QueryResponse, QuerySource}; +use async_trait::async_trait; #[async_trait] pub trait SearchSource: Send + Sync { @@ -13,34 +11,3 @@ pub trait SearchSource: Send + Sync { async fn search(&self, query: SearchQuery) -> Result; } -#[derive(Debug, Error, Serialize)] -pub enum SearchError { - #[error("HTTP request failed: {0}")] - HttpError(String), - - #[error("Invalid response format: {0}")] - ParseError(String), - - #[error("Timeout occurred")] - Timeout, - - #[error("Unknown error: {0}")] - #[allow(dead_code)] - Unknown(String), - - #[error("InternalError error: {0}")] - #[allow(dead_code)] - InternalError(String), -} - -impl From for SearchError { - fn from(err: reqwest::Error) -> Self { - if err.is_timeout() { - SearchError::Timeout - } else if err.is_decode() { - SearchError::ParseError(err.to_string()) - } else { - SearchError::HttpError(err.to_string()) - } - } -} diff --git a/src-tauri/src/local/application.rs b/src-tauri/src/local/application.rs index f21f8803..afce919f 100644 --- a/src-tauri/src/local/application.rs +++ b/src-tauri/src/local/application.rs @@ -1,6 +1,7 @@ use crate::common::document::{DataSourceReference, Document}; +use crate::common::error::SearchError; use crate::common::search::{QueryResponse, QuerySource, SearchQuery}; -use crate::common::traits::{SearchError, SearchSource}; +use crate::common::traits::SearchSource; use crate::local::LOCAL_QUERY_SOURCE_TYPE; use applications::{App, AppInfo, AppInfoContext}; use async_trait::async_trait; diff --git a/src-tauri/src/search/mod.rs b/src-tauri/src/search/mod.rs index 29fa7db5..cde9b972 100644 --- a/src-tauri/src/search/mod.rs +++ b/src-tauri/src/search/mod.rs @@ -1,8 +1,8 @@ +use crate::common::error::SearchError; use crate::common::register::SearchSourceRegistry; use crate::common::search::{ FailedRequest, MultiSourceQueryResponse, QueryHits, QuerySource, SearchQuery, }; -use crate::common::traits::SearchError; use futures::stream::FuturesUnordered; use futures::StreamExt; use std::collections::HashMap; @@ -41,7 +41,7 @@ pub async fn query_coco_fusion( timeout(timeout_duration, async { query_source_clone.search(query).await }) - .await + .await })); } diff --git a/src-tauri/src/server/attachment.rs b/src-tauri/src/server/attachment.rs index 5b05336c..827ffeef 100644 --- a/src-tauri/src/server/attachment.rs +++ b/src-tauri/src/server/attachment.rs @@ -1,4 +1,5 @@ use super::servers::{get_server_by_id, get_server_token}; +use crate::common::http::get_response_body_text; use crate::server::http_client::HttpClient; use reqwest::multipart::{Form, Part}; use serde::{Deserialize, Serialize}; @@ -99,16 +100,10 @@ pub async fn upload_attachment( .await .map_err(|err| err.to_string())?; - if response.status().is_success() { - let result = response - .json::() - .await - .map_err(|err| err.to_string())?; + let body = get_response_body_text(response).await?; - Ok(result) - } else { - Err(format!("Upload failed with status: {}", response.status())) - } + serde_json::from_str::(&body) + .map_err(|e| format!("Failed to parse upload response: {}", e)) } #[command] @@ -119,33 +114,30 @@ pub async fn get_attachment( let mut query_params = HashMap::new(); query_params.insert("session".to_string(), serde_json::Value::String(session_id)); - let response = HttpClient::get(&server_id, "/attachment/_search", Some(query_params)).await?; + let response = HttpClient::get(&server_id, "/attachment/_search", Some(query_params)) + .await + .map_err(|e| format!("Request error: {}", e))?; - if response.status().is_success() { - response - .json::() - .await - .map_err(|e| e.to_string()) - } else { - Err(format!("Request failed with status: {}", response.status())) - } + let body = get_response_body_text(response).await?; + + serde_json::from_str::(&body) + .map_err(|e| format!("Failed to parse attachment response: {}", e)) } #[command] pub async fn delete_attachment(server_id: String, id: String) -> Result { - let response = - HttpClient::delete(&server_id, &format!("/attachment/{}", id), None, None).await?; + let response = HttpClient::delete(&server_id, &format!("/attachment/{}", id), None, None) + .await + .map_err(|e| format!("Request error: {}", e))?; - if response.status().is_success() { - response - .json::() - .await - .map_err(|e| e.to_string())? - .result - .eq("deleted") - .then_some(true) - .ok_or("Delete operation was not successful".to_string()) - } else { - Err(format!("Delete failed with status: {}", response.status())) - } + let body = get_response_body_text(response).await?; + + let parsed: DeleteAttachmentResponse = serde_json::from_str(&body) + .map_err(|e| format!("Failed to parse delete response: {}", e))?; + + parsed + .result + .eq("deleted") + .then_some(true) + .ok_or_else(|| "Delete operation was not successful".to_string()) } diff --git a/src-tauri/src/server/http_client.rs b/src-tauri/src/server/http_client.rs index a1d07923..227d163a 100644 --- a/src-tauri/src/server/http_client.rs +++ b/src-tauri/src/server/http_client.rs @@ -58,6 +58,7 @@ impl HttpClient { ) -> RequestBuilder { let client = HTTP_CLIENT.lock().await; // Acquire the lock on HTTP_CLIENT + // Build the request let mut request_builder = client.request(method.clone(), url); diff --git a/src-tauri/src/server/profile.rs b/src-tauri/src/server/profile.rs index a59e625c..db3c9ae9 100644 --- a/src-tauri/src/server/profile.rs +++ b/src-tauri/src/server/profile.rs @@ -1,3 +1,4 @@ +use crate::common::http::get_response_body_text; use crate::common::profile::UserProfile; use crate::server::http_client::HttpClient; use tauri::{AppHandle, Runtime}; @@ -12,14 +13,16 @@ pub async fn get_user_profiles( .await .map_err(|e| format!("Error fetching profile: {}", e))?; - if let Some(content_length) = response.content_length() { - if content_length > 0 { - let profile: UserProfile = response - .json() - .await - .map_err(|e| format!("Failed to parse response: {}", e))?; - return Ok(profile); - } + // Use get_response_body_text to extract the body content + let response_body = get_response_body_text(response) + .await + .map_err(|e| format!("Failed to read response body: {}", e))?; + + // Check if the response body is not empty before deserializing + if !response_body.is_empty() { + let profile: UserProfile = serde_json::from_str(&response_body) + .map_err(|e| format!("Failed to parse response: {}", e))?; + return Ok(profile); } Err("Profile not found or empty response".to_string()) diff --git a/src-tauri/src/server/search.rs b/src-tauri/src/server/search.rs index 41cbf9ba..58bb03a6 100644 --- a/src-tauri/src/server/search.rs +++ b/src-tauri/src/server/search.rs @@ -1,9 +1,9 @@ use crate::common::document::Document; -use crate::common::search::{ - parse_search_response, QueryHits, QueryResponse, QuerySource, SearchQuery, -}; +use crate::common::error::SearchError; +use crate::common::http::get_response_body_text; +use crate::common::search::{QueryHits, QueryResponse, QuerySource, SearchQuery, SearchResponse}; use crate::common::server::Server; -use crate::common::traits::{SearchError, SearchSource}; +use crate::common::traits::SearchSource; use crate::server::http_client::HttpClient; use crate::server::servers::get_server_token; use async_trait::async_trait; @@ -46,7 +46,7 @@ impl DocumentsSizedCollector { } } - fn documents(self) -> impl ExactSizeIterator { + fn documents(self) -> impl ExactSizeIterator { self.docs.into_iter().map(|(_, doc, _)| doc) } @@ -126,58 +126,42 @@ impl SearchSource for CocoSearchSource { } } - // Directly return Result instead of Future async fn search(&self, query: SearchQuery) -> Result { - let _server_id = self.server.id.clone(); - let _server_name = self.server.name.clone(); - let request_builder = self.build_request_from_query(&query).await.unwrap(); + // Build the request from the provided query + let request_builder = self + .build_request_from_query(&query) + .await + .map_err(|e| SearchError::InternalError(e.to_string()))?; - // Send the HTTP request asynchronously - let response = request_builder.send().await; + // Send the HTTP request and handle errors + let response = request_builder + .send() + .await + .map_err(|e| SearchError::HttpError(format!("Failed to send search request: {}", e)))?; - match response { - Ok(response) => { - let status_code = response.status().as_u16(); + // Use the helper function to parse the response body + let response_body = get_response_body_text(response) + .await + .map_err(|e| SearchError::ParseError(format!("Failed to read response body: {}", e)))?; - if status_code >= 200 && status_code < 400 { - // Parse the response only if the status code is successful - match parse_search_response(response).await { - Ok(response) => { - let total_hits = response.hits.total.value as usize; - let hits: Vec<(Document, f64)> = response - .hits - .hits - .into_iter() - .map(|hit| { - // Handling Option in hit._score by defaulting to 0.0 if None - (hit._source, hit._score.unwrap_or(0.0)) // Use 0.0 if _score is None - }) - .collect(); + // Parse the search response from the body text + let parsed: SearchResponse = serde_json::from_str(&response_body) + .map_err(|e| SearchError::ParseError(format!("Failed to parse search response: {}", e)))?; - // Return the QueryResponse with hits and total hits - Ok(QueryResponse { - source: self.get_type(), - hits, - total_hits, - }) - } - Err(err) => { - // Parse error when response parsing fails - Err(SearchError::ParseError(err.to_string())) - } - } - } else { - // Handle unsuccessful HTTP status codes (e.g., 4xx, 5xx) - Err(SearchError::HttpError(format!( - "Request failed with status code: {}", - status_code - ))) - } - } - Err(err) => { - // Handle error from the request itself - Err(SearchError::HttpError(err.to_string())) - } - } + // Process the parsed response + let total_hits = parsed.hits.total.value as usize; + let hits: Vec<(Document, f64)> = parsed + .hits + .hits + .into_iter() + .map(|hit| (hit._source, hit._score.unwrap_or(0.0))) // Default _score to 0.0 if None + .collect(); + + // Return the final result + Ok(QueryResponse { + source: self.get_type(), + hits, + total_hits, + }) } } diff --git a/src-tauri/src/server/servers.rs b/src-tauri/src/server/servers.rs index 222dcaa3..a907fad6 100644 --- a/src-tauri/src/server/servers.rs +++ b/src-tauri/src/server/servers.rs @@ -1,3 +1,4 @@ +use crate::common::http::get_response_body_text; use crate::common::register::SearchSourceRegistry; use crate::common::server::{AuthProvider, Provider, Server, ServerAccessToken, Sso, Version}; use crate::server::connector::fetch_connectors_by_server; @@ -6,7 +7,7 @@ use crate::server::http_client::HttpClient; use crate::server::search::CocoSearchSource; use crate::COCO_TAURI_STORE; use lazy_static::lazy_static; -use reqwest::{Client, Method, StatusCode}; +use reqwest::{Client, Method}; use serde_json::from_value; use serde_json::Value as JsonValue; use std::collections::HashMap; @@ -298,61 +299,57 @@ pub async fn refresh_coco_server_info( id: String, ) -> Result { // Retrieve the server from the cache - let server = { + let cached_server = { let cache = SERVER_CACHE.read().unwrap(); cache.get(&id).cloned() }; - if let Some(server) = server { - let is_enabled = server.enabled; - let is_builtin = server.builtin; - let profile = server.profile; + let mut server = match cached_server { + Some(server) => server, + None => return Err("Server not found.".into()), + }; - // Use the HttpClient to send the request - let response = HttpClient::get(&id, "/provider/_info", None) // Assuming "/provider-info" is the endpoint - .await - .map_err(|e| format!("Failed to send request to the server: {}", e))?; + // Preserve important local state + let is_enabled = server.enabled; + let is_builtin = server.builtin; + let profile = server.profile; - if response.status() == StatusCode::OK { - if let Some(content_length) = response.content_length() { - if content_length > 0 { - let new_coco_server: Result = response.json().await; - match new_coco_server { - Ok(mut server) => { - server.id = id.clone(); - server.builtin = is_builtin; - server.enabled = is_enabled; - server.available = true; - server.profile = profile; - trim_endpoint_last_forward_slash(&mut server); - save_server(&server); - persist_servers(&app_handle) - .await - .expect("Failed to persist coco servers."); + // Send request to fetch updated server info + let response = HttpClient::get(&id, "/provider/_info", None) + .await + .map_err(|e| format!("Failed to contact the server: {}", e))?; - //refresh connectors and datasources - let _ = fetch_connectors_by_server(&id).await; - - let _ = get_datasources_by_server(&id, None).await; - - Ok(server) - } - Err(e) => Err(format!("Failed to deserialize the response: {:?}", e)), - } - } else { - Err("Received empty response body.".to_string()) - } - } else { - mark_server_as_offline(id.as_str()).await; - Err("Could not determine the content length.".to_string()) - } - } else { - mark_server_as_offline(id.as_str()).await; - Err(format!("Request failed with status: {}", response.status())) - } - } else { - Err("Server not found.".to_string()) + if !response.status().is_success() { + mark_server_as_offline(&id).await; + return Err(format!("Request failed with status: {}", response.status())); } + + // Get body text via helper + let body = get_response_body_text(response).await?; + + // Deserialize server + let mut updated_server: Server = serde_json::from_str(&body) + .map_err(|e| format!("Failed to deserialize the response: {}", e))?; + + // Restore local state + updated_server.id = id.clone(); + updated_server.builtin = is_builtin; + updated_server.enabled = is_enabled; + updated_server.available = true; + updated_server.profile = profile; + trim_endpoint_last_forward_slash(&mut updated_server); + + // Save and persist + save_server(&updated_server); + persist_servers(&app_handle) + .await + .map_err(|e| format!("Failed to persist servers: {}", e))?; + + // Refresh connectors and datasources (best effort) + let _ = fetch_connectors_by_server(&id).await; + let _ = get_datasources_by_server(&id, None).await; + + Ok(updated_server) } #[tauri::command] @@ -362,12 +359,10 @@ pub async fn add_coco_server( ) -> Result { load_or_insert_default_server(&app_handle) .await - .expect("Failed to load default servers"); + .map_err(|e| format!("Failed to load default servers: {}", e))?; - // Remove the trailing '/' from the endpoint to ensure correct URL construction let endpoint = endpoint.trim_end_matches('/'); - // Check if the server with this endpoint already exists if check_endpoint_exists(endpoint) { dbg!(format!( "This Coco server has already been registered: {:?}", @@ -376,59 +371,37 @@ pub async fn add_coco_server( return Err("This Coco server has already been registered.".into()); } - let url = provider_info_url(&endpoint); - - // Use the HttpClient to fetch provider information + let url = provider_info_url(endpoint); let response = HttpClient::send_raw_request(Method::GET, url.as_str(), None, None, None) .await .map_err(|e| format!("Failed to send request to the server: {}", e))?; dbg!(format!("Get provider info response: {:?}", &response)); - // Check if the response status is OK (200) - if response.status() == StatusCode::OK { - if let Some(content_length) = response.content_length() { - if content_length > 0 { - let new_coco_server: Result = response.json().await; + let body = get_response_body_text(response).await?; - match new_coco_server { - Ok(mut server) => { - // Perform necessary checks and adjustments on the server data - trim_endpoint_last_forward_slash(&mut server); + let mut server: Server = serde_json::from_str(&body) + .map_err(|e| format!("Failed to deserialize the response: {}", e))?; - if server.id.is_empty() { - server.id = pizza_common::utils::uuid::Uuid::new().to_string(); - } + trim_endpoint_last_forward_slash(&mut server); - if server.name.is_empty() { - server.name = "Coco Cloud".to_string(); - } - - // Save the new server to the cache - save_server(&server); - - // Register the server to the search source - try_register_server_to_search_source(app_handle.clone(), &server).await; - - // Persist the servers to the store - persist_servers(&app_handle) - .await - .expect("Failed to persist Coco servers."); - - dbg!(format!("Successfully registered server: {:?}", &endpoint)); - Ok(server) - } - Err(e) => Err(format!("Failed to deserialize the response: {}", e)), - } - } else { - Err("Received empty response body.".to_string()) - } - } else { - Err("Could not determine the content length.".to_string()) - } - } else { - Err(format!("Request failed with status: {}", response.status())) + if server.id.is_empty() { + server.id = pizza_common::utils::uuid::Uuid::new().to_string(); } + + if server.name.is_empty() { + server.name = "Coco Server".to_string(); + } + + save_server(&server); + try_register_server_to_search_source(app_handle.clone(), &server).await; + + persist_servers(&app_handle) + .await + .map_err(|e| format!("Failed to persist Coco servers: {}", e))?; + + dbg!(format!("Successfully registered server: {:?}", &endpoint)); + Ok(server) } #[tauri::command] diff --git a/src-tauri/src/server/transcription.rs b/src-tauri/src/server/transcription.rs index e5b53652..715039f9 100644 --- a/src-tauri/src/server/transcription.rs +++ b/src-tauri/src/server/transcription.rs @@ -1,3 +1,4 @@ +use crate::common::http::get_response_body_text; use crate::server::http_client::HttpClient; use serde::{Deserialize, Serialize}; use serde_json::Value as JsonValue; @@ -19,23 +20,24 @@ pub async fn transcription( query_params.insert("type".to_string(), JsonValue::String(audio_type)); query_params.insert("content".to_string(), JsonValue::String(audio_content)); + // Send the HTTP POST request let response = HttpClient::post( &server_id, "/services/audio/transcription", Some(query_params), None, ) - .await?; + .await + .map_err(|e| format!("Error sending transcription request: {}", e))?; - if response.status().is_success() { - response - .json::() - .await - .map_err(|e| e.to_string()) - } else { - Err(format!( - "Transcription failed with status: {}", - response.status() - )) - } + // Use get_response_body_text to extract the response body as text + let response_body = get_response_body_text(response) + .await + .map_err(|e| format!("Failed to read response body: {}", e))?; + + // Deserialize the response body into TranscriptionResponse + let transcription_response: TranscriptionResponse = serde_json::from_str(&response_body) + .map_err(|e| format!("Failed to parse transcription response: {}", e))?; + + Ok(transcription_response) }