mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-02-05 10:53:28 -05:00
Merge branch 'master' of github.com:alphagov/notifications-admin
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -63,3 +63,5 @@ app/assets/stylesheets/govuk_template/.sass-cache/
|
||||
.sass-cache/
|
||||
cache/
|
||||
app/static/*
|
||||
app/static/stylesheets/*.css
|
||||
|
||||
|
||||
@@ -44,4 +44,16 @@
|
||||
|
||||
#global-header #logo {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
.column-three-quarters {
|
||||
@include grid-column(3/4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
a:visited {
|
||||
color: #005ea5;
|
||||
}
|
||||
23
app/assets/stylesheets/components/navigation.scss
Normal file
23
app/assets/stylesheets/components/navigation.scss
Normal file
@@ -0,0 +1,23 @@
|
||||
#navigation {
|
||||
padding: 50px 0 0 0;
|
||||
}
|
||||
|
||||
#navigation li {
|
||||
@include core-16();
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
#navigation ul {
|
||||
border-bottom: 1px solid #777;
|
||||
margin: 0 20px 0 0;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#navigation a:link,a:visited {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#navigation a:hover {
|
||||
color: #2e8aca;
|
||||
}
|
||||
@@ -33,6 +33,7 @@
|
||||
@import "components/sms-message";
|
||||
@import "components/submit-form";
|
||||
@import "components/table";
|
||||
@import "components/navigation";
|
||||
|
||||
// TODO: break this up
|
||||
@import "app";
|
||||
|
||||
@@ -3,4 +3,4 @@ from flask import Blueprint
|
||||
main = Blueprint('main', __name__)
|
||||
|
||||
|
||||
from app.main.views import index, sign_in, register, two_factor, verify, sms
|
||||
from app.main.views import index, sign_in, register, two_factor, verify, sms, add_service
|
||||
|
||||
41
app/main/dao/services_dao.py
Normal file
41
app/main/dao/services_dao.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from datetime import datetime
|
||||
|
||||
from app import db
|
||||
from app.models import Service
|
||||
|
||||
|
||||
def insert_new_service(service_name, user):
|
||||
service = Service(name=service_name,
|
||||
created_at=datetime.now(),
|
||||
limit=1000,
|
||||
active=False,
|
||||
restricted=True)
|
||||
add_service(service)
|
||||
service.users.append(user)
|
||||
db.session.commit()
|
||||
return service.id
|
||||
|
||||
|
||||
def get_service_by_id(id):
|
||||
return Service.query.get(id)
|
||||
|
||||
|
||||
def unrestrict_service(service_id):
|
||||
service = get_service_by_id(service_id)
|
||||
service.restricted = False
|
||||
add_service(service)
|
||||
|
||||
|
||||
def activate_service(service_id):
|
||||
service = get_service_by_id(service_id)
|
||||
service.active = True
|
||||
add_service(service)
|
||||
|
||||
|
||||
def add_service(service):
|
||||
db.session.add(service)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def find_service_by_service_name(service_name):
|
||||
return Service.query.filter_by(name=service_name).first()
|
||||
@@ -5,7 +5,7 @@ from flask_wtf import Form
|
||||
from wtforms import StringField, PasswordField
|
||||
from wtforms.validators import DataRequired, Email, Length, Regexp
|
||||
|
||||
from app.main.dao import verify_codes_dao
|
||||
from app.main.dao import verify_codes_dao, services_dao
|
||||
from app.main.encryption import check_hash
|
||||
from app.main.validators import Blacklist
|
||||
|
||||
@@ -82,3 +82,14 @@ def validate_code(field, code):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
class AddServiceForm(Form):
|
||||
service_name = StringField(validators=[DataRequired(message='Please enter your service name')])
|
||||
|
||||
def validate_service_name(self, a):
|
||||
if services_dao.find_service_by_service_name(self.service_name.data) is not None:
|
||||
self.service_name.errors.append('Duplicate service name')
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
25
app/main/views/add_service.py
Normal file
25
app/main/views/add_service.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from flask import render_template, jsonify, redirect, session
|
||||
from flask_login import login_required
|
||||
|
||||
from app.main import main
|
||||
from app.main.dao import services_dao, users_dao
|
||||
from app.main.forms import AddServiceForm
|
||||
|
||||
|
||||
@main.route("/add-service", methods=['GET'])
|
||||
@login_required
|
||||
def add_service():
|
||||
return render_template('views/add-service.html', form=AddServiceForm())
|
||||
|
||||
|
||||
@main.route("/add-service", methods=['POST'])
|
||||
@login_required
|
||||
def process_add_service():
|
||||
form = AddServiceForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
user = users_dao.get_user_by_id(session['user_id'])
|
||||
services_dao.insert_new_service(form.service_name.data, user)
|
||||
return redirect('/dashboard')
|
||||
else:
|
||||
return jsonify(form.errors), 400
|
||||
@@ -35,12 +35,6 @@ def dashboard():
|
||||
return render_template('views/dashboard.html')
|
||||
|
||||
|
||||
@main.route("/add-service")
|
||||
@login_required
|
||||
def addservice():
|
||||
return render_template('views/add-service.html')
|
||||
|
||||
|
||||
@main.route("/email-not-received")
|
||||
def emailnotreceived():
|
||||
return render_template('views/email-not-received.html')
|
||||
|
||||
@@ -78,6 +78,39 @@ class User(db.Model):
|
||||
return True
|
||||
|
||||
|
||||
user_to_service = db.Table(
|
||||
'user_to_service',
|
||||
db.Model.metadata,
|
||||
db.Column('user_id', db.Integer, db.ForeignKey('users.id')),
|
||||
db.Column('service_id', db.Integer, db.ForeignKey('services.id'))
|
||||
)
|
||||
|
||||
|
||||
class Service(db.Model):
|
||||
__tablename__ = 'services'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(255), nullable=False, unique=True)
|
||||
created_at = db.Column(db.DateTime, index=False, unique=False, nullable=False)
|
||||
active = db.Column(db.Boolean, index=False, unique=False, nullable=False)
|
||||
limit = db.Column(db.BigInteger, index=False, unique=False, nullable=False)
|
||||
users = db.relationship('User', secondary=user_to_service, backref=db.backref('user_to_service', lazy='dynamic'))
|
||||
restricted = db.Column(db.Boolean, index=False, unique=False, nullable=False)
|
||||
|
||||
def serialize(self):
|
||||
serialized = {
|
||||
'id': self.id,
|
||||
'name': self.name,
|
||||
'createdAt': self.created_at.strftime(DATETIME_FORMAT),
|
||||
'active': self.active,
|
||||
'restricted': self.restricted,
|
||||
'limit': self.limit,
|
||||
'user': self.users.serialize()
|
||||
}
|
||||
|
||||
return filter_null_value_fields(serialized)
|
||||
|
||||
|
||||
def filter_null_value_fields(obj):
|
||||
return dict(
|
||||
filter(lambda x: x[1] is not None, obj.items())
|
||||
|
||||
21
app/templates/main_nav.html
Normal file
21
app/templates/main_nav.html
Normal file
@@ -0,0 +1,21 @@
|
||||
{% macro main_nav() %}
|
||||
|
||||
<div id="navigation">
|
||||
<h3><a href="{{ url_for('.dashboard') }}">Service name</a></h3>
|
||||
<ul>
|
||||
<li><a href="{{ url_for('.sendsms') }}">Send text messages</a></li>
|
||||
<li><a href="{{ url_for('.sendemail') }}">Send emails</a></li>
|
||||
<li><a href="{{ url_for('.showjobs') }}">Activity</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="{{ url_for('.apikeys') }}">API keys and documentation</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="{{ url_for('.manageusers') }}">Manage users</a></li>
|
||||
<li><a href="{{ url_for('.servicesettings') }}">Service settings</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a href="/user-profile">Your details</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
@@ -16,15 +16,17 @@ GOV.UK Notify | Set up service
|
||||
<li>as your email sender name</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
<label class="form-label" for="emailverify">Service name</label>
|
||||
<input class="form-control" id="emailverify" type="text"><br>
|
||||
<span class="font-xsmall">For example, 'Vehicle tax' or 'Carer's allowance'</span>
|
||||
</p>
|
||||
<form autocomplete="off" action="" method="post">
|
||||
{{ form.hidden_tag() }}
|
||||
<label class="form-label">Service name</label>
|
||||
{{ form.service_name(class="form-control-2-3", autocomplete="off") }} <br>
|
||||
<span class="font-xsmall">For example, 'Vehicle tax' or 'Carer's allowance'</span>
|
||||
|
||||
<p>
|
||||
<a class="button" href="dashboard" role="button">Continue</a>
|
||||
</p>
|
||||
<p>
|
||||
<button class="button" href="dashboard" role="button">Continue</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | API keys and documentation
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">API keys and documentation</h1>
|
||||
|
||||
<p>Here's where developers can access information about the API and access keys</p>
|
||||
|
||||
<p><a href="dashboard">Back to dashboard</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | Send email
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Send email</h1>
|
||||
|
||||
<p>This page will be where we check the email messages we're about to send</p>
|
||||
@@ -15,7 +13,6 @@ GOV.UK Notify | Send email
|
||||
<p>
|
||||
<a class="button" href="dashboard" role="button">Send email messages</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/sms-message.html" import sms_message %}
|
||||
{% from "components/table.html" import table, field %}
|
||||
{% from "components/placeholder.html" import placeholder %}
|
||||
@@ -8,10 +8,9 @@
|
||||
GOV.UK Notify | Send text messages
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Send text messages</h1>
|
||||
|
||||
<h2 class="heading-medium">Check and confirm</h2>
|
||||
@@ -61,7 +60,6 @@
|
||||
url_for(".sendsms")
|
||||
) }}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,26 +1,15 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | Dashboard
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Dashboard</h1>
|
||||
|
||||
<ul>
|
||||
<li><a href="{{ url_for('.sendsms') }}">Send text messages</a></li>
|
||||
<li><a href="{{ url_for('.sendemail') }}">Send email messages</a></li>
|
||||
<li><a href="{{ url_for('.showjobs') }}">View notifications activity</a></li>
|
||||
<li><a href="{{ url_for('.userprofile') }}">User profile</a></li>
|
||||
<li><a href="{{ url_for('.manageusers') }}">Manage users</a></li>
|
||||
<li><a href="{{ url_for('.managetemplates')}}">Manage templates</a></li>
|
||||
<li><a href="{{ url_for('.servicesettings') }}">Service settings</a></li>
|
||||
<li><a href="{{ url_for('.apikeys') }}">API keys and documentation</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<p>Dashboard goes here.</p>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | Edit template
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Edit template</h1>
|
||||
|
||||
<p>Here's where you can edit an exiting template (including delete) or add a new one</p>
|
||||
|
||||
<p><a href="manage-templates">Back to manage templates</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | Notifications activity
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Notifications for a specific job</h1>
|
||||
|
||||
<p>This page will be where we list the notifications for a specific job.</p>
|
||||
@@ -16,7 +14,6 @@ GOV.UK Notify | Notifications activity
|
||||
<li><a href="/jobs/job/notification">view a specific notification</a></li>
|
||||
<li><a href="/jobs">view all the activity for this service</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | Notifications activity
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Notifications activity</h1>
|
||||
|
||||
<p>This page will be where we show the list of jobs that this service has processed</p>
|
||||
@@ -15,7 +13,6 @@ GOV.UK Notify | Notifications activity
|
||||
<p>
|
||||
<a href="jobs/job">view a particular notification job</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | Manage templates
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Manage templates</h1>
|
||||
|
||||
<p>Here's where you can view templates, choose to add one, or edit/delete one.</p>
|
||||
@@ -17,7 +16,6 @@ GOV.UK Notify | Manage templates
|
||||
<p>
|
||||
<a class="button" href="edit-template" role="button">Add a new message template</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | Manage users
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Manage users</h1>
|
||||
|
||||
<p>Here's where you can add or remove users of a service.</p>
|
||||
|
||||
<p><a href="dashboard">Back to dashboard</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | Notifications activity
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">A specific notification</h1>
|
||||
|
||||
<p>This page will be where we show what happened for a specific notification.</p>
|
||||
@@ -15,7 +14,6 @@ GOV.UK Notify | Notifications activity
|
||||
<p>
|
||||
<a href="/jobs/job">View other notifications in this job</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | Send email
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Send email</h1>
|
||||
|
||||
<p>This page will be where we construct email messages</p>
|
||||
@@ -15,7 +14,6 @@ GOV.UK Notify | Send email
|
||||
<p>
|
||||
<a class="button" href="check-email" role="button">Continue</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
{% from "components/sms-message.html" import sms_message %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | Send text messages
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
<form method="POST" enctype="multipart/form-data">
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
|
||||
<h1 class="heading-xlarge">Send text messages</h1>
|
||||
|
||||
@@ -46,8 +44,7 @@
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="submit" class="button" value="Continue" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | Service settings
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">Service settings</h1>
|
||||
|
||||
<p>Here's where users can update their service profile.</p>
|
||||
|
||||
<p><a href="dashboard">Back to dashboard</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% extends "withnav_template.html" %}
|
||||
|
||||
{% block page_title %}
|
||||
GOV.UK Notify | User settings
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% block maincolumn_content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-two-thirds">
|
||||
<h1 class="heading-xlarge">User profile</h1>
|
||||
|
||||
<p>Here's where users can update their profile, password etc.</p>
|
||||
|
||||
<p><a href="dashboard">Back to dashboard</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
15
app/templates/withnav_template.html
Normal file
15
app/templates/withnav_template.html
Normal file
@@ -0,0 +1,15 @@
|
||||
{% extends "admin_template.html" %}
|
||||
{% from "main_nav.html" import main_nav with context %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="grid-row">
|
||||
<div class="column-third">
|
||||
{{ main_nav() }}
|
||||
</div>
|
||||
<div class="column-two-thirds">
|
||||
{% block maincolumn_content %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
42
migrations/versions/60_add_service.py
Normal file
42
migrations/versions/60_add_service.py
Normal file
@@ -0,0 +1,42 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 60_add_service
|
||||
Revises: 50_alter_verify_code_type
|
||||
Create Date: 2015-12-15 09:25:09.000431
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '60_add_service'
|
||||
down_revision = '50_alter_verify_code_type'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('services',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('active', sa.Boolean(), nullable=False),
|
||||
sa.Column('limit', sa.BigInteger(), nullable=False),
|
||||
sa.Column('restricted', sa.Boolean(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
sa.UniqueConstraint('name')
|
||||
)
|
||||
op.create_table('user_to_service',
|
||||
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||
sa.Column('service_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['service_id'], ['services.id'], ),
|
||||
sa.ForeignKeyConstraint(['user_id'], ['users.id'], )
|
||||
)
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('user_to_service')
|
||||
op.drop_table('services')
|
||||
### end Alembic commands ###
|
||||
62
tests/app/main/dao/test_service_dao.py
Normal file
62
tests/app/main/dao/test_service_dao.py
Normal file
@@ -0,0 +1,62 @@
|
||||
import pytest
|
||||
import sqlalchemy
|
||||
|
||||
from app.main.dao import services_dao
|
||||
from tests.app.main import create_test_user
|
||||
|
||||
|
||||
def test_can_insert_and_retrieve_new_service(notifications_admin, notifications_admin_db, notify_db_session):
|
||||
user = create_test_user()
|
||||
id = services_dao.insert_new_service('testing service', user)
|
||||
saved_service = services_dao.get_service_by_id(id)
|
||||
assert id == saved_service.id
|
||||
assert saved_service.users == [user]
|
||||
assert saved_service.name == 'testing service'
|
||||
|
||||
|
||||
def test_unrestrict_service_updates_the_service(notifications_admin, notifications_admin_db, notify_db_session):
|
||||
user = create_test_user()
|
||||
id = services_dao.insert_new_service('unrestricted service', user)
|
||||
saved_service = services_dao.get_service_by_id(id)
|
||||
assert saved_service.restricted is True
|
||||
services_dao.unrestrict_service(id)
|
||||
unrestricted_service = services_dao.get_service_by_id(id)
|
||||
assert unrestricted_service.restricted is False
|
||||
|
||||
|
||||
def test_activate_service_update_service(notifications_admin, notifications_admin_db, notify_db_session):
|
||||
user = create_test_user()
|
||||
id = services_dao.insert_new_service('activated service', user)
|
||||
service = services_dao.get_service_by_id(id)
|
||||
assert service.active is False
|
||||
services_dao.activate_service(id)
|
||||
activated_service = services_dao.get_service_by_id(id)
|
||||
assert activated_service.active is True
|
||||
|
||||
|
||||
def test_get_service_returns_none_if_service_does_not_exist(notifications_admin,
|
||||
notifications_admin_db,
|
||||
notify_db_session):
|
||||
service = services_dao.get_service_by_id(1)
|
||||
assert service is None
|
||||
|
||||
|
||||
def test_find_by_service_name_returns_right_service(notifications_admin,
|
||||
notifications_admin_db,
|
||||
notify_db_session):
|
||||
user = create_test_user()
|
||||
id = services_dao.insert_new_service('testing service', user)
|
||||
another = services_dao.insert_new_service('Testing the Service', user)
|
||||
found = services_dao.find_service_by_service_name('testing service')
|
||||
assert found.id == id
|
||||
assert found.name == 'testing service'
|
||||
found_another = services_dao.find_service_by_service_name('Testing the Service')
|
||||
assert found_another == services_dao.get_service_by_id(another)
|
||||
|
||||
|
||||
def test_should_not_allow_two_services_of_the_same_name(notifications_admin, notifications_admin_db, notify_db_session):
|
||||
user = create_test_user()
|
||||
services_dao.insert_new_service('duplicate service', user)
|
||||
with pytest.raises(sqlalchemy.exc.IntegrityError) as error:
|
||||
services_dao.insert_new_service('duplicate service', user)
|
||||
assert 'duplicate key value violates unique constraint "services_name_key' in error.value
|
||||
18
tests/app/main/test_add_service_form.py
Normal file
18
tests/app/main/test_add_service_form.py
Normal file
@@ -0,0 +1,18 @@
|
||||
from app.main.dao import services_dao
|
||||
from app.main.forms import AddServiceForm
|
||||
from tests.app.main import create_test_user
|
||||
|
||||
|
||||
def test_form_should_have_errors_when_duplicate_service_is_added(notifications_admin,
|
||||
notifications_admin_db,
|
||||
notify_db_session):
|
||||
with notifications_admin.test_request_context(method='POST',
|
||||
data={'service_name': 'some service'}) as req:
|
||||
user = create_test_user()
|
||||
services_dao.insert_new_service('some service', user)
|
||||
req.session['user_id'] = user.id
|
||||
form = AddServiceForm(req.request.form)
|
||||
assert form.validate() is False
|
||||
assert len(form.errors) == 1
|
||||
expected = {'service_name': ['Duplicate service name']}
|
||||
assert form.errors == expected
|
||||
42
tests/app/main/views/test_add_service.py
Normal file
42
tests/app/main/views/test_add_service.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from app.main.dao import verify_codes_dao, services_dao
|
||||
from tests.app.main import create_test_user
|
||||
|
||||
|
||||
def test_get_should_render_add_service_template(notifications_admin, notifications_admin_db, notify_db_session):
|
||||
with notifications_admin.test_client() as client:
|
||||
with client.session_transaction() as session:
|
||||
user = create_test_user()
|
||||
session['user_id'] = user.id
|
||||
verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms')
|
||||
client.post('/two-factor', data={'sms_code': '12345'})
|
||||
response = client.get('/add-service')
|
||||
assert response.status_code == 200
|
||||
assert 'Set up notifications for your service' in response.get_data(as_text=True)
|
||||
|
||||
|
||||
def test_should_add_service_and_redirect_to_next_page(notifications_admin, notifications_admin_db, notify_db_session):
|
||||
with notifications_admin.test_client() as client:
|
||||
with client.session_transaction() as session:
|
||||
user = create_test_user()
|
||||
session['user_id'] = user.id
|
||||
verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms')
|
||||
client.post('/two-factor', data={'sms_code': '12345'})
|
||||
response = client.post('/add-service', data={'service_name': 'testing the post'})
|
||||
assert response.status_code == 302
|
||||
assert response.location == 'http://localhost/dashboard'
|
||||
saved_service = services_dao.find_service_by_service_name('testing the post')
|
||||
assert saved_service is not None
|
||||
|
||||
|
||||
def test_should_return_form_errors_when_service_name_is_empty(notifications_admin,
|
||||
notifications_admin_db,
|
||||
notify_db_session):
|
||||
with notifications_admin.test_client() as client:
|
||||
with client.session_transaction() as session:
|
||||
user = create_test_user()
|
||||
session['user_id'] = user.id
|
||||
verify_codes_dao.add_code(user_id=user.id, code='12345', code_type='sms')
|
||||
client.post('/two-factor', data={'sms_code': '12345'})
|
||||
response = client.post('/add-service', data={})
|
||||
assert response.status_code == 400
|
||||
assert 'Please enter your service name' in response.get_data(as_text=True)
|
||||
Reference in New Issue
Block a user