diff --git a/.doc_gen/metadata/sesv2_metadata.yaml b/.doc_gen/metadata/sesv2_metadata.yaml index 313a51113c9..7a130fbc5d3 100644 --- a/.doc_gen/metadata/sesv2_metadata.yaml +++ b/.doc_gen/metadata/sesv2_metadata.yaml @@ -39,7 +39,7 @@ sesv2_CreateContactList: Python: versions: - sdk_version: 3 - github: python/example_code/sesv2 + github: python/example_code/sesv2/newsletter_scenario excerpts: - description: genai: most @@ -89,7 +89,7 @@ sesv2_CreateContact: Python: versions: - sdk_version: 3 - github: python/example_code/sesv2 + github: python/example_code/sesv2/newsletter_scenario excerpts: - description: genai: most @@ -101,6 +101,15 @@ sesv2_CreateContact: sesv2: {CreateContact} sesv2_GetEmailIdentity: languages: + Python: + versions: + - sdk_version: 3 + github: python/example_code/sesv2/attachments_scenario + excerpts: + - description: + snippet_tags: + - python.example_code.sesv2.SESv2Wrapper.decl + - python.example_code.sesv2.GetEmailIdentityAttachment Rust: versions: - sdk_version: 1 @@ -163,7 +172,7 @@ sesv2_ListContacts: Python: versions: - sdk_version: 3 - github: python/example_code/sesv2 + github: python/example_code/sesv2/newsletter_scenario excerpts: - description: genai: most @@ -234,7 +243,14 @@ sesv2_SendEmail: Python: versions: - sdk_version: 3 - github: python/example_code/sesv2 + github: python/example_code/sesv2/attachments_scenario + excerpts: + - description: Sends a message with optional attachments. + snippet_tags: + - python.example_code.sesv2.SESv2Wrapper.decl + - python.example_code.sesv2.SendEmailAttachment + - sdk_version: 3 + github: python/example_code/sesv2/newsletter_scenario excerpts: - description: Sends a message to all members of the contact list. genai: most @@ -282,7 +298,14 @@ sesv2_CreateEmailIdentity: Python: versions: - sdk_version: 3 - github: python/example_code/sesv2 + github: python/example_code/sesv2/attachments_scenario + excerpts: + - description: + snippet_tags: + - python.example_code.sesv2.SESv2Wrapper.decl + - python.example_code.sesv2.CreateEmailIdentityAttachment + - sdk_version: 3 + github: python/example_code/sesv2/newsletter_scenario excerpts: - description: genai: most @@ -333,7 +356,14 @@ sesv2_CreateEmailTemplate: Python: versions: - sdk_version: 3 - github: python/example_code/sesv2 + github: python/example_code/sesv2/attachments_scenario + excerpts: + - description: + snippet_tags: + - python.example_code.sesv2.SESv2Wrapper.decl + - python.example_code.sesv2.CreateEmailTemplateAttachment + - sdk_version: 3 + github: python/example_code/sesv2/newsletter_scenario excerpts: - description: genai: most @@ -384,7 +414,7 @@ sesv2_DeleteContactList: Python: versions: - sdk_version: 3 - github: python/example_code/sesv2 + github: python/example_code/sesv2/newsletter_scenario excerpts: - description: genai: most @@ -435,7 +465,14 @@ sesv2_DeleteEmailIdentity: Python: versions: - sdk_version: 3 - github: python/example_code/sesv2 + github: python/example_code/sesv2/attachments_scenario + excerpts: + - description: + snippet_tags: + - python.example_code.sesv2.SESv2Wrapper.decl + - python.example_code.sesv2.DeleteEmailIdentityAttachment + - sdk_version: 3 + github: python/example_code/sesv2/newsletter_scenario excerpts: - description: genai: most @@ -486,7 +523,14 @@ sesv2_DeleteEmailTemplate: Python: versions: - sdk_version: 3 - github: python/example_code/sesv2 + github: python/example_code/sesv2/attachments_scenario + excerpts: + - description: + snippet_tags: + - python.example_code.sesv2.SESv2Wrapper.decl + - python.example_code.sesv2.DeleteEmailTemplateAttachment + - sdk_version: 3 + github: python/example_code/sesv2/newsletter_scenario excerpts: - description: genai: most @@ -544,7 +588,7 @@ sesv2_NewsletterWorkflow: Python: versions: - sdk_version: 3 - github: python/example_code/sesv2 + github: python/example_code/sesv2/newsletter_scenario excerpts: - description: genai: most @@ -581,3 +625,58 @@ sesv2_NewsletterWorkflow: services: sesv2: {CreateContactList, CreateContact, ListContacts, SendEmail.simple, SendEmail.template, CreateEmailIdentity, CreateEmailTemplate, DeleteContactList, DeleteEmailIdentity, DeleteEmailTemplate} + +sesv2_Hello: + title: Hello &SESv2; + title_abbrev: Hello &SESv2; + synopsis: get started using &SESv2;. + category: Hello + languages: + Python: + versions: + - sdk_version: 3 + github: python/example_code/sesv2/attachments_scenario + excerpts: + - description: + snippet_tags: + - python.example_code.sesv2.Hello + services: + sesv2: {ListEmailIdentities} +sesv2_SendBulkEmail: + languages: + Python: + versions: + - sdk_version: 3 + github: python/example_code/sesv2/attachments_scenario + excerpts: + - description: + snippet_tags: + - python.example_code.sesv2.SESv2Wrapper.decl + - python.example_code.sesv2.SendBulkEmail + services: + sesv2: {SendBulkEmail} +sesv2_Scenario_EmailAttachments: + title: Send emails with attachments using &SESv2; + title_abbrev: Email Attachments Scenario + synopsis: send emails with attachments using &SESv2;. + synopsis_list: + - Create an email template for bulk sends. + - Send a simple email with a file attachment. + - Send a simple email with an inline image. + - Send bulk templated emails with attachments. + - Clean up resources. + category: Scenarios + languages: + Python: + versions: + - sdk_version: 3 + github: python/example_code/sesv2/attachments_scenario + excerpts: + - description: Run an interactive scenario demonstrating email attachments. + snippet_tags: + - python.example_code.sesv2.Scenario_EmailAttachments + - description: Create an SESv2 wrapper class to manage operations. + snippet_tags: + - python.example_code.sesv2.SESv2Wrapper.class + services: + sesv2: {SendEmail, SendBulkEmail, CreateEmailIdentity, CreateEmailTemplate, GetEmailIdentity, DeleteEmailTemplate, DeleteEmailIdentity} diff --git a/.gitignore b/.gitignore index 657ad41f507..f9318473e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ kotlin/services/**/.kotlin/ .kiro/settings/ .kiro/steering/ +/codeloom_outputs diff --git a/python/example_code/sesv2/README.md b/python/example_code/sesv2/README.md index a35582ddd21..8a9c02da473 100644 --- a/python/example_code/sesv2/README.md +++ b/python/example_code/sesv2/README.md @@ -34,26 +34,34 @@ python -m pip install -r requirements.txt +### Get started + +- [Hello Amazon SES v2 API](attachments_scenario/sesv2_hello.py#L18) (`ListEmailIdentities`) + + ### Single actions Code excerpts that show you how to call individual service functions. -- [CreateContact](newsletter.py#L155) -- [CreateContactList](newsletter.py#L105) -- [CreateEmailIdentity](newsletter.py#L92) -- [CreateEmailTemplate](newsletter.py#L118) -- [DeleteContactList](newsletter.py#L258) -- [DeleteEmailIdentity](newsletter.py#L286) -- [DeleteEmailTemplate](newsletter.py#L271) -- [ListContacts](newsletter.py#L198) -- [SendEmail](newsletter.py#L164) +- [CreateContact](newsletter_scenario/newsletter.py#L155) +- [CreateContactList](newsletter_scenario/newsletter.py#L105) +- [CreateEmailIdentity](attachments_scenario/sesv2_wrapper.py#L73) +- [CreateEmailTemplate](attachments_scenario/sesv2_wrapper.py#L109) +- [DeleteContactList](newsletter_scenario/newsletter.py#L258) +- [DeleteEmailIdentity](attachments_scenario/sesv2_wrapper.py#L321) +- [DeleteEmailTemplate](attachments_scenario/sesv2_wrapper.py#L291) +- [GetEmailIdentity](attachments_scenario/sesv2_wrapper.py#L42) +- [ListContacts](newsletter_scenario/newsletter.py#L198) +- [SendBulkEmail](attachments_scenario/sesv2_wrapper.py#L227) +- [SendEmail](attachments_scenario/sesv2_wrapper.py#L155) ### Scenarios Code examples that show you how to accomplish a specific task by calling multiple functions within the same service. -- [Newsletter scenario](newsletter.py) +- [Email Attachments Scenario](attachments_scenario/scenario_sesv2_email_attachments.py) +- [Newsletter scenario](newsletter_scenario/newsletter.py) @@ -74,7 +82,37 @@ To run the Newsletter example, copy the files from workflows/sesv2_weekly_mailer +#### Hello Amazon SES v2 API + +This example shows you how to get started using Amazon SES v2 API. + +``` +python attachments_scenario/sesv2_hello.py +``` + + +#### Email Attachments Scenario + +This example shows you how to send emails with attachments using Amazon SES v2 API. + +- Create an email template for bulk sends. +- Send a simple email with a file attachment. +- Send a simple email with an inline image. +- Send bulk templated emails with attachments. +- Clean up resources. + + + + +Start the example by running the following at a command prompt: + +``` +python attachments_scenario/scenario_sesv2_email_attachments.py +``` + + + #### Newsletter scenario @@ -87,7 +125,7 @@ This example shows you how to run the Amazon SES v2 API newsletter scenario. Start the example by running the following at a command prompt: ``` -python newsletter.py +python newsletter_scenario/newsletter.py ``` diff --git a/python/example_code/sesv2/attachments_scenario/scenario_sesv2_email_attachments.py b/python/example_code/sesv2/attachments_scenario/scenario_sesv2_email_attachments.py new file mode 100644 index 00000000000..8b3c2d740a0 --- /dev/null +++ b/python/example_code/sesv2/attachments_scenario/scenario_sesv2_email_attachments.py @@ -0,0 +1,396 @@ +# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +# SPDX-License-Identifier: Apache-2.0 + +""" +Purpose + Shows how to use the AWS SDK for Python (Boto3) with Amazon SESv2 to + send emails with attachments. This scenario demonstrates three use cases: + 1. Send a simple email with a file attachment. + 2. Send a simple email with an inline image rendered in the HTML body. + 3. Send bulk templated emails with attachments to multiple recipients. + + The new attachment support eliminates the need for developers to construct + raw MIME messages. SES handles the MIME assembly automatically. +""" + +import json +import logging +import sys + +import boto3 +from botocore.exceptions import ClientError + +from sesv2_wrapper import SESv2Wrapper + +# Add relative path to include demo_tools in this code example without need for setup. +sys.path.append("../../..") +import demo_tools.question as q # noqa + +logger = logging.getLogger(__name__) + + +# snippet-start:[python.example_code.sesv2.Scenario_EmailAttachments] +class SESv2EmailAttachmentsScenario: + """ + Demonstrates sending emails with attachments using Amazon SESv2. + + This scenario demonstrates: + 1. Setting up an email identity and template. + 2. Sending a simple email with a file attachment. + 3. Sending a simple email with an inline image. + 4. Sending bulk templated emails with attachments. + 5. Cleaning up created resources. + """ + + TEMPLATE_NAME = "AttachmentDemoTemplate" + + def __init__(self, sesv2_wrapper: SESv2Wrapper) -> None: + """ + :param sesv2_wrapper: An instance of the SESv2Wrapper class. + """ + self.sesv2_wrapper = sesv2_wrapper + self.sender_email = "" + self.recipient_emails: list = [] + self.identity_was_created = False + + def run_scenario(self) -> None: + """Runs the SESv2 email attachments scenario.""" + print("-" * 88) + print("Welcome to the Amazon SESv2 Email Attachments Scenario.") + print("-" * 88) + print( + "This scenario demonstrates how to send emails with attachments\n" + "using SESv2 attachment support. SES handles MIME\n" + "construction automatically, so you don't need to build raw\n" + "MIME messages.\n" + ) + + try: + self._setup() + self._step1_send_email_with_attachment() + self._step2_send_email_with_inline_image() + self._step3_send_bulk_email_with_attachments() + except Exception as e: + logger.error("Scenario failed: %s", e) + print(f"\nThe scenario encountered an error: {e}") + finally: + self._cleanup() + + # ---------- Setup ---------- + + def _setup(self) -> None: + """ + Prompts for configuration, verifies the sender identity, prepares a + sample attachment, and creates an email template. + """ + print("\n--- Setup ---\n") + + # Prompt for sender and recipient addresses. + print( + "Both sender and recipient addresses must be verified if your\n" + "account is in the SES sandbox.\n" + ) + self.sender_email = q.ask( + "Enter a verified sender email address: " + ) + recipient_input = q.ask( + "Enter one or more recipient email addresses (comma-separated): " + ) + self.recipient_emails = [ + addr.strip() for addr in recipient_input.split(",") if addr.strip() + ] + + # Verify the sender identity. + print(f"\nChecking identity for {self.sender_email}...") + try: + identity_info = self.sesv2_wrapper.get_email_identity( + self.sender_email + ) + verified = identity_info.get("VerifiedForSendingStatus", False) + if verified: + print(f" {self.sender_email} is verified and ready to send.") + else: + print( + f" {self.sender_email} exists but is not yet verified." + ) + except ClientError as err: + if err.response["Error"]["Code"] == "NotFoundException": + print( + f" Identity {self.sender_email} not found. " + "Creating it now..." + ) + result = self.sesv2_wrapper.create_email_identity( + self.sender_email + ) + self.identity_was_created = True + print( + f" Identity created. Verification status: " + f"{result.get('VerifiedForSendingStatus', False)}" + ) + print( + " Check your inbox and click the verification link " + "before continuing." + ) + q.ask("Press Enter when you have verified the address...") + else: + raise + + # Create the email template for the bulk-send step. + print("\nCreating email template for the bulk email step...") + try: + self.sesv2_wrapper.create_email_template( + template_name=self.TEMPLATE_NAME, + subject="Bulk Email with Attachment for {{name}}", + html_body=( + "
Please find the attached document.
" + ), + text_body=( + "Hello {{name}}, Please find the attached document." + ), + ) + print(f" Template '{self.TEMPLATE_NAME}' created.\n") + except ClientError as err: + if err.response["Error"]["Code"] == "AlreadyExistsException": + print( + f" Template '{self.TEMPLATE_NAME}' already exists. " + "Using it.\n" + ) + else: + raise + + # ---------- Step 1: Simple email with file attachment ---------- + + def _step1_send_email_with_attachment(self) -> None: + """Sends a simple email with a text file attachment.""" + print("\n--- Step 1: Send a Simple Email with a File Attachment ---\n") + print( + "Creating a sample text file attachment and sending it with\n" + "the Simple email content type. SES constructs the MIME message\n" + "automatically.\n" + ) + + # Prepare a sample text file as bytes. + sample_content = b"This is a sample report attachment." + + attachment = { + "RawContent": sample_content, + "FileName": "sample-report.txt", + "ContentType": "text/plain", + "ContentDisposition": "ATTACHMENT", + "ContentDescription": "Sample report text file", + "ContentTransferEncoding": "BASE64", + } + + print( + "Note: When using an AWS SDK, the SDK handles base64 encoding\n" + "automatically. Direct API callers must encode content themselves.\n" + ) + + message_id = self.sesv2_wrapper.send_email( + from_address=self.sender_email, + to_addresses=self.recipient_emails, + subject="SESv2 Attachment Demo - Simple Email with Attachment", + html_body=( + "Please see the attached report document.
" + ), + text_body="Please see the attached report document.", + attachments=[attachment], + ) + + print(f" Email sent. MessageId: {message_id}") + print( + " SES automatically constructed the MIME message with the " + "attachment.\n" + ) + + # ---------- Step 2: Simple email with inline image ---------- + + def _step2_send_email_with_inline_image(self) -> None: + """Sends a simple email with an inline image that renders in HTML.""" + print("\n--- Step 2: Send a Simple Email with an Inline Image ---\n") + print( + "This step demonstrates INLINE disposition. The image renders\n" + "directly in the HTML body using a 'cid:' reference instead of\n" + "appearing as a downloadable attachment.\n" + ) + + # Create a minimal 1x1 red PNG (valid PNG file). + sample_image = ( + b"\x89PNG\r\n\x1a\n" # PNG signature + b"\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01" + b"\x08\x02\x00\x00\x00\x90wS\xde" # 1x1 RGB + b"\x00\x00\x00\x0cIDATx\x9cc\xf8\x0f\x00\x00\x01\x01" + b"\x00\x05\x18\xd8N" # compressed data + b"\x00\x00\x00\x00IEND\xaeB`\x82" # IEND + ) + + attachment = { + "RawContent": sample_image, + "FileName": "logo.png", + "ContentType": "image/png", + "ContentDisposition": "INLINE", + "ContentId": "logo123", + "ContentDescription": "Company logo", + "ContentTransferEncoding": "BASE64", + } + + html_body = ( + "" + "Here is our logo:
" + 'Integration test with attachment.
", + text_body="Integration test with attachment.", + attachments=[attachment], + ) + assert message_id is not None + assert len(message_id) > 0 + finally: + # No cleanup needed for sent emails. + pass + + +@pytest.mark.integ +def test_sesv2_wrapper_send_email_with_inline_image(): + """Test sending a simple email with an inline image.""" + sender_email = os.environ.get("SENDER_EMAIL") + recipient_email = os.environ.get("RECIPIENT_EMAIL", sender_email) + if not sender_email: + pytest.skip("SENDER_EMAIL env var not set; skipping integration test.") + + wrapper = SESv2Wrapper.from_client() + + # Minimal 1x1 PNG + sample_image = ( + b"\x89PNG\r\n\x1a\n" + b"\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01" + b"\x08\x02\x00\x00\x00\x90wS\xde" + b"\x00\x00\x00\x0cIDATx\x9cc\xf8\x0f\x00\x00\x01\x01" + b"\x00\x05\x18\xd8N" + b"\x00\x00\x00\x00IEND\xaeB`\x82" + ) + + attachment = { + "RawContent": sample_image, + "FileName": "test-logo.png", + "ContentType": "image/png", + "ContentDisposition": "INLINE", + "ContentId": "testlogo123", + "ContentDescription": "Test logo", + "ContentTransferEncoding": "BASE64", + } + + html_body = ( + 'Attached document.
", + text_body="Hello {{name}}, attached document.", + ) + + bulk_entries = [ + { + "Destination": {"ToAddresses": [recipient_email]}, + "ReplacementEmailContent": { + "ReplacementTemplate": { + "ReplacementTemplateData": '{"name": "TestUser"}' + } + }, + } + ] + + results = wrapper.send_bulk_email( + from_address=sender_email, + template_name=template_name, + default_template_data='{"name": "Default"}', + bulk_entries=bulk_entries, + attachments=[attachment], + ) + + assert results is not None + assert len(results) == 1 + assert results[0].get("Status") == "SUCCESS" + finally: + try: + wrapper.delete_email_template(template_name) + except Exception: + pass + + +@pytest.mark.integ +def test_sesv2_hello(capsys): + """Test the Hello SESv2 example.""" + from sesv2_hello import hello_sesv2 # noqa: resolved via sys.path + + try: + hello_sesv2(boto3.client("sesv2")) + captured = capsys.readouterr() + assert "Hello, Amazon SESv2!" in captured.out + finally: + pass \ No newline at end of file diff --git a/python/example_code/sesv2/newsletter.py b/python/example_code/sesv2/newsletter_scenario/newsletter.py similarity index 100% rename from python/example_code/sesv2/newsletter.py rename to python/example_code/sesv2/newsletter_scenario/newsletter.py diff --git a/python/example_code/sesv2/newsletter_test.py b/python/example_code/sesv2/newsletter_scenario/test/newsletter_test.py similarity index 99% rename from python/example_code/sesv2/newsletter_test.py rename to python/example_code/sesv2/newsletter_scenario/test/newsletter_test.py index 10633b9a41e..f98204e6b69 100644 --- a/python/example_code/sesv2/newsletter_test.py +++ b/python/example_code/sesv2/newsletter_scenario/test/newsletter_test.py @@ -2,12 +2,15 @@ # SPDX-License-Identifier: Apache-2.0 import json +import os import sys from botocore.exceptions import ClientError from io import StringIO import pytest from unittest.mock import patch +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) + from newsletter import ( SESv2Workflow, get_subaddress_variants, diff --git a/python/example_code/sesv2/requirements.txt b/python/example_code/sesv2/requirements.txt index 621e276912d..1ce66d6ec91 100644 --- a/python/example_code/sesv2/requirements.txt +++ b/python/example_code/sesv2/requirements.txt @@ -1,2 +1,3 @@ -boto3>=1.26.79 +boto3>=1.35.0 +botocore>=1.35.0 pytest>=7.2.1 diff --git a/scenarios/features/sesv2_attachments/README.md b/scenarios/features/sesv2_attachments/README.md new file mode 100644 index 00000000000..7efe851b09e --- /dev/null +++ b/scenarios/features/sesv2_attachments/README.md @@ -0,0 +1,52 @@ +# Amazon SESv2 Email Attachments Scenario + +## Overview + +This example shows how to use AWS SDKs to send emails with attachments using Amazon Simple Email Service v2 (Amazon SESv2). The scenario demonstrates the attachment support in the SESv2 `SendEmail` and `SendBulkEmail` APIs, which enables customers to include file attachments directly in simple and templated email messages without constructing raw MIME messages. SES handles the MIME construction automatically. + +[Working with email attachments in SES](https://docs.aws.amazon.com/ses/latest/dg/attachments.html) describes how to attach files such as PDFs, images, and documents to emails sent through Amazon SES. + +This scenario demonstrates the following steps and tasks: + +1. Set up a verified email identity and create an email template. +2. Send a simple email with a file attachment. + - SES automatically constructs the MIME message with the attachment. +3. Send a simple email with an inline image. + - Uses `INLINE` content disposition and `cid:` references to render images in the HTML body. +4. Send bulk templated emails with attachments to multiple recipients. + - Uses `SendBulkEmail` with personalized template data per recipient. +5. Clean up resources (delete the template and optionally the email identity). + +### Resources + +- A verified email identity (email address or domain) in Amazon SES is required to send emails. The scenario will verify or create a sender email identity. +- An email template is created during the scenario to demonstrate attachments with templated email content via `SendBulkEmail`. +- No additional AWS infrastructure (such as CloudFormation stacks) is required. + +### API actions used + +- [SendEmail](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_SendEmail.html) +- [SendBulkEmail](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_SendBulkEmail.html) +- [CreateEmailIdentity](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_CreateEmailIdentity.html) +- [CreateEmailTemplate](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_CreateEmailTemplate.html) +- [GetEmailIdentity](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_GetEmailIdentity.html) +- [DeleteEmailTemplate](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_DeleteEmailTemplate.html) +- [DeleteEmailIdentity](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_DeleteEmailIdentity.html) + +## Implementations + +This example is implemented in the following languages: + +- [Python](../../../python/example_code/sesv2/attachments_scenario/README.md) + +## Additional reading + +- [What is Amazon SES?](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/Welcome.html) +- [Working with email attachments in SES](https://docs.aws.amazon.com/ses/latest/dg/attachments.html) +- [Moving out of the Amazon SES sandbox](https://docs.aws.amazon.com/ses/latest/dg/request-production-access.html) +- [Attachment object structure](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_Attachment.html) +- [Unsupported attachment types](https://docs.aws.amazon.com/ses/latest/dg/mime-types-appendix.html) + +--- + +Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 diff --git a/scenarios/features/sesv2_attachments/SPECIFICATION.md b/scenarios/features/sesv2_attachments/SPECIFICATION.md new file mode 100644 index 00000000000..be2c4fbf0ff --- /dev/null +++ b/scenarios/features/sesv2_attachments/SPECIFICATION.md @@ -0,0 +1,69 @@ +# Amazon SESv2 Email Attachments Specification + +This document contains a draft proposal for a Code Example for *Amazon SESv2 Email Attachments Scenario*, generated by the Code Examples SpecGen AI tool. The specification demonstrates the new attachment support in the SESv2 `SendEmail` and `SendBulkEmail` APIs. This feature enables customers to include file attachments (such as PDFs, images, and documents) directly in simple and templated email messages without constructing raw MIME messages. SES handles the complex MIME construction automatically, greatly simplifying the developer experience for sending emails with attachments. The following should be reviewed for accuracy and correctness before proceeding on to a final specification. + +### Resources + +- A verified email identity (email address or domain) in Amazon SES is required to send emails. The scenario will use a pre-verified sender email address. +- An email template is needed to demonstrate attachments with templated email content via `SendBulkEmail`. +- No additional AWS infrastructure (such as CloudFormation stacks) is required. + +### Relevant documentation + +* [What is Amazon SES?](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/Welcome.html) +* [Working with email attachments in SES](https://docs.aws.amazon.com/ses/latest/dg/attachments.html) +* [Sending raw email using the Amazon SES API v2](https://docs.aws.amazon.com/ses/latest/dg/send-email-raw.html) +* [Amazon SES API v2 Reference](https://docs.aws.amazon.com/ses/latest/APIReference-V2/Welcome.html) +* [Unsupported attachment types](https://docs.aws.amazon.com/ses/latest/dg/mime-types-appendix.html) +* [Attachment object structure](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_Attachment.html) + +### API Actions Used + +* [SendEmail](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_SendEmail.html) +* [SendBulkEmail](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_SendBulkEmail.html) +* [CreateEmailIdentity](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_CreateEmailIdentity.html) +* [CreateEmailTemplate](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_CreateEmailTemplate.html) +* [GetEmailIdentity](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_GetEmailIdentity.html) +* [DeleteEmailTemplate](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_DeleteEmailTemplate.html) +* [DeleteEmailIdentity](https://docs.aws.amazon.com/ses/latest/APIReference-V2/API_DeleteEmailIdentity.html) + +## Hello SESv2 + +The Hello example is a separate runnable example that introduces the SESv2 service. + +- Set up the SESv2 service client. +- Call `ListEmailIdentities` to retrieve up to 5 email identities associated with the account. +- Display the list of identities along with their type and verification status. + +## Scenario + +This scenario demonstrates how to send emails with attachments using Amazon SESv2. + +## Errors + +| action | Error | Handling | +|-----------------------|------------------------|----------------------------------------------------------------------------------------------| +| `GetEmailIdentity` | `NotFoundException` | Identity not found. Prompt to create a new one. | +| `CreateEmailIdentity` | `LimitExceededException` | Notify the user that the maximum number of email identities has been reached. | +| `CreateEmailTemplate` | `AlreadyExistsException` | Skip creation and use the existing template. | +| `CreateEmailTemplate` | `LimitExceededException` | Notify the user that the maximum number of email templates has been reached. | +| `SendEmail` | `MessageRejected` | Notify the user to check attachment file types and total message size (under 40 MB). | +| `SendBulkEmail` | `MessageRejected` | Notify the user to check the template, attachment file types, and total message size limits. | +| `DeleteEmailTemplate` | `NotFoundException` | Notify the user the template was already deleted. | +| `DeleteEmailIdentity` | `NotFoundException` | Notify the user the identity was already deleted. | + +--- + +## Metadata + +| action / scenario | metadata file | metadata key | +| - | - | - | +| `SESv2 Hello` | sesv2_metadata.yaml | sesv2_Hello | +| `SendEmail` | sesv2_metadata.yaml | sesv2_SendEmail | +| `SendBulkEmail` | sesv2_metadata.yaml | sesv2_SendBulkEmail | +| `CreateEmailIdentity` | sesv2_metadata.yaml | sesv2_CreateEmailIdentity | +| `CreateEmailTemplate` | sesv2_metadata.yaml | sesv2_CreateEmailTemplate | +| `GetEmailIdentity` | sesv2_metadata.yaml | sesv2_GetEmailIdentity | +| `DeleteEmailTemplate` | sesv2_metadata.yaml | sesv2_DeleteEmailTemplate | +| `DeleteEmailIdentity` | sesv2_metadata.yaml | sesv2_DeleteEmailIdentity | +| `SESv2 Email Attachments Scenario` | sesv2_metadata.yaml | sesv2_Scenario_EmailAttachments |