1use async_trait::async_trait;
2use chrono::{DateTime, Utc};
3use http::StatusCode;
4use serde_json::{json, Value};
5
6use std::collections::{BTreeMap, HashMap};
7use std::sync::Arc;
8
9use tokio::sync::Mutex as AsyncMutex;
10
11use fakecloud_aws::arn::Arn;
12use fakecloud_core::delivery::DeliveryBus;
13use fakecloud_core::pagination::paginate;
14use fakecloud_core::service::{AwsRequest, AwsResponse, AwsService, AwsServiceError};
15use fakecloud_core::validation::*;
16use fakecloud_persistence::SnapshotStore;
17
18use fakecloud_lambda::runtime::ContainerRuntime;
19use fakecloud_lambda::{LambdaInvocation, SharedLambdaState};
20use fakecloud_logs::SharedLogsState;
21
22use crate::state::{
23 ApiDestination, Archive, Connection, Endpoint, EventBridgeSnapshot, EventBridgeState, EventBus,
24 EventRule, EventTarget, PartnerEventSource, PutEvent, Replay, SharedEventBridgeState,
25 EVENTBRIDGE_SNAPSHOT_SCHEMA_VERSION,
26};
27
28pub struct EventBridgeService {
29 state: SharedEventBridgeState,
30 delivery: Arc<DeliveryBus>,
31 lambda_state: Option<SharedLambdaState>,
32 logs_state: Option<SharedLogsState>,
33 container_runtime: Option<Arc<ContainerRuntime>>,
34 snapshot_store: Option<Arc<dyn SnapshotStore>>,
35 snapshot_lock: Arc<AsyncMutex<()>>,
36}
37
38impl EventBridgeService {
39 pub fn new(state: SharedEventBridgeState, delivery: Arc<DeliveryBus>) -> Self {
40 Self {
41 state,
42 delivery,
43 lambda_state: None,
44 logs_state: None,
45 container_runtime: None,
46 snapshot_store: None,
47 snapshot_lock: Arc::new(AsyncMutex::new(())),
48 }
49 }
50
51 pub fn with_lambda(mut self, lambda_state: SharedLambdaState) -> Self {
52 self.lambda_state = Some(lambda_state);
53 self
54 }
55
56 pub fn with_logs(mut self, logs_state: SharedLogsState) -> Self {
57 self.logs_state = Some(logs_state);
58 self
59 }
60
61 pub fn with_runtime(mut self, runtime: Arc<ContainerRuntime>) -> Self {
62 self.container_runtime = Some(runtime);
63 self
64 }
65
66 pub fn with_snapshot_store(mut self, store: Arc<dyn SnapshotStore>) -> Self {
67 self.snapshot_store = Some(store);
68 self
69 }
70
71 async fn save_snapshot(&self) {
75 save_eventbridge_snapshot(
76 &self.state,
77 self.snapshot_store.clone(),
78 &self.snapshot_lock,
79 )
80 .await;
81 }
82
83 pub fn snapshot_hook(&self) -> Option<fakecloud_persistence::SnapshotHook> {
88 let store = self.snapshot_store.clone()?;
89 let state = self.state.clone();
90 let lock = self.snapshot_lock.clone();
91 Some(Arc::new(move || {
92 let state = state.clone();
93 let store = store.clone();
94 let lock = lock.clone();
95 Box::pin(async move {
96 save_eventbridge_snapshot(&state, Some(store), &lock).await;
97 })
98 }))
99 }
100}
101
102pub async fn save_eventbridge_snapshot(
108 state: &SharedEventBridgeState,
109 store: Option<Arc<dyn SnapshotStore>>,
110 lock: &AsyncMutex<()>,
111) {
112 let Some(store) = store else {
113 return;
114 };
115 let _guard = lock.lock().await;
116 let snapshot = EventBridgeSnapshot {
117 schema_version: EVENTBRIDGE_SNAPSHOT_SCHEMA_VERSION,
118 accounts: Some(state.read().clone()),
119 state: None,
120 };
121 let join = tokio::task::spawn_blocking(move || -> std::io::Result<()> {
122 let bytes = serde_json::to_vec(&snapshot)
123 .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?;
124 store.save(&bytes)
125 })
126 .await;
127 match join {
128 Ok(Ok(())) => {}
129 Ok(Err(err)) => tracing::error!(%err, "failed to write eventbridge snapshot"),
130 Err(err) => tracing::error!(%err, "eventbridge snapshot task panicked"),
131 }
132}
133
134#[async_trait]
135impl AwsService for EventBridgeService {
136 fn service_name(&self) -> &str {
137 "events"
138 }
139
140 async fn handle(&self, req: AwsRequest) -> Result<AwsResponse, AwsServiceError> {
141 let mutates = is_mutating_action(req.action.as_str());
142 let result = match req.action.as_str() {
143 "CreateEventBus" => self.create_event_bus(&req),
144 "DeleteEventBus" => self.delete_event_bus(&req),
145 "ListEventBuses" => self.list_event_buses(&req),
146 "DescribeEventBus" => self.describe_event_bus(&req),
147 "PutRule" => self.put_rule(&req),
148 "DeleteRule" => self.delete_rule(&req),
149 "ListRules" => self.list_rules(&req),
150 "DescribeRule" => self.describe_rule(&req),
151 "EnableRule" => self.enable_rule(&req),
152 "DisableRule" => self.disable_rule(&req),
153 "PutTargets" => self.put_targets(&req),
154 "RemoveTargets" => self.remove_targets(&req),
155 "ListTargetsByRule" => self.list_targets_by_rule(&req),
156 "ListRuleNamesByTarget" => self.list_rule_names_by_target(&req),
157 "PutEvents" => self.put_events(&req),
158 "PutPermission" => self.put_permission(&req),
159 "RemovePermission" => self.remove_permission(&req),
160 "TagResource" => self.tag_resource(&req),
161 "UntagResource" => self.untag_resource(&req),
162 "ListTagsForResource" => self.list_tags_for_resource(&req),
163 "CreateArchive" => self.create_archive(&req),
164 "DescribeArchive" => self.describe_archive(&req),
165 "ListArchives" => self.list_archives(&req),
166 "UpdateArchive" => self.update_archive(&req),
167 "DeleteArchive" => self.delete_archive(&req),
168 "CreateConnection" => self.create_connection(&req),
169 "DescribeConnection" => self.describe_connection(&req),
170 "ListConnections" => self.list_connections(&req),
171 "UpdateConnection" => self.update_connection(&req),
172 "DeleteConnection" => self.delete_connection(&req),
173 "CreateApiDestination" => self.create_api_destination(&req),
174 "DescribeApiDestination" => self.describe_api_destination(&req),
175 "ListApiDestinations" => self.list_api_destinations(&req),
176 "UpdateApiDestination" => self.update_api_destination(&req),
177 "DeleteApiDestination" => self.delete_api_destination(&req),
178 "StartReplay" => self.start_replay(&req),
179 "DescribeReplay" => self.describe_replay(&req),
180 "ListReplays" => self.list_replays(&req),
181 "CancelReplay" => self.cancel_replay(&req),
182 "CreatePartnerEventSource" => self.create_partner_event_source(&req),
183 "DeletePartnerEventSource" => self.delete_partner_event_source(&req),
184 "DescribePartnerEventSource" => self.describe_partner_event_source(&req),
185 "ListPartnerEventSources" => self.list_partner_event_sources(&req),
186 "ListPartnerEventSourceAccounts" => self.list_partner_event_source_accounts(&req),
187 "ActivateEventSource" => self.activate_event_source(&req),
188 "DeactivateEventSource" => self.deactivate_event_source(&req),
189 "DescribeEventSource" => self.describe_event_source(&req),
190 "ListEventSources" => self.list_event_sources(&req),
191 "PutPartnerEvents" => self.put_partner_events(&req),
192 "TestEventPattern" => self.test_event_pattern(&req),
193 "UpdateEventBus" => self.update_event_bus(&req),
194 "CreateEndpoint" => self.create_endpoint(&req),
195 "DeleteEndpoint" => self.delete_endpoint(&req),
196 "DescribeEndpoint" => self.describe_endpoint(&req),
197 "ListEndpoints" => self.list_endpoints(&req),
198 "UpdateEndpoint" => self.update_endpoint(&req),
199 "DeauthorizeConnection" => self.deauthorize_connection(&req),
200 _ => Err(AwsServiceError::action_not_implemented(
201 "events",
202 &req.action,
203 )),
204 };
205 if mutates && matches!(result.as_ref(), Ok(resp) if resp.status.is_success()) {
206 self.save_snapshot().await;
207 }
208 result
209 }
210
211 fn supported_actions(&self) -> &[&str] {
212 &[
213 "CreateEventBus",
214 "DeleteEventBus",
215 "ListEventBuses",
216 "DescribeEventBus",
217 "PutRule",
218 "DeleteRule",
219 "ListRules",
220 "DescribeRule",
221 "EnableRule",
222 "DisableRule",
223 "PutTargets",
224 "RemoveTargets",
225 "ListTargetsByRule",
226 "ListRuleNamesByTarget",
227 "PutEvents",
228 "PutPermission",
229 "RemovePermission",
230 "TagResource",
231 "UntagResource",
232 "ListTagsForResource",
233 "CreateArchive",
234 "DescribeArchive",
235 "ListArchives",
236 "UpdateArchive",
237 "DeleteArchive",
238 "CreateConnection",
239 "DescribeConnection",
240 "ListConnections",
241 "UpdateConnection",
242 "DeleteConnection",
243 "CreateApiDestination",
244 "DescribeApiDestination",
245 "ListApiDestinations",
246 "UpdateApiDestination",
247 "DeleteApiDestination",
248 "StartReplay",
249 "DescribeReplay",
250 "ListReplays",
251 "CancelReplay",
252 "CreatePartnerEventSource",
253 "DeletePartnerEventSource",
254 "DescribePartnerEventSource",
255 "ListPartnerEventSources",
256 "ListPartnerEventSourceAccounts",
257 "ActivateEventSource",
258 "DeactivateEventSource",
259 "DescribeEventSource",
260 "ListEventSources",
261 "PutPartnerEvents",
262 "TestEventPattern",
263 "UpdateEventBus",
264 "CreateEndpoint",
265 "DeleteEndpoint",
266 "DescribeEndpoint",
267 "ListEndpoints",
268 "UpdateEndpoint",
269 "DeauthorizeConnection",
270 ]
271 }
272}
273
274impl EventBridgeService {
276 fn create_event_bus(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
277 let body = req.json_body();
278 validate_required("Name", &body["Name"])?;
279 let name = body["Name"]
280 .as_str()
281 .ok_or_else(|| missing("Name"))?
282 .to_string();
283 validate_string_length("name", &name, 1, 256)?;
284 validate_optional_string_length(
285 "eventSourceName",
286 body["EventSourceName"].as_str(),
287 1,
288 256,
289 )?;
290 validate_optional_string_length("description", body["Description"].as_str(), 0, 512)?;
291 validate_optional_string_length(
292 "kmsKeyIdentifier",
293 body["KmsKeyIdentifier"].as_str(),
294 0,
295 2048,
296 )?;
297
298 if name.contains('/') && !name.starts_with("aws.partner/") {
300 return Err(AwsServiceError::aws_error(
301 StatusCode::BAD_REQUEST,
302 "ValidationException",
303 "Event bus name must not contain '/'.",
304 ));
305 }
306
307 if name.starts_with("aws.partner/") {
309 let event_source = body["EventSourceName"].as_str().unwrap_or("");
310 let accounts_r = self.state.read();
311 let empty_r = EventBridgeState::new(&req.account_id, &req.region);
312 let state_r = accounts_r.get(&req.account_id).unwrap_or(&empty_r);
313 let has_source = state_r.partner_event_sources.contains_key(event_source);
314 drop(accounts_r);
315 if !has_source {
316 return Err(AwsServiceError::aws_error(
317 StatusCode::BAD_REQUEST,
318 "ResourceNotFoundException",
319 format!("Event source {event_source} does not exist."),
320 ));
321 }
322 }
323
324 let mut accounts = self.state.write();
325 let state = accounts.get_or_create(&req.account_id);
326
327 if state.buses.contains_key(&name) {
328 return Err(AwsServiceError::aws_error(
329 StatusCode::BAD_REQUEST,
330 "ResourceAlreadyExistsException",
331 format!("Event bus {name} already exists."),
332 ));
333 }
334
335 let arn = format!(
336 "arn:aws:events:{}:{}:event-bus/{}",
337 req.region, state.account_id, name
338 );
339 let now = Utc::now();
340 let description = body["Description"].as_str().map(|s| s.to_string());
341 let kms_key_identifier = body["KmsKeyIdentifier"].as_str().map(|s| s.to_string());
342 let dead_letter_config = body.get("DeadLetterConfig").cloned();
343
344 let tags = parse_tags(&body);
345
346 let bus = EventBus {
347 name: name.clone(),
348 arn: arn.clone(),
349 tags,
350 policy: None,
351 description,
352 kms_key_identifier,
353 dead_letter_config,
354 creation_time: now,
355 last_modified_time: now,
356 };
357 state.buses.insert(name, bus);
358
359 Ok(AwsResponse::ok_json(json!({ "EventBusArn": arn })))
360 }
361
362 fn delete_event_bus(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
363 let body = req.json_body();
364 validate_required("Name", &body["Name"])?;
365 let name = body["Name"].as_str().ok_or_else(|| missing("Name"))?;
366 validate_string_length("name", name, 1, 256)?;
367
368 if name == "default" {
369 return Err(AwsServiceError::aws_error(
370 StatusCode::BAD_REQUEST,
371 "ValidationException",
372 format!("Cannot delete event bus {name}."),
373 ));
374 }
375
376 let mut accounts = self.state.write();
377 let state = accounts.get_or_create(&req.account_id);
378 state.buses.remove(name);
379 state.rules.retain(|k, _| k.0 != name);
380
381 Ok(AwsResponse::ok_json(json!({})))
382 }
383
384 fn list_event_buses(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
385 let body = req.json_body();
386 validate_optional_string_length_value("NamePrefix", &body["NamePrefix"], 1, 256)?;
394 validate_optional_string_length_value("NextToken", &body["NextToken"], 1, 2048)?;
395 validate_optional_json_range("Limit", &body["Limit"], 1, 100)?;
396 let name_prefix = body["NamePrefix"].as_str();
397 let limit = body["Limit"].as_i64().unwrap_or(100).clamp(1, 100) as usize;
398
399 let accounts = self.state.read();
400 let empty = EventBridgeState::new(&req.account_id, &req.region);
401 let state = accounts.get(&req.account_id).unwrap_or(&empty);
402 let filtered: Vec<&_> = state
403 .buses
404 .values()
405 .filter(|b| match name_prefix {
406 Some(prefix) => b.name.starts_with(prefix),
407 None => true,
408 })
409 .collect();
410
411 let (page, next_token) = paginate(&filtered, body["NextToken"].as_str(), limit);
412 let buses: Vec<Value> = page
413 .iter()
414 .map(|b| {
415 json!({
419 "Name": b.name,
420 "Arn": b.arn,
421 "CreationTime": b.creation_time.timestamp() as f64,
422 "LastModifiedTime": b.last_modified_time.timestamp() as f64,
423 })
424 })
425 .collect();
426 let mut resp = json!({ "EventBuses": buses });
427 if let Some(token) = next_token {
428 resp["NextToken"] = json!(token);
429 }
430
431 Ok(AwsResponse::ok_json(resp))
432 }
433
434 fn describe_event_bus(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
435 let body = req.json_body();
436 validate_optional_string_length("name", body["Name"].as_str(), 1, 1600)?;
437 let name = body["Name"].as_str().unwrap_or("default");
438
439 let accounts = self.state.read();
440 let empty = EventBridgeState::new(&req.account_id, &req.region);
441 let state = accounts.get(&req.account_id).unwrap_or(&empty);
442 let bus = state.buses.get(name).ok_or_else(|| {
443 AwsServiceError::aws_error(
444 StatusCode::BAD_REQUEST,
445 "ResourceNotFoundException",
446 format!("Event bus {name} does not exist."),
447 )
448 })?;
449
450 let mut resp = json!({
451 "Name": bus.name,
452 "Arn": bus.arn,
453 "CreationTime": bus.creation_time.timestamp() as f64,
454 "LastModifiedTime": bus.last_modified_time.timestamp() as f64,
455 });
456
457 if let Some(ref policy) = bus.policy {
458 resp["Policy"] = Value::String(serde_json::to_string(policy).unwrap());
459 }
460 if let Some(ref desc) = bus.description {
461 resp["Description"] = json!(desc);
462 }
463 if let Some(ref kms) = bus.kms_key_identifier {
464 resp["KmsKeyIdentifier"] = json!(kms);
465 }
466 if let Some(ref dlc) = bus.dead_letter_config {
467 resp["DeadLetterConfig"] = dlc.clone();
468 }
469
470 Ok(AwsResponse::ok_json(resp))
471 }
472
473 fn put_permission(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
476 let body = req.json_body();
477 validate_optional_string_length_value("EventBusName", &body["EventBusName"], 1, 256)?;
484 validate_optional_string_length_value("Action", &body["Action"], 1, 64)?;
485 validate_optional_string_length_value("Principal", &body["Principal"], 1, 12)?;
486 validate_optional_string_length_value("StatementId", &body["StatementId"], 1, 64)?;
487 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
488
489 let mut accounts = self.state.write();
490 let state = accounts.get_or_create(&req.account_id);
491
492 let bus = state.buses.get_mut(event_bus_name).ok_or_else(|| {
493 AwsServiceError::aws_error(
494 StatusCode::BAD_REQUEST,
495 "ResourceNotFoundException",
496 format!("Event bus {event_bus_name} does not exist."),
497 )
498 })?;
499
500 if let Some(policy_str) = body["Policy"].as_str() {
502 if let Ok(policy) = serde_json::from_str::<Value>(policy_str) {
503 bus.policy = Some(policy);
504 return Ok(AwsResponse::ok_json(json!({})));
505 }
506 }
507
508 let action = body["Action"].as_str().unwrap_or("");
515 let principal = body["Principal"].as_str().unwrap_or("");
516 let statement_id = body["StatementId"].as_str().unwrap_or("");
517
518 let principal_value = if principal == "*" {
527 json!("*")
528 } else {
529 json!({ "AWS": Arn::global("iam", principal, "root").to_string() })
530 };
531 let statement = json!({
532 "Sid": statement_id,
533 "Effect": "Allow",
534 "Principal": principal_value,
535 "Action": action,
536 "Resource": bus.arn,
537 });
538
539 let policy = bus.policy.get_or_insert_with(|| {
540 json!({
541 "Version": "2012-10-17",
542 "Statement": [],
543 })
544 });
545
546 if let Some(stmts) = policy["Statement"].as_array_mut() {
547 stmts.retain(|s| s["Sid"].as_str() != Some(statement_id));
551 stmts.push(statement);
552 }
553
554 Ok(AwsResponse::ok_json(json!({})))
555 }
556
557 fn remove_permission(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
558 let body = req.json_body();
559 validate_optional_string_length("statementId", body["StatementId"].as_str(), 1, 64)?;
560 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 256)?;
561 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
562 let statement_id = body["StatementId"].as_str().unwrap_or("");
563 let remove_all = body["RemoveAllPermissions"].as_bool().unwrap_or(false);
564
565 let mut accounts = self.state.write();
566 let state = accounts.get_or_create(&req.account_id);
567
568 let bus = state.buses.get_mut(event_bus_name).ok_or_else(|| {
569 AwsServiceError::aws_error(
570 StatusCode::BAD_REQUEST,
571 "ResourceNotFoundException",
572 format!("Event bus {event_bus_name} does not exist."),
573 )
574 })?;
575
576 if remove_all {
577 bus.policy = None;
578 return Ok(AwsResponse::ok_json(json!({})));
579 }
580
581 let policy = bus.policy.as_mut().ok_or_else(|| {
582 AwsServiceError::aws_error(
583 StatusCode::BAD_REQUEST,
584 "ResourceNotFoundException",
585 "EventBus does not have a policy.",
586 )
587 })?;
588
589 if let Some(stmts) = policy["Statement"].as_array_mut() {
590 let before = stmts.len();
591 stmts.retain(|s| s["Sid"].as_str() != Some(statement_id));
592 if stmts.len() == before {
593 return Err(AwsServiceError::aws_error(
594 StatusCode::BAD_REQUEST,
595 "ResourceNotFoundException",
596 "Statement with the provided id does not exist.",
597 ));
598 }
599 if stmts.is_empty() {
600 bus.policy = None;
601 }
602 }
603
604 Ok(AwsResponse::ok_json(json!({})))
605 }
606
607 fn put_rule(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
610 let body = req.json_body();
611 validate_required("Name", &body["Name"])?;
622 let name = body["Name"]
623 .as_str()
624 .ok_or_else(|| missing("Name"))?
625 .to_string();
626 validate_string_length("Name", &name, 1, 64)?;
627 validate_optional_string_length_value(
628 "ScheduleExpression",
629 &body["ScheduleExpression"],
630 0,
631 256,
632 )?;
633 validate_optional_string_length_value("EventPattern", &body["EventPattern"], 0, 4096)?;
634 if let Some(pattern) = body["EventPattern"].as_str().filter(|s| !s.is_empty()) {
638 validate_event_pattern(pattern)?;
639 }
640 if let Some(schedule) = body["ScheduleExpression"]
644 .as_str()
645 .filter(|s| !s.is_empty())
646 {
647 if !crate::scheduler::is_valid_schedule_expression(schedule) {
648 return Err(AwsServiceError::aws_error(
649 StatusCode::BAD_REQUEST,
650 "ValidationException",
651 format!(
652 "Parameter ScheduleExpression is not valid. Reason: {schedule} is not a valid expression."
653 ),
654 ));
655 }
656 }
657 validate_optional_enum_value(
658 "State",
659 &body["State"],
660 &[
661 "ENABLED",
662 "DISABLED",
663 "ENABLED_WITH_ALL_CLOUDTRAIL_MANAGEMENT_EVENTS",
664 ],
665 )?;
666 validate_optional_string_length_value("Description", &body["Description"], 0, 512)?;
667 validate_optional_string_length_value("RoleArn", &body["RoleArn"], 1, 1600)?;
668 validate_optional_string_length_value("EventBusName", &body["EventBusName"], 1, 1600)?;
669
670 let raw_bus = body["EventBusName"]
671 .as_str()
672 .unwrap_or("default")
673 .to_string();
674
675 let mut accounts = self.state.write();
676 let state = accounts.get_or_create(&req.account_id);
677 let event_bus_name = state.resolve_bus_name(&raw_bus);
678
679 let event_pattern = body["EventPattern"].as_str().and_then(|s| {
680 if s.is_empty() {
681 None
682 } else {
683 Some(s.to_string())
684 }
685 });
686 let schedule_expression = body["ScheduleExpression"].as_str().and_then(|s| {
687 if s.is_empty() {
688 None
689 } else {
690 Some(s.to_string())
691 }
692 });
693 let description = body["Description"].as_str().map(|s| s.to_string());
694 let role_arn = body["RoleArn"].as_str().map(|s| s.to_string());
695 let rule_state = body["State"].as_str().unwrap_or("ENABLED").to_string();
696
697 if !state.buses.contains_key(&event_bus_name) {
703 return Err(AwsServiceError::aws_error(
704 StatusCode::BAD_REQUEST,
705 "ResourceNotFoundException",
706 format!("Event bus {event_bus_name} does not exist."),
707 ));
708 }
709
710 let arn = if event_bus_name == "default" {
711 format!(
712 "arn:aws:events:{}:{}:rule/{}",
713 req.region, state.account_id, name
714 )
715 } else {
716 format!(
717 "arn:aws:events:{}:{}:rule/{}/{}",
718 req.region, state.account_id, event_bus_name, name
719 )
720 };
721
722 let key = (event_bus_name.clone(), name.clone());
723 let (targets, existing_tags, existing_managed_by, existing_created_by) = state
730 .rules
731 .get(&key)
732 .map(|r| {
733 (
734 r.targets.clone(),
735 r.tags.clone(),
736 r.managed_by.clone(),
737 r.created_by.clone(),
738 )
739 })
740 .unwrap_or_default();
741
742 let tags = if body.get("Tags").map(|t| t.is_array()).unwrap_or(false) {
747 parse_tags(&body)
748 } else {
749 existing_tags
750 };
751
752 let rule = EventRule {
753 name: name.clone(),
754 arn: arn.clone(),
755 event_bus_name,
756 event_pattern,
757 schedule_expression,
758 state: rule_state,
759 description,
760 role_arn,
761 managed_by: existing_managed_by,
762 created_by: existing_created_by,
763 targets,
764 tags,
765 last_fired: None,
766 };
767
768 state.rules.insert(key, rule);
769 Ok(AwsResponse::ok_json(json!({ "RuleArn": arn })))
770 }
771
772 fn delete_rule(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
773 let body = req.json_body();
774 validate_required("Name", &body["Name"])?;
775 let name = body["Name"].as_str().ok_or_else(|| missing("Name"))?;
776 validate_string_length("name", name, 1, 64)?;
777 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
778 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
779
780 let mut accounts = self.state.write();
781 let state = accounts.get_or_create(&req.account_id);
782 let bus_name = state.resolve_bus_name(event_bus_name);
783 let key = (bus_name, name.to_string());
784
785 if let Some(rule) = state.rules.get(&key) {
787 if !rule.targets.is_empty() {
788 return Err(AwsServiceError::aws_error(
789 StatusCode::BAD_REQUEST,
790 "ValidationException",
791 "Rule can't be deleted since it has targets.",
792 ));
793 }
794 }
795
796 state.rules.remove(&key);
797 Ok(AwsResponse::ok_json(json!({})))
798 }
799
800 fn list_rules(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
801 let body = req.json_body();
802 validate_optional_string_length("namePrefix", body["NamePrefix"].as_str(), 1, 64)?;
803 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
804 validate_optional_string_length("nextToken", body["NextToken"].as_str(), 1, 2048)?;
805 validate_optional_range_i64("limit", body["Limit"].as_i64(), 1, 100)?;
806 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
807 let name_prefix = body["NamePrefix"].as_str();
808 let limit = body["Limit"].as_u64().map(|n| n as usize);
809 let next_token = body["NextToken"].as_str();
810
811 let accounts = self.state.read();
812 let empty = EventBridgeState::new(&req.account_id, &req.region);
813 let state = accounts.get(&req.account_id).unwrap_or(&empty);
814 let bus_name = state.resolve_bus_name(event_bus_name);
815
816 let mut rules: Vec<&EventRule> = state
817 .rules
818 .values()
819 .filter(|r| r.event_bus_name == bus_name)
820 .filter(|r| match name_prefix {
821 Some(prefix) => r.name.starts_with(prefix),
822 None => true,
823 })
824 .collect();
825 rules.sort_by(|a, b| a.name.cmp(&b.name));
826
827 let start = next_token
829 .and_then(|t| t.parse::<usize>().ok())
830 .unwrap_or(0)
831 .min(rules.len());
832 let rules_slice = &rules[start..];
833
834 let (page, new_next_token) = if let Some(lim) = limit {
835 if rules_slice.len() > lim {
836 (&rules_slice[..lim], Some((start + lim).to_string()))
837 } else {
838 (rules_slice, None)
839 }
840 } else {
841 (rules_slice, None)
842 };
843
844 let rules_json: Vec<Value> = page
845 .iter()
846 .map(|r| {
847 let mut obj = json!({
848 "Name": r.name,
849 "Arn": r.arn,
850 "EventBusName": r.event_bus_name,
851 "State": r.state,
852 });
853 if let Some(ref desc) = r.description {
854 obj["Description"] = json!(desc);
855 }
856 if let Some(ref ep) = r.event_pattern {
857 obj["EventPattern"] = json!(ep);
858 }
859 if let Some(ref se) = r.schedule_expression {
860 obj["ScheduleExpression"] = json!(se);
861 }
862 if let Some(ref role) = r.role_arn {
863 obj["RoleArn"] = json!(role);
864 }
865 if let Some(ref mb) = r.managed_by {
866 obj["ManagedBy"] = json!(mb);
867 }
868 obj
869 })
870 .collect();
871
872 let mut resp = json!({ "Rules": rules_json });
873 if let Some(token) = new_next_token {
874 resp["NextToken"] = json!(token);
875 }
876
877 Ok(AwsResponse::ok_json(resp))
878 }
879
880 fn describe_rule(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
881 let body = req.json_body();
882 validate_required("Name", &body["Name"])?;
883 let name = body["Name"].as_str().ok_or_else(|| missing("Name"))?;
884 validate_string_length("name", name, 1, 64)?;
885 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
886 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
887
888 let accounts = self.state.read();
889 let empty = EventBridgeState::new(&req.account_id, &req.region);
890 let state = accounts.get(&req.account_id).unwrap_or(&empty);
891 let bus_name = state.resolve_bus_name(event_bus_name);
892 let key = (bus_name.clone(), name.to_string());
893
894 let rule = state.rules.get(&key).ok_or_else(|| {
895 AwsServiceError::aws_error(
896 StatusCode::BAD_REQUEST,
897 "ResourceNotFoundException",
898 format!("Rule {name} does not exist."),
899 )
900 })?;
901
902 let mut resp = json!({
903 "Name": rule.name,
904 "Arn": rule.arn,
905 "EventBusName": rule.event_bus_name,
906 "State": rule.state,
907 });
908
909 if let Some(ref desc) = rule.description {
910 resp["Description"] = json!(desc);
911 }
912 if let Some(ref ep) = rule.event_pattern {
913 resp["EventPattern"] = json!(ep);
914 }
915 if let Some(ref se) = rule.schedule_expression {
916 resp["ScheduleExpression"] = json!(se);
917 }
918 if let Some(ref role) = rule.role_arn {
919 resp["RoleArn"] = json!(role);
920 }
921 if let Some(ref mb) = rule.managed_by {
922 resp["ManagedBy"] = json!(mb);
923 }
924 if let Some(ref cb) = rule.created_by {
925 resp["CreatedBy"] = json!(cb);
926 }
927 if rule.event_bus_name != "default" && rule.created_by.is_none() {
929 resp["CreatedBy"] = json!(state.account_id);
930 }
931
932 Ok(AwsResponse::ok_json(resp))
933 }
934
935 fn enable_rule(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
936 let body = req.json_body();
937 validate_required("Name", &body["Name"])?;
938 let name = body["Name"].as_str().ok_or_else(|| missing("Name"))?;
939 validate_string_length("name", name, 1, 64)?;
940 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
941 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
942
943 let mut accounts = self.state.write();
944 let state = accounts.get_or_create(&req.account_id);
945 let bus_name = state.resolve_bus_name(event_bus_name);
946 let key = (bus_name, name.to_string());
947
948 let rule = state.rules.get_mut(&key).ok_or_else(|| {
949 AwsServiceError::aws_error(
950 StatusCode::BAD_REQUEST,
951 "ResourceNotFoundException",
952 format!("Rule {name} does not exist."),
953 )
954 })?;
955
956 rule.state = "ENABLED".to_string();
957 Ok(AwsResponse::ok_json(json!({})))
958 }
959
960 fn disable_rule(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
961 let body = req.json_body();
962 validate_required("Name", &body["Name"])?;
963 let name = body["Name"].as_str().ok_or_else(|| missing("Name"))?;
964 validate_string_length("name", name, 1, 64)?;
965 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
966 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
967
968 let mut accounts = self.state.write();
969 let state = accounts.get_or_create(&req.account_id);
970 let bus_name = state.resolve_bus_name(event_bus_name);
971 let key = (bus_name, name.to_string());
972
973 let rule = state.rules.get_mut(&key).ok_or_else(|| {
974 AwsServiceError::aws_error(
975 StatusCode::BAD_REQUEST,
976 "ResourceNotFoundException",
977 format!("Rule {name} does not exist."),
978 )
979 })?;
980
981 rule.state = "DISABLED".to_string();
982 Ok(AwsResponse::ok_json(json!({})))
983 }
984
985 fn put_targets(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
988 let body = req.json_body();
989 validate_required("Rule", &body["Rule"])?;
996 let rule_name = body["Rule"].as_str().ok_or_else(|| missing("Rule"))?;
997 validate_string_length("Rule", rule_name, 1, 64)?;
998 validate_optional_string_length_value("EventBusName", &body["EventBusName"], 1, 1600)?;
999 validate_required("Targets", &body["Targets"])?;
1004 let targets_array = body["Targets"].as_array().ok_or_else(|| {
1005 AwsServiceError::aws_error(
1006 StatusCode::BAD_REQUEST,
1007 "ValidationException",
1008 "Targets must be a list",
1009 )
1010 })?;
1011 if targets_array.is_empty() || targets_array.len() > 100 {
1012 return Err(AwsServiceError::aws_error(
1013 StatusCode::BAD_REQUEST,
1014 "ValidationException",
1015 "Value at 'Targets' failed to satisfy constraint: \
1016 Member must have length between 1 and 100",
1017 ));
1018 }
1019 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
1020 let targets: Vec<Value> = targets_array.clone();
1021
1022 let mut accounts = self.state.write();
1023 let state = accounts.get_or_create(&req.account_id);
1024 let bus_name = state.resolve_bus_name(event_bus_name);
1025 let key = (bus_name.clone(), rule_name.to_string());
1026
1027 let rule = state.rules.get_mut(&key).ok_or_else(|| {
1028 AwsServiceError::aws_error(
1029 StatusCode::BAD_REQUEST,
1030 "ResourceNotFoundException",
1031 format!("Rule {rule_name} does not exist on EventBus {bus_name}."),
1032 )
1033 })?;
1034
1035 let mut failed_entries: Vec<Value> = Vec::new();
1036 for target in &targets {
1037 let target_id = target["Id"].as_str().unwrap_or("").to_string();
1038 let target_arn = target["Arn"].as_str().unwrap_or("");
1039
1040 if target_arn.ends_with(".fifo") && target.get("SqsParameters").is_none() {
1041 failed_entries.push(json!({
1042 "TargetId": target_id,
1043 "ErrorCode": "ValidationException",
1044 "ErrorMessage": format!(
1045 "Parameter(s) SqsParameters must be specified for target: {target_id}."
1046 ),
1047 }));
1048 continue;
1049 }
1050 if !target_arn.starts_with("arn:") {
1051 failed_entries.push(json!({
1052 "TargetId": target_id,
1053 "ErrorCode": "ValidationException",
1054 "ErrorMessage": format!(
1055 "Parameter {target_arn} is not valid. Reason: Provided Arn is not in correct format."
1056 ),
1057 }));
1058 continue;
1059 }
1060
1061 let input_fields = ["Input", "InputPath", "InputTransformer"]
1068 .iter()
1069 .filter(|k| !target[**k].is_null())
1070 .count();
1071 if input_fields > 1 {
1072 failed_entries.push(json!({
1073 "TargetId": target_id,
1074 "ErrorCode": "ValidationException",
1075 "ErrorMessage": format!(
1076 "Only one of Input, InputPath, or InputTransformer can be \
1077 specified for target: {target_id}."
1078 ),
1079 }));
1080 continue;
1081 }
1082
1083 let et = parse_target(target);
1084 rule.targets.retain(|t| t.id != et.id);
1085 rule.targets.push(et);
1086 }
1087
1088 Ok(AwsResponse::ok_json(json!({
1089 "FailedEntryCount": failed_entries.len(),
1090 "FailedEntries": failed_entries,
1091 })))
1092 }
1093
1094 fn remove_targets(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1095 let body = req.json_body();
1096 validate_required("Rule", &body["Rule"])?;
1097 let rule_name = body["Rule"].as_str().ok_or_else(|| missing("Rule"))?;
1098 validate_string_length("rule", rule_name, 1, 64)?;
1099 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
1100 validate_required("Ids", &body["Ids"])?;
1101 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
1102 let ids = body["Ids"].as_array().ok_or_else(|| missing("Ids"))?;
1103
1104 let target_ids: Vec<String> = ids
1105 .iter()
1106 .filter_map(|v| v.as_str().map(|s| s.to_string()))
1107 .collect();
1108
1109 let mut accounts = self.state.write();
1110 let state = accounts.get_or_create(&req.account_id);
1111 let bus_name = state.resolve_bus_name(event_bus_name);
1112 let key = (bus_name.clone(), rule_name.to_string());
1113
1114 let rule = state.rules.get_mut(&key).ok_or_else(|| {
1115 AwsServiceError::aws_error(
1116 StatusCode::BAD_REQUEST,
1117 "ResourceNotFoundException",
1118 format!("Rule {rule_name} does not exist on EventBus {bus_name}."),
1119 )
1120 })?;
1121
1122 rule.targets.retain(|t| !target_ids.contains(&t.id));
1123
1124 Ok(AwsResponse::ok_json(json!({
1125 "FailedEntryCount": 0,
1126 "FailedEntries": [],
1127 })))
1128 }
1129
1130 fn list_targets_by_rule(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1131 let body = req.json_body();
1132 validate_required("Rule", &body["Rule"])?;
1133 let rule_name = body["Rule"].as_str().ok_or_else(|| missing("Rule"))?;
1134 validate_string_length("rule", rule_name, 1, 64)?;
1135 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
1136 validate_optional_string_length("nextToken", body["NextToken"].as_str(), 1, 2048)?;
1137 validate_optional_range_i64("limit", body["Limit"].as_i64(), 1, 100)?;
1138 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
1139 let limit = body["Limit"].as_u64().map(|n| n as usize);
1140 let next_token = body["NextToken"].as_str();
1141
1142 let accounts = self.state.read();
1143 let empty = EventBridgeState::new(&req.account_id, &req.region);
1144 let state = accounts.get(&req.account_id).unwrap_or(&empty);
1145 let bus_name = state.resolve_bus_name(event_bus_name);
1146 let key = (bus_name, rule_name.to_string());
1147
1148 let rule = state.rules.get(&key).ok_or_else(|| {
1149 AwsServiceError::aws_error(
1150 StatusCode::BAD_REQUEST,
1151 "ResourceNotFoundException",
1152 format!("Rule {rule_name} does not exist."),
1153 )
1154 })?;
1155
1156 let all_targets = &rule.targets;
1157 let start = next_token
1158 .and_then(|t| t.parse::<usize>().ok())
1159 .unwrap_or(0)
1160 .min(all_targets.len());
1161 let slice = &all_targets[start..];
1162
1163 let (page, new_next_token) = if let Some(lim) = limit {
1164 if slice.len() > lim {
1165 (&slice[..lim], Some((start + lim).to_string()))
1166 } else {
1167 (slice, None)
1168 }
1169 } else {
1170 (slice, None)
1171 };
1172
1173 let targets: Vec<Value> = page.iter().map(target_to_json).collect();
1174
1175 let mut resp = json!({ "Targets": targets });
1176 if let Some(token) = new_next_token {
1177 resp["NextToken"] = json!(token);
1178 }
1179
1180 Ok(AwsResponse::ok_json(resp))
1181 }
1182
1183 fn list_rule_names_by_target(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1184 let body = req.json_body();
1185 validate_required("TargetArn", &body["TargetArn"])?;
1186 let target_arn = body["TargetArn"]
1187 .as_str()
1188 .ok_or_else(|| missing("TargetArn"))?;
1189 validate_string_length("targetArn", target_arn, 1, 1600)?;
1190 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
1191 validate_optional_string_length("nextToken", body["NextToken"].as_str(), 1, 2048)?;
1192 validate_optional_range_i64("limit", body["Limit"].as_i64(), 1, 100)?;
1193 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
1194 let limit = body["Limit"].as_u64().map(|n| n as usize);
1195 let next_token = body["NextToken"].as_str();
1196
1197 let accounts = self.state.read();
1198 let empty = EventBridgeState::new(&req.account_id, &req.region);
1199 let state = accounts.get(&req.account_id).unwrap_or(&empty);
1200 let bus_name = state.resolve_bus_name(event_bus_name);
1201
1202 let mut rule_names: Vec<String> = Vec::new();
1204 for rule in state.rules.values() {
1205 if rule.event_bus_name == bus_name
1206 && rule.targets.iter().any(|t| t.arn == target_arn)
1207 && !rule_names.contains(&rule.name)
1208 {
1209 rule_names.push(rule.name.clone());
1210 }
1211 }
1212 rule_names.sort();
1213
1214 let start = next_token
1215 .and_then(|t| t.parse::<usize>().ok())
1216 .unwrap_or(0)
1217 .min(rule_names.len());
1218 let slice = &rule_names[start..];
1219
1220 let (page, new_next_token) = if let Some(lim) = limit {
1221 if slice.len() > lim {
1222 (&slice[..lim], Some((start + lim).to_string()))
1223 } else {
1224 (slice, None)
1225 }
1226 } else {
1227 (slice, None)
1228 };
1229
1230 let mut resp = json!({ "RuleNames": page });
1231 if let Some(token) = new_next_token {
1232 resp["NextToken"] = json!(token);
1233 }
1234
1235 Ok(AwsResponse::ok_json(resp))
1236 }
1237
1238 fn test_event_pattern(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1241 let body = req.json_body();
1242 validate_required("EventPattern", &body["EventPattern"])?;
1243 validate_required("Event", &body["Event"])?;
1244 let event_pattern = body["EventPattern"]
1245 .as_str()
1246 .ok_or_else(|| missing("EventPattern"))?;
1247 let event_str = body["Event"].as_str().ok_or_else(|| missing("Event"))?;
1248
1249 let event: Value = serde_json::from_str(event_str).map_err(|_| {
1251 AwsServiceError::aws_error(
1252 StatusCode::BAD_REQUEST,
1253 "InvalidEventPatternException",
1254 "Event is not valid JSON.",
1255 )
1256 })?;
1257
1258 let pattern: Value = serde_json::from_str(event_pattern).map_err(|_| {
1260 AwsServiceError::aws_error(
1261 StatusCode::BAD_REQUEST,
1262 "InvalidEventPatternException",
1263 "Event pattern is not valid JSON.",
1264 )
1265 })?;
1266
1267 validate_pattern_values(&pattern, "")?;
1271
1272 let result = matches_value(&pattern, &event);
1276
1277 Ok(AwsResponse::ok_json(json!({ "Result": result })))
1278 }
1279
1280 fn update_event_bus(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1283 let body = req.json_body();
1284 validate_optional_string_length("description", body["Description"].as_str(), 0, 512)?;
1285 validate_optional_string_length(
1286 "kmsKeyIdentifier",
1287 body["KmsKeyIdentifier"].as_str(),
1288 0,
1289 2048,
1290 )?;
1291 let name = body["Name"].as_str().unwrap_or("default");
1292
1293 let mut accounts = self.state.write();
1294 let state = accounts.get_or_create(&req.account_id);
1295 let bus = state.buses.get_mut(name).ok_or_else(|| {
1296 AwsServiceError::aws_error(
1297 StatusCode::BAD_REQUEST,
1298 "ResourceNotFoundException",
1299 format!("Event bus {name} does not exist."),
1300 )
1301 })?;
1302
1303 if let Some(desc) = body["Description"].as_str() {
1304 bus.description = Some(desc.to_string());
1305 }
1306 if let Some(kms) = body["KmsKeyIdentifier"].as_str() {
1307 bus.kms_key_identifier = Some(kms.to_string());
1308 }
1309 if let Some(dlc) = body.get("DeadLetterConfig") {
1310 bus.dead_letter_config = Some(dlc.clone());
1311 }
1312 bus.last_modified_time = Utc::now();
1313
1314 let arn = bus.arn.clone();
1315 let bus_name = bus.name.clone();
1316
1317 Ok(AwsResponse::ok_json(json!({
1318 "Arn": arn,
1319 "Name": bus_name,
1320 })))
1321 }
1322
1323 fn put_events(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1326 let body = req.json_body();
1327 validate_optional_string_length_value("EndpointId", &body["EndpointId"], 1, 50)?;
1331 validate_required("Entries", &body["Entries"])?;
1332 let entries_array = body["Entries"].as_array().ok_or_else(|| {
1333 AwsServiceError::aws_error(
1334 StatusCode::BAD_REQUEST,
1335 "ValidationException",
1336 "Entries must be a list",
1337 )
1338 })?;
1339 if entries_array.is_empty() || entries_array.len() > 10 {
1340 return Err(AwsServiceError::aws_error(
1341 StatusCode::BAD_REQUEST,
1342 "ValidationException",
1343 "Value at 'Entries' failed to satisfy constraint: \
1344 Member must have length between 1 and 10",
1345 ));
1346 }
1347 let entries: Vec<Value> = entries_array.clone();
1348 let entries = &entries;
1349
1350 let mut accounts = self.state.write();
1351 let state = accounts.get_or_create(&req.account_id);
1352 let mut result_entries = Vec::new();
1353 let mut events_to_deliver = Vec::new();
1354 let mut failed_count = 0;
1355
1356 for entry in entries {
1357 let source = entry["Source"].as_str().unwrap_or("").to_string();
1358 let detail_type = entry["DetailType"].as_str().unwrap_or("").to_string();
1359 let detail = entry["Detail"].as_str().unwrap_or("").to_string();
1360
1361 if let Err(error) = validate_put_events_entry(&source, &detail_type, &detail) {
1362 failed_count += 1;
1363 result_entries.push(error);
1364 continue;
1365 }
1366
1367 let event_id = uuid::Uuid::new_v4().to_string();
1368 let raw_bus = entry["EventBusName"]
1369 .as_str()
1370 .unwrap_or("default")
1371 .to_string();
1372 let event_bus_name = state.resolve_bus_name(&raw_bus);
1373
1374 let caller_account = req
1380 .principal
1381 .as_ref()
1382 .map(|p| p.account_id.as_str())
1383 .unwrap_or(req.account_id.as_str());
1384 if caller_account != req.account_id {
1385 let bus_policy_value = state
1386 .buses
1387 .get(&event_bus_name)
1388 .and_then(|b| b.policy.clone());
1389 if let Some(policy_value) = bus_policy_value {
1390 let policy_json = serde_json::to_string(&policy_value).unwrap_or_default();
1391 let policy_doc = fakecloud_iam::evaluator::PolicyDocument::parse(&policy_json);
1392 let bus_arn = state
1393 .buses
1394 .get(&event_bus_name)
1395 .map(|b| b.arn.clone())
1396 .unwrap_or_default();
1397 let principal =
1398 req.principal
1399 .clone()
1400 .unwrap_or_else(|| fakecloud_core::auth::Principal {
1401 arn: Arn::global("iam", caller_account, "root").to_string(),
1402 user_id: caller_account.to_string(),
1403 account_id: caller_account.to_string(),
1404 principal_type: fakecloud_core::auth::PrincipalType::Root,
1405 source_identity: None,
1406 tags: None,
1407 });
1408 let context = fakecloud_iam::evaluator::RequestContext {
1409 aws_principal_arn: Some(principal.arn.clone()),
1410 aws_principal_account: Some(principal.account_id.clone()),
1411 ..Default::default()
1412 };
1413 let eval_req = fakecloud_iam::evaluator::EvalRequest {
1414 principal: &principal,
1415 action: "events:PutEvents".to_string(),
1416 resource: bus_arn,
1417 context,
1418 };
1419 let decision = fakecloud_iam::evaluator::evaluate_resource_policy_only(
1420 &policy_doc,
1421 &eval_req,
1422 );
1423 if !matches!(decision, fakecloud_iam::evaluator::Decision::Allow) {
1424 failed_count += 1;
1425 result_entries.push(json!({
1426 "ErrorCode": "AccessDeniedException",
1427 "ErrorMessage": format!(
1428 "User '{}' is not authorized to put events on event bus '{}'",
1429 principal.arn, event_bus_name
1430 ),
1431 }));
1432 continue;
1433 }
1434 }
1435 }
1436
1437 let time = parse_put_events_time(&entry["Time"]);
1438 let resources: Vec<String> = entry["Resources"]
1439 .as_array()
1440 .map(|arr| {
1441 arr.iter()
1442 .filter_map(|v| v.as_str().map(|s| s.to_string()))
1443 .collect()
1444 })
1445 .unwrap_or_default();
1446
1447 let event = PutEvent {
1448 event_id: event_id.clone(),
1449 source: source.clone(),
1450 detail_type: detail_type.clone(),
1451 detail: detail.clone(),
1452 event_bus_name: event_bus_name.clone(),
1453 time,
1454 resources: resources.clone(),
1455 };
1456
1457 archive_matching_event(
1458 state,
1459 &event,
1460 &event_bus_name,
1461 &source,
1462 &detail_type,
1463 &detail,
1464 &req.account_id,
1465 &req.region,
1466 &resources,
1467 );
1468
1469 state.events.push(event);
1470
1471 let matching_targets: Vec<(String, EventTarget)> = state
1474 .rules
1475 .values()
1476 .filter(|r| {
1477 r.event_bus_name == event_bus_name
1478 && r.state == "ENABLED"
1479 && matches_pattern(
1480 r.event_pattern.as_deref(),
1481 &source,
1482 &detail_type,
1483 &detail,
1484 &req.account_id,
1485 &req.region,
1486 &resources,
1487 &event_id,
1488 &time.to_rfc3339(),
1489 )
1490 })
1491 .flat_map(|r| r.targets.iter().map(|t| (r.arn.clone(), t.clone())))
1492 .collect();
1493
1494 if !matching_targets.is_empty() {
1495 events_to_deliver.push((
1496 event_id.clone(),
1497 source,
1498 detail_type,
1499 detail,
1500 time,
1501 resources,
1502 matching_targets,
1503 ));
1504 }
1505
1506 result_entries.push(json!({ "EventId": event_id }));
1507 }
1508
1509 drop(accounts);
1511
1512 for (event_id, source, detail_type, detail, time, resources, targets) in events_to_deliver {
1517 let detail_value: Value = serde_json::from_str(&detail).unwrap_or(json!({}));
1518 let event_json = json!({
1519 "version": "0",
1520 "id": event_id,
1521 "source": source,
1522 "account": req.account_id,
1523 "detail-type": detail_type,
1524 "detail": detail_value,
1525 "time": time.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
1526 "region": req.region,
1527 "resources": resources,
1528 });
1529
1530 let ctx = EventDispatchContext {
1531 state: &self.state,
1532 delivery: &self.delivery,
1533 lambda_state: self.lambda_state.as_ref(),
1534 logs_state: self.logs_state.as_ref(),
1535 container_runtime: &self.container_runtime,
1536 account_id: &req.account_id,
1537 region: &req.region,
1538 };
1539 for (rule_arn, target) in targets {
1540 dispatch_event_target(
1541 &ctx,
1542 &target,
1543 &event_json,
1544 &event_id,
1545 &detail_type,
1546 Some(&rule_arn),
1547 );
1548 }
1549 }
1550
1551 let resp = json!({
1552 "FailedEntryCount": failed_count,
1553 "Entries": result_entries,
1554 });
1555
1556 Ok(AwsResponse::ok_json(resp))
1557 }
1558
1559 fn tag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1562 let body = req.json_body();
1563 validate_required("ResourceARN", &body["ResourceARN"])?;
1564 let arn = body["ResourceARN"]
1565 .as_str()
1566 .ok_or_else(|| missing("ResourceARN"))?;
1567 validate_string_length("resourceARN", arn, 1, 1600)?;
1568 validate_required("Tags", &body["Tags"])?;
1569
1570 let mut accounts = self.state.write();
1571 let state = accounts.get_or_create(&req.account_id);
1572 let tag_map = find_tags_mut(state, arn)?;
1573
1574 fakecloud_core::tags::apply_tags(tag_map, &body, "Tags", "Key", "Value").map_err(|f| {
1575 AwsServiceError::aws_error(
1576 StatusCode::BAD_REQUEST,
1577 "ValidationException",
1578 format!("{f} must be a list"),
1579 )
1580 })?;
1581
1582 Ok(AwsResponse::ok_json(json!({})))
1583 }
1584
1585 fn untag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1586 let body = req.json_body();
1587 validate_required("ResourceARN", &body["ResourceARN"])?;
1588 let arn = body["ResourceARN"]
1589 .as_str()
1590 .ok_or_else(|| missing("ResourceARN"))?;
1591 validate_string_length("resourceARN", arn, 1, 1600)?;
1592 validate_required("TagKeys", &body["TagKeys"])?;
1593
1594 let mut accounts = self.state.write();
1595 let state = accounts.get_or_create(&req.account_id);
1596 let tag_map = find_tags_mut(state, arn)?;
1597
1598 fakecloud_core::tags::remove_tags(tag_map, &body, "TagKeys").map_err(|f| {
1599 AwsServiceError::aws_error(
1600 StatusCode::BAD_REQUEST,
1601 "ValidationException",
1602 format!("{f} must be a list"),
1603 )
1604 })?;
1605
1606 Ok(AwsResponse::ok_json(json!({})))
1607 }
1608
1609 fn list_tags_for_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1610 let body = req.json_body();
1611 validate_required("ResourceARN", &body["ResourceARN"])?;
1612 let arn = body["ResourceARN"]
1613 .as_str()
1614 .ok_or_else(|| missing("ResourceARN"))?;
1615 validate_string_length("resourceARN", arn, 1, 1600)?;
1616
1617 let accounts = self.state.read();
1618 let empty = EventBridgeState::new(&req.account_id, &req.region);
1619 let state = accounts.get(&req.account_id).unwrap_or(&empty);
1620 let tag_map = find_tags(state, arn)?;
1621
1622 let tags = fakecloud_core::tags::tags_to_json(tag_map, "Key", "Value");
1623
1624 Ok(AwsResponse::ok_json(json!({ "Tags": tags })))
1625 }
1626
1627 }
1629
1630struct StartReplayInput {
1640 name: String,
1641 description: Option<String>,
1642 event_source_arn: String,
1643 destination: Value,
1644 destination_arn: String,
1645 event_start_time: DateTime<Utc>,
1646 event_end_time: DateTime<Utc>,
1647}
1648
1649impl StartReplayInput {
1650 fn from_body(body: &Value) -> Result<Self, AwsServiceError> {
1651 let name = body["ReplayName"].as_str().unwrap_or("").to_string();
1659 let description = body["Description"].as_str().map(|s| s.to_string());
1660 let event_source_arn = body["EventSourceArn"].as_str().unwrap_or("").to_string();
1661 let destination = body["Destination"].clone();
1662
1663 let event_start_time = body["EventStartTime"]
1664 .as_f64()
1665 .and_then(|f| DateTime::from_timestamp(f as i64, 0))
1666 .unwrap_or_else(Utc::now);
1667 let event_end_time = body["EventEndTime"]
1668 .as_f64()
1669 .and_then(|f| DateTime::from_timestamp(f as i64, 0))
1670 .unwrap_or_else(Utc::now);
1671
1672 let destination_arn = destination["Arn"].as_str().unwrap_or("").to_string();
1673 if !destination_arn.contains(":event-bus/") {
1674 return Err(AwsServiceError::aws_error(
1678 StatusCode::BAD_REQUEST,
1679 "ResourceNotFoundException",
1680 format!("Destination.Arn {destination_arn} does not point to an event bus."),
1681 ));
1682 }
1683
1684 Ok(Self {
1685 name,
1686 description,
1687 event_source_arn,
1688 destination,
1689 destination_arn,
1690 event_start_time,
1691 event_end_time,
1692 })
1693 }
1694}
1695
1696#[path = "service_archives_replays.rs"]
1697mod service_archives_replays;
1698#[path = "service_connections_apidests.rs"]
1699mod service_connections_apidests;
1700#[path = "service_endpoints.rs"]
1701mod service_endpoints;
1702#[path = "service_partner_sources.rs"]
1703mod service_partner_sources;
1704
1705#[path = "helpers.rs"]
1706pub(crate) mod helpers;
1707pub(crate) use helpers::*;
1708
1709#[cfg(test)]
1710#[path = "service_tests.rs"]
1711mod tests;
1712
1713#[cfg(test)]
1714mod pagination_reject_test {
1715 #[test]
1716 fn paginate_checked_rejects_invalid_token() {
1717 use fakecloud_core::pagination::paginate_checked;
1718 let items: Vec<i32> = (0..5).collect();
1719 assert!(paginate_checked(&items, Some("bad"), 3).is_err());
1720 assert!(paginate_checked(&items, Some("2"), 3).is_ok());
1721 }
1722}