surrealcs 0.4.4

The SurrealCS client code for SurrealDB
Documentation
//! Defines the interface for a transaction that is in progress
//!
//! # Notes
//! You will need a handle to the transaction actor to send messages to the server
//! this can be found at `transactions::interface::bridge::BridgeHandle`
use super::bridge::SendToTransactionClientActor;
use super::interface::{Committed, InProgress, RolledBack, Transaction};
use nanoservices_utils::errors::NanoServiceError;
use std::marker::PhantomData;
use surrealcs_kernel::messages::server::interface::{ServerMessage, ServerTransactionMessage};

impl Transaction<InProgress> {
	/// Sends a transaction operation to the server.
	///
	/// # Arguments
	/// * `message`: the transaction operation to send to the server
	///
	/// # Returns
	/// The response message from the server
	pub async fn send<T: SendToTransactionClientActor>(
		&mut self,
		message: ServerTransactionMessage,
	) -> Result<ServerTransactionMessage, NanoServiceError> {
		let message = ServerMessage::SendOperation(message);
		T::send_to_transaction_client_actor(self, message).await
	}

	/// Commits the transaction.
	///
	/// # Returns
	/// A tuple containing the response message from the server and the committed transaction
	pub async fn commit<T: SendToTransactionClientActor>(
		mut self,
	) -> Result<(ServerTransactionMessage, Transaction<Committed>), NanoServiceError> {
		let message = ServerMessage::CommitTransaction;
		let outcome = T::send_to_transaction_client_actor(&mut self, message).await?;
		let committed_transaction = Transaction {
			client_id: self.client_id,
			server_id: self.server_id,
			receiver: self.receiver,
			sender: self.sender.clone(),
			connection_id: self.connection_id,
			transaction_id: self.transaction_id,
			state: PhantomData,
		};
		Ok((outcome, committed_transaction))
	}

	/// Rolls back the transaction.
	///
	/// # Returns
	/// The rolled back transaction
	#[allow(dead_code)]
	pub async fn rollback<T: SendToTransactionClientActor>(
		mut self,
	) -> Result<Transaction<RolledBack>, NanoServiceError> {
		let message = ServerMessage::RollbackTransaction;
		// TODO => consider returning the rolled back transaction message
		let _ = T::send_to_transaction_client_actor(&mut self, message).await?;
		let rolled_back_transaction = Transaction {
			client_id: self.client_id,
			server_id: self.server_id,
			receiver: self.receiver,
			sender: self.sender.clone(),
			connection_id: self.connection_id,
			transaction_id: self.transaction_id,
			state: PhantomData,
		};
		Ok(rolled_back_transaction)
	}
}

#[cfg(test)]
mod tests {

	use super::*;
	use crate::generate_mock_handle;
	use std::future::Future;
	use surrealcs_kernel::messages::server::interface::ServerTransactionMessage;
	use surrealcs_kernel::messages::server::kv_operations::{MessageGet, ResponseGet};
	use tokio::sync::mpsc;

	#[tokio::test]
	async fn test_send() {
		generate_mock_handle! {
			struct MockHandle;
			match ServerMessage::SendOperation(message) => {
				match message {
					ServerTransactionMessage::Get(message) => {
						assert_eq!(message.key, b"keys".to_vec());
					}
					_ => {
						panic!("message not as expected: {:?}", message);
					}
				}
			}
			return Ok(ServerTransactionMessage::ResponseGet(
				ResponseGet { value: Some(b"value".to_vec()) }
			));
		}

		// define the channel for the actor
		let (tx, _rx) = mpsc::channel(32);

		// define the channel for the handle
		let (_h_tx, h_rx) = mpsc::channel(32);

		let mut transaction = Transaction::<InProgress> {
			client_id: 1,
			server_id: None,
			receiver: h_rx,
			sender: tx,
			connection_id: "connection_id".to_string(),
			transaction_id: "transaction_id".to_string(),
			state: PhantomData,
		};

		let message = ServerTransactionMessage::Get(MessageGet {
			key: b"keys".to_vec(),
			version: None,
		});

		let outcome = transaction.send::<MockHandle>(message).await.unwrap();

		let response = match outcome {
			ServerTransactionMessage::ResponseGet(response) => response,
			_ => panic!("Response not Get"),
		};

		assert_eq!(response.value, Some(b"value".to_vec()));
	}

	#[tokio::test]
	async fn test_commit() {
		generate_mock_handle! {
			struct MockHandle;
			match ServerMessage::CommitTransaction => {}
			return Ok(ServerTransactionMessage::EmptyResponse);
		}

		// define the channel for the actor
		let (tx, _rx) = mpsc::channel(32);

		// define the channel for the handle
		let (_h_tx, h_rx) = mpsc::channel(32);

		let transaction = Transaction::<InProgress> {
			client_id: 1,
			server_id: None,
			receiver: h_rx,
			sender: tx,
			connection_id: "connection_id".to_string(),
			transaction_id: "transaction_id".to_string(),
			state: PhantomData,
		};

		let _transaction = transaction.commit::<MockHandle>().await.unwrap();
	}

	#[tokio::test]
	async fn test_rollback() {
		generate_mock_handle! {
			struct MockHandle;
			match ServerMessage::RollbackTransaction => {}
			return Ok(ServerTransactionMessage::ResponseRolledBack(
				true
			));
		}
		// define the channel for the actor
		let (tx, _rx) = mpsc::channel(32);

		// define the channel for the handle
		let (_h_tx, h_rx) = mpsc::channel(32);

		let transaction = Transaction::<InProgress> {
			client_id: 1,
			server_id: None,
			receiver: h_rx,
			sender: tx,
			connection_id: "connection_id".to_string(),
			transaction_id: "transaction_id".to_string(),
			state: PhantomData,
		};
		let _transaction = transaction.rollback::<MockHandle>().await.unwrap();
	}
}