1use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6use winterbaume_core::{StateChangeNotifier, StateViewError, StatefulService};
7
8use crate::handlers::BackupService;
9use crate::state::BackupState;
10use crate::types::{
11 BackupPlanData, BackupVault, FrameworkData, LegalHoldData, ReportPlanData,
12 RestoreTestingPlanData, RestoreTestingSelectionData, TieringConfigData, VaultAccessPolicy,
13 VaultNotificationConfig,
14};
15
16#[derive(Debug, Clone, Default, Serialize, Deserialize)]
18pub struct BackupStateView {
19 #[serde(default)]
21 pub vaults: HashMap<String, BackupVaultView>,
22 #[serde(default)]
24 pub backup_plans: HashMap<String, BackupPlanView>,
25 #[serde(default)]
27 pub report_plans: HashMap<String, ReportPlanView>,
28 #[serde(default)]
30 pub resource_tags: HashMap<String, HashMap<String, String>>,
31 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
33 pub frameworks: HashMap<String, FrameworkView>,
34 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
36 pub vault_access_policies: HashMap<String, VaultAccessPolicyView>,
37 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
39 pub vault_notifications: HashMap<String, VaultNotificationConfigView>,
40 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
42 pub tiering_configs: HashMap<String, TieringConfigView>,
43 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
45 pub legal_holds: HashMap<String, LegalHoldView>,
46 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
48 pub restore_testing_plans: HashMap<String, RestoreTestingPlanView>,
49 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
51 pub restore_testing_selections: HashMap<String, RestoreTestingSelectionView>,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct BackupVaultView {
57 pub backup_vault_name: String,
58 pub backup_vault_arn: String,
59 pub creation_date: String,
60 pub number_of_recovery_points: i64,
61 pub locked: bool,
62 pub min_retention_days: Option<i64>,
63 pub max_retention_days: Option<i64>,
64 pub lock_date: Option<String>,
65 #[serde(default)]
66 pub tags: HashMap<String, String>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct BackupPlanView {
72 pub backup_plan_id: String,
73 pub backup_plan_arn: String,
74 pub backup_plan_name: String,
75 pub version_id: String,
76 pub creation_date: String,
77 pub backup_plan_json: serde_json::Value,
78 #[serde(default)]
79 pub tags: HashMap<String, String>,
80}
81
82#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct ReportPlanView {
85 pub report_plan_name: String,
86 pub report_plan_arn: String,
87 pub report_plan_description: String,
88 pub report_delivery_channel: serde_json::Value,
89 pub report_setting: serde_json::Value,
90 pub creation_time: String,
91 pub deployment_status: String,
92 #[serde(default)]
93 pub tags: HashMap<String, String>,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize)]
98pub struct FrameworkView {
99 pub framework_name: String,
100 pub framework_arn: String,
101 pub framework_description: String,
102 pub framework_controls: serde_json::Value,
103 pub creation_time: String,
104 pub deployment_status: String,
105 pub number_of_controls: i32,
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct VaultAccessPolicyView {
111 pub backup_vault_name: String,
112 pub backup_vault_arn: String,
113 pub policy: String,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct VaultNotificationConfigView {
119 pub backup_vault_name: String,
120 pub backup_vault_arn: String,
121 pub sns_topic_arn: String,
122 #[serde(default)]
123 pub backup_vault_events: Vec<String>,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct TieringConfigView {
129 pub tiering_configuration_name: String,
130 pub tiering_configuration_arn: String,
131 pub backup_vault_name: String,
132 pub resource_selection: serde_json::Value,
133 pub creation_time: String,
134 pub last_updated_time: String,
135 #[serde(default)]
136 pub creator_request_id: Option<String>,
137 #[serde(default)]
138 pub tags: HashMap<String, String>,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct LegalHoldView {
144 pub legal_hold_id: String,
145 pub legal_hold_arn: String,
146 pub title: String,
147 pub description: String,
148 pub status: String,
149 pub creation_date: String,
150 #[serde(default)]
151 pub cancellation_date: Option<String>,
152 pub recovery_point_selection: serde_json::Value,
153 #[serde(default)]
154 pub tags: HashMap<String, String>,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct RestoreTestingPlanView {
160 pub restore_testing_plan_name: String,
161 pub restore_testing_plan_arn: String,
162 pub schedule_expression: String,
163 #[serde(default)]
164 pub schedule_expression_timezone: Option<String>,
165 #[serde(default)]
166 pub start_window_hours: Option<i32>,
167 pub recovery_point_selection: serde_json::Value,
168 #[serde(default)]
169 pub creator_request_id: Option<String>,
170 pub creation_time: String,
171 pub last_update_time: String,
172 #[serde(default)]
173 pub tags: HashMap<String, String>,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct RestoreTestingSelectionView {
179 pub restore_testing_selection_name: String,
180 pub restore_testing_plan_name: String,
181 pub restore_testing_plan_arn: String,
182 pub iam_role_arn: String,
183 pub protected_resource_type: String,
184 #[serde(default)]
185 pub protected_resource_arns: Vec<String>,
186 pub protected_resource_conditions: serde_json::Value,
187 #[serde(default)]
188 pub restore_metadata_overrides: HashMap<String, String>,
189 #[serde(default)]
190 pub validation_window_hours: Option<i32>,
191 #[serde(default)]
192 pub creator_request_id: Option<String>,
193 pub creation_time: String,
194 pub last_update_time: String,
195}
196
197impl From<&BackupVault> for BackupVaultView {
202 fn from(v: &BackupVault) -> Self {
203 BackupVaultView {
204 backup_vault_name: v.backup_vault_name.clone(),
205 backup_vault_arn: v.backup_vault_arn.clone(),
206 creation_date: v.creation_date.to_rfc3339(),
207 number_of_recovery_points: v.number_of_recovery_points,
208 locked: v.locked,
209 min_retention_days: v.min_retention_days,
210 max_retention_days: v.max_retention_days,
211 lock_date: v.lock_date.as_ref().map(|d| d.to_rfc3339()),
212 tags: v.tags.clone(),
213 }
214 }
215}
216
217impl From<&BackupPlanData> for BackupPlanView {
218 fn from(p: &BackupPlanData) -> Self {
219 BackupPlanView {
220 backup_plan_id: p.backup_plan_id.clone(),
221 backup_plan_arn: p.backup_plan_arn.clone(),
222 backup_plan_name: p.backup_plan_name.clone(),
223 version_id: p.version_id.clone(),
224 creation_date: p.creation_date.to_rfc3339(),
225 backup_plan_json: p.backup_plan_json.clone(),
226 tags: p.tags.clone(),
227 }
228 }
229}
230
231impl From<&ReportPlanData> for ReportPlanView {
232 fn from(r: &ReportPlanData) -> Self {
233 ReportPlanView {
234 report_plan_name: r.report_plan_name.clone(),
235 report_plan_arn: r.report_plan_arn.clone(),
236 report_plan_description: r.report_plan_description.clone(),
237 report_delivery_channel: r.report_delivery_channel.clone(),
238 report_setting: r.report_setting.clone(),
239 creation_time: r.creation_time.to_rfc3339(),
240 deployment_status: r.deployment_status.clone(),
241 tags: r.tags.clone(),
242 }
243 }
244}
245
246impl From<&FrameworkData> for FrameworkView {
247 fn from(f: &FrameworkData) -> Self {
248 FrameworkView {
249 framework_name: f.framework_name.clone(),
250 framework_arn: f.framework_arn.clone(),
251 framework_description: f.framework_description.clone(),
252 framework_controls: f.framework_controls.clone(),
253 creation_time: f.creation_time.to_rfc3339(),
254 deployment_status: f.deployment_status.clone(),
255 number_of_controls: f.number_of_controls,
256 }
257 }
258}
259
260impl From<&VaultAccessPolicy> for VaultAccessPolicyView {
261 fn from(p: &VaultAccessPolicy) -> Self {
262 VaultAccessPolicyView {
263 backup_vault_name: p.backup_vault_name.clone(),
264 backup_vault_arn: p.backup_vault_arn.clone(),
265 policy: p.policy.clone(),
266 }
267 }
268}
269
270impl From<&VaultNotificationConfig> for VaultNotificationConfigView {
271 fn from(n: &VaultNotificationConfig) -> Self {
272 VaultNotificationConfigView {
273 backup_vault_name: n.backup_vault_name.clone(),
274 backup_vault_arn: n.backup_vault_arn.clone(),
275 sns_topic_arn: n.sns_topic_arn.clone(),
276 backup_vault_events: n.backup_vault_events.clone(),
277 }
278 }
279}
280
281impl From<&TieringConfigData> for TieringConfigView {
282 fn from(t: &TieringConfigData) -> Self {
283 TieringConfigView {
284 tiering_configuration_name: t.tiering_configuration_name.clone(),
285 tiering_configuration_arn: t.tiering_configuration_arn.clone(),
286 backup_vault_name: t.backup_vault_name.clone(),
287 resource_selection: t.resource_selection.clone(),
288 creation_time: t.creation_time.to_rfc3339(),
289 last_updated_time: t.last_updated_time.to_rfc3339(),
290 creator_request_id: t.creator_request_id.clone(),
291 tags: t.tags.clone(),
292 }
293 }
294}
295
296impl From<&LegalHoldData> for LegalHoldView {
297 fn from(h: &LegalHoldData) -> Self {
298 LegalHoldView {
299 legal_hold_id: h.legal_hold_id.clone(),
300 legal_hold_arn: h.legal_hold_arn.clone(),
301 title: h.title.clone(),
302 description: h.description.clone(),
303 status: h.status.clone(),
304 creation_date: h.creation_date.to_rfc3339(),
305 cancellation_date: h.cancellation_date.as_ref().map(|d| d.to_rfc3339()),
306 recovery_point_selection: h.recovery_point_selection.clone(),
307 tags: h.tags.clone(),
308 }
309 }
310}
311
312impl From<&RestoreTestingPlanData> for RestoreTestingPlanView {
313 fn from(p: &RestoreTestingPlanData) -> Self {
314 RestoreTestingPlanView {
315 restore_testing_plan_name: p.restore_testing_plan_name.clone(),
316 restore_testing_plan_arn: p.restore_testing_plan_arn.clone(),
317 schedule_expression: p.schedule_expression.clone(),
318 schedule_expression_timezone: p.schedule_expression_timezone.clone(),
319 start_window_hours: p.start_window_hours,
320 recovery_point_selection: p.recovery_point_selection.clone(),
321 creator_request_id: p.creator_request_id.clone(),
322 creation_time: p.creation_time.to_rfc3339(),
323 last_update_time: p.last_update_time.to_rfc3339(),
324 tags: p.tags.clone(),
325 }
326 }
327}
328
329impl From<&RestoreTestingSelectionData> for RestoreTestingSelectionView {
330 fn from(s: &RestoreTestingSelectionData) -> Self {
331 RestoreTestingSelectionView {
332 restore_testing_selection_name: s.restore_testing_selection_name.clone(),
333 restore_testing_plan_name: s.restore_testing_plan_name.clone(),
334 restore_testing_plan_arn: s.restore_testing_plan_arn.clone(),
335 iam_role_arn: s.iam_role_arn.clone(),
336 protected_resource_type: s.protected_resource_type.clone(),
337 protected_resource_arns: s.protected_resource_arns.clone(),
338 protected_resource_conditions: s.protected_resource_conditions.clone(),
339 restore_metadata_overrides: s.restore_metadata_overrides.clone(),
340 validation_window_hours: s.validation_window_hours,
341 creator_request_id: s.creator_request_id.clone(),
342 creation_time: s.creation_time.to_rfc3339(),
343 last_update_time: s.last_update_time.to_rfc3339(),
344 }
345 }
346}
347
348impl From<&BackupState> for BackupStateView {
349 fn from(s: &BackupState) -> Self {
350 let vaults = s
351 .vaults
352 .iter()
353 .map(|(k, v)| (k.clone(), BackupVaultView::from(v)))
354 .collect();
355 let backup_plans = s
356 .backup_plans
357 .iter()
358 .map(|(k, v)| (k.clone(), BackupPlanView::from(v)))
359 .collect();
360 let report_plans = s
361 .report_plans
362 .iter()
363 .map(|(k, v)| (k.clone(), ReportPlanView::from(v)))
364 .collect();
365 let frameworks = s
366 .frameworks
367 .iter()
368 .map(|(k, v)| (k.clone(), FrameworkView::from(v)))
369 .collect();
370 let vault_access_policies = s
371 .vault_access_policies
372 .iter()
373 .map(|(k, v)| (k.clone(), VaultAccessPolicyView::from(v)))
374 .collect();
375 let vault_notifications = s
376 .vault_notifications
377 .iter()
378 .map(|(k, v)| (k.clone(), VaultNotificationConfigView::from(v)))
379 .collect();
380 let tiering_configs = s
381 .tiering_configs
382 .iter()
383 .map(|(k, v)| (k.clone(), TieringConfigView::from(v)))
384 .collect();
385 let legal_holds = s
386 .legal_holds
387 .iter()
388 .map(|(k, v)| (k.clone(), LegalHoldView::from(v)))
389 .collect();
390 let restore_testing_plans = s
391 .restore_testing_plans
392 .iter()
393 .map(|(k, v)| (k.clone(), RestoreTestingPlanView::from(v)))
394 .collect();
395 let restore_testing_selections = s
396 .restore_testing_selections
397 .iter()
398 .map(|((plan, sel), v)| {
399 (
400 format!("{plan}/{sel}"),
401 RestoreTestingSelectionView::from(v),
402 )
403 })
404 .collect();
405 BackupStateView {
406 vaults,
407 backup_plans,
408 report_plans,
409 resource_tags: s.resource_tags.clone(),
410 frameworks,
411 vault_access_policies,
412 vault_notifications,
413 tiering_configs,
414 legal_holds,
415 restore_testing_plans,
416 restore_testing_selections,
417 }
418 }
419}
420
421fn parse_rfc3339_or_now(s: &str) -> chrono::DateTime<chrono::Utc> {
422 use chrono::{DateTime, Utc};
423 DateTime::parse_from_rfc3339(s)
424 .map(|d| d.with_timezone(&Utc))
425 .unwrap_or_else(|_| Utc::now())
426}
427
428fn parse_rfc3339_opt(s: Option<String>) -> Option<chrono::DateTime<chrono::Utc>> {
429 use chrono::{DateTime, Utc};
430 s.as_deref()
431 .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
432 .map(|d| d.with_timezone(&Utc))
433}
434
435impl From<FrameworkView> for FrameworkData {
436 fn from(v: FrameworkView) -> Self {
437 FrameworkData {
438 framework_name: v.framework_name,
439 framework_arn: v.framework_arn,
440 framework_description: v.framework_description,
441 framework_controls: v.framework_controls,
442 creation_time: parse_rfc3339_or_now(&v.creation_time),
443 deployment_status: v.deployment_status,
444 number_of_controls: v.number_of_controls,
445 }
446 }
447}
448
449impl From<VaultAccessPolicyView> for VaultAccessPolicy {
450 fn from(v: VaultAccessPolicyView) -> Self {
451 VaultAccessPolicy {
452 backup_vault_name: v.backup_vault_name,
453 backup_vault_arn: v.backup_vault_arn,
454 policy: v.policy,
455 }
456 }
457}
458
459impl From<VaultNotificationConfigView> for VaultNotificationConfig {
460 fn from(v: VaultNotificationConfigView) -> Self {
461 VaultNotificationConfig {
462 backup_vault_name: v.backup_vault_name,
463 backup_vault_arn: v.backup_vault_arn,
464 sns_topic_arn: v.sns_topic_arn,
465 backup_vault_events: v.backup_vault_events,
466 }
467 }
468}
469
470impl From<TieringConfigView> for TieringConfigData {
471 fn from(v: TieringConfigView) -> Self {
472 TieringConfigData {
473 tiering_configuration_name: v.tiering_configuration_name,
474 tiering_configuration_arn: v.tiering_configuration_arn,
475 backup_vault_name: v.backup_vault_name,
476 resource_selection: v.resource_selection,
477 creation_time: parse_rfc3339_or_now(&v.creation_time),
478 last_updated_time: parse_rfc3339_or_now(&v.last_updated_time),
479 creator_request_id: v.creator_request_id,
480 tags: v.tags,
481 }
482 }
483}
484
485impl From<LegalHoldView> for LegalHoldData {
486 fn from(v: LegalHoldView) -> Self {
487 LegalHoldData {
488 legal_hold_id: v.legal_hold_id,
489 legal_hold_arn: v.legal_hold_arn,
490 title: v.title,
491 description: v.description,
492 status: v.status,
493 creation_date: parse_rfc3339_or_now(&v.creation_date),
494 cancellation_date: parse_rfc3339_opt(v.cancellation_date),
495 recovery_point_selection: v.recovery_point_selection,
496 tags: v.tags,
497 }
498 }
499}
500
501impl From<RestoreTestingPlanView> for RestoreTestingPlanData {
502 fn from(v: RestoreTestingPlanView) -> Self {
503 RestoreTestingPlanData {
504 restore_testing_plan_name: v.restore_testing_plan_name,
505 restore_testing_plan_arn: v.restore_testing_plan_arn,
506 schedule_expression: v.schedule_expression,
507 schedule_expression_timezone: v.schedule_expression_timezone,
508 start_window_hours: v.start_window_hours,
509 recovery_point_selection: v.recovery_point_selection,
510 creator_request_id: v.creator_request_id,
511 creation_time: parse_rfc3339_or_now(&v.creation_time),
512 last_update_time: parse_rfc3339_or_now(&v.last_update_time),
513 tags: v.tags,
514 }
515 }
516}
517
518impl From<RestoreTestingSelectionView> for RestoreTestingSelectionData {
519 fn from(v: RestoreTestingSelectionView) -> Self {
520 RestoreTestingSelectionData {
521 restore_testing_selection_name: v.restore_testing_selection_name,
522 restore_testing_plan_name: v.restore_testing_plan_name,
523 restore_testing_plan_arn: v.restore_testing_plan_arn,
524 iam_role_arn: v.iam_role_arn,
525 protected_resource_type: v.protected_resource_type,
526 protected_resource_arns: v.protected_resource_arns,
527 protected_resource_conditions: v.protected_resource_conditions,
528 restore_metadata_overrides: v.restore_metadata_overrides,
529 validation_window_hours: v.validation_window_hours,
530 creator_request_id: v.creator_request_id,
531 creation_time: parse_rfc3339_or_now(&v.creation_time),
532 last_update_time: parse_rfc3339_or_now(&v.last_update_time),
533 }
534 }
535}
536
537impl StatefulService for BackupService {
542 type StateView = BackupStateView;
543
544 async fn snapshot(&self, account_id: &str, region: &str) -> Self::StateView {
545 let state = self.state.get(account_id, region);
546 let guard = state.read().await;
547 BackupStateView::from(&*guard)
548 }
549
550 async fn restore(
551 &self,
552 account_id: &str,
553 region: &str,
554 view: Self::StateView,
555 ) -> Result<(), StateViewError> {
556 use chrono::{DateTime, Utc};
557
558 let mut new_state = BackupState::default();
559
560 for (name, vv) in view.vaults {
561 let creation_date = DateTime::parse_from_rfc3339(&vv.creation_date)
562 .map(|d| d.with_timezone(&Utc))
563 .unwrap_or_else(|_| Utc::now());
564 let lock_date = vv
565 .lock_date
566 .as_deref()
567 .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
568 .map(|d| d.with_timezone(&Utc));
569 new_state.vaults.insert(
570 name,
571 BackupVault {
572 backup_vault_name: vv.backup_vault_name,
573 backup_vault_arn: vv.backup_vault_arn,
574 creation_date,
575 number_of_recovery_points: vv.number_of_recovery_points,
576 locked: vv.locked,
577 min_retention_days: vv.min_retention_days,
578 max_retention_days: vv.max_retention_days,
579 lock_date,
580 tags: vv.tags,
581 },
582 );
583 }
584
585 for (id, pv) in view.backup_plans {
586 let creation_date = DateTime::parse_from_rfc3339(&pv.creation_date)
587 .map(|d| d.with_timezone(&Utc))
588 .unwrap_or_else(|_| Utc::now());
589 new_state.backup_plans.insert(
590 id,
591 BackupPlanData {
592 backup_plan_id: pv.backup_plan_id,
593 backup_plan_arn: pv.backup_plan_arn,
594 backup_plan_name: pv.backup_plan_name,
595 version_id: pv.version_id,
596 creation_date,
597 backup_plan_json: pv.backup_plan_json,
598 tags: pv.tags,
599 },
600 );
601 }
602
603 for (name, rv) in view.report_plans {
604 let creation_time = DateTime::parse_from_rfc3339(&rv.creation_time)
605 .map(|d| d.with_timezone(&Utc))
606 .unwrap_or_else(|_| Utc::now());
607 new_state.report_plans.insert(
608 name,
609 ReportPlanData {
610 report_plan_name: rv.report_plan_name,
611 report_plan_arn: rv.report_plan_arn,
612 report_plan_description: rv.report_plan_description,
613 report_delivery_channel: rv.report_delivery_channel,
614 report_setting: rv.report_setting,
615 creation_time,
616 deployment_status: rv.deployment_status,
617 tags: rv.tags,
618 },
619 );
620 }
621
622 new_state.resource_tags = view.resource_tags;
623
624 for (k, v) in view.frameworks {
625 new_state.frameworks.insert(k, FrameworkData::from(v));
626 }
627 for (k, v) in view.vault_access_policies {
628 new_state
629 .vault_access_policies
630 .insert(k, VaultAccessPolicy::from(v));
631 }
632 for (k, v) in view.vault_notifications {
633 new_state
634 .vault_notifications
635 .insert(k, VaultNotificationConfig::from(v));
636 }
637 for (k, v) in view.tiering_configs {
638 new_state
639 .tiering_configs
640 .insert(k, TieringConfigData::from(v));
641 }
642 for (k, v) in view.legal_holds {
643 new_state.legal_holds.insert(k, LegalHoldData::from(v));
644 }
645 for (k, v) in view.restore_testing_plans {
646 new_state
647 .restore_testing_plans
648 .insert(k, RestoreTestingPlanData::from(v));
649 }
650 for (key, v) in view.restore_testing_selections {
651 let (plan, sel) = match key.split_once('/') {
653 Some((p, s)) => (p.to_string(), s.to_string()),
654 None => (
655 v.restore_testing_plan_name.clone(),
656 v.restore_testing_selection_name.clone(),
657 ),
658 };
659 new_state
660 .restore_testing_selections
661 .insert((plan, sel), RestoreTestingSelectionData::from(v));
662 }
663
664 {
665 let state = self.state.get(account_id, region);
666 *state.write().await = new_state;
667 }
668 self.notify_state_changed(account_id, region).await;
669 Ok(())
670 }
671
672 async fn merge(
673 &self,
674 account_id: &str,
675 region: &str,
676 view: Self::StateView,
677 ) -> Result<(), StateViewError> {
678 use chrono::{DateTime, Utc};
679
680 let state = self.state.get(account_id, region);
681 {
682 let mut guard = state.write().await;
683
684 for (name, vv) in view.vaults {
685 let creation_date = DateTime::parse_from_rfc3339(&vv.creation_date)
686 .map(|d| d.with_timezone(&Utc))
687 .unwrap_or_else(|_| Utc::now());
688 let lock_date = vv
689 .lock_date
690 .as_deref()
691 .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
692 .map(|d| d.with_timezone(&Utc));
693 guard.vaults.insert(
694 name,
695 BackupVault {
696 backup_vault_name: vv.backup_vault_name,
697 backup_vault_arn: vv.backup_vault_arn,
698 creation_date,
699 number_of_recovery_points: vv.number_of_recovery_points,
700 locked: vv.locked,
701 min_retention_days: vv.min_retention_days,
702 max_retention_days: vv.max_retention_days,
703 lock_date,
704 tags: vv.tags,
705 },
706 );
707 }
708
709 for (id, pv) in view.backup_plans {
710 let creation_date = DateTime::parse_from_rfc3339(&pv.creation_date)
711 .map(|d| d.with_timezone(&Utc))
712 .unwrap_or_else(|_| Utc::now());
713 guard.backup_plans.insert(
714 id,
715 BackupPlanData {
716 backup_plan_id: pv.backup_plan_id,
717 backup_plan_arn: pv.backup_plan_arn,
718 backup_plan_name: pv.backup_plan_name,
719 version_id: pv.version_id,
720 creation_date,
721 backup_plan_json: pv.backup_plan_json,
722 tags: pv.tags,
723 },
724 );
725 }
726
727 for (name, rv) in view.report_plans {
728 let creation_time = DateTime::parse_from_rfc3339(&rv.creation_time)
729 .map(|d| d.with_timezone(&Utc))
730 .unwrap_or_else(|_| Utc::now());
731 guard.report_plans.insert(
732 name,
733 ReportPlanData {
734 report_plan_name: rv.report_plan_name,
735 report_plan_arn: rv.report_plan_arn,
736 report_plan_description: rv.report_plan_description,
737 report_delivery_channel: rv.report_delivery_channel,
738 report_setting: rv.report_setting,
739 creation_time,
740 deployment_status: rv.deployment_status,
741 tags: rv.tags,
742 },
743 );
744 }
745
746 for (arn, tags) in view.resource_tags {
747 guard.resource_tags.entry(arn).or_default().extend(tags);
748 }
749
750 for (k, v) in view.frameworks {
751 guard.frameworks.insert(k, FrameworkData::from(v));
752 }
753 for (k, v) in view.vault_access_policies {
754 guard
755 .vault_access_policies
756 .insert(k, VaultAccessPolicy::from(v));
757 }
758 for (k, v) in view.vault_notifications {
759 guard
760 .vault_notifications
761 .insert(k, VaultNotificationConfig::from(v));
762 }
763 for (k, v) in view.tiering_configs {
764 guard.tiering_configs.insert(k, TieringConfigData::from(v));
765 }
766 for (k, v) in view.legal_holds {
767 guard.legal_holds.insert(k, LegalHoldData::from(v));
768 }
769 for (k, v) in view.restore_testing_plans {
770 guard
771 .restore_testing_plans
772 .insert(k, RestoreTestingPlanData::from(v));
773 }
774 for (key, v) in view.restore_testing_selections {
775 let (plan, sel) = match key.split_once('/') {
776 Some((p, s)) => (p.to_string(), s.to_string()),
777 None => (
778 v.restore_testing_plan_name.clone(),
779 v.restore_testing_selection_name.clone(),
780 ),
781 };
782 guard
783 .restore_testing_selections
784 .insert((plan, sel), RestoreTestingSelectionData::from(v));
785 }
786 }
787 self.notify_state_changed(account_id, region).await;
788 Ok(())
789 }
790
791 fn notifier(&self) -> &StateChangeNotifier<Self::StateView> {
792 &self.notifier
793 }
794}