22import email .encoders
33import email .policy
44
5+ from requests .structures import CaseInsensitiveDict
6+
57from .. import __version__ as ANYMAIL_VERSION
68from ..exceptions import AnymailAPIError , AnymailImproperlyInstalled
79from ..message import AnymailRecipientStatus
@@ -339,10 +341,14 @@ class AmazonSESV2SendBulkEmailPayload(AmazonSESBasePayload):
339341
340342 def init_payload (self ):
341343 super ().init_payload ()
342- # late-bind recipients and merge_data in finalize_payload
344+ # late-bind in finalize_payload:
343345 self .recipients = {"to" : [], "cc" : [], "bcc" : []}
344346 self .merge_data = {}
347+ self .headers = {}
345348 self .merge_headers = {}
349+ self .metadata = {}
350+ self .merge_metadata = {}
351+ self .tags = []
346352
347353 def finalize_payload (self ):
348354 # Build BulkEmailEntries from recipients and merge_data.
@@ -372,11 +378,26 @@ def finalize_payload(self):
372378 },
373379 }
374380
375- if len (self .merge_headers ) > 0 :
376- entry ["ReplacementHeaders" ] = [
377- {"Name" : key , "Value" : value }
378- for key , value in self .merge_headers .get (to .addr_spec , {}).items ()
381+ replacement_headers = []
382+ if self .headers or to .addr_spec in self .merge_headers :
383+ headers = CaseInsensitiveDict (self .headers )
384+ headers .update (self .merge_headers .get (to .addr_spec , {}))
385+ replacement_headers += [
386+ {"Name" : key , "Value" : value } for key , value in headers .items ()
387+ ]
388+ if self .metadata or to .addr_spec in self .merge_metadata :
389+ metadata = self .metadata .copy ()
390+ metadata .update (self .merge_metadata .get (to .addr_spec , {}))
391+ if metadata :
392+ replacement_headers .append (
393+ {"Name" : "X-Metadata" , "Value" : self .serialize_json (metadata )}
394+ )
395+ if self .tags :
396+ replacement_headers += [
397+ {"Name" : "X-Tag" , "Value" : tag } for tag in self .tags
379398 ]
399+ if replacement_headers :
400+ entry ["ReplacementHeaders" ] = replacement_headers
380401 self .params ["BulkEmailEntries" ].append (entry )
381402
382403 def parse_recipient_status (self , response ):
@@ -446,7 +467,7 @@ def set_reply_to(self, emails):
446467 self .params ["ReplyToAddresses" ] = [email .address for email in emails ]
447468
448469 def set_extra_headers (self , headers ):
449- self .unsupported_feature ( "extra_headers with template" )
470+ self .headers = headers
450471
451472 def set_text_body (self , body ):
452473 if body :
@@ -468,27 +489,26 @@ def set_envelope_sender(self, email):
468489 self .params ["FeedbackForwardingEmailAddress" ] = email .addr_spec
469490
470491 def set_metadata (self , metadata ):
471- # no custom headers with SendBulkEmail
472- self .unsupported_feature ("metadata with template" )
492+ self .metadata = metadata
493+
494+ def set_merge_metadata (self , merge_metadata ):
495+ self .merge_metadata = merge_metadata
473496
474497 def set_tags (self , tags ):
475- # no custom headers with SendBulkEmail, but support
476- # AMAZON_SES_MESSAGE_TAG_NAME if used (see tags/metadata in
477- # AmazonSESV2SendEmailPayload for more info)
478- if tags :
479- if self .backend .message_tag_name is not None :
480- if len (tags ) > 1 :
481- self .unsupported_feature (
482- "multiple tags with the AMAZON_SES_MESSAGE_TAG_NAME setting"
483- )
484- self .params ["DefaultEmailTags" ] = [
485- {"Name" : self .backend .message_tag_name , "Value" : tags [0 ]}
486- ]
487- else :
498+ self .tags = tags
499+
500+ # Also *optionally* pass a single Message Tag if the AMAZON_SES_MESSAGE_TAG_NAME
501+ # Anymail setting is set (default no). The AWS API restricts tag content in this
502+ # case. (This is useful for dashboard segmentation; use esp_extra["Tags"] for
503+ # anything more complex.)
504+ if tags and self .backend .message_tag_name is not None :
505+ if len (tags ) > 1 :
488506 self .unsupported_feature (
489- "tags with template (unless using the"
490- " AMAZON_SES_MESSAGE_TAG_NAME setting)"
507+ "multiple tags with the AMAZON_SES_MESSAGE_TAG_NAME setting"
491508 )
509+ self .params ["DefaultEmailTags" ] = [
510+ {"Name" : self .backend .message_tag_name , "Value" : tags [0 ]}
511+ ]
492512
493513 def set_template_id (self , template_id ):
494514 # DefaultContent.Template.TemplateName
0 commit comments