mirror of
https://github.com/GSA/notifications-api.git
synced 2026-06-21 13:41:07 -04:00
Merge pull request #1157 from alphagov/ken-add-inbound-number-model-dao
Add inbound number model and DAO
This commit is contained in:
30
app/dao/inbound_numbers_dao.py
Normal file
30
app/dao/inbound_numbers_dao.py
Normal 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)
|
||||
@@ -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"
|
||||
|
||||
|
||||
35
migrations/versions/0115_add_inbound_numbers.py
Normal file
35
migrations/versions/0115_add_inbound_numbers.py
Normal 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')
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
81
tests/app/dao/test_inbound_numbers_dao.py
Normal file
81
tests/app/dao/test_inbound_numbers_dao.py
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
0
tests/app/inbound_number/__init__.py
Normal file
0
tests/app/inbound_number/__init__.py
Normal 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
|
||||
|
||||
Reference in New Issue
Block a user