client: implements send_message
This commit is contained in:
@@ -20,7 +20,17 @@ use tokio_tungstenite::connect_async;
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream};
|
||||
|
||||
use crate::{
|
||||
model::{Conversation, ConversationID, JwtToken, Message, MessageID, UpdateItem, Event},
|
||||
model::{
|
||||
Conversation,
|
||||
ConversationID,
|
||||
JwtToken,
|
||||
Message,
|
||||
MessageID,
|
||||
UpdateItem,
|
||||
Event,
|
||||
OutgoingMessage,
|
||||
},
|
||||
|
||||
APIInterface
|
||||
};
|
||||
|
||||
@@ -215,6 +225,19 @@ impl<K: AuthenticationStore + Send + Sync> APIInterface for HTTPAPIClient<K> {
|
||||
Ok(messages)
|
||||
}
|
||||
|
||||
async fn send_message(
|
||||
&mut self,
|
||||
outgoing_message: OutgoingMessage,
|
||||
) -> Result<Message, Self::Error> {
|
||||
let message: Message = self.request_with_body(
|
||||
"sendMessage",
|
||||
Method::POST,
|
||||
|| serde_json::to_string(&outgoing_message).unwrap().into()
|
||||
).await?;
|
||||
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
async fn open_event_socket(&mut self) -> Result<WebsocketEventSocket, Self::Error> {
|
||||
use tungstenite::http::StatusCode;
|
||||
use tungstenite::handshake::client::Request as TungsteniteRequest;
|
||||
@@ -285,24 +308,23 @@ impl<K: AuthenticationStore + Send + Sync> HTTPAPIClient<K> {
|
||||
}
|
||||
|
||||
async fn request<T: DeserializeOwned>(&mut self, endpoint: &str, method: Method) -> Result<T, Error> {
|
||||
self.request_with_body(endpoint, method, || { Body::empty() }).await
|
||||
self.request_with_body(endpoint, method, Body::empty).await
|
||||
}
|
||||
|
||||
async fn request_with_body<T, B>(&mut self, endpoint: &str, method: Method, body_fn: B) -> Result<T, Error>
|
||||
where T: DeserializeOwned, B: Fn() -> Body
|
||||
async fn request_with_body<T>(&mut self, endpoint: &str, method: Method, body_fn: impl Fn() -> Body) -> Result<T, Error>
|
||||
where T: DeserializeOwned
|
||||
{
|
||||
self.request_with_body_retry(endpoint, method, body_fn, true).await
|
||||
}
|
||||
|
||||
async fn request_with_body_retry<T, B>(
|
||||
async fn request_with_body_retry<T>(
|
||||
&mut self,
|
||||
endpoint: &str,
|
||||
method: Method,
|
||||
body_fn: B,
|
||||
body_fn: impl Fn() -> Body,
|
||||
retry_auth: bool) -> Result<T, Error>
|
||||
where
|
||||
T: DeserializeOwned,
|
||||
B: Fn() -> Body
|
||||
{
|
||||
use hyper::StatusCode;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use async_trait::async_trait;
|
||||
pub use crate::model::{
|
||||
Conversation, Message, ConversationID, MessageID,
|
||||
Conversation, Message, ConversationID, MessageID, OutgoingMessage,
|
||||
};
|
||||
|
||||
pub mod auth;
|
||||
@@ -35,6 +35,12 @@ pub trait APIInterface {
|
||||
after: Option<MessageID>,
|
||||
) -> Result<Vec<Message>, Self::Error>;
|
||||
|
||||
// (POST) /sendMessage
|
||||
async fn send_message(
|
||||
&mut self,
|
||||
outgoing_message: OutgoingMessage,
|
||||
) -> Result<Message, Self::Error>;
|
||||
|
||||
// (POST) /authenticate
|
||||
async fn authenticate(&mut self, credentials: Credentials) -> Result<JwtToken, Self::Error>;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub mod conversation;
|
||||
pub mod event;
|
||||
pub mod message;
|
||||
pub mod outgoing_message;
|
||||
pub mod update;
|
||||
|
||||
pub use conversation::Conversation;
|
||||
@@ -9,6 +10,9 @@ pub use conversation::ConversationID;
|
||||
pub use message::Message;
|
||||
pub use message::MessageID;
|
||||
|
||||
pub use outgoing_message::OutgoingMessage;
|
||||
pub use outgoing_message::OutgoingMessageBuilder;
|
||||
|
||||
pub use update::UpdateItem;
|
||||
|
||||
pub use event::Event;
|
||||
|
||||
56
kordophone/src/model/outgoing_message.rs
Normal file
56
kordophone/src/model/outgoing_message.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use serde::Serialize;
|
||||
use super::conversation::ConversationID;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct OutgoingMessage {
|
||||
#[serde(rename = "body")]
|
||||
pub text: String,
|
||||
|
||||
#[serde(rename = "guid")]
|
||||
pub conversation_id: ConversationID,
|
||||
|
||||
#[serde(rename = "fileTransferGUIDs")]
|
||||
pub file_transfer_guids: Vec<String>,
|
||||
}
|
||||
|
||||
impl OutgoingMessage {
|
||||
pub fn builder() -> OutgoingMessageBuilder {
|
||||
OutgoingMessageBuilder::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct OutgoingMessageBuilder {
|
||||
text: Option<String>,
|
||||
conversation_id: Option<ConversationID>,
|
||||
file_transfer_guids: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl OutgoingMessageBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn text(mut self, text: String) -> Self {
|
||||
self.text = Some(text);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn conversation_id(mut self, conversation_id: ConversationID) -> Self {
|
||||
self.conversation_id = Some(conversation_id);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn file_transfer_guids(mut self, file_transfer_guids: Vec<String>) -> Self {
|
||||
self.file_transfer_guids = Some(file_transfer_guids);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> OutgoingMessage {
|
||||
OutgoingMessage {
|
||||
text: self.text.unwrap(),
|
||||
conversation_id: self.conversation_id.unwrap(),
|
||||
file_transfer_guids: self.file_transfer_guids.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,13 @@
|
||||
use async_trait::async_trait;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub use crate::APIInterface;
|
||||
use crate::{
|
||||
api::http_client::Credentials,
|
||||
model::{Conversation, ConversationID, JwtToken, Message, MessageID, UpdateItem, Event},
|
||||
model::{Conversation, ConversationID, JwtToken, Message, MessageID, UpdateItem, Event, OutgoingMessage},
|
||||
api::event_socket::EventSocket,
|
||||
};
|
||||
|
||||
@@ -88,6 +91,20 @@ impl APIInterface for TestClient {
|
||||
Err(TestError::ConversationNotFound)
|
||||
}
|
||||
|
||||
async fn send_message(
|
||||
&mut self,
|
||||
outgoing_message: OutgoingMessage,
|
||||
) -> Result<Message, Self::Error> {
|
||||
let message = Message::builder()
|
||||
.guid(Uuid::new_v4().to_string())
|
||||
.text(outgoing_message.text)
|
||||
.date(OffsetDateTime::now_utc())
|
||||
.build();
|
||||
|
||||
self.messages.entry(outgoing_message.conversation_id).or_insert(vec![]).push(message.clone());
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
async fn open_event_socket(&mut self) -> Result<impl EventSocket, Self::Error> {
|
||||
Ok(TestEventSocket::new())
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ use anyhow::Result;
|
||||
use clap::Subcommand;
|
||||
use crate::printers::{ConversationPrinter, MessagePrinter};
|
||||
use kordophone::model::event::Event;
|
||||
use kordophone::model::outgoing_message::OutgoingMessage;
|
||||
|
||||
use futures_util::StreamExt;
|
||||
|
||||
@@ -47,6 +48,12 @@ pub enum Commands {
|
||||
|
||||
/// Prints all raw updates from the server.
|
||||
RawUpdates,
|
||||
|
||||
/// Sends a message to the server.
|
||||
SendMessage {
|
||||
conversation_id: String,
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl Commands {
|
||||
@@ -58,6 +65,7 @@ impl Commands {
|
||||
Commands::Messages { conversation_id } => client.print_messages(conversation_id).await,
|
||||
Commands::RawUpdates => client.print_raw_updates().await,
|
||||
Commands::Events => client.print_events().await,
|
||||
Commands::SendMessage { conversation_id, message } => client.send_message(conversation_id, message).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,6 +131,17 @@ impl ClientCli {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_message(&mut self, conversation_id: String, message: String) -> Result<()> {
|
||||
let outgoing_message = OutgoingMessage::builder()
|
||||
.conversation_id(conversation_id)
|
||||
.text(message)
|
||||
.build();
|
||||
|
||||
let message = self.api.send_message(outgoing_message).await?;
|
||||
println!("Message sent: {}", message.guid);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user