Codex Handbook
cloud-config/src/backend.rs 136 lines
use codex_backend_client::Client as BackendClient;use codex_backend_client::ConfigBundleResponse;use codex_backend_client::DeliveredTomlFragment;use codex_config::CloudConfigBundle;use codex_config::CloudConfigFragment;use codex_config::CloudConfigTomlBundle;use codex_config::CloudRequirementsFragment;use codex_config::CloudRequirementsTomlBundle;use codex_login::CodexAuth;use std::future::Future;#[derive(Clone, Copy, Debug, Eq, PartialEq)]pub(crate) enum RetryableFailureKind {    BackendClientInit,    Request { status_code: Option<u16> },}impl RetryableFailureKind {    pub(crate) fn status_code(self) -> Option<u16> {        match self {            Self::BackendClientInit => None,            Self::Request { status_code } => status_code,        }    }}#[derive(Clone, Debug, Eq, PartialEq)]pub(crate) enum BundleRequestError {    Retryable(RetryableFailureKind),    Unauthorized {        status_code: Option<u16>,        message: String,    },}/// Retrieves one cloud config bundle from the backend.////// Implementations should return the backend-selected bundle exactly as delivered and leave/// validation, caching, and config/requirements parsing decisions to the service layer.pub(crate) trait BundleClient: Send + Sync {    fn get_bundle(        &self,        auth: &CodexAuth,    ) -> impl Future<Output = Result<CloudConfigBundle, BundleRequestError>> + Send;}pub(crate) struct BackendBundleClient {    base_url: String,}impl BackendBundleClient {    pub(crate) fn new(base_url: String) -> Self {        Self { base_url }    }}impl BundleClient for BackendBundleClient {    async fn get_bundle(&self, auth: &CodexAuth) -> Result<CloudConfigBundle, BundleRequestError> {        let client = BackendClient::from_auth(self.base_url.clone(), auth)            .inspect_err(|err| {                tracing::warn!(                    error = %err,                    "Failed to construct backend client for cloud config bundle"                );            })            .map_err(|_| BundleRequestError::Retryable(RetryableFailureKind::BackendClientInit))?;        let response = client            .get_config_bundle()            .await            .inspect_err(|err| {                tracing::warn!(error = %err, "Failed to fetch cloud config bundle");            })            .map_err(|err| {                let status_code = err.status().map(|status| status.as_u16());                if err.is_unauthorized() {                    BundleRequestError::Unauthorized {                        status_code,                        message: err.to_string(),                    }                } else {                    BundleRequestError::Retryable(RetryableFailureKind::Request { status_code })                }            })?;        Ok(bundle_from_response(response))    }}pub(crate) fn bundle_from_response(response: ConfigBundleResponse) -> CloudConfigBundle {    let config_toml = response        .config_toml        .flatten()        .map(|config_toml| *config_toml)        .and_then(|config_toml| config_toml.enterprise_managed.flatten())        .unwrap_or_default()        .into_iter()        .map(config_fragment_from_delivered)        .collect();    let requirements_toml = response        .requirements_toml        .flatten()        .map(|requirements_toml| *requirements_toml)        .and_then(|requirements_toml| requirements_toml.enterprise_managed.flatten())        .unwrap_or_default()        .into_iter()        .map(requirements_fragment_from_delivered)        .collect();    CloudConfigBundle {        config_toml: CloudConfigTomlBundle {            enterprise_managed: config_toml,        },        requirements_toml: CloudRequirementsTomlBundle {            enterprise_managed: requirements_toml,        },    }}fn config_fragment_from_delivered(fragment: DeliveredTomlFragment) -> CloudConfigFragment {    CloudConfigFragment {        id: fragment.id,        name: fragment.name,        contents: fragment.contents,    }}fn requirements_fragment_from_delivered(    fragment: DeliveredTomlFragment,) -> CloudRequirementsFragment {    CloudRequirementsFragment {        id: fragment.id,        name: fragment.name,        contents: fragment.contents,    }}