diff --git a/app/dao/notifications_dao.py b/app/dao/notifications_dao.py new file mode 100644 index 000000000..be4e56ef6 --- /dev/null +++ b/app/dao/notifications_dao.py @@ -0,0 +1,22 @@ +from app import db +from app.models import Notification + + +def save_notification(notification, update_dict={}): + if update_dict: + update_dict.pop('id', None) + update_dict.pop('job', None) + update_dict.pop('service_id', None) + update_dict.pop('template_id', None) + Notification.query.filter_by(id=notification.id).update(update_dict) + else: + db.session.add(notification) + db.session.commit() + + +def get_notification(job_id, notification_id): + return Notification.query.filter_by(job_id=job_id, id=notification_id).one() + + +def get_notifications_by_job(job_id): + return Notification.query.filter_by(job_id=job_id).all() diff --git a/app/models.py b/app/models.py index dc260836b..22337a6a5 100644 --- a/app/models.py +++ b/app/models.py @@ -189,3 +189,32 @@ class VerifyCode(db.Model): def check_code(self, cde): return check_hash(cde, self._code) + + +NOTIFICATION_STATUS_TYPES = ['sent', 'failed'] + + +class Notification(db.Model): + + __tablename__ = 'notifications' + + id = db.Column(UUID(as_uuid=True), primary_key=True) + to = db.Column(db.String, nullable=False) + job_id = db.Column(UUID(as_uuid=True), db.ForeignKey('jobs.id'), index=True, unique=False, nullable=False) + job = db.relationship('Job', backref=db.backref('notifications', lazy='dynamic')) + service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, unique=False) + template_id = db.Column(db.BigInteger, db.ForeignKey('templates.id'), index=True, unique=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) + status = db.Column( + db.Enum(*NOTIFICATION_STATUS_TYPES, name='notification_status_types'), nullable=False, default='sent') diff --git a/migrations/versions/0013_add_notifications.py b/migrations/versions/0013_add_notifications.py new file mode 100644 index 000000000..61fd55a66 --- /dev/null +++ b/migrations/versions/0013_add_notifications.py @@ -0,0 +1,47 @@ +"""empty message + +Revision ID: 0013_add_notifications +Revises: 0012_add_status_to_job +Create Date: 2016-02-09 11:14:46.708551 + +""" + +# revision identifiers, used by Alembic. +revision = '0013_add_notifications' +down_revision = '0012_add_status_to_job' + +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +def upgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.create_table('notifications', + sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('to', sa.String(), nullable=False), + sa.Column('job_id', postgresql.UUID(as_uuid=True), nullable=False), + sa.Column('service_id', postgresql.UUID(as_uuid=True), nullable=True), + sa.Column('template_id', sa.BigInteger(), nullable=True), + sa.Column('created_at', sa.DateTime(), nullable=False), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('status', sa.Enum('sent', 'failed', name='notification_status_types'), nullable=False), + sa.ForeignKeyConstraint(['job_id'], ['jobs.id'], ), + sa.ForeignKeyConstraint(['service_id'], ['services.id'], ), + sa.ForeignKeyConstraint(['template_id'], ['templates.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_index(op.f('ix_notifications_job_id'), 'notifications', ['job_id'], unique=False) + op.create_index(op.f('ix_notifications_service_id'), 'notifications', ['service_id'], unique=False) + op.create_index(op.f('ix_notifications_template_id'), 'notifications', ['template_id'], unique=False) + ### end Alembic commands ### + + +def downgrade(): + ### commands auto generated by Alembic - please adjust! ### + op.drop_index(op.f('ix_notifications_template_id'), table_name='notifications') + op.drop_index(op.f('ix_notifications_service_id'), table_name='notifications') + op.drop_index(op.f('ix_notifications_job_id'), table_name='notifications') + op.drop_table('notifications') + op.get_bind() + op.execute("drop type notification_status_types") + ### end Alembic commands ### diff --git a/tests/app/conftest.py b/tests/app/conftest.py index e1abea888..741d896eb 100644 --- a/tests/app/conftest.py +++ b/tests/app/conftest.py @@ -1,12 +1,13 @@ import pytest from flask import jsonify -from app.models import (User, Service, Template, ApiKey, Job, VerifyCode) +from app.models import (User, Service, Template, ApiKey, Job, VerifyCode, Notification) from app.dao.users_dao import (save_model_user, create_user_code, create_secret_code) from app.dao.services_dao import save_model_service from app.dao.templates_dao import save_model_template from app.dao.api_key_dao import save_model_api_key from app.dao.jobs_dao import save_job +from app.dao.notifications_dao import save_notification import uuid @@ -180,3 +181,31 @@ def mock_secret_code(mocker): mock_class = mocker.patch('app.dao.users_dao.create_secret_code', side_effect=_create) return mock_class + + +@pytest.fixture(scope='function') +def sample_notification(notify_db, + notify_db_session, + service=None, + template=None, + job=None): + if service is None: + service = sample_service(notify_db, notify_db_session) + if template is None: + template = sample_template(notify_db, notify_db_session, service=service) + if job is None: + job = sample_job(notify_db, notify_db_session, service=service, template=template) + + notificaton_id = uuid.uuid4() + to = '+44709123456' + + data = { + 'id': notificaton_id, + 'to': to, + 'job_id': job.id, + 'service_id': service.id, + 'template_id': template.id + } + notification = Notification(**data) + save_notification(notification) + return notification diff --git a/tests/app/dao/test_notification_dao.py b/tests/app/dao/test_notification_dao.py new file mode 100644 index 000000000..23ff6fe23 --- /dev/null +++ b/tests/app/dao/test_notification_dao.py @@ -0,0 +1,74 @@ +import uuid + +from app.models import Notification + +from app.dao.notifications_dao import ( + save_notification, + get_notification, + get_notifications_by_job +) + + +def test_save_notification(notify_db, notify_db_session, sample_template, sample_job): + + assert Notification.query.count() == 0 + + notification_id = uuid.uuid4() + to = '+44709123456' + job_id = sample_job.id + data = { + 'id': notification_id, + 'to': to, + 'job_id': job_id, + 'service_id': sample_template.service.id, + 'template_id': sample_template.id + } + + notification = Notification(**data) + save_notification(notification) + + assert Notification.query.count() == 1 + + notification_from_db = Notification.query.get(notification_id) + + assert data['id'] == notification_from_db.id + assert data['to'] == notification_from_db.to + assert data['job_id'] == notification_from_db.job_id + assert data['service_id'] == notification_from_db.service_id + assert data['template_id'] == notification_from_db.template_id + assert 'sent' == notification_from_db.status + + +def test_get_notification_for_job(notify_db, notify_db_session, sample_notification): + notifcation_from_db = get_notification(sample_notification.job_id, sample_notification.id) + assert sample_notification == notifcation_from_db + + +def test_get_all_notifications_for_job(notify_db, notify_db_session, sample_job): + + from tests.app.conftest import sample_notification + for i in range(0, 5): + sample_notification(notify_db, + notify_db_session, + service=sample_job.service, + template=sample_job.template, + job=sample_job) + + notifcations_from_db = get_notifications_by_job(sample_job.id) + assert len(notifcations_from_db) == 5 + + +def test_update_notification(notify_db, notify_db_session, sample_notification): + assert sample_notification.status == 'sent' + + update_dict = { + 'id': sample_notification.id, + 'service_id': sample_notification.service_id, + 'template_id': sample_notification.template_id, + 'job': sample_notification.job, + 'status': 'failed' + } + + save_notification(sample_notification, update_dict=update_dict) + notification_from_db = Notification.query.get(sample_notification.id) + assert notification_from_db.status == 'failed'