django-webhook-subscriber 0.4.0__py3-none-any.whl → 2.0.0__py3-none-any.whl

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 (41) hide show
  1. django_webhook_subscriber/__init__.py +7 -1
  2. django_webhook_subscriber/admin.py +831 -182
  3. django_webhook_subscriber/apps.py +3 -20
  4. django_webhook_subscriber/conf.py +11 -24
  5. django_webhook_subscriber/delivery.py +414 -159
  6. django_webhook_subscriber/http.py +51 -0
  7. django_webhook_subscriber/management/commands/webhook.py +169 -0
  8. django_webhook_subscriber/management/commands/webhook_cache.py +173 -0
  9. django_webhook_subscriber/management/commands/webhook_logs.py +226 -0
  10. django_webhook_subscriber/management/commands/webhook_performance_test.py +469 -0
  11. django_webhook_subscriber/management/commands/webhook_send.py +96 -0
  12. django_webhook_subscriber/management/commands/webhook_status.py +139 -0
  13. django_webhook_subscriber/managers.py +36 -14
  14. django_webhook_subscriber/migrations/0002_remove_webhookregistry_content_type_and_more.py +192 -0
  15. django_webhook_subscriber/models.py +291 -114
  16. django_webhook_subscriber/serializers.py +16 -50
  17. django_webhook_subscriber/tasks.py +434 -56
  18. django_webhook_subscriber/tests/factories.py +40 -0
  19. django_webhook_subscriber/tests/settings.py +27 -8
  20. django_webhook_subscriber/tests/test_delivery.py +453 -190
  21. django_webhook_subscriber/tests/test_http.py +32 -0
  22. django_webhook_subscriber/tests/test_managers.py +26 -37
  23. django_webhook_subscriber/tests/test_models.py +341 -251
  24. django_webhook_subscriber/tests/test_serializers.py +22 -56
  25. django_webhook_subscriber/tests/test_tasks.py +477 -189
  26. django_webhook_subscriber/tests/test_utils.py +98 -94
  27. django_webhook_subscriber/utils.py +87 -69
  28. django_webhook_subscriber/validators.py +53 -0
  29. django_webhook_subscriber-2.0.0.dist-info/METADATA +774 -0
  30. django_webhook_subscriber-2.0.0.dist-info/RECORD +38 -0
  31. django_webhook_subscriber/management/commands/check_webhook_tasks.py +0 -113
  32. django_webhook_subscriber/management/commands/clean_webhook_logs.py +0 -65
  33. django_webhook_subscriber/management/commands/test_webhook.py +0 -96
  34. django_webhook_subscriber/signals.py +0 -152
  35. django_webhook_subscriber/testing.py +0 -14
  36. django_webhook_subscriber/tests/test_signals.py +0 -268
  37. django_webhook_subscriber-0.4.0.dist-info/METADATA +0 -448
  38. django_webhook_subscriber-0.4.0.dist-info/RECORD +0 -33
  39. {django_webhook_subscriber-0.4.0.dist-info → django_webhook_subscriber-2.0.0.dist-info}/WHEEL +0 -0
  40. {django_webhook_subscriber-0.4.0.dist-info → django_webhook_subscriber-2.0.0.dist-info}/licenses/LICENSE +0 -0
  41. {django_webhook_subscriber-0.4.0.dist-info → django_webhook_subscriber-2.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,774 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-webhook-subscriber
3
+ Version: 2.0.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
+ ## Configuration
176
+
177
+ Configure the package in your Django settings:
178
+
179
+ <!-- TODO: confirm these are all the settings -->
180
+
181
+ ```python
182
+ WEBHOOK_SUBSCRIBER = {
183
+ # Log retention
184
+ "LOG_RETENTION_DAYS": 30,
185
+ "AUTO_CLEANUP": True,
186
+
187
+ # Performance settings
188
+ "MAX_WEBHOOK_BATCH_SIZE": 50,
189
+ "WEBHOOK_CACHE_TTL": 300,
190
+
191
+ # Defaults for new subscribers
192
+ "DEFAULT_MAX_RETRIES": 3,
193
+ "DEFAULT_RETRY_DELAY": 60,
194
+ "REQUEST_TIMEOUT": 30,
195
+ }
196
+
197
+
198
+ # Recommended cache configuration
199
+ CACHES = {
200
+ 'default': {
201
+ 'BACKEND': 'django.core.cache.backends.redis.RedisCache',
202
+ 'LOCATION': 'redis://127.0.0.1:6379/1',
203
+ 'OPTIONS': {
204
+ 'CONNECTION_POOL_KWARGS': {'max_connections': 20},
205
+ }
206
+ }
207
+ }
208
+
209
+
210
+ # Optional: Disable webhooks globally
211
+ DISABLE_WEBHOOKS = False # Set to True to disable all webhook sending
212
+ ```
213
+
214
+ ## Basic Usage
215
+
216
+ ### Sending Webhooks
217
+
218
+ The main API is the `send_webhooks()` function:
219
+
220
+ ```python
221
+ from django_webhook_subscriber import send_webhooks
222
+
223
+ # Basic usage
224
+ send_webhooks(instance, 'event_name')
225
+ ```
226
+
227
+ ### Common Integration Patterns
228
+
229
+ #### Django Signals
230
+
231
+ ```python
232
+ # signals.py
233
+ from django.db.models.signals import post_save
234
+ from django.dispatch import receiver
235
+ from django_webhook_subscriber import send_webhooks
236
+ from django.contrib.auth import get_user_model
237
+
238
+ User = get_user_model()
239
+
240
+ @receiver(post_save, sender=User)
241
+ def user_saved(sender, instance, created, **kwargs):
242
+ event_name = "created" if created else "updated"
243
+ send_webhooks(instance, event_name)
244
+
245
+ ```
246
+
247
+ #### Django Lifecycle
248
+
249
+ ```python
250
+ # models.py
251
+ from django_lifecycle import LifecycleModel, hook, AFTER_CREATE, AFTER_UPDATE
252
+ from django_webhook_subscriber import send_webhooks
253
+
254
+ class User(LifecycleModel):
255
+ name = models.CharField(max_length=100)
256
+ email = models.EmailField()
257
+
258
+ @hook(AFTER_CREATE)
259
+ def on_create(self):
260
+ send_webhooks(self, 'created')
261
+
262
+ @hook(AFTER_UPDATE, when='email', has_changed=True)
263
+ def on_email_update(self):
264
+ send_webhooks(self, 'email_updated')
265
+ ```
266
+
267
+ #### Manual Integration
268
+
269
+ ```python
270
+ # myapp/views.py
271
+ from django_webhook_subscriber import send_webhooks
272
+
273
+ class UserViewSet(viewsets.ModelViewSet):
274
+ def perform_create(self, serializer):
275
+ user = serializer.save()
276
+ send_webhooks(user, "created")
277
+
278
+ def perform_update(self, serializer):
279
+ user = serializer.save()
280
+ send_webhooks(user, "updated")
281
+ ```
282
+
283
+ ### Disabling Webhooks Temporarily
284
+
285
+ ```python
286
+ from django_webhook_subscriber.utils import disable_webhooks
287
+
288
+ # Disable webhooks for bulk operations
289
+ with disable_webhooks():
290
+ User.objects.bulk_create([...]) # No webhooks sent
291
+
292
+ # Webhooks resume normal operation after the context
293
+ ```
294
+
295
+ ## Models Reference
296
+
297
+ ### WebhookSubscriber
298
+
299
+ Represents an external service that wants to receive webhooks.
300
+
301
+ **Key Fields:**
302
+
303
+ - `name`: Human-readable name for the subscriber
304
+ - `target_url`: Base URL for webhook delivery
305
+ - `content_type`: Django model this subscriber watches
306
+ - `serializer_class`: Optional custom DRF serializer
307
+ - `max_retries`: Maximum delivery attempts
308
+ - `retry_delay`: Seconds between retries
309
+ - `timeout`: Request timeout
310
+ - `auto_disable_after_failures`: Auto-disable threshold
311
+
312
+ **Methods:**
313
+
314
+ - `record_success()`: Mark successful delivery
315
+ - `record_failure()`: Mark failed delivery, handle auto-disable
316
+
317
+ ### WebhookSubscription
318
+
319
+ Individual event subscription for a subscriber.
320
+
321
+ **Key Fields:**
322
+
323
+ - `subscriber`: Foreign key to WebhookSubscriber
324
+ - `event_name`: Event to subscribe to (e.g., 'created', 'updated')
325
+ - `custom_endpoint`: Optional endpoint override
326
+ - `success_rate`: Calculated property showing delivery success percentage
327
+
328
+ **Properties:**
329
+
330
+ - `endpoint`: Full URL for this subscription
331
+ - `success_rate`: Percentage of successful deliveries
332
+
333
+ **Methods:**
334
+
335
+ - `record_delivery_attempt(success, response_text)`: Track delivery stats
336
+
337
+ ### WebhookDeliveryLog
338
+
339
+ Log of individual webhook delivery attempts.
340
+
341
+ **Key Fields:**
342
+
343
+ - `subscription`: Foreign key to WebhookSubscription
344
+ - `attempt_number`: Retry attempt number
345
+ - `response_status`: HTTP response status
346
+ - `response_body`: HTTP response body (truncated)
347
+ - `delivery_duration_ms`: Delivery time in milliseconds
348
+ - `error_message`: Exception details if delivery failed
349
+
350
+ **Properties:**
351
+
352
+ - `is_success`: True if delivery was successful (2xx status)
353
+ - `is_client_error`: True for 4xx status codes
354
+ - `is_server_error`: True for 5xx status codes
355
+
356
+ ## API Reference
357
+
358
+ ### Core Functions
359
+
360
+ #### `send_webhooks(instance, event_name, context=None, **kwargs)`
361
+
362
+ Send webhooks for a model instance and event.
363
+
364
+ **Parameters:**
365
+
366
+ - `instance`: Django model instance
367
+ - `event_name`: String event name
368
+ - `context`: Optional dict with additional data
369
+ - `**kwargs`: Additional arguments
370
+
371
+ **Returns:**
372
+
373
+ - Dict with processing summary
374
+
375
+ **Example:**
376
+
377
+ ```python
378
+ result = send_webhooks(user, 'created')
379
+ # Returns: {'processed': 3, 'batches': 1, 'task_ids': [...]}
380
+ ```
381
+
382
+ #### `clear_webhook_cache(content_type=None, event_name=None)`
383
+
384
+ Clear cached webhook subscription data.
385
+
386
+ **Parameters:**
387
+
388
+ - `content_type`: Optional ContentType to limit clearing
389
+ - `event_name`: Optional event name to limit clearing
390
+
391
+ #### `get_webhook_cache_stats()`
392
+
393
+ Get detailed cache statistics.
394
+
395
+ **Returns:**
396
+
397
+ - Dict with cache hit ratios, key counts, etc.
398
+
399
+ ### Utility Functions
400
+
401
+ #### `webhooks_disabled()`
402
+
403
+ Check if webhooks are currently disabled.
404
+
405
+ #### `disable_webhooks()` (context manager)
406
+
407
+ Temporarily disable webhook sending.
408
+
409
+ #### `generate_secret()`
410
+
411
+ Generate a new webhook secret key.
412
+
413
+ ## Admin Interface
414
+
415
+ The package provides a comprehensive Django admin interface:
416
+
417
+ ### WebhookSubscriber Admin
418
+
419
+ - **Health indicators**: Visual status showing healthy/warning/critical
420
+ - **Performance tracking**: Success rates and failure counts
421
+ - **Connectivity testing**: Test endpoints via admin actions
422
+ - **Cache management**: Clear cache for specific subscribers
423
+
424
+ **Available Actions:**
425
+
426
+ - Activate/deactivate subscribers
427
+ - Test endpoint connectivity
428
+ - Reset failure counters
429
+ - Clear cache
430
+
431
+ ### WebhookSubscription Admin
432
+
433
+ - **Performance metrics**: Success rates with color coding
434
+ - **Recent deliveries**: Visual icons showing last 5 attempts
435
+ - **Statistics management**: Reset performance counters
436
+
437
+ ### WebhookDeliveryLog Admin
438
+
439
+ - **Detailed logging**: Formatted JSON payloads and responses
440
+ - **Performance analysis**: Delivery times and retry information
441
+ - **Error categorization**: Grouped error analysis
442
+
443
+ ## Management Commands
444
+
445
+ ### Cache Management
446
+
447
+ ```bash
448
+ # Show cache statistics
449
+ python manage.py webhook_cache stats
450
+
451
+ # Clear all cache
452
+ python manage.py webhook_cache clear
453
+
454
+ # Clear specific content type
455
+ python manage.py webhook_cache clear --content-type=myapp.User
456
+
457
+ # Pre-warm cache
458
+ python manage.py webhook_cache warm
459
+ ```
460
+
461
+ ### Testing Endpoints
462
+
463
+ ```bash
464
+ # Test all active subscribers
465
+ python manage.py webhook_test --all
466
+
467
+ # Test with custom payload
468
+ python manage.py webhook_test --subscriber-id=1 --method=POST --payload='{"test": true}'
469
+ ```
470
+
471
+ ### Log Management
472
+
473
+ ```bash
474
+ # Clean up old logs
475
+ python manage.py webhook_logs cleanup --days=30
476
+
477
+ # Show statistics
478
+ python manage.py webhook_logs stats --days=7
479
+
480
+ # Show recent errors
481
+ python manage.py webhook_logs errors --limit=20
482
+ ```
483
+
484
+ ### System Status
485
+
486
+ ```bash
487
+ # Overview
488
+ python manage.py webhook_status
489
+
490
+ # Detailed per-subscriber status
491
+ python manage.py webhook_status --detailed
492
+ ```
493
+
494
+ ### Manual Webhook Sending
495
+
496
+ ```bash
497
+ # Send test webhook
498
+ python manage.py webhook_send myapp.User 123 created
499
+
500
+ # With custom context
501
+ python manage.py webhook_send myapp.Order 456 paid --context='{"payment_method": "stripe"}'
502
+ ```
503
+
504
+ ## Performance Testing
505
+
506
+ Test your webhook system under load:
507
+
508
+ ```bash
509
+ # Basic performance test
510
+ python manage.py webhook_performance_test \
511
+ --model=myapp.User \
512
+ --event=created \
513
+ --object-count=20 \
514
+ --concurrent-webhooks=10
515
+
516
+ # High-load test with delivery measurement
517
+ python manage.py webhook_performance_test \
518
+ --model=myapp.Order \
519
+ --event=paid \
520
+ --object-count=50 \
521
+ --concurrent-webhooks=20 \
522
+ --measure-delivery \
523
+ --timeout=60 \
524
+ --warmup
525
+
526
+ # Sustained load test
527
+ python manage.py webhook_performance_test \
528
+ --model=myapp.Product \
529
+ --event=updated \
530
+ --object-count=15 \
531
+ --batches=5 \
532
+ --batch-delay=2.0
533
+ ```
534
+
535
+ **Performance Metrics Provided:**
536
+
537
+ - Send throughput (webhooks/second)
538
+ - Delivery timing statistics
539
+ - Success/failure rates
540
+ - Error categorization
541
+ - Concurrency analysis
542
+
543
+ ## Production Deployment
544
+
545
+ ### Celery Setup
546
+
547
+ Ensure Celery is properly configured for production:
548
+
549
+ ```python
550
+ # celery.py
551
+ from celery import Celery
552
+
553
+ app = Celery('myproject')
554
+ app.config_from_object('django.conf:settings', namespace='CELERY')
555
+
556
+ # Configure for production
557
+ app.conf.update(
558
+ task_serializer='json',
559
+ accept_content=['json'],
560
+ result_serializer='json',
561
+ timezone='UTC',
562
+ enable_utc=True,
563
+ task_track_started=True,
564
+ worker_hijack_root_logger=False,
565
+ worker_log_format='[%(asctime)s: %(levelname)s/%(processName)s] %(message)s',
566
+ )
567
+
568
+ app.autodiscover_tasks()
569
+ ```
570
+
571
+ ### Monitoring Setup
572
+
573
+ Configure logging for webhook monitoring:
574
+
575
+ ```python
576
+ # settings.py
577
+ LOGGING = {
578
+ 'version': 1,
579
+ 'disable_existing_loggers': False,
580
+ 'handlers': {
581
+ 'webhook_file': {
582
+ 'level': 'INFO',
583
+ 'class': 'logging.FileHandler',
584
+ 'filename': '/var/log/django/webhooks.log',
585
+ },
586
+ },
587
+ 'loggers': {
588
+ 'django_webhook_subscriber': {
589
+ 'handlers': ['webhook_file'],
590
+ 'level': 'INFO',
591
+ 'propagate': True,
592
+ },
593
+ },
594
+ }
595
+ ```
596
+
597
+ ### Automated Maintenance
598
+
599
+ Set up cron jobs for maintenance:
600
+
601
+ ```bash
602
+ # Daily log cleanup at 2 AM
603
+ 0 2 * * * cd /path/to/project && python manage.py webhook_logs cleanup
604
+
605
+ # Weekly health check report
606
+ 0 9 * * 1 cd /path/to/project && python manage.py webhook_status --detailed
607
+
608
+ # Cache warming after deployments
609
+ @reboot cd /path/to/project && python manage.py webhook_cache warm
610
+ ```
611
+
612
+ ### Performance Optimization
613
+
614
+ 1. **Use Redis for caching:**
615
+
616
+ ```python
617
+ CACHES = {
618
+ 'default': {
619
+ 'BACKEND': 'django.core.cache.backends.redis.RedisCache',
620
+ 'LOCATION': 'redis://127.0.0.1:6379/1',
621
+ }
622
+ }
623
+ ```
624
+
625
+ 2. **Configure appropriate batch sizes:**
626
+
627
+ ```python
628
+ WEBHOOK_SUBSCRIBER = {
629
+ 'MAX_WEBHOOK_BATCH_SIZE': 25, # Adjust based on your load
630
+ 'WEBHOOK_CACHE_TTL': 600, # 10 minutes
631
+ }
632
+ ```
633
+
634
+ 3. **Monitor delivery performance:**
635
+ ```bash
636
+ # Regular performance testing
637
+ python manage.py webhook_performance_test --model=myapp.User --event=test --object-count=10
638
+ ```
639
+
640
+ ## Troubleshooting
641
+
642
+ ### Common Issues
643
+
644
+ #### 1. Webhooks Not Sending
645
+
646
+ **Check:**
647
+
648
+ - Webhooks globally enabled: `DISABLE_WEBHOOKS = False`
649
+ - Active subscriptions exist for the model/event
650
+ - Celery workers are running
651
+
652
+ **Debug:**
653
+
654
+ ```bash
655
+ python manage.py webhook_status --detailed
656
+ python manage.py webhook_cache stats
657
+ ```
658
+
659
+ #### 2. High Delivery Failures
660
+
661
+ **Check:**
662
+
663
+ - Endpoint accessibility
664
+ - Network connectivity
665
+ - Authentication headers
666
+
667
+ **Debug:**
668
+
669
+ ```bash
670
+ python manage.py webhook_test --all
671
+ python manage.py webhook_logs errors --limit=50
672
+ ```
673
+
674
+ #### 3. Performance Issues
675
+
676
+ **Check:**
677
+
678
+ - Cache hit ratios
679
+ - Batch sizes
680
+ - Celery worker count
681
+
682
+ **Debug:**
683
+
684
+ ```bash
685
+ python manage.py webhook_cache stats
686
+ python manage.py webhook_performance_test --model=myapp.User --event=test --object-count=5
687
+ ```
688
+
689
+ #### 4. Memory Usage
690
+
691
+ **Solutions:**
692
+
693
+ - Reduce `LOG_RETENTION_DAYS`
694
+ - Increase cleanup frequency
695
+ - Limit response body storage
696
+
697
+ ```bash
698
+ python manage.py webhook_logs cleanup --days=7
699
+ ```
700
+
701
+ ### Error Reference
702
+
703
+ #### Common Error Messages
704
+
705
+ - **"No subscriptions found"**: No active subscriptions for model/event
706
+ - **"Webhook disabled"**: Global or context-specific disabling active
707
+ - **"Subscription not found"**: Subscription deleted during processing
708
+ - **"Connection error"**: Network connectivity issues
709
+ - **"Timeout"**: Endpoint response too slow
710
+
711
+ #### Performance Warnings
712
+
713
+ - **Cache miss ratio > 50%**: Consider warming cache more frequently
714
+ - **Delivery time > 5s**: Endpoint performance issues
715
+ - **Success rate < 95%**: Check endpoint reliability
716
+
717
+ ### Debug Mode
718
+
719
+ Enable detailed logging for debugging:
720
+
721
+ ```python
722
+ # settings.py
723
+ LOGGING = {
724
+ 'loggers': {
725
+ 'django_webhook_subscriber': {
726
+ 'level': 'DEBUG',
727
+ 'handlers': ['console'],
728
+ },
729
+ },
730
+ }
731
+ ```
732
+
733
+ ## Contributing
734
+
735
+ ### Development Setup
736
+
737
+ 1. Clone the repository
738
+ 2. Install development dependencies: `pip install -e .[dev]`
739
+ 3. Run tests: `python -m pytest`
740
+ 4. Run linting: `flake8 django_webhook_subscriber/`
741
+
742
+ ### Running Tests
743
+
744
+ ```bash
745
+ # Run all tests
746
+ python -m pytest
747
+
748
+ # Run specific test file
749
+ python -m pytest tests/test_delivery.py
750
+
751
+ # Run with coverage
752
+ python -m pytest --cov=django_webhook_subscriber
753
+ ```
754
+
755
+ ### Contributing Guidelines
756
+
757
+ 1. Fork the repository
758
+ 2. Create a feature branch
759
+ 3. Add tests for new functionality
760
+ 4. Ensure all tests pass
761
+ 5. Submit a pull request
762
+
763
+ ### Reporting Issues
764
+
765
+ Please include:
766
+
767
+ - Django version
768
+ - Package version
769
+ - Full error traceback
770
+ - Minimal reproduction case
771
+
772
+ ## License
773
+
774
+ This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.