More logging, delete, new convo
This commit is contained in:
@@ -65,7 +65,15 @@ impl std::error::Error for Error {
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
match self {
|
||||
Error::ClientError(message) => write!(f, "{}", message),
|
||||
Error::HTTPError(err) => write!(f, "HTTP transport error: {}", err),
|
||||
Error::SerdeError(err) => write!(f, "JSON error: {}", err),
|
||||
Error::DecodeError(message) => write!(f, "Decode error: {}", message),
|
||||
Error::PongError(err) => write!(f, "WebSocket error: {}", err),
|
||||
Error::URLError => write!(f, "Invalid URL"),
|
||||
Error::Unauthorized => write!(f, "Unauthorized"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,6 +292,17 @@ impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_conversation(
|
||||
&mut self,
|
||||
conversation_id: &ConversationID,
|
||||
) -> Result<(), Self::Error> {
|
||||
// SERVER JANK: This should be DELETE or POST, but it's GET for some reason.
|
||||
let endpoint = format!("delete?guid={}", conversation_id);
|
||||
self.response_with_body_retry(&endpoint, Method::GET, Body::empty, true)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_messages(
|
||||
&mut self,
|
||||
conversation_id: &ConversationID,
|
||||
@@ -313,6 +332,25 @@ impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
|
||||
&mut self,
|
||||
outgoing_message: &OutgoingMessage,
|
||||
) -> Result<SendMessageResponse, Self::Error> {
|
||||
match &outgoing_message.target {
|
||||
OutgoingMessageTarget::Conversation(conversation_id) => {
|
||||
log::debug!(
|
||||
"Sending message to conversation {} (body_length={}, attachment_count={})",
|
||||
conversation_id,
|
||||
outgoing_message.text.len(),
|
||||
outgoing_message.file_transfer_guids.len()
|
||||
);
|
||||
}
|
||||
OutgoingMessageTarget::Handles(handle_ids) => {
|
||||
log::debug!(
|
||||
"Sending message to resolved handles {:?} (body_length={}, attachment_count={})",
|
||||
handle_ids,
|
||||
outgoing_message.text.len(),
|
||||
outgoing_message.file_transfer_guids.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let message: SendMessageResponse = self
|
||||
.deserialized_response_with_body("sendMessage", Method::POST, || {
|
||||
Self::send_message_request_body(outgoing_message)
|
||||
@@ -326,6 +364,7 @@ impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
|
||||
&mut self,
|
||||
handle_id: &str,
|
||||
) -> Result<ResolveHandleResponse, Self::Error> {
|
||||
log::debug!("Resolving handle {}", handle_id);
|
||||
let endpoint = format!("resolveHandle?id={}", urlencoding::encode(handle_id));
|
||||
let response: ResolveHandleResponse =
|
||||
self.deserialized_response(&endpoint, Method::GET).await?;
|
||||
@@ -542,6 +581,18 @@ impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
|
||||
}
|
||||
}
|
||||
|
||||
fn log_transport_error(method: &Method, target: &str, err: &hyper::Error) {
|
||||
log::error!("HTTP transport error for {} {}: {}", method, target, err);
|
||||
|
||||
if format!("{:?}", err).contains("IncompleteMessage") {
|
||||
log::error!(
|
||||
"The server closed the connection before a complete response was received for {} {}.",
|
||||
method,
|
||||
target
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async fn deserialized_response<T: DeserializeOwned>(
|
||||
&mut self,
|
||||
endpoint: &str,
|
||||
@@ -575,15 +626,26 @@ impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
|
||||
T: DeserializeOwned,
|
||||
{
|
||||
let response = self
|
||||
.response_with_body_retry(endpoint, method, body_fn, retry_auth)
|
||||
.response_with_body_retry(endpoint, method.clone(), body_fn, retry_auth)
|
||||
.await?;
|
||||
|
||||
// Read and parse response body
|
||||
let body = hyper::body::to_bytes(response.into_body()).await?;
|
||||
let body = match hyper::body::to_bytes(response.into_body()).await {
|
||||
Ok(body) => body,
|
||||
Err(err) => {
|
||||
Self::log_transport_error(&method, endpoint, &err);
|
||||
return Err(Error::HTTPError(err));
|
||||
}
|
||||
};
|
||||
let parsed: T = match serde_json::from_slice(&body) {
|
||||
Ok(result) => Ok(result),
|
||||
Err(json_err) => {
|
||||
log::error!("Error deserializing JSON: {:?}", json_err);
|
||||
log::error!(
|
||||
"Error deserializing JSON for {} {}: {:?}",
|
||||
method,
|
||||
endpoint,
|
||||
json_err
|
||||
);
|
||||
log::error!("Body: {:?}", String::from_utf8_lossy(&body));
|
||||
|
||||
// If JSON deserialization fails, try to interpret it as plain text
|
||||
@@ -606,7 +668,8 @@ impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
|
||||
use hyper::StatusCode;
|
||||
|
||||
let uri = self.uri_for_endpoint(endpoint, None)?;
|
||||
log::debug!("Requesting {:?} {:?}", method, uri);
|
||||
let uri_string = uri.to_string();
|
||||
log::debug!("Requesting {} {}", method, uri_string);
|
||||
|
||||
let mut build_request = |auth: &Option<String>| {
|
||||
let body = body_fn();
|
||||
@@ -620,13 +683,24 @@ impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
|
||||
|
||||
log::trace!("Obtaining token from auth store");
|
||||
let token = self.auth_store.get_token().await;
|
||||
log::trace!("Token: {:?}", token);
|
||||
log::trace!("Token present: {}", token.is_some());
|
||||
|
||||
let request = build_request(&token);
|
||||
log::trace!("Request: {:?}. Sending request...", request);
|
||||
log::trace!(
|
||||
"Sending request: method={} uri={} authenticated={}",
|
||||
method,
|
||||
uri_string,
|
||||
token.is_some()
|
||||
);
|
||||
|
||||
let mut response = self.client.request(request).await?;
|
||||
log::debug!("-> Response: {:}", response.status());
|
||||
let mut response = match self.client.request(request).await {
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
Self::log_transport_error(&method, &uri_string, &err);
|
||||
return Err(Error::HTTPError(err));
|
||||
}
|
||||
};
|
||||
log::debug!("-> Response: {}", response.status());
|
||||
|
||||
match response.status() {
|
||||
StatusCode::OK => { /* cool */ }
|
||||
@@ -645,7 +719,19 @@ impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
|
||||
let new_token = self.authenticate(credentials.clone()).await?;
|
||||
|
||||
let request = build_request(&Some(new_token.to_string()));
|
||||
response = self.client.request(request).await?;
|
||||
log::trace!(
|
||||
"Retrying request after authentication: method={} uri={} authenticated=true",
|
||||
method,
|
||||
uri_string
|
||||
);
|
||||
response = match self.client.request(request).await {
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
Self::log_transport_error(&method, &uri_string, &err);
|
||||
return Err(Error::HTTPError(err));
|
||||
}
|
||||
};
|
||||
log::debug!("-> Retry response: {}", response.status());
|
||||
} else {
|
||||
return Err(Error::ClientError(
|
||||
"Unauthorized, no credentials provided".into(),
|
||||
|
||||
@@ -79,6 +79,12 @@ pub trait APIInterface {
|
||||
conversation_id: &ConversationID,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
// (GET) /delete
|
||||
async fn delete_conversation(
|
||||
&mut self,
|
||||
conversation_id: &ConversationID,
|
||||
) -> Result<(), Self::Error>;
|
||||
|
||||
// (WS) /updates
|
||||
async fn open_event_socket(
|
||||
&mut self,
|
||||
|
||||
@@ -52,4 +52,20 @@ pub mod api_interface {
|
||||
assert_eq!(sent.message.text, "hello");
|
||||
assert_eq!(sent.conversation_id, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_conversation() {
|
||||
let mut client = TestClient::new();
|
||||
|
||||
let test_convo = Conversation::builder()
|
||||
.display_name("Delete Me")
|
||||
.build();
|
||||
|
||||
client.conversations.push(test_convo.clone());
|
||||
|
||||
client.delete_conversation(&test_convo.guid).await.unwrap();
|
||||
|
||||
let conversations = client.get_conversations().await.unwrap();
|
||||
assert!(conversations.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,4 +187,19 @@ impl APIInterface for TestClient {
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_conversation(
|
||||
&mut self,
|
||||
conversation_id: &ConversationID,
|
||||
) -> Result<(), Self::Error> {
|
||||
let previous_len = self.conversations.len();
|
||||
self.conversations.retain(|c| &c.guid != conversation_id);
|
||||
self.messages.remove(conversation_id);
|
||||
|
||||
if self.conversations.len() == previous_len {
|
||||
return Err(TestError::ConversationNotFound);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user