diff --git a/app/main/__init__.py b/app/main/__init__.py index b15fcf945..f0bd8c4a8 100644 --- a/app/main/__init__.py +++ b/app/main/__init__.py @@ -12,6 +12,7 @@ from app.main.views import ( # noqa isort:skip dashboard, email_branding, feedback, + find_services, find_users, forgot_password, inbound_number, diff --git a/app/main/forms.py b/app/main/forms.py index 50f510d19..b33f3ef42 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -1127,7 +1127,10 @@ class RequiredDateFilterForm(StripWhitespaceForm): class SearchByNameForm(StripWhitespaceForm): - search = SearchField('Search by name') + search = SearchField( + 'Search by name', + validators=[DataRequired("You need to enter full or partial name to search by.")], + ) class SearchUsersByEmailForm(StripWhitespaceForm): diff --git a/app/main/views/find_services.py b/app/main/views/find_services.py new file mode 100644 index 000000000..21448ef47 --- /dev/null +++ b/app/main/views/find_services.py @@ -0,0 +1,20 @@ +from flask import render_template + +from app import service_api_client +from app.main import main +from app.main.forms import SearchByNameForm +from app.utils import user_is_platform_admin + + +@main.route("/find-services-by-name", methods=['GET', 'POST']) +@user_is_platform_admin +def find_services_by_name(): + form = SearchByNameForm() + services_found = None + if form.validate_on_submit(): + services_found = service_api_client.find_services_by_name(service_name=form.search.data)['data'] + return render_template( + 'views/find-services/find-services-by-name.html', + form=form, + services_found=services_found + ) diff --git a/app/main/views/find_users.py b/app/main/views/find_users.py index 11c1242f6..fa42b5a19 100644 --- a/app/main/views/find_users.py +++ b/app/main/views/find_users.py @@ -14,16 +14,13 @@ from app.utils import user_is_platform_admin def find_users_by_email(): form = SearchUsersByEmailForm() users_found = None - status = 200 if form.validate_on_submit(): users_found = user_api_client.find_users_by_full_or_partial_email(form.search.data)['data'] - elif request.method == 'POST': - status = 400 return render_template( 'views/find-users/find-users-by-email.html', form=form, users_found=users_found - ), status + ) @main.route("/users/", methods=['GET']) diff --git a/app/navigation.py b/app/navigation.py index cfcff353d..ed7d7c87f 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -85,6 +85,7 @@ class HeaderNavigation(Navigation): 'create_email_branding', 'create_letter_branding', 'email_branding', + 'find_services_by_name', 'find_users_by_email', 'letter_branding', 'live_services', @@ -487,6 +488,7 @@ class MainNavigation(Navigation): 'features_letters', 'features_sms', 'feedback', + 'find_services_by_name', 'find_users_by_email', 'forgot_password', 'get_example_csv', @@ -705,6 +707,7 @@ class CaseworkNavigation(Navigation): 'features_letters', 'features_sms', 'feedback', + 'find_services_by_name', 'find_users_by_email', 'forgot_password', 'get_example_csv', @@ -984,6 +987,7 @@ class OrgNavigation(Navigation): 'features_letters', 'features_sms', 'feedback', + 'find_services_by_name', 'find_users_by_email', 'forgot_password', 'get_example_csv', diff --git a/app/notify_client/service_api_client.py b/app/notify_client/service_api_client.py index 519e268d3..04448e67e 100644 --- a/app/notify_client/service_api_client.py +++ b/app/notify_client/service_api_client.py @@ -48,6 +48,9 @@ class ServiceAPIClient(NotifyAdminAPIClient): """ return self.get('/service', params=params_dict) + def find_services_by_name(self, service_name): + return self.get('/service/find-services-by-name', params={"service_name": service_name}) + def get_live_services_data(self, params_dict=None): """ Retrieve a list of live services data with contact names and notification counts. diff --git a/app/templates/views/find-services/find-services-by-name.html b/app/templates/views/find-services/find-services-by-name.html new file mode 100644 index 000000000..415c3ebb1 --- /dev/null +++ b/app/templates/views/find-services/find-services-by-name.html @@ -0,0 +1,51 @@ +{% extends "views/platform-admin/_base_template.html" %} +{% from "components/page-footer.html" import page_footer %} +{% from "components/form.html" import form_wrapper %} + +{% block per_page_title %} + Find services by name +{% endblock %} + +{% block platform_admin_content %} + +

+ Find services by name +

+ + + {% call form_wrapper( + action=url_for('.find_services_by_name'), + class='grid-row' + ) %} +
+ {{ textbox( + form.search, + width='1-1', + label='Find services by name, or by partial name' + ) }} +
+
+ + +
+ {% endcall %} + + {% call form_wrapper(id='search-form' ) %} + + {% endcall %} + + {% if services_found %} + + {% elif services_found == [] %} +

No services found.

+ {% endif %} +{% endblock %} diff --git a/app/templates/views/platform-admin/_base_template.html b/app/templates/views/platform-admin/_base_template.html index 23852fb38..a55e57d46 100644 --- a/app/templates/views/platform-admin/_base_template.html +++ b/app/templates/views/platform-admin/_base_template.html @@ -20,6 +20,7 @@ ('Email branding', url_for('main.email_branding')), ('Letter branding', url_for('main.letter_branding')), ('Inbound SMS numbers', url_for('main.inbound_sms_admin')), + ('Find services by name', url_for('main.find_services_by_name')), ('Find users by email', url_for('main.find_users_by_email')), ('Email Complaints', url_for('main.platform_admin_list_complaints')), ('Returned letters', url_for('main.platform_admin_returned_letters')), diff --git a/tests/app/main/views/test_find_services.py b/tests/app/main/views/test_find_services.py new file mode 100644 index 000000000..d6ffae576 --- /dev/null +++ b/tests/app/main/views/test_find_services.py @@ -0,0 +1,73 @@ +from tests import service_json + + +def test_find_services_by_name_page_loads_correctly(client_request, platform_admin_user): + client_request.login(platform_admin_user) + document = client_request.get('main.find_services_by_name') + + assert document.h1.text.strip() == 'Find services by name' + assert len(document.find_all('input', {'type': 'search'})) > 0 + + +def test_find_services_by_name_displays_services_found( + client_request, + platform_admin_user, + mocker +): + client_request.login(platform_admin_user) + get_services = mocker.patch( + 'app.service_api_client.find_services_by_name', + return_value={"data": [service_json()]} + ) + document = client_request.post( + 'main.find_services_by_name', + _data={"search": "Test Service"}, + _expected_status=200 + ) + get_services.assert_called_once_with(service_name="Test Service") + result = document.find('a', {'class': 'browse-list-link'}) + assert result.text.strip() == 'Test Service' + assert result.attrs["href"] == "/services/1234" + + +def test_find_services_by_name_displays_multiple_services( + client_request, + platform_admin_user, + mocker +): + client_request.login(platform_admin_user) + mocker.patch( + 'app.service_api_client.find_services_by_name', + return_value={"data": [service_json(name="Tadfield Police"), service_json(name="Tadfield Air Base")]} + ) + document = client_request.post('main.find_services_by_name', _data={"search": "Tadfield"}, _expected_status=200) + + results = document.findAll('a', {'class': 'browse-list-link'}) + assert len(results) == 2 + assert sorted([result.text.strip() for result in results]) == ["Tadfield Air Base", "Tadfield Police"] + + +def test_find_services_by_name_displays_message_if_no_services_found( + client_request, + platform_admin_user, + mocker +): + client_request.login(platform_admin_user) + mocker.patch('app.service_api_client.find_services_by_name', return_value={"data": []}) + document = client_request.post( + 'main.find_services_by_name', _data={"search": "Nabuchodonosorian Empire"}, _expected_status=200 + ) + + assert document.find('p', {'class': 'browse-list-hint'}).text.strip() == 'No services found.' + + +def test_find_services_by_name_validates_against_empty_search_submission( + client_request, + platform_admin_user, + mocker +): + client_request.login(platform_admin_user) + document = client_request.post('main.find_services_by_name', _data={"search": ""}, _expected_status=200) + + expected_message = "You need to enter full or partial name to search by." + assert document.find('span', {'class': 'error-message'}).text.strip() == expected_message diff --git a/tests/app/main/views/test_find_users.py b/tests/app/main/views/test_find_users.py index 345c10bef..5c64e8dd9 100644 --- a/tests/app/main/views/test_find_users.py +++ b/tests/app/main/views/test_find_users.py @@ -82,7 +82,7 @@ def test_find_users_by_email_validates_against_empty_search_submission( mocker ): client_request.login(platform_admin_user) - document = client_request.post('main.find_users_by_email', _data={"search": ""}, _expected_status=400) + document = client_request.post('main.find_users_by_email', _data={"search": ""}, _expected_status=200) expected_message = "You need to enter full or partial email address to search by." assert document.find('span', {'class': 'error-message'}).text.strip() == expected_message