cipherstash-client 0.12.5

The official CipherStash SDK
Documentation
use std::path::PathBuf;

use url::Url;

use crate::credentials::service_credentials::ServiceAccessKeyCredentials;
use crate::logger_client::LoggerClient;

use super::cts_config::CtsConfig;
use super::errors::ConfigError;
use super::paths::resolve_config_dir;
use super::vars::CS_CLIENT_ACCESS_KEY;
use super::workspace::resolve_workspace;

pub const DEFAULT_LOG_ENDPOINT_URL: &str = "https://console-api.stashdata.net/api/log_ingestion";
pub const DEFAULT_LOG_AUDIENCE: &str = "https://console-api.stashdata.net";

#[derive(Default)]
pub struct LoggerConfigBuilder {
    config_dir: Option<String>,
    endpoint_url: Option<String>,
    audience: Option<String>,

    // auth
    cts_config: Option<CtsConfig>,
    access_key: Option<String>,
    workspace_id: Option<String>,
}

impl LoggerConfigBuilder {
    pub fn build(self) -> Result<LoggerConfig, ConfigError> {
        let config_dir = resolve_config_dir(self.config_dir.map(PathBuf::from))
            .map_err(|_| ConfigError::ValueNotSet("config_dir"))?;
        std::fs::create_dir_all(&config_dir).map_err(|e| {
            ConfigError::Io(
                e.to_string(),
                config_dir.to_str().unwrap_or("Unknown").to_string(),
            )
        })?;

        let workspace_id = resolve_workspace(&config_dir, self.workspace_id.as_deref())
            .ok_or(ConfigError::ValueNotSet("workspace_id"))?;

        let workspace_dir = config_dir.join(&workspace_id);
        std::fs::create_dir_all(workspace_dir).map_err(|e| {
            ConfigError::Io(
                e.to_string(),
                config_dir.to_str().unwrap_or("Unknown").to_string(),
            )
        })?;

        let endpoint_url = match &self.endpoint_url {
            Some(s) => s,
            None => DEFAULT_LOG_ENDPOINT_URL,
        };

        let endpoint_url = endpoint_url.parse()?;

        let audience = match &self.audience {
            Some(s) => s.to_owned(),
            None => DEFAULT_LOG_AUDIENCE.to_owned(),
        };

        let cts_config = if let Some(cts_config) = self.cts_config {
            cts_config
        } else {
            CtsConfig::builder().build()?
        };

        let Some(access_key) = self.access_key else {
            return Err(ConfigError::ValueNotSet("access_key"));
        };

        Ok(LoggerConfig {
            config_dir,
            endpoint_url,
            audience,
            cts_config,
            access_key,
            workspace_id,
        })
    }

    pub fn access_key(mut self, value: &str) -> Self {
        self.access_key = Some(value.to_string());
        self
    }

    pub fn endpoint_url(mut self, value: &str) -> Self {
        self.endpoint_url = Some(value.to_string());
        self
    }

    pub fn audience(mut self, value: &str) -> Self {
        self.audience = Some(value.to_string());
        self
    }

    pub fn config_dir(mut self, value: &str) -> Self {
        self.config_dir = Some(value.to_owned());
        self
    }

    pub fn cts_config(mut self, value: &CtsConfig) -> Self {
        self.cts_config = Some(value.to_owned());
        self
    }

    pub fn workspace_id(mut self, value: &str) -> Self {
        self.workspace_id = Some(value.to_string());
        self
    }

    pub fn with_env(mut self) -> Self {
        if let Ok(value) = std::env::var(CS_CLIENT_ACCESS_KEY) {
            self.access_key = Some(value);
        }

        self
    }
}

#[derive(Clone)]
pub struct LoggerConfig {
    config_dir: PathBuf,
    endpoint_url: Url,
    audience: String,

    // auth
    cts_config: CtsConfig,
    access_key: String,
    workspace_id: String,
}

impl LoggerConfig {
    pub fn builder() -> LoggerConfigBuilder {
        LoggerConfigBuilder::default()
    }

    pub fn audience(&self) -> String {
        self.audience.to_owned()
    }

    pub fn endpoint_url(&self) -> Url {
        self.endpoint_url.to_owned()
    }

    pub fn token_path(&self) -> PathBuf {
        self.config_dir
            .join(&self.workspace_id)
            .join("logger-access-key-auth.json")
    }

    pub fn credentials(&self) -> ServiceAccessKeyCredentials {
        ServiceAccessKeyCredentials::new(
            &self.token_path(),
            &self.access_key,
            &self.cts_config.base_url(),
            Some(&self.audience()),
        )
    }

    pub fn client(&self) -> LoggerClient<ServiceAccessKeyCredentials> {
        LoggerClient::new(&self.endpoint_url, self.credentials())
    }
}