-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
Notify users about the usage of deprecated webhooks #4898
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
0778294
ebf278b
ec54cfa
095607b
6123d45
ecb1f8d
8a6bb8a
fb4bd86
2e243c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# -*- coding: utf-8 -*- | ||
|
||
import logging | ||
|
||
from django.db.models import Q | ||
from django.http import HttpRequest | ||
|
||
from readthedocs.projects.models import Project | ||
from readthedocs.projects.notifications import DeprecatedWebhookEndpointNotification | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
def notify_deprecated_endpoint(function): | ||
""" | ||
Decorator to notify owners that the endpoint is DEPRECATED. | ||
|
||
.. note:: | ||
|
||
See the class ``DeprecatedWebhookEndpointNotification`` which contains | ||
all the logic to send notification for this messages properly without | ||
spamming. | ||
""" | ||
def wrap(*args, **kwargs): | ||
# Called from ``generic_build`` | ||
project_id_or_slug = kwargs.get('project_id_or_slug') | ||
|
||
if project_id_or_slug: | ||
projects = Project.objects.filter( | ||
Q(pk=project_id_or_slug) | Q(slug=project_id_or_slug), | ||
) | ||
else: | ||
# Called from ``_build_url`` | ||
projects = args[1] # ``projects`` argument | ||
|
||
if projects: | ||
for project in projects: | ||
# Send one notification to each owner of the project | ||
for user in project.users.all(): | ||
notification = DeprecatedWebhookEndpointNotification( | ||
project, | ||
HttpRequest(), | ||
user, | ||
) | ||
notification.send() | ||
else: | ||
log.info('Projects not found when hitting deprecated webhook') | ||
|
||
return function(*args, **kwargs) | ||
|
||
return wrap |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,10 @@ | ||
# -*- coding: utf-8 -*- | ||
"""Project notifications""" | ||
|
||
from __future__ import absolute_import | ||
from datetime import timedelta | ||
from django.utils import timezone | ||
from messages_extends.models import Message | ||
from readthedocs.notifications import Notification | ||
from readthedocs.notifications.constants import REQUIREMENT | ||
|
||
|
@@ -11,3 +15,69 @@ class ResourceUsageNotification(Notification): | |
context_object_name = 'project' | ||
subject = 'Builds for {{ project.name }} are using too many resources' | ||
level = REQUIREMENT | ||
|
||
|
||
class DeprecatedWebhookEndpointNotification(Notification): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This notification could be generalized in the future so other notifications can extend from the base class and use the same workflow for de-duplication and sending emails. |
||
|
||
""" | ||
Notification for the usage of deprecated webhook endpoints. | ||
|
||
Each time that a view decorated with ``notify_deprecated_endpoint`` is hit, | ||
a new instance of this class is created. Then, ``.send`` is called and the | ||
``SiteBackend`` will create (avoiding duplication) a site notification and | ||
the ``EmailBackend`` will do nothing (because of ``send_email=False``). | ||
|
||
Besides, a ``message_extends.models.Message`` object is created to track | ||
sending an email if this endpoint is hit again after ``email_period``. When, | ||
``.send`` is call and the ``email_period`` was reach from the | ||
``Message.created`` time we mark ``send_email=True`` in this instance and | ||
call the super ``.send`` method that will effectively send the email and | ||
mark the message as ``extra_tags='email_sent'``. | ||
""" | ||
|
||
name = 'deprecated_webhook_endpoint' | ||
context_object_name = 'project' | ||
subject = '{{ project.name }} project webhook needs to be updated' | ||
send_email = False | ||
email_period = timedelta(days=7) | ||
level = REQUIREMENT | ||
agjohnson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
def __init__(self, context_object, request, user=None): | ||
super(DeprecatedWebhookEndpointNotification, self).__init__( | ||
context_object, | ||
request, | ||
user, | ||
) | ||
self.message, created = self._create_message() | ||
|
||
# Mark this notification to be sent as email the first time that it's | ||
# created (user hits this endpoint for the first time) | ||
if created: | ||
self.send_email = True | ||
|
||
def _create_message(self): | ||
# Each time this class is instantiated we create a new Message (it's | ||
# de-duplicated by using the ``message``, ``user`` and ``extra_tags`` | ||
# status) | ||
return Message.objects.get_or_create( | ||
message='{}: {}'.format(self.name, self.get_subject()), | ||
level=self.level, | ||
user=self.user, | ||
extra_tags='email_delayed', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At this point, I think we want to check
to avoid showing the email notification as a site notification. |
||
) | ||
|
||
def send(self, *args, **kwargs): # noqa | ||
if self.message.created + self.email_period < timezone.now(): | ||
# Mark this instance to really send the email and rely on the | ||
# EmailBackend to effectively send the email | ||
self.send_email = True | ||
|
||
# Mark the message as sent and send the email | ||
self.message.extra_tags = 'email_sent' | ||
self.message.save() | ||
|
||
# Create a new Message with ``email_delayed`` so we are prepared to | ||
# de-duplicate the following one | ||
self._create_message() | ||
|
||
super(DeprecatedWebhookEndpointNotification, self).send(*args, **kwargs) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<p>Your project, {{ project.name }}, is currently using GitHub Services to trigger builds on Read the Docs. Effective January 31, 2019, GitHub will no longer process requests using the Services feature, and so Read the Docs will not receive notifications on updates to your repository.</p> | ||
|
||
<p>To continue building your Read the Docs project on changes to your repository, you will need to add a new webhook on your GitHub repository. You can either connect your GitHub account and configure a GitHub webhook integration, or you can add a generic webhook integration.</p> | ||
|
||
<p>You can find more information on our webhook intergrations in our documentation, at:</p> | ||
|
||
{% comment %}Plain text link because of text version of email{% endcomment %} | ||
<p><a href="https://docs.readthedocs.io/en/latest/webhooks.html">https://docs.readthedocs.io/en/latest/webhooks.html</a></p> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Your project, {{ project.name }}, needs to be reconfigured in order to continue building automatically after Jan 31st. For more information, <a href="https://docs.readthedocs.io/en/latest/webhooks.html#webhook-github-services">see our documentation on webhook integrations</a>. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,6 +19,8 @@ Mercurial==4.4.2 | |
yamale==1.8.0 | ||
pytest-mock==1.10.0 | ||
|
||
freezegun==0.3.11 | ||
|
||
# local debugging tools | ||
datadiff | ||
ipdb |
Uh oh!
There was an error while loading. Please reload this page.