From da536bbd2edfba03fc948139686f759891a96305 Mon Sep 17 00:00:00 2001 From: Nicholas Staples Date: Tue, 19 Apr 2016 13:51:16 +0100 Subject: [PATCH] Feedback page working with all tests passing. Updated to include team id. Give Feedback -> Give feedback --- app/main/forms.py | 7 ++ app/main/views/index.py | 39 +++++++++- app/templates/admin_template.html | 2 +- app/templates/views/feedback.html | 28 +++++++ config.py | 4 + environment_test.sh | 4 + tests/app/main/views/test_accept_invite.py | 2 +- tests/app/main/views/test_index.py | 87 +++++++++++++++++++++- 8 files changed, 169 insertions(+), 4 deletions(-) create mode 100644 app/templates/views/feedback.html diff --git a/app/main/forms.py b/app/main/forms.py index 04d9e4cc7..48fe081f5 100644 --- a/app/main/forms.py +++ b/app/main/forms.py @@ -306,3 +306,10 @@ class CreateKeyForm(Form): def validate_key_name(self, key_name): if key_name.data.lower() in self.existing_key_names: raise ValidationError('A key with this name already exists') + + +class Feedback(Form): + + name = StringField('Name') + email_address = StringField('Email address') + feedback = TextAreaField(u'', validators=[DataRequired(message="Can’t be empty")]) diff --git a/app/main/views/index.py b/app/main/views/index.py index d81bcdc23..a68eac7c2 100644 --- a/app/main/views/index.py +++ b/app/main/views/index.py @@ -1,8 +1,11 @@ import markdown import os -from flask import render_template, url_for, redirect, Markup +import requests +import json +from flask import (render_template, url_for, redirect, Markup, flash, current_app, abort) from app.main import main from flask_login import login_required +from app.main.forms import Feedback from flask.ext.login import current_user from mdx_gfm import GithubFlavoredMarkdownExtension @@ -41,6 +44,40 @@ def terms(): return render_template('views/terms-of-use.html') +@main.route('/feedback', methods=['GET', 'POST']) +def feedback(): + form = Feedback() + if form.validate_on_submit(): + data = { + 'person_email': current_app.config.get('DESKPRO_PERSON_EMAIL'), + 'agent_team_id': current_app.config.get('DESKPRO_TEAM_ID'), + 'subject': 'Notify feedback', + 'message': '{}\n{}\n{}'.format( + form.name.data, + form.email_address.data, + form.feedback.data) + } + headers = { + "X-DeskPRO-API-Key": current_app.config.get('DESKPRO_API_KEY'), + 'Content-Type': "application/x-www-form-urlencoded" + } + resp = requests.post( + current_app.config.get('DESKPRO_API_HOST') + '/api/tickets', + data=data, + headers=headers) + if resp.status_code != 201: + current_app.logger.error( + "Deskpro create ticket request failed with {} '{}'".format( + resp.status_code, + resp.json()) + ) + abort(500, "Feedback submission failed") + flash("Your feedback has been submitted") + return redirect(url_for('.feedback')) + + return render_template('views/feedback.html', form=form) + + @main.route('/documentation') def documentation(): curr_dir = os.path.dirname(os.path.realpath(__file__)) diff --git a/app/templates/admin_template.html b/app/templates/admin_template.html index 168525842..9c5461dca 100644 --- a/app/templates/admin_template.html +++ b/app/templates/admin_template.html @@ -78,7 +78,7 @@

Contact

diff --git a/app/templates/views/feedback.html b/app/templates/views/feedback.html new file mode 100644 index 000000000..3ee5f7401 --- /dev/null +++ b/app/templates/views/feedback.html @@ -0,0 +1,28 @@ +{% extends "withoutnav_template.html" %} +{% from "components/textbox.html" import textbox %} +{% from "components/page-footer.html" import page_footer %} + +{% block page_title %} + Feedback – GOV.UK Notify +{% endblock %} + +{% block maincolumn_content %} + +

+ Give feedback +

+
+
+

What went wrong, if anything? What went well? How could we improve this service?

+
+ {{ textbox(form.feedback, width='1-1', hint='', rows=10) }} +

Do you want a reply?

+

Leave your details below if you'd like a response.

+ {{ textbox(form.name, width='1-1') }} + {{ textbox(form.email_address, width='1-1') }} + {{ page_footer('Send') }} +
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/config.py b/config.py index b8971c7f8..ed8cbd7da 100644 --- a/config.py +++ b/config.py @@ -36,6 +36,10 @@ class Config(object): TOKEN_MAX_AGE_SECONDS = 3600 WTF_CSRF_ENABLED = True CSV_UPLOAD_BUCKET_NAME = 'local-notifications-csv-upload' + DESKPRO_API_HOST = os.environ['DESKPRO_API_HOST'] + DESKPRO_API_KEY = os.environ['DESKPRO_API_KEY'] + DESKPRO_PERSON_EMAIL = os.environ['DESKPRO_PERSON_EMAIL'] + DESKPRO_TEAM_ID = os.environ['DESKPRO_TEAM_ID'] EMAIL_DOMAIN_REGEXES = [ "gov\.uk", diff --git a/environment_test.sh b/environment_test.sh index 57f89ae58..e3f02a1a7 100644 --- a/environment_test.sh +++ b/environment_test.sh @@ -4,3 +4,7 @@ export ADMIN_CLIENT_USER_NAME='dev-notify-admin' export API_HOST_NAME='http://localhost:6311' export DANGEROUS_SALT='dev-notify-salt' export SECRET_KEY='dev-notify-secret-key' +export DESKPRO_API_HOST="" +export DESKPRO_API_KEY="" +export DESKPRO_PERSON_EMAIL="" +export DESKPRO_TEAM_ID="" diff --git a/tests/app/main/views/test_accept_invite.py b/tests/app/main/views/test_accept_invite.py index 7cfbd8142..7c45e5d5d 100644 --- a/tests/app/main/views/test_accept_invite.py +++ b/tests/app/main/views/test_accept_invite.py @@ -1,6 +1,7 @@ from flask import url_for from bs4 import BeautifulSoup +from unittest.mock import ANY import app @@ -255,7 +256,6 @@ def test_new_user_accept_invite_completes_new_registration_redirects_to_verify(a assert response.status_code == 302 assert response.location == expected_redirect_location - from unittest.mock import ANY mock_send_verify_code.assert_called_once_with(ANY, 'sms', data['mobile_number']) mock_register_user.assert_called_with(data['name'], diff --git a/tests/app/main/views/test_index.py b/tests/app/main/views/test_index.py index 3f25ae0c9..eee655ecf 100644 --- a/tests/app/main/views/test_index.py +++ b/tests/app/main/views/test_index.py @@ -1,4 +1,7 @@ -from flask import url_for +import pytest +from flask import (url_for, current_app) +from werkzeug.exceptions import InternalServerError +from unittest.mock import Mock, ANY def test_logged_in_user_redirects_to_choose_service(app_, @@ -14,3 +17,85 @@ def test_logged_in_user_redirects_to_choose_service(app_, response = client.get(url_for('main.sign_in', follow_redirects=True)) assert response.location == url_for('main.choose_service', _external=True) + + +def test_get_feedback_page(app_): + with app_.test_request_context(): + with app_.test_client() as client: + resp = client.get(url_for('main.feedback')) + assert resp.status_code == 200 + + +def test_post_feedback_with_no_name_email(app_, mocker): + mock_post = mocker.patch( + 'app.main.views.index.requests.post', + return_value=Mock(status_code=201)) + with app_.test_request_context(): + with app_.test_client() as client: + resp = client.post(url_for('main.feedback'), data={'feedback': "blah"}) + assert resp.status_code == 302 + + +def test_post_feedback_with_no_name_email(app_, mocker): + mock_post = mocker.patch( + 'app.main.views.index.requests.post', + return_value=Mock(status_code=201)) + with app_.test_request_context(): + with app_.test_client() as client: + resp = client.post(url_for('main.feedback'), data={'feedback': "blah"}) + assert resp.status_code == 302 + mock_post.assert_called_with( + ANY, + data={ + 'agent_team_id': ANY, + 'subject': 'Notify feedback', + 'message': '\n\nblah', + 'person_email': ANY}, + headers=ANY) + + +def test_post_feedback_with_name_email(app_, mocker): + mock_post = mocker.patch( + 'app.main.views.index.requests.post', + return_value=Mock(status_code=201)) + with app_.test_request_context(): + with app_.test_client() as client: + resp = client.post( + url_for('main.feedback'), + data={'feedback': "blah", 'name': "Steve Irwin", 'email_address': 'rip@gmail.com'}) + assert resp.status_code == 302 + mock_post.assert_called_with( + ANY, + data={ + 'subject': 'Notify feedback', + 'agent_team_id': ANY, + 'message': 'Steve Irwin\nrip@gmail.com\nblah', + 'person_email': ANY}, + headers=ANY) + + +def test_log_error_on_post(app_, mocker): + mock_post = mocker.patch( + 'app.main.views.index.requests.post', + return_value=Mock( + status_code=401, + json=lambda: { + 'error_code': 'invalid_auth', + 'error_message': 'Please provide a valid API key or token'})) + with app_.test_request_context(): + mock_logger = mocker.patch.object(app_.logger, 'error') + with app_.test_client() as client: + with pytest.raises(InternalServerError): + resp = client.post( + url_for('main.feedback'), + data={'feedback': "blah", 'name': "Steve Irwin", 'email_address': 'rip@gmail.com'}) + mock_post.assert_called_with( + ANY, + data={ + 'subject': 'Notify feedback', + 'agent_team_id': ANY, + 'message': 'Steve Irwin\nrip@gmail.com\nblah', + 'person_email': ANY}, + headers=ANY) + mock_logger.assert_called_with( + "Deskpro create ticket request failed with {} '{}'".format(mock_post().status_code, mock_post().json()))