diff --git a/.gitignore b/.gitignore index 9d19f87..5b81476 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ yarn-error.log* /js/public/logo.png /.mypy_cache/ /activate.sh +/scratch/ diff --git a/aioaws/ses.py b/aioaws/ses.py index 5f07003..4945d1b 100644 --- a/aioaws/ses.py +++ b/aioaws/ses.py @@ -40,6 +40,7 @@ class SesAttachment: file: Union[Path, bytes] name: Optional[str] = None mime_type: Optional[str] = None + content_id: Optional[str] = None @dataclass @@ -188,7 +189,11 @@ async def prepare_attachment(a: SesAttachment) -> Tuple[MIMEBase, int]: msg.set_payload(data) encode_base64(msg) - msg.add_header('Content-Disposition', 'attachment', filename=filename) + if a.content_id is None: + msg.add_header('Content-Disposition', 'attachment', filename=filename) + else: + msg.add_header('Content-ID', a.content_id) + msg.add_header('Content-Disposition', 'inline', filename=filename) return msg, len(data) diff --git a/aioaws/testing.py b/aioaws/testing.py index 94c75d8..63d0077 100644 --- a/aioaws/testing.py +++ b/aioaws/testing.py @@ -22,8 +22,9 @@ def ses_email_data(data: Dict[str, str]) -> Dict[str, Any]: for part in msg.walk(): if payload := part.get_payload(decode=True): part_info = {'Content-Type': part.get_content_type(), 'payload': payload.decode().replace('\r\n', '\n')} - if cd := part['Content-Disposition']: - part_info['Content-Disposition'] = cd + for key in 'Content-Disposition', 'Content-ID': + if cd := part[key]: + part_info[key] = cd d['payload'].append(part_info) return {'body': dict(data), 'email': d} diff --git a/tests/test_ses.py b/tests/test_ses.py index b81af78..a88c7f0 100644 --- a/tests/test_ses.py +++ b/tests/test_ses.py @@ -248,6 +248,36 @@ async def test_custom_headers(client: AsyncClient, aws: DummyServer): } +async def test_inline_attachment(client: AsyncClient, aws: DummyServer): + ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) + + await ses.send_email( + 'testing@sender.com', + 'the subject', + ['testing@recipient.com'], + 'this is a test email', + html_body='this is the html body.', + attachments=[SesAttachment(file=b'some attachment', name='foobar.txt', content_id='')], + ) + assert len(aws.app['emails']) == 1 + assert aws.app['emails'][0]['email']['payload'] == [ + { + 'Content-Type': 'text/plain', + 'payload': 'this is a test email\n', + }, + { + 'Content-Type': 'text/html', + 'payload': 'this is the html body.\n', + }, + { + 'Content-Type': 'text/plain', + 'payload': 'some attachment', + 'Content-Disposition': 'inline; filename="foobar.txt"', + 'Content-ID': '', + }, + ] + + async def test_encoded_unsub(client: AsyncClient, aws: DummyServer): ses = SesClient(client, SesConfig('test_access_key', 'test_secret_key', 'testing-region-1')) unsub_link = 'https://www.example.com/unsubscrible?blob=?blob=$MzMgMTYwMTY3MDEyOCBMMzcbN_nhcDZNg-6D=='