devui 0.1.1

A comprehensive development tools UI library
Documentation
use crate::services::kafka::cluster_manager::{ClientManager, ClusterManagerError};
use crate::services::kafka::config::Config;
use crate::services::kafka::models::{Broker, ClusterMetadata, Partition, Topic};
use rdkafka::consumer::{Consumer, StreamConsumer};
use rdkafka::error::KafkaError;
use rdkafka::message::ToBytes;
use rdkafka::producer::FutureRecord;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Duration;
use thiserror::Error;

#[derive(Clone)]
pub struct Service {
    pub cluster_manager: Arc<ClientManager>,
}

#[derive(Error, Debug)]
pub enum ServiceError {
    #[error("client manager error")]
    ClientManagerError(#[from] ClusterManagerError),
    #[error("Kafka error")]
    KafkaError(#[from] KafkaError),
}

impl Service {
    pub fn new(configs: Config) -> Result<Self, ServiceError> {
        let client_manager =
            ClientManager::new(configs).map_err(ServiceError::ClientManagerError)?;
        Ok(Self {
            cluster_manager: Arc::new(client_manager),
        })
    }

    pub fn get_clusters(&self) -> Vec<String> {
        self.cluster_manager.get_clusters()
    }

    pub fn metadata(&self, cluster_name: String) -> Result<ClusterMetadata, ServiceError> {
        let base_consumer = self
            .cluster_manager
            .get_cluster_base_consumer(&cluster_name)
            .map_err(ServiceError::ClientManagerError)?;

        let metadata = base_consumer
            .fetch_metadata(None, Duration::from_secs(5))
            .map_err(ServiceError::KafkaError)?;

        let mut brokers = HashMap::new();
        let mut topics = Vec::new();

        for broker in metadata.brokers() {
            brokers.insert(
                broker.id().to_string(),
                Broker {
                    id: broker.id(),
                    host: broker.host().to_string(),
                    port: broker.port(),
                },
            );
        }

        for topic in metadata.topics() {
            let mut partitions = Vec::new();
            for partition in topic.partitions() {
                partitions.push(Partition {
                    partition: partition.id(),
                    leader_id: partition.leader(),
                });
            }
            topics.push(Topic {
                name: topic.name().to_string(),
                partitions,
            })
        }

        let cluster_metadata = ClusterMetadata { brokers, topics };

        Ok(cluster_metadata)
    }

    pub async fn produce(
        &self,
        cluster_name: String,
        topic_name: String,
        key: impl ToBytes,
        payload: impl ToBytes,
    ) -> Result<(), ServiceError> {
        let producer = self
            .cluster_manager
            .get_cluster_producer(&cluster_name)
            .map_err(ServiceError::ClientManagerError)?;
        let delivery_status = producer
            .send(
                FutureRecord::to(&topic_name).payload(&payload).key(&key),
                Duration::from_secs(0),
            )
            .await;

        match delivery_status {
            Ok(delivery) => {
                tracing::info!("kafka message produced successfully {:?}", delivery);
                Ok(())
            }
            Err((e, _)) => Err(ServiceError::KafkaError(e)),
        }
    }

    pub async fn create_consumer(
        &self,
        cluster_name: String,
        topic_name: String,
    ) -> Result<StreamConsumer, ServiceError> {
        let consumer = self
            .cluster_manager
            .create_stream_consumer(&cluster_name)
            .map_err(ServiceError::ClientManagerError)?;

        consumer
            .subscribe(&[&topic_name])
            .map_err(ServiceError::KafkaError)?;

        Ok(consumer)
    }
}