mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-07 10:58:25 -04:00
Merge pull request #1297 from alphagov/search-outbound
Add search box to filter notifications by recipient
This commit is contained in:
@@ -59,3 +59,21 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.align-button-with-textbox {
|
||||||
|
|
||||||
|
.button {
|
||||||
|
|
||||||
|
@include media(desktop) {
|
||||||
|
position: relative;
|
||||||
|
top: 32px;
|
||||||
|
left: -30px;
|
||||||
|
width: 100%;
|
||||||
|
margin-right: -30px;
|
||||||
|
padding-top: 8px;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -641,6 +641,11 @@ class SearchTemplatesForm(Form):
|
|||||||
search = SearchField('Search by name')
|
search = SearchField('Search by name')
|
||||||
|
|
||||||
|
|
||||||
|
class SearchNotificationsForm(Form):
|
||||||
|
|
||||||
|
to = SearchField('Search by phone number or email address')
|
||||||
|
|
||||||
|
|
||||||
class PlaceholderForm(Form):
|
class PlaceholderForm(Form):
|
||||||
|
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ from app import (
|
|||||||
current_service,
|
current_service,
|
||||||
format_datetime_short)
|
format_datetime_short)
|
||||||
from app.main import main
|
from app.main import main
|
||||||
|
from app.main.forms import SearchNotificationsForm
|
||||||
from app.utils import (
|
from app.utils import (
|
||||||
get_page_from_request,
|
get_page_from_request,
|
||||||
generate_next_dict,
|
generate_next_dict,
|
||||||
@@ -197,8 +198,10 @@ def view_notifications(service_id, message_type):
|
|||||||
'views/notifications.html',
|
'views/notifications.html',
|
||||||
partials=get_notifications(service_id, message_type),
|
partials=get_notifications(service_id, message_type),
|
||||||
message_type=message_type,
|
message_type=message_type,
|
||||||
status=request.args.get('status'),
|
status=request.args.get('status') or 'sending,delivered,failed',
|
||||||
page=request.args.get('page', 1)
|
page=request.args.get('page', 1),
|
||||||
|
to=request.args.get('to'),
|
||||||
|
search_form=SearchNotificationsForm(to=request.args.get('to')),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -241,7 +244,9 @@ def get_notifications(service_id, message_type, status_override=None):
|
|||||||
page=page,
|
page=page,
|
||||||
template_type=[message_type],
|
template_type=[message_type],
|
||||||
status=filter_args.get('status'),
|
status=filter_args.get('status'),
|
||||||
limit_days=current_app.config['ACTIVITY_STATS_LIMIT_DAYS'])
|
limit_days=current_app.config['ACTIVITY_STATS_LIMIT_DAYS'],
|
||||||
|
to=request.args.get('to'),
|
||||||
|
)
|
||||||
|
|
||||||
url_args = {
|
url_args = {
|
||||||
'message_type': message_type,
|
'message_type': message_type,
|
||||||
@@ -249,11 +254,11 @@ def get_notifications(service_id, message_type, status_override=None):
|
|||||||
}
|
}
|
||||||
prev_page = None
|
prev_page = None
|
||||||
|
|
||||||
if notifications['links'].get('prev', None):
|
if 'links' in notifications and notifications['links'].get('prev', None):
|
||||||
prev_page = generate_previous_dict('main.view_notifications', service_id, page, url_args=url_args)
|
prev_page = generate_previous_dict('main.view_notifications', service_id, page, url_args=url_args)
|
||||||
next_page = None
|
next_page = None
|
||||||
|
|
||||||
if notifications['links'].get('next', None):
|
if 'links' in notifications and notifications['links'].get('next', None):
|
||||||
next_page = generate_next_dict('main.view_notifications', service_id, page, url_args)
|
next_page = generate_next_dict('main.view_notifications', service_id, page, url_args)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ class NotificationApiClient(NotifyAdminAPIClient):
|
|||||||
limit_days=None,
|
limit_days=None,
|
||||||
include_jobs=None,
|
include_jobs=None,
|
||||||
include_from_test_key=None,
|
include_from_test_key=None,
|
||||||
format_for_csv=None
|
format_for_csv=None,
|
||||||
|
to=None,
|
||||||
):
|
):
|
||||||
params = {}
|
params = {}
|
||||||
if page is not None:
|
if page is not None:
|
||||||
@@ -38,6 +39,8 @@ class NotificationApiClient(NotifyAdminAPIClient):
|
|||||||
params['include_from_test_key'] = include_from_test_key
|
params['include_from_test_key'] = include_from_test_key
|
||||||
if format_for_csv is not None:
|
if format_for_csv is not None:
|
||||||
params['format_for_csv'] = format_for_csv
|
params['format_for_csv'] = format_for_csv
|
||||||
|
if to is not None:
|
||||||
|
params['to'] = to
|
||||||
if job_id:
|
if job_id:
|
||||||
return self.get(
|
return self.get(
|
||||||
url='/service/{}/job/{}/notifications'.format(service_id, job_id),
|
url='/service/{}/job/{}/notifications'.format(service_id, job_id),
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{% extends "withnav_template.html" %}
|
{% extends "withnav_template.html" %}
|
||||||
{% from "components/ajax-block.html" import ajax_block %}
|
{% from "components/ajax-block.html" import ajax_block %}
|
||||||
{% from "components/message-count-label.html" import message_count_label, recipient_count_label %}
|
{% from "components/message-count-label.html" import message_count_label, recipient_count_label %}
|
||||||
|
{% from "components/page-footer.html" import page_footer %}
|
||||||
|
{% from "components/textbox.html" import textbox %}
|
||||||
|
|
||||||
{% block service_page_title %}
|
{% block service_page_title %}
|
||||||
{{ message_count_label(99, message_type, suffix='') | capitalize }}
|
{{ message_count_label(99, message_type, suffix='') | capitalize }}
|
||||||
@@ -18,9 +20,27 @@
|
|||||||
'counts'
|
'counts'
|
||||||
) }}
|
) }}
|
||||||
|
|
||||||
|
<form
|
||||||
|
method="get"
|
||||||
|
action="{{ url_for('.view_notifications', service_id=current_service.id, message_type=message_type) }}"
|
||||||
|
class="grid-row"
|
||||||
|
>
|
||||||
|
<div class="column-three-quarters">
|
||||||
|
<input type="hidden" name="status" value="{{ status }}">
|
||||||
|
{{ textbox(
|
||||||
|
search_form.to,
|
||||||
|
width='1-1',
|
||||||
|
label='Search by {}'.format('email address' if message_type == 'email' else 'phone number')
|
||||||
|
) }}
|
||||||
|
</div>
|
||||||
|
<div class="column-one-quarter align-button-with-textbox">
|
||||||
|
<input type="submit" class="button" value="Search">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
{{ ajax_block(
|
{{ ajax_block(
|
||||||
partials,
|
partials,
|
||||||
url_for('.get_notifications_as_json', service_id=current_service.id, message_type=message_type, status=status, page=page),
|
url_for('.get_notifications_as_json', service_id=current_service.id, message_type=message_type, status=status, page=page, to=to),
|
||||||
'notifications'
|
'notifications'
|
||||||
) }}
|
) }}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ from bs4 import BeautifulSoup
|
|||||||
|
|
||||||
from app.main.views.jobs import get_time_left, get_status_filters
|
from app.main.views.jobs import get_time_left, get_status_filters
|
||||||
from tests import notification_json
|
from tests import notification_json
|
||||||
|
from tests.conftest import SERVICE_ONE_ID
|
||||||
from freezegun import freeze_time
|
from freezegun import freeze_time
|
||||||
|
|
||||||
|
|
||||||
@@ -311,6 +312,13 @@ def test_should_show_updates_for_one_job_as_json(
|
|||||||
(None, 1)
|
(None, 1)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
"to_argument, expected_to_argument", [
|
||||||
|
('', ''),
|
||||||
|
('+447900900123', '+447900900123'),
|
||||||
|
('test@example.com', 'test@example.com'),
|
||||||
|
]
|
||||||
|
)
|
||||||
def test_can_show_notifications(
|
def test_can_show_notifications(
|
||||||
logged_in_client,
|
logged_in_client,
|
||||||
service_one,
|
service_one,
|
||||||
@@ -322,13 +330,17 @@ def test_can_show_notifications(
|
|||||||
expected_api_call,
|
expected_api_call,
|
||||||
page_argument,
|
page_argument,
|
||||||
expected_page_argument,
|
expected_page_argument,
|
||||||
|
to_argument,
|
||||||
|
expected_to_argument,
|
||||||
):
|
):
|
||||||
response = logged_in_client.get(url_for(
|
response = logged_in_client.get(url_for(
|
||||||
'main.view_notifications',
|
'main.view_notifications',
|
||||||
service_id=service_one['id'],
|
service_id=service_one['id'],
|
||||||
message_type=message_type,
|
message_type=message_type,
|
||||||
status=status_argument,
|
status=status_argument,
|
||||||
page=page_argument))
|
page=page_argument,
|
||||||
|
to=to_argument,
|
||||||
|
))
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
content = response.get_data(as_text=True)
|
content = response.get_data(as_text=True)
|
||||||
notifications = notification_json(service_one['id'])
|
notifications = notification_json(service_one['id'])
|
||||||
@@ -348,13 +360,16 @@ def test_can_show_notifications(
|
|||||||
assert query_dict['status'] == [status_argument]
|
assert query_dict['status'] == [status_argument]
|
||||||
if expected_page_argument:
|
if expected_page_argument:
|
||||||
assert query_dict['page'] == [str(expected_page_argument)]
|
assert query_dict['page'] == [str(expected_page_argument)]
|
||||||
|
if to_argument:
|
||||||
|
assert query_dict['to'] == [to_argument]
|
||||||
|
|
||||||
mock_get_notifications.assert_called_with(
|
mock_get_notifications.assert_called_with(
|
||||||
limit_days=7,
|
limit_days=7,
|
||||||
page=expected_page_argument,
|
page=expected_page_argument,
|
||||||
service_id=service_one['id'],
|
service_id=service_one['id'],
|
||||||
status=expected_api_call,
|
status=expected_api_call,
|
||||||
template_type=[message_type]
|
template_type=[message_type],
|
||||||
|
to=expected_to_argument,
|
||||||
)
|
)
|
||||||
|
|
||||||
json_response = logged_in_client.get(url_for(
|
json_response = logged_in_client.get(url_for(
|
||||||
@@ -367,6 +382,62 @@ def test_can_show_notifications(
|
|||||||
assert json_content.keys() == {'counts', 'notifications'}
|
assert json_content.keys() == {'counts', 'notifications'}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("initial_query_arguments, expected_status_field_value, expected_search_box_contents", [
|
||||||
|
(
|
||||||
|
{
|
||||||
|
'message_type': 'sms',
|
||||||
|
},
|
||||||
|
'sending,delivered,failed',
|
||||||
|
'',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
'message_type': 'sms',
|
||||||
|
'to': '+33(0)5-12-34-56-78',
|
||||||
|
},
|
||||||
|
'sending,delivered,failed',
|
||||||
|
'+33(0)5-12-34-56-78',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
{
|
||||||
|
'status': 'failed',
|
||||||
|
'message_type': 'email',
|
||||||
|
'page': '99',
|
||||||
|
'to': 'test@example.com',
|
||||||
|
},
|
||||||
|
'failed',
|
||||||
|
'test@example.com',
|
||||||
|
),
|
||||||
|
])
|
||||||
|
def test_search_recipient_form(
|
||||||
|
logged_in_client,
|
||||||
|
mock_get_notifications,
|
||||||
|
mock_get_detailed_service,
|
||||||
|
initial_query_arguments,
|
||||||
|
expected_status_field_value,
|
||||||
|
expected_search_box_contents,
|
||||||
|
):
|
||||||
|
response = logged_in_client.get(url_for(
|
||||||
|
'main.view_notifications',
|
||||||
|
service_id=SERVICE_ONE_ID,
|
||||||
|
**initial_query_arguments
|
||||||
|
))
|
||||||
|
assert response.status_code == 200
|
||||||
|
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||||
|
|
||||||
|
action_url = page.find("form")['action']
|
||||||
|
url = urlparse(action_url)
|
||||||
|
assert url.path == '/services/{}/notifications/{}'.format(
|
||||||
|
SERVICE_ONE_ID,
|
||||||
|
initial_query_arguments['message_type']
|
||||||
|
)
|
||||||
|
query_dict = parse_qs(url.query)
|
||||||
|
assert query_dict == {}
|
||||||
|
|
||||||
|
assert page.find("input", {'name': 'status'})['value'] == expected_status_field_value
|
||||||
|
assert page.find("input", {'name': 'to'})['value'] == expected_search_box_contents
|
||||||
|
|
||||||
|
|
||||||
def test_should_show_notifications_for_a_service_with_next_previous(
|
def test_should_show_notifications_for_a_service_with_next_previous(
|
||||||
logged_in_client,
|
logged_in_client,
|
||||||
service_one,
|
service_one,
|
||||||
|
|||||||
@@ -1069,6 +1069,7 @@ def mock_get_notifications(mocker, api_user_active):
|
|||||||
rows=5,
|
rows=5,
|
||||||
include_jobs=None,
|
include_jobs=None,
|
||||||
include_from_test_key=None,
|
include_from_test_key=None,
|
||||||
|
to=None,
|
||||||
):
|
):
|
||||||
job = None
|
job = None
|
||||||
if job_id is not None:
|
if job_id is not None:
|
||||||
@@ -1101,7 +1102,9 @@ def mock_get_notifications_with_previous_next(mocker):
|
|||||||
status=None,
|
status=None,
|
||||||
limit_days=None,
|
limit_days=None,
|
||||||
include_jobs=None,
|
include_jobs=None,
|
||||||
include_from_test_key=None):
|
include_from_test_key=None,
|
||||||
|
to=None,
|
||||||
|
):
|
||||||
return notification_json(service_id, with_links=True)
|
return notification_json(service_id, with_links=True)
|
||||||
|
|
||||||
return mocker.patch(
|
return mocker.patch(
|
||||||
|
|||||||
Reference in New Issue
Block a user