Merge branch 'master' into email-registered-users

This commit is contained in:
Rebecca Law
2016-07-12 11:05:16 +01:00
23 changed files with 405 additions and 141 deletions

View File

@@ -12,7 +12,9 @@ from flask import (
make_response,
current_app,
request,
g)
g,
url_for
)
from flask._compat import string_types
from flask.globals import _lookup_req_object
from flask_login import LoginManager
@@ -112,6 +114,7 @@ def create_app():
application.add_template_filter(format_date_short)
application.add_template_filter(format_notification_status)
application.add_template_filter(format_notification_status_as_field_status)
application.add_template_filter(format_notification_status_as_url)
application.after_request(useful_headers_after_request)
application.after_request(save_service_after_request)
@@ -235,16 +238,16 @@ def format_notification_status(status, template_type):
'email': {
'failed': 'Failed',
'technical-failure': 'Technical failure',
'temporary-failure': 'Temporary failure',
'permanent-failure': 'Email address does not exist',
'temporary-failure': 'Inbox not accepting messages right now',
'permanent-failure': 'Email address doesnt exist',
'delivered': 'Delivered',
'sending': 'Sending'
},
'sms': {
'failed': 'Failed',
'technical-failure': 'Technical failure',
'temporary-failure': 'Temporary failure',
'permanent-failure': 'Phone number does not exist',
'temporary-failure': 'Phone not accepting messages right now',
'permanent-failure': 'Phone number doesnt exist',
'delivered': 'Delivered',
'sending': 'Sending'
}
@@ -262,6 +265,15 @@ def format_notification_status_as_field_status(status):
}.get(status, 'error')
def format_notification_status_as_url(status):
url = partial(url_for, "main.delivery_and_failure")
return {
'technical-failure': url(_anchor='technical-failure'),
'temporary-failure': url(_anchor='not-accepting-messages'),
'permanent-failure': url(_anchor='does-not-exist')
}.get(status)
@login_manager.user_loader
def load_user(user_id):
return user_api_client.get_user(user_id)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,46 @@
%tick-cross {
@include core-16;
display: inline-block;
background-size: 19px 19px;
background-repeat: no-repeat;
background-position: 0 0;
padding: 1px 0 0 25px;
}
.tick-cross {
&-tick {
@extend %tick-cross;
background-image: file-url('tick.png');
}
&-cross {
@extend %tick-cross;
background-image: file-url('cross-grey.png');
color: $secondary-text-colour;
}
&-list {
@extend %grid-row;
margin-top: 5px;
&-permissions {
@include grid-column(3/4);
li {
display: inline-block;
margin-right: 0.5em;
}
}
&-edit-link {
@include grid-column(1/4);
text-align: right;
}
}
}

View File

@@ -54,11 +54,13 @@ $path: '/static/images/';
@import 'components/message';
@import 'components/phone';
@import 'components/research-mode';
@import 'components/tick-cross';
@import 'views/job';
@import 'views/edit-template';
@import 'views/documenation';
@import 'views/dashboard';
@import 'views/users';
// TODO: break this up
@import 'app';

View File

@@ -0,0 +1,16 @@
.user-list {
@include core-16;
&-item {
padding: $gutter-half 0;
border-top: 1px solid $border-colour;
&:last-child {
border-bottom: 1px solid $border-colour;
}
}
}

View File

@@ -28,6 +28,7 @@ from app.utils import (
user_has_permissions,
generate_notifications_csv)
from app.statistics_utils import sum_of_statistics, statistics_by_state, add_rate_to_jobs
from app.utils import get_help_argument
def _parse_filter_args(filter_dict):
@@ -98,7 +99,8 @@ def view_job(service_id, job_id):
job_id=job['id'],
status=request.args.get('status', '')
),
partials=get_job_partials(job)
partials=get_job_partials(job),
help=get_help_argument()
)
@@ -311,7 +313,8 @@ def get_job_partials(job):
notifications=notification_api_client.get_notifications_for_service(
job['service'], job['id'], status=filter_args.get('status')
)['notifications'],
status=request.args.get('status', '')
status=request.args.get('status', ''),
help=get_help_argument()
),
'status': render_template(
'partials/jobs/status.html',

View File

@@ -29,7 +29,7 @@ from app.main.uploader import (
s3download
)
from app import job_api_client, service_api_client, current_service, user_api_client, statistics_api_client
from app.utils import user_has_permissions, get_errors_for_csv, Spreadsheet
from app.utils import user_has_permissions, get_errors_for_csv, Spreadsheet, get_help_argument
def get_page_headings(template_type):
@@ -181,7 +181,8 @@ def send_test(service_id, template_id):
'views/send-test.html',
template=template,
recipient_column=first_column_heading[template.template_type],
example=[get_example_csv_rows(template, use_example_as_example=False)]
example=[get_example_csv_rows(template, use_example_as_example=False)],
help=get_help_argument()
)
@@ -278,7 +279,8 @@ def check_messages(service_id, template_type, upload_id):
upload_id=upload_id,
form=CsvUploadForm(),
statistics=statistics,
back_link=back_link
back_link=back_link,
help=get_help_argument()
)

View File

@@ -0,0 +1,15 @@
{% macro tick_cross(yes, label) %}
<li>
{% if yes %}
<span class="tick-cross-tick">
<span class="visually-hidden">Can</span>
{{ label}}
</span>
{% else %}
<span class="tick-cross-cross">
<span class="visually-hidden">Cant</span>
{{ label}}
</span>
{% endif %}
</li>
{% endmacro %}

View File

@@ -1,9 +1,9 @@
{% from "components/banner.html" import banner_wrapper %}
{% if request.args['help'] and request.args['help'] != '0' %}
{% if help %}
{% call banner_wrapper(type='tour') %}
<p class="heading-medium">Try this example</p>
<div class="grid-row bottom-gutter" {% if request.args['help'] != '1' %}style="opacity: 0.6"{% endif %}>
<div class="grid-row bottom-gutter" {% if help != '1' %}style="opacity: 0.6"{% endif %}>
<div class="column-one-sixth">
<p class="heading-large" style="float: left;">1.</p>
</div>
@@ -13,7 +13,7 @@
</p>
</div>
</div>
<div class="grid-row bottom-gutter" {% if request.args['help'] != '2' %}style="opacity: 0.6"{% endif %}>
<div class="grid-row bottom-gutter" {% if help != '2' %}style="opacity: 0.6"{% endif %}>
<div class="column-one-sixth">
<p class="heading-large">2.</p>
</div>
@@ -23,7 +23,7 @@
</p>
</div>
</div>
<div class="grid-row bottom-gutter" {% if request.args['help'] != '3' %}style="opacity: 0.6"{% endif %}>
<div class="grid-row bottom-gutter" {% if help != '3' %}style="opacity: 0.6"{% endif %}>
<div class="column-one-sixth">
<p class="heading-large">3.</p>
</div>
@@ -31,7 +31,7 @@
<p>
Notify delivers the message
</p>
{% if request.args['help'] == '3' %}
{% if help == '3' %}
<a href='{{ url_for(".go_to_dashboard_after_tour", service_id=current_service.id, example_template_id=template.id) }}'>
Now go to your dashboard
</a>

View File

@@ -4,7 +4,7 @@
<div class="dashboard-table">
{% endif %}
{% if notifications %}
{% if notifications and not help %}
<p class="bottom-gutter">
<a href="{{ url_for('.view_job_csv', service_id=current_service.id, job_id=job.id, status=status) }}" download="download" class="heading-small">Download as a CSV file</a>
&emsp;
@@ -34,7 +34,13 @@
align='right',
status=item.status|format_notification_status_as_field_status
) %}
{% if item.status|format_notification_status_as_url %}
<a href="{{ item.status|format_notification_status_as_url }}">
{% endif %}
{{ item.status|format_notification_status(item.template.template_type) }}
{% if item.status|format_notification_status_as_url %}
</a>
{% endif %}
{% endcall %}
{% endcall %}

View File

@@ -174,7 +174,7 @@
{% else %}
<form method="post" enctype="multipart/form-data" action="{{url_for('main.start_job', service_id=current_service.id, upload_id=upload_id)}}" class='page-footer'>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden" name="help" value="{{ '3' if request.args['help'] == '2' else 0 }}" />
<input type="hidden" name="help" value="{{ '3' if help else 0 }}" />
<input type="submit" class="button" value="Send {{ count_of_recipients }} {{ message_count_label(count_of_recipients, template.template_type, suffix='') }}" />
<a href="{{ back_link }}" class="page-footer-back-link">Back</a>
</form>

View File

@@ -16,18 +16,17 @@ Delivery and failure GOV.UK Notify
<ul class="list list-bullet">
<li><a href="#sending">Sending</a></li>
<li><a href="#delivered">Delivered</a></li>
<li><a href="#permanent-failure">Permanently failed</a></li>
<li><a href="#temporary-failure">Temporarily failed</a></li>
<li><a href="#does-not-exist">Phone number or email address does not exist</a></li>
<li><a href="#inbox-not-accepting-messages">Inbox not accepting messages right now</a></li>
<li><a href="#phone-not-accepting-messages">Phone not accepting messages right now</a></li>
<li><a href="#technical-failure">Technical failure</a></li>
</ul>
<p>Our delivery states are the same for both email and text message.</p>
<h2 id="sending" class="heading-medium">Sending</h2>
<p>All new messages start with the state Sending.</p>
<p>All messages start in the Sending state.</p>
<p>This means that we have accepted the message, the message is waiting in a queue to be sent to our email or text message delivery partners.</p>
<p>This means that we have accepted the message. Its waiting in a queue to be sent to our email or text message delivery partners.</p>
<h2 id="delivered" class="heading-medium">Delivered</h2>
@@ -35,23 +34,27 @@ Delivery and failure GOV.UK Notify
<p>We cant tell you if theyve read it to do so would require invasive and unreliable tracking techniques.</p>
<h2 id="permanent-failure" class="heading-medium">Permanently failed</h2>
<h2 id="does-not-exist" class="heading-medium">Phone number or email address does not exist</h2>
<p>This means the email address or mobile number doesnt exist or is blacklisted also known as a hard bounce.</p>
<p>Youre still billed for text messages to non-existant phone numbers.</p>
<p>Youre still billed for these text messages.</p>
<p><strong>You need to remove these email addresses or phone numbers from your database.</strong></p>
<p><strong>You need to remove this email address or mobile number from your database.</strong></p>
<a id="not-accepting-messages"></a>
<h2 id="temporary-failure" class="heading-medium">Temporarily failed</h2>
<h2 id="inbox-not-accepting-messages" class="heading-medium">Inbox not accepting messages right now</h2>
<p>This means the email address or mobile number was full, or the mobile phone was switched off  also known as a soft bounce.</p>
<p>This can happen for a number of reasons, eg the users inbox was full.</p>
<p>We mark messages as Temporarily failed.</p>
<p><strong>You can choose to retry these messages later or not.</strong></p>
<h2 id="phone-not-accepting-messages" class="heading-medium">Phone not accepting messages right now</h2>
<p>This means the users phone was full or hasnt been switched on in the last 72 hours.</p>
<p>Youre still billed for these messages.</p>
<p><strong>You can choose to retry this message later or not.</strong></p>
<p><strong>You can choose to retry these messages later or not.</strong></p>
<h2 id="technical-failure" class="heading-medium">Technical failure</h2>
@@ -61,7 +64,7 @@ Delivery and failure GOV.UK Notify
<p>You wont be billed for these messages.</p>
<p><strong>You need to retry this message yourself later.</strong></p>
<p><strong>You need to retry these messages yourself later.</strong></p>
</div>
</div>

View File

@@ -1,6 +1,7 @@
{% extends "withnav_template.html" %}
{% from "components/table.html" import list_table, row, field, boolean_field, hidden_field_heading %}
{% from "components/page-footer.html" import page_footer %}
{% from "components/tick-cross.html" import tick_cross %}
{% set table_options = {
'field_headings': [
@@ -29,49 +30,84 @@ Manage users GOV.UK Notify
{% endif %}
</div>
{% call(item, row_number) list_table(
users, caption='Active', **table_options
) %}
{% call field() %}
{{ item.name }}
{%- if item.email_address == current_user.email_address -%}
&ensp;<span class="hint">(you)</span>
{% endif %}
{% endcall %}
{{ boolean_field(item.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters'])) }}
{{ boolean_field(item.has_permissions(permissions=['manage_users', 'manage_templates', 'manage_settings'])) }}
{{ boolean_field(item.has_permissions(permissions=['manage_api_keys'])) }}
{% call field(align='right') %}
{% if current_user.has_permissions(['manage_users']) %}
{% if current_user.id != item.id %}
<a href="{{ url_for('.edit_user_permissions', service_id=current_service.id, user_id=item.id)}}">Edit</a>
{% endif %}
{% endif %}
{% endcall %}
{% endcall %}
<h2 class="heading-medium">
Active
</h2>
<div class="user-list">
{% for user in users %}
<div class="user-list-item">
<h3>
{{ user.name }}&ensp;<span class="hint">
{%- if user.email_address == current_user.email_address -%}
(you)
{% else %}
{{ user.email_address }}
{% endif %}
</span>
</h3>
<ul class="tick-cross-list">
<div class="tick-cross-list-permissions">
{{ tick_cross(
user.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters']),
'Send messages'
) }}
{{ tick_cross(
user.has_permissions(permissions=['manage_users', 'manage_templates', 'manage_settings']),
'Manage service'
) }}
{{ tick_cross(
user.has_permissions(permissions=['manage_api_keys']),
'Access API keys'
) }}
</div>
{% if current_user.has_permissions(['manage_users']) %}
{% if current_user.id != user.id %}
<li class="tick-cross-list-edit-link">
<a href="{{ url_for('.edit_user_permissions', service_id=current_service.id, user_id=user.id)}}">Edit permissions</a>
</li>
{% endif %}
{% endif %}
</ul>
</div>
{% endfor %}
</div>
{% if invited_users %}
{% call(item, row_number) list_table(
invited_users, caption='Invited', **table_options
) %}
{% call field() %}
{{ item.email_address }}
{% endcall %}
{{ boolean_field(item.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters'])) }}
{{ boolean_field(item.has_permissions(permissions=['manage_users', 'manage_templates', 'manage_settings'])) }}
{{ boolean_field(item.has_permissions(permissions=['manage_api_keys'])) }}
{% if item.status == 'pending' %}
{% call field(align='right') %}
{% if current_user.has_permissions(['manage_users']) %}
<a href="{{ url_for('.cancel_invited_user', service_id=current_service.id, invited_user_id=item.id)}}">Cancel invitation</a>
{% endif %}
{% endcall %}
{% else %}
{% call field() %}
{{ item.status }}
{% endcall %}
{% endif %}
{% endcall %}
<h2 class="heading-medium">
Invited
</h2>
<div class="user-list">
{% for user in invited_users %}
<div class="user-list-item">
<h3>
{{ user.email_address }}
</h3>
<ul class="tick-cross-list">
<div class="tick-cross-list-permissions">
{{ tick_cross(
user.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters']),
'Send messages'
) }}
{{ tick_cross(
user.has_permissions(permissions=['manage_users', 'manage_templates', 'manage_settings']),
'Manage service'
) }}
{{ tick_cross(
user.has_permissions(permissions=['manage_api_keys']),
'Access API keys'
) }}
</div>
<li class="tick-cross-list-edit-link">
{% if user.status == 'pending' and current_user.has_permissions(['manage_users']) %}
<a href="{{ url_for('.cancel_invited_user', service_id=current_service.id, invited_user_id=user.id)}}">Cancel invitation</a>
{% else %}
{{ user.status|title }}
{% endif %}
</li>
</ul>
</div>
{% endfor %}
</div>
{% endif %}
{% endblock %}

View File

@@ -73,7 +73,13 @@
) }}
{% call field(status=item.status|format_notification_status_as_field_status, align='right') %}
{% if item.status|format_notification_status_as_url %}
<a href="{{ item.status|format_notification_status_as_url }}">
{% endif %}
{{ item.status|format_notification_status(item.template.template_type) }}
{% if item.status|format_notification_status_as_url %}
</a>
{% endif %}
{% endcall %}
{% endcall %}

View File

@@ -55,7 +55,7 @@
{% endcall %}
{{ page_footer("Preview", back_link=(
url_for('.choose_template', service_id=current_service.id, template_type=template.template_type)) if not request.args['help'] else None
url_for('.send_messages', service_id=current_service.id, template_id=template.id)) if not help else None
) }}
</form>

View File

@@ -89,7 +89,7 @@ def get_errors_for_csv(recipients, template_type):
def generate_notifications_csv(json_list):
from app import format_datetime
from app import format_datetime, format_notification_status
content = StringIO()
retval = None
with content as csvfile:
@@ -102,7 +102,7 @@ def generate_notifications_csv(json_list):
x['template']['name'],
x['template']['template_type'],
x['job']['original_file_name'] if x['job'] else '',
x['status'],
format_notification_status(x['status'], x['template']['template_type']),
format_datetime(x['created_at'])])
retval = content.getvalue()
return retval
@@ -186,3 +186,7 @@ class Spreadsheet():
file_type=extension,
file_content=file_content.getvalue()
).to_array(), filename)
def get_help_argument():
return request.args.get('help') if request.args.get('help') in ('1', '2', '3') else None

View File

@@ -2,12 +2,12 @@
This document is for central government developers and technical architects who want to use the GOV.UK Notify platform to send notifications to users of their digital service.
* [About GOV.UK Notify](#about_Notify)
* [Before you start](#beforestart)
* [Integrate the GOV.UK Notify API into your service](#integrate_Notify)
* [Authenticate requests](#AuthRequests)
* [JSON Web Tokens: claims](#JWT_claims)
* [API client libraries](#client_libraries)
* [Test your integration with GOV.UK Notify](#test_integ)
* [API keys](#API_keys)
* [API endpoints](#API_endpoints)
* [Send notifications: POST](#sendnotifications)
* [Retrieve notifications: GET](#getnotifications)
@@ -17,47 +17,14 @@ This document is for central government developers and technical architects who
<h2 id="about_Notify">About GOV.UK Notify</h2>
GOV.UK Notify is a cross-government platform that lets government services send notifications by text or email. It's currently in beta.
There are 2 ways to send notifications:
* use the [GOV.UK Notify](https://www.notifications.service.gov.uk/) web application
* [integrate your web applications or back office systems](#integrate_Notify) with the GOV.UK Notify API
The GOV.UK Notify allows you to [send notifications (POST)](#sendnotifications) and [get the status of notifications (GET)](#getnotifications) you have sent.
To find out more about GOV.UK Notify, see the [Government as a Platform](https://governmentasaplatform.blog.gov.uk/) blog.
<h2 id="beforestart">Before you start</h2>
1. Register for a [GOV.UK Notify](https://www.notifications.service.gov.uk/) account.
You'll need an email address from a local or central government organisation and your mobile phone for 2-factor authentication.
2. Add a template so you can send text and email notifications.
**Note:** A template is required even if you send notifications with the GOV.UK Notify API.
You can personalise the template using double brackets for placeholders. For example:
> Dear ((name)),
>
> Your ((item)) is due for renewal on ((date)).
3. Create an API key. This will be used to connect to the GOV.UK Notify API.
Each service can have multiple API keys. This allows you to integrate several systems, each with its own key. You can also have separate keys for your development and test environments.
**Important:** API keys are secret, so save them somewhere safe. Don't commit API keys to public source code repositories.
<h2 id="integrate_Notify">Integrate the GOV.UK Notify API into your service</h2>
There are 2 ways to integrate the API into your service:
* use one of the client libraries provided by GOV.UK Notify:
* [Python library](https://github.com/alphagov/notifications-python-client/blob/master/README.md#usage)
* [PHP library](https://github.com/alphagov/notifications-php-client/blob/master/README.md#usage)
* [Java library](https://github.com/alphagov/notifications-java-client)
@@ -111,6 +78,57 @@ GOV.UK Notify supports the following client libraries:
These provide example code for calling the API and for creating API tokens.
<h2 id="test_integ">Test your integration with GOV.UK Notify</h2>
Service teams should do all their testing within the GOV.UK Notify production environment (https://api.notifications.service.gov.uk).
You dont need different service accounts or environments. Instead, there are 3 types of API key that let you do functional and performance integration testing.
<h3 id="API_keys">API keys</h3>
The 3 types of API key that you can create within GOV.UK Notify are:
* [normal keys](#normal_keys)
* [team keys](#team_keys)
* [test keys](#test_keys)
Type of key | Sends real messages? | Appears in activity and statistics? | Daily service limit
--- | --- | --- | ---
Normal key | Yes | Yes | 50 (trial), unlimited (live)
Team key | Yes (only to team members) | Yes | 50 (trial), unlimited (live)
Test key | No | No | Unlimited
<h4 id="normal_keys">Normal keys</h4>
Normal keys have the same permissions as the service:
* when the service is in trial mode, you can send only to members of your team and you are restricted to 50 messages per day
* when the service is live, you can use the key to send messages to anyone
* three
Messages sent with a normal key show up on your dashboard and count against your text message and email allowances.
There is no need to generate a new key when the service moves from trial to live.
Dont use your normal key for automated testing.
<h4 id="team_keys">Team keys</h4>
Use team keys for end-to-end functional testing.
A team key lets you send real messages to members of your team. You get an error if you try to send messages to anyone else.
Messages sent with a team key show up on your dashboard and count against your text message and email allowances.
<h4 id="test_keys">Test keys</h4>
Use test keys to test the performance of your service and its integration with GOV.UK Notify under load.
Test keys dont send real messages but they do generate realistic responses. Theres no restriction on who you can send to or how many messages you can send per day.
Messages sent using a test key dont show up on your dashboard or count against your text message and email allowances.
<h2 id="API_endpoints">API endpoints</h2>
You can use the GOV.UK Notify API to:

View File

@@ -21,7 +21,7 @@ function display_result {
}
if [ ! $VIRTUAL_ENV ]; then
virtualenv ./venv
virtualenv -p python3 ./venv
. ./venv/bin/activate
fi

View File

@@ -149,13 +149,15 @@ def job_json(service_id,
original_file_name="thisisatest.csv",
notification_count=1,
notifications_sent=1,
status=''):
status=None):
if job_id is None:
job_id = str(generate_uuid())
if template_id is None:
template_id = str(generate_uuid())
if created_at is None:
created_at = str(datetime.utcnow().time())
if status is None:
status = 'Delivered'
data = {
'id': job_id,
'service': service_id,
@@ -174,16 +176,19 @@ def job_json(service_id,
return data
def notification_json(service_id,
job=None,
template=None,
to='07123456789',
status='delivered',
sent_at=None,
job_row_number=None,
created_at=None,
updated_at=None,
with_links=False):
def notification_json(
service_id,
job=None,
template=None,
to='07123456789',
status=None,
sent_at=None,
job_row_number=None,
created_at=None,
updated_at=None,
with_links=False,
rows=5
):
if template is None:
template = template_json(service_id, str(generate_uuid()))
if sent_at is None:
@@ -192,6 +197,8 @@ def notification_json(service_id,
created_at = str(datetime.utcnow().time())
if updated_at is None:
updated_at = str((datetime.utcnow() + timedelta(minutes=1)).time())
if status is None:
status = 'delivered'
links = {}
if with_links:
links = {
@@ -213,7 +220,7 @@ def notification_json(service_id,
'updated_at': updated_at,
'job_row_number': job_row_number,
'template_version': template['version']
} for i in range(5)],
} for i in range(rows)],
'total': 5,
'page_size': 50,
'links': links

View File

@@ -99,6 +99,34 @@ def test_should_show_page_for_one_job(
)
def test_should_show_not_show_csv_download_in_tour(
app_,
service_one,
active_user_with_permissions,
mock_get_service_template,
mock_get_service_statistics,
mock_get_job,
mocker,
mock_get_notifications,
fake_uuid
):
with app_.test_request_context(), app_.test_client() as client:
client.login(active_user_with_permissions, mocker, service_one)
response = client.get(url_for(
'main.view_job',
service_id=service_one['id'],
job_id=fake_uuid,
help=3
))
assert response.status_code == 200
assert url_for(
'main.view_job_csv',
service_id=service_one['id'],
job_id=fake_uuid
) not in response.get_data(as_text=True)
@freeze_time("2016-01-01 11:09:00.061258")
def test_should_show_updates_for_one_job_as_json(
app_,
@@ -124,7 +152,7 @@ def test_should_show_updates_for_one_job_as_json(
assert 'Recipient' in content['notifications']
assert '07123456789' in content['notifications']
assert 'Status' in content['notifications']
assert job_json['status'] in content['status']
print(content['notifications'])
assert 'Delivered' in content['notifications']
assert '11:10' in content['notifications']
assert 'Uploaded by Test User on 1 January at 11:09' in content['status']

View File

@@ -205,10 +205,9 @@ def test_manage_users_shows_invited_user(app_,
assert response.status_code == 200
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
assert page.h1.string.strip() == 'Team members'
invites_table = page.find_all('table')[1]
cols = invites_table.find_all('td')
assert cols[0].text.strip() == 'invited_user@test.gov.uk'
assert cols[4].text.strip() == 'Cancel invitation'
invited_users_list = page.find_all('div', {'class': 'user-list'})[1]
assert invited_users_list.find_all('h3')[0].text.strip() == 'invited_user@test.gov.uk'
assert invited_users_list.find_all('a')[0].text.strip() == 'Cancel invitation'
def test_manage_users_does_not_show_accepted_invite(app_,
@@ -232,8 +231,8 @@ def test_manage_users_does_not_show_accepted_invite(app_,
assert response.status_code == 200
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
assert page.h1.string.strip() == 'Team members'
tables = page.find_all('table')
assert len(tables) == 1
user_lists = page.find_all('div', {'class': 'user-list'})
assert len(user_lists) == 1
assert not page.find(text='invited_user@test.gov.uk')

View File

@@ -1,4 +1,8 @@
from app.utils import email_safe
import pytest
from io import StringIO
from app.utils import email_safe, generate_notifications_csv
from csv import DictReader
from freezegun import freeze_time
def test_email_safe_return_dot_separated_email_domain():
@@ -6,3 +10,41 @@ def test_email_safe_return_dot_separated_email_domain():
expected = 'some.service.withstuff.b123'
actual = email_safe(test_name)
assert actual == expected
@pytest.mark.parametrize(
"status, template_type, expected_status",
[
('sending', None, 'Sending'),
('delivered', None, 'Delivered'),
('failed', None, 'Failed'),
('technical-failure', None, 'Technical failure'),
('temporary-failure', 'email', 'Inbox not accepting messages right now'),
('permanent-failure', 'email', 'Email address doesnt exist'),
('temporary-failure', 'sms', 'Phone not accepting messages right now'),
('permanent-failure', 'sms', 'Phone number doesnt exist')
]
)
@freeze_time("2016-01-01 11:09:00.061258")
def test_generate_csv_from_notifications(
app_,
service_one,
active_user_with_permissions,
mock_get_notifications,
status,
template_type,
expected_status
):
with app_.test_request_context():
csv_content = generate_notifications_csv(
mock_get_notifications(
service_one['id'],
rows=1,
set_template_type=template_type,
set_status=status
)['notifications']
)
for row in DictReader(StringIO(csv_content)):
assert row['Time'] == 'Friday 01 January 2016 at 11:09'
assert row['Status'] == expected_status

View File

@@ -870,17 +870,36 @@ def mock_get_jobs(mocker, api_user_active):
@pytest.fixture(scope='function')
def mock_get_notifications(mocker, api_user_active):
def _get_notifications(service_id,
job_id=None,
page=1,
page_size=50,
template_type=None,
status=None,
limit_days=None):
def _get_notifications(
service_id,
job_id=None,
page=1,
page_size=50,
template_type=None,
status=None,
limit_days=None,
rows=5,
set_template_type=None,
set_status=None
):
job = None
if job_id is not None:
job = job_json(service_id, api_user_active, job_id=job_id)
return notification_json(service_id, job=job)
if set_template_type:
return notification_json(
service_id,
template={'template_type': set_template_type, 'name': 'name', 'id': 'id', 'version': 1},
rows=rows,
status=set_status,
job=job
)
else:
return notification_json(
service_id,
rows=rows,
status=set_status,
job=job
)
return mocker.patch(
'app.notification_api_client.get_notifications_for_service',