ray-dashboard-sdk 0.4.0

A simple Rust client for the Ray Dashboard REST API.
Documentation
use std::sync::Arc;

use tracing::info;

use crate::schema::common::RayVersionResponse;

mod jobs;
mod packages;

pub use jobs::JobsClient;
pub use packages::PackagesClient;

/// Shared HTTP state used by the top-level client and all sub-API clients.
/// Cheap to reference via `Arc` so sub-clients can be cloned and stored freely.
#[derive(Debug)]
pub(crate) struct HttpClient {
    base_url: url::Url,
    client: reqwest::Client,
}

impl HttpClient {
    /// Build base request with common headers.
    /// Ray dashboard server requires User-Agent header to be set or else 500s.
    pub(crate) fn request(
        &self,
        method: reqwest::Method,
        path: &str,
    ) -> crate::Result<reqwest::RequestBuilder> {
        let url = self.base_url.join(path)?;
        info!("Building request: {} {}", method, url);
        let request = self
            .client
            .request(method, url)
            .header("User-Agent", crate::utils::constants::SDK_USER_AGENT);
        Ok(request)
    }
}

/// Client for the Ray Dashboard REST API.
///
/// Individual Dashboard APIs are exposed through accessor methods returning
/// lightweight sub-clients that share the underlying HTTP connection, e.g.
/// `client.jobs().list()` or `client.packages().exists(uri)`.
#[derive(Debug, Clone)]
pub struct RayDashboardClient {
    http: Arc<HttpClient>,
}

impl RayDashboardClient {
    pub fn new(base_url: &str) -> crate::Result<Self> {
        let client = reqwest::Client::builder().build()?;
        Self::new_with_client(base_url, client)
    }

    pub fn new_with_client(base_url: &str, client: reqwest::Client) -> crate::Result<Self> {
        let base_url = url::Url::parse(base_url)?;
        Ok(Self {
            http: Arc::new(HttpClient { base_url, client }),
        })
    }

    /// Client for the Job Submission API.
    pub fn jobs(&self) -> JobsClient {
        JobsClient::new(self.http.clone())
    }

    /// Client for the Packages API.
    pub fn packages(&self) -> PackagesClient {
        PackagesClient::new(self.http.clone())
    }

    pub async fn ping(&self) -> crate::Result<()> {
        self.get_version().await?;
        Ok(())
    }

    pub async fn get_version(&self) -> crate::Result<RayVersionResponse> {
        let path = "/api/version";
        let request = self.http.request(reqwest::Method::GET, path)?;
        let response = request.send().await?.error_for_status()?;
        Ok(response.json::<RayVersionResponse>().await?)
    }
}