diff --git a/app/main/forms.py b/app/main/forms.py index f483e042a7..464732cfe4 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -5,6 +5,7 @@ from functools import partial from itertools import chain from numbers import Number +from urllib.parse import urlparse import pytz from flask import request @@ -2280,6 +2281,50 @@ def validate(self, *args, **kwargs): return super().validate(*args, **kwargs) or self.url.data == "" +class UrlForm(StripWhitespaceForm): + url = GovukTextInputField( + "URL", + validators=[ + DataRequired(message="Cannot be empty"), + Regexp(regex="^https.*", message="Must be a valid https URL"), + ], + ) + + def validate(self, *args, **kwargs): + self.url.validators.append(self.check_url) + return super().validate(*args, **kwargs) + + def check_url(self, *args, **kwargs): + parsed_url = urlparse(self.url.data) + + if parsed_url.hostname == "docs.notifications.service.gov.uk" and parsed_url.fragment: + return parsed_url + else: + raise ValidationError("Must be a valid https URL, pointing to a section within the GOV.UK Notify API docs.") + + +class ChooseDocsForm(StripWhitespaceForm): + + def __init__(self, section_tag): + super().__init__(section_tag=section_tag) + + docs_version = GovukRadiosField( + "Which version of the docs would you like to view?", + choices=[ + ("python", "Python"), + ("ruby", "Ruby"), + ("java", "Java"), + ("node", "Node JS"), + ("net", ".Net"), + ("php", "PHP"), + ("rest-api", "Rest API"), + ("rest-api", "Any is fine"), + ], + thing="a language version of GOV.UK Notify's API docs", + ) + section_tag = HiddenField("section tag") + + class SMSPrefixForm(StripWhitespaceForm): enabled = OnOffField("") # label is assigned on instantiation diff --git a/app/main/views/index.py b/app/main/views/index.py index 4bfad425a5..13e5cf2b01 100644 --- a/app/main/views/index.py +++ b/app/main/views/index.py @@ -15,7 +15,7 @@ from app import status_api_client from app.formatters import message_count from app.main import main -from app.main.forms import FieldWithNoneOption +from app.main.forms import ChooseDocsForm, FieldWithNoneOption, UrlForm from app.main.views.sub_navigation_dictionaries import features_nav, using_notify_nav from app.models.branding import EmailBranding from app.models.letter_rates import LetterRates @@ -159,6 +159,39 @@ def guidance_api_documentation(): ) +@main.route("/using-notify/api-documentation/section", methods=["GET", "POST"]) +def guidance_api_documentation_section(): + + form = UrlForm() + + if form.validate_on_submit(): + section_tag = form.url.data.split("#")[-1] + return redirect(url_for(".guidance_api_documentation_section_choose_docs", section_tag=section_tag)) + + return render_template( + "views/guidance/using-notify/api-documentation-section.html", + navigation_links=using_notify_nav(), + form=form, + ) + + +@main.route("/using-notify/api-documentation/section/choose-docs", methods=["GET", "POST"]) +def guidance_api_documentation_section_choose_docs(): + form = ChooseDocsForm(section_tag=request.args.get("section_tag")) + + if form.validate_on_submit(): + redirect_url = ( + f"https://docs.notifications.service.gov.uk/{form.docs_version.data}.html#{form.section_tag.data}" + ) + return redirect(redirect_url) + + return render_template( + "views/guidance/using-notify/api-documentation-section-choose-docs.html", + navigation_links=using_notify_nav(), + form=form, + ) + + @main.route("/using-notify/attach-pages") def guidance_attach_pages(): return render_template( diff --git a/app/navigation.py b/app/navigation.py index 6e07885937..1114501bdd 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -59,6 +59,8 @@ class HeaderNavigation(Navigation): "using-notify": { "guidance_using_notify", "guidance_api_documentation", + "guidance_api_documentation_section", + "guidance_api_documentation_section_choose_docs", "guidance_attach_pages", "guidance_bulk_sending", "guidance_data_retention_period", diff --git a/app/templates/views/guidance/using-notify/api-documentation-section-choose-docs.html b/app/templates/views/guidance/using-notify/api-documentation-section-choose-docs.html new file mode 100644 index 0000000000..ed97bd22fc --- /dev/null +++ b/app/templates/views/guidance/using-notify/api-documentation-section-choose-docs.html @@ -0,0 +1,23 @@ +{% extends "content_template.html" %} +{% from "components/form.html" import form_wrapper %} +{% from "components/page-footer.html" import page_footer %} + +{# Used by the content_template.html layout, prefixes the "navigation" accessible name #} +{% set navigation_label_prefix = 'Using Notify' %} +{% set page_title = "You have been sent a link to GOV.UK Notify API documentation" %} + + +{% block per_page_title %} +{{page_title}} +{% endblock %} + +{% block content_column_content %} + +
Below, copy paste the full URL for docs section you would like to send to a service user, and click "Submit".
+ +You will be taken to +a landing page for that section. Send link to that page to your user, and they will be able to choose which language version they want to see.
+ +{% call form_wrapper() %} + {{ form.url }} + {{ page_footer('Submit') }} +{% endcall %} + +{% endblock %} diff --git a/app/templates/views/guidance/using-notify/api-documentation.html b/app/templates/views/guidance/using-notify/api-documentation.html index 764be8d091..33f1bc03b2 100644 --- a/app/templates/views/guidance/using-notify/api-documentation.html +++ b/app/templates/views/guidance/using-notify/api-documentation.html @@ -4,12 +4,22 @@ {% set navigation_label_prefix = 'Using Notify' %} {% block per_page_title %} - Documentation + API documentation {% endblock %} {% block content_column_content %} -This documentation is for developers who want to integrate the GOV.UK Notify API with a web application or back office system.
Links to documentation open in a new tab.
diff --git a/tests/app/main/views/test_index.py b/tests/app/main/views/test_index.py index e302b15dc1..2442d15a0d 100644 --- a/tests/app/main/views/test_index.py +++ b/tests/app/main/views/test_index.py @@ -384,3 +384,86 @@ def test_trial_mode_sending_limits(client_request): "send 50 text messages per day", "create letter templates, but not send them", ] + + +def test_guidance_api_documentation_links_to_section_flow_for_platform_admins(client_request, platform_admin_user): + client_request.login(platform_admin_user) + + page = client_request.get("main.guidance_api_documentation") + + assert len(page.select('a[href^="{link}"]'.format(link=url_for(".guidance_api_documentation_section")))) == 1 + + +def test_guidance_api_documentation_does_not_link_to_section_flow_for_non_platform_admins(client_request): + page = client_request.get("main.guidance_api_documentation") + + assert len(page.select('a[href^="{link}"]'.format(link=url_for(".guidance_api_documentation_section")))) == 0 + + +def test_GET_guidance_api_documentation_section(client_request): + page = client_request.get("main.guidance_api_documentation_section") + + assert page.select_one("h1").text == "Send a link to an API docs section" + assert page.select_one("input", attrs={"type": "text"})["name"] == "url" + + +def test_POST_guidance_api_documentation_section(client_request): + client_request.post( + "main.guidance_api_documentation_section", + _data={"url": "https://docs.notifications.service.gov.uk/python.html#send-a-file-by-email"}, + _expected_redirect=url_for( + "main.guidance_api_documentation_section_choose_docs", + section_tag="send-a-file-by-email", + ), + ) + + +@pytest.mark.parametrize( + "url, expected_error_message", + [ + ["", "Cannot be empty"], # empty string + [ + "https://docs.notifications.service.gov.uk/python.html", + "Must be a valid https URL, pointing to a section within the GOV.UK Notify API docs.", + ], # no section + [ + "https://docs.payments.service.gov.uk/making_payments/#creating-a-payment", + "Must be a valid https URL, pointing to a section within the GOV.UK Notify API docs.", + ], # URL is notfor Notify's docs + [ + "http://docs.notifications.service.gov.uk/python.html#send-a-file-by-email", + "Must be a valid https URL", + ], # http instead of https + ], +) +def test_POST_guidance_api_documentation_section_with_incorrect_url(client_request, url, expected_error_message): + page = client_request.post( + "main.guidance_api_documentation_section", + _data={"url": url}, + _expected_status=200, + ) + + assert expected_error_message in page.select_one(".govuk-error-message").text + + +def test_GET_guidance_api_documentation_section_choose_docs(client_request): + page = client_request.get("main.guidance_api_documentation_section_choose_docs", section_tag="send-a-file-by-email") + + assert page.select_one("h1").text == "You have been sent a link to GOV.UK Notify API documentation" + + assert ["python", "ruby", "java", "node", "net", "php", "rest-api", "rest-api"] == [ + radio["value"] for radio in page.select("input[type=radio]") + ] + form = page.select_one("form") + assert form["action"] == url_for( + "main.guidance_api_documentation_section_choose_docs", + section_tag="send-a-file-by-email", + ) + + +def test_POST_guidance_api_documentation_section_choose_docs(client_request): + client_request.post( + "main.guidance_api_documentation_section_choose_docs", + _data={"docs_version": "python", "section_tag": "send-a-file-by-email"}, + _expected_redirect="https://docs.notifications.service.gov.uk/python.html#send-a-file-by-email", + ) diff --git a/tests/app/test_navigation.py b/tests/app/test_navigation.py index 3927cb1010..7e0feef642 100644 --- a/tests/app/test_navigation.py +++ b/tests/app/test_navigation.py @@ -130,6 +130,8 @@ "go_to_dashboard_after_tour", "guest_list", "guidance_api_documentation", + "guidance_api_documentation_section", + "guidance_api_documentation_section_choose_docs", "guidance_attach_pages", "guidance_billing_details", "guidance_bulk_sending", diff --git a/tests/route-list.json b/tests/route-list.json index 7c72a22848..e9c41f9a2a 100644 --- a/tests/route-list.json +++ b/tests/route-list.json @@ -379,6 +379,8 @@ "/users/