Skip to main content

chronon_axum/
state.rs

1//! Shared state for Chronon API handlers.
2
3use std::sync::Arc;
4
5use chronon_core::{ChrononError, Result};
6use chronon_executor::ScriptRegistry;
7use chronon_runtime::CoordinatorService;
8use serde_json::Value as JsonValue;
9
10use crate::auth::{require_admin_auth_from_env, AdminAuth};
11
12/// Callback that supplies `actor_json` when HTTP upsert omits a client actor.
13pub type HttpUpsertActorProvider = Arc<dyn Fn() -> JsonValue + Send + Sync>;
14
15/// Shared state for Chronon API handlers.
16///
17/// Install on the host router via [`axum::extract::FromRef`] and pass to [`crate::chronon_router`].
18/// Construct with [`ChrononState::new`] or [`ChrononState::builder`].
19#[derive(Clone)]
20pub struct ChrononState {
21    /// Job and run persistence API.
22    pub coordinator: Arc<CoordinatorService>,
23    /// Script catalog for upsert validation and `GET /scripts`.
24    pub registry: Arc<ScriptRegistry>,
25    /// Optional host verifier for admin routes.
26    pub admin_auth: Option<Arc<dyn AdminAuth>>,
27    /// When true, requests are rejected if [`admin_auth`](Self::admin_auth) is `None`.
28    pub require_admin_auth: bool,
29    /// Optional override for default HTTP upsert actor JSON (when request omits actor).
30    pub http_upsert_actor: Option<HttpUpsertActorProvider>,
31}
32
33impl ChrononState {
34    /// Build handler state from coordinator and registry (no admin auth; require-flag from env).
35    #[must_use]
36    pub fn new(coordinator: Arc<CoordinatorService>, registry: Arc<ScriptRegistry>) -> Self {
37        Self {
38            coordinator,
39            registry,
40            admin_auth: None,
41            require_admin_auth: require_admin_auth_from_env(),
42            http_upsert_actor: None,
43        }
44    }
45
46    /// Builder for authenticated / customized admin mounts.
47    #[must_use]
48    pub fn builder(
49        coordinator: Arc<CoordinatorService>,
50        registry: Arc<ScriptRegistry>,
51    ) -> ChrononStateBuilder {
52        ChrononStateBuilder {
53            coordinator,
54            registry,
55            admin_auth: None,
56            require_admin_auth: require_admin_auth_from_env(),
57            http_upsert_actor: None,
58        }
59    }
60
61    /// Actor JSON used when HTTP upsert omits `actor_json`.
62    #[must_use]
63    pub fn default_upsert_actor_json(&self) -> JsonValue {
64        if let Some(ref provider) = self.http_upsert_actor {
65            return provider();
66        }
67        chronon_core::default_http_enqueue_actor()
68    }
69}
70
71/// Build [`ChrononState`] with admin auth and actor overrides.
72pub struct ChrononStateBuilder {
73    coordinator: Arc<CoordinatorService>,
74    registry: Arc<ScriptRegistry>,
75    admin_auth: Option<Arc<dyn AdminAuth>>,
76    require_admin_auth: bool,
77    http_upsert_actor: Option<HttpUpsertActorProvider>,
78}
79
80impl ChrononStateBuilder {
81    /// Install a host [`AdminAuth`] verifier.
82    #[must_use]
83    pub fn admin_auth(mut self, auth: Arc<dyn AdminAuth>) -> Self {
84        self.admin_auth = Some(auth);
85        self
86    }
87
88    /// Force require-admin-auth (overrides env when set).
89    #[must_use]
90    pub const fn require_admin_auth(mut self, require: bool) -> Self {
91        self.require_admin_auth = require;
92        self
93    }
94
95    /// Override default HTTP upsert actor JSON (when the request omits actor).
96    #[must_use]
97    pub fn http_upsert_actor(
98        mut self,
99        provider: impl Fn() -> JsonValue + Send + Sync + 'static,
100    ) -> Self {
101        self.http_upsert_actor = Some(Arc::new(provider));
102        self
103    }
104
105    /// Build state.
106    ///
107    /// # Errors
108    ///
109    /// Returns [`ChrononError::Internal`] when `require_admin_auth` is set and no verifier
110    /// was installed.
111    pub fn build(self) -> Result<ChrononState> {
112        if self.require_admin_auth && self.admin_auth.is_none() {
113            return Err(ChrononError::Internal(
114                "CHRONON_REQUIRE_ADMIN_AUTH is set but no AdminAuth verifier was configured".into(),
115            ));
116        }
117        Ok(ChrononState {
118            coordinator: self.coordinator,
119            registry: self.registry,
120            admin_auth: self.admin_auth,
121            require_admin_auth: self.require_admin_auth,
122            http_upsert_actor: self.http_upsert_actor,
123        })
124    }
125}