1use std::collections::BTreeMap;
2use std::sync::Arc;
3
4use chrono::{DateTime, Utc};
5use parking_lot::RwLock;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9pub type SharedStepFunctionsState =
10 Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<StepFunctionsState>>>;
11
12impl fakecloud_core::multi_account::AccountState for StepFunctionsState {
13 fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
14 Self::new(account_id, region)
15 }
16}
17
18pub const STEPFUNCTIONS_SNAPSHOT_SCHEMA_VERSION: u32 = 2;
19
20#[derive(Debug, Serialize, Deserialize)]
21pub struct StepFunctionsSnapshot {
22 pub schema_version: u32,
23 #[serde(default)]
24 pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<StepFunctionsState>>,
25 #[serde(default)]
26 pub state: Option<StepFunctionsState>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct StepFunctionsState {
31 pub account_id: String,
32 pub region: String,
33 #[serde(default)]
35 pub state_machines: BTreeMap<String, StateMachine>,
36 #[serde(default)]
38 pub executions: BTreeMap<String, Execution>,
39 #[serde(default)]
40 pub activities: BTreeMap<String, Activity>,
41 #[serde(default)]
42 pub state_machine_versions: BTreeMap<String, StateMachineVersion>,
43 #[serde(default)]
44 pub state_machine_aliases: BTreeMap<String, StateMachineAlias>,
45 #[serde(default)]
46 pub map_runs: BTreeMap<String, MapRun>,
47 #[serde(default)]
49 pub task_tokens: BTreeMap<String, TaskTokenState>,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
53pub struct Activity {
54 pub name: String,
55 pub arn: String,
56 pub creation_date: DateTime<Utc>,
57 pub tags: BTreeMap<String, String>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
61pub struct StateMachineVersion {
62 pub state_machine_arn: String,
63 pub version: i64,
64 pub revision_id: String,
65 pub description: String,
66 pub creation_date: DateTime<Utc>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct StateMachineAlias {
71 pub name: String,
72 pub arn: String,
73 pub description: String,
74 pub routing_configuration: Vec<AliasRoute>,
75 pub creation_date: DateTime<Utc>,
76 pub update_date: DateTime<Utc>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct AliasRoute {
81 pub state_machine_version_arn: String,
82 pub weight: i32,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct MapRun {
87 pub map_run_arn: String,
88 pub execution_arn: String,
89 pub max_concurrency: i32,
90 pub tolerated_failure_percentage: f64,
91 pub tolerated_failure_count: i64,
92 pub status: String,
93 pub start_date: DateTime<Utc>,
94 pub stop_date: Option<DateTime<Utc>>,
95 #[serde(default)]
97 pub total_count: i64,
98 #[serde(default)]
100 pub succeeded_count: i64,
101 #[serde(default)]
103 pub failed_count: i64,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct TaskTokenState {
108 pub activity_arn: String,
109 pub status: String,
113 pub output: Option<String>,
114 pub error: Option<String>,
115 pub cause: Option<String>,
116 #[serde(default)]
120 pub input: Option<String>,
121 #[serde(default = "default_now")]
122 pub created_at: DateTime<Utc>,
123 #[serde(default)]
124 pub last_heartbeat_at: Option<DateTime<Utc>>,
125 #[serde(default)]
128 pub heartbeat_seconds: Option<i64>,
129 #[serde(default)]
131 pub timeout_seconds: Option<i64>,
132}
133
134fn default_now() -> DateTime<Utc> {
135 Utc::now()
136}
137
138#[derive(Debug, Clone, Serialize, Deserialize)]
139pub struct StateMachine {
140 pub name: String,
141 pub arn: String,
142 pub definition: String,
143 pub role_arn: String,
144 pub machine_type: StateMachineType,
145 pub status: StateMachineStatus,
146 pub creation_date: DateTime<Utc>,
147 pub update_date: DateTime<Utc>,
148 pub tags: BTreeMap<String, String>,
149 pub revision_id: String,
150 pub logging_configuration: Option<Value>,
151 pub tracing_configuration: Option<Value>,
152 pub description: String,
153 #[serde(default)]
154 pub encryption_configuration: Option<Value>,
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
158pub enum StateMachineType {
159 Standard,
160 Express,
161}
162
163impl StateMachineType {
164 pub fn as_str(&self) -> &'static str {
165 match self {
166 Self::Standard => "STANDARD",
167 Self::Express => "EXPRESS",
168 }
169 }
170
171 pub fn parse(s: &str) -> Option<Self> {
172 match s {
173 "STANDARD" => Some(Self::Standard),
174 "EXPRESS" => Some(Self::Express),
175 _ => None,
176 }
177 }
178}
179
180#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
181pub enum StateMachineStatus {
182 Active,
183 Deleting,
184}
185
186impl StateMachineStatus {
187 pub fn as_str(&self) -> &'static str {
188 match self {
189 Self::Active => "ACTIVE",
190 Self::Deleting => "DELETING",
191 }
192 }
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct Execution {
197 pub execution_arn: String,
198 pub state_machine_arn: String,
199 pub state_machine_name: String,
200 pub name: String,
201 pub status: ExecutionStatus,
202 pub input: Option<String>,
203 pub output: Option<String>,
204 pub start_date: DateTime<Utc>,
205 pub stop_date: Option<DateTime<Utc>>,
206 pub error: Option<String>,
207 pub cause: Option<String>,
208 pub history_events: Vec<HistoryEvent>,
209 #[serde(default)]
213 pub parent_execution_arn: Option<String>,
214 #[serde(default)]
218 pub is_sync: bool,
219 #[serde(default)]
223 pub billed_duration_ms: Option<i64>,
224 #[serde(default)]
227 pub billed_memory_mb: Option<i64>,
228}
229
230#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
231pub enum ExecutionStatus {
232 Running,
233 Succeeded,
234 Failed,
235 TimedOut,
236 Aborted,
237 PendingRedrive,
238}
239
240impl ExecutionStatus {
241 pub fn as_str(&self) -> &'static str {
242 match self {
243 Self::Running => "RUNNING",
244 Self::Succeeded => "SUCCEEDED",
245 Self::Failed => "FAILED",
246 Self::TimedOut => "TIMED_OUT",
247 Self::Aborted => "ABORTED",
248 Self::PendingRedrive => "PENDING_REDRIVE",
249 }
250 }
251}
252
253#[derive(Debug, Clone, Serialize, Deserialize)]
254pub struct HistoryEvent {
255 pub id: i64,
256 pub event_type: String,
257 pub timestamp: DateTime<Utc>,
258 pub previous_event_id: i64,
259 pub details: Value,
260}
261
262impl StepFunctionsState {
263 pub fn new(account_id: &str, region: &str) -> Self {
264 Self {
265 account_id: account_id.to_string(),
266 region: region.to_string(),
267 state_machines: BTreeMap::new(),
268 executions: BTreeMap::new(),
269 activities: BTreeMap::new(),
270 state_machine_versions: BTreeMap::new(),
271 state_machine_aliases: BTreeMap::new(),
272 map_runs: BTreeMap::new(),
273 task_tokens: BTreeMap::new(),
274 }
275 }
276
277 pub fn reset(&mut self) {
278 self.state_machines.clear();
279 self.executions.clear();
280 self.activities.clear();
281 self.state_machine_versions.clear();
282 self.state_machine_aliases.clear();
283 self.map_runs.clear();
284 self.task_tokens.clear();
285 }
286
287 pub fn state_machine_arn(&self, name: &str) -> String {
288 format!(
289 "arn:aws:states:{}:{}:stateMachine:{}",
290 self.region, self.account_id, name
291 )
292 }
293
294 pub fn execution_arn(&self, state_machine_name: &str, execution_name: &str) -> String {
295 format!(
296 "arn:aws:states:{}:{}:execution:{}:{}",
297 self.region, self.account_id, state_machine_name, execution_name
298 )
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn state_machine_type_as_str() {
308 assert_eq!(StateMachineType::Standard.as_str(), "STANDARD");
309 assert_eq!(StateMachineType::Express.as_str(), "EXPRESS");
310 }
311
312 #[test]
313 fn state_machine_type_parse() {
314 assert_eq!(
315 StateMachineType::parse("STANDARD"),
316 Some(StateMachineType::Standard)
317 );
318 assert_eq!(
319 StateMachineType::parse("EXPRESS"),
320 Some(StateMachineType::Express)
321 );
322 assert_eq!(StateMachineType::parse("bogus"), None);
323 }
324
325 #[test]
326 fn state_machine_status_as_str() {
327 assert_eq!(StateMachineStatus::Active.as_str(), "ACTIVE");
328 assert_eq!(StateMachineStatus::Deleting.as_str(), "DELETING");
329 }
330
331 #[test]
332 fn execution_status_as_str() {
333 assert_eq!(ExecutionStatus::Running.as_str(), "RUNNING");
334 assert_eq!(ExecutionStatus::Succeeded.as_str(), "SUCCEEDED");
335 assert_eq!(ExecutionStatus::Failed.as_str(), "FAILED");
336 assert_eq!(ExecutionStatus::TimedOut.as_str(), "TIMED_OUT");
337 assert_eq!(ExecutionStatus::Aborted.as_str(), "ABORTED");
338 assert_eq!(ExecutionStatus::PendingRedrive.as_str(), "PENDING_REDRIVE");
339 }
340
341 #[test]
342 fn state_machine_arn_format() {
343 let state = StepFunctionsState::new("123456789012", "us-east-1");
344 assert_eq!(
345 state.state_machine_arn("my-sm"),
346 "arn:aws:states:us-east-1:123456789012:stateMachine:my-sm"
347 );
348 }
349
350 #[test]
351 fn execution_arn_format() {
352 let state = StepFunctionsState::new("123456789012", "us-east-1");
353 assert_eq!(
354 state.execution_arn("sm", "exec-1"),
355 "arn:aws:states:us-east-1:123456789012:execution:sm:exec-1"
356 );
357 }
358
359 #[test]
360 fn state_reset_clears_all() {
361 let mut state = StepFunctionsState::new("123456789012", "us-east-1");
362 state.state_machines.insert(
363 "x".to_string(),
364 StateMachine {
365 name: "sm".to_string(),
366 arn: "arn:aws:states:us-east-1:123:stateMachine:sm".to_string(),
367 definition: "{}".to_string(),
368 role_arn: "r".to_string(),
369 machine_type: StateMachineType::Standard,
370 status: StateMachineStatus::Active,
371 creation_date: Utc::now(),
372 update_date: Utc::now(),
373 tags: BTreeMap::new(),
374 revision_id: "v1".to_string(),
375 logging_configuration: None,
376 tracing_configuration: None,
377 description: String::new(),
378 encryption_configuration: None,
379 },
380 );
381 state.reset();
382 assert!(state.state_machines.is_empty());
383 assert!(state.executions.is_empty());
384 }
385}