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.
Files changed (21) hide show
  1. fastapi_plugin_notification-0.1.0/LICENSE +21 -0
  2. fastapi_plugin_notification-0.1.0/PKG-INFO +375 -0
  3. fastapi_plugin_notification-0.1.0/README.md +357 -0
  4. fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/__init__.py +29 -0
  5. fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/app.py +162 -0
  6. fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/client.py +206 -0
  7. fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/controller.py +19 -0
  8. fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/models.py +46 -0
  9. fastapi_plugin_notification-0.1.0/fastapi_plugin_notification/schemas.py +91 -0
  10. fastapi_plugin_notification-0.1.0/fastapi_plugin_notification.egg-info/PKG-INFO +375 -0
  11. fastapi_plugin_notification-0.1.0/fastapi_plugin_notification.egg-info/SOURCES.txt +19 -0
  12. fastapi_plugin_notification-0.1.0/fastapi_plugin_notification.egg-info/dependency_links.txt +1 -0
  13. fastapi_plugin_notification-0.1.0/fastapi_plugin_notification.egg-info/requires.txt +10 -0
  14. fastapi_plugin_notification-0.1.0/fastapi_plugin_notification.egg-info/top_level.txt +1 -0
  15. fastapi_plugin_notification-0.1.0/pyproject.toml +29 -0
  16. fastapi_plugin_notification-0.1.0/setup.cfg +4 -0
  17. fastapi_plugin_notification-0.1.0/tests/test_app.py +172 -0
  18. fastapi_plugin_notification-0.1.0/tests/test_client.py +347 -0
  19. fastapi_plugin_notification-0.1.0/tests/test_controller.py +205 -0
  20. fastapi_plugin_notification-0.1.0/tests/test_models.py +76 -0
  21. 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
+