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}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct TaskTokenState {
99 pub activity_arn: String,
100 pub status: String,
104 pub output: Option<String>,
105 pub error: Option<String>,
106 pub cause: Option<String>,
107 #[serde(default)]
111 pub input: Option<String>,
112 #[serde(default = "default_now")]
113 pub created_at: DateTime<Utc>,
114 #[serde(default)]
115 pub last_heartbeat_at: Option<DateTime<Utc>>,
116 #[serde(default)]
119 pub heartbeat_seconds: Option<i64>,
120 #[serde(default)]
122 pub timeout_seconds: Option<i64>,
123}
124
125fn default_now() -> DateTime<Utc> {
126 Utc::now()
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct StateMachine {
131 pub name: String,
132 pub arn: String,
133 pub definition: String,
134 pub role_arn: String,
135 pub machine_type: StateMachineType,
136 pub status: StateMachineStatus,
137 pub creation_date: DateTime<Utc>,
138 pub update_date: DateTime<Utc>,
139 pub tags: BTreeMap<String, String>,
140 pub revision_id: String,
141 pub logging_configuration: Option<Value>,
142 pub tracing_configuration: Option<Value>,
143 pub description: String,
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
147pub enum StateMachineType {
148 Standard,
149 Express,
150}
151
152impl StateMachineType {
153 pub fn as_str(&self) -> &'static str {
154 match self {
155 Self::Standard => "STANDARD",
156 Self::Express => "EXPRESS",
157 }
158 }
159
160 pub fn parse(s: &str) -> Option<Self> {
161 match s {
162 "STANDARD" => Some(Self::Standard),
163 "EXPRESS" => Some(Self::Express),
164 _ => None,
165 }
166 }
167}
168
169#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
170pub enum StateMachineStatus {
171 Active,
172 Deleting,
173}
174
175impl StateMachineStatus {
176 pub fn as_str(&self) -> &'static str {
177 match self {
178 Self::Active => "ACTIVE",
179 Self::Deleting => "DELETING",
180 }
181 }
182}
183
184#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct Execution {
186 pub execution_arn: String,
187 pub state_machine_arn: String,
188 pub state_machine_name: String,
189 pub name: String,
190 pub status: ExecutionStatus,
191 pub input: Option<String>,
192 pub output: Option<String>,
193 pub start_date: DateTime<Utc>,
194 pub stop_date: Option<DateTime<Utc>>,
195 pub error: Option<String>,
196 pub cause: Option<String>,
197 pub history_events: Vec<HistoryEvent>,
198 #[serde(default)]
202 pub parent_execution_arn: Option<String>,
203 #[serde(default)]
207 pub is_sync: bool,
208 #[serde(default)]
212 pub billed_duration_ms: Option<i64>,
213 #[serde(default)]
216 pub billed_memory_mb: Option<i64>,
217}
218
219#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
220pub enum ExecutionStatus {
221 Running,
222 Succeeded,
223 Failed,
224 TimedOut,
225 Aborted,
226 PendingRedrive,
227}
228
229impl ExecutionStatus {
230 pub fn as_str(&self) -> &'static str {
231 match self {
232 Self::Running => "RUNNING",
233 Self::Succeeded => "SUCCEEDED",
234 Self::Failed => "FAILED",
235 Self::TimedOut => "TIMED_OUT",
236 Self::Aborted => "ABORTED",
237 Self::PendingRedrive => "PENDING_REDRIVE",
238 }
239 }
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct HistoryEvent {
244 pub id: i64,
245 pub event_type: String,
246 pub timestamp: DateTime<Utc>,
247 pub previous_event_id: i64,
248 pub details: Value,
249}
250
251impl StepFunctionsState {
252 pub fn new(account_id: &str, region: &str) -> Self {
253 Self {
254 account_id: account_id.to_string(),
255 region: region.to_string(),
256 state_machines: BTreeMap::new(),
257 executions: BTreeMap::new(),
258 activities: BTreeMap::new(),
259 state_machine_versions: BTreeMap::new(),
260 state_machine_aliases: BTreeMap::new(),
261 map_runs: BTreeMap::new(),
262 task_tokens: BTreeMap::new(),
263 }
264 }
265
266 pub fn reset(&mut self) {
267 self.state_machines.clear();
268 self.executions.clear();
269 self.activities.clear();
270 self.state_machine_versions.clear();
271 self.state_machine_aliases.clear();
272 self.map_runs.clear();
273 self.task_tokens.clear();
274 }
275
276 pub fn state_machine_arn(&self, name: &str) -> String {
277 format!(
278 "arn:aws:states:{}:{}:stateMachine:{}",
279 self.region, self.account_id, name
280 )
281 }
282
283 pub fn execution_arn(&self, state_machine_name: &str, execution_name: &str) -> String {
284 format!(
285 "arn:aws:states:{}:{}:execution:{}:{}",
286 self.region, self.account_id, state_machine_name, execution_name
287 )
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 #[test]
296 fn state_machine_type_as_str() {
297 assert_eq!(StateMachineType::Standard.as_str(), "STANDARD");
298 assert_eq!(StateMachineType::Express.as_str(), "EXPRESS");
299 }
300
301 #[test]
302 fn state_machine_type_parse() {
303 assert_eq!(
304 StateMachineType::parse("STANDARD"),
305 Some(StateMachineType::Standard)
306 );
307 assert_eq!(
308 StateMachineType::parse("EXPRESS"),
309 Some(StateMachineType::Express)
310 );
311 assert_eq!(StateMachineType::parse("bogus"), None);
312 }
313
314 #[test]
315 fn state_machine_status_as_str() {
316 assert_eq!(StateMachineStatus::Active.as_str(), "ACTIVE");
317 assert_eq!(StateMachineStatus::Deleting.as_str(), "DELETING");
318 }
319
320 #[test]
321 fn execution_status_as_str() {
322 assert_eq!(ExecutionStatus::Running.as_str(), "RUNNING");
323 assert_eq!(ExecutionStatus::Succeeded.as_str(), "SUCCEEDED");
324 assert_eq!(ExecutionStatus::Failed.as_str(), "FAILED");
325 assert_eq!(ExecutionStatus::TimedOut.as_str(), "TIMED_OUT");
326 assert_eq!(ExecutionStatus::Aborted.as_str(), "ABORTED");
327 assert_eq!(ExecutionStatus::PendingRedrive.as_str(), "PENDING_REDRIVE");
328 }
329
330 #[test]
331 fn state_machine_arn_format() {
332 let state = StepFunctionsState::new("123456789012", "us-east-1");
333 assert_eq!(
334 state.state_machine_arn("my-sm"),
335 "arn:aws:states:us-east-1:123456789012:stateMachine:my-sm"
336 );
337 }
338
339 #[test]
340 fn execution_arn_format() {
341 let state = StepFunctionsState::new("123456789012", "us-east-1");
342 assert_eq!(
343 state.execution_arn("sm", "exec-1"),
344 "arn:aws:states:us-east-1:123456789012:execution:sm:exec-1"
345 );
346 }
347
348 #[test]
349 fn state_reset_clears_all() {
350 let mut state = StepFunctionsState::new("123456789012", "us-east-1");
351 state.state_machines.insert(
352 "x".to_string(),
353 StateMachine {
354 name: "sm".to_string(),
355 arn: "arn:aws:states:us-east-1:123:stateMachine:sm".to_string(),
356 definition: "{}".to_string(),
357 role_arn: "r".to_string(),
358 machine_type: StateMachineType::Standard,
359 status: StateMachineStatus::Active,
360 creation_date: Utc::now(),
361 update_date: Utc::now(),
362 tags: BTreeMap::new(),
363 revision_id: "v1".to_string(),
364 logging_configuration: None,
365 tracing_configuration: None,
366 description: String::new(),
367 },
368 );
369 state.reset();
370 assert!(state.state_machines.is_empty());
371 assert!(state.executions.is_empty());
372 }
373}