Skip to content

Commit 2e243c4

Browse files
authored
Split up deprecated view notification to GitHub and other webhook endpoints (#5083)
* Updated copy on webhooks * Drop "deprecated webhook endpoint" copy, this is core team nomenclature, not user nomenclature. * Add small amount of docs to point to * Update docs and point to docs in notification message * Split up deprecated view notification to GitHub and other webhook endpoints This sets a date for deprecated of these endpoints as Mar 1st 2019. Too soon? * Reduce complexity and drop decorator pattern for Notification classmethod pattern used in other notifications * Add notifications for non-GitHub incoming webhooks * Add docs as well * More renaming and slight refactor Found out 2x messages are being generated, so this stops the automated mechanism for triggering these messages. * Update dates * Also update docs * Typo on date * Back out some more of the changes to notifications to make them operable without automation * Add admin method for notification * Add admin filter for project features
1 parent fb4bd86 commit 2e243c4

14 files changed

+116
-196
lines changed

docs/webhooks.rst

+31
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ details and a list of HTTP exchanges that have taken place for the integration.
2020
You need this information for the URL, webhook, or Payload URL needed by the
2121
repository provider such as GitHub, GitLab, or Bitbucket.
2222

23+
.. _webhook-creation:
24+
2325
Webhook Creation
2426
----------------
2527

@@ -181,3 +183,32 @@ on your Read the Docs project, or you can use a
181183
account.
182184

183185
.. [1] https://developer.github.com/changes/2018-04-25-github-services-deprecation/
186+
187+
.. _webhook-deprecated-endpoints:
188+
189+
I was warned that my project won't automatically build after April 1st
190+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
191+
192+
In addition to :ref:`no longer supporting GitHub Services <webhook-github-services>`,
193+
we have decided to no longer support several other legacy incoming webhook
194+
endpoints that were used before we introduced project webhook integrations. When
195+
we introduced our webhook integrations, we added several features and improved
196+
security for incoming webhooks and these features were not added to our leagcy
197+
incoming webhooks. New projects have not been able to use our legacy incoming
198+
webhooks since, however if you have a project that has been established for a
199+
while, you may still be using these endpoints.
200+
201+
After March 1st, 2019, we will stop accepting incoming webhook notifications for
202+
these legacy incoming webhooks. Your project will need to be reconfigured and
203+
have a webhook integration configured, pointing to a new webhook with your VCS
204+
provider.
205+
206+
In particular, the incoming webhook URLs that will be removed are:
207+
208+
* ``https://readthedocs.org/build``
209+
* ``https://readthedocs.org/bitbucket``
210+
* ``https://readthedocs.org/github`` (as noted :ref:`above <webhook-github-services>`)
211+
* ``https://readthedocs.org/gitlab``
212+
213+
In order to establish a new project webhook integration, :ref:`follow
214+
the directions for your VCS provider <webhook-creation>`

readthedocs/core/views/hooks.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
from readthedocs.builds.constants import LATEST
1919
from readthedocs.core.utils import trigger_build
20-
from readthedocs.notifications.decorators import notify_deprecated_endpoint
2120
from readthedocs.projects import constants
2221
from readthedocs.projects.models import Feature, Project
2322
from readthedocs.projects.tasks import sync_repository_task
@@ -127,7 +126,6 @@ def log_info(project, msg):
127126
msg=msg))
128127

129128

130-
@notify_deprecated_endpoint
131129
def _build_url(url, projects, branches):
132130
"""
133131
Map a URL onto specific projects to build that are linked to that URL.
@@ -343,7 +341,6 @@ def bitbucket_build(request):
343341

344342

345343
@csrf_exempt
346-
@notify_deprecated_endpoint
347344
def generic_build(request, project_id_or_slug=None):
348345
"""
349346
Generic webhook build endpoint.
@@ -373,7 +370,10 @@ def generic_build(request, project_id_or_slug=None):
373370
if request.method == 'POST':
374371
slug = request.POST.get('version_slug', project.default_version)
375372
log.info(
376-
"(Incoming Generic Build) %s [%s]", project.slug, slug)
373+
"(Incoming Generic Build) %s [%s]",
374+
project.slug,
375+
slug,
376+
)
377377
_build_version(project, slug)
378378
else:
379379
return HttpResponse("You must POST to this resource.")

readthedocs/notifications/backends.py

+7-6
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,7 @@ def send_notification(request, notification):
3232
backends = getattr(settings, 'NOTIFICATION_BACKENDS', [])
3333
for cls_name in backends:
3434
backend = import_string(cls_name)(request)
35-
# Do not send email notification if defined explicitly
36-
if backend.name == EmailBackend.name and not notification.send_email:
37-
pass
38-
else:
39-
backend.send(notification)
35+
backend.send(notification)
4036

4137

4238
class Backend(object):
@@ -55,11 +51,16 @@ class EmailBackend(Backend):
5551
5652
The content body is first rendered from an on-disk template, then passed
5753
into the standard email templates as a string.
54+
55+
If the notification is set to ``send_email=False``, this backend will exit
56+
early from :py:meth:`send`.
5857
"""
5958

6059
name = 'email'
6160

6261
def send(self, notification):
62+
if not notification.send_email:
63+
return
6364
# FIXME: if the level is an ERROR an email is received and sometimes
6465
# it's not necessary. This behavior should be clearly documented in the
6566
# code
@@ -114,6 +115,6 @@ def send(self, notification):
114115
backend_name=self.name,
115116
source_format=HTML,
116117
),
117-
extra_tags='',
118+
extra_tags=notification.extra_tags,
118119
user=notification.user,
119120
)

readthedocs/notifications/decorators.py

-51
This file was deleted.

readthedocs/notifications/notification.py

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class Notification(object):
3535
subject = None
3636
user = None
3737
send_email = True
38+
extra_tags = ''
3839

3940
def __init__(self, context_object, request, user=None):
4041
self.object = context_object

readthedocs/projects/admin.py

+11-3
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,20 @@
3030
ProjectRelationship,
3131
WebHook,
3232
)
33-
from .notifications import ResourceUsageNotification
33+
from .notifications import (
34+
ResourceUsageNotification,
35+
DeprecatedBuildWebhookNotification,
36+
DeprecatedGitHubWebhookNotification,
37+
)
3438
from .tasks import remove_dir
3539

3640

3741
class ProjectSendNotificationView(SendNotificationView):
38-
notification_classes = [ResourceUsageNotification]
42+
notification_classes = [
43+
ResourceUsageNotification,
44+
DeprecatedBuildWebhookNotification,
45+
DeprecatedGitHubWebhookNotification,
46+
]
3947

4048
def get_object_recipients(self, obj):
4149
for owner in obj.users.all():
@@ -119,7 +127,7 @@ class ProjectAdmin(GuardedModelAdmin):
119127
list_display = ('name', 'slug', 'repo', 'repo_type', 'featured')
120128
list_filter = ('repo_type', 'featured', 'privacy_level',
121129
'documentation_type', 'programming_language',
122-
ProjectOwnerBannedFilter)
130+
'feature__feature_id', ProjectOwnerBannedFilter)
123131
list_editable = ('featured',)
124132
search_fields = ('slug', 'repo')
125133
inlines = [ProjectRelationshipInline, RedirectInline,

readthedocs/projects/notifications.py

+30-58
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from __future__ import absolute_import
55
from datetime import timedelta
66
from django.utils import timezone
7+
from django.http import HttpRequest
78
from messages_extends.models import Message
89
from readthedocs.notifications import Notification
910
from readthedocs.notifications.constants import REQUIREMENT
@@ -17,67 +18,38 @@ class ResourceUsageNotification(Notification):
1718
level = REQUIREMENT
1819

1920

20-
class DeprecatedWebhookEndpointNotification(Notification):
21+
class DeprecatedViewNotification(Notification):
2122

22-
"""
23-
Notification for the usage of deprecated webhook endpoints.
23+
"""Notification to alert user of a view that is going away."""
2424

25-
Each time that a view decorated with ``notify_deprecated_endpoint`` is hit,
26-
a new instance of this class is created. Then, ``.send`` is called and the
27-
``SiteBackend`` will create (avoiding duplication) a site notification and
28-
the ``EmailBackend`` will do nothing (because of ``send_email=False``).
29-
30-
Besides, a ``message_extends.models.Message`` object is created to track
31-
sending an email if this endpoint is hit again after ``email_period``. When,
32-
``.send`` is call and the ``email_period`` was reach from the
33-
``Message.created`` time we mark ``send_email=True`` in this instance and
34-
call the super ``.send`` method that will effectively send the email and
35-
mark the message as ``extra_tags='email_sent'``.
36-
"""
37-
38-
name = 'deprecated_webhook_endpoint'
3925
context_object_name = 'project'
4026
subject = '{{ project.name }} project webhook needs to be updated'
41-
send_email = False
42-
email_period = timedelta(days=7)
4327
level = REQUIREMENT
4428

45-
def __init__(self, context_object, request, user=None):
46-
super(DeprecatedWebhookEndpointNotification, self).__init__(
47-
context_object,
48-
request,
49-
user,
50-
)
51-
self.message, created = self._create_message()
52-
53-
# Mark this notification to be sent as email the first time that it's
54-
# created (user hits this endpoint for the first time)
55-
if created:
56-
self.send_email = True
57-
58-
def _create_message(self):
59-
# Each time this class is instantiated we create a new Message (it's
60-
# de-duplicated by using the ``message``, ``user`` and ``extra_tags``
61-
# status)
62-
return Message.objects.get_or_create(
63-
message='{}: {}'.format(self.name, self.get_subject()),
64-
level=self.level,
65-
user=self.user,
66-
extra_tags='email_delayed',
67-
)
68-
69-
def send(self, *args, **kwargs): # noqa
70-
if self.message.created + self.email_period < timezone.now():
71-
# Mark this instance to really send the email and rely on the
72-
# EmailBackend to effectively send the email
73-
self.send_email = True
74-
75-
# Mark the message as sent and send the email
76-
self.message.extra_tags = 'email_sent'
77-
self.message.save()
78-
79-
# Create a new Message with ``email_delayed`` so we are prepared to
80-
# de-duplicate the following one
81-
self._create_message()
82-
83-
super(DeprecatedWebhookEndpointNotification, self).send(*args, **kwargs)
29+
@classmethod
30+
def notify_project_users(cls, projects):
31+
"""
32+
Notify project users of deprecated view.
33+
34+
:param projects: List of project instances
35+
:type projects: [:py:class:`Project`]
36+
"""
37+
for project in projects:
38+
# Send one notification to each owner of the project
39+
for user in project.users.all():
40+
notification = cls(
41+
context_object=project,
42+
request=HttpRequest(),
43+
user=user,
44+
)
45+
notification.send()
46+
47+
48+
class DeprecatedGitHubWebhookNotification(DeprecatedViewNotification):
49+
50+
name = 'deprecated_github_webhook'
51+
52+
53+
class DeprecatedBuildWebhookNotification(DeprecatedViewNotification):
54+
55+
name = 'deprecated_build_webhook'

0 commit comments

Comments
 (0)