mirror of
https://github.com/GSA/notifications-api.git
synced 2026-02-05 10:42:41 -05:00
@@ -30,9 +30,11 @@ def create_app(config_name):
|
||||
from .main import main as main_blueprint
|
||||
from .service import service as service_blueprint
|
||||
from .user import user as user_blueprint
|
||||
from .template import template as template_blueprint
|
||||
application.register_blueprint(main_blueprint)
|
||||
application.register_blueprint(service_blueprint, url_prefix='/service')
|
||||
application.register_blueprint(user_blueprint, url_prefix='/user')
|
||||
application.register_blueprint(template_blueprint, url_prefix="/template")
|
||||
|
||||
from .status import status as status_blueprint
|
||||
application.register_blueprint(status_blueprint)
|
||||
|
||||
34
app/dao/templates_dao.py
Normal file
34
app/dao/templates_dao.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import json
|
||||
from datetime import datetime
|
||||
from sqlalchemy.orm import load_only
|
||||
from . import DAOException
|
||||
from app import db
|
||||
from app.models import (Template, Service)
|
||||
|
||||
|
||||
def save_model_template(template, update_dict=None):
|
||||
if update_dict:
|
||||
update_dict.pop('id', None)
|
||||
service_id = update_dict.pop('service')
|
||||
Template.query.filter_by(id=template.id).update(update_dict)
|
||||
template.service = Service.query.get(service_id)
|
||||
else:
|
||||
db.session.add(template)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def delete_model_template(template):
|
||||
db.session.delete(template)
|
||||
db.session.commit()
|
||||
|
||||
|
||||
def get_model_templates(template_id=None, service_id=None):
|
||||
# TODO need better mapping from function params to sql query.
|
||||
if template_id and service_id:
|
||||
return Template.query.filter_by(
|
||||
id=template_id, service_id=service_id).one()
|
||||
elif template_id:
|
||||
return Template.query.filter_by(id=template_id).one()
|
||||
elif service_id:
|
||||
return Template.query.filter_by(service=Service.get(service_id)).one()
|
||||
return Template.query.all()
|
||||
@@ -59,3 +59,29 @@ class Service(db.Model):
|
||||
secondary=user_to_service,
|
||||
backref=db.backref('user_to_service', lazy='dynamic'))
|
||||
restricted = db.Column(db.Boolean, index=False, unique=False, nullable=False)
|
||||
|
||||
|
||||
TEMPLATE_TYPES = ['sms', 'email', 'letter']
|
||||
|
||||
|
||||
class Template(db.Model):
|
||||
__tablename__ = 'templates'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(255), nullable=False)
|
||||
template_type = db.Column(db.Enum(*TEMPLATE_TYPES, name='template_type'), nullable=False)
|
||||
created_at = db.Column(
|
||||
db.DateTime,
|
||||
index=False,
|
||||
unique=False,
|
||||
nullable=False,
|
||||
default=datetime.datetime.now)
|
||||
updated_at = db.Column(
|
||||
db.DateTime,
|
||||
index=False,
|
||||
unique=False,
|
||||
nullable=True,
|
||||
onupdate=datetime.datetime.now)
|
||||
content = db.Column(db.Text, index=False, unique=False, nullable=False)
|
||||
service_id = db.Column(db.BigInteger, db.ForeignKey('services.id'), index=True, unique=False)
|
||||
service = db.relationship('Service', backref=db.backref('templates', lazy='dynamic'))
|
||||
|
||||
@@ -19,10 +19,18 @@ class UserSchema(ma.ModelSchema):
|
||||
class ServiceSchema(ma.ModelSchema):
|
||||
class Meta:
|
||||
model = models.Service
|
||||
exclude = ("updated_at", "created_at")
|
||||
exclude = ("updated_at", "created_at", "templates")
|
||||
|
||||
|
||||
class TemplateSchema(ma.ModelSchema):
|
||||
class Meta:
|
||||
model = models.Template
|
||||
exclude = ("updated_at", "created_at", "service_id")
|
||||
|
||||
|
||||
user_schema = UserSchema()
|
||||
users_schema = UserSchema(many=True)
|
||||
service_schema = ServiceSchema()
|
||||
services_schema = ServiceSchema(many=True)
|
||||
template_schema = TemplateSchema()
|
||||
templates_schema = TemplateSchema(many=True)
|
||||
|
||||
@@ -3,11 +3,14 @@ from sqlalchemy.exc import DataError
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from app.dao.services_dao import (
|
||||
save_model_service, get_model_services, delete_model_service)
|
||||
from app.dao.templates_dao import (
|
||||
save_model_template, get_model_templates)
|
||||
from app.dao.users_dao import get_model_users
|
||||
from app.dao import DAOException
|
||||
from .. import service
|
||||
from app import db
|
||||
from app.schemas import (services_schema, service_schema)
|
||||
from app.schemas import (
|
||||
services_schema, service_schema, template_schema, templates_schema)
|
||||
|
||||
|
||||
# TODO auth to be added.
|
||||
@@ -67,3 +70,58 @@ def get_service(service_id=None):
|
||||
return jsonify(result="error", message="Service not found"), 404
|
||||
data, errors = services_schema.dump(services) if isinstance(services, list) else service_schema.dump(services)
|
||||
return jsonify(data=data)
|
||||
|
||||
|
||||
# TODO auth to be added.
|
||||
@service.route('/<int:service_id>/template/', methods=['POST'])
|
||||
def create_template(service_id):
|
||||
try:
|
||||
service = get_model_services(service_id=service_id)
|
||||
except DataError:
|
||||
return jsonify(result="error", message="Invalid service id"), 400
|
||||
except NoResultFound:
|
||||
return jsonify(result="error", message="Service not found"), 404
|
||||
template, errors = template_schema.load(request.get_json())
|
||||
if errors:
|
||||
return jsonify(result="error", message=errors), 400
|
||||
template.service = service
|
||||
# I believe service is already added to the session but just needs a
|
||||
# db.session.commit
|
||||
save_model_template(template)
|
||||
return jsonify(data=template_schema.dump(template).data), 201
|
||||
|
||||
|
||||
# TODO auth to be added
|
||||
@service.route('/<int:service_id>/template/<int:template_id>', methods=['PUT', 'DELETE'])
|
||||
def update_template(service_id, template_id):
|
||||
try:
|
||||
service = get_model_services(service_id=service_id)
|
||||
except DataError:
|
||||
return jsonify(result="error", message="Invalid service id"), 400
|
||||
except NoResultFound:
|
||||
return jsonify(result="error", message="Service not found"), 404
|
||||
try:
|
||||
template = get_model_templates(template_id=template_id)
|
||||
except DataError:
|
||||
return jsonify(result="error", message="Invalid template id"), 400
|
||||
except NoResultFound:
|
||||
return jsonify(result="error", message="Template not found"), 404
|
||||
if request.method == 'DELETE':
|
||||
status_code = 202
|
||||
delete_model_template(template)
|
||||
else:
|
||||
status_code = 200
|
||||
# TODO there has got to be a better way to do the next three lines
|
||||
upd_temp, errors = template_schema.load(request.get_json())
|
||||
if errors:
|
||||
return jsonify(result="error", message=errors), 400
|
||||
upd_temp.service = service
|
||||
update_dict, errors = template_schema.dump(upd_temp)
|
||||
# TODO FIX ME
|
||||
# Remove update_temp model which is added to db.session
|
||||
db.session.rollback()
|
||||
try:
|
||||
save_model_template(template, update_dict=update_dict)
|
||||
except DAOException as e:
|
||||
return jsonify(result="error", message=str(e)), 400
|
||||
return jsonify(data=template_schema.dump(template).data), status_code
|
||||
|
||||
5
app/template/__init__.py
Normal file
5
app/template/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from flask import Blueprint
|
||||
|
||||
template = Blueprint('template', __name__)
|
||||
|
||||
from app.template import rest
|
||||
34
app/template/rest.py
Normal file
34
app/template/rest.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from flask import (jsonify, request)
|
||||
from sqlalchemy.exc import DataError
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from app.dao.templates_dao import get_model_templates
|
||||
from app.dao.services_dao import get_model_services
|
||||
from app.schemas import (template_schema, templates_schema)
|
||||
from app import db
|
||||
|
||||
from flask import (jsonify, request)
|
||||
from sqlalchemy.exc import DataError
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from . import template
|
||||
from app import db
|
||||
|
||||
|
||||
# I am going to keep these for admin like operations
|
||||
# Permissions should restrict who can access this endpoint
|
||||
# TODO auth to be added.
|
||||
@template.route('/<int:template_id>', methods=['GET'])
|
||||
@template.route('/', methods=['GET'])
|
||||
def get_template(template_id=None):
|
||||
try:
|
||||
templates = get_model_templates(template_id=template_id)
|
||||
except DataError:
|
||||
return jsonify(result="error", message="Invalid template id"), 400
|
||||
except NoResultFound:
|
||||
return jsonify(result="error", message="Template not found"), 404
|
||||
if isinstance(templates, list):
|
||||
data, errors = templates_schema.dump(templates)
|
||||
else:
|
||||
data, errors = template_schema.dump(templates)
|
||||
if errors:
|
||||
return jsonify(result="error", message=str(errors))
|
||||
return jsonify(data=data)
|
||||
38
migrations/versions/0002_add_templates.py
Normal file
38
migrations/versions/0002_add_templates.py
Normal file
@@ -0,0 +1,38 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 0002_add_templates
|
||||
Revises: 0001_initialise_data
|
||||
Create Date: 2016-01-13 10:10:37.303109
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0002_add_templates'
|
||||
down_revision = '0001_initialise_data'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('templates',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('name', sa.String(length=255), nullable=False),
|
||||
sa.Column('template_type', sa.Enum('sms', 'email', 'letter', name='template_type'), nullable=False),
|
||||
sa.Column('created_at', sa.DateTime(), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(), nullable=True),
|
||||
sa.Column('content', sa.Text(), nullable=False),
|
||||
sa.Column('service_id', sa.BigInteger(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['service_id'], ['services.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_templates_service_id'), 'templates', ['service_id'], unique=False)
|
||||
### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_index(op.f('ix_templates_service_id'), table_name='templates')
|
||||
op.drop_table('templates')
|
||||
### end Alembic commands ###
|
||||
@@ -1,7 +1,8 @@
|
||||
import pytest
|
||||
from app.models import (User, Service)
|
||||
from app.models import (User, Service, Template)
|
||||
from app.dao.users_dao import (save_model_user, get_model_users)
|
||||
from app.dao.services_dao import save_model_service
|
||||
from app.dao.templates_dao import save_model_template
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
@@ -29,3 +30,23 @@ def sample_service(notify_db,
|
||||
service = Service(**data)
|
||||
save_model_service(service)
|
||||
return service
|
||||
|
||||
|
||||
@pytest.fixture(scope='function')
|
||||
def sample_template(notify_db,
|
||||
notify_db_session,
|
||||
template_name="Template Name",
|
||||
template_type="sms",
|
||||
content="This is a template",
|
||||
service=None):
|
||||
if service is None:
|
||||
service = sample_service(notify_db, notify_db_session)
|
||||
data = {
|
||||
'name': template_name,
|
||||
'template_type': template_type,
|
||||
'content': content,
|
||||
'service': service
|
||||
}
|
||||
template = Template(**data)
|
||||
save_model_template(template)
|
||||
return template
|
||||
|
||||
52
tests/app/dao/test_templates_dao.py
Normal file
52
tests/app/dao/test_templates_dao.py
Normal file
@@ -0,0 +1,52 @@
|
||||
import pytest
|
||||
from app.dao.templates_dao import (
|
||||
save_model_template, get_model_templates, delete_model_template)
|
||||
from tests.app.conftest import sample_template as create_sample_template
|
||||
from app.models import Template
|
||||
|
||||
|
||||
def test_create_template(notify_api, notify_db, notify_db_session, sample_service):
|
||||
assert Template.query.count() == 0
|
||||
template_name = 'Sample Template'
|
||||
data = {
|
||||
'name': template_name,
|
||||
'template_type': "sms",
|
||||
'content': "Template content",
|
||||
'service': sample_service}
|
||||
template = Template(**data)
|
||||
save_model_template(template)
|
||||
assert Template.query.count() == 1
|
||||
assert Template.query.first().name == template_name
|
||||
assert Template.query.first().id == template.id
|
||||
|
||||
|
||||
def test_get_Templates(notify_api, notify_db, notify_db_session, sample_service):
|
||||
sample_template = create_sample_template(notify_db,
|
||||
notify_db_session,
|
||||
service=sample_service)
|
||||
assert Template.query.count() == 1
|
||||
assert len(get_model_templates()) == 1
|
||||
template_name = "Another Template"
|
||||
sample_template = create_sample_template(notify_db,
|
||||
notify_db_session,
|
||||
template_name=template_name,
|
||||
service=sample_service)
|
||||
assert Template.query.count() == 2
|
||||
assert len(get_model_templates()) == 2
|
||||
|
||||
|
||||
def test_get_user_Template(notify_api, notify_db, notify_db_session, sample_service):
|
||||
assert Template.query.count() == 0
|
||||
template_name = "Random Template"
|
||||
sample_template = create_sample_template(notify_db,
|
||||
notify_db_session,
|
||||
template_name=template_name,
|
||||
service=sample_service)
|
||||
assert get_model_templates(template_id=sample_template.id).name == template_name
|
||||
assert Template.query.count() == 1
|
||||
|
||||
|
||||
def test_delete_template(notify_api, notify_db, notify_db_session, sample_template):
|
||||
assert Template.query.count() == 1
|
||||
delete_model_template(sample_template)
|
||||
assert Template.query.count() == 0
|
||||
@@ -1,5 +1,5 @@
|
||||
import json
|
||||
from app.models import (Service, User)
|
||||
from app.models import (Service, User, Template)
|
||||
from app.dao.services_dao import save_model_service
|
||||
from tests.app.conftest import sample_user as create_sample_user
|
||||
from flask import url_for
|
||||
@@ -261,3 +261,183 @@ def test_delete_service_not_exists(notify_api, notify_db, notify_db_session, sam
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
assert resp.status_code == 404
|
||||
assert Service.query.count() == 1
|
||||
|
||||
|
||||
def test_create_template(notify_api, notify_db, notify_db_session, sample_service):
|
||||
"""
|
||||
Tests POST endpoint '/<service_id>/template' a template can be created
|
||||
from a service.
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
assert Template.query.count() == 0
|
||||
template_name = "template name"
|
||||
template_type = "sms"
|
||||
template_content = "This is a template"
|
||||
data = {
|
||||
'name': template_name,
|
||||
'template_type': template_type,
|
||||
'content': template_content,
|
||||
'service': sample_service.id
|
||||
}
|
||||
resp = client.post(
|
||||
url_for('service.create_template', service_id=sample_service.id),
|
||||
data=json.dumps(data),
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
assert resp.status_code == 201
|
||||
assert Template.query.count() == 1
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert json_resp['data']['name'] == template_name
|
||||
assert json_resp['data']['template_type'] == template_type
|
||||
assert json_resp['data']['content'] == template_content
|
||||
|
||||
|
||||
def test_create_template_service_not_exists(notify_api, notify_db, notify_db_session, sample_service):
|
||||
"""
|
||||
Tests POST endpoint '/<service_id>/template' a template can be created
|
||||
from a service.
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
assert Template.query.count() == 0
|
||||
template_name = "template name"
|
||||
template_type = "sms"
|
||||
template_content = "This is a template"
|
||||
data = {
|
||||
'name': template_name,
|
||||
'template_type': template_type,
|
||||
'content': template_content,
|
||||
'service': sample_service.id
|
||||
}
|
||||
resp = client.post(
|
||||
url_for('service.create_template', service_id="123"),
|
||||
data=json.dumps(data),
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
assert resp.status_code == 404
|
||||
assert Template.query.count() == 0
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert "Service not found" in json_resp['message']
|
||||
|
||||
|
||||
def test_update_template(notify_api, notify_db, notify_db_session, sample_template):
|
||||
"""
|
||||
Tests PUT endpoint '/<service_id>/template/<template_id>' a template can be
|
||||
updated.
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
assert Template.query.count() == 1
|
||||
sample_service = Service.query.first()
|
||||
old_name = sample_template.name
|
||||
template_name = "new name"
|
||||
template_type = "sms"
|
||||
template_content = "content has been changed"
|
||||
data = {
|
||||
'name': template_name,
|
||||
'template_type': template_type,
|
||||
'content': template_content,
|
||||
'service': sample_service.id
|
||||
}
|
||||
resp = client.put(
|
||||
url_for('service.update_template',
|
||||
service_id=sample_service.id,
|
||||
template_id=sample_template.id),
|
||||
data=json.dumps(data),
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
assert resp.status_code == 200
|
||||
assert Template.query.count() == 1
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert json_resp['data']['name'] == template_name
|
||||
assert json_resp['data']['template_type'] == template_type
|
||||
assert json_resp['data']['content'] == template_content
|
||||
assert old_name != template_name
|
||||
|
||||
|
||||
def test_update_template_service_not_exists(notify_api, notify_db, notify_db_session, sample_template):
|
||||
"""
|
||||
Tests PUT endpoint '/<service_id>/template/<template_id>' a 404 if service
|
||||
doesn't exist.
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
assert Template.query.count() == 1
|
||||
sample_service = Service.query.first()
|
||||
template_name = "new name"
|
||||
template_type = "sms"
|
||||
template_content = "content has been changed"
|
||||
data = {
|
||||
'name': template_name,
|
||||
'template_type': template_type,
|
||||
'content': template_content,
|
||||
'service': sample_service.id
|
||||
}
|
||||
resp = client.put(
|
||||
url_for('service.update_template',
|
||||
service_id="123",
|
||||
template_id=sample_template.id),
|
||||
data=json.dumps(data),
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
assert resp.status_code == 404
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert "Service not found" in json_resp['message']
|
||||
assert template_name != sample_template.name
|
||||
|
||||
|
||||
def test_update_template_template_not_exists(notify_api, notify_db, notify_db_session, sample_template):
|
||||
"""
|
||||
Tests PUT endpoint '/<service_id>/template/<template_id>' a 404 if template
|
||||
doesn't exist.
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
assert Template.query.count() == 1
|
||||
sample_service = Service.query.first()
|
||||
template_name = "new name"
|
||||
template_type = "sms"
|
||||
template_content = "content has been changed"
|
||||
data = {
|
||||
'name': template_name,
|
||||
'template_type': template_type,
|
||||
'content': template_content,
|
||||
'service': sample_service.id
|
||||
}
|
||||
resp = client.put(
|
||||
url_for('service.update_template',
|
||||
service_id=sample_service.id,
|
||||
template_id="123"),
|
||||
data=json.dumps(data),
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
assert resp.status_code == 404
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert "Template not found" in json_resp['message']
|
||||
assert template_name != sample_template.name
|
||||
|
||||
|
||||
def test_create_template_unicode_content(notify_api, notify_db, notify_db_session, sample_service):
|
||||
"""
|
||||
Tests POST endpoint '/<service_id>/template/<template_id>' a template is
|
||||
created and the content encoding is respected after saving and loading
|
||||
from the db.
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
assert Template.query.count() == 0
|
||||
template_name = "template name"
|
||||
template_type = "sms"
|
||||
template_content = 'Россия'
|
||||
data = {
|
||||
'name': template_name,
|
||||
'template_type': template_type,
|
||||
'content': template_content,
|
||||
'service': sample_service.id
|
||||
}
|
||||
resp = client.post(
|
||||
url_for('service.create_template', service_id=sample_service.id),
|
||||
data=json.dumps(data),
|
||||
headers=[('Content-Type', 'application/json')])
|
||||
assert resp.status_code == 201
|
||||
assert Template.query.count() == 1
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert json_resp['data']['name'] == template_name
|
||||
assert json_resp['data']['template_type'] == template_type
|
||||
assert json_resp['data']['content'] == template_content
|
||||
|
||||
0
tests/app/template/__init__.py
Normal file
0
tests/app/template/__init__.py
Normal file
31
tests/app/template/test_rest.py
Normal file
31
tests/app/template/test_rest.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import json
|
||||
from app.models import Template
|
||||
from flask import url_for
|
||||
|
||||
|
||||
def test_get_template_list(notify_api, notify_db, notify_db_session, sample_template):
|
||||
"""
|
||||
Tests GET endpoint '/' to retrieve entire template list.
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
response = client.get(url_for('template.get_template'))
|
||||
assert response.status_code == 200
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert len(json_resp['data']) == 1
|
||||
assert json_resp['data'][0]['name'] == sample_template.name
|
||||
assert json_resp['data'][0]['id'] == sample_template.id
|
||||
|
||||
|
||||
def test_get_template(notify_api, notify_db, notify_db_session, sample_template):
|
||||
"""
|
||||
Tests GET endpoint '/<template_id>' to retrieve a single template.
|
||||
"""
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
resp = client.get(url_for(
|
||||
'template.get_template', template_id=sample_template.id))
|
||||
assert resp.status_code == 200
|
||||
json_resp = json.loads(resp.get_data(as_text=True))
|
||||
assert json_resp['data']['name'] == sample_template.name
|
||||
assert json_resp['data']['id'] == sample_template.id
|
||||
Reference in New Issue
Block a user