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 = state
724 .rules
725 .get(&key)
726 .map(|r| r.targets.clone())
727 .unwrap_or_default();
728
729 let tags = parse_tags(&body);
730
731 let rule = EventRule {
732 name: name.clone(),
733 arn: arn.clone(),
734 event_bus_name,
735 event_pattern,
736 schedule_expression,
737 state: rule_state,
738 description,
739 role_arn,
740 managed_by: None,
741 created_by: None,
742 targets,
743 tags,
744 last_fired: None,
745 };
746
747 state.rules.insert(key, rule);
748 Ok(AwsResponse::ok_json(json!({ "RuleArn": arn })))
749 }
750
751 fn delete_rule(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
752 let body = req.json_body();
753 validate_required("Name", &body["Name"])?;
754 let name = body["Name"].as_str().ok_or_else(|| missing("Name"))?;
755 validate_string_length("name", name, 1, 64)?;
756 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
757 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
758
759 let mut accounts = self.state.write();
760 let state = accounts.get_or_create(&req.account_id);
761 let bus_name = state.resolve_bus_name(event_bus_name);
762 let key = (bus_name, name.to_string());
763
764 if let Some(rule) = state.rules.get(&key) {
766 if !rule.targets.is_empty() {
767 return Err(AwsServiceError::aws_error(
768 StatusCode::BAD_REQUEST,
769 "ValidationException",
770 "Rule can't be deleted since it has targets.",
771 ));
772 }
773 }
774
775 state.rules.remove(&key);
776 Ok(AwsResponse::ok_json(json!({})))
777 }
778
779 fn list_rules(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
780 let body = req.json_body();
781 validate_optional_string_length("namePrefix", body["NamePrefix"].as_str(), 1, 64)?;
782 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
783 validate_optional_string_length("nextToken", body["NextToken"].as_str(), 1, 2048)?;
784 validate_optional_range_i64("limit", body["Limit"].as_i64(), 1, 100)?;
785 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
786 let name_prefix = body["NamePrefix"].as_str();
787 let limit = body["Limit"].as_u64().map(|n| n as usize);
788 let next_token = body["NextToken"].as_str();
789
790 let accounts = self.state.read();
791 let empty = EventBridgeState::new(&req.account_id, &req.region);
792 let state = accounts.get(&req.account_id).unwrap_or(&empty);
793 let bus_name = state.resolve_bus_name(event_bus_name);
794
795 let mut rules: Vec<&EventRule> = state
796 .rules
797 .values()
798 .filter(|r| r.event_bus_name == bus_name)
799 .filter(|r| match name_prefix {
800 Some(prefix) => r.name.starts_with(prefix),
801 None => true,
802 })
803 .collect();
804 rules.sort_by(|a, b| a.name.cmp(&b.name));
805
806 let start = next_token
808 .and_then(|t| t.parse::<usize>().ok())
809 .unwrap_or(0)
810 .min(rules.len());
811 let rules_slice = &rules[start..];
812
813 let (page, new_next_token) = if let Some(lim) = limit {
814 if rules_slice.len() > lim {
815 (&rules_slice[..lim], Some((start + lim).to_string()))
816 } else {
817 (rules_slice, None)
818 }
819 } else {
820 (rules_slice, None)
821 };
822
823 let rules_json: Vec<Value> = page
824 .iter()
825 .map(|r| {
826 let mut obj = json!({
827 "Name": r.name,
828 "Arn": r.arn,
829 "EventBusName": r.event_bus_name,
830 "State": r.state,
831 });
832 if let Some(ref desc) = r.description {
833 obj["Description"] = json!(desc);
834 }
835 if let Some(ref ep) = r.event_pattern {
836 obj["EventPattern"] = json!(ep);
837 }
838 if let Some(ref se) = r.schedule_expression {
839 obj["ScheduleExpression"] = json!(se);
840 }
841 if let Some(ref role) = r.role_arn {
842 obj["RoleArn"] = json!(role);
843 }
844 if let Some(ref mb) = r.managed_by {
845 obj["ManagedBy"] = json!(mb);
846 }
847 obj
848 })
849 .collect();
850
851 let mut resp = json!({ "Rules": rules_json });
852 if let Some(token) = new_next_token {
853 resp["NextToken"] = json!(token);
854 }
855
856 Ok(AwsResponse::ok_json(resp))
857 }
858
859 fn describe_rule(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
860 let body = req.json_body();
861 validate_required("Name", &body["Name"])?;
862 let name = body["Name"].as_str().ok_or_else(|| missing("Name"))?;
863 validate_string_length("name", name, 1, 64)?;
864 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
865 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
866
867 let accounts = self.state.read();
868 let empty = EventBridgeState::new(&req.account_id, &req.region);
869 let state = accounts.get(&req.account_id).unwrap_or(&empty);
870 let bus_name = state.resolve_bus_name(event_bus_name);
871 let key = (bus_name.clone(), name.to_string());
872
873 let rule = state.rules.get(&key).ok_or_else(|| {
874 AwsServiceError::aws_error(
875 StatusCode::BAD_REQUEST,
876 "ResourceNotFoundException",
877 format!("Rule {name} does not exist."),
878 )
879 })?;
880
881 let mut resp = json!({
882 "Name": rule.name,
883 "Arn": rule.arn,
884 "EventBusName": rule.event_bus_name,
885 "State": rule.state,
886 });
887
888 if let Some(ref desc) = rule.description {
889 resp["Description"] = json!(desc);
890 }
891 if let Some(ref ep) = rule.event_pattern {
892 resp["EventPattern"] = json!(ep);
893 }
894 if let Some(ref se) = rule.schedule_expression {
895 resp["ScheduleExpression"] = json!(se);
896 }
897 if let Some(ref role) = rule.role_arn {
898 resp["RoleArn"] = json!(role);
899 }
900 if let Some(ref mb) = rule.managed_by {
901 resp["ManagedBy"] = json!(mb);
902 }
903 if let Some(ref cb) = rule.created_by {
904 resp["CreatedBy"] = json!(cb);
905 }
906 if rule.event_bus_name != "default" && rule.created_by.is_none() {
908 resp["CreatedBy"] = json!(state.account_id);
909 }
910
911 Ok(AwsResponse::ok_json(resp))
912 }
913
914 fn enable_rule(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
915 let body = req.json_body();
916 validate_required("Name", &body["Name"])?;
917 let name = body["Name"].as_str().ok_or_else(|| missing("Name"))?;
918 validate_string_length("name", name, 1, 64)?;
919 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
920 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
921
922 let mut accounts = self.state.write();
923 let state = accounts.get_or_create(&req.account_id);
924 let bus_name = state.resolve_bus_name(event_bus_name);
925 let key = (bus_name, name.to_string());
926
927 let rule = state.rules.get_mut(&key).ok_or_else(|| {
928 AwsServiceError::aws_error(
929 StatusCode::BAD_REQUEST,
930 "ResourceNotFoundException",
931 format!("Rule {name} does not exist."),
932 )
933 })?;
934
935 rule.state = "ENABLED".to_string();
936 Ok(AwsResponse::ok_json(json!({})))
937 }
938
939 fn disable_rule(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
940 let body = req.json_body();
941 validate_required("Name", &body["Name"])?;
942 let name = body["Name"].as_str().ok_or_else(|| missing("Name"))?;
943 validate_string_length("name", name, 1, 64)?;
944 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
945 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
946
947 let mut accounts = self.state.write();
948 let state = accounts.get_or_create(&req.account_id);
949 let bus_name = state.resolve_bus_name(event_bus_name);
950 let key = (bus_name, name.to_string());
951
952 let rule = state.rules.get_mut(&key).ok_or_else(|| {
953 AwsServiceError::aws_error(
954 StatusCode::BAD_REQUEST,
955 "ResourceNotFoundException",
956 format!("Rule {name} does not exist."),
957 )
958 })?;
959
960 rule.state = "DISABLED".to_string();
961 Ok(AwsResponse::ok_json(json!({})))
962 }
963
964 fn put_targets(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
967 let body = req.json_body();
968 validate_required("Rule", &body["Rule"])?;
975 let rule_name = body["Rule"].as_str().ok_or_else(|| missing("Rule"))?;
976 validate_string_length("Rule", rule_name, 1, 64)?;
977 validate_optional_string_length_value("EventBusName", &body["EventBusName"], 1, 1600)?;
978 validate_required("Targets", &body["Targets"])?;
983 let targets_array = body["Targets"].as_array().ok_or_else(|| {
984 AwsServiceError::aws_error(
985 StatusCode::BAD_REQUEST,
986 "ValidationException",
987 "Targets must be a list",
988 )
989 })?;
990 if targets_array.is_empty() || targets_array.len() > 100 {
991 return Err(AwsServiceError::aws_error(
992 StatusCode::BAD_REQUEST,
993 "ValidationException",
994 "Value at 'Targets' failed to satisfy constraint: \
995 Member must have length between 1 and 100",
996 ));
997 }
998 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
999 let targets: Vec<Value> = targets_array.clone();
1000
1001 let mut accounts = self.state.write();
1002 let state = accounts.get_or_create(&req.account_id);
1003 let bus_name = state.resolve_bus_name(event_bus_name);
1004 let key = (bus_name.clone(), rule_name.to_string());
1005
1006 let rule = state.rules.get_mut(&key).ok_or_else(|| {
1007 AwsServiceError::aws_error(
1008 StatusCode::BAD_REQUEST,
1009 "ResourceNotFoundException",
1010 format!("Rule {rule_name} does not exist on EventBus {bus_name}."),
1011 )
1012 })?;
1013
1014 let mut failed_entries: Vec<Value> = Vec::new();
1015 for target in &targets {
1016 let target_id = target["Id"].as_str().unwrap_or("").to_string();
1017 let target_arn = target["Arn"].as_str().unwrap_or("");
1018
1019 if target_arn.ends_with(".fifo") && target.get("SqsParameters").is_none() {
1020 failed_entries.push(json!({
1021 "TargetId": target_id,
1022 "ErrorCode": "ValidationException",
1023 "ErrorMessage": format!(
1024 "Parameter(s) SqsParameters must be specified for target: {target_id}."
1025 ),
1026 }));
1027 continue;
1028 }
1029 if !target_arn.starts_with("arn:") {
1030 failed_entries.push(json!({
1031 "TargetId": target_id,
1032 "ErrorCode": "ValidationException",
1033 "ErrorMessage": format!(
1034 "Parameter {target_arn} is not valid. Reason: Provided Arn is not in correct format."
1035 ),
1036 }));
1037 continue;
1038 }
1039
1040 let et = parse_target(target);
1041 rule.targets.retain(|t| t.id != et.id);
1042 rule.targets.push(et);
1043 }
1044
1045 Ok(AwsResponse::ok_json(json!({
1046 "FailedEntryCount": failed_entries.len(),
1047 "FailedEntries": failed_entries,
1048 })))
1049 }
1050
1051 fn remove_targets(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1052 let body = req.json_body();
1053 validate_required("Rule", &body["Rule"])?;
1054 let rule_name = body["Rule"].as_str().ok_or_else(|| missing("Rule"))?;
1055 validate_string_length("rule", rule_name, 1, 64)?;
1056 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
1057 validate_required("Ids", &body["Ids"])?;
1058 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
1059 let ids = body["Ids"].as_array().ok_or_else(|| missing("Ids"))?;
1060
1061 let target_ids: Vec<String> = ids
1062 .iter()
1063 .filter_map(|v| v.as_str().map(|s| s.to_string()))
1064 .collect();
1065
1066 let mut accounts = self.state.write();
1067 let state = accounts.get_or_create(&req.account_id);
1068 let bus_name = state.resolve_bus_name(event_bus_name);
1069 let key = (bus_name.clone(), rule_name.to_string());
1070
1071 let rule = state.rules.get_mut(&key).ok_or_else(|| {
1072 AwsServiceError::aws_error(
1073 StatusCode::BAD_REQUEST,
1074 "ResourceNotFoundException",
1075 format!("Rule {rule_name} does not exist on EventBus {bus_name}."),
1076 )
1077 })?;
1078
1079 rule.targets.retain(|t| !target_ids.contains(&t.id));
1080
1081 Ok(AwsResponse::ok_json(json!({
1082 "FailedEntryCount": 0,
1083 "FailedEntries": [],
1084 })))
1085 }
1086
1087 fn list_targets_by_rule(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1088 let body = req.json_body();
1089 validate_required("Rule", &body["Rule"])?;
1090 let rule_name = body["Rule"].as_str().ok_or_else(|| missing("Rule"))?;
1091 validate_string_length("rule", rule_name, 1, 64)?;
1092 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
1093 validate_optional_string_length("nextToken", body["NextToken"].as_str(), 1, 2048)?;
1094 validate_optional_range_i64("limit", body["Limit"].as_i64(), 1, 100)?;
1095 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
1096 let limit = body["Limit"].as_u64().map(|n| n as usize);
1097 let next_token = body["NextToken"].as_str();
1098
1099 let accounts = self.state.read();
1100 let empty = EventBridgeState::new(&req.account_id, &req.region);
1101 let state = accounts.get(&req.account_id).unwrap_or(&empty);
1102 let bus_name = state.resolve_bus_name(event_bus_name);
1103 let key = (bus_name, rule_name.to_string());
1104
1105 let rule = state.rules.get(&key).ok_or_else(|| {
1106 AwsServiceError::aws_error(
1107 StatusCode::BAD_REQUEST,
1108 "ResourceNotFoundException",
1109 format!("Rule {rule_name} does not exist."),
1110 )
1111 })?;
1112
1113 let all_targets = &rule.targets;
1114 let start = next_token
1115 .and_then(|t| t.parse::<usize>().ok())
1116 .unwrap_or(0)
1117 .min(all_targets.len());
1118 let slice = &all_targets[start..];
1119
1120 let (page, new_next_token) = if let Some(lim) = limit {
1121 if slice.len() > lim {
1122 (&slice[..lim], Some((start + lim).to_string()))
1123 } else {
1124 (slice, None)
1125 }
1126 } else {
1127 (slice, None)
1128 };
1129
1130 let targets: Vec<Value> = page.iter().map(target_to_json).collect();
1131
1132 let mut resp = json!({ "Targets": targets });
1133 if let Some(token) = new_next_token {
1134 resp["NextToken"] = json!(token);
1135 }
1136
1137 Ok(AwsResponse::ok_json(resp))
1138 }
1139
1140 fn list_rule_names_by_target(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1141 let body = req.json_body();
1142 validate_required("TargetArn", &body["TargetArn"])?;
1143 let target_arn = body["TargetArn"]
1144 .as_str()
1145 .ok_or_else(|| missing("TargetArn"))?;
1146 validate_string_length("targetArn", target_arn, 1, 1600)?;
1147 validate_optional_string_length("eventBusName", body["EventBusName"].as_str(), 1, 1600)?;
1148 validate_optional_string_length("nextToken", body["NextToken"].as_str(), 1, 2048)?;
1149 validate_optional_range_i64("limit", body["Limit"].as_i64(), 1, 100)?;
1150 let event_bus_name = body["EventBusName"].as_str().unwrap_or("default");
1151 let limit = body["Limit"].as_u64().map(|n| n as usize);
1152 let next_token = body["NextToken"].as_str();
1153
1154 let accounts = self.state.read();
1155 let empty = EventBridgeState::new(&req.account_id, &req.region);
1156 let state = accounts.get(&req.account_id).unwrap_or(&empty);
1157 let bus_name = state.resolve_bus_name(event_bus_name);
1158
1159 let mut rule_names: Vec<String> = Vec::new();
1161 for rule in state.rules.values() {
1162 if rule.event_bus_name == bus_name
1163 && rule.targets.iter().any(|t| t.arn == target_arn)
1164 && !rule_names.contains(&rule.name)
1165 {
1166 rule_names.push(rule.name.clone());
1167 }
1168 }
1169 rule_names.sort();
1170
1171 let start = next_token
1172 .and_then(|t| t.parse::<usize>().ok())
1173 .unwrap_or(0)
1174 .min(rule_names.len());
1175 let slice = &rule_names[start..];
1176
1177 let (page, new_next_token) = if let Some(lim) = limit {
1178 if slice.len() > lim {
1179 (&slice[..lim], Some((start + lim).to_string()))
1180 } else {
1181 (slice, None)
1182 }
1183 } else {
1184 (slice, None)
1185 };
1186
1187 let mut resp = json!({ "RuleNames": page });
1188 if let Some(token) = new_next_token {
1189 resp["NextToken"] = json!(token);
1190 }
1191
1192 Ok(AwsResponse::ok_json(resp))
1193 }
1194
1195 fn test_event_pattern(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1198 let body = req.json_body();
1199 validate_required("EventPattern", &body["EventPattern"])?;
1200 validate_required("Event", &body["Event"])?;
1201 let event_pattern = body["EventPattern"]
1202 .as_str()
1203 .ok_or_else(|| missing("EventPattern"))?;
1204 let event_str = body["Event"].as_str().ok_or_else(|| missing("Event"))?;
1205
1206 let event: Value = serde_json::from_str(event_str).map_err(|_| {
1208 AwsServiceError::aws_error(
1209 StatusCode::BAD_REQUEST,
1210 "InvalidEventPatternException",
1211 "Event is not valid JSON.",
1212 )
1213 })?;
1214
1215 let pattern: Value = serde_json::from_str(event_pattern).map_err(|_| {
1217 AwsServiceError::aws_error(
1218 StatusCode::BAD_REQUEST,
1219 "InvalidEventPatternException",
1220 "Event pattern is not valid JSON.",
1221 )
1222 })?;
1223
1224 validate_pattern_values(&pattern, "")?;
1228
1229 let result = matches_value(&pattern, &event);
1233
1234 Ok(AwsResponse::ok_json(json!({ "Result": result })))
1235 }
1236
1237 fn update_event_bus(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1240 let body = req.json_body();
1241 validate_optional_string_length("description", body["Description"].as_str(), 0, 512)?;
1242 validate_optional_string_length(
1243 "kmsKeyIdentifier",
1244 body["KmsKeyIdentifier"].as_str(),
1245 0,
1246 2048,
1247 )?;
1248 let name = body["Name"].as_str().unwrap_or("default");
1249
1250 let mut accounts = self.state.write();
1251 let state = accounts.get_or_create(&req.account_id);
1252 let bus = state.buses.get_mut(name).ok_or_else(|| {
1253 AwsServiceError::aws_error(
1254 StatusCode::BAD_REQUEST,
1255 "ResourceNotFoundException",
1256 format!("Event bus {name} does not exist."),
1257 )
1258 })?;
1259
1260 if let Some(desc) = body["Description"].as_str() {
1261 bus.description = Some(desc.to_string());
1262 }
1263 if let Some(kms) = body["KmsKeyIdentifier"].as_str() {
1264 bus.kms_key_identifier = Some(kms.to_string());
1265 }
1266 if let Some(dlc) = body.get("DeadLetterConfig") {
1267 bus.dead_letter_config = Some(dlc.clone());
1268 }
1269 bus.last_modified_time = Utc::now();
1270
1271 let arn = bus.arn.clone();
1272 let bus_name = bus.name.clone();
1273
1274 Ok(AwsResponse::ok_json(json!({
1275 "Arn": arn,
1276 "Name": bus_name,
1277 })))
1278 }
1279
1280 fn put_events(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1283 let body = req.json_body();
1284 validate_optional_string_length_value("EndpointId", &body["EndpointId"], 1, 50)?;
1288 validate_required("Entries", &body["Entries"])?;
1289 let entries_array = body["Entries"].as_array().ok_or_else(|| {
1290 AwsServiceError::aws_error(
1291 StatusCode::BAD_REQUEST,
1292 "ValidationException",
1293 "Entries must be a list",
1294 )
1295 })?;
1296 if entries_array.is_empty() || entries_array.len() > 10 {
1297 return Err(AwsServiceError::aws_error(
1298 StatusCode::BAD_REQUEST,
1299 "ValidationException",
1300 "Value at 'Entries' failed to satisfy constraint: \
1301 Member must have length between 1 and 10",
1302 ));
1303 }
1304 let entries: Vec<Value> = entries_array.clone();
1305 let entries = &entries;
1306
1307 let mut accounts = self.state.write();
1308 let state = accounts.get_or_create(&req.account_id);
1309 let mut result_entries = Vec::new();
1310 let mut events_to_deliver = Vec::new();
1311 let mut failed_count = 0;
1312
1313 for entry in entries {
1314 let source = entry["Source"].as_str().unwrap_or("").to_string();
1315 let detail_type = entry["DetailType"].as_str().unwrap_or("").to_string();
1316 let detail = entry["Detail"].as_str().unwrap_or("").to_string();
1317
1318 if let Err(error) = validate_put_events_entry(&source, &detail_type, &detail) {
1319 failed_count += 1;
1320 result_entries.push(error);
1321 continue;
1322 }
1323
1324 let event_id = uuid::Uuid::new_v4().to_string();
1325 let raw_bus = entry["EventBusName"]
1326 .as_str()
1327 .unwrap_or("default")
1328 .to_string();
1329 let event_bus_name = state.resolve_bus_name(&raw_bus);
1330
1331 let caller_account = req
1337 .principal
1338 .as_ref()
1339 .map(|p| p.account_id.as_str())
1340 .unwrap_or(req.account_id.as_str());
1341 if caller_account != req.account_id {
1342 let bus_policy_value = state
1343 .buses
1344 .get(&event_bus_name)
1345 .and_then(|b| b.policy.clone());
1346 if let Some(policy_value) = bus_policy_value {
1347 let policy_json = serde_json::to_string(&policy_value).unwrap_or_default();
1348 let policy_doc = fakecloud_iam::evaluator::PolicyDocument::parse(&policy_json);
1349 let bus_arn = state
1350 .buses
1351 .get(&event_bus_name)
1352 .map(|b| b.arn.clone())
1353 .unwrap_or_default();
1354 let principal =
1355 req.principal
1356 .clone()
1357 .unwrap_or_else(|| fakecloud_core::auth::Principal {
1358 arn: Arn::global("iam", caller_account, "root").to_string(),
1359 user_id: caller_account.to_string(),
1360 account_id: caller_account.to_string(),
1361 principal_type: fakecloud_core::auth::PrincipalType::Root,
1362 source_identity: None,
1363 tags: None,
1364 });
1365 let context = fakecloud_iam::evaluator::RequestContext {
1366 aws_principal_arn: Some(principal.arn.clone()),
1367 aws_principal_account: Some(principal.account_id.clone()),
1368 ..Default::default()
1369 };
1370 let eval_req = fakecloud_iam::evaluator::EvalRequest {
1371 principal: &principal,
1372 action: "events:PutEvents".to_string(),
1373 resource: bus_arn,
1374 context,
1375 };
1376 let decision = fakecloud_iam::evaluator::evaluate_resource_policy_only(
1377 &policy_doc,
1378 &eval_req,
1379 );
1380 if !matches!(decision, fakecloud_iam::evaluator::Decision::Allow) {
1381 failed_count += 1;
1382 result_entries.push(json!({
1383 "ErrorCode": "AccessDeniedException",
1384 "ErrorMessage": format!(
1385 "User '{}' is not authorized to put events on event bus '{}'",
1386 principal.arn, event_bus_name
1387 ),
1388 }));
1389 continue;
1390 }
1391 }
1392 }
1393
1394 let time = parse_put_events_time(&entry["Time"]);
1395 let resources: Vec<String> = entry["Resources"]
1396 .as_array()
1397 .map(|arr| {
1398 arr.iter()
1399 .filter_map(|v| v.as_str().map(|s| s.to_string()))
1400 .collect()
1401 })
1402 .unwrap_or_default();
1403
1404 let event = PutEvent {
1405 event_id: event_id.clone(),
1406 source: source.clone(),
1407 detail_type: detail_type.clone(),
1408 detail: detail.clone(),
1409 event_bus_name: event_bus_name.clone(),
1410 time,
1411 resources: resources.clone(),
1412 };
1413
1414 archive_matching_event(
1415 state,
1416 &event,
1417 &event_bus_name,
1418 &source,
1419 &detail_type,
1420 &detail,
1421 &req.account_id,
1422 &req.region,
1423 &resources,
1424 );
1425
1426 state.events.push(event);
1427
1428 let matching_targets: Vec<(String, EventTarget)> = state
1431 .rules
1432 .values()
1433 .filter(|r| {
1434 r.event_bus_name == event_bus_name
1435 && r.state == "ENABLED"
1436 && matches_pattern(
1437 r.event_pattern.as_deref(),
1438 &source,
1439 &detail_type,
1440 &detail,
1441 &req.account_id,
1442 &req.region,
1443 &resources,
1444 &event_id,
1445 &time.to_rfc3339(),
1446 )
1447 })
1448 .flat_map(|r| r.targets.iter().map(|t| (r.arn.clone(), t.clone())))
1449 .collect();
1450
1451 if !matching_targets.is_empty() {
1452 events_to_deliver.push((
1453 event_id.clone(),
1454 source,
1455 detail_type,
1456 detail,
1457 time,
1458 resources,
1459 matching_targets,
1460 ));
1461 }
1462
1463 result_entries.push(json!({ "EventId": event_id }));
1464 }
1465
1466 drop(accounts);
1468
1469 for (event_id, source, detail_type, detail, time, resources, targets) in events_to_deliver {
1474 let detail_value: Value = serde_json::from_str(&detail).unwrap_or(json!({}));
1475 let event_json = json!({
1476 "version": "0",
1477 "id": event_id,
1478 "source": source,
1479 "account": req.account_id,
1480 "detail-type": detail_type,
1481 "detail": detail_value,
1482 "time": time.format("%Y-%m-%dT%H:%M:%SZ").to_string(),
1483 "region": req.region,
1484 "resources": resources,
1485 });
1486
1487 let ctx = EventDispatchContext {
1488 state: &self.state,
1489 delivery: &self.delivery,
1490 lambda_state: self.lambda_state.as_ref(),
1491 logs_state: self.logs_state.as_ref(),
1492 container_runtime: &self.container_runtime,
1493 account_id: &req.account_id,
1494 region: &req.region,
1495 };
1496 for (rule_arn, target) in targets {
1497 dispatch_event_target(
1498 &ctx,
1499 &target,
1500 &event_json,
1501 &event_id,
1502 &detail_type,
1503 Some(&rule_arn),
1504 );
1505 }
1506 }
1507
1508 let resp = json!({
1509 "FailedEntryCount": failed_count,
1510 "Entries": result_entries,
1511 });
1512
1513 Ok(AwsResponse::ok_json(resp))
1514 }
1515
1516 fn tag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1519 let body = req.json_body();
1520 validate_required("ResourceARN", &body["ResourceARN"])?;
1521 let arn = body["ResourceARN"]
1522 .as_str()
1523 .ok_or_else(|| missing("ResourceARN"))?;
1524 validate_string_length("resourceARN", arn, 1, 1600)?;
1525 validate_required("Tags", &body["Tags"])?;
1526
1527 let mut accounts = self.state.write();
1528 let state = accounts.get_or_create(&req.account_id);
1529 let tag_map = find_tags_mut(state, arn)?;
1530
1531 fakecloud_core::tags::apply_tags(tag_map, &body, "Tags", "Key", "Value").map_err(|f| {
1532 AwsServiceError::aws_error(
1533 StatusCode::BAD_REQUEST,
1534 "ValidationException",
1535 format!("{f} must be a list"),
1536 )
1537 })?;
1538
1539 Ok(AwsResponse::ok_json(json!({})))
1540 }
1541
1542 fn untag_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1543 let body = req.json_body();
1544 validate_required("ResourceARN", &body["ResourceARN"])?;
1545 let arn = body["ResourceARN"]
1546 .as_str()
1547 .ok_or_else(|| missing("ResourceARN"))?;
1548 validate_string_length("resourceARN", arn, 1, 1600)?;
1549 validate_required("TagKeys", &body["TagKeys"])?;
1550
1551 let mut accounts = self.state.write();
1552 let state = accounts.get_or_create(&req.account_id);
1553 let tag_map = find_tags_mut(state, arn)?;
1554
1555 fakecloud_core::tags::remove_tags(tag_map, &body, "TagKeys").map_err(|f| {
1556 AwsServiceError::aws_error(
1557 StatusCode::BAD_REQUEST,
1558 "ValidationException",
1559 format!("{f} must be a list"),
1560 )
1561 })?;
1562
1563 Ok(AwsResponse::ok_json(json!({})))
1564 }
1565
1566 fn list_tags_for_resource(&self, req: &AwsRequest) -> Result<AwsResponse, AwsServiceError> {
1567 let body = req.json_body();
1568 validate_required("ResourceARN", &body["ResourceARN"])?;
1569 let arn = body["ResourceARN"]
1570 .as_str()
1571 .ok_or_else(|| missing("ResourceARN"))?;
1572 validate_string_length("resourceARN", arn, 1, 1600)?;
1573
1574 let accounts = self.state.read();
1575 let empty = EventBridgeState::new(&req.account_id, &req.region);
1576 let state = accounts.get(&req.account_id).unwrap_or(&empty);
1577 let tag_map = find_tags(state, arn)?;
1578
1579 let tags = fakecloud_core::tags::tags_to_json(tag_map, "Key", "Value");
1580
1581 Ok(AwsResponse::ok_json(json!({ "Tags": tags })))
1582 }
1583
1584 }
1586
1587struct StartReplayInput {
1597 name: String,
1598 description: Option<String>,
1599 event_source_arn: String,
1600 destination: Value,
1601 destination_arn: String,
1602 event_start_time: DateTime<Utc>,
1603 event_end_time: DateTime<Utc>,
1604}
1605
1606impl StartReplayInput {
1607 fn from_body(body: &Value) -> Result<Self, AwsServiceError> {
1608 let name = body["ReplayName"].as_str().unwrap_or("").to_string();
1616 let description = body["Description"].as_str().map(|s| s.to_string());
1617 let event_source_arn = body["EventSourceArn"].as_str().unwrap_or("").to_string();
1618 let destination = body["Destination"].clone();
1619
1620 let event_start_time = body["EventStartTime"]
1621 .as_f64()
1622 .and_then(|f| DateTime::from_timestamp(f as i64, 0))
1623 .unwrap_or_else(Utc::now);
1624 let event_end_time = body["EventEndTime"]
1625 .as_f64()
1626 .and_then(|f| DateTime::from_timestamp(f as i64, 0))
1627 .unwrap_or_else(Utc::now);
1628
1629 let destination_arn = destination["Arn"].as_str().unwrap_or("").to_string();
1630 if !destination_arn.contains(":event-bus/") {
1631 return Err(AwsServiceError::aws_error(
1635 StatusCode::BAD_REQUEST,
1636 "ResourceNotFoundException",
1637 format!("Destination.Arn {destination_arn} does not point to an event bus."),
1638 ));
1639 }
1640
1641 Ok(Self {
1642 name,
1643 description,
1644 event_source_arn,
1645 destination,
1646 destination_arn,
1647 event_start_time,
1648 event_end_time,
1649 })
1650 }
1651}
1652
1653#[path = "service_archives_replays.rs"]
1654mod service_archives_replays;
1655#[path = "service_connections_apidests.rs"]
1656mod service_connections_apidests;
1657#[path = "service_endpoints.rs"]
1658mod service_endpoints;
1659#[path = "service_partner_sources.rs"]
1660mod service_partner_sources;
1661
1662#[path = "helpers.rs"]
1663pub(crate) mod helpers;
1664pub(crate) use helpers::*;
1665
1666#[cfg(test)]
1667#[path = "service_tests.rs"]
1668mod tests;
1669
1670#[cfg(test)]
1671mod pagination_reject_test {
1672 #[test]
1673 fn paginate_checked_rejects_invalid_token() {
1674 use fakecloud_core::pagination::paginate_checked;
1675 let items: Vec<i32> = (0..5).collect();
1676 assert!(paginate_checked(&items, Some("bad"), 3).is_err());
1677 assert!(paginate_checked(&items, Some("2"), 3).is_ok());
1678 }
1679}