mailcue 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.
- mailcue-0.1.0/.gitignore +11 -0
- mailcue-0.1.0/LICENSE +21 -0
- mailcue-0.1.0/PKG-INFO +201 -0
- mailcue-0.1.0/README.md +162 -0
- mailcue-0.1.0/mailcue/__init__.py +110 -0
- mailcue-0.1.0/mailcue/_version.py +5 -0
- mailcue-0.1.0/mailcue/auth.py +50 -0
- mailcue-0.1.0/mailcue/client.py +164 -0
- mailcue-0.1.0/mailcue/events.py +200 -0
- mailcue-0.1.0/mailcue/exceptions.py +81 -0
- mailcue-0.1.0/mailcue/py.typed +0 -0
- mailcue-0.1.0/mailcue/resources/__init__.py +3 -0
- mailcue-0.1.0/mailcue/resources/_base.py +19 -0
- mailcue-0.1.0/mailcue/resources/aliases.py +114 -0
- mailcue-0.1.0/mailcue/resources/api_keys.py +63 -0
- mailcue-0.1.0/mailcue/resources/domains.py +87 -0
- mailcue-0.1.0/mailcue/resources/emails.py +532 -0
- mailcue-0.1.0/mailcue/resources/gpg.py +169 -0
- mailcue-0.1.0/mailcue/resources/mailboxes.py +178 -0
- mailcue-0.1.0/mailcue/resources/system.py +98 -0
- mailcue-0.1.0/mailcue/transport.py +413 -0
- mailcue-0.1.0/mailcue/types.py +275 -0
- mailcue-0.1.0/pyproject.toml +124 -0
mailcue-0.1.0/.gitignore
ADDED
mailcue-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Olib AI
|
|
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.
|
mailcue-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mailcue
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for MailCue — open-source email testing and production server.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Olib-AI/mailcue
|
|
6
|
+
Project-URL: Repository, https://github.com/Olib-AI/mailcue
|
|
7
|
+
Project-URL: Documentation, https://github.com/Olib-AI/mailcue#readme
|
|
8
|
+
Project-URL: Issues, https://github.com/Olib-AI/mailcue/issues
|
|
9
|
+
Author-email: Olib AI <hello@olib.ai>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: async,dovecot,email,email-testing,imap,mailbox,mailcue,postfix,smtp,transactional-email
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Framework :: AsyncIO
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Communications :: Email
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
27
|
+
Classifier: Typing :: Typed
|
|
28
|
+
Requires-Python: >=3.9
|
|
29
|
+
Requires-Dist: httpx>=0.25.0
|
|
30
|
+
Requires-Dist: pydantic>=2.5.0
|
|
31
|
+
Requires-Dist: typing-extensions>=4.7.0; python_version < '3.11'
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: mypy>=1.8.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: respx>=0.21.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff>=0.4.0; extra == 'dev'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# MailCue Python SDK
|
|
41
|
+
|
|
42
|
+
Official Python client for [MailCue](https://github.com/Olib-AI/mailcue) — the
|
|
43
|
+
open-source email testing and production server (Postfix + Dovecot + FastAPI +
|
|
44
|
+
React) packaged as one Docker container.
|
|
45
|
+
|
|
46
|
+
The SDK is the same in dev and prod: point it at `http://localhost:8088` while
|
|
47
|
+
you build, then swap `base_url` to your production MailCue deployment when you
|
|
48
|
+
ship.
|
|
49
|
+
|
|
50
|
+
## Install
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install mailcue
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Requires Python 3.9+.
|
|
57
|
+
|
|
58
|
+
## Quick start: send an email
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from mailcue import Mailcue
|
|
62
|
+
|
|
63
|
+
client = Mailcue(api_key="mc_xxx") # base_url defaults to http://localhost:8088
|
|
64
|
+
|
|
65
|
+
result = client.emails.send(
|
|
66
|
+
from_="hello@example.com",
|
|
67
|
+
to=["user@example.com"],
|
|
68
|
+
subject="Welcome",
|
|
69
|
+
html="<h1>Hi there</h1>",
|
|
70
|
+
)
|
|
71
|
+
print(result.message_id)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Need async? Use `AsyncMailcue` — same surface, all methods become coroutines:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
import asyncio
|
|
78
|
+
from mailcue import AsyncMailcue
|
|
79
|
+
|
|
80
|
+
async def main() -> None:
|
|
81
|
+
async with AsyncMailcue(api_key="mc_xxx") as client:
|
|
82
|
+
await client.emails.send(
|
|
83
|
+
from_="hello@example.com",
|
|
84
|
+
to=["user@example.com"],
|
|
85
|
+
subject="Welcome",
|
|
86
|
+
html="<h1>Hi there</h1>",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
asyncio.run(main())
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Listing an inbox
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
inbox = client.emails.list(mailbox="user@example.com", page_size=20)
|
|
96
|
+
for email in inbox.emails:
|
|
97
|
+
print(email.uid, email.subject, email.from_address)
|
|
98
|
+
|
|
99
|
+
detail = client.emails.get(inbox.emails[0].uid, mailbox="user@example.com")
|
|
100
|
+
print(detail.text_body)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Attachments
|
|
104
|
+
|
|
105
|
+
`attachments` accepts raw `bytes`, `str`, or a `pathlib.Path`. The SDK
|
|
106
|
+
base64-encodes the content for you.
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
from pathlib import Path
|
|
110
|
+
|
|
111
|
+
client.emails.send(
|
|
112
|
+
from_="hello@example.com",
|
|
113
|
+
to=["user@example.com"],
|
|
114
|
+
subject="Invoice",
|
|
115
|
+
html="<p>See attached.</p>",
|
|
116
|
+
attachments=[
|
|
117
|
+
{
|
|
118
|
+
"filename": "invoice.pdf",
|
|
119
|
+
"content_type": "application/pdf",
|
|
120
|
+
"content": Path("./invoice.pdf"),
|
|
121
|
+
}
|
|
122
|
+
],
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Real-time events (SSE)
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
for event in client.events.stream():
|
|
130
|
+
print(event.event_type, event.data)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The async version:
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
async with AsyncMailcue(api_key="mc_xxx") as client:
|
|
137
|
+
async for event in client.events.stream():
|
|
138
|
+
print(event.event_type, event.data)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The SSE client auto-reconnects with exponential backoff if the connection
|
|
142
|
+
drops.
|
|
143
|
+
|
|
144
|
+
## Error handling
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from mailcue import (
|
|
148
|
+
Mailcue,
|
|
149
|
+
AuthenticationError,
|
|
150
|
+
NotFoundError,
|
|
151
|
+
RateLimitError,
|
|
152
|
+
ValidationError,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
client = Mailcue(api_key="mc_xxx")
|
|
156
|
+
|
|
157
|
+
try:
|
|
158
|
+
client.emails.get("not-a-real-uid", mailbox="user@example.com")
|
|
159
|
+
except NotFoundError as exc:
|
|
160
|
+
print("missing:", exc)
|
|
161
|
+
except RateLimitError as exc:
|
|
162
|
+
print(f"slow down; retry after {exc.retry_after}s")
|
|
163
|
+
except AuthenticationError:
|
|
164
|
+
print("bad API key")
|
|
165
|
+
except ValidationError as exc:
|
|
166
|
+
print("server rejected the request:", exc.detail)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Configuration
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
client = Mailcue(
|
|
173
|
+
api_key="mc_xxx", # or bearer_token="eyJ..."
|
|
174
|
+
base_url="https://mail.example.com", # default: http://localhost:8088
|
|
175
|
+
timeout=30.0, # seconds
|
|
176
|
+
max_retries=3, # 502/503/504 + network errors
|
|
177
|
+
verify=True, # set False for self-signed dev TLS
|
|
178
|
+
)
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
You can also inject your own `httpx.Client` / `httpx.AsyncClient` via
|
|
182
|
+
`http_client=` for advanced cases (custom transports, proxies, mTLS).
|
|
183
|
+
|
|
184
|
+
## Resources
|
|
185
|
+
|
|
186
|
+
| Resource | Methods |
|
|
187
|
+
|----------|---------|
|
|
188
|
+
| `client.emails` | `send`, `list`, `get`, `get_raw`, `get_attachment`, `delete`, `inject`, `bulk_inject` |
|
|
189
|
+
| `client.mailboxes` | `list`, `create`, `delete`, `stats`, `purge`, `list_emails` |
|
|
190
|
+
| `client.domains` | `list`, `create`, `get`, `verify_dns`, `delete` |
|
|
191
|
+
| `client.aliases` | `list`, `create`, `get`, `update`, `delete` |
|
|
192
|
+
| `client.gpg` | `list`, `generate`, `get`, `export_public`, `import_key`, `publish`, `delete` |
|
|
193
|
+
| `client.api_keys` | `list`, `create`, `delete` |
|
|
194
|
+
| `client.system` | `health`, `get_certificate`, `download_certificate`, `settings`, `tls_status` |
|
|
195
|
+
| `client.events` | `stream()` (SSE iterator) |
|
|
196
|
+
|
|
197
|
+
## License
|
|
198
|
+
|
|
199
|
+
MIT — see `LICENSE`.
|
|
200
|
+
|
|
201
|
+
Project home: https://github.com/Olib-AI/mailcue
|
mailcue-0.1.0/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# MailCue Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python client for [MailCue](https://github.com/Olib-AI/mailcue) — the
|
|
4
|
+
open-source email testing and production server (Postfix + Dovecot + FastAPI +
|
|
5
|
+
React) packaged as one Docker container.
|
|
6
|
+
|
|
7
|
+
The SDK is the same in dev and prod: point it at `http://localhost:8088` while
|
|
8
|
+
you build, then swap `base_url` to your production MailCue deployment when you
|
|
9
|
+
ship.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install mailcue
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Requires Python 3.9+.
|
|
18
|
+
|
|
19
|
+
## Quick start: send an email
|
|
20
|
+
|
|
21
|
+
```python
|
|
22
|
+
from mailcue import Mailcue
|
|
23
|
+
|
|
24
|
+
client = Mailcue(api_key="mc_xxx") # base_url defaults to http://localhost:8088
|
|
25
|
+
|
|
26
|
+
result = client.emails.send(
|
|
27
|
+
from_="hello@example.com",
|
|
28
|
+
to=["user@example.com"],
|
|
29
|
+
subject="Welcome",
|
|
30
|
+
html="<h1>Hi there</h1>",
|
|
31
|
+
)
|
|
32
|
+
print(result.message_id)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Need async? Use `AsyncMailcue` — same surface, all methods become coroutines:
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
import asyncio
|
|
39
|
+
from mailcue import AsyncMailcue
|
|
40
|
+
|
|
41
|
+
async def main() -> None:
|
|
42
|
+
async with AsyncMailcue(api_key="mc_xxx") as client:
|
|
43
|
+
await client.emails.send(
|
|
44
|
+
from_="hello@example.com",
|
|
45
|
+
to=["user@example.com"],
|
|
46
|
+
subject="Welcome",
|
|
47
|
+
html="<h1>Hi there</h1>",
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
asyncio.run(main())
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Listing an inbox
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
inbox = client.emails.list(mailbox="user@example.com", page_size=20)
|
|
57
|
+
for email in inbox.emails:
|
|
58
|
+
print(email.uid, email.subject, email.from_address)
|
|
59
|
+
|
|
60
|
+
detail = client.emails.get(inbox.emails[0].uid, mailbox="user@example.com")
|
|
61
|
+
print(detail.text_body)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Attachments
|
|
65
|
+
|
|
66
|
+
`attachments` accepts raw `bytes`, `str`, or a `pathlib.Path`. The SDK
|
|
67
|
+
base64-encodes the content for you.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from pathlib import Path
|
|
71
|
+
|
|
72
|
+
client.emails.send(
|
|
73
|
+
from_="hello@example.com",
|
|
74
|
+
to=["user@example.com"],
|
|
75
|
+
subject="Invoice",
|
|
76
|
+
html="<p>See attached.</p>",
|
|
77
|
+
attachments=[
|
|
78
|
+
{
|
|
79
|
+
"filename": "invoice.pdf",
|
|
80
|
+
"content_type": "application/pdf",
|
|
81
|
+
"content": Path("./invoice.pdf"),
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Real-time events (SSE)
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
for event in client.events.stream():
|
|
91
|
+
print(event.event_type, event.data)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The async version:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
async with AsyncMailcue(api_key="mc_xxx") as client:
|
|
98
|
+
async for event in client.events.stream():
|
|
99
|
+
print(event.event_type, event.data)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The SSE client auto-reconnects with exponential backoff if the connection
|
|
103
|
+
drops.
|
|
104
|
+
|
|
105
|
+
## Error handling
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from mailcue import (
|
|
109
|
+
Mailcue,
|
|
110
|
+
AuthenticationError,
|
|
111
|
+
NotFoundError,
|
|
112
|
+
RateLimitError,
|
|
113
|
+
ValidationError,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
client = Mailcue(api_key="mc_xxx")
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
client.emails.get("not-a-real-uid", mailbox="user@example.com")
|
|
120
|
+
except NotFoundError as exc:
|
|
121
|
+
print("missing:", exc)
|
|
122
|
+
except RateLimitError as exc:
|
|
123
|
+
print(f"slow down; retry after {exc.retry_after}s")
|
|
124
|
+
except AuthenticationError:
|
|
125
|
+
print("bad API key")
|
|
126
|
+
except ValidationError as exc:
|
|
127
|
+
print("server rejected the request:", exc.detail)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Configuration
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
client = Mailcue(
|
|
134
|
+
api_key="mc_xxx", # or bearer_token="eyJ..."
|
|
135
|
+
base_url="https://mail.example.com", # default: http://localhost:8088
|
|
136
|
+
timeout=30.0, # seconds
|
|
137
|
+
max_retries=3, # 502/503/504 + network errors
|
|
138
|
+
verify=True, # set False for self-signed dev TLS
|
|
139
|
+
)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
You can also inject your own `httpx.Client` / `httpx.AsyncClient` via
|
|
143
|
+
`http_client=` for advanced cases (custom transports, proxies, mTLS).
|
|
144
|
+
|
|
145
|
+
## Resources
|
|
146
|
+
|
|
147
|
+
| Resource | Methods |
|
|
148
|
+
|----------|---------|
|
|
149
|
+
| `client.emails` | `send`, `list`, `get`, `get_raw`, `get_attachment`, `delete`, `inject`, `bulk_inject` |
|
|
150
|
+
| `client.mailboxes` | `list`, `create`, `delete`, `stats`, `purge`, `list_emails` |
|
|
151
|
+
| `client.domains` | `list`, `create`, `get`, `verify_dns`, `delete` |
|
|
152
|
+
| `client.aliases` | `list`, `create`, `get`, `update`, `delete` |
|
|
153
|
+
| `client.gpg` | `list`, `generate`, `get`, `export_public`, `import_key`, `publish`, `delete` |
|
|
154
|
+
| `client.api_keys` | `list`, `create`, `delete` |
|
|
155
|
+
| `client.system` | `health`, `get_certificate`, `download_certificate`, `settings`, `tls_status` |
|
|
156
|
+
| `client.events` | `stream()` (SSE iterator) |
|
|
157
|
+
|
|
158
|
+
## License
|
|
159
|
+
|
|
160
|
+
MIT — see `LICENSE`.
|
|
161
|
+
|
|
162
|
+
Project home: https://github.com/Olib-AI/mailcue
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""MailCue Python SDK.
|
|
2
|
+
|
|
3
|
+
Drop-in client for the MailCue REST API. Both ``Mailcue`` (sync) and
|
|
4
|
+
``AsyncMailcue`` (async) clients expose the same resource surface:
|
|
5
|
+
``emails``, ``mailboxes``, ``domains``, ``aliases``, ``gpg``,
|
|
6
|
+
``api_keys``, ``system``, and the SSE ``events`` stream.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> from mailcue import Mailcue
|
|
10
|
+
>>> client = Mailcue(api_key="mc_...")
|
|
11
|
+
>>> client.emails.send(
|
|
12
|
+
... from_="hello@example.com",
|
|
13
|
+
... to=["user@example.com"],
|
|
14
|
+
... subject="Welcome",
|
|
15
|
+
... html="<h1>Hi</h1>",
|
|
16
|
+
... )
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
from mailcue._version import __version__
|
|
22
|
+
from mailcue.client import AsyncMailcue, Mailcue
|
|
23
|
+
from mailcue.exceptions import (
|
|
24
|
+
AuthenticationError,
|
|
25
|
+
AuthorizationError,
|
|
26
|
+
ConflictError,
|
|
27
|
+
MailcueError,
|
|
28
|
+
NetworkError,
|
|
29
|
+
NotFoundError,
|
|
30
|
+
RateLimitError,
|
|
31
|
+
ServerError,
|
|
32
|
+
TimeoutError,
|
|
33
|
+
ValidationError,
|
|
34
|
+
)
|
|
35
|
+
from mailcue.transport import DEFAULT_BASE_URL
|
|
36
|
+
from mailcue.types import (
|
|
37
|
+
Alias,
|
|
38
|
+
AliasListResponse,
|
|
39
|
+
ApiKey,
|
|
40
|
+
AttachmentInfo,
|
|
41
|
+
BulkInjectResponse,
|
|
42
|
+
CreatedApiKey,
|
|
43
|
+
DnsCheckResponse,
|
|
44
|
+
DnsRecordInfo,
|
|
45
|
+
Domain,
|
|
46
|
+
DomainDetail,
|
|
47
|
+
DomainListResponse,
|
|
48
|
+
EmailDetail,
|
|
49
|
+
EmailListResponse,
|
|
50
|
+
EmailSummary,
|
|
51
|
+
Event,
|
|
52
|
+
FolderInfo,
|
|
53
|
+
GpgEmailInfo,
|
|
54
|
+
GpgKey,
|
|
55
|
+
GpgKeyExport,
|
|
56
|
+
GpgKeyListResponse,
|
|
57
|
+
HealthResponse,
|
|
58
|
+
KeyserverPublishResult,
|
|
59
|
+
Mailbox,
|
|
60
|
+
MailboxListResponse,
|
|
61
|
+
MailboxStats,
|
|
62
|
+
SendResult,
|
|
63
|
+
SignatureStatus,
|
|
64
|
+
TlsCertificateStatus,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
__all__ = [
|
|
68
|
+
"DEFAULT_BASE_URL",
|
|
69
|
+
"Alias",
|
|
70
|
+
"AliasListResponse",
|
|
71
|
+
"ApiKey",
|
|
72
|
+
"AsyncMailcue",
|
|
73
|
+
"AttachmentInfo",
|
|
74
|
+
"AuthenticationError",
|
|
75
|
+
"AuthorizationError",
|
|
76
|
+
"BulkInjectResponse",
|
|
77
|
+
"ConflictError",
|
|
78
|
+
"CreatedApiKey",
|
|
79
|
+
"DnsCheckResponse",
|
|
80
|
+
"DnsRecordInfo",
|
|
81
|
+
"Domain",
|
|
82
|
+
"DomainDetail",
|
|
83
|
+
"DomainListResponse",
|
|
84
|
+
"EmailDetail",
|
|
85
|
+
"EmailListResponse",
|
|
86
|
+
"EmailSummary",
|
|
87
|
+
"Event",
|
|
88
|
+
"FolderInfo",
|
|
89
|
+
"GpgEmailInfo",
|
|
90
|
+
"GpgKey",
|
|
91
|
+
"GpgKeyExport",
|
|
92
|
+
"GpgKeyListResponse",
|
|
93
|
+
"HealthResponse",
|
|
94
|
+
"KeyserverPublishResult",
|
|
95
|
+
"Mailbox",
|
|
96
|
+
"MailboxListResponse",
|
|
97
|
+
"MailboxStats",
|
|
98
|
+
"Mailcue",
|
|
99
|
+
"MailcueError",
|
|
100
|
+
"NetworkError",
|
|
101
|
+
"NotFoundError",
|
|
102
|
+
"RateLimitError",
|
|
103
|
+
"SendResult",
|
|
104
|
+
"ServerError",
|
|
105
|
+
"SignatureStatus",
|
|
106
|
+
"TimeoutError",
|
|
107
|
+
"TlsCertificateStatus",
|
|
108
|
+
"ValidationError",
|
|
109
|
+
"__version__",
|
|
110
|
+
]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""Auth header builders.
|
|
2
|
+
|
|
3
|
+
MailCue accepts either an API key (``X-API-Key: mc_...``) or a JWT bearer
|
|
4
|
+
token (``Authorization: Bearer ...``). The transport calls ``headers()``
|
|
5
|
+
on the configured strategy for each request.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from abc import ABC, abstractmethod
|
|
11
|
+
from typing import Dict
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AuthStrategy(ABC):
|
|
15
|
+
"""Strategy that produces auth headers for outgoing requests."""
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def headers(self) -> Dict[str, str]:
|
|
19
|
+
"""Return the headers to merge into each request."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ApiKeyAuth(AuthStrategy):
|
|
23
|
+
"""Authenticate using a MailCue API key."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, api_key: str) -> None:
|
|
26
|
+
if not api_key:
|
|
27
|
+
raise ValueError("api_key must not be empty")
|
|
28
|
+
self._api_key = api_key
|
|
29
|
+
|
|
30
|
+
def headers(self) -> Dict[str, str]:
|
|
31
|
+
return {"X-API-Key": self._api_key}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class BearerAuth(AuthStrategy):
|
|
35
|
+
"""Authenticate using a JWT bearer token."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, token: str) -> None:
|
|
38
|
+
if not token:
|
|
39
|
+
raise ValueError("token must not be empty")
|
|
40
|
+
self._token = token
|
|
41
|
+
|
|
42
|
+
def headers(self) -> Dict[str, str]:
|
|
43
|
+
return {"Authorization": f"Bearer {self._token}"}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class NoAuth(AuthStrategy):
|
|
47
|
+
"""No-op strategy used for unauthenticated endpoints (e.g. /health)."""
|
|
48
|
+
|
|
49
|
+
def headers(self) -> Dict[str, str]:
|
|
50
|
+
return {}
|