mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-09-11 10:28:41 -04:00
Merge branch 'master' into platform-admin
Conflicts: app/__init__.py app/main/views/add_service.py app/main/views/jobs.py app/templates/main_nav.html tests/app/main/views/test_dashboard.py tests/conftest.py
This commit is contained in:
+21
@@ -41,6 +41,27 @@ deploy:
|
||||
deployment_group: notifications_admin_deployment_group
|
||||
region: eu-west-1
|
||||
on: *2
|
||||
- provider: s3
|
||||
access_key_id: AKIAJ5MKF6G3P2JQP4QQ
|
||||
secret_access_key: &1
|
||||
secure: xfjg4kNBvU0B9xhRETr14mB0bCpVonlAKqGnKL2AoqpnF19yihqGNA8sv/pOGUFpeWZO3cW2GA3anyL2gGG1X0K3f81649mneVJkSHaZ2fiG/S1eKtS0Ws5XblSqLmKTPC7H9ndUxT8r+r2wLg62netBE5g8tAxw2QwN/gVz2fK/68owiyeD/jl6gw/iQ47F+mmGdAY/eFe8sUuGR4Oxj2xNAYARaDQOmHpQF/IG3M69FO5uOJtck8fUWnpd0rWxsyWBOVwwIRQHL6cWOyodeIK7YvLmzviCi1GojBPKQwQbjJu89LHfMJJTW1625drj5CNounuENTFte0Pip8zp90bg090VA8OlTXNWcyFjBQD1vNIE59vyQ/hCh90NK1nlTXdnNOwL0VZTMxQ/zYulXoMqwDLfDozhQbmnkXmexJl6BF4/dz+XmwDu7st5A/PI6U5zCK86ST/6g3MklGSseFi5Rkt6kmJrdlRIhiLnoaab8YgI0FPWjzHBC6B98ZtgIUiUk7Ng5ZTM8Sjq1HCC7mUDrDL0c7aerZA5bq2hQiKGhvjBXFU17iHZ0eEDZ8kO+jumeMwmpW6NbjlS0PuDx+lHywMSG7r+YVmkjxq5gwrTVl3evRxhHe8H/lU18y2dOKpIyX5UEZpXRq9kAWuQruCDBwoDe3Y3QP+Rg7HVGBU=
|
||||
local_dir: dpl_cd_upload
|
||||
skip_cleanup: true
|
||||
on: &2
|
||||
repo: alphagov/notifications-admin
|
||||
branch: live
|
||||
bucket: live-notifications-api-codedeploy
|
||||
region: eu-west-1
|
||||
- provider: codedeploy
|
||||
access_key_id: AKIAJ5MKF6G3P2JQP4QQ
|
||||
secret_access_key: *1
|
||||
bucket: live-notifications-api-codedeploy
|
||||
key: notifications-admin-$TRAVIS_BRANCH-$TRAVIS_BUILD_NUMBER-$TRAVIS_COMMIT.zip
|
||||
bundle_type: zip
|
||||
application: notifications-admin
|
||||
deployment_group: live_notifications_admin_deployment_group
|
||||
region: eu-west-1
|
||||
on: *2
|
||||
- provider: s3
|
||||
access_key_id: AKIAJQPPNM6P6V53SWKA
|
||||
secret_access_key: &1
|
||||
|
||||
@@ -3,7 +3,7 @@ from flask.ext.script import Manager, Server
|
||||
from app import create_app
|
||||
|
||||
|
||||
application = create_app(os.getenv('NOTIFICATIONS_ADMIN_ENVIRONMENT') or 'development')
|
||||
application = create_app()
|
||||
manager = Manager(application)
|
||||
port = int(os.environ.get('PORT', 6012))
|
||||
manager.add_command("runserver", Server(host='0.0.0.0', port=port))
|
||||
|
||||
+20
-21
@@ -2,7 +2,8 @@ import os
|
||||
import re
|
||||
|
||||
import dateutil
|
||||
from flask import (Flask, session, Markup, escape, render_template, make_response)
|
||||
import urllib
|
||||
from flask import (Flask, session, Markup, escape, render_template, make_response, current_app)
|
||||
from flask._compat import string_types
|
||||
from flask_login import LoginManager
|
||||
from flask_wtf import CsrfProtect
|
||||
@@ -19,6 +20,7 @@ from app.notify_client.job_api_client import JobApiClient
|
||||
from app.notify_client.notification_api_client import NotificationApiClient
|
||||
from app.notify_client.status_api_client import StatusApiClient
|
||||
from app.notify_client.invite_api_client import InviteApiClient
|
||||
from app.notify_client.statistics_api_client import StatisticsApiClient
|
||||
from app.its_dangerous_session import ItsdangerousSessionInterface
|
||||
from app.asset_fingerprinter import AssetFingerprinter
|
||||
from utils.recipients import validate_phone_number, InvalidPhoneError
|
||||
@@ -36,15 +38,16 @@ job_api_client = JobApiClient()
|
||||
notification_api_client = NotificationApiClient()
|
||||
status_api_client = StatusApiClient()
|
||||
invite_api_client = InviteApiClient()
|
||||
statistics_api_client = StatisticsApiClient()
|
||||
asset_fingerprinter = AssetFingerprinter()
|
||||
|
||||
|
||||
def create_app(config_name, config_overrides=None):
|
||||
def create_app():
|
||||
application = Flask(__name__)
|
||||
|
||||
application.config['NOTIFY_ADMIN_ENVIRONMENT'] = config_name
|
||||
application.config.from_object(configs[config_name])
|
||||
init_app(application, config_overrides)
|
||||
application.config.from_object(os.environ['NOTIFY_ADMIN_ENVIRONMENT'])
|
||||
|
||||
init_app(application)
|
||||
logging.init_app(application)
|
||||
init_csrf(application)
|
||||
|
||||
@@ -55,6 +58,7 @@ def create_app(config_name, config_overrides=None):
|
||||
notification_api_client.init_app(application)
|
||||
status_api_client.init_app(application)
|
||||
invite_api_client.init_app(application)
|
||||
statistics_api_client.init_app(application)
|
||||
|
||||
login_manager.init_app(application)
|
||||
login_manager.login_view = 'main.sign_in'
|
||||
@@ -75,6 +79,7 @@ def create_app(config_name, config_overrides=None):
|
||||
application.add_template_filter(format_time)
|
||||
application.add_template_filter(syntax_highlight_json)
|
||||
application.add_template_filter(valid_phone_number)
|
||||
application.add_template_filter(linkable_name)
|
||||
|
||||
application.after_request(useful_headers_after_request)
|
||||
register_errorhandlers(application)
|
||||
@@ -101,22 +106,12 @@ def init_csrf(application):
|
||||
abort(400, reason)
|
||||
|
||||
|
||||
def init_app(app, config_overrides):
|
||||
|
||||
if config_overrides:
|
||||
for key in app.config.keys():
|
||||
if key in config_overrides:
|
||||
app.config[key] = config_overrides[key]
|
||||
|
||||
for key, value in app.config.items():
|
||||
if key in os.environ:
|
||||
app.config[key] = convert_to_boolean(os.environ[key])
|
||||
|
||||
@app.context_processor
|
||||
def init_app(application):
|
||||
@application.context_processor
|
||||
def inject_global_template_variables():
|
||||
return {
|
||||
'asset_path': '/static/',
|
||||
'header_colour': app.config['HEADER_COLOUR'],
|
||||
'header_colour': application.config['HEADER_COLOUR'],
|
||||
'asset_url': asset_fingerprinter.get_url
|
||||
}
|
||||
|
||||
@@ -139,6 +134,10 @@ def nl2br(value):
|
||||
return Markup(result)
|
||||
|
||||
|
||||
def linkable_name(value):
|
||||
return urllib.parse.quote_plus(value)
|
||||
|
||||
|
||||
def syntax_highlight_json(code):
|
||||
return Markup(highlight(code, JavascriptLexer(), HtmlFormatter(noclasses=True)))
|
||||
|
||||
@@ -203,7 +202,7 @@ def register_errorhandlers(application):
|
||||
|
||||
@application.errorhandler(Exception)
|
||||
def handle_bad_request(error):
|
||||
from flask import current_app
|
||||
if current_app.config.get('DEBUG'):
|
||||
print(error)
|
||||
# We want the Flask in browser stacktrace
|
||||
if current_app.config.get('DEBUG', None):
|
||||
raise error
|
||||
return _error_response(500)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
@@ -20,8 +20,9 @@
|
||||
<div class="textbox-highlight-mask" aria-hidden="true" />
|
||||
<div class="textbox-highlight-foreground" aria-hidden="true" />
|
||||
`))
|
||||
.on("input", this.update)
|
||||
.on("scroll", this.maintainScrollParity);
|
||||
.on("input", this.update);
|
||||
|
||||
this.initialHeight = this.$textbox.height();
|
||||
|
||||
this.$backgroundMaskForeground.width(
|
||||
this.$textbox.width()
|
||||
@@ -32,14 +33,21 @@
|
||||
|
||||
};
|
||||
|
||||
this.update = () => this.$backgroundMaskForeground.html(
|
||||
this.resize = () => this.$textbox.height(
|
||||
Math.max(
|
||||
this.initialHeight,
|
||||
this.$backgroundMaskForeground.outerHeight()
|
||||
)
|
||||
);
|
||||
|
||||
this.replacePlaceholders = () => this.$backgroundMaskForeground.html(
|
||||
$('<div/>').text(this.$textbox.val()).html().replace(
|
||||
tagPattern, match => `<span class='tag'>${match}</span>`
|
||||
)
|
||||
);
|
||||
|
||||
this.maintainScrollParity = () => this.$backgroundMaskForeground.scrollTop(
|
||||
this.$textbox.scrollTop()
|
||||
this.update = () => (
|
||||
this.replacePlaceholders() && this.resize()
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
@include grid-column(3/4);
|
||||
}
|
||||
|
||||
.column-one-sixth {
|
||||
@include grid-column(1/6);
|
||||
}
|
||||
|
||||
.column-one-eighth {
|
||||
@include grid-column(1/8);
|
||||
}
|
||||
@@ -16,7 +20,7 @@
|
||||
}
|
||||
|
||||
.bottom-gutter-2-3 {
|
||||
margin-bottom: $gutter * 2/3;
|
||||
margin-bottom: $gutter-two-thirds;
|
||||
}
|
||||
|
||||
.align-with-heading {
|
||||
@@ -26,3 +30,19 @@
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
|
||||
.global-cookie-message {
|
||||
p {
|
||||
@extend %site-width-container;
|
||||
}
|
||||
}
|
||||
|
||||
.footer-nav {
|
||||
@include copy-16;
|
||||
margin-bottom: $gutter-two-thirds;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
margin-right: $gutter-half;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,3 +80,11 @@ a {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.heading-xlarge {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
Taken from the GOV.UK component at
|
||||
https://github.com/alphagov/static/blob/a9d462e71709d2ff6348bcce7e8c625af2b86114/app/assets/stylesheets/govuk-component/_previous-and-next-navigation.scss
|
||||
and
|
||||
https://github.com/alphagov/static/blob/da8aeeaa749093eab30286d7fc9f965533b66f47/app/assets/stylesheets/styleguide/_conditionals2.scss
|
||||
*/
|
||||
@import "_colours.scss";
|
||||
@import "_typography.scss";
|
||||
|
||||
// Media query helpers. These make producing IE layouts
|
||||
// super easy.
|
||||
|
||||
// These are desktop and down media queries
|
||||
|
||||
// There is also a local version of this in Smartanswers.
|
||||
|
||||
$is-ie: false !default;
|
||||
|
||||
@mixin media-down($size: false, $max-width: false, $min-width: false) {
|
||||
@if $is-ie == false {
|
||||
@if $size == mobile {
|
||||
@media (max-width: 640px){
|
||||
@content;
|
||||
}
|
||||
} @else if $size == tablet {
|
||||
@media (max-width: 800px){
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.govuk-previous-and-next-navigation {
|
||||
|
||||
@include media-down(mobile) {
|
||||
margin: 2em 0 0 0;
|
||||
}
|
||||
|
||||
display: block;
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
@include core-16($line-height: (20 / 16));
|
||||
float: left;
|
||||
list-style: none;
|
||||
text-align: right;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 49%;
|
||||
|
||||
a {
|
||||
|
||||
@include ie-lte(7) {
|
||||
height: 4.5em;
|
||||
}
|
||||
|
||||
display: block;
|
||||
color: $link-colour;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
background-color: $canvas-colour;
|
||||
}
|
||||
|
||||
.pagination-part-title {
|
||||
@include core-27($line-height: (33.75 / 27));
|
||||
margin-bottom: 0.1em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
&.next-page {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
&.next-page a:before {
|
||||
background: transparent file-url("arrow-sprite.png") no-repeat -102px -11px;
|
||||
margin: -4px -32px 0 0;
|
||||
display: block;
|
||||
float: right;
|
||||
width: 30px;
|
||||
height: 38px;
|
||||
content: " ";
|
||||
}
|
||||
|
||||
&.previous-page a:before {
|
||||
background: transparent file-url("arrow-sprite.png") no-repeat -20px -11px;
|
||||
margin: -4px 0 0 -32px;
|
||||
display: block;
|
||||
float: left;
|
||||
width: 30px;
|
||||
height: 38px;
|
||||
content: " ";
|
||||
}
|
||||
|
||||
&.previous-page {
|
||||
float: left;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
&.previous-page a {
|
||||
padding: 0.75em 0 0.75em 3em;
|
||||
}
|
||||
|
||||
&.next-page a {
|
||||
padding: 0.75em 3em 0.75em 0;
|
||||
}
|
||||
|
||||
@include media-down(mobile) {
|
||||
&.previous-page,
|
||||
&.next-page {
|
||||
float: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.next-page a {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,9 +26,8 @@
|
||||
|
||||
}
|
||||
|
||||
.banner-with-tick,
|
||||
.banner-default-with-tick {
|
||||
@extend %banner;
|
||||
%banner-with-tick,
|
||||
.banner-with-tick {
|
||||
padding: $gutter-half ($gutter + $gutter-half);
|
||||
background-image: file-url('tick-white.png');
|
||||
background-size: 19px;
|
||||
@@ -37,6 +36,11 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.banner-default-with-tick {
|
||||
@extend %banner;
|
||||
@extend %banner-with-tick;
|
||||
}
|
||||
|
||||
.banner-dangerous {
|
||||
|
||||
@extend %banner;
|
||||
@@ -54,13 +58,15 @@
|
||||
|
||||
}
|
||||
|
||||
%banner-tip,
|
||||
.banner-tip {
|
||||
|
||||
@extend %banner;
|
||||
background: $yellow;
|
||||
@include bold-19;
|
||||
background-color: $yellow;
|
||||
color: $text-colour;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
margin-top: 0;
|
||||
|
||||
a {
|
||||
&:link,
|
||||
@@ -76,6 +82,12 @@
|
||||
|
||||
}
|
||||
|
||||
.banner-tip-with-tick {
|
||||
@extend %banner-with-tick;
|
||||
@extend %banner-tip;
|
||||
background-image: file-url('tick-black.png');
|
||||
}
|
||||
|
||||
.banner-info,
|
||||
.banner-important {
|
||||
@extend %banner;
|
||||
@@ -91,3 +103,40 @@
|
||||
.banner-info {
|
||||
background-image: file-url('icon-information-2x.png');
|
||||
}
|
||||
|
||||
.banner-mode {
|
||||
|
||||
@extend %banner;
|
||||
background: $govuk-blue;
|
||||
color: $white;
|
||||
margin-top: $gutter;
|
||||
|
||||
.heading-medium {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
&:link,
|
||||
&:visited {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active {
|
||||
color: $light-blue-25;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.big-number {
|
||||
|
||||
margin-top: 10px;
|
||||
|
||||
&-label {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
%big-number,
|
||||
.big-number {
|
||||
|
||||
@include bold-48($tabular-numbers: true);
|
||||
@@ -5,6 +6,62 @@
|
||||
&-label {
|
||||
@include core-19;
|
||||
display: block;
|
||||
padding-bottom: $gutter-half;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.big-number-with-status {
|
||||
|
||||
@extend %big-number;
|
||||
background: $text-colour;
|
||||
color: $white;
|
||||
|
||||
.big-number {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.big-number-label {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.big-number-status {
|
||||
|
||||
display: block;
|
||||
background: $green;
|
||||
position: relative;
|
||||
|
||||
&-error-percentage {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: $error-colour;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
&:link,
|
||||
&:visited,
|
||||
&:active,
|
||||
&:hover {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.big-number {
|
||||
@include bold-19;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.big-number-label {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
resize: none;
|
||||
z-index: 20;
|
||||
background: none;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
&-textbox,
|
||||
@@ -23,7 +24,6 @@
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
overflow: hidden;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
&-background,
|
||||
@@ -36,6 +36,7 @@
|
||||
color: transparent;
|
||||
white-space: pre-wrap;
|
||||
border: 2px solid transparent;
|
||||
padding-bottom: $gutter;
|
||||
}
|
||||
|
||||
&-background {
|
||||
|
||||
@@ -48,6 +48,7 @@ $path: '/static/images/';
|
||||
@import 'components/email-message';
|
||||
@import 'components/api-key';
|
||||
@import 'components/yes-no';
|
||||
@import "components/_previous-next-navigation";
|
||||
|
||||
@import 'views/job';
|
||||
@import 'views/edit-template';
|
||||
|
||||
@@ -43,20 +43,11 @@ def increment_failed_login_count(id):
|
||||
|
||||
|
||||
def activate_user(user):
|
||||
user.state = 'active'
|
||||
return user_api_client.update_user(user)
|
||||
return user_api_client.activate_user(user)
|
||||
|
||||
|
||||
def is_email_unique(email_address):
|
||||
try:
|
||||
if user_api_client.get_user_by_email(email_address):
|
||||
return False
|
||||
return True
|
||||
except HTTPError as ex:
|
||||
if ex.status_code == 404:
|
||||
return True
|
||||
else:
|
||||
raise ex
|
||||
return user_api_client.is_email_unique(email_address)
|
||||
|
||||
|
||||
def send_verify_code(user_id, code_type, to):
|
||||
|
||||
@@ -150,49 +150,6 @@ class TwoFactorForm(Form):
|
||||
raise ValidationError(reason)
|
||||
|
||||
|
||||
class VerifySmsForm(Form):
|
||||
def __init__(self, validate_code_func, *args, **kwargs):
|
||||
'''
|
||||
Keyword arguments:
|
||||
validate_code_func -- Validates the code with the API.
|
||||
'''
|
||||
self.validate_code_func = validate_code_func
|
||||
super(VerifySmsForm, self).__init__(*args, **kwargs)
|
||||
|
||||
sms_code = sms_code()
|
||||
|
||||
def validate_sms_code(self, field):
|
||||
is_valid, reason = self.validate_code_func(field.data, 'sms')
|
||||
if not is_valid:
|
||||
raise ValidationError(reason)
|
||||
|
||||
|
||||
class VerifyForm(Form):
|
||||
def __init__(self, validate_code_func, *args, **kwargs):
|
||||
'''
|
||||
Keyword arguments:
|
||||
validate_code_func -- Validates the code with the API.
|
||||
'''
|
||||
self.validate_code_func = validate_code_func
|
||||
super(VerifyForm, self).__init__(*args, **kwargs)
|
||||
|
||||
sms_code = sms_code()
|
||||
email_code = email_code()
|
||||
|
||||
def _validate_code(self, cde, code_type):
|
||||
is_valid, reason = self.validate_code_func(cde, code_type)
|
||||
if not is_valid:
|
||||
raise ValidationError(reason)
|
||||
|
||||
def validate_email_code(self, field):
|
||||
if self.sms_code.data:
|
||||
self._validate_code(field.data, 'email')
|
||||
|
||||
def validate_sms_code(self, field):
|
||||
if self.email_code.data:
|
||||
self._validate_code(field.data, 'sms')
|
||||
|
||||
|
||||
class EmailNotReceivedForm(Form):
|
||||
email_address = email_address()
|
||||
|
||||
|
||||
@@ -8,25 +8,25 @@ from flask import (
|
||||
from flask_login import login_required
|
||||
|
||||
from app.main import main
|
||||
from app.main.dao import services_dao, users_dao
|
||||
from app.main.dao import services_dao
|
||||
from app.main.forms import AddServiceForm
|
||||
from app.notify_client.models import InvitedUser
|
||||
|
||||
from app import (
|
||||
invite_api_client,
|
||||
user_api_client,
|
||||
service_api_client)
|
||||
service_api_client
|
||||
)
|
||||
|
||||
|
||||
@main.route("/add-service", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def add_service():
|
||||
|
||||
invited_user = session.get('invited_user')
|
||||
if invited_user:
|
||||
invitation = InvitedUser(**invited_user)
|
||||
# if invited user add to service and redirect to dashboard
|
||||
user = users_dao.get_user_by_id(session['user_id'])
|
||||
user = user_api_client.get_user(session['user_id'])
|
||||
service_id = invited_user['service']
|
||||
user_api_client.add_user_to_service(service_id, user.id, invitation.permissions)
|
||||
invite_api_client.accept_invite(service_id, invitation.id)
|
||||
|
||||
@@ -1,35 +1,32 @@
|
||||
from flask import (
|
||||
render_template, redirect, session, url_for)
|
||||
|
||||
from flask_login import current_user
|
||||
render_template,
|
||||
redirect,
|
||||
session,
|
||||
url_for
|
||||
)
|
||||
|
||||
from app import user_api_client
|
||||
from app.main import main
|
||||
from app.main.dao import users_dao
|
||||
from app.main.forms import EmailNotReceivedForm, TextNotReceivedForm
|
||||
from app.main.forms import TextNotReceivedForm
|
||||
|
||||
|
||||
@main.route('/email-not-received', methods=['GET', 'POST'])
|
||||
def check_and_resend_email_code():
|
||||
@main.route('/resend-email-verification')
|
||||
def resend_email_verification():
|
||||
# TODO there needs to be a way to regenerate a session id
|
||||
user = users_dao.get_user_by_email(session['user_details']['email'])
|
||||
form = EmailNotReceivedForm(email_address=user.email_address)
|
||||
if form.validate_on_submit():
|
||||
users_dao.send_verify_code(user.id, 'email', to=form.email_address.data)
|
||||
user.email_address = form.email_address.data
|
||||
users_dao.update_user(user)
|
||||
return redirect(url_for('.verify'))
|
||||
return render_template('views/email-not-received.html', form=form)
|
||||
user = user_api_client.get_user_by_email(session['user_details']['email'])
|
||||
user_api_client.send_verify_email(user.id, user.email_address)
|
||||
return render_template('views/resend-email-verification.html', email=user.email_address)
|
||||
|
||||
|
||||
@main.route('/text-not-received', methods=['GET', 'POST'])
|
||||
def check_and_resend_text_code():
|
||||
# TODO there needs to be a way to regenerate a session id
|
||||
user = users_dao.get_user_by_email(session['user_details']['email'])
|
||||
user = user_api_client.get_user_by_email(session['user_details']['email'])
|
||||
form = TextNotReceivedForm(mobile_number=user.mobile_number)
|
||||
if form.validate_on_submit():
|
||||
users_dao.send_verify_code(user.id, 'sms', to=form.mobile_number.data)
|
||||
user_api_client.send_verify_code(user.id, 'sms', to=form.mobile_number.data)
|
||||
user.mobile_number = form.mobile_number.data
|
||||
users_dao.update_user(user)
|
||||
user_api_client.update_user(user)
|
||||
return redirect(url_for('.verify'))
|
||||
return render_template('views/text-not-received.html', form=form)
|
||||
|
||||
@@ -42,6 +39,6 @@ def verification_code_not_received():
|
||||
@main.route('/send-new-code', methods=['GET'])
|
||||
def check_and_resend_verification_code():
|
||||
# TODO there needs to be a way to generate a new session id
|
||||
user = users_dao.get_user_by_email(session['user_details']['email'])
|
||||
users_dao.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
user = user_api_client.get_user_by_email(session['user_details']['email'])
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
return redirect(url_for('main.two_factor'))
|
||||
|
||||
@@ -8,7 +8,7 @@ from flask_login import login_required
|
||||
from app.main import main
|
||||
from app.main.dao.services_dao import get_service_by_id
|
||||
from app.main.dao import templates_dao
|
||||
from app import job_api_client
|
||||
from app import job_api_client, statistics_api_client
|
||||
|
||||
|
||||
@main.route("/services/<service_id>/dashboard")
|
||||
@@ -27,11 +27,37 @@ def service_dashboard(service_id):
|
||||
message = 'You have successfully accepted your invitation and been added to {}'.format(service_name)
|
||||
flash(message, 'default_with_tick')
|
||||
|
||||
statistics = statistics_api_client.get_statistics_for_service(service_id)['data']
|
||||
|
||||
return render_template(
|
||||
'views/service_dashboard.html',
|
||||
'views/dashboard/dashboard.html',
|
||||
jobs=jobs[:5],
|
||||
more_jobs_to_show=(len(jobs) > 5),
|
||||
free_text_messages_remaining='250,000',
|
||||
spent_this_month='0.00',
|
||||
template_count=len(templates),
|
||||
service=service['data'],
|
||||
statistics=expand_statistics(statistics),
|
||||
templates=templates,
|
||||
service_id=str(service_id))
|
||||
|
||||
|
||||
def expand_statistics(statistics, danger_zone=25):
|
||||
|
||||
if not statistics or not statistics[0]:
|
||||
return {}
|
||||
|
||||
today = statistics[0]
|
||||
|
||||
today.update({
|
||||
'emails_failure_rate':
|
||||
int(today['emails_error'] / today['emails_requested'] * 100) if today['emails_requested'] else 0,
|
||||
'sms_failure_rate':
|
||||
int(today['sms_error'] / today['sms_requested'] * 100) if today['sms_requested'] else 0
|
||||
})
|
||||
|
||||
today.update({
|
||||
'emails_percentage_of_danger_zone': min(today['emails_failure_rate'] / (danger_zone / 100), 100),
|
||||
'sms_percentage_of_danger_zone': min(today['sms_failure_rate'] / (danger_zone / 100), 100)
|
||||
})
|
||||
|
||||
return today
|
||||
|
||||
@@ -16,3 +16,13 @@ def index():
|
||||
@login_required
|
||||
def verify_mobile():
|
||||
return render_template('views/verify-mobile.html')
|
||||
|
||||
|
||||
@main.route('/cookies')
|
||||
def cookies():
|
||||
return render_template('views/cookies.html')
|
||||
|
||||
|
||||
@main.route('/help')
|
||||
def help():
|
||||
return render_template('views/help.html')
|
||||
|
||||
@@ -3,11 +3,15 @@ from flask import (
|
||||
url_for,
|
||||
session,
|
||||
flash,
|
||||
render_template)
|
||||
render_template
|
||||
)
|
||||
|
||||
|
||||
from notifications_python_client.errors import HTTPError
|
||||
|
||||
from app.main import main
|
||||
from app.main.dao.services_dao import get_service_by_id_or_404
|
||||
|
||||
from app import (
|
||||
invite_api_client,
|
||||
user_api_client
|
||||
@@ -32,7 +36,11 @@ def accept_invite(token):
|
||||
|
||||
session['invited_user'] = invited_user.serialize()
|
||||
|
||||
existing_user = user_api_client.get_user_by_email(invited_user.email_address)
|
||||
try:
|
||||
existing_user = user_api_client.get_user_by_email(invited_user.email_address)
|
||||
except HTTPError as ex:
|
||||
if ex.status_code == 404:
|
||||
existing_user = False
|
||||
|
||||
service_users = user_api_client.get_users_for_service(invited_user.service)
|
||||
|
||||
|
||||
+35
-1
@@ -4,7 +4,12 @@ import time
|
||||
|
||||
from flask import (
|
||||
render_template,
|
||||
jsonify
|
||||
abort,
|
||||
jsonify,
|
||||
flash,
|
||||
redirect,
|
||||
request,
|
||||
url_for
|
||||
)
|
||||
from flask_login import login_required
|
||||
from utils.template import Template
|
||||
@@ -13,6 +18,7 @@ from app import job_api_client, notification_api_client
|
||||
from app.main import main
|
||||
from app.main.dao import templates_dao
|
||||
from app.main.dao import services_dao
|
||||
from app.utils import (get_page_from_request, generate_previous_next_dict)
|
||||
|
||||
|
||||
@main.route("/services/<service_id>/jobs")
|
||||
@@ -85,6 +91,34 @@ def view_job_updates(service_id, job_id):
|
||||
})
|
||||
|
||||
|
||||
@main.route('/services/<service_id>/notifications')
|
||||
@login_required
|
||||
def view_notifications(service_id):
|
||||
# TODO get the api to return count of pages as well.
|
||||
page = get_page_from_request()
|
||||
if page is None:
|
||||
abort(404, "Invalid page argument ({}) reverting to page 1.".format(request.args['page'], None))
|
||||
notifications = notification_api_client.get_notifications_for_service(service_id=service_id, page=page)
|
||||
prev_page = None
|
||||
if notifications['links'].get('prev', None):
|
||||
prev_page = generate_previous_next_dict(
|
||||
'main.view_notifications',
|
||||
{'service_id': service_id}, page - 1, 'Previous page', 'page {}'.format(page - 1))
|
||||
next_page = None
|
||||
if notifications['links'].get('next', None):
|
||||
next_page = generate_previous_next_dict(
|
||||
'main.view_notifications',
|
||||
{'service_id': service_id}, page + 1, 'Next page', 'page {}'.format(page + 1))
|
||||
return render_template(
|
||||
'views/notifications.html',
|
||||
service_id=service_id,
|
||||
notifications=notifications['notifications'],
|
||||
page=page,
|
||||
prev_page=prev_page,
|
||||
next_page=next_page
|
||||
)
|
||||
|
||||
|
||||
@main.route("/services/<service_id>/jobs/<job_id>/notification/<string:notification_id>")
|
||||
@login_required
|
||||
def view_notification(service_id, job_id, notification_id):
|
||||
|
||||
@@ -3,7 +3,8 @@ from flask import (
|
||||
render_template,
|
||||
redirect,
|
||||
url_for,
|
||||
flash)
|
||||
flash
|
||||
)
|
||||
|
||||
from flask_login import (
|
||||
login_required,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import (
|
||||
datetime,
|
||||
timedelta
|
||||
)
|
||||
|
||||
from flask import (
|
||||
render_template,
|
||||
@@ -12,7 +15,7 @@ from flask import (
|
||||
from flask.ext.login import current_user
|
||||
|
||||
from app.main import main
|
||||
from app.main.dao import users_dao
|
||||
|
||||
from app.main.forms import (
|
||||
RegisterUserForm,
|
||||
RegisterUserFromInviteForm
|
||||
@@ -28,9 +31,9 @@ def register():
|
||||
|
||||
form = RegisterUserForm()
|
||||
if form.validate_on_submit():
|
||||
registered = _do_registration(form)
|
||||
registered = _do_registration(form, send_sms=False)
|
||||
if registered:
|
||||
return redirect(url_for('main.verify'))
|
||||
return redirect(url_for('main.registration_continue'))
|
||||
else:
|
||||
flash('There was an error registering your account')
|
||||
return render_template('views/register.html', form=form), 400
|
||||
@@ -40,7 +43,6 @@ def register():
|
||||
|
||||
@main.route('/register-from-invite', methods=['GET', 'POST'])
|
||||
def register_from_invite():
|
||||
|
||||
form = RegisterUserFromInviteForm()
|
||||
invited_user = session.get('invited_user')
|
||||
if not invited_user:
|
||||
@@ -61,8 +63,8 @@ def register_from_invite():
|
||||
return render_template('views/register-from-invite.html', email_address=invited_user['email_address'], form=form)
|
||||
|
||||
|
||||
def _do_registration(form, service=None, send_email=True):
|
||||
if users_dao.is_email_unique(form.email_address.data):
|
||||
def _do_registration(form, service=None, send_sms=True, send_email=True):
|
||||
if user_api_client.is_email_unique(form.email_address.data):
|
||||
user = user_api_client.register_user(form.name.data,
|
||||
form.email_address.data,
|
||||
form.mobile_number.data,
|
||||
@@ -73,11 +75,19 @@ def _do_registration(form, service=None, send_email=True):
|
||||
# How do we report to the user there is a problem with
|
||||
# sending codes apart from service unavailable?
|
||||
# at the moment i believe http 500 is fine.
|
||||
users_dao.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
|
||||
if send_email:
|
||||
users_dao.send_verify_code(user.id, 'email', user.email_address)
|
||||
user_api_client.send_verify_email(user.id, user.email_address)
|
||||
|
||||
if send_sms:
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
session['expiry_date'] = str(datetime.now() + timedelta(hours=1))
|
||||
session['user_details'] = {"email": user.email_address, "id": user.id}
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
@main.route('/registration-continue')
|
||||
def registration_continue():
|
||||
return render_template('views/registration-continue.html')
|
||||
|
||||
@@ -3,15 +3,22 @@ from flask import (
|
||||
redirect,
|
||||
url_for,
|
||||
session,
|
||||
abort,
|
||||
flash,
|
||||
request
|
||||
)
|
||||
|
||||
from flask.ext.login import (current_user, login_fresh, confirm_login)
|
||||
from flask.ext.login import (
|
||||
current_user,
|
||||
login_fresh,
|
||||
confirm_login
|
||||
)
|
||||
|
||||
from app.main import main
|
||||
from app.main.dao import (users_dao, services_dao)
|
||||
from app.main.dao import services_dao
|
||||
|
||||
from app import user_api_client
|
||||
|
||||
|
||||
from app.main.forms import LoginForm
|
||||
|
||||
|
||||
@@ -22,7 +29,7 @@ def sign_in():
|
||||
|
||||
form = LoginForm()
|
||||
if form.validate_on_submit():
|
||||
user = users_dao.get_user_by_email(form.email_address.data)
|
||||
user = user_api_client.get_user_by_email(form.email_address.data)
|
||||
user = _get_and_verify_user(user, form.password.data)
|
||||
if user:
|
||||
# Remember me login
|
||||
@@ -41,7 +48,7 @@ def sign_in():
|
||||
if user.state == 'pending':
|
||||
return redirect(url_for('.verify'))
|
||||
elif user.is_active():
|
||||
users_dao.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
if request.args.get('next'):
|
||||
return redirect(url_for('.two_factor', next=request.args.get('next')))
|
||||
else:
|
||||
@@ -62,7 +69,7 @@ def _get_and_verify_user(user, password):
|
||||
return None
|
||||
elif user.is_locked():
|
||||
return None
|
||||
elif not users_dao.verify_password(user.id, password):
|
||||
elif not user_api_client.verify_password(user.id, password):
|
||||
return None
|
||||
else:
|
||||
return user
|
||||
|
||||
@@ -1,16 +1,35 @@
|
||||
from flask import (
|
||||
request, render_template, redirect, url_for, session)
|
||||
request,
|
||||
render_template,
|
||||
redirect,
|
||||
url_for,
|
||||
session
|
||||
)
|
||||
|
||||
from flask.ext.login import current_user
|
||||
from flask_login import login_required
|
||||
from app.main import main
|
||||
|
||||
from app.main.dao.users_dao import (
|
||||
verify_password, update_user, check_verify_code, is_email_unique,
|
||||
send_verify_code)
|
||||
from app.main.forms import (
|
||||
ChangePasswordForm, ChangeNameForm, ChangeEmailForm, ConfirmEmailForm,
|
||||
ChangeMobileNumberForm, ConfirmMobileNumberForm, ConfirmPasswordForm
|
||||
verify_password,
|
||||
update_user,
|
||||
check_verify_code,
|
||||
is_email_unique,
|
||||
send_verify_code
|
||||
)
|
||||
|
||||
from app.main.forms import (
|
||||
ChangePasswordForm,
|
||||
ChangeNameForm,
|
||||
ChangeEmailForm,
|
||||
ConfirmEmailForm,
|
||||
ChangeMobileNumberForm,
|
||||
ConfirmMobileNumberForm,
|
||||
ConfirmPasswordForm
|
||||
)
|
||||
|
||||
from app import user_api_client
|
||||
|
||||
NEW_EMAIL = 'new-email'
|
||||
NEW_MOBILE = 'new-mob'
|
||||
NEW_EMAIL_PASSWORD_CONFIRMED = 'new-email-password-confirmed'
|
||||
@@ -63,7 +82,6 @@ def user_profile_email():
|
||||
@main.route("/user-profile/email/authenticate", methods=['GET', 'POST'])
|
||||
@login_required
|
||||
def user_profile_email_authenticate():
|
||||
|
||||
# Validate password for form
|
||||
def _check_password(pwd):
|
||||
return verify_password(current_user.id, pwd)
|
||||
|
||||
+46
-16
@@ -1,42 +1,72 @@
|
||||
import json
|
||||
|
||||
from flask import (
|
||||
render_template,
|
||||
redirect,
|
||||
session,
|
||||
url_for
|
||||
url_for,
|
||||
current_app,
|
||||
flash
|
||||
)
|
||||
|
||||
from itsdangerous import SignatureExpired
|
||||
|
||||
from flask_login import login_user
|
||||
|
||||
from notifications_python_client.errors import HTTPError
|
||||
|
||||
from app.main import main
|
||||
from app.main.dao import users_dao
|
||||
from app.main.forms import (
|
||||
VerifyForm,
|
||||
VerifySmsForm
|
||||
)
|
||||
from app.main.forms import TwoFactorForm
|
||||
|
||||
from app import user_api_client
|
||||
|
||||
|
||||
@main.route('/verify', methods=['GET', 'POST'])
|
||||
def verify():
|
||||
|
||||
# TODO there needs to be a way to regenerate a session id
|
||||
# or handle gracefully.
|
||||
user_id = session['user_details']['id']
|
||||
|
||||
def _check_code(code, code_type):
|
||||
return users_dao.check_verify_code(user_id, code, code_type)
|
||||
def _check_code(code):
|
||||
return user_api_client.check_verify_code(user_id, code, 'sms')
|
||||
|
||||
if session.get('invited_user'):
|
||||
form = VerifySmsForm(_check_code)
|
||||
else:
|
||||
form = VerifyForm(_check_code)
|
||||
form = TwoFactorForm(_check_code)
|
||||
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
user = users_dao.get_user_by_id(user_id)
|
||||
activated_user = users_dao.activate_user(user)
|
||||
user = user_api_client.get_user(user_id)
|
||||
activated_user = user_api_client.activate_user(user)
|
||||
login_user(activated_user)
|
||||
return redirect(url_for('main.add_service', first='first'))
|
||||
finally:
|
||||
session.pop('user_details', None)
|
||||
|
||||
return render_template('views/verify.html', form=form)
|
||||
return render_template('views/two-factor.html', form=form)
|
||||
|
||||
|
||||
@main.route('/verify-email/<token>')
|
||||
def verify_email(token):
|
||||
from utils.url_safe_token import check_token
|
||||
try:
|
||||
token_data = check_token(token,
|
||||
current_app.config['SECRET_KEY'],
|
||||
current_app.config['DANGEROUS_SALT'],
|
||||
current_app.config['EMAIL_EXPIRY_SECONDS'])
|
||||
|
||||
token_data = json.loads(token_data)
|
||||
verified = user_api_client.check_verify_code(token_data['user_id'], token_data['secret_code'], 'email')
|
||||
|
||||
if verified[0]:
|
||||
user = user_api_client.get_user(token_data['user_id'])
|
||||
user_api_client.send_verify_code(user.id, 'sms', user.mobile_number)
|
||||
session['user_details'] = {"email": user.email_address, "id": user.id}
|
||||
return redirect('verify')
|
||||
else:
|
||||
message = "There was a problem verifying your account. Error message: '{}'".format(verified[1])
|
||||
flash(message)
|
||||
# TODO could this ask for a resend instead?
|
||||
return redirect(url_for('main.index'))
|
||||
|
||||
except SignatureExpired:
|
||||
flash('The link in the email we sent you has expired')
|
||||
return redirect(url_for('main.resend_email_verification'))
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
from notifications_python_client.base import BaseAPIClient
|
||||
|
||||
|
||||
class StatisticsApiClient(BaseAPIClient):
|
||||
def __init__(self, base_url=None, client_id=None, secret=None):
|
||||
super(self.__class__, self).__init__(base_url=base_url or 'base_url',
|
||||
client_id=client_id or 'client_id',
|
||||
secret=secret or 'secret')
|
||||
|
||||
def init_app(self, app):
|
||||
self.base_url = app.config['API_HOST_NAME']
|
||||
self.client_id = app.config['ADMIN_CLIENT_USER_NAME']
|
||||
self.secret = app.config['ADMIN_CLIENT_SECRET']
|
||||
|
||||
def get_statistics_for_service(self, service_id):
|
||||
return self.get(
|
||||
url='/service/{}/notifications-statistics'.format(service_id),
|
||||
)
|
||||
@@ -32,14 +32,7 @@ class UserApiClient(BaseAPIClient):
|
||||
return User(user_data['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
|
||||
def get_user_by_email(self, email_address):
|
||||
try:
|
||||
params = {'email': email_address}
|
||||
user_data = self.get('/user/email', params=params)
|
||||
except HTTPError as e:
|
||||
if e.status_code == 404:
|
||||
return None
|
||||
else:
|
||||
raise e
|
||||
user_data = self.get('/user/email', params={'email': email_address})
|
||||
return User(user_data['data'], max_failed_login_count=self.max_failed_login_count)
|
||||
|
||||
def get_users(self):
|
||||
@@ -68,7 +61,12 @@ class UserApiClient(BaseAPIClient):
|
||||
def send_verify_code(self, user_id, code_type, to):
|
||||
data = {'to': to}
|
||||
endpoint = '/user/{0}/{1}-code'.format(user_id, code_type)
|
||||
resp = self.post(endpoint, data=data)
|
||||
self.post(endpoint, data=data)
|
||||
|
||||
def send_verify_email(self, user_id, to):
|
||||
data = {'to': to}
|
||||
endpoint = '/user/{0}/email-verification'.format(user_id)
|
||||
self.post(endpoint, data=data)
|
||||
|
||||
def check_verify_code(self, user_id, code, code_type):
|
||||
data = {'code_type': code_type, 'code': code}
|
||||
@@ -106,3 +104,18 @@ class UserApiClient(BaseAPIClient):
|
||||
endpoint = '/user/reset-password'
|
||||
data = {'email': email_address}
|
||||
self.post(endpoint, data=data)
|
||||
|
||||
def is_email_unique(self, email_address):
|
||||
try:
|
||||
if self.get_user_by_email(email_address):
|
||||
return False
|
||||
return True
|
||||
except HTTPError as ex:
|
||||
if ex.status_code == 404:
|
||||
return True
|
||||
else:
|
||||
raise ex
|
||||
|
||||
def activate_user(self, user):
|
||||
user.state = 'active'
|
||||
return self.update_user(user)
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block cookie_message %}
|
||||
<p>
|
||||
GOV.UK Notify uses cookies to make the site simpler.
|
||||
<a href="{{ url_for("main.cookies") }}">Find out more about cookies</a>
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block inside_header %}
|
||||
@@ -68,6 +72,15 @@
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer_support_links %}
|
||||
<nav class="footer-nav">
|
||||
<a href="{{ url_for("main.help") }}">Help</a>
|
||||
<a href="https://docs.google.com/forms/d/1AL8U-xJX_HAFEiQiJszGQw0PcEaEUnYATSntEghNDGo/viewform">Support and feedback</a>
|
||||
<a href="{{ url_for("main.cookies") }}">Cookies</a>
|
||||
Built by the <a href="https://www.gov.uk/government/organisations/government-digital-service">Government Digital Service</a>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
||||
{% block body_end %}
|
||||
<script type="text/javascript" src="{{ asset_url('javascripts/all.js') }}" /></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
{% macro banner(body, type=None, with_tick=False, delete_button=None, subhead=None) %}
|
||||
<div class='banner{% if type %}-{{ type }}{% endif %}{% if with_tick %}-with-tick{% endif %}'>
|
||||
{% if subhead %}
|
||||
<div class="grid-row">
|
||||
<div class="column-one-third">
|
||||
{{ subhead }}
|
||||
</div>
|
||||
<div class="column-two-thirds">
|
||||
{% endif %}
|
||||
|
||||
{% if subhead -%}
|
||||
{{ subhead }} 
|
||||
{%- endif -%}
|
||||
{{ body }}
|
||||
|
||||
{% if subhead %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if delete_button %}
|
||||
<form method='post'>
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
|
||||
@@ -4,3 +4,14 @@
|
||||
<span class="big-number-label">{{ label }}</span>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
|
||||
{% macro big_number_with_status(number, label, status_number, status_label, percentage_bad=0) %}
|
||||
<div class="big-number-with-status">
|
||||
{{ big_number(number, label) }}
|
||||
<div class="big-number-status">
|
||||
<div class="big-number-status-error-percentage" style="opacity: {{ percentage_bad / 100 }}"></div>
|
||||
{{ big_number(status_number, status_label) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{% macro previous_next_navigation(previous_page, next_page) %}
|
||||
<nav class="govuk-previous-and-next-navigation" role="navigation" aria-label="Pagination">
|
||||
<ul class="group">
|
||||
{% if previous_page %}
|
||||
<li class="previous-page">
|
||||
<a href="{{previous_page['url']}}" rel="previous" >
|
||||
<span class="pagination-part-title">{{previous_page['title']}}</span>
|
||||
<span class="pagination-label">{{previous_page['label']}}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if next_page %}
|
||||
<li class="next-page">
|
||||
<a href="{{next_page['url']}}" rel="next">
|
||||
<span class="pagination-part-title">{{next_page['title']}}</span>
|
||||
<span class="pagination-label">{{next_page['label']}}</span>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</nav>
|
||||
{% endmacro %}
|
||||
@@ -4,7 +4,7 @@
|
||||
{% if name %}
|
||||
<h3 class="sms-message-name">
|
||||
{% if edit_link %}
|
||||
<a href="{{ edit_link }}">{{ name }}</a>
|
||||
<a name="{{ name|linkable_name }}" href="{{ edit_link }}">{{ name }}</a>
|
||||
{% else %}
|
||||
{{ name }}
|
||||
{% endif %}
|
||||
|
||||
@@ -2,29 +2,34 @@
|
||||
<h2 class="navigation-service-name">
|
||||
<a href="{{ url_for('.service_dashboard', service_id=service_id) }}">{{ session.get('service_name', 'Service') }}</a>
|
||||
</h2>
|
||||
{% if current_user.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters']) %}
|
||||
{% if current_user.has_permissions(['send_texts', 'send_emails', 'send_letters']) %}
|
||||
<ul>
|
||||
<li><a href="{{ url_for('.choose_template', service_id=service_id, template_type='sms') }}">Send text messages</a></li>
|
||||
<li><a href="{{ url_for('.choose_template', service_id=service_id, template_type='email') }}">Send emails</a></li>
|
||||
</ul>
|
||||
{% elif current_user.has_permissions(permissions=['manage_templates'], admin_override=True) %}
|
||||
{% elif current_user.has_permissions(['manage_templates']) %}
|
||||
<ul>
|
||||
<li><a href="{{ url_for('.choose_template', service_id=service_id, template_type='sms') }}">Text message templates</a></li>
|
||||
<li><a href="{{ url_for('.choose_template', service_id=service_id, template_type='email') }}">Email templates</a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if current_user.has_permissions(permissions=['manage_users', 'manage_settings'], admin_override=True) %}
|
||||
{% if current_user.has_permissions(['manage_users', 'manage_settings']) %}
|
||||
<ul>
|
||||
<li><a href="{{ url_for('.manage_users', service_id=service_id) }}">Manage team</a></li>
|
||||
<li><a href="{{ url_for('.service_settings', service_id=service_id) }}">Manage settings</a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if current_user.has_permissions(permissions=['manage_api_keys', 'access_developer_docs']) %}
|
||||
{% if current_user.has_permissions(['manage_api_keys', 'access_developer_docs']) %}
|
||||
<ul>
|
||||
<li><a href="{{ url_for('.api_keys', service_id=service_id) }}">API keys</a></li>
|
||||
<li><a href="{{ url_for('.documentation', service_id=service_id) }}">Developer documentation</a></li>
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
<ul>
|
||||
<li><a href="{{ url_for('.view_notifications', service_id=service_id) }}">Notification history</a></li>
|
||||
<li><a href="{{ url_for('.view_jobs', service_id=service_id) }}">Job history</a><li>
|
||||
</ul>
|
||||
{% if current_user.has_permissions(admin_override=True) %}
|
||||
<ul>
|
||||
<li><a href="{{ url_for('.show_all_services') }}"> List all services </a></li>
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
{% if current_user.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters'], or_=True) %}
|
||||
{{ banner(
|
||||
"""
|
||||
Send yourself a test message
|
||||
Send yourself a test
|
||||
""",
|
||||
subhead='Next step',
|
||||
subhead='Next step:',
|
||||
type="tip"
|
||||
)}}
|
||||
{% endif %}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
Cookies – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Cookies</h1>
|
||||
<p class="summary">
|
||||
GOV.UK Notify puts small files (known as ‘cookies’)
|
||||
on to your computer.
|
||||
</p>
|
||||
<p>These cookies are used to remember you once you’ve logged in</p>
|
||||
<p>
|
||||
Find out <a href="http://www.aboutcookies.org/">how to manage cookies</a>.
|
||||
</p>
|
||||
|
||||
<h2 class="heading-medium">Session cookies</h2>
|
||||
<p>
|
||||
We store session cookies on your computer to help keep your information
|
||||
secure while you use the service.
|
||||
</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Purpose</th>
|
||||
<th>Expires</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
notify_admin_session
|
||||
</td>
|
||||
<td width="50%">
|
||||
Used to keep you logged in
|
||||
</td>
|
||||
<td>
|
||||
1 hour
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2 class="heading-medium">Introductory message cookie</h2>
|
||||
<p>
|
||||
When you first use the service, you may see a pop-up ‘welcome’ message.
|
||||
Once you’ve seen the message, we store a cookie on your computer so it
|
||||
knows not to show it again.
|
||||
</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Purpose</th>
|
||||
<th>Expires</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
seen_cookie_message
|
||||
</td>
|
||||
<td width="50%">
|
||||
Saves a message to let us know that you have seen our cookie
|
||||
message
|
||||
</td>
|
||||
<td>
|
||||
1 month
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,20 @@
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
{{ session.get('service_name', 'Dashboard') }} – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
{% if service.restricted %}
|
||||
{% include 'views/dashboard/trial-mode-banner.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% if not jobs %}
|
||||
{% include 'views/dashboard/get-started.html' %}
|
||||
{% else %}
|
||||
{% include 'views/dashboard/today.html' %}
|
||||
{% include 'views/dashboard/jobs.html' %}
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,19 @@
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
|
||||
<h2 class="heading-medium">Get started</h2>
|
||||
<ol class="grid-row">
|
||||
{% if current_user.has_permissions(['manage_templates']) %}
|
||||
<li class="column-half">
|
||||
{% call banner_wrapper(type="tip", subhead='1.' if not templates else None, with_tick=templates|length) %}
|
||||
<a href='{{ url_for(".add_service_template", service_id=service_id, template_type="sms") }}'>Add a template</a>
|
||||
{% endcall %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if current_user.has_permissions(['send_texts', 'send_emails', 'send_letters']) %}
|
||||
<li class="column-half">
|
||||
{% call banner_wrapper(type="tip", subhead='2.') %}
|
||||
<a href='{{ url_for(".choose_template", service_id=service_id, template_type="sms") }}'>Send yourself a message</a>
|
||||
{% endcall %}
|
||||
</li>
|
||||
{% endif %}
|
||||
</ol>
|
||||
@@ -0,0 +1,28 @@
|
||||
{% from "components/table.html" import list_table, field, right_aligned_field_heading, hidden_field_heading %}
|
||||
|
||||
{% call(item) list_table(
|
||||
jobs,
|
||||
caption="Recent batch jobs",
|
||||
empty_message='You haven’t sent any text messages yet',
|
||||
field_headings=['File', 'Started', right_aligned_field_heading('Rows'), right_aligned_field_heading('Sent')]
|
||||
) %}
|
||||
{% call field() %}
|
||||
<a href="{{ url_for('.view_job', service_id=service_id, job_id=item.id) }}">{{ item.original_file_name }}</a>
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.created_at|format_datetime }}
|
||||
{% endcall %}
|
||||
{% call field(align='right') %}
|
||||
{{ item.notification_count }}
|
||||
{% endcall %}
|
||||
{% call field(align='right') %}
|
||||
{{ item.notifications_sent }}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{% if more_jobs_to_show %}
|
||||
{% if current_user.has_permissions(['send_texts', 'send_emails', 'send_letters']) %}
|
||||
<p class="table-show-more-link">
|
||||
<a href="{{ url_for('.view_jobs', service_id=service_id) }}">See all sent text messages</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,25 @@
|
||||
{% from "components/big-number.html" import big_number_with_status %}
|
||||
|
||||
<h2 class="heading-medium">
|
||||
Sent today
|
||||
</h2>
|
||||
<div class="grid-row">
|
||||
<div class="column-half">
|
||||
{{ big_number_with_status(
|
||||
statistics.get('emails_requested', 0),
|
||||
'email' if statistics.get('emails_requested') == 1 else 'emails',
|
||||
'{}%'.format(statistics.get('emails_failure_rate', 0)),
|
||||
'failed',
|
||||
statistics.get('emails_percentage_of_danger_zone', 0)
|
||||
) }}
|
||||
</div>
|
||||
<div class="column-half">
|
||||
{{ big_number_with_status(
|
||||
statistics.get('sms_requested', 0),
|
||||
'text message' if statistics.get('sms_requested') == 1 else 'text messages',
|
||||
'{}%'.format(statistics.get('sms_failure_rate', 0)),
|
||||
'failed',
|
||||
statistics.get('sms_percentage_of_danger_zone', 0)
|
||||
) }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,25 @@
|
||||
{% from "components/banner.html" import banner_wrapper %}
|
||||
{% from "components/big-number.html" import big_number %}
|
||||
|
||||
{% call banner_wrapper(type="mode") %}
|
||||
|
||||
<h2 class="heading-medium">Trial mode</h2>
|
||||
<div class="grid-row">
|
||||
<div class="column-one-half">
|
||||
<p>
|
||||
We’ll only deliver messages to you and members of your team
|
||||
<br>
|
||||
<a href="{{ url_for(".help", _anchor="trial-mode") }}">Find out more</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="column-one-sixth">
|
||||
|
||||
</div>
|
||||
<div class="column-one-third">
|
||||
{{ big_number(
|
||||
service.limit - statistics.get('emails_requested', 0) - statistics.get('sms_requested', 0),
|
||||
'messages left today'
|
||||
) }}
|
||||
</div>
|
||||
</div>
|
||||
{% endcall %}
|
||||
@@ -1,24 +0,0 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
{% from "components/textbox.html" import textbox %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
|
||||
{% block page_title %}
|
||||
Check your email address – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-large">Check your email address</h1>
|
||||
|
||||
<p>Check your email address is correct and then resend the confirmation code.</p>
|
||||
|
||||
<form autocomplete="off" method="post">
|
||||
{{ textbox(form.email_address, hint='Your email address must end in .gov.uk') }}
|
||||
{{ page_footer('Resend confirmation code') }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,79 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
Cookies – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Help</h1>
|
||||
|
||||
<h2 class="heading-large" id="trial-mode">Trial mode</h2>
|
||||
|
||||
<p>
|
||||
When you create a service on GOV.UK Notify, you start off in trial
|
||||
mode. This means:
|
||||
</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>
|
||||
you can only send messages to yourself and your team members
|
||||
</li>
|
||||
<li>
|
||||
you can only send 50 messages per day
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
Anyone working in central government can create an account on
|
||||
GOV.UK Notify and try it out. So it’s important that you start off
|
||||
with some restrictions in place.
|
||||
</p>
|
||||
|
||||
<h3 class="heading-medium">To remove these restrictions</h3>
|
||||
|
||||
<p>
|
||||
You need to request to go live:
|
||||
</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>
|
||||
You’ll need to agree to our terms of use
|
||||
</li>
|
||||
<li>
|
||||
If you plan to send more than 250,000 text messages per year, you’ll
|
||||
need to agree to pay for what you use – take a look at our pricing
|
||||
</li>
|
||||
<li>
|
||||
We’ll check your templates to make sure they’re consistent with our
|
||||
design patterns, style guide and information security principles
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h3 class="heading-medium">
|
||||
All other aspects of GOV.UK Notify are exactly the same
|
||||
</h2>
|
||||
|
||||
<p>
|
||||
You can still:
|
||||
</p>
|
||||
<ul class="list list-bullet">
|
||||
<li>
|
||||
upload a batch of recipients
|
||||
</li>
|
||||
<li>
|
||||
do an API integration
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
We’ll send the messages you have permission to send. The messages you
|
||||
don’t have permission to send will still be listed on your
|
||||
GOV.UK Notify dashboard but won’t actually get sent. This means you
|
||||
can check that everything is fully working without accidentally sending
|
||||
hundreds of text messages or emails.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,42 @@
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/table.html" import list_table, field, right_aligned_field_heading %}
|
||||
{% from "components/previous-next-navigation.html" import previous_next_navigation %}
|
||||
|
||||
{% block page_title %}
|
||||
Notifications activity – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<h1 class="heading-large">Notifications activity</h1>
|
||||
|
||||
{% call(item) list_table(
|
||||
notifications,
|
||||
caption="Recent activity",
|
||||
caption_visible=False,
|
||||
empty_message='You haven’t sent any notifications yet',
|
||||
field_headings=['Recipient', 'Template', 'Type', 'Job', 'Status', 'Time'])
|
||||
%}
|
||||
{% call field() %}
|
||||
{{ item.to }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
<a href="{{ url_for(".choose_template", service_id=service_id, template_type=item.template.template_type, _anchor=item.template.name|linkable_name) }}">{{ item.template.name }}</a>
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.template.template_type }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{% if item.job %}
|
||||
<a href="{{ url_for(".view_job", service_id=service_id, job_id=item.job.id) }}">{{ item.job.original_file_name }}</a>
|
||||
{% endif %}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.status }}
|
||||
{% endcall %}
|
||||
{% call field() %}
|
||||
{{ item.created_at | format_datetime}}
|
||||
{% endcall %}
|
||||
{% endcall %}
|
||||
{{ previous_next_navigation(prev_page, next_page) }}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,16 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
Check your email – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-large">Now check your email</h1>
|
||||
<p>Click the link in the email we’ve sent you to continue your registration</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,18 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
{% from "components/textbox.html" import textbox %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
|
||||
{% block page_title %}
|
||||
Check your email – GOV.UK Notify
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-large">Check your email</h1>
|
||||
<p>In order to verify your email address we've sent a new confirmation link to {{email}}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
@@ -65,7 +65,7 @@
|
||||
{% if more_jobs_to_show %}
|
||||
{% if current_user.has_permissions(permissions=['send_texts', 'send_emails', 'send_letters']) %}
|
||||
<p class="table-show-more-link">
|
||||
<a href="{{ url_for('.view_jobs', service_id=service_id) }}">See all sent text messages</a>
|
||||
<a href="{{ url_for('.view_notifications', service_id=service_id) }}">See all sent text messages</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
{% extends "withoutnav_template.html" %}
|
||||
{% from "components/textbox.html" import textbox %}
|
||||
{% from "components/page-footer.html" import page_footer %}
|
||||
|
||||
{% block page_title %}
|
||||
{% if form.email_code %}
|
||||
Enter the codes we’ve sent you – GOV.UK Notify
|
||||
{% else %}
|
||||
Enter the code we’ve sent you – GOV.UK Notify
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
{% if form.email_code %}
|
||||
<h1 class="heading-large">Enter the codes we’ve sent you</h1>
|
||||
{% else %}
|
||||
<h1 class="heading-large">Enter the code we’ve sent you</h1>
|
||||
{% endif %}
|
||||
{% if form.email_code %}
|
||||
<p>
|
||||
We’ve sent you confirmation codes by email and text message.
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
We’ve sent you a confirmation code by text message.
|
||||
</p>
|
||||
{% endif %}
|
||||
<form autocomplete="off" method="post">
|
||||
{% if form.sms_code %}
|
||||
{{ textbox(
|
||||
form.sms_code,
|
||||
width='5em',
|
||||
help_link=url_for('.check_and_resend_text_code'),
|
||||
help_link_text='I haven’t received a text message'
|
||||
) }}
|
||||
{% endif %}
|
||||
{% if form.email_code %}
|
||||
{{ textbox(
|
||||
form.email_code,
|
||||
width='5em',
|
||||
help_link=url_for('.check_and_resend_email_code'),
|
||||
help_link_text='I haven’t received an email'
|
||||
) }}
|
||||
{% endif %}
|
||||
{{ page_footer("Continue") }}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
+19
-1
@@ -1,7 +1,7 @@
|
||||
import re
|
||||
|
||||
from functools import wraps
|
||||
from flask import (abort, session)
|
||||
from flask import (abort, session, request, url_for)
|
||||
|
||||
|
||||
class BrowsableItem(object):
|
||||
@@ -83,3 +83,21 @@ def get_errors_for_csv(recipients, template_type):
|
||||
errors.append("fill in {} empty cells".format(number_of_rows_with_missing_data))
|
||||
|
||||
return errors
|
||||
|
||||
|
||||
def get_page_from_request():
|
||||
if 'page' in request.args:
|
||||
try:
|
||||
return int(request.args['page'])
|
||||
except ValueError:
|
||||
return None
|
||||
else:
|
||||
return 1
|
||||
|
||||
|
||||
def generate_previous_next_dict(view, view_dict, page, title, label):
|
||||
return {
|
||||
'url': url_for(view, page=page, **view_dict),
|
||||
'title': title,
|
||||
'label': label
|
||||
}
|
||||
|
||||
@@ -28,17 +28,16 @@ class Config(object):
|
||||
REMEMBER_COOKIE_SECURE = True
|
||||
|
||||
API_HOST_NAME = os.getenv('API_HOST_NAME')
|
||||
NOTIFY_API_SECRET = os.getenv('NOTIFY_API_SECRET', "dev-secret")
|
||||
NOTIFY_API_CLIENT = os.getenv('NOTIFY_API_CLIENT', "admin")
|
||||
|
||||
ADMIN_CLIENT_USER_NAME = os.getenv('ADMIN_CLIENT_USER_NAME')
|
||||
ADMIN_CLIENT_SECRET = os.getenv('ADMIN_CLIENT_SECRET')
|
||||
|
||||
WTF_CSRF_ENABLED = True
|
||||
SECRET_KEY = 'secret-key'
|
||||
SECRET_KEY = 'dev-notify-secret-key'
|
||||
HTTP_PROTOCOL = 'http'
|
||||
DANGEROUS_SALT = 'itsdangeroussalt'
|
||||
DANGEROUS_SALT = 'dev-notify-salt'
|
||||
TOKEN_MAX_AGE_SECONDS = 3600
|
||||
EMAIL_EXPIRY_SECONDS = TOKEN_MAX_AGE_SECONDS * 24 * 7 # one week
|
||||
|
||||
DEFAULT_SERVICE_LIMIT = 50
|
||||
|
||||
@@ -63,6 +62,7 @@ class Development(Config):
|
||||
|
||||
|
||||
class Test(Development):
|
||||
DEBUG = True
|
||||
WTF_CSRF_ENABLED = False
|
||||
|
||||
|
||||
@@ -72,18 +72,10 @@ class Preview(Config):
|
||||
HEADER_COLOUR = '#F47738' # $orange
|
||||
|
||||
|
||||
class Staging(Preview):
|
||||
SHOW_STYLEGUIDE = False
|
||||
|
||||
|
||||
class Live(Staging):
|
||||
HEADER_COLOUR = '#B10E1E' # $red
|
||||
|
||||
|
||||
configs = {
|
||||
'development': Development,
|
||||
'test': Test,
|
||||
'preview': Preview,
|
||||
'staging': Staging,
|
||||
'live': Live
|
||||
'development': 'config.Development',
|
||||
'test': 'config.Test',
|
||||
'preview': 'config.Preview',
|
||||
'staging': 'config_staging.Staging',
|
||||
'live': 'config_live.Live'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import os
|
||||
from config import Config
|
||||
|
||||
|
||||
class Live(Config):
|
||||
SHOW_STYLEGUIDE = False
|
||||
HEADER_COLOUR = '#B10E1E' # $red
|
||||
HTTP_PROTOCOL = 'https'
|
||||
API_HOST_NAME = os.getenv('LIVE_API_HOST_NAME')
|
||||
ADMIN_CLIENT_SECRET = os.getenv('LIVE_ADMIN_CLIENT_SECRET')
|
||||
SECRET_KEY = os.getenv('LIVE_SECRET_KEY')
|
||||
DANGEROUS_SALT = os.getenv('LIVE_DANGEROUS_SALT')
|
||||
@@ -0,0 +1,11 @@
|
||||
import os
|
||||
from config import Config
|
||||
|
||||
|
||||
class Staging(Config):
|
||||
SHOW_STYLEGUIDE = False
|
||||
HTTP_PROTOCOL = 'https'
|
||||
API_HOST_NAME = os.getenv('STAGING_API_HOST_NAME')
|
||||
ADMIN_CLIENT_SECRET = os.getenv('STAGING_ADMIN_CLIENT_SECRET')
|
||||
SECRET_KEY = os.getenv('STAGING_SECRET_KEY')
|
||||
DANGEROUS_SALT = os.getenv('STAGING_DANGEROUS_SALT')
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/bin/bash
|
||||
|
||||
export NOTIFY_ADMIN_ENVIRONMENT='config.Development'
|
||||
python3 app.py runserver
|
||||
|
||||
@@ -32,5 +32,6 @@ display_result $? 2 "Front end code style check"
|
||||
#py.test --cov=app tests/
|
||||
#display_result $? 3 "Code coverage"
|
||||
|
||||
export NOTIFY_ADMIN_ENVIRONMENT='config.Test'
|
||||
py.test -v
|
||||
display_result $? 4 "Unit tests"
|
||||
|
||||
+31
-4
@@ -31,7 +31,7 @@ def service_json(id_, name, users, limit=1000, active=False, restricted=True):
|
||||
}
|
||||
|
||||
|
||||
def template_json(id_, name, type_, content, service_id):
|
||||
def template_json(service_id, id_=1, name="sample template", type_="sms", content="template content"):
|
||||
return {
|
||||
'id': id_,
|
||||
'name': name,
|
||||
@@ -116,13 +116,40 @@ def job_json():
|
||||
return data
|
||||
|
||||
|
||||
def notification_json():
|
||||
def notification_json(service_id,
|
||||
job=None,
|
||||
template=None,
|
||||
to='07123456789',
|
||||
status='sent',
|
||||
sent_at=None,
|
||||
created_at=None,
|
||||
with_links=False):
|
||||
import datetime
|
||||
if job is None:
|
||||
job = job_json()
|
||||
if template is None:
|
||||
template = template_json(service_id)
|
||||
if sent_at is None:
|
||||
sent_at = str(datetime.datetime.now().time())
|
||||
if created_at is None:
|
||||
created_at = str(datetime.datetime.now().time())
|
||||
links = {}
|
||||
if with_links:
|
||||
links = {
|
||||
'prev': '/service/{}/notifications'.format(service_id),
|
||||
'next': '/service/{}/notifications'.format(service_id),
|
||||
'last': '/service/{}/notifications'.format(service_id)
|
||||
}
|
||||
data = {
|
||||
'notifications': [{
|
||||
'sent_at': str(datetime.datetime.now().time())
|
||||
'to': to,
|
||||
'template': {'id': template['id'], 'name': template['name']},
|
||||
'job': {'id': job['id'], 'original_file_name': job['original_file_name']},
|
||||
'sent_at': sent_at,
|
||||
'status': status,
|
||||
'created_at': created_at
|
||||
} for i in range(5)],
|
||||
'links': {}
|
||||
'links': links
|
||||
}
|
||||
return data
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
from app.notify_client.statistics_api_client import StatisticsApiClient
|
||||
|
||||
|
||||
def test_client_uses_correct_find_by_email(mocker, api_user_active):
|
||||
|
||||
expected_url = '/service/a1b2c3d4/notifications-statistics'
|
||||
|
||||
client = StatisticsApiClient()
|
||||
mock_get = mocker.patch('app.notify_client.statistics_api_client.StatisticsApiClient.get')
|
||||
|
||||
client.get_statistics_for_service('a1b2c3d4')
|
||||
|
||||
mock_get.assert_called_once_with(url=expected_url)
|
||||
@@ -1,91 +0,0 @@
|
||||
from datetime import datetime, timedelta
|
||||
from app.main.forms import VerifyForm
|
||||
from app.main.dao import users_dao
|
||||
from tests import create_test_user
|
||||
|
||||
|
||||
def test_form_should_have_error_when_code_is_not_valid(app_,
|
||||
mock_check_verify_code):
|
||||
with app_.test_request_context(method='POST',
|
||||
data={'sms_code': '12345aa', 'email_code': 'abcde'}) as req:
|
||||
|
||||
def _check_code(code, code_type):
|
||||
return users_dao.check_verify_code('1', code, code_type)
|
||||
|
||||
form = VerifyForm(_check_code)
|
||||
assert form.validate() is False
|
||||
errors = form.errors
|
||||
assert len(errors) == 2
|
||||
expected = {'email_code': ['Code must be 5 digits', 'Code does not match'],
|
||||
'sms_code': ['Code does not match', 'Code must be 5 digits']}
|
||||
assert 'sms_code' in errors
|
||||
assert set(errors) == set(expected)
|
||||
|
||||
|
||||
def test_should_return_errors_when_code_missing(app_,
|
||||
mock_check_verify_code):
|
||||
with app_.test_request_context(method='POST',
|
||||
data={}) as req:
|
||||
|
||||
def _check_code(code, code_type):
|
||||
return users_dao.check_verify_code('1', code, code_type)
|
||||
|
||||
form = VerifyForm(_check_code)
|
||||
assert form.validate() is False
|
||||
errors = form.errors
|
||||
expected = {'sms_code': ['SMS code can not be empty'],
|
||||
'email_code': ['Email code can not be empty']}
|
||||
assert len(errors) == 2
|
||||
assert set(errors) == set(expected)
|
||||
|
||||
|
||||
def test_should_return_errors_when_code_is_too_short(app_,
|
||||
mock_check_verify_code):
|
||||
with app_.test_request_context(method='POST',
|
||||
data={'sms_code': '123', 'email_code': '123'}) as req:
|
||||
|
||||
def _check_code(code, code_type):
|
||||
return users_dao.check_verify_code('1', code, code_type)
|
||||
|
||||
form = VerifyForm(_check_code)
|
||||
assert form.validate() is False
|
||||
errors = form.errors
|
||||
expected = {'sms_code': ['Code must be 5 digits', 'Code does not match'],
|
||||
'email_code': ['Code must be 5 digits', 'Code does not match']}
|
||||
assert len(errors) == 2
|
||||
assert set(errors) == set(expected)
|
||||
|
||||
|
||||
def test_should_return_errors_when_code_does_not_match(app_,
|
||||
mock_check_verify_code_code_not_found):
|
||||
with app_.test_request_context(method='POST',
|
||||
data={'sms_code': '34567', 'email_code': '34567'}) as req:
|
||||
|
||||
def _check_code(code, code_type):
|
||||
return users_dao.check_verify_code('1', code, code_type)
|
||||
|
||||
form = VerifyForm(_check_code)
|
||||
assert form.validate() is False
|
||||
errors = form.errors
|
||||
expected = {'sms_code': ['Code not found'],
|
||||
'email_code': ['Code not found']}
|
||||
assert len(errors) == 2
|
||||
assert set(errors) == set(expected)
|
||||
|
||||
|
||||
def test_should_return_errors_when_code_is_expired(app_,
|
||||
mock_check_verify_code_code_expired):
|
||||
with app_.test_request_context(method='POST',
|
||||
data={'sms_code': '23456',
|
||||
'email_code': '23456'}) as req:
|
||||
|
||||
def _check_code(code, code_type):
|
||||
return users_dao.check_verify_code('1', code, code_type)
|
||||
|
||||
form = VerifyForm(_check_code)
|
||||
assert form.validate() is False
|
||||
errors = form.errors
|
||||
expected = {'sms_code': ['Code has expired'],
|
||||
'email_code': ['Code has expired']}
|
||||
assert len(errors) == 2
|
||||
assert set(errors) == set(expected)
|
||||
@@ -175,7 +175,7 @@ def test_new_user_accept_invite_calls_api_and_views_registration_page(app_,
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
assert page.h1.string.strip() == 'Create an account'
|
||||
|
||||
email_in_page = page.find('p')
|
||||
email_in_page = page.find('main').find('p')
|
||||
assert email_in_page.text.strip() == 'Your account will be created with this email: invited_user@test.gov.uk' # noqa
|
||||
|
||||
form = page.find('form')
|
||||
@@ -215,6 +215,7 @@ def test_new_user_accept_invite_completes_new_registration_redirects_to_verify(a
|
||||
api_user_active,
|
||||
mock_check_invite_token,
|
||||
mock_dont_get_user_by_email,
|
||||
mock_is_email_unique,
|
||||
mock_register_user,
|
||||
mock_send_verify_code,
|
||||
mock_get_users_by_service,
|
||||
@@ -266,6 +267,7 @@ def test_new_invited_user_verifies_and_added_to_service(app_,
|
||||
api_user_active,
|
||||
mock_check_invite_token,
|
||||
mock_dont_get_user_by_email,
|
||||
mock_is_email_unique,
|
||||
mock_register_user,
|
||||
mock_send_verify_code,
|
||||
mock_check_verify_code,
|
||||
@@ -275,6 +277,7 @@ def test_new_invited_user_verifies_and_added_to_service(app_,
|
||||
mock_accept_invite,
|
||||
mock_get_service,
|
||||
mock_get_service_templates,
|
||||
mock_get_service_statistics,
|
||||
mock_get_jobs):
|
||||
|
||||
with app_.test_request_context():
|
||||
@@ -298,6 +301,7 @@ def test_new_invited_user_verifies_and_added_to_service(app_,
|
||||
# when they post codes back to admin user should be added to
|
||||
# service and sent on to dash board
|
||||
expected_permissions = ['send_messages', 'manage_service', 'manage_api_keys']
|
||||
|
||||
with client.session_transaction() as session:
|
||||
new_user_id = session['user_id']
|
||||
mock_add_user_to_service.assert_called_with(data['service'], new_user_id, expected_permissions)
|
||||
|
||||
@@ -1,39 +1,32 @@
|
||||
import pytest
|
||||
|
||||
from flask import url_for
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
def test_should_render_email_verification_resent_show_email_address_and_resend_verify_email(app_,
|
||||
mocker,
|
||||
api_user_active,
|
||||
mock_get_user_by_email,
|
||||
mock_send_verify_email):
|
||||
|
||||
def test_should_render_email_code_not_received_template_and_populate_email_address(app_,
|
||||
api_user_active,
|
||||
mock_get_user_by_email,
|
||||
mock_send_verify_code):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
with client.session_transaction() as session:
|
||||
session['user_details'] = {
|
||||
'id': api_user_active.id,
|
||||
'email': api_user_active.email_address}
|
||||
response = client.get(url_for('main.check_and_resend_email_code'))
|
||||
response = client.get(url_for('main.resend_email_verification'))
|
||||
assert response.status_code == 200
|
||||
assert 'Check your email address is correct and then resend the confirmation code' \
|
||||
in response.get_data(as_text=True)
|
||||
assert 'value="test@user.gov.uk"' in response.get_data(as_text=True)
|
||||
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
|
||||
def test_should_check_and_resend_email_code_redirect_to_verify(app_,
|
||||
api_user_active,
|
||||
mock_get_user_by_email,
|
||||
mock_update_user,
|
||||
mock_send_verify_code):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
with client.session_transaction() as session:
|
||||
session['user_details'] = {
|
||||
'id': api_user_active.id,
|
||||
'email': api_user_active.email_address}
|
||||
response = client.post(url_for('main.check_and_resend_email_code'),
|
||||
data={'email_address': 'test@user.gov.uk'})
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('main.verify', _external=True)
|
||||
assert page.h1.string == 'Check your email'
|
||||
expected = "In order to verify your email address we've sent a new confirmation link to {}".format(api_user_active.email_address) # noqa
|
||||
|
||||
message = page.find_all('p')[1].text
|
||||
assert message == expected
|
||||
mock_send_verify_email.assert_called_with(api_user_active.id, api_user_active.email_address)
|
||||
|
||||
|
||||
def test_should_render_text_code_not_received_template(app_,
|
||||
@@ -70,23 +63,6 @@ def test_should_check_and_redirect_to_verify(app_,
|
||||
assert response.location == url_for('main.verify', _external=True)
|
||||
|
||||
|
||||
def test_should_update_email_address_resend_code(app_,
|
||||
api_user_active,
|
||||
mock_get_user_by_email,
|
||||
mock_update_user,
|
||||
mock_send_verify_code):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
with client.session_transaction() as session:
|
||||
session['user_details'] = {
|
||||
'id': api_user_active.id,
|
||||
'email': api_user_active.email_address}
|
||||
response = client.post(url_for('main.check_and_resend_email_code'),
|
||||
data={'email_address': 'new@address.gov.uk'})
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('main.verify', _external=True)
|
||||
|
||||
|
||||
def test_should_update_mobile_number_resend_code(app_,
|
||||
api_user_active,
|
||||
mock_get_user_by_email,
|
||||
|
||||
@@ -5,6 +5,7 @@ def test_should_show_recent_jobs_on_dashboard(app_,
|
||||
api_user_active,
|
||||
mock_get_service,
|
||||
mock_get_service_templates,
|
||||
mock_get_service_statistics,
|
||||
mock_get_user,
|
||||
mock_get_user_by_email,
|
||||
mock_login,
|
||||
@@ -33,6 +34,7 @@ def _test_dashboard_menu(mocker, app_, usr, service, permissions):
|
||||
mocker.patch('app.user_api_client.get_user', return_value=usr)
|
||||
mocker.patch('app.user_api_client.get_user_by_email', return_value=usr)
|
||||
mocker.patch('app.service_api_client.get_service', return_value={'data': service})
|
||||
mocker.patch('app.statistics_api_client.get_statistics_for_service', return_value={'data': [{}]})
|
||||
client.login(usr)
|
||||
return client.get(url_for('main.service_dashboard', service_id=service['id']))
|
||||
|
||||
|
||||
@@ -80,3 +80,45 @@ def test_should_show_updates_for_one_job_as_json(
|
||||
assert 'Recipient' in content['notifications']
|
||||
assert 'Status' in content['notifications']
|
||||
assert 'Started' in content['status']
|
||||
|
||||
|
||||
def test_should_show_notifications_for_a_service(app_,
|
||||
service_one,
|
||||
api_user_active,
|
||||
mock_login,
|
||||
mock_get_user,
|
||||
mock_get_user_by_email,
|
||||
mock_get_service,
|
||||
mock_get_notifications):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
client.login(api_user_active)
|
||||
response = client.get(url_for('main.view_notifications', service_id=service_one['id']))
|
||||
assert response.status_code == 200
|
||||
content = response.get_data(as_text=True)
|
||||
notifications = mock_get_notifications(service_one['id'])
|
||||
notification = notifications['notifications'][0]
|
||||
assert notification['to'] in content
|
||||
assert notification['status'] in content
|
||||
assert notification['template']['name'] in content
|
||||
assert '.csv' in content
|
||||
|
||||
|
||||
def test_should_show_notifications_for_a_service_with_next_previous(app_,
|
||||
service_one,
|
||||
api_user_active,
|
||||
mock_login,
|
||||
mock_get_user,
|
||||
mock_get_user_by_email,
|
||||
mock_get_service,
|
||||
mock_get_notifications_with_previous_next):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
client.login(api_user_active)
|
||||
response = client.get(url_for('main.view_notifications', service_id=service_one['id'], page=2))
|
||||
assert response.status_code == 200
|
||||
content = response.get_data(as_text=True)
|
||||
assert url_for('main.view_notifications', service_id=service_one['id'], page=3) in content
|
||||
assert url_for('main.view_notifications', service_id=service_one['id'], page=1) in content
|
||||
assert 'Previous page' in content
|
||||
assert 'Next page' in content
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
from flask import url_for
|
||||
from flask import (
|
||||
url_for,
|
||||
session
|
||||
)
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
@@ -24,24 +28,31 @@ def test_logged_in_user_redirects_to_choose_service(app_,
|
||||
assert response.location == url_for('main.choose_service', _external=True)
|
||||
|
||||
|
||||
def test_process_register_creates_new_user(app_,
|
||||
mock_send_verify_code,
|
||||
mock_register_user,
|
||||
mock_get_user_by_email_not_found,
|
||||
mock_login):
|
||||
user_data = {
|
||||
'name': 'Some One Valid',
|
||||
'email_address': 'notfound@example.gov.uk',
|
||||
'mobile_number': '+4407700900460',
|
||||
'password': 'validPassword!'
|
||||
}
|
||||
def test_register_creates_new_user_and_redirects_to_continue_page(app_,
|
||||
mock_send_verify_code,
|
||||
mock_register_user,
|
||||
mock_get_user_by_email_not_found,
|
||||
mock_is_email_unique,
|
||||
mock_send_verify_email,
|
||||
mock_login):
|
||||
|
||||
user_data = {'name': 'Some One Valid',
|
||||
'email_address': 'notfound@example.gov.uk',
|
||||
'mobile_number': '+4407700900460',
|
||||
'password': 'validPassword!'
|
||||
}
|
||||
|
||||
with app_.test_request_context():
|
||||
response = app_.test_client().post(url_for('main.register'),
|
||||
data=user_data)
|
||||
response = app_.test_client().post(url_for('main.register'), data=user_data)
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('main.verify', _external=True)
|
||||
assert mock_register_user.called
|
||||
assert response.location == url_for('main.registration_continue', _external=True)
|
||||
|
||||
from unittest.mock import ANY
|
||||
mock_send_verify_email.assert_called_with(ANY, user_data['email_address'])
|
||||
mock_register_user.assert_called_with(user_data['name'],
|
||||
user_data['email_address'],
|
||||
user_data['mobile_number'],
|
||||
user_data['password'])
|
||||
|
||||
|
||||
def test_process_register_returns_200_when_mobile_number_is_invalid(app_,
|
||||
@@ -59,7 +70,7 @@ def test_process_register_returns_200_when_mobile_number_is_invalid(app_,
|
||||
assert 'Must not contain letters or symbols' in response.get_data(as_text=True)
|
||||
|
||||
|
||||
def test_should_return_400_when_email_is_not_gov_uk(app_,
|
||||
def test_should_return_200_when_email_is_not_gov_uk(app_,
|
||||
mock_send_verify_code,
|
||||
mock_get_user_by_email,
|
||||
mock_login):
|
||||
@@ -74,11 +85,13 @@ def test_should_return_400_when_email_is_not_gov_uk(app_,
|
||||
assert 'Enter a gov.uk email address' in response.get_data(as_text=True)
|
||||
|
||||
|
||||
def test_should_add_verify_codes_on_session(app_,
|
||||
def test_should_add_user_details_to_session(app_,
|
||||
mock_send_verify_code,
|
||||
mock_register_user,
|
||||
mock_get_user,
|
||||
mock_get_user_by_email_not_found,
|
||||
mock_is_email_unique,
|
||||
mock_send_verify_email,
|
||||
mock_login):
|
||||
user_data = {
|
||||
'name': 'Test Codes',
|
||||
@@ -91,11 +104,12 @@ def test_should_add_verify_codes_on_session(app_,
|
||||
with app_.test_client() as client:
|
||||
response = client.post(url_for('main.register'),
|
||||
data=user_data)
|
||||
|
||||
assert response.status_code == 302
|
||||
assert 'notify_admin_session' in response.headers.get('Set-Cookie')
|
||||
assert session['user_details']['email'] == user_data['email_address']
|
||||
|
||||
|
||||
def test_should_return_400_if_password_is_blacklisted(app_,
|
||||
def test_should_return_200_if_password_is_blacklisted(app_,
|
||||
mock_get_user_by_email,
|
||||
mock_login):
|
||||
with app_.test_request_context():
|
||||
|
||||
@@ -16,6 +16,7 @@ def test_sign_out_user(app_,
|
||||
mock_get_user,
|
||||
mock_get_user_by_email,
|
||||
mock_get_service_templates,
|
||||
mock_get_service_statistics,
|
||||
mock_login,
|
||||
mock_get_jobs,
|
||||
mock_has_permissions):
|
||||
|
||||
@@ -2,105 +2,109 @@ import json
|
||||
from flask import url_for
|
||||
|
||||
|
||||
def test_should_show_overview_page(app_,
|
||||
api_user_active,
|
||||
mock_login,
|
||||
mock_get_user):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
client.login(api_user_active)
|
||||
response = client.get(url_for('main.user_profile'))
|
||||
# def test_should_show_overview_page(app_,
|
||||
# api_user_active,
|
||||
# mock_login,
|
||||
# mock_get_user):
|
||||
# with app_.test_request_context():
|
||||
# with app_.test_client() as client:
|
||||
# client.login(api_user_active)
|
||||
# response = client.get(url_for('main.user_profile'))
|
||||
|
||||
assert 'Your profile' in response.get_data(as_text=True)
|
||||
assert response.status_code == 200
|
||||
# assert 'Your profile' in response.get_data(as_text=True)
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
def test_should_show_name_page(app_,
|
||||
api_user_active,
|
||||
mock_login,
|
||||
mock_get_user):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
client.login(api_user_active)
|
||||
response = client.get(url_for('main.user_profile_name'))
|
||||
# def test_should_show_name_page(app_,
|
||||
# api_user_active,
|
||||
# mock_login,
|
||||
# mock_get_user):
|
||||
# with app_.test_request_context():
|
||||
# with app_.test_client() as client:
|
||||
# client.login(api_user_active)
|
||||
# response = client.get(url_for('main.user_profile_name'))
|
||||
|
||||
assert 'Change your name' in response.get_data(as_text=True)
|
||||
assert response.status_code == 200
|
||||
# assert 'Change your name' in response.get_data(as_text=True)
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
def test_should_redirect_after_name_change(app_,
|
||||
api_user_active,
|
||||
mock_login,
|
||||
mock_update_user,
|
||||
mock_get_user):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
client.login(api_user_active)
|
||||
new_name = 'New Name'
|
||||
data = {'new_name': new_name}
|
||||
response = client.post(url_for(
|
||||
'main.user_profile_name'), data=data)
|
||||
# def test_should_redirect_after_name_change(app_,
|
||||
# api_user_active,
|
||||
# mock_login,
|
||||
# mock_update_user,
|
||||
# mock_get_user):
|
||||
# with app_.test_request_context():
|
||||
# with app_.test_client() as client:
|
||||
# client.login(api_user_active)
|
||||
# new_name = 'New Name'
|
||||
# data = {'new_name': new_name}
|
||||
# response = client.post(url_for(
|
||||
# 'main.user_profile_name'), data=data)
|
||||
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for(
|
||||
'main.user_profile', _external=True)
|
||||
api_user_active.name = new_name
|
||||
assert mock_update_user.called
|
||||
# assert response.status_code == 302
|
||||
# assert response.location == url_for(
|
||||
# 'main.user_profile', _external=True)
|
||||
# api_user_active.name = new_name
|
||||
# assert mock_update_user.called
|
||||
|
||||
|
||||
def test_should_show_email_page(app_,
|
||||
api_user_active,
|
||||
mock_login,
|
||||
mock_get_user):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
client.login(api_user_active)
|
||||
response = client.get(url_for(
|
||||
'main.user_profile_email'))
|
||||
# def test_should_show_email_page(app_,
|
||||
# api_user_active,
|
||||
# mock_login,
|
||||
# mock_get_user):
|
||||
# with app_.test_request_context():
|
||||
# with app_.test_client() as client:
|
||||
# client.login(api_user_active)
|
||||
# response = client.get(url_for(
|
||||
# 'main.user_profile_email'))
|
||||
|
||||
assert 'Change your email address' in response.get_data(as_text=True)
|
||||
assert response.status_code == 200
|
||||
# assert 'Change your email address' in response.get_data(as_text=True)
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
def test_should_redirect_after_email_change(app_,
|
||||
api_user_active,
|
||||
mock_login,
|
||||
mock_get_user,
|
||||
mock_get_user_by_email_not_found):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
client.login(api_user_active)
|
||||
data = {'email_address': 'new_notify@notify.gov.uk'}
|
||||
response = client.post(
|
||||
url_for('main.user_profile_email'),
|
||||
data=data)
|
||||
# def test_should_redirect_after_email_change(app_,
|
||||
# api_user_active,
|
||||
# mock_login,
|
||||
# mock_get_user,
|
||||
# mock_get_user_by_email_not_found,
|
||||
# mock_is_email_unique):
|
||||
# with app_.test_request_context():
|
||||
# with app_.test_client() as client:
|
||||
# client.login(api_user_active)
|
||||
# data = {'email_address': 'new_notify@notify.gov.uk'}
|
||||
# response = client.post(
|
||||
# url_for('main.user_profile_email'),
|
||||
# data=data)
|
||||
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for(
|
||||
'main.user_profile_email_authenticate', _external=True)
|
||||
# assert response.status_code == 302
|
||||
# assert response.location == url_for(
|
||||
# 'main.user_profile_email_authenticate', _external=True)
|
||||
|
||||
|
||||
def test_should_show_authenticate_after_email_change(app_,
|
||||
api_user_active,
|
||||
mock_login,
|
||||
mock_get_user,
|
||||
mock_verify_password):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
client.login(api_user_active)
|
||||
with client.session_transaction() as session:
|
||||
session['new-email'] = 'new_notify@notify.gov.uk'
|
||||
response = client.get(url_for('main.user_profile_email_authenticate'))
|
||||
# def test_should_show_authenticate_after_email_change(app_,
|
||||
# api_user_active,
|
||||
# mock_login,
|
||||
# mock_get_user,
|
||||
# mock_verify_password):
|
||||
# with app_.test_request_context():
|
||||
# with app_.test_client() as client:
|
||||
# client.login(api_user_active)
|
||||
# with client.session_transaction() as session:
|
||||
# session['new-email'] = 'new_notify@notify.gov.uk'
|
||||
# response = client.get(url_for('main.user_profile_email_authenticate'))
|
||||
|
||||
assert 'Change your email address' in response.get_data(as_text=True)
|
||||
assert 'Confirm' in response.get_data(as_text=True)
|
||||
assert response.status_code == 200
|
||||
# assert 'Change your email address' in response.get_data(as_text=True)
|
||||
# assert 'Confirm' in response.get_data(as_text=True)
|
||||
# assert response.status_code == 200
|
||||
|
||||
|
||||
def test_should_redirect_after_email_change_confirm(app_,
|
||||
api_user_active,
|
||||
mock_login,
|
||||
mock_get_user):
|
||||
mock_get_user,
|
||||
mock_verify_password,
|
||||
mock_send_verify_code,
|
||||
mock_is_email_unique):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
client.login(api_user_active)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
from flask import url_for
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
def test_should_return_verify_template(app_,
|
||||
api_user_active,
|
||||
@@ -12,26 +14,29 @@ def test_should_return_verify_template(app_,
|
||||
session['user_details'] = {'email_address': api_user_active.email_address, 'id': api_user_active.id}
|
||||
response = client.get(url_for('main.verify'))
|
||||
assert response.status_code == 200
|
||||
assert (
|
||||
"We’ve sent you confirmation codes by email and text message."
|
||||
) in response.get_data(as_text=True)
|
||||
|
||||
page = BeautifulSoup(response.data.decode('utf-8'), 'html.parser')
|
||||
assert page.h1.text == 'Text verification'
|
||||
message = page.find_all('p')[1].text
|
||||
assert message == "We've sent you a text message with a verification code."
|
||||
|
||||
|
||||
def test_should_redirect_to_add_service_when_code_are_correct(app_,
|
||||
api_user_active,
|
||||
mock_get_user,
|
||||
mock_update_user,
|
||||
mock_check_verify_code):
|
||||
def test_should_redirect_to_add_service_when_sms_code_is_correct(app_,
|
||||
api_user_active,
|
||||
mock_get_user,
|
||||
mock_update_user,
|
||||
mock_check_verify_code):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
with client.session_transaction() as session:
|
||||
session['user_details'] = {'email_address': api_user_active.email_address, 'id': api_user_active.id}
|
||||
response = client.post(url_for('main.verify'),
|
||||
data={'sms_code': '12345',
|
||||
'email_code': '23456'})
|
||||
data={'sms_code': '12345'})
|
||||
assert response.status_code == 302
|
||||
assert response.location == url_for('main.add_service', first='first', _external=True)
|
||||
|
||||
mock_check_verify_code.assert_called_once_with(api_user_active.id, '12345', 'sms')
|
||||
|
||||
|
||||
def test_should_activate_user_after_verify(app_,
|
||||
api_user_active,
|
||||
@@ -44,45 +49,20 @@ def test_should_activate_user_after_verify(app_,
|
||||
with client.session_transaction() as session:
|
||||
session['user_details'] = {'email_address': api_user_active.email_address, 'id': api_user_active.id}
|
||||
client.post(url_for('main.verify'),
|
||||
data={'sms_code': '12345',
|
||||
'email_code': '23456'})
|
||||
data={'sms_code': '12345'})
|
||||
assert mock_update_user.called
|
||||
|
||||
|
||||
def test_should_return_200_when_codes_are_wrong(app_,
|
||||
api_user_active,
|
||||
mock_get_user,
|
||||
mock_check_verify_code_code_not_found):
|
||||
def test_should_return_200_when_sms_code_is_wrong(app_,
|
||||
api_user_active,
|
||||
mock_get_user,
|
||||
mock_check_verify_code_code_not_found):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
with client.session_transaction() as session:
|
||||
session['user_details'] = {'email_address': api_user_active.email_address, 'id': api_user_active.id}
|
||||
response = client.post(url_for('main.verify'),
|
||||
data={'sms_code': '12345',
|
||||
'email_code': '23456'})
|
||||
data={'sms_code': '12345'})
|
||||
assert response.status_code == 200
|
||||
resp_data = response.get_data(as_text=True)
|
||||
assert resp_data.count('Code not found') == 2
|
||||
|
||||
|
||||
def test_should_only_check_codes_in_validation_if_both_are_present(app_,
|
||||
api_user_active,
|
||||
mock_get_user,
|
||||
mock_update_user,
|
||||
mock_check_verify_code):
|
||||
with app_.test_request_context():
|
||||
with app_.test_client() as client:
|
||||
with client.session_transaction() as session:
|
||||
session['user_details'] = {'email_address': api_user_active.email_address, 'id': api_user_active.id}
|
||||
response = client.post(url_for('main.verify'), data={'sms_code': '12345'})
|
||||
assert response.status_code == 200
|
||||
assert not mock_check_verify_code.called
|
||||
|
||||
response = client.post(url_for('main.verify'), data={'email_code': '12345'})
|
||||
assert response.status_code == 200
|
||||
assert not mock_check_verify_code.called
|
||||
|
||||
response = client.post(url_for('main.verify'), data={'sms_code': '12345', 'email_code': '12345'})
|
||||
assert response.status_code == 302
|
||||
assert mock_check_verify_code.called
|
||||
assert mock_check_verify_code.call_count == 2
|
||||
assert resp_data.count('Code not found') == 1
|
||||
|
||||
+47
-32
@@ -2,7 +2,9 @@ import uuid
|
||||
from datetime import date, datetime, timedelta
|
||||
from unittest.mock import Mock
|
||||
import pytest
|
||||
|
||||
from app import create_app
|
||||
|
||||
from . import (
|
||||
service_json,
|
||||
TestClient,
|
||||
@@ -16,12 +18,13 @@ from app.notify_client.models import (
|
||||
User,
|
||||
InvitedUser
|
||||
)
|
||||
|
||||
from notifications_python_client.errors import HTTPError
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def app_(request):
|
||||
app = create_app('test')
|
||||
app = create_app()
|
||||
|
||||
ctx = app.app_context()
|
||||
ctx.push()
|
||||
@@ -144,11 +147,20 @@ def mock_delete_service(mocker, mock_get_service):
|
||||
'app.service_api_client.delete_service', side_effect=_delete)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_get_service_statistics(mocker):
|
||||
def _create(service_id):
|
||||
return {'data': [{}]}
|
||||
|
||||
return mocker.patch(
|
||||
'app.statistics_api_client.get_statistics_for_service', side_effect=_create)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_get_service_template(mocker):
|
||||
def _create(service_id, template_id):
|
||||
template = template_json(
|
||||
template_id, "Two week reminder", "sms", "Your vehicle tax is about to expire", service_id)
|
||||
service_id, template_id, "Two week reminder", "sms", "Your vehicle tax is about to expire")
|
||||
return {'data': template}
|
||||
|
||||
return mocker.patch(
|
||||
@@ -159,7 +171,7 @@ def mock_get_service_template(mocker):
|
||||
def mock_get_service_email_template(mocker):
|
||||
def _create(service_id, template_id):
|
||||
template = template_json(
|
||||
template_id, "Two week reminder", "email", "Your vehicle tax is about to expire", service_id)
|
||||
service_id, template_id, "Two week reminder", "email", "Your vehicle tax is about to expire")
|
||||
return {'data': template}
|
||||
|
||||
return mocker.patch(
|
||||
@@ -195,16 +207,16 @@ def mock_get_service_templates(mocker):
|
||||
def _create(service_id):
|
||||
return {'data': [
|
||||
template_json(
|
||||
1, "sms_template_one", "sms", "sms template one content", service_id
|
||||
service_id, 1, "sms_template_one", "sms", "sms template one content"
|
||||
),
|
||||
template_json(
|
||||
2, "sms_template_two", "sms", "sms template two content", service_id
|
||||
service_id, 2, "sms_template_two", "sms", "sms template two content"
|
||||
),
|
||||
template_json(
|
||||
3, "email_template_one", "email", "email template one content", service_id
|
||||
service_id, 3, "email_template_one", "email", "email template one content"
|
||||
),
|
||||
template_json(
|
||||
4, "email_template_two", "email", "email template two content", service_id
|
||||
service_id, 4, "email_template_two", "email", "email template two content"
|
||||
)
|
||||
]}
|
||||
|
||||
@@ -217,8 +229,7 @@ def mock_get_service_templates(mocker):
|
||||
def mock_delete_service_template(mocker):
|
||||
def _delete(service_id, template_id):
|
||||
template = template_json(
|
||||
template_id, "Template to delete",
|
||||
"sms", "content to be deleted", service_id)
|
||||
service_id, template_id, "Template to delete", "sms", "content to be deleted")
|
||||
return {'data': template}
|
||||
|
||||
return mocker.patch(
|
||||
@@ -366,7 +377,6 @@ def mock_get_user(mocker, api_user_active):
|
||||
def _get_user(id):
|
||||
api_user_active.id = id
|
||||
return api_user_active
|
||||
|
||||
return mocker.patch(
|
||||
'app.user_api_client.get_user', side_effect=_get_user)
|
||||
|
||||
@@ -385,10 +395,10 @@ def mock_get_user_pending(mocker, api_user_pending):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_get_user_by_email(mocker, api_user_active):
|
||||
|
||||
def _get_user(email_address):
|
||||
api_user_active._email_address = email_address
|
||||
return api_user_active
|
||||
|
||||
return mocker.patch('app.user_api_client.get_user_by_email', side_effect=_get_user)
|
||||
|
||||
|
||||
@@ -397,16 +407,15 @@ def mock_get_user_with_permissions(mocker, api_user_active):
|
||||
def _get_user(id):
|
||||
api_user_active._permissions[''] = ['manage_users', 'manage_templates', 'manage_settings']
|
||||
return api_user_active
|
||||
|
||||
return mocker.patch(
|
||||
'app.user_api_client.get_user', side_effect=_get_user)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_dont_get_user_by_email(mocker):
|
||||
|
||||
def _get_user(email_address):
|
||||
return None
|
||||
|
||||
return mocker.patch(
|
||||
'app.user_api_client.get_user_by_email',
|
||||
side_effect=_get_user,
|
||||
@@ -455,7 +464,6 @@ def mock_get_user_by_email_not_found(mocker):
|
||||
def mock_verify_password(mocker):
|
||||
def _verify_password(user, password):
|
||||
return True
|
||||
|
||||
return mocker.patch(
|
||||
'app.user_api_client.verify_password',
|
||||
side_effect=_verify_password)
|
||||
@@ -463,15 +471,20 @@ def mock_verify_password(mocker):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_update_user(mocker):
|
||||
|
||||
def _update(user):
|
||||
return user
|
||||
|
||||
return mocker.patch('app.user_api_client.update_user', side_effect=_update)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_is_email_unique(mocker):
|
||||
return mocker.patch('app.user_api_client.get_user_by_email', return_value=None)
|
||||
return mocker.patch('app.user_api_client.is_email_unique', return_value=True)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_is_email_not_unique(mocker):
|
||||
return mocker.patch('app.user_api_client.is_email_unique', return_value=False)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
@@ -481,6 +494,7 @@ def mock_get_all_users_from_api(mocker):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_create_api_key(mocker):
|
||||
|
||||
def _create(service_id, key_name):
|
||||
import uuid
|
||||
return {'data': str(uuid.uuid4())}
|
||||
@@ -542,11 +556,15 @@ def mock_send_verify_code(mocker):
|
||||
return mocker.patch('app.user_api_client.send_verify_code')
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_send_verify_email(mocker):
|
||||
return mocker.patch('app.user_api_client.send_verify_email')
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_check_verify_code(mocker):
|
||||
def _verify(user_id, code, code_type):
|
||||
return True, ''
|
||||
|
||||
return mocker.patch(
|
||||
'app.user_api_client.check_verify_code',
|
||||
side_effect=_verify)
|
||||
@@ -556,7 +574,6 @@ def mock_check_verify_code(mocker):
|
||||
def mock_check_verify_code_code_not_found(mocker):
|
||||
def _verify(user_id, code, code_type):
|
||||
return False, 'Code not found'
|
||||
|
||||
return mocker.patch(
|
||||
'app.user_api_client.check_verify_code',
|
||||
side_effect=_verify)
|
||||
@@ -566,7 +583,6 @@ def mock_check_verify_code_code_not_found(mocker):
|
||||
def mock_check_verify_code_code_expired(mocker):
|
||||
def _verify(user_id, code, code_type):
|
||||
return False, 'Code has expired'
|
||||
|
||||
return mocker.patch(
|
||||
'app.user_api_client.check_verify_code',
|
||||
side_effect=_verify)
|
||||
@@ -588,7 +604,6 @@ def mock_create_job(mocker, job_data):
|
||||
job_data['file_name'] = '{}.csv'.format(job_id)
|
||||
job_data['notification_count'] = notification_count
|
||||
return job_data
|
||||
|
||||
return mocker.patch('app.job_api_client.create_job', side_effect=_create)
|
||||
|
||||
|
||||
@@ -598,7 +613,6 @@ def mock_get_job(mocker, job_data):
|
||||
job_data['id'] = job_id
|
||||
job_data['service'] = service_id
|
||||
return {"data": job_data}
|
||||
|
||||
return mocker.patch('app.job_api_client.get_job', side_effect=_get_job)
|
||||
|
||||
|
||||
@@ -613,15 +627,23 @@ def mock_get_jobs(mocker):
|
||||
job_data['service'] = service_id
|
||||
data.append(job_data)
|
||||
return {"data": data}
|
||||
|
||||
return mocker.patch('app.job_api_client.get_job', side_effect=_get_jobs)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_get_notifications(mocker):
|
||||
def _get_notifications(service_id, job_id):
|
||||
return notification_json()
|
||||
def _get_notifications(service_id, job_id=None, page=1):
|
||||
return notification_json(service_id)
|
||||
return mocker.patch(
|
||||
'app.notification_api_client.get_notifications_for_service',
|
||||
side_effect=_get_notifications
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_get_notifications_with_previous_next(mocker):
|
||||
def _get_notifications(service_id, job_id=None, page=1):
|
||||
return notification_json(service_id, with_links=True)
|
||||
return mocker.patch(
|
||||
'app.notification_api_client.get_notifications_for_service',
|
||||
side_effect=_get_notifications
|
||||
@@ -632,7 +654,6 @@ def mock_get_notifications(mocker):
|
||||
def mock_has_permissions(mocker):
|
||||
def _has_permission(permissions=None, or_=False, admin_override=False):
|
||||
return True
|
||||
|
||||
return mocker.patch(
|
||||
'app.notify_client.user_api_client.User.has_permissions',
|
||||
side_effect=_has_permission)
|
||||
@@ -658,7 +679,6 @@ def mock_get_users_by_service(mocker):
|
||||
'email_address': 'notify@digital.cabinet-office.gov.uk',
|
||||
'failed_login_count': 0}]
|
||||
return [User(data[0])]
|
||||
|
||||
return mocker.patch('app.user_api_client.get_users_for_service', side_effect=_get_users_for_service, autospec=True)
|
||||
|
||||
|
||||
@@ -666,7 +686,6 @@ def mock_get_users_by_service(mocker):
|
||||
def mock_s3_upload(mocker):
|
||||
def _upload(upload_id, service_id, filedata, region):
|
||||
pass
|
||||
|
||||
return mocker.patch('app.main.views.send.s3upload', side_effect=_upload)
|
||||
|
||||
|
||||
@@ -689,6 +708,7 @@ def sample_invited_user(mocker, sample_invite):
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def mock_create_invite(mocker, sample_invite):
|
||||
|
||||
def _create_invite(from_user, service_id, email_address, permissions):
|
||||
sample_invite['from_user'] = from_user
|
||||
sample_invite['service'] = service_id
|
||||
@@ -696,7 +716,6 @@ def mock_create_invite(mocker, sample_invite):
|
||||
sample_invite['status'] = 'pending'
|
||||
sample_invite['permissions'] = permissions
|
||||
return InvitedUser(**sample_invite)
|
||||
|
||||
return mocker.patch('app.invite_api_client.create_invite', side_effect=_create_invite)
|
||||
|
||||
|
||||
@@ -711,7 +730,6 @@ def mock_get_invites_for_service(mocker, service_one, sample_invite):
|
||||
invite['email_address'] = 'user_{}@testnotify.gov.uk'.format(i)
|
||||
data.append(InvitedUser(**invite))
|
||||
return data
|
||||
|
||||
return mocker.patch('app.invite_api_client.get_invites_for_service', side_effect=_get_invites)
|
||||
|
||||
|
||||
@@ -719,7 +737,6 @@ def mock_get_invites_for_service(mocker, service_one, sample_invite):
|
||||
def mock_check_invite_token(mocker, sample_invite):
|
||||
def _check_token(token):
|
||||
return InvitedUser(**sample_invite)
|
||||
|
||||
return mocker.patch('app.invite_api_client.check_token', side_effect=_check_token)
|
||||
|
||||
|
||||
@@ -727,7 +744,6 @@ def mock_check_invite_token(mocker, sample_invite):
|
||||
def mock_accept_invite(mocker, sample_invite):
|
||||
def _accept(service_id, invite_id):
|
||||
return InvitedUser(**sample_invite)
|
||||
|
||||
return mocker.patch('app.invite_api_client.accept_invite', side_effect=_accept)
|
||||
|
||||
|
||||
@@ -735,7 +751,6 @@ def mock_accept_invite(mocker, sample_invite):
|
||||
def mock_add_user_to_service(mocker, service_one, api_user_active):
|
||||
def _add_user(service_id, user_id, permissions):
|
||||
return api_user_active
|
||||
|
||||
return mocker.patch('app.user_api_client.add_user_to_service', side_effect=_add_user)
|
||||
|
||||
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
from app import create_app
|
||||
from credstash import getAllSecrets
|
||||
import os
|
||||
|
||||
config = 'live'
|
||||
default_env_file = '/home/ubuntu/environment'
|
||||
environment = 'live'
|
||||
|
||||
if os.path.isfile(default_env_file):
|
||||
environment = open(default_env_file, 'r')
|
||||
config = environment.readline().strip()
|
||||
with open(default_env_file, 'r') as environment_file:
|
||||
environment = environment_file.readline().strip()
|
||||
|
||||
secrets = getAllSecrets(region="eu-west-1")
|
||||
|
||||
application = create_app(config, secrets)
|
||||
# on aws get secrets and export to env
|
||||
os.environ.update(getAllSecrets(region="eu-west-1"))
|
||||
|
||||
from config import configs
|
||||
|
||||
os.environ['NOTIFY_ADMIN_ENVIRONMENT'] = configs[environment]
|
||||
|
||||
from app import create_app
|
||||
|
||||
application = create_app()
|
||||
|
||||
if __name__ == "__main__":
|
||||
application.run()
|
||||
|
||||
Reference in New Issue
Block a user