diff --git a/setup.py b/setup.py index e5e6b92..bdf3a32 100644 --- a/setup.py +++ b/setup.py @@ -103,6 +103,7 @@ setup( 'django-haystack<2.8', 'django-reversion>=2.0', 'django-taggit', + 'djangorestframework>=3.3, <3.7', 'requests', 'whoosh', 'XStatic-Select2', diff --git a/tests/test_source_maarch.py b/tests/test_source_maarch.py index 2d2dcdf..d01c924 100644 --- a/tests/test_source_maarch.py +++ b/tests/test_source_maarch.py @@ -18,6 +18,8 @@ import json import pytest +from django.contrib.auth.models import User + from httmock import urlmatch, HTTMock @@ -153,7 +155,7 @@ def test_utils(maarch): PDF_MOCK = '%PDF-1.4 ...' -def test_feed(app, maarch, wcs, user): +def test_feed(settings, app, maarch, wcs, user): import base64 from django.core.management import call_command from django.contrib.contenttypes.models import ContentType @@ -241,6 +243,44 @@ def test_feed(app, maarch, wcs, user): ] } + # verify we can answer + maarch.clear() + app.set_user(None) + user = User.objects.create(username='test') + user.set_password('test') + user.save() + # verify authentication error + response = app.post_json('/api/mail/response/', params={}, status=401) + app.authorization = ('Basic', ('test', 'test')) + # verify serializer error + response = app.post_json('/api/mail/response/', params={}, status=400) + assert response.json['err'] == 1 + # verify error when maarch feed is not configured + settings.MAARCH_FEED['ENABLE'] = False + response = app.post_json('/api/mail/response/', + params={'mail_id': 'maarch-1', 'content': 'coucou'}, + status=200) + assert response.json['err'] == 1 + assert response.json['err_desc'] == 'maarch is unconfigured' + settings.MAARCH_FEED['ENABLE'] = True + # verify error when mail_id is unknown + response = app.post_json('/api/mail/response/', + params={'mail_id': 'maarch-231', 'content': 'coucou'}, + status=404) + assert response.json['err'] == 1 + + # successfull call + maarch.responses.append({}) + with maarch.ctx_manager: + response = app.post_json('/api/mail/response/', + params={'mail_id': 'maarch-1', 'content': 'coucou'}, + status=200) + assert maarch.requests[0][3] == { + 'historyMessage': 'coucou', + 'resId': [1], + 'status': 'GRC_RESPONSE', + } + def test_command_is_noop(): from django.core.management import call_command diff --git a/welco/settings.py b/welco/settings.py index aaa5f23..28d427a 100644 --- a/welco/settings.py +++ b/welco/settings.py @@ -51,6 +51,7 @@ INSTALLED_APPS = ( 'welco.contacts', 'gadjo', 'xstatic.pkg.select2', + 'rest_framework', ) MIDDLEWARE_CLASSES = ( @@ -227,6 +228,9 @@ PHONE_AUTOTAKE_MELLON_USERNAME = False # ex: FLAVOURS = ['alfortville'] FLAVOURS = [] +REST_FRAMEWORK = {} +REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] = ['rest_framework.authentication.BasicAuthentication'] + local_settings_file = os.environ.get('WELCO_SETTINGS_FILE', os.path.join(os.path.dirname(__file__), 'local_settings.py')) if os.path.exists(local_settings_file): diff --git a/welco/sources/mail/urls.py b/welco/sources/mail/urls.py index d37bb82..272c250 100644 --- a/welco/sources/mail/urls.py +++ b/welco/sources/mail/urls.py @@ -17,7 +17,7 @@ from django.conf.urls import url from .views import (viewer, feeder, qualification_save, edit_note, note, - reject, mail_count) + reject, mail_count, mail_response) urlpatterns = [ url('viewer/$', viewer, name='mail-viewer'), @@ -27,4 +27,5 @@ urlpatterns = [ url(r'^ajax/mail/edit-note/$', edit_note, name='mail-edit-note'), url(r'^ajax/mail/note/(?P\w+)$', note, name='mail-note'), url(r'^ajax/count/mail/$', mail_count, name='mail-count'), + url(r'^api/mail/response/$', mail_response, name='mail-api-response'), ] diff --git a/welco/sources/mail/utils.py b/welco/sources/mail/utils.py index 6c59add..babb71a 100644 --- a/welco/sources/mail/utils.py +++ b/welco/sources/mail/utils.py @@ -16,18 +16,19 @@ from django.conf import settings -from .maarch import MaarchCourrier +from .maarch import MaarchCourrier, MaarchError class WelcoMaarchCourrier(MaarchCourrier): def __init__(self, url, username, password, grc_status, grc_received_status, grc_send_status, grc_refused_status, - batch_size=10): + grc_response_status, batch_size=10): super(WelcoMaarchCourrier, self).__init__(url, username, password) self.grc_status = grc_status self.grc_received_status = grc_received_status self.grc_send_status = grc_send_status self.grc_refused_status = grc_refused_status + self.grc_response_status = grc_response_status self.batch_size = batch_size def get_mails(self): @@ -53,6 +54,10 @@ class WelcoMaarchCourrier(MaarchCourrier): mail = self.Courrier(self, pk=mail_pk) self.update_status([mail], self.grc_refused_status) + def set_grc_response_status(self, mail_pk, history_message): + mail = self.Courrier(self, pk=mail_pk) + self.update_status([mail], self.grc_response_status, history_message) + def get_maarch(): config = getattr(settings, 'MAARCH_FEED', {}) @@ -68,5 +73,6 @@ def get_maarch(): grc_status=config.get('STATUS_GRC', 'GRC'), grc_received_status=config.get('STATUS_RECEIVED', 'GRC_TRT'), grc_send_status=config.get('STATUS_SEND', 'GRCSENT'), - grc_refused_status=config.get('STATUS_REFUSED', 'GRCREFUSED')) + grc_refused_status=config.get('STATUS_REFUSED', 'GRCREFUSED'), + grc_response_status=config.get('STATUS_REFUSED', 'GRC_RESPONSE')) diff --git a/welco/sources/mail/views.py b/welco/sources/mail/views.py index d162019..8a7b1fb 100644 --- a/welco/sources/mail/views.py +++ b/welco/sources/mail/views.py @@ -29,11 +29,15 @@ from django.views.decorators.csrf import csrf_exempt from django.views.generic import TemplateView from django.db.transaction import atomic +from rest_framework import authentication, serializers, permissions, status +from rest_framework.generics import GenericAPIView +from rest_framework.response import Response + from welco.utils import response_for_json from .models import Mail from .forms import MailQualificationForm -from .utils import get_maarch +from .utils import get_maarch, MaarchError logger = logging.getLogger(__name__) @@ -149,3 +153,43 @@ def reject(request, *args, **kwargs): def mail_count(request, *args, **kwargs): count = Mail.objects.exclude(status__startswith='done-').count() return response_for_json(request, {'count': count}) + + +class NotificationSerializer(serializers.Serializer): + mail_id = serializers.CharField() + content = serializers.CharField() + + +class MailResponseAPIView(GenericAPIView): + permission_classes = (permissions.IsAuthenticated,) + serializer_class = NotificationSerializer + + def post(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + if not serializer.is_valid(): + response = {'err': 1, 'err_desc': serializer.errors} + return Response(response, status.HTTP_400_BAD_REQUEST) + mail_id = serializer.validated_data['mail_id'] + content = serializer.validated_data['content'] + # for now, we only support maarch + if not mail_id.startswith('maarch-'): + response = {'err': 1, 'err_desc': 'only maarch is supported'} + return Response(response) + mail = Mail.objects.filter(external_id=mail_id).first() + if not mail: + response = {'err': 1, 'err_desc': 'unknown mail_id'} + return Response(response, status.HTTP_404_NOT_FOUND) + return self.maarch_response(mail, content) + + def maarch_response(self, mail, content): + maarch = get_maarch() + if not maarch: + return Response({'err': 1, 'err_desc': 'maarch is unconfigured'}) + mail_pk = int(mail.external_id.split('-', 1)[1]) + try: + maarch.set_grc_response_status(mail_pk, content) + except MaarchError as e: + return Response({'err': 1, 'err_desc': str(e)}) + return Response({'err': 0}) + +mail_response = MailResponseAPIView.as_view()