Skip to content

Commit

Permalink
feat: chat ffi find by message (#6354)
Browse files Browse the repository at this point in the history
Description
---
Add functionality for a chat client to locate a message by id.

Motivation and Context
---
Making it simpler for clients to do things like reference an older
message in history via reply.

How Has This Been Tested?
---
Cucumber only. It's kind of a weird integration test, but it made the
most sense to test there.

Breaking Changes
---

- [x] None
- [ ] Requires data directory on base node to be deleted
- [ ] Requires hard fork
- [ ] Other - Please specify
  • Loading branch information
brianp committed May 28, 2024
1 parent ef387d7 commit 28c7659
Show file tree
Hide file tree
Showing 11 changed files with 223 additions and 64 deletions.
20 changes: 20 additions & 0 deletions base_layer/chat_ffi/chat.h
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,26 @@ struct Message *create_chat_message(struct TariAddress *receiver,
*/
void destroy_chat_message(struct Message *ptr);

/**
* Get a ptr to a message from a message_id
*
* ## Arguments
* `client` - The ChatClient pointer
* `message_id` - A pointer to a byte vector representing a message id
* `error_out` - Pointer to an int which will be modified
*
* ## Returns
* `*mut Message` - A pointer to a message
*
* # Safety
* The returned pointer to ```Message``` should be destroyed after use
* ```client``` should be destroyed after use
* ```message_id``` should be destroyed after use
*/
struct Message *get_chat_message(struct ChatClient *client,
struct ChatByteVector *message_id,
int *error_out);

/**
* Sends a message over a client
*
Expand Down
49 changes: 48 additions & 1 deletion base_layer/chat_ffi/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use tari_contacts::contacts_service::types::{Message, MessageBuilder, MessageMet
use tari_utilities::ByteArray;

use crate::{
byte_vector::{chat_byte_vector_create, ChatByteVector},
byte_vector::{chat_byte_vector_create, process_vector, ChatByteVector},
error::{InterfaceError, LibChatError},
ChatClient,
};
Expand Down Expand Up @@ -95,6 +95,53 @@ pub unsafe extern "C" fn destroy_chat_message(ptr: *mut Message) {
}
}

/// Get a ptr to a message from a message_id
///
/// ## Arguments
/// `client` - The ChatClient pointer
/// `message_id` - A pointer to a byte vector representing a message id
/// `error_out` - Pointer to an int which will be modified
///
/// ## Returns
/// `*mut Message` - A pointer to a message
///
/// # Safety
/// The returned pointer to ```Message``` should be destroyed after use
/// ```client``` should be destroyed after use
/// ```message_id``` should be destroyed after use
#[no_mangle]
pub unsafe extern "C" fn get_chat_message(
client: *mut ChatClient,
message_id: *mut ChatByteVector,
error_out: *mut c_int,
) -> *mut Message {
let mut error = 0;
ptr::swap(error_out, &mut error as *mut c_int);

if client.is_null() {
error = LibChatError::from(InterfaceError::NullError("client".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
}

if message_id.is_null() {
error = LibChatError::from(InterfaceError::NullError("message_id".to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
}

let id = process_vector(message_id, error_out);

let result = (*client).runtime.block_on((*client).client.get_message(&id));

match result {
Ok(message) => Box::into_raw(Box::new(message)),
Err(e) => {
error = LibChatError::from(InterfaceError::ContactServiceError(e.to_string())).code;
ptr::swap(error_out, &mut error as *mut c_int);
ptr::null_mut()
},
}
}

/// Sends a message over a client
///
/// ## Arguments
Expand Down
3 changes: 2 additions & 1 deletion base_layer/common_types/src/wallet_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#[cfg(feature = "ledger")]
use std::convert::TryFrom;
use std::{
convert::TryFrom,
fmt,
fmt::{Display, Formatter},
};
Expand Down
66 changes: 38 additions & 28 deletions base_layer/contacts/src/chat_client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ pub trait ChatClient {
async fn check_online_status(&self, address: &TariAddress) -> Result<ContactOnlineStatus, Error>;
fn create_message(&self, receiver: &TariAddress, message: String) -> Message;
async fn get_messages(&self, sender: &TariAddress, limit: u64, page: u64) -> Result<Vec<Message>, Error>;
async fn get_message(&self, id: &[u8]) -> Result<Message, Error>;
async fn send_message(&self, message: Message) -> Result<(), Error>;
async fn send_read_receipt(&self, message: Message) -> Result<(), Error>;
async fn get_conversationalists(&self) -> Result<Vec<TariAddress>, Error>;
Expand Down Expand Up @@ -149,14 +150,6 @@ impl Client {

#[async_trait]
impl ChatClient for Client {
fn address(&self) -> TariAddress {
TariAddress::from_public_key(self.identity.public_key(), self.config.chat_client.network)
}

fn shutdown(&mut self) {
self.shutdown.trigger();
}

async fn add_contact(&self, address: &TariAddress) -> Result<(), Error> {
if let Some(mut contacts_service) = self.contacts.clone() {
contacts_service.upsert_contact(address.into()).await?;
Expand All @@ -165,6 +158,16 @@ impl ChatClient for Client {
Ok(())
}

fn add_metadata(&self, mut message: Message, key: String, data: String) -> Message {
let metadata = MessageMetadata {
key: key.into_bytes(),
data: data.into_bytes(),
};

message.push(metadata);
message
}

async fn check_online_status(&self, address: &TariAddress) -> Result<ContactOnlineStatus, Error> {
if let Some(mut contacts_service) = self.contacts.clone() {
let contact = contacts_service.get_contact(address.clone()).await?;
Expand All @@ -175,12 +178,8 @@ impl ChatClient for Client {
Ok(ContactOnlineStatus::Offline)
}

async fn send_message(&self, message: Message) -> Result<(), Error> {
if let Some(mut contacts_service) = self.contacts.clone() {
contacts_service.send_message(message).await?;
}

Ok(())
fn create_message(&self, receiver: &TariAddress, message: String) -> Message {
MessageBuilder::new().address(receiver.clone()).message(message).build()
}

async fn get_messages(&self, sender: &TariAddress, limit: u64, page: u64) -> Result<Vec<Message>, Error> {
Expand All @@ -192,6 +191,23 @@ impl ChatClient for Client {
Ok(messages)
}

async fn get_message(&self, message_id: &[u8]) -> Result<Message, Error> {
match self.contacts.clone() {
Some(mut contacts_service) => contacts_service.get_message(message_id).await.map_err(|e| e.into()),
None => Err(Error::InitializationError(
"ContactsServiceHandle unavailable".to_string(),
)),
}
}

async fn send_message(&self, message: Message) -> Result<(), Error> {
if let Some(mut contacts_service) = self.contacts.clone() {
contacts_service.send_message(message).await?;
}

Ok(())
}

async fn send_read_receipt(&self, message: Message) -> Result<(), Error> {
if let Some(mut contacts_service) = self.contacts.clone() {
contacts_service
Expand All @@ -202,20 +218,6 @@ impl ChatClient for Client {
Ok(())
}

fn create_message(&self, receiver: &TariAddress, message: String) -> Message {
MessageBuilder::new().address(receiver.clone()).message(message).build()
}

fn add_metadata(&self, mut message: Message, key: String, data: String) -> Message {
let metadata = MessageMetadata {
key: key.into_bytes(),
data: data.into_bytes(),
};

message.push(metadata);
message
}

async fn get_conversationalists(&self) -> Result<Vec<TariAddress>, Error> {
let mut addresses = vec![];
if let Some(mut contacts_service) = self.contacts.clone() {
Expand All @@ -224,6 +226,14 @@ impl ChatClient for Client {

Ok(addresses)
}

fn address(&self) -> TariAddress {
TariAddress::from_public_key(self.identity.public_key(), self.config.chat_client.network)
}

fn shutdown(&mut self) {
self.shutdown.trigger();
}
}

pub async fn wait_for_connectivity(comms: CommsNode) -> anyhow::Result<()> {
Expand Down
13 changes: 13 additions & 0 deletions base_layer/contacts/src/contacts_service/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ pub enum ContactsServiceRequest {
GetContactOnlineStatus(Contact),
SendMessage(TariAddress, Message),
GetMessages(TariAddress, i64, i64),
GetMessage(Vec<u8>),
SendReadConfirmation(TariAddress, Confirmation),
GetConversationalists,
}
Expand All @@ -150,6 +151,7 @@ pub enum ContactsServiceResponse {
Contacts(Vec<Contact>),
OnlineStatus(ContactOnlineStatus),
Messages(Vec<Message>),
Message(Message),
MessageSent,
ReadConfirmationSent,
Conversationalists(Vec<TariAddress>),
Expand Down Expand Up @@ -277,6 +279,17 @@ impl ContactsServiceHandle {
}
}

pub async fn get_message(&mut self, message_id: &[u8]) -> Result<Message, ContactsServiceError> {
match self
.request_response_service
.call(ContactsServiceRequest::GetMessage(message_id.to_vec()))
.await??
{
ContactsServiceResponse::Message(message) => Ok(message),
_ => Err(ContactsServiceError::UnexpectedApiResponse),
}
}

pub async fn send_message(&mut self, message: Message) -> Result<(), ContactsServiceError> {
match self
.request_response_service
Expand Down
8 changes: 6 additions & 2 deletions base_layer/contacts/src/contacts_service/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,8 @@ where T: ContactsBackend + 'static
request: ContactsServiceRequest,
) -> Result<ContactsServiceResponse, ContactsServiceError> {
match request {
ContactsServiceRequest::GetContact(pk) => {
let result = self.db.get_contact(pk.clone());
ContactsServiceRequest::GetContact(address) => {
let result = self.db.get_contact(address.clone());
if let Ok(ref contact) = result {
self.liveness.check_add_monitored_peer(contact.node_id.clone()).await?;
};
Expand Down Expand Up @@ -350,6 +350,10 @@ where T: ContactsBackend + 'static
let result = self.db.get_conversationlists();
Ok(result.map(ContactsServiceResponse::Conversationalists)?)
},
ContactsServiceRequest::GetMessage(message_id) => {
let result = self.db.get_message(message_id);
Ok(result.map(ContactsServiceResponse::Message)?)
},
}
}

Expand Down
5 changes: 5 additions & 0 deletions base_layer/contacts/src/contacts_service/storage/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,11 @@ where T: ContactsBackend + 'static
}
}

pub fn get_message(&self, message_id: Vec<u8>) -> Result<Message, ContactsServiceStorageError> {
let db_clone = self.db.clone();
fetch!(db_clone, message_id, Message)
}

pub fn save_message(&self, message: Message) -> Result<(), ContactsServiceStorageError> {
self.db
.write(WriteOperation::Insert(Box::new(DbValue::Message(Box::new(message)))))?;
Expand Down
Loading

0 comments on commit 28c7659

Please sign in to comment.