2024-12-22 20:56:52 -03:00
|
|
|
use async_trait::async_trait;
|
2024-12-23 00:20:59 -03:00
|
|
|
|
2024-12-22 20:56:52 -03:00
|
|
|
use gb_core::{Result, Error};
|
2024-12-23 00:20:59 -03:00
|
|
|
use redis::{Client, AsyncCommands};
|
2024-12-22 20:56:52 -03:00
|
|
|
use serde::{de::DeserializeOwned, Serialize};
|
|
|
|
use std::sync::Arc;
|
2024-12-23 00:20:59 -03:00
|
|
|
use tracing::instrument;
|
2024-12-22 20:56:52 -03:00
|
|
|
|
|
|
|
pub struct RedisPubSub {
|
2024-12-23 00:20:59 -03:00
|
|
|
client: Arc<Client>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Clone for RedisPubSub {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
Self {
|
|
|
|
client: self.client.clone(),
|
|
|
|
}
|
|
|
|
}
|
2024-12-22 20:56:52 -03:00
|
|
|
}
|
|
|
|
|
|
|
|
impl RedisPubSub {
|
|
|
|
pub async fn new(url: &str) -> Result<Self> {
|
|
|
|
let client = Client::open(url)
|
2024-12-23 00:20:59 -03:00
|
|
|
.map_err(|e| Error::redis(e.to_string()))?;
|
2024-12-22 20:56:52 -03:00
|
|
|
|
2024-12-23 00:20:59 -03:00
|
|
|
// Test connection
|
|
|
|
client.get_async_connection()
|
2024-12-22 20:56:52 -03:00
|
|
|
.await
|
2024-12-23 00:20:59 -03:00
|
|
|
.map_err(|e| Error::redis(e.to_string()))?;
|
2024-12-22 20:56:52 -03:00
|
|
|
|
|
|
|
Ok(Self {
|
2024-12-23 00:20:59 -03:00
|
|
|
client: Arc::new(client),
|
2024-12-22 20:56:52 -03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-12-23 00:20:59 -03:00
|
|
|
#[instrument(skip(self, payload))]
|
|
|
|
pub async fn publish<T>(&self, channel: &str, payload: &T) -> Result<()>
|
2024-12-22 20:56:52 -03:00
|
|
|
where
|
2024-12-23 00:20:59 -03:00
|
|
|
T: Serialize + std::fmt::Debug,
|
2024-12-22 20:56:52 -03:00
|
|
|
{
|
2024-12-23 00:20:59 -03:00
|
|
|
let mut conn = self.client.get_async_connection()
|
2024-12-22 20:56:52 -03:00
|
|
|
.await
|
2024-12-23 00:20:59 -03:00
|
|
|
.map_err(|e| Error::redis(e.to_string()))?;
|
2024-12-22 20:56:52 -03:00
|
|
|
|
2024-12-23 00:20:59 -03:00
|
|
|
let payload = serde_json::to_string(payload)
|
|
|
|
.map_err(|e| Error::redis(e.to_string()))?;
|
2024-12-22 20:56:52 -03:00
|
|
|
|
2024-12-23 00:20:59 -03:00
|
|
|
conn.publish(channel, payload)
|
|
|
|
.await
|
|
|
|
.map_err(|e| Error::redis(e.to_string()))?;
|
2024-12-22 20:56:52 -03:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use rstest::*;
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use uuid::Uuid;
|
2024-12-23 00:20:59 -03:00
|
|
|
use std::time::Duration;
|
2024-12-22 20:56:52 -03:00
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
|
|
|
struct TestMessage {
|
|
|
|
id: Uuid,
|
|
|
|
content: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[fixture]
|
|
|
|
async fn redis_pubsub() -> RedisPubSub {
|
|
|
|
RedisPubSub::new("redis://localhost")
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[fixture]
|
|
|
|
fn test_message() -> TestMessage {
|
|
|
|
TestMessage {
|
|
|
|
id: Uuid::new_v4(),
|
|
|
|
content: "test message".to_string(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[rstest]
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_publish_subscribe(
|
|
|
|
redis_pubsub: RedisPubSub,
|
|
|
|
test_message: TestMessage,
|
|
|
|
) {
|
|
|
|
let channel = "test-channel";
|
|
|
|
|
|
|
|
let pubsub_clone = redis_pubsub.clone();
|
|
|
|
let test_message_clone = test_message.clone();
|
|
|
|
|
|
|
|
let handle = tokio::spawn(async move {
|
|
|
|
let handler = |msg: TestMessage| async move {
|
|
|
|
assert_eq!(msg, test_message_clone);
|
|
|
|
Ok(())
|
|
|
|
};
|
|
|
|
|
|
|
|
pubsub_clone.subscribe(&[channel], handler).await.unwrap();
|
|
|
|
});
|
|
|
|
|
|
|
|
tokio::time::sleep(Duration::from_millis(100)).await;
|
|
|
|
|
|
|
|
redis_pubsub.publish(channel, &test_message)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
tokio::time::sleep(Duration::from_secs(1)).await;
|
|
|
|
handle.abort();
|
|
|
|
}
|
|
|
|
}
|