forge_core/cron/
context.rs1use std::sync::Arc;
2
3use chrono::{DateTime, Utc};
4use tracing::Span;
5use uuid::Uuid;
6
7use crate::env::{EnvAccess, EnvProvider, RealEnvProvider};
8use crate::function::AuthContext;
9
10pub struct CronContext {
12 pub run_id: Uuid,
14 pub cron_name: String,
16 pub scheduled_time: DateTime<Utc>,
18 pub execution_time: DateTime<Utc>,
20 pub timezone: String,
22 pub is_catch_up: bool,
24 pub auth: AuthContext,
26 db_pool: sqlx::PgPool,
28 http_client: reqwest::Client,
30 pub log: CronLog,
32 env_provider: Arc<dyn EnvProvider>,
34 span: Span,
36}
37
38impl CronContext {
39 pub fn new(
41 run_id: Uuid,
42 cron_name: String,
43 scheduled_time: DateTime<Utc>,
44 timezone: String,
45 is_catch_up: bool,
46 db_pool: sqlx::PgPool,
47 http_client: reqwest::Client,
48 ) -> Self {
49 Self {
50 run_id,
51 cron_name: cron_name.clone(),
52 scheduled_time,
53 execution_time: Utc::now(),
54 timezone,
55 is_catch_up,
56 auth: AuthContext::unauthenticated(),
57 db_pool,
58 http_client,
59 log: CronLog::new(cron_name),
60 env_provider: Arc::new(RealEnvProvider::new()),
61 span: Span::current(),
62 }
63 }
64
65 pub fn with_env_provider(mut self, provider: Arc<dyn EnvProvider>) -> Self {
67 self.env_provider = provider;
68 self
69 }
70
71 pub fn db(&self) -> crate::function::ForgeDb {
72 crate::function::ForgeDb::from_pool(&self.db_pool)
73 }
74
75 pub async fn conn(&self) -> sqlx::Result<crate::function::ForgeConn<'static>> {
77 Ok(crate::function::ForgeConn::Pool(
78 self.db_pool.acquire().await?,
79 ))
80 }
81
82 pub fn http(&self) -> &reqwest::Client {
83 &self.http_client
84 }
85
86 pub fn delay(&self) -> chrono::Duration {
88 self.execution_time - self.scheduled_time
89 }
90
91 pub fn is_late(&self) -> bool {
93 self.delay() > chrono::Duration::minutes(1)
94 }
95
96 pub fn with_auth(mut self, auth: AuthContext) -> Self {
98 self.auth = auth;
99 self
100 }
101
102 pub fn trace_id(&self) -> String {
106 self.run_id.to_string()
110 }
111
112 pub fn span(&self) -> &Span {
116 &self.span
117 }
118}
119
120impl EnvAccess for CronContext {
121 fn env_provider(&self) -> &dyn EnvProvider {
122 self.env_provider.as_ref()
123 }
124}
125
126#[derive(Clone)]
128pub struct CronLog {
129 cron_name: String,
130}
131
132impl CronLog {
133 pub fn new(cron_name: String) -> Self {
135 Self { cron_name }
136 }
137
138 pub fn info(&self, message: &str, data: serde_json::Value) {
140 tracing::info!(
141 cron_name = %self.cron_name,
142 data = %data,
143 "{}",
144 message
145 );
146 }
147
148 pub fn warn(&self, message: &str, data: serde_json::Value) {
150 tracing::warn!(
151 cron_name = %self.cron_name,
152 data = %data,
153 "{}",
154 message
155 );
156 }
157
158 pub fn error(&self, message: &str, data: serde_json::Value) {
160 tracing::error!(
161 cron_name = %self.cron_name,
162 data = %data,
163 "{}",
164 message
165 );
166 }
167
168 pub fn debug(&self, message: &str, data: serde_json::Value) {
170 tracing::debug!(
171 cron_name = %self.cron_name,
172 data = %data,
173 "{}",
174 message
175 );
176 }
177}
178
179#[cfg(test)]
180#[allow(clippy::unwrap_used, clippy::indexing_slicing)]
181mod tests {
182 use super::*;
183
184 #[tokio::test]
185 async fn test_cron_context_creation() {
186 let pool = sqlx::postgres::PgPoolOptions::new()
187 .max_connections(1)
188 .connect_lazy("postgres://localhost/nonexistent")
189 .expect("Failed to create mock pool");
190
191 let run_id = Uuid::new_v4();
192 let scheduled = Utc::now() - chrono::Duration::seconds(30);
193
194 let ctx = CronContext::new(
195 run_id,
196 "test_cron".to_string(),
197 scheduled,
198 "UTC".to_string(),
199 false,
200 pool,
201 reqwest::Client::new(),
202 );
203
204 assert_eq!(ctx.run_id, run_id);
205 assert_eq!(ctx.cron_name, "test_cron");
206 assert!(!ctx.is_catch_up);
207 }
208
209 #[tokio::test]
210 async fn test_cron_delay() {
211 let pool = sqlx::postgres::PgPoolOptions::new()
212 .max_connections(1)
213 .connect_lazy("postgres://localhost/nonexistent")
214 .expect("Failed to create mock pool");
215
216 let scheduled = Utc::now() - chrono::Duration::minutes(5);
217
218 let ctx = CronContext::new(
219 Uuid::new_v4(),
220 "test_cron".to_string(),
221 scheduled,
222 "UTC".to_string(),
223 false,
224 pool,
225 reqwest::Client::new(),
226 );
227
228 assert!(ctx.is_late());
229 assert!(ctx.delay() >= chrono::Duration::minutes(5));
230 }
231
232 #[test]
233 fn test_cron_log() {
234 let log = CronLog::new("test_cron".to_string());
235 log.info("Test message", serde_json::json!({"key": "value"}));
236 }
237}