Skip to content
This repository has been archived by the owner on May 12, 2022. It is now read-only.

Commit

Permalink
Merge pull request #190 from open-craft/paulo/purge-notifications
Browse files Browse the repository at this point in the history
Add API to purge notifications for specific users
  • Loading branch information
David Ormsbee authored Dec 12, 2019
2 parents c7ded99 + 7ab4307 commit 85a34a9
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 7 deletions.
22 changes: 22 additions & 0 deletions edx_notifications/lib/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
"""
All in-proc API endpoints for acting as a Notifications Admin
"""
from edx_notifications.stores.store import notification_store


def purge_user_data(user_ids):
"""
This will purge the notifications and preferences for the given user IDs
:param user_ids: and iterable of user IDs
"""
store = notification_store()
store.purge_notifications_for_users(user_ids)


def purge_notifications_with_payload(payload):
"""
This will purge the notifications and containing the given payload
:payload: string content contained in notifications payload
"""
store = notification_store()
store.purge_notifications_containing(payload)
25 changes: 25 additions & 0 deletions edx_notifications/server/api/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""
Administration endpoints
"""
from django.http import HttpResponseBadRequest, HttpResponse, HttpResponseForbidden

from edx_notifications.lib.admin import purge_user_data
from edx_notifications.server.api.api_utils import AuthenticatedAPIView


class DeleteUsersData(AuthenticatedAPIView):
"""
POST removes all data for given user IDs
"""

def post(self, request):
"""
HTTP POST Handler
"""
if 'user_ids' not in request.data:
return HttpResponseBadRequest('Missing user ids')
if not request.user.is_staff:
return HttpResponseForbidden()
user_ids = request.data.get('user_ids')
purge_user_data(user_ids)
return HttpResponse(status=204)
51 changes: 51 additions & 0 deletions edx_notifications/server/api/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
Tests for the administration endpoints
"""
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.test import TestCase

from edx_notifications.server.api.tests.utils import TestClient


class AdminAPITests(TestCase):
"""
Tests for the admin.py
"""

def setUp(self):
"""
Create clients
"""

self.admin_client = TestClient()
self.admin_user = User(username='admin', is_staff=True)
self.admin_user.save()
self.admin_client.login_user(self.admin_user)

self.client = TestClient()
self.user = User(username='user', is_staff=False)
self.user.save()
self.client.login_user(self.user)

def test_purge_user_data(self):
"""
Make sure purging user data works
"""

response = self.admin_client.post(
reverse('edx_notifications.admin.delete_users_data'),
{'user_ids': [self.admin_user.id]}
)
self.assertEqual(response.status_code, 204)

def test_admin_required_to_purge(self):
"""
Make sure purging is only available to staff users
"""

response = self.client.post(
reverse('edx_notifications.admin.delete_users_data'),
{'user_ids': [self.admin_user.id]}
)
self.assertEqual(response.status_code, 403)
2 changes: 2 additions & 0 deletions edx_notifications/server/api/url_regex.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@
CONSUMER_USER_PREFERENCES_DETAIL_REGEX = r'edx_notifications/v1/consumer/user_preferences/(?P<name>[0-9A-Za-z-]+)$'
CONSUMER_USER_PREFERENCES_DETAIL_NO_PARAM_REGEX = r'edx_notifications/v1/consumer/user_preferences/$'
CONSUMER_RENDERERS_TEMPLATES_REGEX = r'edx_notifications/v1/consumer/renderers/templates$'

ADMIN_USERS_DELETE = r'edx_notifications/v1/admin/user/delete$'
14 changes: 9 additions & 5 deletions edx_notifications/server/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@
All URL mappings for HTTP-based APIs
"""
from django.conf.urls import patterns, url

from rest_framework.urlpatterns import format_suffix_patterns

from edx_notifications.server.api import consumer as consumer_views

from edx_notifications.server.api import consumer as consumer_views, admin as admin_views
from .url_regex import (
CONSUMER_NOTIFICATIONS_COUNT_REGEX,
CONSUMER_NOTIFICATION_DETAIL_REGEX,
Expand All @@ -17,8 +15,9 @@
CONSUMER_USER_PREFERENCES_DETAIL_REGEX,
CONSUMER_NOTIFICATIONS_PREFERENCES_REGEX,
CONSUMER_USER_PREFERENCES_REGEX,
CONSUMER_USER_PREFERENCES_DETAIL_NO_PARAM_REGEX)

CONSUMER_USER_PREFERENCES_DETAIL_NO_PARAM_REGEX,
ADMIN_USERS_DELETE
)

urlpatterns = patterns( # pylint: disable=invalid-name
'',
Expand Down Expand Up @@ -72,6 +71,11 @@
consumer_views.UserPreferenceDetail.as_view(),
name='edx_notifications.consumer.user_preferences.detail.no_param'
),
url(
ADMIN_USERS_DELETE,
admin_views.DeleteUsersData.as_view(),
name='edx_notifications.admin.delete_users_data'
),

)

Expand Down
6 changes: 6 additions & 0 deletions edx_notifications/server/api/urls_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
CONSUMER_NOTIFICATIONS_PREFERENCES_REGEX,
CONSUMER_USER_PREFERENCES_REGEX,
CONSUMER_USER_PREFERENCES_DETAIL_NO_PARAM_REGEX,
ADMIN_USERS_DELETE
)


Expand Down Expand Up @@ -79,4 +80,9 @@ def mock_handler(request): # pylint: disable=unused-argument
mock_handler,
name='edx_notifications.consumer.user_preferences.detail.no_param'
),
url(
ADMIN_USERS_DELETE,
mock_handler,
name='edx_notifications.admin.delete_user_notifications'
),
)
22 changes: 21 additions & 1 deletion edx_notifications/stores/sql/store_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
SQLNotificationType,
SQLUserNotification,
SQLNotificationCallbackTimer,
SQLNotificationPreference, SQLUserNotificationPreferences)
SQLNotificationPreference,
SQLUserNotificationPreferences,
SQLUserNotificationArchive
)


class SQLNotificationStoreProvider(BaseNotificationStoreProvider):
Expand Down Expand Up @@ -554,3 +557,20 @@ def get_all_namespaces(self, start_datetime=None, end_datetime=None):
result_set = result_set.values_list('namespace', flat=True).order_by('namespace').distinct()

return result_set

def purge_notifications_for_users(self, user_ids):
"""
This will remove all notifications and preferences for given user IDs
"""
user_notifications = SQLUserNotification.objects.filter(user_id__in=user_ids)
archived_notifications = SQLUserNotificationArchive.objects.filter(user_id__in=user_ids)
SQLNotificationMessage.objects.filter(id__in=user_notifications.values('msg_id')).delete()
SQLNotificationMessage.objects.filter(id__in=archived_notifications.values('msg_id')).delete()
SQLUserNotificationArchive.objects.filter(user_id__in=user_ids).delete()
SQLUserNotificationPreferences.objects.filter(user_id__in=user_ids).delete()

def purge_notifications_containing(self, payload):
"""
This will remove all notifications which contains the given payload
"""
SQLNotificationMessage.objects.filter(payload__contains=payload).delete()
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def load_requirements(*requirements_paths):

setup(
name='edx-notifications',
version='0.8.3',
version='0.9.0',
description='Notification subsystem for Open edX',
long_description=open('README.md').read(),
author='edX',
Expand Down

0 comments on commit 85a34a9

Please sign in to comment.