1use std::collections::HashMap;
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: HashMap<String, StateMachine>,
36 #[serde(default)]
38 pub executions: HashMap<String, Execution>,
39 #[serde(default)]
40 pub activities: HashMap<String, Activity>,
41 #[serde(default)]
42 pub state_machine_versions: HashMap<String, StateMachineVersion>,
43 #[serde(default)]
44 pub state_machine_aliases: HashMap<String, StateMachineAlias>,
45 #[serde(default)]
46 pub map_runs: HashMap<String, MapRun>,
47 #[serde(default)]
49 pub task_tokens: HashMap<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: HashMap<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, pub output: Option<String>,
102 pub error: Option<String>,
103 pub cause: Option<String>,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct StateMachine {
108 pub name: String,
109 pub arn: String,
110 pub definition: String,
111 pub role_arn: String,
112 pub machine_type: StateMachineType,
113 pub status: StateMachineStatus,
114 pub creation_date: DateTime<Utc>,
115 pub update_date: DateTime<Utc>,
116 pub tags: HashMap<String, String>,
117 pub revision_id: String,
118 pub logging_configuration: Option<Value>,
119 pub tracing_configuration: Option<Value>,
120 pub description: String,
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
124pub enum StateMachineType {
125 Standard,
126 Express,
127}
128
129impl StateMachineType {
130 pub fn as_str(&self) -> &'static str {
131 match self {
132 Self::Standard => "STANDARD",
133 Self::Express => "EXPRESS",
134 }
135 }
136
137 pub fn parse(s: &str) -> Option<Self> {
138 match s {
139 "STANDARD" => Some(Self::Standard),
140 "EXPRESS" => Some(Self::Express),
141 _ => None,
142 }
143 }
144}
145
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
147pub enum StateMachineStatus {
148 Active,
149 Deleting,
150}
151
152impl StateMachineStatus {
153 pub fn as_str(&self) -> &'static str {
154 match self {
155 Self::Active => "ACTIVE",
156 Self::Deleting => "DELETING",
157 }
158 }
159}
160
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct Execution {
163 pub execution_arn: String,
164 pub state_machine_arn: String,
165 pub state_machine_name: String,
166 pub name: String,
167 pub status: ExecutionStatus,
168 pub input: Option<String>,
169 pub output: Option<String>,
170 pub start_date: DateTime<Utc>,
171 pub stop_date: Option<DateTime<Utc>>,
172 pub error: Option<String>,
173 pub cause: Option<String>,
174 pub history_events: Vec<HistoryEvent>,
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
178pub enum ExecutionStatus {
179 Running,
180 Succeeded,
181 Failed,
182 TimedOut,
183 Aborted,
184 PendingRedrive,
185}
186
187impl ExecutionStatus {
188 pub fn as_str(&self) -> &'static str {
189 match self {
190 Self::Running => "RUNNING",
191 Self::Succeeded => "SUCCEEDED",
192 Self::Failed => "FAILED",
193 Self::TimedOut => "TIMED_OUT",
194 Self::Aborted => "ABORTED",
195 Self::PendingRedrive => "PENDING_REDRIVE",
196 }
197 }
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct HistoryEvent {
202 pub id: i64,
203 pub event_type: String,
204 pub timestamp: DateTime<Utc>,
205 pub previous_event_id: i64,
206 pub details: Value,
207}
208
209impl StepFunctionsState {
210 pub fn new(account_id: &str, region: &str) -> Self {
211 Self {
212 account_id: account_id.to_string(),
213 region: region.to_string(),
214 state_machines: HashMap::new(),
215 executions: HashMap::new(),
216 activities: HashMap::new(),
217 state_machine_versions: HashMap::new(),
218 state_machine_aliases: HashMap::new(),
219 map_runs: HashMap::new(),
220 task_tokens: HashMap::new(),
221 }
222 }
223
224 pub fn reset(&mut self) {
225 self.state_machines.clear();
226 self.executions.clear();
227 self.activities.clear();
228 self.state_machine_versions.clear();
229 self.state_machine_aliases.clear();
230 self.map_runs.clear();
231 self.task_tokens.clear();
232 }
233
234 pub fn state_machine_arn(&self, name: &str) -> String {
235 format!(
236 "arn:aws:states:{}:{}:stateMachine:{}",
237 self.region, self.account_id, name
238 )
239 }
240
241 pub fn execution_arn(&self, state_machine_name: &str, execution_name: &str) -> String {
242 format!(
243 "arn:aws:states:{}:{}:execution:{}:{}",
244 self.region, self.account_id, state_machine_name, execution_name
245 )
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 use super::*;
252
253 #[test]
254 fn state_machine_type_as_str() {
255 assert_eq!(StateMachineType::Standard.as_str(), "STANDARD");
256 assert_eq!(StateMachineType::Express.as_str(), "EXPRESS");
257 }
258
259 #[test]
260 fn state_machine_type_parse() {
261 assert_eq!(
262 StateMachineType::parse("STANDARD"),
263 Some(StateMachineType::Standard)
264 );
265 assert_eq!(
266 StateMachineType::parse("EXPRESS"),
267 Some(StateMachineType::Express)
268 );
269 assert_eq!(StateMachineType::parse("bogus"), None);
270 }
271
272 #[test]
273 fn state_machine_status_as_str() {
274 assert_eq!(StateMachineStatus::Active.as_str(), "ACTIVE");
275 assert_eq!(StateMachineStatus::Deleting.as_str(), "DELETING");
276 }
277
278 #[test]
279 fn execution_status_as_str() {
280 assert_eq!(ExecutionStatus::Running.as_str(), "RUNNING");
281 assert_eq!(ExecutionStatus::Succeeded.as_str(), "SUCCEEDED");
282 assert_eq!(ExecutionStatus::Failed.as_str(), "FAILED");
283 assert_eq!(ExecutionStatus::TimedOut.as_str(), "TIMED_OUT");
284 assert_eq!(ExecutionStatus::Aborted.as_str(), "ABORTED");
285 assert_eq!(ExecutionStatus::PendingRedrive.as_str(), "PENDING_REDRIVE");
286 }
287
288 #[test]
289 fn state_machine_arn_format() {
290 let state = StepFunctionsState::new("123456789012", "us-east-1");
291 assert_eq!(
292 state.state_machine_arn("my-sm"),
293 "arn:aws:states:us-east-1:123456789012:stateMachine:my-sm"
294 );
295 }
296
297 #[test]
298 fn execution_arn_format() {
299 let state = StepFunctionsState::new("123456789012", "us-east-1");
300 assert_eq!(
301 state.execution_arn("sm", "exec-1"),
302 "arn:aws:states:us-east-1:123456789012:execution:sm:exec-1"
303 );
304 }
305
306 #[test]
307 fn state_reset_clears_all() {
308 let mut state = StepFunctionsState::new("123456789012", "us-east-1");
309 state.state_machines.insert(
310 "x".to_string(),
311 StateMachine {
312 name: "sm".to_string(),
313 arn: "arn:aws:states:us-east-1:123:stateMachine:sm".to_string(),
314 definition: "{}".to_string(),
315 role_arn: "r".to_string(),
316 machine_type: StateMachineType::Standard,
317 status: StateMachineStatus::Active,
318 creation_date: Utc::now(),
319 update_date: Utc::now(),
320 tags: HashMap::new(),
321 revision_id: "v1".to_string(),
322 logging_configuration: None,
323 tracing_configuration: None,
324 description: String::new(),
325 },
326 );
327 state.reset();
328 assert!(state.state_machines.is_empty());
329 assert!(state.executions.is_empty());
330 }
331}