sendery-django 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sendery_django-0.1.0/LICENSE +21 -0
- sendery_django-0.1.0/PKG-INFO +104 -0
- sendery_django-0.1.0/README.md +90 -0
- sendery_django-0.1.0/pyproject.toml +20 -0
- sendery_django-0.1.0/setup.cfg +4 -0
- sendery_django-0.1.0/src/sendery_django/__init__.py +11 -0
- sendery_django-0.1.0/src/sendery_django/backend.py +20 -0
- sendery_django-0.1.0/src/sendery_django/forms.py +12 -0
- sendery_django-0.1.0/src/sendery_django.egg-info/PKG-INFO +104 -0
- sendery_django-0.1.0/src/sendery_django.egg-info/SOURCES.txt +12 -0
- sendery_django-0.1.0/src/sendery_django.egg-info/dependency_links.txt +1 -0
- sendery_django-0.1.0/src/sendery_django.egg-info/requires.txt +2 -0
- sendery_django-0.1.0/src/sendery_django.egg-info/top_level.txt +1 -0
- sendery_django-0.1.0/tests/test_backend.py +46 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sendery contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sendery-django
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Sendery template email client for Django
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Documentation, https://sendery.co/en/docs/django
|
|
7
|
+
Project-URL: Repository, https://github.com/sendery-co/sendery-django
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: sendery<0.2,>=0.1
|
|
12
|
+
Requires-Dist: Django<5.3,>=5.2
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# Sendery for Django
|
|
16
|
+
|
|
17
|
+
Send Sendery templates through Django’s email backend.
|
|
18
|
+
|
|
19
|
+
[Documentation](https://sendery.co/en/docs/django) · [API reference](https://sendery.co/en/docs/send-email) · [Changelog](CHANGELOG.md)
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
Django 5.2 and Python 3.10+.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install sendery-django
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Configure the backend
|
|
32
|
+
|
|
33
|
+
Publish a `welcome` template with `name` and `action_url` variables, and create a [project API key](https://sendery.co/en/docs/authentication). Store it as `SENDERY_API_KEY` on your server. Add the backend and key to your Django settings.
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
# settings.py
|
|
37
|
+
import os
|
|
38
|
+
|
|
39
|
+
EMAIL_BACKEND = "sendery_django.backend.EmailBackend"
|
|
40
|
+
SENDERY_API_KEY = os.environ["SENDERY_API_KEY"]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Send an email
|
|
44
|
+
|
|
45
|
+
Use `TemplateEmail` with one recipient. Its `sendery_receipt` contains the accepted email’s ID and status. Ordinary `EmailMessage` objects, attachments, and `cc` or `bcc` recipients are not supported. Set the sender in your Sendery project.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from sendery_django import TemplateEmail
|
|
49
|
+
|
|
50
|
+
email = TemplateEmail(
|
|
51
|
+
to="alex@example.com",
|
|
52
|
+
template="welcome",
|
|
53
|
+
data={"name": "Alex", "action_url": "https://example.com/start"},
|
|
54
|
+
)
|
|
55
|
+
email.send(fail_silently=False)
|
|
56
|
+
|
|
57
|
+
print(email.sendery_receipt["id"])
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Password resets
|
|
61
|
+
|
|
62
|
+
Publish a `password-reset` template with `name` and `action_url`. Use `SenderyPasswordResetForm` with Django’s authentication views. Add these routes to your `urlpatterns` and provide Django’s standard password-reset HTML pages under `templates/registration/`. If the routes already exist, change only the reset view’s `form_class`.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
# urls.py
|
|
66
|
+
from django.contrib.auth import views as auth_views
|
|
67
|
+
from django.urls import path
|
|
68
|
+
from sendery_django.forms import SenderyPasswordResetForm
|
|
69
|
+
|
|
70
|
+
urlpatterns = [
|
|
71
|
+
path("password-reset/", auth_views.PasswordResetView.as_view(
|
|
72
|
+
form_class=SenderyPasswordResetForm,
|
|
73
|
+
), name="password_reset"),
|
|
74
|
+
path("password-reset/done/", auth_views.PasswordResetDoneView.as_view(),
|
|
75
|
+
name="password_reset_done"),
|
|
76
|
+
path("reset/<uidb64>/<token>/", auth_views.PasswordResetConfirmView.as_view(),
|
|
77
|
+
name="password_reset_confirm"),
|
|
78
|
+
path("reset/done/", auth_views.PasswordResetCompleteView.as_view(),
|
|
79
|
+
name="password_reset_complete"),
|
|
80
|
+
]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Retry a send
|
|
84
|
+
|
|
85
|
+
Keep `fail_silently=False` to receive [`SenderyError`](https://sendery.co/en/docs/python) on API failures. The backend makes one attempt. In a [background task](https://sendery.co/en/docs/queues), save the recipient, variables, and `idempotency_key` before sending and reuse them for retries.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
# Use the same saved recipient, variables, and event key on every attempt.
|
|
89
|
+
email = TemplateEmail(
|
|
90
|
+
to="alex@example.com",
|
|
91
|
+
template="welcome",
|
|
92
|
+
data={"name": "Alex", "action_url": "https://example.com/start"},
|
|
93
|
+
idempotency_key="welcome-123",
|
|
94
|
+
)
|
|
95
|
+
email.send(fail_silently=False)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## More
|
|
99
|
+
|
|
100
|
+
See [idempotency and retries](https://sendery.co/en/docs/idempotency) for retry conditions, delays, and reusing a key across attempts.
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Sendery for Django
|
|
2
|
+
|
|
3
|
+
Send Sendery templates through Django’s email backend.
|
|
4
|
+
|
|
5
|
+
[Documentation](https://sendery.co/en/docs/django) · [API reference](https://sendery.co/en/docs/send-email) · [Changelog](CHANGELOG.md)
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
Django 5.2 and Python 3.10+.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install sendery-django
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Configure the backend
|
|
18
|
+
|
|
19
|
+
Publish a `welcome` template with `name` and `action_url` variables, and create a [project API key](https://sendery.co/en/docs/authentication). Store it as `SENDERY_API_KEY` on your server. Add the backend and key to your Django settings.
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
# settings.py
|
|
23
|
+
import os
|
|
24
|
+
|
|
25
|
+
EMAIL_BACKEND = "sendery_django.backend.EmailBackend"
|
|
26
|
+
SENDERY_API_KEY = os.environ["SENDERY_API_KEY"]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Send an email
|
|
30
|
+
|
|
31
|
+
Use `TemplateEmail` with one recipient. Its `sendery_receipt` contains the accepted email’s ID and status. Ordinary `EmailMessage` objects, attachments, and `cc` or `bcc` recipients are not supported. Set the sender in your Sendery project.
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from sendery_django import TemplateEmail
|
|
35
|
+
|
|
36
|
+
email = TemplateEmail(
|
|
37
|
+
to="alex@example.com",
|
|
38
|
+
template="welcome",
|
|
39
|
+
data={"name": "Alex", "action_url": "https://example.com/start"},
|
|
40
|
+
)
|
|
41
|
+
email.send(fail_silently=False)
|
|
42
|
+
|
|
43
|
+
print(email.sendery_receipt["id"])
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Password resets
|
|
47
|
+
|
|
48
|
+
Publish a `password-reset` template with `name` and `action_url`. Use `SenderyPasswordResetForm` with Django’s authentication views. Add these routes to your `urlpatterns` and provide Django’s standard password-reset HTML pages under `templates/registration/`. If the routes already exist, change only the reset view’s `form_class`.
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
# urls.py
|
|
52
|
+
from django.contrib.auth import views as auth_views
|
|
53
|
+
from django.urls import path
|
|
54
|
+
from sendery_django.forms import SenderyPasswordResetForm
|
|
55
|
+
|
|
56
|
+
urlpatterns = [
|
|
57
|
+
path("password-reset/", auth_views.PasswordResetView.as_view(
|
|
58
|
+
form_class=SenderyPasswordResetForm,
|
|
59
|
+
), name="password_reset"),
|
|
60
|
+
path("password-reset/done/", auth_views.PasswordResetDoneView.as_view(),
|
|
61
|
+
name="password_reset_done"),
|
|
62
|
+
path("reset/<uidb64>/<token>/", auth_views.PasswordResetConfirmView.as_view(),
|
|
63
|
+
name="password_reset_confirm"),
|
|
64
|
+
path("reset/done/", auth_views.PasswordResetCompleteView.as_view(),
|
|
65
|
+
name="password_reset_complete"),
|
|
66
|
+
]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Retry a send
|
|
70
|
+
|
|
71
|
+
Keep `fail_silently=False` to receive [`SenderyError`](https://sendery.co/en/docs/python) on API failures. The backend makes one attempt. In a [background task](https://sendery.co/en/docs/queues), save the recipient, variables, and `idempotency_key` before sending and reuse them for retries.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
# Use the same saved recipient, variables, and event key on every attempt.
|
|
75
|
+
email = TemplateEmail(
|
|
76
|
+
to="alex@example.com",
|
|
77
|
+
template="welcome",
|
|
78
|
+
data={"name": "Alex", "action_url": "https://example.com/start"},
|
|
79
|
+
idempotency_key="welcome-123",
|
|
80
|
+
)
|
|
81
|
+
email.send(fail_silently=False)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## More
|
|
85
|
+
|
|
86
|
+
See [idempotency and retries](https://sendery.co/en/docs/idempotency) for retry conditions, delays, and reusing a key across attempts.
|
|
87
|
+
|
|
88
|
+
## License
|
|
89
|
+
|
|
90
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "sendery-django"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Sendery template email client for Django"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
dependencies = ["sendery>=0.1,<0.2", "Django>=5.2,<5.3"]
|
|
14
|
+
|
|
15
|
+
[tool.setuptools.packages.find]
|
|
16
|
+
where = ["src"]
|
|
17
|
+
|
|
18
|
+
[project.urls]
|
|
19
|
+
Documentation = "https://sendery.co/en/docs/django"
|
|
20
|
+
Repository = "https://github.com/sendery-co/sendery-django"
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Sendery integration for Django."""
|
|
2
|
+
from django.core.mail import EmailMessage
|
|
3
|
+
import json
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TemplateEmail(EmailMessage):
|
|
8
|
+
def __init__(self, *, to, template, data, locale=None, idempotency_key=None, **kwargs):
|
|
9
|
+
super().__init__(to=[to], **kwargs)
|
|
10
|
+
self.sendery_payload = json.loads(json.dumps({"template": template, "data": data, "locale": locale}, allow_nan=False))
|
|
11
|
+
self.sendery_key = idempotency_key if idempotency_key is not None else str(uuid.uuid4())
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.core.mail.backends.base import BaseEmailBackend
|
|
3
|
+
from sendery import Sendery, SenderyError
|
|
4
|
+
from . import TemplateEmail
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class EmailBackend(BaseEmailBackend):
|
|
8
|
+
def send_messages(self, email_messages):
|
|
9
|
+
client = Sendery(settings.SENDERY_API_KEY, getattr(settings, "SENDERY_URL", "https://sendery.co"))
|
|
10
|
+
sent = 0
|
|
11
|
+
for message in email_messages or []:
|
|
12
|
+
if not isinstance(message, TemplateEmail) or len(message.to) != 1 or message.cc or message.bcc or message.attachments:
|
|
13
|
+
raise ValueError("Use TemplateEmail with exactly one recipient and no attachments.")
|
|
14
|
+
try:
|
|
15
|
+
message.sendery_receipt = client.send(to=message.to[0], **message.sendery_payload, idempotency_key=message.sendery_key)
|
|
16
|
+
sent += 1
|
|
17
|
+
except SenderyError:
|
|
18
|
+
if not self.fail_silently:
|
|
19
|
+
raise
|
|
20
|
+
return sent
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from django.contrib.auth.forms import PasswordResetForm
|
|
2
|
+
from django.urls import reverse
|
|
3
|
+
from . import TemplateEmail
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SenderyPasswordResetForm(PasswordResetForm):
|
|
7
|
+
template_key = "password-reset"
|
|
8
|
+
|
|
9
|
+
def send_mail(self, subject_template_name, email_template_name, context, from_email, to_email, html_email_template_name=None):
|
|
10
|
+
path = reverse("password_reset_confirm", kwargs={"uidb64": context["uid"], "token": context["token"]})
|
|
11
|
+
url = f"{context['protocol']}://{context['domain']}{path}"
|
|
12
|
+
TemplateEmail(to=to_email, template=self.template_key, data={"name": context["user"].get_username(), "action_url": url}, from_email=from_email).send()
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sendery-django
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Sendery template email client for Django
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Documentation, https://sendery.co/en/docs/django
|
|
7
|
+
Project-URL: Repository, https://github.com/sendery-co/sendery-django
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: sendery<0.2,>=0.1
|
|
12
|
+
Requires-Dist: Django<5.3,>=5.2
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# Sendery for Django
|
|
16
|
+
|
|
17
|
+
Send Sendery templates through Django’s email backend.
|
|
18
|
+
|
|
19
|
+
[Documentation](https://sendery.co/en/docs/django) · [API reference](https://sendery.co/en/docs/send-email) · [Changelog](CHANGELOG.md)
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
Django 5.2 and Python 3.10+.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install sendery-django
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Configure the backend
|
|
32
|
+
|
|
33
|
+
Publish a `welcome` template with `name` and `action_url` variables, and create a [project API key](https://sendery.co/en/docs/authentication). Store it as `SENDERY_API_KEY` on your server. Add the backend and key to your Django settings.
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
# settings.py
|
|
37
|
+
import os
|
|
38
|
+
|
|
39
|
+
EMAIL_BACKEND = "sendery_django.backend.EmailBackend"
|
|
40
|
+
SENDERY_API_KEY = os.environ["SENDERY_API_KEY"]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Send an email
|
|
44
|
+
|
|
45
|
+
Use `TemplateEmail` with one recipient. Its `sendery_receipt` contains the accepted email’s ID and status. Ordinary `EmailMessage` objects, attachments, and `cc` or `bcc` recipients are not supported. Set the sender in your Sendery project.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from sendery_django import TemplateEmail
|
|
49
|
+
|
|
50
|
+
email = TemplateEmail(
|
|
51
|
+
to="alex@example.com",
|
|
52
|
+
template="welcome",
|
|
53
|
+
data={"name": "Alex", "action_url": "https://example.com/start"},
|
|
54
|
+
)
|
|
55
|
+
email.send(fail_silently=False)
|
|
56
|
+
|
|
57
|
+
print(email.sendery_receipt["id"])
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Password resets
|
|
61
|
+
|
|
62
|
+
Publish a `password-reset` template with `name` and `action_url`. Use `SenderyPasswordResetForm` with Django’s authentication views. Add these routes to your `urlpatterns` and provide Django’s standard password-reset HTML pages under `templates/registration/`. If the routes already exist, change only the reset view’s `form_class`.
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
# urls.py
|
|
66
|
+
from django.contrib.auth import views as auth_views
|
|
67
|
+
from django.urls import path
|
|
68
|
+
from sendery_django.forms import SenderyPasswordResetForm
|
|
69
|
+
|
|
70
|
+
urlpatterns = [
|
|
71
|
+
path("password-reset/", auth_views.PasswordResetView.as_view(
|
|
72
|
+
form_class=SenderyPasswordResetForm,
|
|
73
|
+
), name="password_reset"),
|
|
74
|
+
path("password-reset/done/", auth_views.PasswordResetDoneView.as_view(),
|
|
75
|
+
name="password_reset_done"),
|
|
76
|
+
path("reset/<uidb64>/<token>/", auth_views.PasswordResetConfirmView.as_view(),
|
|
77
|
+
name="password_reset_confirm"),
|
|
78
|
+
path("reset/done/", auth_views.PasswordResetCompleteView.as_view(),
|
|
79
|
+
name="password_reset_complete"),
|
|
80
|
+
]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Retry a send
|
|
84
|
+
|
|
85
|
+
Keep `fail_silently=False` to receive [`SenderyError`](https://sendery.co/en/docs/python) on API failures. The backend makes one attempt. In a [background task](https://sendery.co/en/docs/queues), save the recipient, variables, and `idempotency_key` before sending and reuse them for retries.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
# Use the same saved recipient, variables, and event key on every attempt.
|
|
89
|
+
email = TemplateEmail(
|
|
90
|
+
to="alex@example.com",
|
|
91
|
+
template="welcome",
|
|
92
|
+
data={"name": "Alex", "action_url": "https://example.com/start"},
|
|
93
|
+
idempotency_key="welcome-123",
|
|
94
|
+
)
|
|
95
|
+
email.send(fail_silently=False)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## More
|
|
99
|
+
|
|
100
|
+
See [idempotency and retries](https://sendery.co/en/docs/idempotency) for retry conditions, delays, and reusing a key across attempts.
|
|
101
|
+
|
|
102
|
+
## License
|
|
103
|
+
|
|
104
|
+
[MIT](LICENSE).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/sendery_django/__init__.py
|
|
5
|
+
src/sendery_django/backend.py
|
|
6
|
+
src/sendery_django/forms.py
|
|
7
|
+
src/sendery_django.egg-info/PKG-INFO
|
|
8
|
+
src/sendery_django.egg-info/SOURCES.txt
|
|
9
|
+
src/sendery_django.egg-info/dependency_links.txt
|
|
10
|
+
src/sendery_django.egg-info/requires.txt
|
|
11
|
+
src/sendery_django.egg-info/top_level.txt
|
|
12
|
+
tests/test_backend.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
sendery_django
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import pickle
|
|
2
|
+
import unittest
|
|
3
|
+
from unittest.mock import patch
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
if not settings.configured:
|
|
6
|
+
settings.configure(SECRET_KEY="test-only", ROOT_URLCONF=__name__, INSTALLED_APPS=["django.contrib.auth", "django.contrib.contenttypes"], SENDERY_API_KEY="test", EMAIL_BACKEND="sendery_django.backend.EmailBackend", DEFAULT_FROM_EMAIL="hello@example.com")
|
|
7
|
+
import django
|
|
8
|
+
django.setup()
|
|
9
|
+
from django.urls import path
|
|
10
|
+
from django.http import HttpResponse
|
|
11
|
+
urlpatterns = [path("reset/<uidb64>/<token>/", lambda request, **kwargs: HttpResponse(), name="password_reset_confirm")]
|
|
12
|
+
from django.core.mail import EmailMessage
|
|
13
|
+
from sendery_django import TemplateEmail
|
|
14
|
+
from sendery_django.backend import EmailBackend
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BackendTests(unittest.TestCase):
|
|
18
|
+
def test_queue_serialization_preserves_key_and_data(self):
|
|
19
|
+
data = {"name": "Original"}
|
|
20
|
+
email = TemplateEmail(to="alex@example.com", template="welcome", data=data)
|
|
21
|
+
data["name"] = "Changed"
|
|
22
|
+
restored = pickle.loads(pickle.dumps(email))
|
|
23
|
+
with patch("sendery_django.backend.Sendery") as client:
|
|
24
|
+
self.assertEqual(1, EmailBackend().send_messages([restored]))
|
|
25
|
+
payload = client.return_value.send.call_args.kwargs
|
|
26
|
+
self.assertEqual(email.sendery_key, payload["idempotency_key"])
|
|
27
|
+
self.assertEqual("Original", payload["data"]["name"])
|
|
28
|
+
|
|
29
|
+
def test_rejects_ordinary_emails_and_multiple_recipients(self):
|
|
30
|
+
with self.assertRaises(ValueError):
|
|
31
|
+
EmailBackend().send_messages([EmailMessage("subject", "body", to=["a@example.com"])])
|
|
32
|
+
email = TemplateEmail(to="a@example.com", template="welcome", data={})
|
|
33
|
+
email.to.append("b@example.com")
|
|
34
|
+
with self.assertRaises(ValueError):
|
|
35
|
+
EmailBackend().send_messages([email])
|
|
36
|
+
|
|
37
|
+
def test_password_reset_keeps_django_token_and_url(self):
|
|
38
|
+
from sendery_django.forms import SenderyPasswordResetForm
|
|
39
|
+
from types import SimpleNamespace
|
|
40
|
+
user = SimpleNamespace(get_username=lambda: "Alex")
|
|
41
|
+
context = {"uid": "MTI", "token": "signed-token", "protocol": "https", "domain": "product.example", "user": user}
|
|
42
|
+
with patch("sendery_django.forms.TemplateEmail") as message:
|
|
43
|
+
SenderyPasswordResetForm().send_mail("subject", "body", context, "hello@example.com", "alex@example.com")
|
|
44
|
+
self.assertEqual("https://product.example/reset/MTI/signed-token/", message.call_args.kwargs["data"]["action_url"])
|
|
45
|
+
self.assertEqual("password-reset", message.call_args.kwargs["template"])
|
|
46
|
+
message.return_value.send.assert_called_once()
|