cloud_api_client: Add a few helpers for response handling (#58346)
This centralizes some repeated logic across `cloud_api_client`, to map generic request failures (`ConnectionFailed`, 401s) to errors and read response bodies, handling failures there too. Closes CLO-759. Release Notes: - N/A
This commit is contained in:
1 parent
2991e00e5e
commit
de1effbd56
3 files changed
+51
-119
No files matched your search
Generated
+1
@@ -3194,6 +3194,7 @@ dependencies = [
|
||||
"gpui_tokio",
|
||||
"http_client",
|
||||
"parking_lot",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
"yawc",
|
||||
|
||||
@@ -19,6 +19,7 @@ gpui.workspace = true
|
||||
gpui_tokio.workspace = true
|
||||
http_client.workspace = true
|
||||
parking_lot.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
async-lock.workspace = true
|
||||
thiserror.workspace = true
|
||||
|
||||
@@ -11,9 +11,10 @@ use gpui::{App, Task};
|
||||
use gpui_tokio::Tokio;
|
||||
use http_client::http::request;
|
||||
use http_client::{
|
||||
AsyncBody, HttpClientWithUrl, HttpRequestExt, Json, Method, Request, StatusCode,
|
||||
AsyncBody, HttpClientWithUrl, HttpRequestExt, Json, Method, Request, Response, StatusCode,
|
||||
};
|
||||
use parking_lot::RwLock;
|
||||
use serde::de::DeserializeOwned;
|
||||
use thiserror::Error;
|
||||
use yawc::WebSocket;
|
||||
|
||||
@@ -108,7 +109,6 @@ impl CloudApiClient {
|
||||
&self,
|
||||
system_id: Option<String>,
|
||||
) -> Result<GetAuthenticatedUserResponse, ClientApiError> {
|
||||
let host = self.cloud_host();
|
||||
let request_builder = Request::builder()
|
||||
.method(Method::GET)
|
||||
.uri(
|
||||
@@ -122,37 +122,7 @@ impl CloudApiClient {
|
||||
});
|
||||
|
||||
let request = self.build_request(request_builder, AsyncBody::default())?;
|
||||
|
||||
let mut response = self.http_client.send(request).await.map_err(|source| {
|
||||
ClientApiError::ConnectionFailed {
|
||||
host: host.clone(),
|
||||
source,
|
||||
}
|
||||
})?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
if response.status() == StatusCode::UNAUTHORIZED {
|
||||
return Err(ClientApiError::Unauthorized);
|
||||
}
|
||||
|
||||
let mut body = String::new();
|
||||
response.body_mut().read_to_string(&mut body).await.ok();
|
||||
|
||||
return Err(ClientApiError::ServerError {
|
||||
host,
|
||||
status: response.status(),
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
let mut body = String::new();
|
||||
response
|
||||
.body_mut()
|
||||
.read_to_string(&mut body)
|
||||
.await
|
||||
.map_err(|e| ClientApiError::InvalidResponse(e.into()))?;
|
||||
|
||||
serde_json::from_str(&body).map_err(|e| ClientApiError::InvalidResponse(e.into()))
|
||||
self.send_authenticated_json_request(request).await
|
||||
}
|
||||
|
||||
pub fn connect(&self, cx: &App) -> Result<Task<Result<Connection>>> {
|
||||
@@ -184,12 +154,11 @@ impl CloudApiClient {
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn create_llm_token(
|
||||
async fn create_llm_token(
|
||||
&self,
|
||||
system_id: Option<String>,
|
||||
organization_id: Option<OrganizationId>,
|
||||
) -> Result<CreateLlmTokenResponse, ClientApiError> {
|
||||
let host = self.cloud_host();
|
||||
let request_builder = Request::builder()
|
||||
.method(Method::POST)
|
||||
.uri(
|
||||
@@ -206,37 +175,7 @@ impl CloudApiClient {
|
||||
request_builder,
|
||||
Json(CreateLlmTokenBody { organization_id }),
|
||||
)?;
|
||||
|
||||
let mut response = self.http_client.send(request).await.map_err(|source| {
|
||||
ClientApiError::ConnectionFailed {
|
||||
host: host.clone(),
|
||||
source,
|
||||
}
|
||||
})?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
if response.status() == StatusCode::UNAUTHORIZED {
|
||||
return Err(ClientApiError::Unauthorized);
|
||||
}
|
||||
|
||||
let mut body = String::new();
|
||||
response.body_mut().read_to_string(&mut body).await.ok();
|
||||
|
||||
return Err(ClientApiError::ServerError {
|
||||
host,
|
||||
status: response.status(),
|
||||
body,
|
||||
});
|
||||
}
|
||||
|
||||
let mut body = String::new();
|
||||
response
|
||||
.body_mut()
|
||||
.read_to_string(&mut body)
|
||||
.await
|
||||
.map_err(|e| ClientApiError::InvalidResponse(e.into()))?;
|
||||
|
||||
serde_json::from_str(&body).map_err(|e| ClientApiError::InvalidResponse(e.into()))
|
||||
self.send_authenticated_json_request(request).await
|
||||
}
|
||||
|
||||
pub async fn update_system_settings(
|
||||
@@ -244,7 +183,6 @@ impl CloudApiClient {
|
||||
system_id: String,
|
||||
body: UpdateSystemSettingsBody,
|
||||
) -> Result<SystemSettings, ClientApiError> {
|
||||
let host = self.cloud_host();
|
||||
let request_builder = Request::builder()
|
||||
.method(Method::PATCH)
|
||||
.uri(
|
||||
@@ -256,7 +194,22 @@ impl CloudApiClient {
|
||||
.header(ZED_SYSTEM_ID_HEADER_NAME, system_id);
|
||||
|
||||
let request = self.build_request(request_builder, Json(body))?;
|
||||
self.send_authenticated_json_request(request).await
|
||||
}
|
||||
|
||||
async fn send_authenticated_json_request<T: DeserializeOwned>(
|
||||
&self,
|
||||
request: Request<AsyncBody>,
|
||||
) -> Result<T, ClientApiError> {
|
||||
let mut response = self.send_authenticated_request(request).await?;
|
||||
Self::read_response_json(&mut response).await
|
||||
}
|
||||
|
||||
async fn send_authenticated_request(
|
||||
&self,
|
||||
request: Request<AsyncBody>,
|
||||
) -> Result<Response<AsyncBody>, ClientApiError> {
|
||||
let host = self.cloud_host();
|
||||
let mut response = self.http_client.send(request).await.map_err(|source| {
|
||||
ClientApiError::ConnectionFailed {
|
||||
host: host.clone(),
|
||||
@@ -264,29 +217,39 @@ impl CloudApiClient {
|
||||
}
|
||||
})?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
if response.status() == StatusCode::UNAUTHORIZED {
|
||||
return Err(ClientApiError::Unauthorized);
|
||||
}
|
||||
|
||||
let mut body = String::new();
|
||||
response.body_mut().read_to_string(&mut body).await.ok();
|
||||
|
||||
return Err(ClientApiError::ServerError {
|
||||
host,
|
||||
status: response.status(),
|
||||
body,
|
||||
});
|
||||
let status = response.status();
|
||||
if status.is_success() {
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
if status == StatusCode::UNAUTHORIZED {
|
||||
return Err(ClientApiError::Unauthorized);
|
||||
}
|
||||
|
||||
let body = match Self::read_response_body(&mut response).await {
|
||||
Ok(body) => body,
|
||||
Err(error) => format!("failed to read response body: {error}"),
|
||||
};
|
||||
Err(ClientApiError::ServerError { host, status, body })
|
||||
}
|
||||
|
||||
async fn read_response_json<T: DeserializeOwned>(
|
||||
response: &mut Response<AsyncBody>,
|
||||
) -> Result<T, ClientApiError> {
|
||||
let body = Self::read_response_body(response).await?;
|
||||
serde_json::from_str(&body).map_err(|error| ClientApiError::InvalidResponse(error.into()))
|
||||
}
|
||||
|
||||
async fn read_response_body(
|
||||
response: &mut Response<AsyncBody>,
|
||||
) -> Result<String, ClientApiError> {
|
||||
let mut body = String::new();
|
||||
response
|
||||
.body_mut()
|
||||
.read_to_string(&mut body)
|
||||
.await
|
||||
.map_err(|e| ClientApiError::InvalidResponse(e.into()))?;
|
||||
|
||||
serde_json::from_str(&body).map_err(|e| ClientApiError::InvalidResponse(e.into()))
|
||||
.map_err(|error| ClientApiError::InvalidResponse(error.into()))?;
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
pub async fn validate_credentials(&self, user_id: u32, access_token: &str) -> Result<bool> {
|
||||
@@ -331,18 +294,7 @@ impl CloudApiClient {
|
||||
AsyncBody::from(serde_json::to_string(&body)?),
|
||||
)?;
|
||||
|
||||
let mut response = self.http_client.send(request).await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let mut body = String::new();
|
||||
response.body_mut().read_to_string(&mut body).await?;
|
||||
|
||||
anyhow::bail!(
|
||||
"Failed to submit agent feedback.\nStatus: {:?}\nBody: {body}",
|
||||
response.status()
|
||||
)
|
||||
}
|
||||
|
||||
self.send_authenticated_request(request).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -359,18 +311,7 @@ impl CloudApiClient {
|
||||
AsyncBody::from(serde_json::to_string(&body)?),
|
||||
)?;
|
||||
|
||||
let mut response = self.http_client.send(request).await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let mut body = String::new();
|
||||
response.body_mut().read_to_string(&mut body).await?;
|
||||
|
||||
anyhow::bail!(
|
||||
"Failed to submit agent feedback comments.\nStatus: {:?}\nBody: {body}",
|
||||
response.status()
|
||||
)
|
||||
}
|
||||
|
||||
self.send_authenticated_request(request).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -387,18 +328,7 @@ impl CloudApiClient {
|
||||
AsyncBody::from(serde_json::to_string(&body)?),
|
||||
)?;
|
||||
|
||||
let mut response = self.http_client.send(request).await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
let mut body = String::new();
|
||||
response.body_mut().read_to_string(&mut body).await?;
|
||||
|
||||
anyhow::bail!(
|
||||
"Failed to submit edit prediction feedback.\nStatus: {:?}\nBody: {body}",
|
||||
response.status()
|
||||
)
|
||||
}
|
||||
|
||||
self.send_authenticated_request(request).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user