django-webhook-subscriber 1.0.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-1.0.0.dist-info/METADATA +0 -448
  38. django_webhook_subscriber-1.0.0.dist-info/RECORD +0 -33
  39. {django_webhook_subscriber-1.0.0.dist-info → django_webhook_subscriber-2.0.0.dist-info}/WHEEL +0 -0
  40. {django_webhook_subscriber-1.0.0.dist-info → django_webhook_subscriber-2.0.0.dist-info}/licenses/LICENSE +0 -0
  41. {django_webhook_subscriber-1.0.0.dist-info → django_webhook_subscriber-2.0.0.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,13 @@
1
1
  """Managers for Django Webhook Subscriber."""
2
2
 
3
+ import logging
4
+
3
5
  from django.db import models
4
6
  from django.utils import timezone
5
7
 
6
- from django_webhook_subscriber.conf import rest_webhook_settings
8
+ from .conf import rest_webhook_settings
9
+
10
+ logger = logging.getLogger(__name__)
7
11
 
8
12
 
9
13
  class WebhookDeliveryLogManager(models.Manager):
@@ -13,41 +17,59 @@ class WebhookDeliveryLogManager(models.Manager):
13
17
  age, and to clean up old logs based on retention settings.
14
18
  """
15
19
 
16
- def cleanup_old_logs(self, webhook=None):
20
+ def cleanup_old_logs(self, subscription=None):
17
21
  """This method will cleanup old logs based on retention settings."""
18
22
 
19
23
  # Get retention period from settings
20
- days = getattr(rest_webhook_settings, 'LOG_RETENTION_DAYS')
24
+ days = getattr(rest_webhook_settings, "LOG_RETENTION_DAYS")
21
25
 
22
26
  # Calculate cutoff date
23
27
  cutoff_date = timezone.now() - timezone.timedelta(days=days)
24
28
 
25
29
  # Build query
26
30
  query = self.filter(created_at__lt=cutoff_date)
27
- if webhook:
28
- # If a webhook is provided, filter by it
29
- query = query.filter(webhook=webhook)
31
+
32
+ logger.debug("Cleaning up logs older than: %s...", cutoff_date)
33
+
34
+ if subscription:
35
+ # If a subscription is provided, filter by it
36
+ query = query.filter(subscription=subscription)
37
+
38
+ logger.debug("Found a total of %d old logs.", query.count())
30
39
 
31
40
  # Delete old logs
32
- query.delete()
41
+ deleted, _ = query.delete()
42
+
43
+ logger.info("Deleted a total of %d old logs.", deleted)
33
44
 
34
45
  def create(self, **kwargs):
35
- """This method will create a new log entry, and proceed to cleanup
36
- old logs if necessary."""
46
+ """
47
+ This method will create a new log entry, and proceed to cleanup old
48
+ logs if necessary.
49
+ """
37
50
 
38
51
  # create the log entry
39
52
  log = super().create(**kwargs)
40
53
 
41
54
  # Checking if AUTO_CLEANUP is set to True
42
- auto_cleanup = getattr(rest_webhook_settings, 'AUTO_CLEANUP')
55
+ auto_cleanup = getattr(rest_webhook_settings, "AUTO_CLEANUP")
43
56
 
44
57
  # cleanup old logs if necessary
45
- if auto_cleanup and kwargs['webhook']:
58
+ if (
59
+ auto_cleanup
60
+ and "subscription" in kwargs
61
+ and kwargs["subscription"]
62
+ ):
46
63
  # Using a try/except block to prevent any errors during cleanup
47
64
  # from affecting the log creation process
48
65
  try:
49
- self.cleanup_old_logs(webhook=kwargs['webhook'])
50
- except Exception:
51
- pass
66
+ logger.debug(
67
+ "Cleaning up old logs for subscription: %s...",
68
+ kwargs["subscription"],
69
+ )
70
+
71
+ self.cleanup_old_logs(subscription=kwargs["subscription"])
72
+ except Exception as e:
73
+ logger.error("Error cleaning up old logs: %s", str(e))
52
74
 
53
75
  return log
@@ -0,0 +1,192 @@
1
+ # Generated by Django 5.2.6 on 2025-09-29 10:38
2
+
3
+ import django.db.models.deletion
4
+ import django_webhook_subscriber.utils
5
+ import django_webhook_subscriber.validators
6
+ from django.db import migrations, models
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+
11
+ dependencies = [
12
+ ('contenttypes', '0002_remove_content_type_name'),
13
+ ('django_webhook_subscriber', '0001_initial'),
14
+ ]
15
+
16
+ operations = [
17
+ migrations.RemoveField(
18
+ model_name='webhookregistry',
19
+ name='content_type',
20
+ ),
21
+ migrations.RemoveField(
22
+ model_name='webhookdeliverylog',
23
+ name='webhook',
24
+ ),
25
+ migrations.RemoveField(
26
+ model_name='webhookdeliverylog',
27
+ name='event_signal',
28
+ ),
29
+ migrations.AddField(
30
+ model_name='webhookdeliverylog',
31
+ name='attempt_number',
32
+ field=models.PositiveSmallIntegerField(default=1),
33
+ ),
34
+ migrations.AddField(
35
+ model_name='webhookdeliverylog',
36
+ name='delivery_duration_ms',
37
+ field=models.PositiveIntegerField(blank=True, null=True),
38
+ ),
39
+ migrations.AddField(
40
+ model_name='webhookdeliverylog',
41
+ name='delivery_url',
42
+ field=models.CharField(default='', max_length=500),
43
+ preserve_default=False,
44
+ ),
45
+ migrations.AddField(
46
+ model_name='webhookdeliverylog',
47
+ name='is_retry',
48
+ field=models.BooleanField(default=False),
49
+ ),
50
+ migrations.AddField(
51
+ model_name='webhookdeliverylog',
52
+ name='response_headers',
53
+ field=models.JSONField(blank=True, default=dict),
54
+ ),
55
+ migrations.AlterField(
56
+ model_name='webhookdeliverylog',
57
+ name='error_message',
58
+ field=models.TextField(blank=True, default=''),
59
+ preserve_default=False,
60
+ ),
61
+ migrations.AlterField(
62
+ model_name='webhookdeliverylog',
63
+ name='payload',
64
+ field=models.JSONField(default=dict),
65
+ ),
66
+ migrations.AlterField(
67
+ model_name='webhookdeliverylog',
68
+ name='response_body',
69
+ field=models.TextField(blank=True, default=None),
70
+ preserve_default=False,
71
+ ),
72
+ migrations.AlterField(
73
+ model_name='webhookdeliverylog',
74
+ name='response_status',
75
+ field=models.PositiveIntegerField(blank=True, null=True),
76
+ ),
77
+ migrations.CreateModel(
78
+ name='WebhookSubscriber',
79
+ fields=[
80
+ ('id', models.AutoField(primary_key=True, serialize=False)),
81
+ ('name', models.CharField(help_text='Name of this subscriber', max_length=255)),
82
+ ('description', models.TextField(blank=True)),
83
+ ('target_url', models.CharField(help_text='The base URL for webhook delivery', max_length=500)),
84
+ ('secret', models.CharField(default=django_webhook_subscriber.utils.generate_secret, help_text='Secret key for webhook authentication via X-Secret header', max_length=64)),
85
+ ('serializer_class', models.CharField(blank=True, help_text="Dot path to DRF serializer class (e.g., 'myapp.serializers.MySerializer')", max_length=512, validators=[django_webhook_subscriber.validators.validate_class_path])),
86
+ ('headers', models.JSONField(blank=True, default=dict, help_text='Additional headers to send (JSON format)', validators=[django_webhook_subscriber.validators.validate_headers])),
87
+ ('max_retries', models.PositiveIntegerField(default=3, help_text='Max delivery attempts')),
88
+ ('retry_delay', models.PositiveIntegerField(default=60, help_text='Seconds between retries')),
89
+ ('timeout', models.PositiveIntegerField(default=30, help_text='Request timeout in seconds')),
90
+ ('auto_disable_after_failures', models.PositiveIntegerField(default=10, help_text='Auto-disable after N consecutive failures (0 = never)')),
91
+ ('is_active', models.BooleanField(default=True)),
92
+ ('consecutive_failures', models.PositiveIntegerField(default=0)),
93
+ ('last_success', models.DateTimeField(blank=True, null=True)),
94
+ ('last_failure', models.DateTimeField(blank=True, null=True)),
95
+ ('created_at', models.DateTimeField(auto_now_add=True)),
96
+ ('updated_at', models.DateTimeField(auto_now=True)),
97
+ ('content_type', models.ForeignKey(help_text='The model this subscriber watches', on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')),
98
+ ],
99
+ options={
100
+ 'verbose_name': 'Webhook Subscriber',
101
+ 'verbose_name_plural': 'Webhook Subscribers',
102
+ 'db_table': 'django_webhook_subscriber_webhook_subscriber',
103
+ 'ordering': ['-created_at'],
104
+ },
105
+ ),
106
+ migrations.CreateModel(
107
+ name='WebhookSubscription',
108
+ fields=[
109
+ ('id', models.AutoField(primary_key=True, serialize=False)),
110
+ ('event_name', models.CharField(help_text="Event name (e.g., 'created', 'published', 'archived')", max_length=100)),
111
+ ('custom_endpoint', models.CharField(blank=True, help_text='Optional custom endpoint path or full URL', max_length=512)),
112
+ ('is_active', models.BooleanField(default=True)),
113
+ ('keep_last_response', models.BooleanField(default=True, help_text='Whether to store the last response received')),
114
+ ('last_response', models.TextField(blank=True, help_text='Last response received (truncated if too long)')),
115
+ ('last_success', models.DateTimeField(blank=True, null=True)),
116
+ ('last_failure', models.DateTimeField(blank=True, null=True)),
117
+ ('consecutive_failures', models.PositiveIntegerField(default=0)),
118
+ ('total_deliveries', models.PositiveIntegerField(default=0)),
119
+ ('successful_deliveries', models.PositiveIntegerField(default=0)),
120
+ ('created_at', models.DateTimeField(auto_now_add=True)),
121
+ ('updated_at', models.DateTimeField(auto_now=True)),
122
+ ('subscriber', models.ForeignKey(limit_choices_to=models.Q(('is_active', True)), on_delete=django.db.models.deletion.CASCADE, related_name='subscriptions', to='django_webhook_subscriber.webhooksubscriber')),
123
+ ],
124
+ options={
125
+ 'verbose_name': 'Webhook Subscription',
126
+ 'verbose_name_plural': 'Webhook Subscriptions',
127
+ 'db_table': 'django_webhook_subscriber_subscription',
128
+ 'ordering': ('subscriber__name', 'event_name'),
129
+ },
130
+ ),
131
+ migrations.AddField(
132
+ model_name='webhookdeliverylog',
133
+ name='subscription',
134
+ field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='delivery_logs', to='django_webhook_subscriber.webhooksubscription'),
135
+ preserve_default=False,
136
+ ),
137
+ migrations.AddIndex(
138
+ model_name='webhookdeliverylog',
139
+ index=models.Index(fields=['subscription', '-created_at'], name='django_webh_subscri_af8f0d_idx'),
140
+ ),
141
+ migrations.AddIndex(
142
+ model_name='webhookdeliverylog',
143
+ index=models.Index(fields=['response_status'], name='django_webh_respons_52f5e1_idx'),
144
+ ),
145
+ migrations.AddIndex(
146
+ model_name='webhookdeliverylog',
147
+ index=models.Index(fields=['created_at'], name='django_webh_created_bc530f_idx'),
148
+ ),
149
+ migrations.AddIndex(
150
+ model_name='webhookdeliverylog',
151
+ index=models.Index(fields=['attempt_number'], name='django_webh_attempt_e33fed_idx'),
152
+ ),
153
+ migrations.DeleteModel(
154
+ name='WebhookRegistry',
155
+ ),
156
+ migrations.AddIndex(
157
+ model_name='webhooksubscriber',
158
+ index=models.Index(fields=['content_type', 'is_active'], name='django_webh_content_784d7f_idx'),
159
+ ),
160
+ migrations.AddIndex(
161
+ model_name='webhooksubscriber',
162
+ index=models.Index(fields=['is_active'], name='django_webh_is_acti_0cbbec_idx'),
163
+ ),
164
+ migrations.AddIndex(
165
+ model_name='webhooksubscriber',
166
+ index=models.Index(fields=['content_type'], name='django_webh_content_6d305b_idx'),
167
+ ),
168
+ migrations.AlterUniqueTogether(
169
+ name='webhooksubscriber',
170
+ unique_together={('target_url', 'content_type')},
171
+ ),
172
+ migrations.AddIndex(
173
+ model_name='webhooksubscription',
174
+ index=models.Index(fields=['subscriber', 'event_name'], name='django_webh_subscri_ae1285_idx'),
175
+ ),
176
+ migrations.AddIndex(
177
+ model_name='webhooksubscription',
178
+ index=models.Index(fields=['is_active'], name='django_webh_is_acti_f01243_idx'),
179
+ ),
180
+ migrations.AddIndex(
181
+ model_name='webhooksubscription',
182
+ index=models.Index(fields=['subscriber', 'is_active'], name='django_webh_subscri_0376a3_idx'),
183
+ ),
184
+ migrations.AddIndex(
185
+ model_name='webhooksubscription',
186
+ index=models.Index(fields=['event_name', 'is_active'], name='django_webh_event_n_8184a3_idx'),
187
+ ),
188
+ migrations.AlterUniqueTogether(
189
+ name='webhooksubscription',
190
+ unique_together={('subscriber', 'event_name')},
191
+ ),
192
+ ]