Merge pull request #1157 from alphagov/ken-add-inbound-number-model-dao

Add inbound number model and DAO
This commit is contained in:
kentsanggds
2017-08-11 15:17:47 +01:00
committed by GitHub
9 changed files with 232 additions and 2 deletions

View File

@@ -0,0 +1,30 @@
from app import db
from app.dao.dao_utils import transactional
from app.models import InboundNumber
def dao_get_inbound_numbers():
return InboundNumber.query.all()
def dao_get_available_inbound_numbers():
return InboundNumber.query.filter(InboundNumber.active, InboundNumber.service_id.is_(None)).all()
def dao_get_inbound_number_for_service(service_id):
return InboundNumber.query.filter(InboundNumber.service_id == service_id).first()
@transactional
def dao_set_inbound_number_to_service(service_id, inbound_number):
inbound_number.service_id = service_id
db.session.add(inbound_number)
@transactional
def dao_set_inbound_number_active_flag(inbound_number_id, active):
inbound_number = InboundNumber.query.filter(InboundNumber.id == inbound_number_id).first()
inbound_number.active = active
db.session.add(inbound_number)

View File

@@ -242,6 +242,36 @@ class Service(db.Model, Versioned):
return cls(**fields)
class InboundNumber(db.Model):
__tablename__ = "inbound_numbers"
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
number = db.Column(db.String(11), unique=True, nullable=False)
provider = db.Column(db.String(), nullable=False)
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), unique=True, index=True, nullable=True)
service = db.relationship(Service, backref=db.backref("inbound_number", uselist=False))
active = db.Column(db.Boolean, index=False, unique=False, nullable=False, default=True)
created_at = db.Column(db.DateTime, default=datetime.datetime.utcnow, nullable=False)
updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow)
def serialize(self):
def serialize_service():
return {
"id": str(self.service_id),
"name": self.service.name
}
return {
"id": str(self.id),
"number": self.number,
"provider": self.provider,
"service": serialize_service() if self.service else None,
"active": self.active,
"created_at": self.created_at.strftime(DATETIME_FORMAT),
"updated_at": self.updated_at.strftime(DATETIME_FORMAT) if self.updated_at else None,
}
class ServicePermission(db.Model):
__tablename__ = "service_permissions"

View File

@@ -0,0 +1,35 @@
"""empty message
Revision ID: 0115_add_inbound_numbers
Revises: 0014_drop_monthly_billing_cols
Create Date: 2017-08-10 17:30:01.507694
"""
# revision identifiers, used by Alembic.
revision = '0115_add_inbound_numbers'
down_revision = '0014_drop_monthly_billing_cols'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
def upgrade():
op.create_table('inbound_numbers',
sa.Column('id', postgresql.UUID(as_uuid=True), nullable=False),
sa.Column('number', sa.String(length=11), nullable=False),
sa.Column('provider', sa.String(), nullable=False),
sa.Column('service_id', postgresql.UUID(as_uuid=True), nullable=True),
sa.Column('active', sa.Boolean(), nullable=False),
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['service_id'], ['services.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('number')
)
op.create_index(op.f('ix_inbound_numbers_service_id'), 'inbound_numbers', ['service_id'], unique=True)
def downgrade():
op.drop_index(op.f('ix_inbound_numbers_service_id'), table_name='inbound_numbers')
op.drop_table('inbound_numbers')

View File

@@ -39,7 +39,14 @@ from app.dao.invited_user_dao import save_invited_user
from app.dao.provider_rates_dao import create_provider_rates
from app.clients.sms.firetext import FiretextClient
from tests import create_authorization_header
from tests.app.db import create_user, create_template, create_notification, create_api_key
from tests.app.db import (
create_user,
create_template,
create_notification,
create_service,
create_api_key,
create_inbound_number
)
@pytest.yield_fixture
@@ -983,6 +990,16 @@ def sample_provider_rate(notify_db, notify_db_session, valid_from=None, rate=Non
)
@pytest.fixture
def sample_inbound_numbers(notify_db, notify_db_session, sample_service):
service = create_service(service_name='sample service 2')
inbound_numbers = []
inbound_numbers.append(create_inbound_number(number='1', provider='mmg'))
inbound_numbers.append(create_inbound_number(number='2', provider='mmg', active=False, service_id=service.id))
inbound_numbers.append(create_inbound_number(number='3', provider='firetext', service_id=sample_service.id))
return inbound_numbers
@pytest.fixture
def restore_provider_details(notify_db, notify_db_session):
"""

View File

@@ -0,0 +1,81 @@
import pytest
from sqlalchemy.exc import IntegrityError
from app.dao.inbound_numbers_dao import (
dao_get_inbound_numbers,
dao_get_inbound_number_for_service,
dao_get_available_inbound_numbers,
dao_set_inbound_number_to_service,
dao_set_inbound_number_active_flag
)
from app.models import InboundNumber
from tests.app.db import create_service, create_inbound_number
def test_get_inbound_numbers(notify_db, notify_db_session, sample_inbound_numbers):
res = dao_get_inbound_numbers()
assert len(res) == len(sample_inbound_numbers)
assert res == sample_inbound_numbers
def test_get_available_inbound_numbers(notify_db, notify_db_session):
inbound_number = create_inbound_number(number='1')
res = dao_get_available_inbound_numbers()
assert len(res) == 1
assert res[0] == inbound_number
def test_set_service_id_on_inbound_number(notify_db, notify_db_session, sample_inbound_numbers):
service = create_service(service_name='test service')
numbers = dao_get_available_inbound_numbers()
dao_set_inbound_number_to_service(service.id, numbers[0])
res = InboundNumber.query.filter(InboundNumber.service_id == service.id).all()
assert len(res) == 1
assert res[0].service_id == service.id
def test_after_setting_service_id_that_inbound_number_is_unavailable(
notify_db, notify_db_session, sample_inbound_numbers):
service = create_service(service_name='test service')
numbers = dao_get_available_inbound_numbers()
assert len(numbers) == 1
dao_set_inbound_number_to_service(service.id, numbers[0])
res = dao_get_available_inbound_numbers()
assert len(res) == 0
def test_setting_a_service_twice_will_raise_an_error(notify_db, notify_db_session):
create_inbound_number(number='1')
create_inbound_number(number='2')
service = create_service(service_name='test service')
numbers = dao_get_available_inbound_numbers()
dao_set_inbound_number_to_service(service.id, numbers[0])
with pytest.raises(IntegrityError) as e:
dao_set_inbound_number_to_service(service.id, numbers[1])
assert 'duplicate key value violates unique constraint' in str(e.value)
@pytest.mark.parametrize("active", [True, False])
def test_set_inbound_number_active_flag(notify_db, notify_db_session, sample_service, active):
inbound_number = create_inbound_number(number='1')
dao_set_inbound_number_to_service(sample_service.id, inbound_number)
dao_set_inbound_number_active_flag(inbound_number.id, active=active)
inbound_number = dao_get_inbound_number_for_service(sample_service.id)
assert inbound_number.active is active

View File

@@ -7,6 +7,10 @@ from sqlalchemy.orm.exc import FlushError, NoResultFound
from sqlalchemy.exc import IntegrityError
from freezegun import freeze_time
from app import db
from app.dao.inbound_numbers_dao import (
dao_set_inbound_number_to_service,
dao_get_available_inbound_numbers
)
from app.dao.services_dao import (
dao_create_service,
dao_add_user_to_service,
@@ -896,3 +900,13 @@ def test_dao_fetch_services_by_sms_sender(notify_db_session):
services = dao_fetch_services_by_sms_sender('foo')
assert {foo1.id, foo2.id} == {x.id for x in services}
def test_dao_allocating_inbound_number_shows_on_service(notify_db_session, sample_inbound_numbers):
inbound_numbers = dao_get_available_inbound_numbers()
service = create_service(service_name='test service')
dao_set_inbound_number_to_service(service.id, inbound_numbers[0])
assert service.inbound_number.number == inbound_numbers[0].number

View File

@@ -15,6 +15,7 @@ from app.models import (
Rate,
Job,
InboundSms,
InboundNumber,
Organisation,
EMAIL_TYPE,
SMS_TYPE,
@@ -276,3 +277,16 @@ def create_api_key(service, key_type=KEY_TYPE_NORMAL):
db.session.add(api_key)
db.session.commit()
return api_key
def create_inbound_number(number, provider='mmg', active=True, service_id=None):
inbound_number = InboundNumber(
id=uuid.uuid4(),
number=number,
provider=provider,
active=active,
service_id=service_id
)
db.session.add(inbound_number)
db.session.commit()
return inbound_number

View File

View File

@@ -19,7 +19,7 @@ from tests.app.conftest import (
sample_template as create_sample_template,
sample_notification_with_job as create_sample_notification_with_job
)
from tests.app.db import create_notification
from tests.app.db import create_notification, create_service, create_inbound_number
@pytest.mark.parametrize('mobile_number', [
@@ -218,3 +218,12 @@ def test_email_notification_serializes_with_subject(client, sample_email_templat
def test_letter_notification_serializes_with_subject(client, sample_letter_template):
res = sample_letter_template.serialize()
assert res['subject'] == 'Template subject'
def test_inbound_number_serializes_with_service(client, notify_db_session):
service = create_service()
inbound_number = create_inbound_number(number='1', service_id=service.id)
serialized_inbound_number = inbound_number.serialize()
assert serialized_inbound_number.get('id') == str(inbound_number.id)
assert serialized_inbound_number.get('service').get('id') == str(inbound_number.service.id)
assert serialized_inbound_number.get('service').get('name') == inbound_number.service.name