diff --git a/app/__init__.py b/app/__init__.py index 376c976c5..5b37c57bb 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -58,6 +58,7 @@ from app.notify_client.org_invite_api_client import OrgInviteApiClient from app.notify_client.letter_jobs_client import LetterJobsClient from app.notify_client.inbound_number_client import InboundNumberClient from app.notify_client.billing_api_client import BillingAPIClient +from app.notify_client.complaint_api_client import ComplaintApiClient from app.commands import setup_commands from app.utils import get_cdn_domain from app.utils import gmt_timezones @@ -84,6 +85,7 @@ zendesk_client = ZendeskClient() letter_jobs_client = LetterJobsClient() inbound_number_client = InboundNumberClient() billing_api_client = BillingAPIClient() +complaint_api_client = ComplaintApiClient() # The current service attached to the request stack. current_service = LocalProxy(partial(_lookup_req_object, 'service')) @@ -128,6 +130,7 @@ def create_app(application): letter_jobs_client.init_app(application) inbound_number_client.init_app(application) billing_api_client.init_app(application) + complaint_api_client.init_app(application) login_manager.init_app(application) login_manager.login_view = 'main.sign_in' diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index 872a85095..6321d862d 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -4,7 +4,7 @@ from datetime import datetime from flask import render_template, request from flask_login import login_required -from app import service_api_client +from app import complaint_api_client, service_api_client from app.main import main from app.main.forms import DateFilterForm from app.statistics_utils import get_formatted_percentage @@ -70,6 +70,18 @@ def platform_admin_services(): ) +@main.route("/platform-admin/complaints") +@login_required +@user_is_platform_admin +def platform_admin_list_complaints(): + complaints = complaint_api_client.get_all_complaints() + return render_template( + 'views/platform-admin/complaints.html', + complaints=complaints, + page_title='All Complaints', + ) + + def sum_service_usage(service): total = 0 for notification_type in service['statistics'].keys(): diff --git a/app/navigation.py b/app/navigation.py index d724d2bfa..7f223265d 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -84,6 +84,7 @@ class HeaderNavigation(Navigation): 'update_email_branding', 'view_provider', 'view_providers', + 'platform_admin_list_complaints', }, 'sign-in': { 'sign_in', @@ -418,6 +419,7 @@ class MainNavigation(Navigation): 'organisation_settings', 'organisations', 'platform_admin', + 'platform_admin_list_complaints', 'pricing', 'privacy', 'public_agreement', @@ -586,6 +588,7 @@ class OrgNavigation(Navigation): 'old_using_notify', 'organisations', 'platform_admin', + 'platform_admin_list_complaints', 'pricing', 'privacy', 'public_agreement', diff --git a/app/notify_client/complaint_api_client.py b/app/notify_client/complaint_api_client.py new file mode 100644 index 000000000..6e657396c --- /dev/null +++ b/app/notify_client/complaint_api_client.py @@ -0,0 +1,11 @@ +from app.notify_client import NotifyAdminAPIClient + + +class ComplaintApiClient(NotifyAdminAPIClient): + # Fudge assert in the super __init__ so + # we can set those variables later. + def __init__(self): + super().__init__("a" * 73, "b") + + def get_all_complaints(self): + return self.get('/complaint') diff --git a/app/templates/views/platform-admin/_base_template.html b/app/templates/views/platform-admin/_base_template.html index 786a79dfb..4434534f0 100644 --- a/app/templates/views/platform-admin/_base_template.html +++ b/app/templates/views/platform-admin/_base_template.html @@ -19,7 +19,8 @@ ('Providers', url_for('main.view_providers')), ('Email branding', url_for('main.email_branding')), ('Letter jobs', url_for('main.letter_jobs')), - ('Inbound SMS numbers', url_for('main.inbound_sms_admin')) + ('Inbound SMS numbers', url_for('main.inbound_sms_admin')), + ('Email Complaints', url_for('main.platform_admin_list_complaints')) ] %}
  • diff --git a/app/templates/views/platform-admin/complaints.html b/app/templates/views/platform-admin/complaints.html new file mode 100644 index 000000000..ff7c90a4a --- /dev/null +++ b/app/templates/views/platform-admin/complaints.html @@ -0,0 +1,36 @@ +{% extends "views/platform-admin/_base_template.html" %} +{% from "components/page-footer.html" import page_footer %} +{% from "components/table.html" import list_table, field, text_field, link_field, right_aligned_field_heading, hidden_field_heading %} + +{% block per_page_title %} + {{ page_title|capitalize }} +{% endblock %} + +{% block platform_admin_content %} + +

    + Email complaints +

    + + + {% call(item, row_number) list_table( + complaints, + caption="Complaints", + caption_visible=False, + empty_message='No complaints', + field_headings=['Notification Id', 'Service', 'Complaint type', 'Complaint Date'], + field_headings_visible=True + ) %} + + {{ link_field(item.notification_id, url_for('main.view_notification', service_id=item.service_id, notification_id=item.notification_id)) }} + + {{ link_field(item.service_name, url_for('main.service_dashboard', service_id=item.service_id)) }} + + {{ text_field(item.complaint_type) }} + + {{ text_field(item.complaint_date|format_datetime_short) }} + + {% endcall %} + + +{% endblock %} diff --git a/tests/app/main/views/test_platform_admin.py b/tests/app/main/views/test_platform_admin.py index f475a1cac..3749a2a0f 100644 --- a/tests/app/main/views/test_platform_admin.py +++ b/tests/app/main/views/test_platform_admin.py @@ -1,4 +1,5 @@ import datetime +import uuid from unittest.mock import ANY import pytest @@ -637,3 +638,32 @@ def test_sum_service_usage_with_zeros(fake_uuid): sms_failed=0 ) assert sum_service_usage(service) == 0 + + +def test_platform_admin_list_complaints( + client, + platform_admin_user, + mocker +): + mock_get_user(mocker, user=platform_admin_user) + client.login(platform_admin_user) + complaint = { + 'id': str(uuid.uuid4()), + 'notification_id': str(uuid.uuid4()), + 'service_id': str(uuid.uuid4()), + 'service_name': 'Sample service', + 'ses_feedback_id': 'Some ses id', + 'complaint_type': 'abuse', + 'complaint_date': '2018-06-05T13:50:30.012354', + 'created_at': '2018-06-05T13:50:30.012354', + } + mock = mocker.patch('app.complaint_api_client.get_all_complaints', + return_value=[complaint]) + + client.login(platform_admin_user) + response = client.get(url_for('main.platform_admin_list_complaints')) + + assert response.status_code == 200 + resp_data = response.get_data(as_text=True) + assert 'Email complaints' in resp_data + mock.assert_called_once() diff --git a/tests/app/notify_client/test_compliant_client.py b/tests/app/notify_client/test_compliant_client.py new file mode 100644 index 000000000..9d05d1490 --- /dev/null +++ b/tests/app/notify_client/test_compliant_client.py @@ -0,0 +1,10 @@ +from app import ComplaintApiClient + + +def test_get_all_complaints(mocker): + client = ComplaintApiClient() + + mock = mocker.patch('app.notify_client.complaint_api_client.ComplaintApiClient.get') + + client.get_all_complaints() + mock.assert_called_once_with('/complaint')