django-webhook-subscriber 1.0.0__tar.gz → 2.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 (68) hide show
  1. django_webhook_subscriber-2.1.0/PKG-INFO +822 -0
  2. django_webhook_subscriber-2.1.0/README.md +787 -0
  3. django_webhook_subscriber-2.1.0/django_webhook_subscriber/__init__.py +7 -0
  4. django_webhook_subscriber-2.1.0/django_webhook_subscriber/admin.py +915 -0
  5. django_webhook_subscriber-2.1.0/django_webhook_subscriber/apps.py +6 -0
  6. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/django_webhook_subscriber/conf.py +11 -24
  7. django_webhook_subscriber-2.1.0/django_webhook_subscriber/delivery.py +469 -0
  8. django_webhook_subscriber-2.1.0/django_webhook_subscriber/http.py +51 -0
  9. django_webhook_subscriber-2.1.0/django_webhook_subscriber/management/commands/webhook.py +169 -0
  10. django_webhook_subscriber-2.1.0/django_webhook_subscriber/management/commands/webhook_cache.py +173 -0
  11. django_webhook_subscriber-2.1.0/django_webhook_subscriber/management/commands/webhook_logs.py +226 -0
  12. django_webhook_subscriber-2.1.0/django_webhook_subscriber/management/commands/webhook_performance_test.py +469 -0
  13. django_webhook_subscriber-2.1.0/django_webhook_subscriber/management/commands/webhook_send.py +96 -0
  14. django_webhook_subscriber-2.1.0/django_webhook_subscriber/management/commands/webhook_status.py +139 -0
  15. django_webhook_subscriber-2.1.0/django_webhook_subscriber/managers.py +75 -0
  16. django_webhook_subscriber-2.1.0/django_webhook_subscriber/migrations/0002_remove_webhookregistry_content_type_and_more.py +192 -0
  17. django_webhook_subscriber-2.1.0/django_webhook_subscriber/models.py +358 -0
  18. django_webhook_subscriber-2.1.0/django_webhook_subscriber/serializers.py +54 -0
  19. django_webhook_subscriber-2.1.0/django_webhook_subscriber/tasks.py +451 -0
  20. django_webhook_subscriber-2.1.0/django_webhook_subscriber/tests/factories.py +40 -0
  21. django_webhook_subscriber-2.1.0/django_webhook_subscriber/tests/settings.py +38 -0
  22. django_webhook_subscriber-2.1.0/django_webhook_subscriber/tests/test_delivery.py +532 -0
  23. django_webhook_subscriber-2.1.0/django_webhook_subscriber/tests/test_http.py +32 -0
  24. django_webhook_subscriber-2.1.0/django_webhook_subscriber/tests/test_managers.py +82 -0
  25. django_webhook_subscriber-2.1.0/django_webhook_subscriber/tests/test_models.py +391 -0
  26. django_webhook_subscriber-2.1.0/django_webhook_subscriber/tests/test_serializers.py +53 -0
  27. django_webhook_subscriber-2.1.0/django_webhook_subscriber/tests/test_tasks.py +507 -0
  28. django_webhook_subscriber-2.1.0/django_webhook_subscriber/tests/test_utils.py +112 -0
  29. django_webhook_subscriber-2.1.0/django_webhook_subscriber/utils.py +102 -0
  30. django_webhook_subscriber-2.1.0/django_webhook_subscriber/validators.py +53 -0
  31. django_webhook_subscriber-2.1.0/django_webhook_subscriber.egg-info/PKG-INFO +822 -0
  32. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/django_webhook_subscriber.egg-info/SOURCES.txt +11 -6
  33. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/django_webhook_subscriber.egg-info/requires.txt +2 -4
  34. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/pyproject.toml +4 -6
  35. django_webhook_subscriber-1.0.0/PKG-INFO +0 -448
  36. django_webhook_subscriber-1.0.0/README.md +0 -412
  37. django_webhook_subscriber-1.0.0/django_webhook_subscriber/__init__.py +0 -1
  38. django_webhook_subscriber-1.0.0/django_webhook_subscriber/admin.py +0 -266
  39. django_webhook_subscriber-1.0.0/django_webhook_subscriber/apps.py +0 -23
  40. django_webhook_subscriber-1.0.0/django_webhook_subscriber/delivery.py +0 -208
  41. django_webhook_subscriber-1.0.0/django_webhook_subscriber/management/commands/check_webhook_tasks.py +0 -113
  42. django_webhook_subscriber-1.0.0/django_webhook_subscriber/management/commands/clean_webhook_logs.py +0 -65
  43. django_webhook_subscriber-1.0.0/django_webhook_subscriber/management/commands/test_webhook.py +0 -96
  44. django_webhook_subscriber-1.0.0/django_webhook_subscriber/managers.py +0 -53
  45. django_webhook_subscriber-1.0.0/django_webhook_subscriber/models.py +0 -181
  46. django_webhook_subscriber-1.0.0/django_webhook_subscriber/serializers.py +0 -75
  47. django_webhook_subscriber-1.0.0/django_webhook_subscriber/signals.py +0 -152
  48. django_webhook_subscriber-1.0.0/django_webhook_subscriber/tasks.py +0 -73
  49. django_webhook_subscriber-1.0.0/django_webhook_subscriber/testing.py +0 -14
  50. django_webhook_subscriber-1.0.0/django_webhook_subscriber/tests/settings.py +0 -19
  51. django_webhook_subscriber-1.0.0/django_webhook_subscriber/tests/test_delivery.py +0 -197
  52. django_webhook_subscriber-1.0.0/django_webhook_subscriber/tests/test_managers.py +0 -93
  53. django_webhook_subscriber-1.0.0/django_webhook_subscriber/tests/test_models.py +0 -301
  54. django_webhook_subscriber-1.0.0/django_webhook_subscriber/tests/test_serializers.py +0 -87
  55. django_webhook_subscriber-1.0.0/django_webhook_subscriber/tests/test_signals.py +0 -268
  56. django_webhook_subscriber-1.0.0/django_webhook_subscriber/tests/test_tasks.py +0 -219
  57. django_webhook_subscriber-1.0.0/django_webhook_subscriber/tests/test_utils.py +0 -108
  58. django_webhook_subscriber-1.0.0/django_webhook_subscriber/utils.py +0 -84
  59. django_webhook_subscriber-1.0.0/django_webhook_subscriber.egg-info/PKG-INFO +0 -448
  60. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/LICENSE +0 -0
  61. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/django_webhook_subscriber/management/__init__.py +0 -0
  62. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/django_webhook_subscriber/management/commands/__init__.py +0 -0
  63. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/django_webhook_subscriber/migrations/0001_initial.py +0 -0
  64. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/django_webhook_subscriber/migrations/__init__.py +0 -0
  65. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/django_webhook_subscriber/tests/__init__.py +0 -0
  66. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/django_webhook_subscriber.egg-info/dependency_links.txt +0 -0
  67. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/django_webhook_subscriber.egg-info/top_level.txt +0 -0
  68. {django_webhook_subscriber-1.0.0 → django_webhook_subscriber-2.1.0}/setup.cfg +0 -0
@@ -0,0 +1,822 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-webhook-subscriber
3
+ Version: 2.1.0
4
+ Summary: A Django package designed to handle webhook creation, management, and delivery.
5
+ Author-email: 42 Portugal <root@42porto.com>
6
+ Classifier: Environment :: Web Environment
7
+ Classifier: Framework :: Django
8
+ Classifier: Framework :: Django :: 5.0
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: BSD License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Internet :: WWW/HTTP
20
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: Django<5.3,>=5.0
25
+ Requires-Dist: djangorestframework<3.17,>=3.15.0
26
+ Requires-Dist: requests<2.33,>=2.32.3
27
+ Requires-Dist: celery<5.6,>=5.0.0
28
+ Requires-Dist: redis<6.5,>=6.4
29
+ Provides-Extra: test
30
+ Requires-Dist: pytest<8.4,>=8.3.4; extra == "test"
31
+ Requires-Dist: pytest-django<5.0,>=4.9.0; extra == "test"
32
+ Requires-Dist: pytest-cov<6.1,>=6.0.0; extra == "test"
33
+ Requires-Dist: pytest-factoryboy<2.9,>=2.8; extra == "test"
34
+ Dynamic: license-file
35
+
36
+ # Django Webhook Subscriber
37
+
38
+ A Django package for managing webhook subscriptions and deliveries with robust retry logic, performance monitoring, and comprehensive admin interface.
39
+
40
+ ## Table of Contents
41
+
42
+ - [Installation](#installation)
43
+ - [Quick Start](#quick-start)
44
+ - [Configuration](#configuration)
45
+ - [Basic Usage](#basic-usage)
46
+ - [Models Reference](#models-reference)
47
+ - [API Reference](#api-reference)
48
+ - [Admin Interface](#admin-interface)
49
+ - [Management Commands](#management-commands)
50
+ - [Performance Testing](#performance-testing)
51
+ - [Production Deployment](#production-deployment)
52
+ - [Troubleshooting](#troubleshooting)
53
+ - [Contributing](#contributing)
54
+
55
+ ## Installation
56
+
57
+ ### Requirements
58
+
59
+ - Python 3.8+
60
+ - Django 3.2+
61
+ - Django REST Framework 3.12+
62
+ - Celery 5.0+ (for async webhook delivery)
63
+ - Redis or another cache backend (recommended)
64
+
65
+ ### Install Package
66
+
67
+ ```bash
68
+ pip install django-webhook-subscriber
69
+ ```
70
+
71
+ ### Django Settings
72
+
73
+ Add to your `INSTALLED_APPS`:
74
+
75
+ ```python
76
+ INSTALLED_APPS = [
77
+ "django_webhook_subscriber",
78
+ # ... your apps
79
+ ]
80
+ ```
81
+
82
+ ### Database Migration
83
+
84
+ ```bash
85
+ python manage.py migrate django_webhook_subscriber
86
+ ```
87
+
88
+ ### Celery Configuration
89
+
90
+ Configure Celery for async webhook delivery:
91
+
92
+ ```python
93
+ # settings.py
94
+ CELERY_BROKER_URL = "redis://localhost:6379/0"
95
+ CELERY_RESULT_BACKEND = "redis://localhost:6379/0"
96
+ ```
97
+
98
+ ```python
99
+ # celery.py
100
+ from celery import Celery
101
+ import os
102
+
103
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
104
+
105
+ app = Celery("myproject")
106
+ app.config_from_object("django.conf:settings", namespace="CELERY")
107
+ app.autodiscover_tasks()
108
+ ```
109
+
110
+ ## Quick Start
111
+
112
+ ### 1. Create a Webhook Subscriber
113
+
114
+ ```python
115
+ from django.contrib.contenttypes.models import ContentType
116
+ from django_webhook_subscriber.models import WebhookSubscriber, WebhookSubscription
117
+ from django.contrib.auth import get_user_model
118
+
119
+ User = get_user_model()
120
+
121
+ # Create subscriber for User model
122
+ user_content_type = ContentType.objects.get_for_model(User)
123
+
124
+ subscriber = WebhookSubscriber.objects.create(
125
+ name="My External API",
126
+ description="Receives user events",
127
+ content_type=user_content_type,
128
+ target_url="https://api.example.com/webhooks",
129
+ max_retries=3,
130
+ retry_delay=60,
131
+ timeout=30
132
+ )
133
+
134
+ # Create subscription for 'created' events
135
+ subscription = WebhookSubscription.objects.create(
136
+ subscriber=subscriber,
137
+ event_name="created",
138
+ is_active=True
139
+ )
140
+ ```
141
+
142
+ ### 2. Send Webhooks
143
+
144
+ ```python
145
+ from django_webhook_subscriber import send_webhooks
146
+ from django.contrib.auth import get_user_model
147
+
148
+ User = get_user_model()
149
+
150
+ # Create a user and send webhook
151
+ user = User.objects.create(name="John Doe", email="john@example.com")
152
+
153
+ # Send webhook for "created" event
154
+ send_webhooks(user, "created")
155
+ ```
156
+
157
+ ### 3. Custom Serialization
158
+
159
+ ```python
160
+ from rest_framework import serializers
161
+ from django.contrib.auth import get_user_model
162
+
163
+ User = get_user_model()
164
+
165
+ class UserWebhookSerializer(serializers.ModelSerializer):
166
+ class Meta:
167
+ model = User
168
+ fields = ["id", "name", "email"]
169
+
170
+ # Update subscriber to use custom serializer
171
+ subscriber.serializer_class = "myapp.serializers.UserWebhookSerializer"
172
+ subscriber.save()
173
+ ```
174
+
175
+ ### 4. Raw Payload (No Metadata Wrapper)
176
+
177
+ By default, webhook payloads include metadata and the serialized data is nested inside a `fields` key:
178
+
179
+ ```json
180
+ {
181
+ "pk": 1,
182
+ "event_signal": "created",
183
+ "source": "auth.user",
184
+ "timestamp": "2026-02-05T10:30:00Z",
185
+ "fields": {
186
+ "id": 1,
187
+ "name": "John Doe",
188
+ "email": "john@example.com"
189
+ }
190
+ }
191
+ ```
192
+
193
+ If you prefer a raw payload containing only the serialized data (without the metadata wrapper), use the `WebhookRawPayloadMixin`:
194
+
195
+ ```python
196
+ from rest_framework import serializers
197
+ from django.contrib.auth import get_user_model
198
+ from django_webhook_subscriber.serializers import WebhookRawPayloadMixin
199
+
200
+ User = get_user_model()
201
+
202
+ class UserRawWebhookSerializer(WebhookRawPayloadMixin, serializers.ModelSerializer):
203
+ class Meta:
204
+ model = User
205
+ fields = ["id", "name", "email"]
206
+
207
+ # Update subscriber to use the raw payload serializer
208
+ subscriber.serializer_class = "myapp.serializers.UserRawWebhookSerializer"
209
+ subscriber.save()
210
+ ```
211
+
212
+ With `WebhookRawPayloadMixin`, the webhook payload will be just the serialized data:
213
+
214
+ ```json
215
+ {
216
+ "id": 1,
217
+ "name": "John Doe",
218
+ "email": "john@example.com"
219
+ }
220
+ ```
221
+
222
+ ## Configuration
223
+
224
+ Configure the package in your Django settings:
225
+
226
+ <!-- TODO: confirm these are all the settings -->
227
+
228
+ ```python
229
+ WEBHOOK_SUBSCRIBER = {
230
+ # Log retention
231
+ "LOG_RETENTION_DAYS": 30,
232
+ "AUTO_CLEANUP": True,
233
+
234
+ # Performance settings
235
+ "MAX_WEBHOOK_BATCH_SIZE": 50,
236
+ "WEBHOOK_CACHE_TTL": 300,
237
+
238
+ # Defaults for new subscribers
239
+ "DEFAULT_MAX_RETRIES": 3,
240
+ "DEFAULT_RETRY_DELAY": 60,
241
+ "REQUEST_TIMEOUT": 30,
242
+ }
243
+
244
+
245
+ # Recommended cache configuration
246
+ CACHES = {
247
+ 'default': {
248
+ 'BACKEND': 'django.core.cache.backends.redis.RedisCache',
249
+ 'LOCATION': 'redis://127.0.0.1:6379/1',
250
+ 'OPTIONS': {
251
+ 'CONNECTION_POOL_KWARGS': {'max_connections': 20},
252
+ }
253
+ }
254
+ }
255
+
256
+
257
+ # Optional: Disable webhooks globally
258
+ DISABLE_WEBHOOKS = False # Set to True to disable all webhook sending
259
+ ```
260
+
261
+ ## Basic Usage
262
+
263
+ ### Sending Webhooks
264
+
265
+ The main API is the `send_webhooks()` function:
266
+
267
+ ```python
268
+ from django_webhook_subscriber import send_webhooks
269
+
270
+ # Basic usage
271
+ send_webhooks(instance, 'event_name')
272
+ ```
273
+
274
+ ### Common Integration Patterns
275
+
276
+ #### Django Signals
277
+
278
+ ```python
279
+ # signals.py
280
+ from django.db.models.signals import post_save
281
+ from django.dispatch import receiver
282
+ from django_webhook_subscriber import send_webhooks
283
+ from django.contrib.auth import get_user_model
284
+
285
+ User = get_user_model()
286
+
287
+ @receiver(post_save, sender=User)
288
+ def user_saved(sender, instance, created, **kwargs):
289
+ event_name = "created" if created else "updated"
290
+ send_webhooks(instance, event_name)
291
+
292
+ ```
293
+
294
+ #### Django Lifecycle
295
+
296
+ ```python
297
+ # models.py
298
+ from django_lifecycle import LifecycleModel, hook, AFTER_CREATE, AFTER_UPDATE
299
+ from django_webhook_subscriber import send_webhooks
300
+
301
+ class User(LifecycleModel):
302
+ name = models.CharField(max_length=100)
303
+ email = models.EmailField()
304
+
305
+ @hook(AFTER_CREATE)
306
+ def on_create(self):
307
+ send_webhooks(self, 'created')
308
+
309
+ @hook(AFTER_UPDATE, when='email', has_changed=True)
310
+ def on_email_update(self):
311
+ send_webhooks(self, 'email_updated')
312
+ ```
313
+
314
+ #### Manual Integration
315
+
316
+ ```python
317
+ # myapp/views.py
318
+ from django_webhook_subscriber import send_webhooks
319
+
320
+ class UserViewSet(viewsets.ModelViewSet):
321
+ def perform_create(self, serializer):
322
+ user = serializer.save()
323
+ send_webhooks(user, "created")
324
+
325
+ def perform_update(self, serializer):
326
+ user = serializer.save()
327
+ send_webhooks(user, "updated")
328
+ ```
329
+
330
+ ### Disabling Webhooks Temporarily
331
+
332
+ ```python
333
+ from django_webhook_subscriber.utils import disable_webhooks
334
+
335
+ # Disable webhooks for bulk operations
336
+ with disable_webhooks():
337
+ User.objects.bulk_create([...]) # No webhooks sent
338
+
339
+ # Webhooks resume normal operation after the context
340
+ ```
341
+
342
+ ## Models Reference
343
+
344
+ ### WebhookSubscriber
345
+
346
+ Represents an external service that wants to receive webhooks.
347
+
348
+ **Key Fields:**
349
+
350
+ - `name`: Human-readable name for the subscriber
351
+ - `target_url`: Base URL for webhook delivery
352
+ - `content_type`: Django model this subscriber watches
353
+ - `serializer_class`: Optional custom DRF serializer
354
+ - `max_retries`: Maximum delivery attempts
355
+ - `retry_delay`: Seconds between retries
356
+ - `timeout`: Request timeout
357
+ - `auto_disable_after_failures`: Auto-disable threshold
358
+
359
+ **Methods:**
360
+
361
+ - `record_success()`: Mark successful delivery
362
+ - `record_failure()`: Mark failed delivery, handle auto-disable
363
+
364
+ ### WebhookSubscription
365
+
366
+ Individual event subscription for a subscriber.
367
+
368
+ **Key Fields:**
369
+
370
+ - `subscriber`: Foreign key to WebhookSubscriber
371
+ - `event_name`: Event to subscribe to (e.g., 'created', 'updated')
372
+ - `custom_endpoint`: Optional endpoint override
373
+ - `success_rate`: Calculated property showing delivery success percentage
374
+
375
+ **Properties:**
376
+
377
+ - `endpoint`: Full URL for this subscription
378
+ - `success_rate`: Percentage of successful deliveries
379
+
380
+ **Methods:**
381
+
382
+ - `record_delivery_attempt(success, response_text)`: Track delivery stats
383
+
384
+ ### WebhookDeliveryLog
385
+
386
+ Log of individual webhook delivery attempts.
387
+
388
+ **Key Fields:**
389
+
390
+ - `subscription`: Foreign key to WebhookSubscription
391
+ - `attempt_number`: Retry attempt number
392
+ - `response_status`: HTTP response status
393
+ - `response_body`: HTTP response body (truncated)
394
+ - `delivery_duration_ms`: Delivery time in milliseconds
395
+ - `error_message`: Exception details if delivery failed
396
+
397
+ **Properties:**
398
+
399
+ - `is_success`: True if delivery was successful (2xx status)
400
+ - `is_client_error`: True for 4xx status codes
401
+ - `is_server_error`: True for 5xx status codes
402
+
403
+ ## API Reference
404
+
405
+ ### Core Functions
406
+
407
+ #### `send_webhooks(instance, event_name, context=None, **kwargs)`
408
+
409
+ Send webhooks for a model instance and event.
410
+
411
+ **Parameters:**
412
+
413
+ - `instance`: Django model instance
414
+ - `event_name`: String event name
415
+ - `context`: Optional dict with additional data
416
+ - `**kwargs`: Additional arguments
417
+
418
+ **Returns:**
419
+
420
+ - Dict with processing summary
421
+
422
+ **Example:**
423
+
424
+ ```python
425
+ result = send_webhooks(user, 'created')
426
+ # Returns: {'processed': 3, 'batches': 1, 'task_ids': [...]}
427
+ ```
428
+
429
+ #### `clear_webhook_cache(content_type=None, event_name=None)`
430
+
431
+ Clear cached webhook subscription data.
432
+
433
+ **Parameters:**
434
+
435
+ - `content_type`: Optional ContentType to limit clearing
436
+ - `event_name`: Optional event name to limit clearing
437
+
438
+ #### `get_webhook_cache_stats()`
439
+
440
+ Get detailed cache statistics.
441
+
442
+ **Returns:**
443
+
444
+ - Dict with cache hit ratios, key counts, etc.
445
+
446
+ ### Utility Functions
447
+
448
+ #### `webhooks_disabled()`
449
+
450
+ Check if webhooks are currently disabled.
451
+
452
+ #### `disable_webhooks()` (context manager)
453
+
454
+ Temporarily disable webhook sending.
455
+
456
+ #### `generate_secret()`
457
+
458
+ Generate a new webhook secret key.
459
+
460
+ ## Admin Interface
461
+
462
+ The package provides a comprehensive Django admin interface:
463
+
464
+ ### WebhookSubscriber Admin
465
+
466
+ - **Health indicators**: Visual status showing healthy/warning/critical
467
+ - **Performance tracking**: Success rates and failure counts
468
+ - **Connectivity testing**: Test endpoints via admin actions
469
+ - **Cache management**: Clear cache for specific subscribers
470
+
471
+ **Available Actions:**
472
+
473
+ - Activate/deactivate subscribers
474
+ - Test endpoint connectivity
475
+ - Reset failure counters
476
+ - Clear cache
477
+
478
+ ### WebhookSubscription Admin
479
+
480
+ - **Performance metrics**: Success rates with color coding
481
+ - **Recent deliveries**: Visual icons showing last 5 attempts
482
+ - **Statistics management**: Reset performance counters
483
+
484
+ ### WebhookDeliveryLog Admin
485
+
486
+ - **Detailed logging**: Formatted JSON payloads and responses
487
+ - **Performance analysis**: Delivery times and retry information
488
+ - **Error categorization**: Grouped error analysis
489
+
490
+ ## Management Commands
491
+
492
+ ### Cache Management
493
+
494
+ ```bash
495
+ # Show cache statistics
496
+ python manage.py webhook_cache stats
497
+
498
+ # Clear all cache
499
+ python manage.py webhook_cache clear
500
+
501
+ # Clear specific content type
502
+ python manage.py webhook_cache clear --content-type=myapp.User
503
+
504
+ # Pre-warm cache
505
+ python manage.py webhook_cache warm
506
+ ```
507
+
508
+ ### Testing Endpoints
509
+
510
+ ```bash
511
+ # Test all active subscribers
512
+ python manage.py webhook_test --all
513
+
514
+ # Test with custom payload
515
+ python manage.py webhook_test --subscriber-id=1 --method=POST --payload='{"test": true}'
516
+ ```
517
+
518
+ ### Log Management
519
+
520
+ ```bash
521
+ # Clean up old logs
522
+ python manage.py webhook_logs cleanup --days=30
523
+
524
+ # Show statistics
525
+ python manage.py webhook_logs stats --days=7
526
+
527
+ # Show recent errors
528
+ python manage.py webhook_logs errors --limit=20
529
+ ```
530
+
531
+ ### System Status
532
+
533
+ ```bash
534
+ # Overview
535
+ python manage.py webhook_status
536
+
537
+ # Detailed per-subscriber status
538
+ python manage.py webhook_status --detailed
539
+ ```
540
+
541
+ ### Manual Webhook Sending
542
+
543
+ ```bash
544
+ # Send test webhook
545
+ python manage.py webhook_send myapp.User 123 created
546
+
547
+ # With custom context
548
+ python manage.py webhook_send myapp.Order 456 paid --context='{"payment_method": "stripe"}'
549
+ ```
550
+
551
+ ## Performance Testing
552
+
553
+ Test your webhook system under load:
554
+
555
+ ```bash
556
+ # Basic performance test
557
+ python manage.py webhook_performance_test \
558
+ --model=myapp.User \
559
+ --event=created \
560
+ --object-count=20 \
561
+ --concurrent-webhooks=10
562
+
563
+ # High-load test with delivery measurement
564
+ python manage.py webhook_performance_test \
565
+ --model=myapp.Order \
566
+ --event=paid \
567
+ --object-count=50 \
568
+ --concurrent-webhooks=20 \
569
+ --measure-delivery \
570
+ --timeout=60 \
571
+ --warmup
572
+
573
+ # Sustained load test
574
+ python manage.py webhook_performance_test \
575
+ --model=myapp.Product \
576
+ --event=updated \
577
+ --object-count=15 \
578
+ --batches=5 \
579
+ --batch-delay=2.0
580
+ ```
581
+
582
+ **Performance Metrics Provided:**
583
+
584
+ - Send throughput (webhooks/second)
585
+ - Delivery timing statistics
586
+ - Success/failure rates
587
+ - Error categorization
588
+ - Concurrency analysis
589
+
590
+ ## Production Deployment
591
+
592
+ ### Celery Setup
593
+
594
+ Ensure Celery is properly configured for production:
595
+
596
+ ```python
597
+ # celery.py
598
+ from celery import Celery
599
+
600
+ app = Celery('myproject')
601
+ app.config_from_object('django.conf:settings', namespace='CELERY')
602
+
603
+ # Configure for production
604
+ app.conf.update(
605
+ task_serializer='json',
606
+ accept_content=['json'],
607
+ result_serializer='json',
608
+ timezone='UTC',
609
+ enable_utc=True,
610
+ task_track_started=True,
611
+ worker_hijack_root_logger=False,
612
+ worker_log_format='[%(asctime)s: %(levelname)s/%(processName)s] %(message)s',
613
+ )
614
+
615
+ app.autodiscover_tasks()
616
+ ```
617
+
618
+ ### Monitoring Setup
619
+
620
+ Configure logging for webhook monitoring:
621
+
622
+ ```python
623
+ # settings.py
624
+ LOGGING = {
625
+ 'version': 1,
626
+ 'disable_existing_loggers': False,
627
+ 'handlers': {
628
+ 'webhook_file': {
629
+ 'level': 'INFO',
630
+ 'class': 'logging.FileHandler',
631
+ 'filename': '/var/log/django/webhooks.log',
632
+ },
633
+ },
634
+ 'loggers': {
635
+ 'django_webhook_subscriber': {
636
+ 'handlers': ['webhook_file'],
637
+ 'level': 'INFO',
638
+ 'propagate': True,
639
+ },
640
+ },
641
+ }
642
+ ```
643
+
644
+ ### Automated Maintenance
645
+
646
+ Set up cron jobs for maintenance:
647
+
648
+ ```bash
649
+ # Daily log cleanup at 2 AM
650
+ 0 2 * * * cd /path/to/project && python manage.py webhook_logs cleanup
651
+
652
+ # Weekly health check report
653
+ 0 9 * * 1 cd /path/to/project && python manage.py webhook_status --detailed
654
+
655
+ # Cache warming after deployments
656
+ @reboot cd /path/to/project && python manage.py webhook_cache warm
657
+ ```
658
+
659
+ ### Performance Optimization
660
+
661
+ 1. **Use Redis for caching:**
662
+
663
+ ```python
664
+ CACHES = {
665
+ 'default': {
666
+ 'BACKEND': 'django.core.cache.backends.redis.RedisCache',
667
+ 'LOCATION': 'redis://127.0.0.1:6379/1',
668
+ }
669
+ }
670
+ ```
671
+
672
+ 2. **Configure appropriate batch sizes:**
673
+
674
+ ```python
675
+ WEBHOOK_SUBSCRIBER = {
676
+ 'MAX_WEBHOOK_BATCH_SIZE': 25, # Adjust based on your load
677
+ 'WEBHOOK_CACHE_TTL': 600, # 10 minutes
678
+ }
679
+ ```
680
+
681
+ 3. **Monitor delivery performance:**
682
+
683
+ ```bash
684
+ # Regular performance testing
685
+ python manage.py webhook_performance_test --model=myapp.User --event=test --object-count=10
686
+ ```
687
+
688
+ ## Troubleshooting
689
+
690
+ ### Common Issues
691
+
692
+ #### 1. Webhooks Not Sending
693
+
694
+ **Check:**
695
+
696
+ - Webhooks globally enabled: `DISABLE_WEBHOOKS = False`
697
+ - Active subscriptions exist for the model/event
698
+ - Celery workers are running
699
+
700
+ **Debug:**
701
+
702
+ ```bash
703
+ python manage.py webhook_status --detailed
704
+ python manage.py webhook_cache stats
705
+ ```
706
+
707
+ #### 2. High Delivery Failures
708
+
709
+ **Check:**
710
+
711
+ - Endpoint accessibility
712
+ - Network connectivity
713
+ - Authentication headers
714
+
715
+ **Debug:**
716
+
717
+ ```bash
718
+ python manage.py webhook_test --all
719
+ python manage.py webhook_logs errors --limit=50
720
+ ```
721
+
722
+ #### 3. Performance Issues
723
+
724
+ **Check:**
725
+
726
+ - Cache hit ratios
727
+ - Batch sizes
728
+ - Celery worker count
729
+
730
+ **Debug:**
731
+
732
+ ```bash
733
+ python manage.py webhook_cache stats
734
+ python manage.py webhook_performance_test --model=myapp.User --event=test --object-count=5
735
+ ```
736
+
737
+ #### 4. Memory Usage
738
+
739
+ **Solutions:**
740
+
741
+ - Reduce `LOG_RETENTION_DAYS`
742
+ - Increase cleanup frequency
743
+ - Limit response body storage
744
+
745
+ ```bash
746
+ python manage.py webhook_logs cleanup --days=7
747
+ ```
748
+
749
+ ### Error Reference
750
+
751
+ #### Common Error Messages
752
+
753
+ - **"No subscriptions found"**: No active subscriptions for model/event
754
+ - **"Webhook disabled"**: Global or context-specific disabling active
755
+ - **"Subscription not found"**: Subscription deleted during processing
756
+ - **"Connection error"**: Network connectivity issues
757
+ - **"Timeout"**: Endpoint response too slow
758
+
759
+ #### Performance Warnings
760
+
761
+ - **Cache miss ratio > 50%**: Consider warming cache more frequently
762
+ - **Delivery time > 5s**: Endpoint performance issues
763
+ - **Success rate < 95%**: Check endpoint reliability
764
+
765
+ ### Debug Mode
766
+
767
+ Enable detailed logging for debugging:
768
+
769
+ ```python
770
+ # settings.py
771
+ LOGGING = {
772
+ 'loggers': {
773
+ 'django_webhook_subscriber': {
774
+ 'level': 'DEBUG',
775
+ 'handlers': ['console'],
776
+ },
777
+ },
778
+ }
779
+ ```
780
+
781
+ ## Contributing
782
+
783
+ ### Development Setup
784
+
785
+ 1. Clone the repository
786
+ 2. Install development dependencies: `pip install -e .[dev]`
787
+ 3. Run tests: `python -m pytest`
788
+ 4. Run linting: `flake8 django_webhook_subscriber/`
789
+
790
+ ### Running Tests
791
+
792
+ ```bash
793
+ # Run all tests
794
+ python -m pytest
795
+
796
+ # Run specific test file
797
+ python -m pytest tests/test_delivery.py
798
+
799
+ # Run with coverage
800
+ python -m pytest --cov=django_webhook_subscriber
801
+ ```
802
+
803
+ ### Contributing Guidelines
804
+
805
+ 1. Fork the repository
806
+ 2. Create a feature branch
807
+ 3. Add tests for new functionality
808
+ 4. Ensure all tests pass
809
+ 5. Submit a pull request
810
+
811
+ ### Reporting Issues
812
+
813
+ Please include:
814
+
815
+ - Django version
816
+ - Package version
817
+ - Full error traceback
818
+ - Minimal reproduction case
819
+
820
+ ## License
821
+
822
+ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.