fastapi-plugin-notification 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.
- fastapi_plugin_notification-0.1.0/LICENSE +21 -0
- fastapi_plugin_notification-0.1.0/PKG-INFO +375 -0
- fastapi_plugin_notification-0.1.0/README.md +357 -0
- fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/__init__.py +29 -0
- fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/app.py +162 -0
- fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/client.py +206 -0
- fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/controller.py +19 -0
- fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/models.py +46 -0
- fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/schemas.py +91 -0
- fastapi_plugin_notification-0.1.0/fastapi_plugin_notification.egg-info/PKG-INFO +375 -0
- fastapi_plugin_notification-0.1.0/fastapi_plugin_notification.egg-info/SOURCES.txt +19 -0
- fastapi_plugin_notification-0.1.0/fastapi_plugin_notification.egg-info/dependency_links.txt +1 -0
- fastapi_plugin_notification-0.1.0/fastapi_plugin_notification.egg-info/requires.txt +10 -0
- fastapi_plugin_notification-0.1.0/fastapi_plugin_notification.egg-info/top_level.txt +1 -0
- fastapi_plugin_notification-0.1.0/pyproject.toml +29 -0
- fastapi_plugin_notification-0.1.0/setup.cfg +4 -0
- fastapi_plugin_notification-0.1.0/tests/test_app.py +172 -0
- fastapi_plugin_notification-0.1.0/tests/test_client.py +347 -0
- fastapi_plugin_notification-0.1.0/tests/test_controller.py +205 -0
- fastapi_plugin_notification-0.1.0/tests/test_models.py +76 -0
- fastapi_plugin_notification-0.1.0/tests/test_schemas.py +164 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Guillaume Piot
|
|
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,375 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fastapi-plugin-notification
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Notification plugin for FastAPI projects using FastAPI SDK and Postmark provider.
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Dist: fastapi-sdk>=0.9.7
|
|
9
|
+
Requires-Dist: fastapi-provider-postmark>=0.1.1
|
|
10
|
+
Requires-Dist: fastapi>=0.115.11
|
|
11
|
+
Requires-Dist: odmantic>=1.0.2
|
|
12
|
+
Requires-Dist: pydantic>=2.10.6
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=9.0.1; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-asyncio>=0.25.3; extra == "dev"
|
|
16
|
+
Requires-Dist: motor>=3.6.0; extra == "dev"
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# FastAPI Plugin Notification
|
|
20
|
+
|
|
21
|
+
A notification plugin for FastAPI that integrates with FastAPI SDK and Postmark provider to send notifications via email and store them in a database.
|
|
22
|
+
|
|
23
|
+
## Features
|
|
24
|
+
|
|
25
|
+
- 📧 **Email Notifications**: Send notifications via Postmark email provider
|
|
26
|
+
- 💾 **Database Storage**: Store notifications in MongoDB using ODMantic
|
|
27
|
+
- 🔐 **User Ownership**: Automatic ownership filtering based on user claims
|
|
28
|
+
- 📊 **Notification Management**:
|
|
29
|
+
- Get all notifications for a user
|
|
30
|
+
- Mark notifications as seen/acknowledged
|
|
31
|
+
- Get unseen notification count
|
|
32
|
+
- 🚀 **FastAPI Integration**: Full CRUD API endpoints with authentication
|
|
33
|
+
- 📦 **PyPI Package**: Easy to install and use in any FastAPI project
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
Using UV:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uv add fastapi-plugin-notification
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or using pip:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install fastapi-plugin-notification
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
### 1. Initialize the Notification Client
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
from fastapi import FastAPI, Depends
|
|
55
|
+
from odmantic import AIOEngine
|
|
56
|
+
from fastapi_provider_postmark import PostmarkProvider
|
|
57
|
+
from fastapi_plugin_notification import NotificationClient
|
|
58
|
+
|
|
59
|
+
app = FastAPI()
|
|
60
|
+
|
|
61
|
+
# Initialize Postmark provider
|
|
62
|
+
postmark = PostmarkProvider(
|
|
63
|
+
api_key="your-postmark-api-key",
|
|
64
|
+
from_email="noreply@example.com",
|
|
65
|
+
from_name="My App",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Initialize notification client (no DB engine needed)
|
|
69
|
+
notification_client = NotificationClient(
|
|
70
|
+
postmark_provider=postmark,
|
|
71
|
+
default_template_id=123456, # Your Postmark template ID
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Dependency for notification client
|
|
75
|
+
def get_notification_client() -> NotificationClient:
|
|
76
|
+
return notification_client
|
|
77
|
+
|
|
78
|
+
# Dependency for database engine
|
|
79
|
+
def get_db() -> AIOEngine:
|
|
80
|
+
return AIOEngine(database="your_database")
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 2. Add Notification Routes to Your App
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from fastapi_plugin_notification.app import create_notification_router
|
|
87
|
+
|
|
88
|
+
# Add notification routes
|
|
89
|
+
notification_router = create_notification_router(
|
|
90
|
+
prefix="/notifications",
|
|
91
|
+
get_db=lambda: engine,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
app.include_router(notification_router)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 3. Send Notifications
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
@app.post("/send-welcome")
|
|
101
|
+
async def send_welcome_notification(
|
|
102
|
+
user_id: str,
|
|
103
|
+
user_email: str,
|
|
104
|
+
notification_client: NotificationClient = Depends(get_notification_client),
|
|
105
|
+
db: AIOEngine = Depends(get_db),
|
|
106
|
+
):
|
|
107
|
+
"""Send a welcome notification to a user."""
|
|
108
|
+
notification = await notification_client.send_notification(
|
|
109
|
+
db_engine=db,
|
|
110
|
+
name="welcome",
|
|
111
|
+
user_id=user_id,
|
|
112
|
+
user_email=user_email,
|
|
113
|
+
claims=request.state.claims, # Required: user claims for ownership verification
|
|
114
|
+
metadata={
|
|
115
|
+
"welcome_message": "Welcome to our platform!",
|
|
116
|
+
},
|
|
117
|
+
link="https://example.com/dashboard",
|
|
118
|
+
link_name="Go to Dashboard",
|
|
119
|
+
template_variables={
|
|
120
|
+
"user_name": "John Doe",
|
|
121
|
+
"action_url": "https://example.com/dashboard",
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
return {"notification_id": notification.uuid, "message": "Notification sent"}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 4. Get User Notifications
|
|
128
|
+
|
|
129
|
+
The plugin automatically provides these user-specific endpoints:
|
|
130
|
+
|
|
131
|
+
- `GET /notifications/me` - Get all notifications for the current user
|
|
132
|
+
- `GET /notifications/me?seen=false` - Get only unseen notifications
|
|
133
|
+
- `GET /notifications/me/{notification_id}` - Get a specific notification (automatically marks as seen)
|
|
134
|
+
- `DELETE /notifications/me/{notification_id}` - Delete a notification
|
|
135
|
+
- `GET /notifications/count/unseen` - Get count of unseen notifications
|
|
136
|
+
|
|
137
|
+
## Usage Examples
|
|
138
|
+
|
|
139
|
+
### Sending a Notification with Email
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
# In a FastAPI route handler
|
|
143
|
+
@app.post("/send-password-reset")
|
|
144
|
+
async def send_password_reset(
|
|
145
|
+
user_id: str,
|
|
146
|
+
user_email: str,
|
|
147
|
+
notification_client: NotificationClient = Depends(get_notification_client),
|
|
148
|
+
db: AIOEngine = Depends(get_db),
|
|
149
|
+
):
|
|
150
|
+
notification = await notification_client.send_notification(
|
|
151
|
+
db_engine=db,
|
|
152
|
+
name="password_reset",
|
|
153
|
+
user_id=user_id,
|
|
154
|
+
user_email=user_email,
|
|
155
|
+
claims=request.state.claims, # Required: user claims for ownership verification
|
|
156
|
+
metadata={
|
|
157
|
+
"reset_token": "abc123",
|
|
158
|
+
"expires_in": 3600,
|
|
159
|
+
},
|
|
160
|
+
link="https://example.com/reset-password?token=abc123",
|
|
161
|
+
link_name="Reset Password",
|
|
162
|
+
template_variables={
|
|
163
|
+
"reset_link": "https://example.com/reset-password?token=abc123",
|
|
164
|
+
"expires_in_minutes": 60,
|
|
165
|
+
},
|
|
166
|
+
)
|
|
167
|
+
return {"notification_id": notification.uuid}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Getting User Notifications
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
# Get all notifications for a user
|
|
174
|
+
notifications = await notification_client.get_user_notifications(
|
|
175
|
+
db_engine=db,
|
|
176
|
+
user_id="user_123",
|
|
177
|
+
page=1,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Get only unseen notifications
|
|
181
|
+
unseen_notifications = await notification_client.get_user_notifications(
|
|
182
|
+
db_engine=db,
|
|
183
|
+
user_id="user_123",
|
|
184
|
+
page=1,
|
|
185
|
+
seen=False,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# Get unseen count
|
|
189
|
+
unseen_count = await notification_client.get_unseen_count(
|
|
190
|
+
db_engine=db,
|
|
191
|
+
user_id="user_123",
|
|
192
|
+
)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Marking Notifications as Seen
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
# Mark a specific notification as seen
|
|
199
|
+
notification = await notification_client.mark_as_seen(
|
|
200
|
+
db_engine=db,
|
|
201
|
+
notification_uuid="not_abc123",
|
|
202
|
+
)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Creating Notifications Without Email
|
|
206
|
+
|
|
207
|
+
```python
|
|
208
|
+
# Create notification without sending email
|
|
209
|
+
notification = await notification_client.send_notification(
|
|
210
|
+
db_engine=db,
|
|
211
|
+
name="system_alert",
|
|
212
|
+
user_id="user_123",
|
|
213
|
+
user_email="user@example.com",
|
|
214
|
+
claims=request.state.claims, # Required: user claims for ownership verification
|
|
215
|
+
metadata={"alert_type": "info"},
|
|
216
|
+
send_email=False, # Don't send email
|
|
217
|
+
)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## API Endpoints
|
|
221
|
+
|
|
222
|
+
### Get My Notifications
|
|
223
|
+
|
|
224
|
+
```http
|
|
225
|
+
GET /notifications/me?page=1&seen=false
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
**Query Parameters:**
|
|
229
|
+
- `page` (int): Page number (default: 1)
|
|
230
|
+
- `seen` (bool, optional): Filter by seen status
|
|
231
|
+
|
|
232
|
+
**Response:**
|
|
233
|
+
```json
|
|
234
|
+
{
|
|
235
|
+
"items": [
|
|
236
|
+
{
|
|
237
|
+
"uuid": "not_abc123",
|
|
238
|
+
"name": "welcome",
|
|
239
|
+
"user_id": "user_123",
|
|
240
|
+
"user_email": "user@example.com",
|
|
241
|
+
"metadata": {},
|
|
242
|
+
"link": "https://example.com/dashboard",
|
|
243
|
+
"seen": false,
|
|
244
|
+
"created_at": "2024-01-01T00:00:00Z",
|
|
245
|
+
"updated_at": "2024-01-01T00:00:00Z",
|
|
246
|
+
"deleted": false
|
|
247
|
+
}
|
|
248
|
+
],
|
|
249
|
+
"total": 1,
|
|
250
|
+
"page": 1,
|
|
251
|
+
"pages": 1,
|
|
252
|
+
"size": 1
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Get Unseen Count
|
|
257
|
+
|
|
258
|
+
```http
|
|
259
|
+
GET /notifications/count/unseen
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
**Response:**
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"count": 5
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Get My Notification (Auto-marks as Seen)
|
|
270
|
+
|
|
271
|
+
```http
|
|
272
|
+
GET /notifications/me/{notification_id}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
**Note:** This endpoint automatically marks the notification as `seen: true` when accessed. Only returns notifications belonging to the current user.
|
|
276
|
+
|
|
277
|
+
**Response:**
|
|
278
|
+
```json
|
|
279
|
+
{
|
|
280
|
+
"uuid": "not_abc123",
|
|
281
|
+
"name": "welcome",
|
|
282
|
+
"user_id": "user_123",
|
|
283
|
+
"user_email": "user@example.com",
|
|
284
|
+
"metadata": {},
|
|
285
|
+
"link": "https://example.com/dashboard",
|
|
286
|
+
"link_name": "Go to Dashboard",
|
|
287
|
+
"seen": true,
|
|
288
|
+
"created_at": "2024-01-01T00:00:00Z",
|
|
289
|
+
"updated_at": "2024-01-01T00:00:00Z",
|
|
290
|
+
"deleted": false
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Model Structure
|
|
295
|
+
|
|
296
|
+
The `NotificationModel` includes:
|
|
297
|
+
|
|
298
|
+
- `uuid`: Unique identifier (short UUID with "not" prefix)
|
|
299
|
+
- `name`: Name/type of the notification
|
|
300
|
+
- `metadata`: Additional metadata (dict)
|
|
301
|
+
- `user_id`: ID of the user who should receive the notification
|
|
302
|
+
- `user_email`: Email address of the user
|
|
303
|
+
- `link`: Call to action link (optional)
|
|
304
|
+
- `link_name`: Text to display on the call to action button/link in the email (optional)
|
|
305
|
+
- `seen`: Whether the notification has been seen (default: False)
|
|
306
|
+
- `created_at`: Creation timestamp
|
|
307
|
+
- `updated_at`: Last update timestamp
|
|
308
|
+
- `deleted`: Soft delete flag
|
|
309
|
+
|
|
310
|
+
## Ownership and Permissions
|
|
311
|
+
|
|
312
|
+
The plugin uses FastAPI SDK's ownership rules to ensure users can only access their own notifications. The ownership is based on the `user_id` claim in the JWT token.
|
|
313
|
+
|
|
314
|
+
Required permissions:
|
|
315
|
+
- `notification:create` - Create notifications
|
|
316
|
+
- `notification:read` - Read notifications
|
|
317
|
+
- `notification:update` - Update notifications
|
|
318
|
+
- `notification:delete` - Delete notifications
|
|
319
|
+
|
|
320
|
+
## Configuration
|
|
321
|
+
|
|
322
|
+
### Custom Router Configuration
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
from fastapi_plugin_notification.app import create_notification_router
|
|
326
|
+
|
|
327
|
+
# Create router with custom prefix and tags
|
|
328
|
+
notification_router = create_notification_router(
|
|
329
|
+
prefix="/notifications",
|
|
330
|
+
tags=["notifications", "alerts"],
|
|
331
|
+
get_db=lambda: engine,
|
|
332
|
+
)
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
**Note:** All routes are user-specific and automatically filter by the current user's ID from JWT claims.
|
|
336
|
+
|
|
337
|
+
### Custom Template Variables
|
|
338
|
+
|
|
339
|
+
The notification client automatically includes:
|
|
340
|
+
- `notification_name`: The name of the notification
|
|
341
|
+
- `user_email`: The user's email
|
|
342
|
+
- `link`: The notification link
|
|
343
|
+
- `link_name`: The text for the call to action button/link
|
|
344
|
+
- All `metadata` fields
|
|
345
|
+
- All `template_variables` fields
|
|
346
|
+
|
|
347
|
+
These are merged and passed to the Postmark template. You can use `link_name` in your email template to display custom button text for the call to action link.
|
|
348
|
+
|
|
349
|
+
## Error Handling
|
|
350
|
+
|
|
351
|
+
The plugin handles errors gracefully:
|
|
352
|
+
- If email sending fails, the notification is still stored in the database
|
|
353
|
+
- Errors are logged but don't interrupt the notification creation process
|
|
354
|
+
- Database errors are propagated as HTTP exceptions
|
|
355
|
+
|
|
356
|
+
## Development
|
|
357
|
+
|
|
358
|
+
### Setup
|
|
359
|
+
|
|
360
|
+
1. Clone the repository
|
|
361
|
+
2. Install dependencies:
|
|
362
|
+
```bash
|
|
363
|
+
uv sync
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Running Tests
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
pytest
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
## License
|
|
373
|
+
|
|
374
|
+
MIT License - see LICENSE file for details
|
|
375
|
+
|