1use std::{future::Future, pin::Pin, sync::Arc};
11
12use base64::Engine;
13use bytes::Bytes;
14use rustack_ses_http::{
15 body::SesResponseBody,
16 dispatch::SesHandler,
17 request::{
18 get_optional_bool, get_optional_param, get_required_param, parse_form_params,
19 parse_member_list, parse_tag_list,
20 },
21 response::{XmlWriter, xml_response},
22};
23use rustack_ses_model::{
24 error::SesError,
25 input::{
26 CloneReceiptRuleSetInput, CreateConfigurationSetEventDestinationInput,
27 CreateConfigurationSetInput, CreateReceiptRuleInput, CreateReceiptRuleSetInput,
28 CreateTemplateInput, DeleteConfigurationSetEventDestinationInput,
29 DeleteConfigurationSetInput, DeleteIdentityInput, DeleteIdentityPolicyInput,
30 DeleteReceiptRuleInput, DeleteReceiptRuleSetInput, DeleteTemplateInput,
31 DeleteVerifiedEmailAddressInput, DescribeActiveReceiptRuleSetInput,
32 DescribeConfigurationSetInput, DescribeReceiptRuleSetInput, GetIdentityDkimAttributesInput,
33 GetIdentityMailFromDomainAttributesInput, GetIdentityNotificationAttributesInput,
34 GetIdentityPoliciesInput, GetIdentityVerificationAttributesInput, GetTemplateInput,
35 ListConfigurationSetsInput, ListIdentitiesInput, ListIdentityPoliciesInput,
36 ListTemplatesInput, PutIdentityPolicyInput, SendEmailInput, SendRawEmailInput,
37 SendTemplatedEmailInput, SetActiveReceiptRuleSetInput,
38 SetIdentityFeedbackForwardingEnabledInput, SetIdentityMailFromDomainInput,
39 SetIdentityNotificationTopicInput, UpdateConfigurationSetEventDestinationInput,
40 UpdateTemplateInput, VerifyDomainDkimInput, VerifyDomainIdentityInput,
41 VerifyEmailAddressInput, VerifyEmailIdentityInput,
42 },
43 operations::SesOperation,
44 types::{
45 BehaviorOnMXFailure, Body, ConfigurationSet, Content, Destination, EventDestination,
46 IdentityType, Message, MessageTag, NotificationType, RawMessage, ReceiptRule, Template,
47 },
48};
49
50use crate::provider::RustackSes;
51
52#[derive(Debug)]
54pub struct RustackSesHandler {
55 provider: Arc<RustackSes>,
56}
57
58impl RustackSesHandler {
59 #[must_use]
61 pub fn new(provider: Arc<RustackSes>) -> Self {
62 Self { provider }
63 }
64
65 #[must_use]
67 pub fn provider(&self) -> &RustackSes {
68 &self.provider
69 }
70}
71
72impl SesHandler for RustackSesHandler {
73 fn handle_operation(
74 &self,
75 op: SesOperation,
76 body: Bytes,
77 ) -> Pin<Box<dyn Future<Output = Result<http::Response<SesResponseBody>, SesError>> + Send>>
78 {
79 let provider = Arc::clone(&self.provider);
80 Box::pin(async move { dispatch(&provider, op, &body) })
81 }
82
83 fn handle_v2_operation(
84 &self,
85 _method: http::Method,
86 _path: String,
87 _body: Bytes,
88 ) -> Pin<Box<dyn Future<Output = Result<http::Response<SesResponseBody>, SesError>> + Send>>
89 {
90 Box::pin(async { Err(SesError::not_implemented("SES v2")) })
91 }
92
93 fn query_emails(&self, filter_id: Option<&str>, filter_source: Option<&str>) -> String {
94 let messages = self.provider.emails.query(filter_id, filter_source);
95 let body = serde_json::json!({ "messages": messages });
96 body.to_string()
97 }
98
99 fn clear_emails(&self, filter_id: Option<&str>) {
100 if let Some(id) = filter_id {
101 self.provider.emails.remove(id);
102 } else {
103 self.provider.emails.clear();
104 }
105 }
106}
107
108#[allow(clippy::too_many_lines)]
110fn dispatch(
111 provider: &RustackSes,
112 op: SesOperation,
113 body: &[u8],
114) -> Result<http::Response<SesResponseBody>, SesError> {
115 let params = parse_form_params(body);
116 let request_id = uuid::Uuid::new_v4().to_string();
117
118 match op {
119 SesOperation::VerifyEmailIdentity => {
123 let input = VerifyEmailIdentityInput {
124 email_address: get_required_param(¶ms, "EmailAddress")?.to_owned(),
125 };
126 provider.verify_email_identity(input)?;
127 Ok(empty_xml_response("VerifyEmailIdentity", &request_id))
128 }
129
130 SesOperation::VerifyDomainIdentity => {
131 let input = VerifyDomainIdentityInput {
132 domain: get_required_param(¶ms, "Domain")?.to_owned(),
133 };
134 let output = provider.verify_domain_identity(input)?;
135 let mut w = XmlWriter::new();
136 w.start_response("VerifyDomainIdentity");
137 w.start_result("VerifyDomainIdentity");
138 w.write_element("VerificationToken", &output.verification_token);
139 w.end_element("VerifyDomainIdentityResult");
140 w.write_response_metadata(&request_id);
141 w.end_element("VerifyDomainIdentityResponse");
142 Ok(xml_response(w.into_string(), &request_id))
143 }
144
145 SesOperation::ListIdentities => {
146 let identity_type = get_optional_param(¶ms, "IdentityType").map(|s| {
147 if s == "EmailAddress" {
148 IdentityType::EmailAddress
149 } else {
150 IdentityType::Domain
151 }
152 });
153 let input = ListIdentitiesInput {
154 identity_type,
155 max_items: None,
156 next_token: None,
157 };
158 let output = provider.list_identities(input)?;
159 let mut w = XmlWriter::new();
160 w.start_response("ListIdentities");
161 w.start_result("ListIdentities");
162 w.raw("<Identities>");
163 for identity in &output.identities {
164 w.write_element("member", identity);
165 }
166 w.raw("</Identities>");
167 w.write_optional_element("NextToken", output.next_token.as_deref());
168 w.end_element("ListIdentitiesResult");
169 w.write_response_metadata(&request_id);
170 w.end_element("ListIdentitiesResponse");
171 Ok(xml_response(w.into_string(), &request_id))
172 }
173
174 SesOperation::DeleteIdentity => {
175 let input = DeleteIdentityInput {
176 identity: get_required_param(¶ms, "Identity")?.to_owned(),
177 };
178 provider.delete_identity(input)?;
179 Ok(empty_xml_response("DeleteIdentity", &request_id))
180 }
181
182 SesOperation::GetIdentityVerificationAttributes => {
183 let identities = parse_member_list(¶ms, "Identities");
184 let input = GetIdentityVerificationAttributesInput { identities };
185 let output = provider.get_identity_verification_attributes(input)?;
186 let mut w = XmlWriter::new();
187 w.start_response("GetIdentityVerificationAttributes");
188 w.start_result("GetIdentityVerificationAttributes");
189 w.raw("<VerificationAttributes>");
190 for (identity, attrs) in &output.verification_attributes {
191 w.raw("<entry>");
192 w.write_element("key", identity);
193 w.raw("<value>");
194 w.write_element("VerificationStatus", attrs.verification_status.as_str());
195 w.write_optional_element("VerificationToken", attrs.verification_token.as_deref());
196 w.raw("</value>");
197 w.raw("</entry>");
198 }
199 w.raw("</VerificationAttributes>");
200 w.end_element("GetIdentityVerificationAttributesResult");
201 w.write_response_metadata(&request_id);
202 w.end_element("GetIdentityVerificationAttributesResponse");
203 Ok(xml_response(w.into_string(), &request_id))
204 }
205
206 SesOperation::VerifyEmailAddress => {
207 let input = VerifyEmailAddressInput {
208 email_address: get_required_param(¶ms, "EmailAddress")?.to_owned(),
209 };
210 provider.verify_email_address(input)?;
211 Ok(empty_xml_response("VerifyEmailAddress", &request_id))
212 }
213
214 SesOperation::DeleteVerifiedEmailAddress => {
215 let input = DeleteVerifiedEmailAddressInput {
216 email_address: get_required_param(¶ms, "EmailAddress")?.to_owned(),
217 };
218 provider.delete_verified_email_address(input)?;
219 Ok(empty_xml_response(
220 "DeleteVerifiedEmailAddress",
221 &request_id,
222 ))
223 }
224
225 SesOperation::ListVerifiedEmailAddresses => {
226 let output = provider.list_verified_email_addresses()?;
227 let mut w = XmlWriter::new();
228 w.start_response("ListVerifiedEmailAddresses");
229 w.start_result("ListVerifiedEmailAddresses");
230 w.raw("<VerifiedEmailAddresses>");
231 for email in &output.verified_email_addresses {
232 w.write_element("member", email);
233 }
234 w.raw("</VerifiedEmailAddresses>");
235 w.end_element("ListVerifiedEmailAddressesResult");
236 w.write_response_metadata(&request_id);
237 w.end_element("ListVerifiedEmailAddressesResponse");
238 Ok(xml_response(w.into_string(), &request_id))
239 }
240
241 SesOperation::SendEmail => {
242 let input = deserialize_send_email(¶ms)?;
243 let output = provider.send_email(input)?;
244 let mut w = XmlWriter::new();
245 w.start_response("SendEmail");
246 w.start_result("SendEmail");
247 w.write_element("MessageId", &output.message_id);
248 w.end_element("SendEmailResult");
249 w.write_response_metadata(&request_id);
250 w.end_element("SendEmailResponse");
251 Ok(xml_response(w.into_string(), &request_id))
252 }
253
254 SesOperation::SendRawEmail => {
255 let input = deserialize_send_raw_email(¶ms)?;
256 let output = provider.send_raw_email(input)?;
257 let mut w = XmlWriter::new();
258 w.start_response("SendRawEmail");
259 w.start_result("SendRawEmail");
260 w.write_element("MessageId", &output.message_id);
261 w.end_element("SendRawEmailResult");
262 w.write_response_metadata(&request_id);
263 w.end_element("SendRawEmailResponse");
264 Ok(xml_response(w.into_string(), &request_id))
265 }
266
267 SesOperation::GetSendQuota => {
268 let output = provider.get_send_quota()?;
269 let mut w = XmlWriter::new();
270 w.start_response("GetSendQuota");
271 w.start_result("GetSendQuota");
272 if let Some(v) = output.max24_hour_send {
273 w.write_f64_element("Max24HourSend", v);
274 }
275 if let Some(v) = output.max_send_rate {
276 w.write_f64_element("MaxSendRate", v);
277 }
278 if let Some(v) = output.sent_last24_hours {
279 w.write_f64_element("SentLast24Hours", v);
280 }
281 w.end_element("GetSendQuotaResult");
282 w.write_response_metadata(&request_id);
283 w.end_element("GetSendQuotaResponse");
284 Ok(xml_response(w.into_string(), &request_id))
285 }
286
287 SesOperation::GetSendStatistics => {
288 let output = provider.get_send_statistics()?;
289 let mut w = XmlWriter::new();
290 w.start_response("GetSendStatistics");
291 w.start_result("GetSendStatistics");
292 w.raw("<SendDataPoints>");
293 for dp in &output.send_data_points {
294 w.raw("<member>");
295 if let Some(v) = dp.delivery_attempts {
296 w.write_i64_element("DeliveryAttempts", v);
297 }
298 if let Some(v) = dp.bounces {
299 w.write_i64_element("Bounces", v);
300 }
301 if let Some(v) = dp.complaints {
302 w.write_i64_element("Complaints", v);
303 }
304 if let Some(v) = dp.rejects {
305 w.write_i64_element("Rejects", v);
306 }
307 if let Some(ts) = &dp.timestamp {
308 w.write_element("Timestamp", &ts.to_rfc3339());
309 }
310 w.raw("</member>");
311 }
312 w.raw("</SendDataPoints>");
313 w.end_element("GetSendStatisticsResult");
314 w.write_response_metadata(&request_id);
315 w.end_element("GetSendStatisticsResponse");
316 Ok(xml_response(w.into_string(), &request_id))
317 }
318
319 SesOperation::CreateTemplate => {
323 let input = deserialize_create_template(¶ms)?;
324 provider.create_template(input)?;
325 Ok(empty_xml_response("CreateTemplate", &request_id))
326 }
327
328 SesOperation::GetTemplate => {
329 let input = GetTemplateInput {
330 template_name: get_required_param(¶ms, "TemplateName")?.to_owned(),
331 };
332 let output = provider.get_template(input)?;
333 let mut w = XmlWriter::new();
334 w.start_response("GetTemplate");
335 w.start_result("GetTemplate");
336 if let Some(t) = &output.template {
337 w.raw("<Template>");
338 w.write_element("TemplateName", &t.template_name);
339 w.write_optional_element("SubjectPart", t.subject_part.as_deref());
340 w.write_optional_element("TextPart", t.text_part.as_deref());
341 w.write_optional_element("HtmlPart", t.html_part.as_deref());
342 w.raw("</Template>");
343 }
344 w.end_element("GetTemplateResult");
345 w.write_response_metadata(&request_id);
346 w.end_element("GetTemplateResponse");
347 Ok(xml_response(w.into_string(), &request_id))
348 }
349
350 SesOperation::UpdateTemplate => {
351 let input = deserialize_update_template(¶ms)?;
352 provider.update_template(input)?;
353 Ok(empty_xml_response("UpdateTemplate", &request_id))
354 }
355
356 SesOperation::DeleteTemplate => {
357 let input = DeleteTemplateInput {
358 template_name: get_required_param(¶ms, "TemplateName")?.to_owned(),
359 };
360 provider.delete_template(input)?;
361 Ok(empty_xml_response("DeleteTemplate", &request_id))
362 }
363
364 SesOperation::ListTemplates => {
365 let input = ListTemplatesInput {
366 max_items: None,
367 next_token: None,
368 };
369 let output = provider.list_templates(input)?;
370 let mut w = XmlWriter::new();
371 w.start_response("ListTemplates");
372 w.start_result("ListTemplates");
373 w.raw("<TemplatesMetadata>");
374 for meta in &output.templates_metadata {
375 w.raw("<member>");
376 w.write_optional_element("Name", meta.name.as_deref());
377 if let Some(ts) = &meta.created_timestamp {
378 w.write_element("CreatedTimestamp", &ts.to_rfc3339());
379 }
380 w.raw("</member>");
381 }
382 w.raw("</TemplatesMetadata>");
383 w.write_optional_element("NextToken", output.next_token.as_deref());
384 w.end_element("ListTemplatesResult");
385 w.write_response_metadata(&request_id);
386 w.end_element("ListTemplatesResponse");
387 Ok(xml_response(w.into_string(), &request_id))
388 }
389
390 SesOperation::SendTemplatedEmail => {
391 let input = deserialize_send_templated_email(¶ms)?;
392 let output = provider.send_templated_email(input)?;
393 let mut w = XmlWriter::new();
394 w.start_response("SendTemplatedEmail");
395 w.start_result("SendTemplatedEmail");
396 w.write_element("MessageId", &output.message_id);
397 w.end_element("SendTemplatedEmailResult");
398 w.write_response_metadata(&request_id);
399 w.end_element("SendTemplatedEmailResponse");
400 Ok(xml_response(w.into_string(), &request_id))
401 }
402
403 SesOperation::CreateConfigurationSet => {
404 let name = get_required_param(¶ms, "ConfigurationSet.Name")?.to_owned();
405 let input = CreateConfigurationSetInput {
406 configuration_set: ConfigurationSet { name },
407 };
408 provider.create_configuration_set(input)?;
409 Ok(empty_xml_response("CreateConfigurationSet", &request_id))
410 }
411
412 SesOperation::DeleteConfigurationSet => {
413 let input = DeleteConfigurationSetInput {
414 configuration_set_name: get_required_param(¶ms, "ConfigurationSetName")?
415 .to_owned(),
416 };
417 provider.delete_configuration_set(input)?;
418 Ok(empty_xml_response("DeleteConfigurationSet", &request_id))
419 }
420
421 SesOperation::DescribeConfigurationSet => {
422 let input = DescribeConfigurationSetInput {
423 configuration_set_name: get_required_param(¶ms, "ConfigurationSetName")?
424 .to_owned(),
425 configuration_set_attribute_names: Vec::new(),
426 };
427 let output = provider.describe_configuration_set(input)?;
428 let mut w = XmlWriter::new();
429 w.start_response("DescribeConfigurationSet");
430 w.start_result("DescribeConfigurationSet");
431 if let Some(cs) = &output.configuration_set {
432 w.raw("<ConfigurationSet>");
433 w.write_element("Name", &cs.name);
434 w.raw("</ConfigurationSet>");
435 }
436 w.raw("<EventDestinations>");
437 for dest in &output.event_destinations {
438 write_event_destination_xml(&mut w, dest);
439 }
440 w.raw("</EventDestinations>");
441 w.end_element("DescribeConfigurationSetResult");
442 w.write_response_metadata(&request_id);
443 w.end_element("DescribeConfigurationSetResponse");
444 Ok(xml_response(w.into_string(), &request_id))
445 }
446
447 SesOperation::ListConfigurationSets => {
448 let input = ListConfigurationSetsInput {
449 max_items: None,
450 next_token: None,
451 };
452 let output = provider.list_configuration_sets(input)?;
453 let mut w = XmlWriter::new();
454 w.start_response("ListConfigurationSets");
455 w.start_result("ListConfigurationSets");
456 w.raw("<ConfigurationSets>");
457 for cs in &output.configuration_sets {
458 w.raw("<member>");
459 w.write_element("Name", &cs.name);
460 w.raw("</member>");
461 }
462 w.raw("</ConfigurationSets>");
463 w.write_optional_element("NextToken", output.next_token.as_deref());
464 w.end_element("ListConfigurationSetsResult");
465 w.write_response_metadata(&request_id);
466 w.end_element("ListConfigurationSetsResponse");
467 Ok(xml_response(w.into_string(), &request_id))
468 }
469
470 SesOperation::CreateConfigurationSetEventDestination => {
474 let input = deserialize_create_config_set_event_dest(¶ms)?;
475 provider.create_configuration_set_event_destination(input)?;
476 Ok(empty_xml_response(
477 "CreateConfigurationSetEventDestination",
478 &request_id,
479 ))
480 }
481
482 SesOperation::UpdateConfigurationSetEventDestination => {
483 let input = deserialize_update_config_set_event_dest(¶ms)?;
484 provider.update_configuration_set_event_destination(input)?;
485 Ok(empty_xml_response(
486 "UpdateConfigurationSetEventDestination",
487 &request_id,
488 ))
489 }
490
491 SesOperation::DeleteConfigurationSetEventDestination => {
492 let input = DeleteConfigurationSetEventDestinationInput {
493 configuration_set_name: get_required_param(¶ms, "ConfigurationSetName")?
494 .to_owned(),
495 event_destination_name: get_required_param(¶ms, "EventDestinationName")?
496 .to_owned(),
497 };
498 provider.delete_configuration_set_event_destination(input)?;
499 Ok(empty_xml_response(
500 "DeleteConfigurationSetEventDestination",
501 &request_id,
502 ))
503 }
504
505 SesOperation::CreateReceiptRuleSet => {
506 let input = CreateReceiptRuleSetInput {
507 rule_set_name: get_required_param(¶ms, "RuleSetName")?.to_owned(),
508 };
509 provider.create_receipt_rule_set(input)?;
510 Ok(empty_xml_response("CreateReceiptRuleSet", &request_id))
511 }
512
513 SesOperation::DeleteReceiptRuleSet => {
514 let input = DeleteReceiptRuleSetInput {
515 rule_set_name: get_required_param(¶ms, "RuleSetName")?.to_owned(),
516 };
517 provider.delete_receipt_rule_set(input)?;
518 Ok(empty_xml_response("DeleteReceiptRuleSet", &request_id))
519 }
520
521 SesOperation::CreateReceiptRule => {
522 let input = deserialize_create_receipt_rule(¶ms)?;
523 provider.create_receipt_rule(input)?;
524 Ok(empty_xml_response("CreateReceiptRule", &request_id))
525 }
526
527 SesOperation::DeleteReceiptRule => {
528 let input = DeleteReceiptRuleInput {
529 rule_set_name: get_required_param(¶ms, "RuleSetName")?.to_owned(),
530 rule_name: get_required_param(¶ms, "RuleName")?.to_owned(),
531 };
532 provider.delete_receipt_rule(input)?;
533 Ok(empty_xml_response("DeleteReceiptRule", &request_id))
534 }
535
536 SesOperation::DescribeReceiptRuleSet => {
537 let input = DescribeReceiptRuleSetInput {
538 rule_set_name: get_required_param(¶ms, "RuleSetName")?.to_owned(),
539 };
540 let output = provider.describe_receipt_rule_set(input)?;
541 let mut w = XmlWriter::new();
542 w.start_response("DescribeReceiptRuleSet");
543 w.start_result("DescribeReceiptRuleSet");
544 if let Some(meta) = &output.metadata {
545 w.raw("<Metadata>");
546 w.write_optional_element("Name", meta.name.as_deref());
547 if let Some(ts) = &meta.created_timestamp {
548 w.write_element("CreatedTimestamp", &ts.to_rfc3339());
549 }
550 w.raw("</Metadata>");
551 }
552 w.raw("<Rules>");
553 for rule in &output.rules {
554 write_receipt_rule_xml(&mut w, rule);
555 }
556 w.raw("</Rules>");
557 w.end_element("DescribeReceiptRuleSetResult");
558 w.write_response_metadata(&request_id);
559 w.end_element("DescribeReceiptRuleSetResponse");
560 Ok(xml_response(w.into_string(), &request_id))
561 }
562
563 SesOperation::CloneReceiptRuleSet => {
564 let input = CloneReceiptRuleSetInput {
565 original_rule_set_name: get_required_param(¶ms, "OriginalRuleSetName")?
566 .to_owned(),
567 rule_set_name: get_required_param(¶ms, "RuleSetName")?.to_owned(),
568 };
569 provider.clone_receipt_rule_set(input)?;
570 Ok(empty_xml_response("CloneReceiptRuleSet", &request_id))
571 }
572
573 SesOperation::DescribeActiveReceiptRuleSet => {
574 let output =
575 provider.describe_active_receipt_rule_set(DescribeActiveReceiptRuleSetInput {})?;
576 let mut w = XmlWriter::new();
577 w.start_response("DescribeActiveReceiptRuleSet");
578 w.start_result("DescribeActiveReceiptRuleSet");
579 if let Some(meta) = &output.metadata {
580 w.raw("<Metadata>");
581 w.write_optional_element("Name", meta.name.as_deref());
582 if let Some(ts) = &meta.created_timestamp {
583 w.write_element("CreatedTimestamp", &ts.to_rfc3339());
584 }
585 w.raw("</Metadata>");
586 }
587 w.raw("<Rules>");
588 for rule in &output.rules {
589 write_receipt_rule_xml(&mut w, rule);
590 }
591 w.raw("</Rules>");
592 w.end_element("DescribeActiveReceiptRuleSetResult");
593 w.write_response_metadata(&request_id);
594 w.end_element("DescribeActiveReceiptRuleSetResponse");
595 Ok(xml_response(w.into_string(), &request_id))
596 }
597
598 SesOperation::SetActiveReceiptRuleSet => {
599 let input = SetActiveReceiptRuleSetInput {
600 rule_set_name: get_optional_param(¶ms, "RuleSetName").map(str::to_owned),
601 };
602 provider.set_active_receipt_rule_set(input)?;
603 Ok(empty_xml_response("SetActiveReceiptRuleSet", &request_id))
604 }
605
606 SesOperation::SetIdentityNotificationTopic => {
610 let notification_type_str = get_required_param(¶ms, "NotificationType")?;
611 let notification_type = NotificationType::from(notification_type_str);
612 let input = SetIdentityNotificationTopicInput {
613 identity: get_required_param(¶ms, "Identity")?.to_owned(),
614 notification_type,
615 sns_topic: get_optional_param(¶ms, "SnsTopic").map(str::to_owned),
616 };
617 provider.set_identity_notification_topic(input)?;
618 Ok(empty_xml_response(
619 "SetIdentityNotificationTopic",
620 &request_id,
621 ))
622 }
623
624 SesOperation::SetIdentityFeedbackForwardingEnabled => {
625 let input = SetIdentityFeedbackForwardingEnabledInput {
626 identity: get_required_param(¶ms, "Identity")?.to_owned(),
627 forwarding_enabled: get_optional_bool(¶ms, "ForwardingEnabled").unwrap_or(true),
628 };
629 provider.set_identity_feedback_forwarding_enabled(input)?;
630 Ok(empty_xml_response(
631 "SetIdentityFeedbackForwardingEnabled",
632 &request_id,
633 ))
634 }
635
636 SesOperation::GetIdentityNotificationAttributes => {
637 let identities = parse_member_list(¶ms, "Identities");
638 let input = GetIdentityNotificationAttributesInput { identities };
639 let output = provider.get_identity_notification_attributes(input)?;
640 let mut w = XmlWriter::new();
641 w.start_response("GetIdentityNotificationAttributes");
642 w.start_result("GetIdentityNotificationAttributes");
643 w.raw("<NotificationAttributes>");
644 for (identity, attrs) in &output.notification_attributes {
645 w.raw("<entry>");
646 w.write_element("key", identity);
647 w.raw("<value>");
648 w.write_element("BounceTopic", &attrs.bounce_topic);
649 w.write_element("ComplaintTopic", &attrs.complaint_topic);
650 w.write_element("DeliveryTopic", &attrs.delivery_topic);
651 w.write_bool_element("ForwardingEnabled", attrs.forwarding_enabled);
652 w.write_bool_element(
653 "HeadersInBounceNotificationsEnabled",
654 attrs
655 .headers_in_bounce_notifications_enabled
656 .unwrap_or(false),
657 );
658 w.write_bool_element(
659 "HeadersInComplaintNotificationsEnabled",
660 attrs
661 .headers_in_complaint_notifications_enabled
662 .unwrap_or(false),
663 );
664 w.write_bool_element(
665 "HeadersInDeliveryNotificationsEnabled",
666 attrs
667 .headers_in_delivery_notifications_enabled
668 .unwrap_or(false),
669 );
670 w.raw("</value>");
671 w.raw("</entry>");
672 }
673 w.raw("</NotificationAttributes>");
674 w.end_element("GetIdentityNotificationAttributesResult");
675 w.write_response_metadata(&request_id);
676 w.end_element("GetIdentityNotificationAttributesResponse");
677 Ok(xml_response(w.into_string(), &request_id))
678 }
679
680 SesOperation::VerifyDomainDkim => {
681 let input = VerifyDomainDkimInput {
682 domain: get_required_param(¶ms, "Domain")?.to_owned(),
683 };
684 let output = provider.verify_domain_dkim(input)?;
685 let mut w = XmlWriter::new();
686 w.start_response("VerifyDomainDkim");
687 w.start_result("VerifyDomainDkim");
688 w.raw("<DkimTokens>");
689 for token in &output.dkim_tokens {
690 w.write_element("member", token);
691 }
692 w.raw("</DkimTokens>");
693 w.end_element("VerifyDomainDkimResult");
694 w.write_response_metadata(&request_id);
695 w.end_element("VerifyDomainDkimResponse");
696 Ok(xml_response(w.into_string(), &request_id))
697 }
698
699 SesOperation::GetIdentityDkimAttributes => {
700 let identities = parse_member_list(¶ms, "Identities");
701 let input = GetIdentityDkimAttributesInput { identities };
702 let output = provider.get_identity_dkim_attributes(input)?;
703 let mut w = XmlWriter::new();
704 w.start_response("GetIdentityDkimAttributes");
705 w.start_result("GetIdentityDkimAttributes");
706 w.raw("<DkimAttributes>");
707 for (identity, attrs) in &output.dkim_attributes {
708 w.raw("<entry>");
709 w.write_element("key", identity);
710 w.raw("<value>");
711 w.write_bool_element("DkimEnabled", attrs.dkim_enabled);
712 w.write_element(
713 "DkimVerificationStatus",
714 attrs.dkim_verification_status.as_str(),
715 );
716 w.raw("<DkimTokens>");
717 for token in &attrs.dkim_tokens {
718 w.write_element("member", token);
719 }
720 w.raw("</DkimTokens>");
721 w.raw("</value>");
722 w.raw("</entry>");
723 }
724 w.raw("</DkimAttributes>");
725 w.end_element("GetIdentityDkimAttributesResult");
726 w.write_response_metadata(&request_id);
727 w.end_element("GetIdentityDkimAttributesResponse");
728 Ok(xml_response(w.into_string(), &request_id))
729 }
730
731 SesOperation::SetIdentityMailFromDomain => {
732 let behavior =
733 get_optional_param(¶ms, "BehaviorOnMXFailure").map(BehaviorOnMXFailure::from);
734 let input = SetIdentityMailFromDomainInput {
735 identity: get_required_param(¶ms, "Identity")?.to_owned(),
736 mail_from_domain: get_optional_param(¶ms, "MailFromDomain").map(str::to_owned),
737 behavior_on_mx_failure: behavior,
738 };
739 provider.set_identity_mail_from_domain(input)?;
740 Ok(empty_xml_response("SetIdentityMailFromDomain", &request_id))
741 }
742
743 SesOperation::GetIdentityMailFromDomainAttributes => {
744 let identities = parse_member_list(¶ms, "Identities");
745 let input = GetIdentityMailFromDomainAttributesInput { identities };
746 let output = provider.get_identity_mail_from_domain_attributes(input)?;
747 let mut w = XmlWriter::new();
748 w.start_response("GetIdentityMailFromDomainAttributes");
749 w.start_result("GetIdentityMailFromDomainAttributes");
750 w.raw("<MailFromDomainAttributes>");
751 for (identity, attrs) in &output.mail_from_domain_attributes {
752 w.raw("<entry>");
753 w.write_element("key", identity);
754 w.raw("<value>");
755 w.write_element("MailFromDomain", &attrs.mail_from_domain);
756 w.write_element(
757 "MailFromDomainStatus",
758 attrs.mail_from_domain_status.as_str(),
759 );
760 w.write_element("BehaviorOnMXFailure", attrs.behavior_on_mx_failure.as_str());
761 w.raw("</value>");
762 w.raw("</entry>");
763 }
764 w.raw("</MailFromDomainAttributes>");
765 w.end_element("GetIdentityMailFromDomainAttributesResult");
766 w.write_response_metadata(&request_id);
767 w.end_element("GetIdentityMailFromDomainAttributesResponse");
768 Ok(xml_response(w.into_string(), &request_id))
769 }
770
771 SesOperation::GetIdentityPolicies => {
772 let identity = get_required_param(¶ms, "Identity")?.to_owned();
773 let policy_names = parse_member_list(¶ms, "PolicyNames");
774 let input = GetIdentityPoliciesInput {
775 identity,
776 policy_names,
777 };
778 let output = provider.get_identity_policies(input)?;
779 let mut w = XmlWriter::new();
780 w.start_response("GetIdentityPolicies");
781 w.start_result("GetIdentityPolicies");
782 w.raw("<Policies>");
783 for (name, policy) in &output.policies {
784 w.raw("<entry>");
785 w.write_element("key", name);
786 w.write_element("value", policy);
787 w.raw("</entry>");
788 }
789 w.raw("</Policies>");
790 w.end_element("GetIdentityPoliciesResult");
791 w.write_response_metadata(&request_id);
792 w.end_element("GetIdentityPoliciesResponse");
793 Ok(xml_response(w.into_string(), &request_id))
794 }
795
796 SesOperation::PutIdentityPolicy => {
797 let input = PutIdentityPolicyInput {
798 identity: get_required_param(¶ms, "Identity")?.to_owned(),
799 policy_name: get_required_param(¶ms, "PolicyName")?.to_owned(),
800 policy: get_required_param(¶ms, "Policy")?.to_owned(),
801 };
802 provider.put_identity_policy(input)?;
803 Ok(empty_xml_response("PutIdentityPolicy", &request_id))
804 }
805
806 SesOperation::DeleteIdentityPolicy => {
807 let input = DeleteIdentityPolicyInput {
808 identity: get_required_param(¶ms, "Identity")?.to_owned(),
809 policy_name: get_required_param(¶ms, "PolicyName")?.to_owned(),
810 };
811 provider.delete_identity_policy(input)?;
812 Ok(empty_xml_response("DeleteIdentityPolicy", &request_id))
813 }
814
815 SesOperation::ListIdentityPolicies => {
816 let input = ListIdentityPoliciesInput {
817 identity: get_required_param(¶ms, "Identity")?.to_owned(),
818 };
819 let output = provider.list_identity_policies(input)?;
820 let mut w = XmlWriter::new();
821 w.start_response("ListIdentityPolicies");
822 w.start_result("ListIdentityPolicies");
823 w.raw("<PolicyNames>");
824 for name in &output.policy_names {
825 w.write_element("member", name);
826 }
827 w.raw("</PolicyNames>");
828 w.end_element("ListIdentityPoliciesResult");
829 w.write_response_metadata(&request_id);
830 w.end_element("ListIdentityPoliciesResponse");
831 Ok(xml_response(w.into_string(), &request_id))
832 }
833 }
834}
835
836fn empty_xml_response(operation: &str, request_id: &str) -> http::Response<SesResponseBody> {
841 let mut w = XmlWriter::new();
842 w.start_response(operation);
843 w.start_result(operation);
844 w.end_element(&format!("{operation}Result"));
845 w.write_response_metadata(request_id);
846 w.end_element(&format!("{operation}Response"));
847 xml_response(w.into_string(), request_id)
848}
849
850fn deserialize_send_email(params: &[(String, String)]) -> Result<SendEmailInput, SesError> {
855 let source = get_required_param(params, "Source")?.to_owned();
856 let to = parse_member_list(params, "Destination.ToAddresses");
857 let cc = parse_member_list(params, "Destination.CcAddresses");
858 let bcc = parse_member_list(params, "Destination.BccAddresses");
859 let subject_data = get_optional_param(params, "Message.Subject.Data")
860 .unwrap_or("")
861 .to_owned();
862 let subject_charset = get_optional_param(params, "Message.Subject.Charset").map(str::to_owned);
863 let text_data = get_optional_param(params, "Message.Body.Text.Data").map(str::to_owned);
864 let html_data = get_optional_param(params, "Message.Body.Html.Data").map(str::to_owned);
865
866 let tags = parse_tag_list(params, "Tags");
867
868 Ok(SendEmailInput {
869 source,
870 destination: Destination {
871 to_addresses: to,
872 cc_addresses: cc,
873 bcc_addresses: bcc,
874 },
875 message: Message {
876 subject: Content {
877 data: subject_data,
878 charset: subject_charset,
879 },
880 body: Body {
881 text: text_data.map(|d| Content {
882 data: d,
883 charset: get_optional_param(params, "Message.Body.Text.Charset")
884 .map(str::to_owned),
885 }),
886 html: html_data.map(|d| Content {
887 data: d,
888 charset: get_optional_param(params, "Message.Body.Html.Charset")
889 .map(str::to_owned),
890 }),
891 },
892 },
893 tags: tags
894 .into_iter()
895 .map(|(n, v)| MessageTag { name: n, value: v })
896 .collect(),
897 configuration_set_name: get_optional_param(params, "ConfigurationSetName")
898 .map(str::to_owned),
899 return_path: get_optional_param(params, "ReturnPath").map(str::to_owned),
900 return_path_arn: get_optional_param(params, "ReturnPathArn").map(str::to_owned),
901 source_arn: get_optional_param(params, "SourceArn").map(str::to_owned),
902 reply_to_addresses: parse_member_list(params, "ReplyToAddresses"),
903 })
904}
905
906fn deserialize_send_raw_email(params: &[(String, String)]) -> Result<SendRawEmailInput, SesError> {
907 let raw_data = get_required_param(params, "RawMessage.Data")?.to_owned();
908 let decoded = base64::engine::general_purpose::STANDARD
909 .decode(raw_data.as_bytes())
910 .map_err(|e| {
911 SesError::invalid_parameter_value(format!("RawMessage.Data is not valid base64: {e}"))
912 })?;
913 let tags = parse_tag_list(params, "Tags");
914 Ok(SendRawEmailInput {
915 raw_message: RawMessage { data: decoded },
916 source: get_optional_param(params, "Source").map(str::to_owned),
917 destinations: parse_member_list(params, "Destinations"),
918 tags: tags
919 .into_iter()
920 .map(|(n, v)| MessageTag { name: n, value: v })
921 .collect(),
922 configuration_set_name: get_optional_param(params, "ConfigurationSetName")
923 .map(str::to_owned),
924 from_arn: get_optional_param(params, "FromArn").map(str::to_owned),
925 return_path_arn: get_optional_param(params, "ReturnPathArn").map(str::to_owned),
926 source_arn: get_optional_param(params, "SourceArn").map(str::to_owned),
927 })
928}
929
930fn deserialize_send_templated_email(
931 params: &[(String, String)],
932) -> Result<SendTemplatedEmailInput, SesError> {
933 let source = get_required_param(params, "Source")?.to_owned();
934 let to = parse_member_list(params, "Destination.ToAddresses");
935 let cc = parse_member_list(params, "Destination.CcAddresses");
936 let bcc = parse_member_list(params, "Destination.BccAddresses");
937 let template = get_required_param(params, "Template")?.to_owned();
938 let template_data = get_required_param(params, "TemplateData")?.to_owned();
939 let tags = parse_tag_list(params, "Tags");
940
941 Ok(SendTemplatedEmailInput {
942 source,
943 destination: Destination {
944 to_addresses: to,
945 cc_addresses: cc,
946 bcc_addresses: bcc,
947 },
948 template,
949 template_data,
950 tags: tags
951 .into_iter()
952 .map(|(n, v)| MessageTag { name: n, value: v })
953 .collect(),
954 configuration_set_name: get_optional_param(params, "ConfigurationSetName")
955 .map(str::to_owned),
956 return_path: get_optional_param(params, "ReturnPath").map(str::to_owned),
957 return_path_arn: get_optional_param(params, "ReturnPathArn").map(str::to_owned),
958 source_arn: get_optional_param(params, "SourceArn").map(str::to_owned),
959 template_arn: get_optional_param(params, "TemplateArn").map(str::to_owned),
960 reply_to_addresses: parse_member_list(params, "ReplyToAddresses"),
961 })
962}
963
964fn deserialize_create_template(
965 params: &[(String, String)],
966) -> Result<CreateTemplateInput, SesError> {
967 Ok(CreateTemplateInput {
968 template: Template {
969 template_name: get_required_param(params, "Template.TemplateName")?.to_owned(),
970 subject_part: get_optional_param(params, "Template.SubjectPart").map(str::to_owned),
971 text_part: get_optional_param(params, "Template.TextPart").map(str::to_owned),
972 html_part: get_optional_param(params, "Template.HtmlPart").map(str::to_owned),
973 },
974 })
975}
976
977fn deserialize_update_template(
978 params: &[(String, String)],
979) -> Result<UpdateTemplateInput, SesError> {
980 Ok(UpdateTemplateInput {
981 template: Template {
982 template_name: get_required_param(params, "Template.TemplateName")?.to_owned(),
983 subject_part: get_optional_param(params, "Template.SubjectPart").map(str::to_owned),
984 text_part: get_optional_param(params, "Template.TextPart").map(str::to_owned),
985 html_part: get_optional_param(params, "Template.HtmlPart").map(str::to_owned),
986 },
987 })
988}
989
990fn deserialize_create_config_set_event_dest(
991 params: &[(String, String)],
992) -> Result<CreateConfigurationSetEventDestinationInput, SesError> {
993 let config_set_name = get_required_param(params, "ConfigurationSetName")?.to_owned();
994 let dest_name = get_required_param(params, "EventDestination.Name")?.to_owned();
995 let enabled = get_optional_bool(params, "EventDestination.Enabled");
996 let matching_types = parse_member_list(params, "EventDestination.MatchingEventTypes");
997
998 Ok(CreateConfigurationSetEventDestinationInput {
999 configuration_set_name: config_set_name,
1000 event_destination: EventDestination {
1001 name: dest_name,
1002 enabled,
1003 matching_event_types: matching_types
1004 .into_iter()
1005 .map(|s| s.as_str().into())
1006 .collect(),
1007 ..EventDestination::default()
1008 },
1009 })
1010}
1011
1012fn deserialize_update_config_set_event_dest(
1013 params: &[(String, String)],
1014) -> Result<UpdateConfigurationSetEventDestinationInput, SesError> {
1015 let config_set_name = get_required_param(params, "ConfigurationSetName")?.to_owned();
1016 let dest_name = get_required_param(params, "EventDestination.Name")?.to_owned();
1017 let enabled = get_optional_bool(params, "EventDestination.Enabled");
1018 let matching_types = parse_member_list(params, "EventDestination.MatchingEventTypes");
1019
1020 Ok(UpdateConfigurationSetEventDestinationInput {
1021 configuration_set_name: config_set_name,
1022 event_destination: EventDestination {
1023 name: dest_name,
1024 enabled,
1025 matching_event_types: matching_types
1026 .into_iter()
1027 .map(|s| s.as_str().into())
1028 .collect(),
1029 ..EventDestination::default()
1030 },
1031 })
1032}
1033
1034fn deserialize_create_receipt_rule(
1035 params: &[(String, String)],
1036) -> Result<CreateReceiptRuleInput, SesError> {
1037 let rule_set_name = get_required_param(params, "RuleSetName")?.to_owned();
1038 let after = get_optional_param(params, "After").map(str::to_owned);
1039 let rule_name = get_required_param(params, "Rule.Name")?.to_owned();
1040 let enabled = get_optional_bool(params, "Rule.Enabled");
1041 let scan_enabled = get_optional_bool(params, "Rule.ScanEnabled");
1042 let recipients = parse_member_list(params, "Rule.Recipients");
1043
1044 Ok(CreateReceiptRuleInput {
1045 rule_set_name,
1046 after,
1047 rule: ReceiptRule {
1048 name: rule_name,
1049 enabled,
1050 scan_enabled,
1051 recipients,
1052 actions: Vec::new(),
1053 tls_policy: None,
1054 },
1055 })
1056}
1057
1058fn write_event_destination_xml(w: &mut XmlWriter, dest: &EventDestination) {
1063 w.raw("<member>");
1064 w.write_element("Name", &dest.name);
1065 if let Some(enabled) = dest.enabled {
1066 w.write_bool_element("Enabled", enabled);
1067 }
1068 w.raw("<MatchingEventTypes>");
1069 for et in &dest.matching_event_types {
1070 w.write_element("member", et.as_str());
1071 }
1072 w.raw("</MatchingEventTypes>");
1073 w.raw("</member>");
1074}
1075
1076fn write_receipt_rule_xml(w: &mut XmlWriter, rule: &ReceiptRule) {
1077 w.raw("<member>");
1078 w.write_element("Name", &rule.name);
1079 if let Some(enabled) = rule.enabled {
1080 w.write_bool_element("Enabled", enabled);
1081 }
1082 if let Some(scan) = rule.scan_enabled {
1083 w.write_bool_element("ScanEnabled", scan);
1084 }
1085 w.raw("<Recipients>");
1086 for r in &rule.recipients {
1087 w.write_element("member", r);
1088 }
1089 w.raw("</Recipients>");
1090 w.raw("<Actions>");
1091 for _action in &rule.actions {
1092 w.raw("<member/>");
1093 }
1094 w.raw("</Actions>");
1095 w.raw("</member>");
1096}