Skip to main content

fakecloud_ses/
state.rs

1use chrono::{DateTime, Utc};
2use parking_lot::RwLock;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::sync::Arc;
6
7#[derive(Debug, Clone, Serialize)]
8pub struct EmailIdentity {
9    pub identity_name: String,
10    pub identity_type: String,
11    pub verified: bool,
12    pub created_at: DateTime<Utc>,
13    // DKIM attributes
14    pub dkim_signing_enabled: bool,
15    pub dkim_signing_attributes_origin: String,
16    pub dkim_domain_signing_private_key: Option<String>,
17    pub dkim_domain_signing_selector: Option<String>,
18    pub dkim_next_signing_key_length: Option<String>,
19    // Feedback attributes
20    pub email_forwarding_enabled: bool,
21    // Mail-from attributes
22    pub mail_from_domain: Option<String>,
23    pub mail_from_behavior_on_mx_failure: String,
24    // Configuration set association
25    pub configuration_set_name: Option<String>,
26}
27
28#[derive(Debug, Clone, Serialize)]
29pub struct EmailTemplate {
30    pub template_name: String,
31    pub subject: Option<String>,
32    pub html_body: Option<String>,
33    pub text_body: Option<String>,
34    pub created_at: DateTime<Utc>,
35}
36
37#[derive(Debug, Clone, Serialize)]
38pub struct ConfigurationSet {
39    pub name: String,
40    // Sending options
41    pub sending_enabled: bool,
42    // Delivery options
43    pub tls_policy: String,
44    pub sending_pool_name: Option<String>,
45    // Tracking options
46    pub custom_redirect_domain: Option<String>,
47    pub https_policy: Option<String>,
48    // Suppression options
49    pub suppressed_reasons: Vec<String>,
50    // Reputation options
51    pub reputation_metrics_enabled: bool,
52    // VDM options
53    pub vdm_options: Option<serde_json::Value>,
54    // Archiving options
55    pub archive_arn: Option<String>,
56}
57
58#[derive(Debug, Clone, Serialize)]
59pub struct CustomVerificationEmailTemplate {
60    pub template_name: String,
61    pub from_email_address: String,
62    pub template_subject: String,
63    pub template_content: String,
64    pub success_redirection_url: String,
65    pub failure_redirection_url: String,
66    pub created_at: DateTime<Utc>,
67}
68
69#[derive(Debug, Clone, Serialize)]
70pub struct SentEmail {
71    pub message_id: String,
72    pub from: String,
73    pub to: Vec<String>,
74    pub cc: Vec<String>,
75    pub bcc: Vec<String>,
76    pub subject: Option<String>,
77    pub html_body: Option<String>,
78    pub text_body: Option<String>,
79    pub raw_data: Option<String>,
80    pub template_name: Option<String>,
81    pub template_data: Option<String>,
82    pub timestamp: DateTime<Utc>,
83}
84
85#[derive(Debug, Clone, Serialize)]
86pub struct ContactList {
87    pub contact_list_name: String,
88    pub description: Option<String>,
89    pub topics: Vec<Topic>,
90    pub created_at: DateTime<Utc>,
91    pub last_updated_at: DateTime<Utc>,
92}
93
94#[derive(Debug, Clone, Serialize, Deserialize)]
95pub struct Topic {
96    pub topic_name: String,
97    pub display_name: String,
98    pub description: String,
99    pub default_subscription_status: String,
100}
101
102#[derive(Debug, Clone, Serialize)]
103pub struct Contact {
104    pub email_address: String,
105    pub topic_preferences: Vec<TopicPreference>,
106    pub unsubscribe_all: bool,
107    pub attributes_data: Option<String>,
108    pub created_at: DateTime<Utc>,
109    pub last_updated_at: DateTime<Utc>,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct TopicPreference {
114    pub topic_name: String,
115    pub subscription_status: String,
116}
117
118#[derive(Debug, Clone, Serialize)]
119pub struct SuppressedDestination {
120    pub email_address: String,
121    pub reason: String,
122    pub last_update_time: DateTime<Utc>,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct EventDestination {
127    pub name: String,
128    pub enabled: bool,
129    pub matching_event_types: Vec<String>,
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub kinesis_firehose_destination: Option<serde_json::Value>,
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub cloud_watch_destination: Option<serde_json::Value>,
134    #[serde(skip_serializing_if = "Option::is_none")]
135    pub sns_destination: Option<serde_json::Value>,
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub event_bridge_destination: Option<serde_json::Value>,
138    #[serde(skip_serializing_if = "Option::is_none")]
139    pub pinpoint_destination: Option<serde_json::Value>,
140}
141
142#[derive(Debug, Clone, Serialize)]
143pub struct DedicatedIpPool {
144    pub pool_name: String,
145    pub scaling_mode: String,
146}
147
148#[derive(Debug, Clone, Serialize)]
149pub struct DedicatedIp {
150    pub ip: String,
151    pub warmup_status: String,
152    pub warmup_percentage: i32,
153    pub pool_name: String,
154}
155
156#[derive(Debug, Clone, Serialize)]
157pub struct MultiRegionEndpoint {
158    pub endpoint_name: String,
159    pub endpoint_id: String,
160    pub status: String,
161    pub regions: Vec<String>,
162    pub created_at: DateTime<Utc>,
163    pub last_updated_at: DateTime<Utc>,
164}
165
166#[derive(Debug, Clone, Serialize, Default)]
167pub struct AccountDetails {
168    pub mail_type: Option<String>,
169    pub website_url: Option<String>,
170    pub contact_language: Option<String>,
171    pub use_case_description: Option<String>,
172    pub additional_contact_email_addresses: Vec<String>,
173    pub production_access_enabled: Option<bool>,
174}
175
176#[derive(Debug, Clone, Serialize, Default)]
177pub struct AccountSettings {
178    pub sending_enabled: bool,
179    pub dedicated_ip_auto_warmup_enabled: bool,
180    pub suppressed_reasons: Vec<String>,
181    pub vdm_attributes: Option<serde_json::Value>,
182    pub details: Option<AccountDetails>,
183}
184
185#[derive(Debug, Clone, Serialize)]
186pub struct ImportJob {
187    pub job_id: String,
188    pub import_destination: serde_json::Value,
189    pub import_data_source: serde_json::Value,
190    pub job_status: String,
191    pub created_timestamp: DateTime<Utc>,
192    pub completed_timestamp: Option<DateTime<Utc>>,
193    pub processed_records_count: i32,
194    pub failed_records_count: i32,
195}
196
197#[derive(Debug, Clone, Serialize)]
198pub struct ExportJob {
199    pub job_id: String,
200    pub export_source_type: String,
201    pub export_destination: serde_json::Value,
202    pub export_data_source: serde_json::Value,
203    pub job_status: String,
204    pub created_timestamp: DateTime<Utc>,
205    pub completed_timestamp: Option<DateTime<Utc>>,
206}
207
208#[derive(Debug, Clone, Serialize)]
209pub struct Tenant {
210    pub tenant_name: String,
211    pub tenant_id: String,
212    pub tenant_arn: String,
213    pub created_timestamp: DateTime<Utc>,
214    pub sending_status: String,
215    pub tags: Vec<serde_json::Value>,
216}
217
218#[derive(Debug, Clone, Serialize)]
219pub struct TenantResourceAssociation {
220    pub resource_arn: String,
221    pub associated_timestamp: DateTime<Utc>,
222}
223
224#[derive(Debug, Clone, Serialize)]
225pub struct ReputationEntityState {
226    pub reputation_entity_reference: String,
227    pub reputation_entity_type: String,
228    pub reputation_management_policy: Option<String>,
229    pub customer_managed_status: String,
230    pub sending_status_aggregate: String,
231}
232
233// ── SES v1 Receipt Rule types ──
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct ReceiptRuleSet {
237    pub name: String,
238    pub rules: Vec<ReceiptRule>,
239    pub created_at: DateTime<Utc>,
240}
241
242#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct ReceiptRule {
244    pub name: String,
245    pub enabled: bool,
246    pub scan_enabled: bool,
247    pub tls_policy: String,
248    pub recipients: Vec<String>,
249    pub actions: Vec<ReceiptAction>,
250}
251
252#[derive(Debug, Clone, Serialize, Deserialize)]
253pub enum ReceiptAction {
254    S3 {
255        bucket_name: String,
256        object_key_prefix: Option<String>,
257        topic_arn: Option<String>,
258        kms_key_arn: Option<String>,
259    },
260    Sns {
261        topic_arn: String,
262        encoding: Option<String>,
263    },
264    Lambda {
265        function_arn: String,
266        invocation_type: Option<String>,
267        topic_arn: Option<String>,
268    },
269    Bounce {
270        smtp_reply_code: String,
271        message: String,
272        sender: String,
273        status_code: Option<String>,
274        topic_arn: Option<String>,
275    },
276    AddHeader {
277        header_name: String,
278        header_value: String,
279    },
280    Stop {
281        scope: String,
282        topic_arn: Option<String>,
283    },
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
287pub struct ReceiptFilter {
288    pub name: String,
289    pub ip_filter: IpFilter,
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize)]
293pub struct IpFilter {
294    pub cidr: String,
295    pub policy: String, // "Allow" or "Block"
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct InboundEmail {
300    pub message_id: String,
301    pub from: String,
302    pub to: Vec<String>,
303    pub subject: String,
304    pub body: String,
305    pub matched_rules: Vec<String>,
306    pub actions_executed: Vec<String>,
307    pub timestamp: DateTime<Utc>,
308}
309
310pub struct SesState {
311    pub account_id: String,
312    pub region: String,
313    pub identities: HashMap<String, EmailIdentity>,
314    pub configuration_sets: HashMap<String, ConfigurationSet>,
315    pub templates: HashMap<String, EmailTemplate>,
316    pub sent_emails: Vec<SentEmail>,
317    pub contact_lists: HashMap<String, ContactList>,
318    pub contacts: HashMap<String, HashMap<String, Contact>>,
319    /// Tags keyed by resource ARN, value is key→value tag map.
320    pub tags: HashMap<String, HashMap<String, String>>,
321    /// Suppression list: email → suppressed destination info.
322    pub suppressed_destinations: HashMap<String, SuppressedDestination>,
323    /// Event destinations: config set name → list of event destinations.
324    pub event_destinations: HashMap<String, Vec<EventDestination>>,
325    /// Identity policies: identity name → policy name → policy JSON document.
326    pub identity_policies: HashMap<String, HashMap<String, String>>,
327    /// Custom verification email templates: template name → template.
328    pub custom_verification_email_templates: HashMap<String, CustomVerificationEmailTemplate>,
329    /// Dedicated IP pools: pool name → pool.
330    pub dedicated_ip_pools: HashMap<String, DedicatedIpPool>,
331    /// Dedicated IPs: IP address → dedicated IP info.
332    pub dedicated_ips: HashMap<String, DedicatedIp>,
333    /// Multi-region endpoints: endpoint name → endpoint.
334    pub multi_region_endpoints: HashMap<String, MultiRegionEndpoint>,
335    /// Account-level settings (sending, suppression, VDM, details).
336    pub account_settings: AccountSettings,
337    /// Import jobs: job_id → ImportJob.
338    pub import_jobs: HashMap<String, ImportJob>,
339    /// Export jobs: job_id → ExportJob.
340    pub export_jobs: HashMap<String, ExportJob>,
341    /// Tenants: tenant_name → Tenant.
342    pub tenants: HashMap<String, Tenant>,
343    /// Tenant resource associations: tenant_name → Vec<resource_arn>.
344    pub tenant_resource_associations: HashMap<String, Vec<TenantResourceAssociation>>,
345    /// Reputation entities: "type/reference" → ReputationEntity.
346    pub reputation_entities: HashMap<String, ReputationEntityState>,
347    // ── SES v1 Receipt Rule state ──
348    /// Receipt rule sets: name → rule set.
349    pub receipt_rule_sets: HashMap<String, ReceiptRuleSet>,
350    /// Which rule set is active (by name).
351    pub active_receipt_rule_set: Option<String>,
352    /// Receipt filters: name → filter.
353    pub receipt_filters: HashMap<String, ReceiptFilter>,
354    /// Inbound emails processed by the introspection endpoint.
355    pub inbound_emails: Vec<InboundEmail>,
356}
357
358impl SesState {
359    pub fn new(account_id: &str, region: &str) -> Self {
360        Self {
361            account_id: account_id.to_string(),
362            region: region.to_string(),
363            identities: HashMap::new(),
364            configuration_sets: HashMap::new(),
365            templates: HashMap::new(),
366            sent_emails: Vec::new(),
367            contact_lists: HashMap::new(),
368            contacts: HashMap::new(),
369            tags: HashMap::new(),
370            suppressed_destinations: HashMap::new(),
371            event_destinations: HashMap::new(),
372            identity_policies: HashMap::new(),
373            custom_verification_email_templates: HashMap::new(),
374            dedicated_ip_pools: HashMap::new(),
375            dedicated_ips: HashMap::new(),
376            multi_region_endpoints: HashMap::new(),
377            account_settings: AccountSettings {
378                sending_enabled: true,
379                dedicated_ip_auto_warmup_enabled: false,
380                suppressed_reasons: Vec::new(),
381                vdm_attributes: None,
382                details: None,
383            },
384            import_jobs: HashMap::new(),
385            export_jobs: HashMap::new(),
386            tenants: HashMap::new(),
387            tenant_resource_associations: HashMap::new(),
388            reputation_entities: HashMap::new(),
389            receipt_rule_sets: HashMap::new(),
390            active_receipt_rule_set: None,
391            receipt_filters: HashMap::new(),
392            inbound_emails: Vec::new(),
393        }
394    }
395
396    pub fn reset(&mut self) {
397        self.identities.clear();
398        self.configuration_sets.clear();
399        self.templates.clear();
400        self.sent_emails.clear();
401        self.contact_lists.clear();
402        self.contacts.clear();
403        self.tags.clear();
404        self.suppressed_destinations.clear();
405        self.event_destinations.clear();
406        self.identity_policies.clear();
407        self.custom_verification_email_templates.clear();
408        self.dedicated_ip_pools.clear();
409        self.dedicated_ips.clear();
410        self.multi_region_endpoints.clear();
411        self.account_settings = AccountSettings {
412            sending_enabled: true,
413            dedicated_ip_auto_warmup_enabled: false,
414            suppressed_reasons: Vec::new(),
415            vdm_attributes: None,
416            details: None,
417        };
418        self.import_jobs.clear();
419        self.export_jobs.clear();
420        self.tenants.clear();
421        self.tenant_resource_associations.clear();
422        self.reputation_entities.clear();
423        self.receipt_rule_sets.clear();
424        self.active_receipt_rule_set = None;
425        self.receipt_filters.clear();
426        self.inbound_emails.clear();
427    }
428}
429
430pub type SharedSesState = Arc<RwLock<SesState>>;