mirror of
https://github.com/GSA/notifications-api.git
synced 2026-08-18 13:38:53 -04:00
Merge branch 'master' into sent-status
This commit is contained in:
@@ -95,6 +95,7 @@ def register_blueprint(application):
|
|||||||
from app.notifications.receive_notifications import receive_notifications_blueprint
|
from app.notifications.receive_notifications import receive_notifications_blueprint
|
||||||
from app.notifications.notifications_ses_callback import ses_callback_blueprint
|
from app.notifications.notifications_ses_callback import ses_callback_blueprint
|
||||||
from app.notifications.notifications_sms_callback import sms_callback_blueprint
|
from app.notifications.notifications_sms_callback import sms_callback_blueprint
|
||||||
|
from app.notifications.notifications_letter_callback import letter_callback_blueprint
|
||||||
from app.authentication.auth import requires_admin_auth, requires_auth, requires_no_auth
|
from app.authentication.auth import requires_admin_auth, requires_auth, requires_no_auth
|
||||||
from app.letters.send_letter_jobs import letter_job
|
from app.letters.send_letter_jobs import letter_job
|
||||||
|
|
||||||
@@ -155,6 +156,9 @@ def register_blueprint(application):
|
|||||||
letter_job.before_request(requires_admin_auth)
|
letter_job.before_request(requires_admin_auth)
|
||||||
application.register_blueprint(letter_job)
|
application.register_blueprint(letter_job)
|
||||||
|
|
||||||
|
letter_callback_blueprint.before_request(requires_no_auth)
|
||||||
|
application.register_blueprint(letter_callback_blueprint)
|
||||||
|
|
||||||
|
|
||||||
def register_v2_blueprints(application):
|
def register_v2_blueprints(application):
|
||||||
from app.v2.notifications.post_notifications import v2_notification_blueprint as post_notifications
|
from app.v2.notifications.post_notifications import v2_notification_blueprint as post_notifications
|
||||||
|
|||||||
@@ -443,6 +443,7 @@ class ProviderDetails(db.Model):
|
|||||||
updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow)
|
updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow)
|
||||||
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=True)
|
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=True)
|
||||||
created_by = db.relationship('User')
|
created_by = db.relationship('User')
|
||||||
|
supports_international = db.Column(db.Boolean, nullable=False, default=False)
|
||||||
|
|
||||||
|
|
||||||
class ProviderDetailsHistory(db.Model, HistoryModel):
|
class ProviderDetailsHistory(db.Model, HistoryModel):
|
||||||
@@ -458,6 +459,7 @@ class ProviderDetailsHistory(db.Model, HistoryModel):
|
|||||||
updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow)
|
updated_at = db.Column(db.DateTime, nullable=True, onupdate=datetime.datetime.utcnow)
|
||||||
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=True)
|
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=True)
|
||||||
created_by = db.relationship('User')
|
created_by = db.relationship('User')
|
||||||
|
supports_international = db.Column(db.Boolean, nullable=False, default=False)
|
||||||
|
|
||||||
|
|
||||||
JOB_STATUS_PENDING = 'pending'
|
JOB_STATUS_PENDING = 'pending'
|
||||||
@@ -664,6 +666,12 @@ class Notification(db.Model):
|
|||||||
foreign(template_version) == remote(TemplateHistory.version)
|
foreign(template_version) == remote(TemplateHistory.version)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
client_reference = db.Column(db.String, index=True, nullable=True)
|
||||||
|
|
||||||
|
international = db.Column(db.Boolean, nullable=False, default=False)
|
||||||
|
phone_prefix = db.Column(db.String, nullable=True)
|
||||||
|
rate_multiplier = db.Column(db.Numeric(), nullable=True)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def personalisation(self):
|
def personalisation(self):
|
||||||
if self._personalisation:
|
if self._personalisation:
|
||||||
@@ -837,6 +845,10 @@ class NotificationHistory(db.Model, HistoryModel):
|
|||||||
reference = db.Column(db.String, nullable=True, index=True)
|
reference = db.Column(db.String, nullable=True, index=True)
|
||||||
client_reference = db.Column(db.String, nullable=True)
|
client_reference = db.Column(db.String, nullable=True)
|
||||||
|
|
||||||
|
international = db.Column(db.Boolean, nullable=False, default=False)
|
||||||
|
phone_prefix = db.Column(db.String, nullable=True)
|
||||||
|
rate_multiplier = db.Column(db.Numeric(), nullable=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_original(cls, notification):
|
def from_original(cls, notification):
|
||||||
history = super().from_original(notification)
|
history = super().from_original(notification)
|
||||||
|
|||||||
39
app/notifications/notifications_letter_callback.py
Normal file
39
app/notifications/notifications_letter_callback.py
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from flask import (
|
||||||
|
Blueprint,
|
||||||
|
jsonify,
|
||||||
|
request,
|
||||||
|
current_app,
|
||||||
|
json
|
||||||
|
)
|
||||||
|
|
||||||
|
from app import statsd_client
|
||||||
|
from app.clients.email.aws_ses import get_aws_responses
|
||||||
|
from app.dao import (
|
||||||
|
notifications_dao
|
||||||
|
)
|
||||||
|
|
||||||
|
from app.notifications.process_client_response import validate_callback_data
|
||||||
|
|
||||||
|
letter_callback_blueprint = Blueprint('notifications_letter_callback', __name__)
|
||||||
|
|
||||||
|
from app.errors import (
|
||||||
|
register_errors,
|
||||||
|
InvalidRequest
|
||||||
|
)
|
||||||
|
|
||||||
|
register_errors(letter_callback_blueprint)
|
||||||
|
|
||||||
|
|
||||||
|
@letter_callback_blueprint.route('/notifications/letter/dvla', methods=['POST'])
|
||||||
|
def process_letter_response():
|
||||||
|
try:
|
||||||
|
dvla_request = json.loads(request.data)
|
||||||
|
current_app.logger.info(dvla_request)
|
||||||
|
return jsonify(
|
||||||
|
result="success", message="DVLA callback succeeded"
|
||||||
|
), 200
|
||||||
|
except ValueError:
|
||||||
|
error = "DVLA callback failed: invalid json"
|
||||||
|
raise InvalidRequest(error, status_code=400)
|
||||||
27
migrations/versions/0076_add_intl_flag_to_provider.py
Normal file
27
migrations/versions/0076_add_intl_flag_to_provider.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 0076_add_intl_flag_to_provider
|
||||||
|
Revises: 0075_create_rates_table
|
||||||
|
Create Date: 2017-04-25 09:44:13.194164
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '0076_add_intl_flag_to_provider'
|
||||||
|
down_revision = '0075_create_rates_table'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('provider_details', sa.Column('supports_international', sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||||
|
op.add_column('provider_details_history', sa.Column('supports_international', sa.Boolean(), nullable=False, server_default=sa.false()))
|
||||||
|
|
||||||
|
op.execute("UPDATE provider_details SET supports_international=True WHERE identifier='mmg'")
|
||||||
|
op.execute("UPDATE provider_details_history SET supports_international=True WHERE identifier='mmg'")
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_column('provider_details_history', 'supports_international')
|
||||||
|
op.drop_column('provider_details', 'supports_international')
|
||||||
32
migrations/versions/0077_add_intl_notification.py
Normal file
32
migrations/versions/0077_add_intl_notification.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: 0077_add_intl_notification
|
||||||
|
Revises: 0076_add_intl_flag_to_provider
|
||||||
|
Create Date: 2017-04-25 11:34:43.229494
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '0077_add_intl_notification'
|
||||||
|
down_revision = '0076_add_intl_flag_to_provider'
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
op.add_column('notification_history', sa.Column('international', sa.Boolean(), nullable=True))
|
||||||
|
op.add_column('notification_history', sa.Column('phone_prefix', sa.String(), nullable=True))
|
||||||
|
op.add_column('notification_history', sa.Column('rate_multiplier', sa.Numeric(), nullable=True))
|
||||||
|
op.add_column('notifications', sa.Column('international', sa.Boolean(), nullable=True))
|
||||||
|
op.add_column('notifications', sa.Column('phone_prefix', sa.String(), nullable=True))
|
||||||
|
op.add_column('notifications', sa.Column('rate_multiplier', sa.Numeric(), nullable=True))
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_column('notifications', 'rate_multiplier')
|
||||||
|
op.drop_column('notifications', 'phone_prefix')
|
||||||
|
op.drop_column('notifications', 'international')
|
||||||
|
op.drop_column('notification_history', 'rate_multiplier')
|
||||||
|
op.drop_column('notification_history', 'phone_prefix')
|
||||||
|
op.drop_column('notification_history', 'international')
|
||||||
@@ -13,6 +13,16 @@ from app.models import NotificationStatistics
|
|||||||
from tests.app.conftest import sample_notification as create_sample_notification
|
from tests.app.conftest import sample_notification as create_sample_notification
|
||||||
|
|
||||||
|
|
||||||
|
def test_dvla_callback_should_not_need_auth(client):
|
||||||
|
data = json.dumps({"somekey": "somevalue"})
|
||||||
|
response = client.post(
|
||||||
|
path='/notifications/letter/dvla',
|
||||||
|
data=data,
|
||||||
|
headers=[('Content-Type', 'application/json')])
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
def test_firetext_callback_should_not_need_auth(client, mocker):
|
def test_firetext_callback_should_not_need_auth(client, mocker):
|
||||||
mocker.patch('app.statsd_client.incr')
|
mocker.patch('app.statsd_client.incr')
|
||||||
response = client.post(
|
response = client.post(
|
||||||
|
|||||||
@@ -169,18 +169,21 @@ def test_persist_notification_with_optionals(sample_job, sample_api_key, mocker)
|
|||||||
'app.notifications.process_notifications.redis_store.get_all_from_hash')
|
'app.notifications.process_notifications.redis_store.get_all_from_hash')
|
||||||
n_id = uuid.uuid4()
|
n_id = uuid.uuid4()
|
||||||
created_at = datetime.datetime(2016, 11, 11, 16, 8, 18)
|
created_at = datetime.datetime(2016, 11, 11, 16, 8, 18)
|
||||||
persist_notification(template_id=sample_job.template.id,
|
persist_notification(
|
||||||
template_version=sample_job.template.version,
|
template_id=sample_job.template.id,
|
||||||
recipient='+447111111111',
|
template_version=sample_job.template.version,
|
||||||
service=sample_job.service,
|
recipient='+447111111111',
|
||||||
personalisation=None, notification_type='sms',
|
service=sample_job.service,
|
||||||
api_key_id=sample_api_key.id,
|
personalisation=None,
|
||||||
key_type=sample_api_key.key_type,
|
notification_type='sms',
|
||||||
created_at=created_at,
|
api_key_id=sample_api_key.id,
|
||||||
job_id=sample_job.id,
|
key_type=sample_api_key.key_type,
|
||||||
job_row_number=10,
|
created_at=created_at,
|
||||||
client_reference="ref from client",
|
job_id=sample_job.id,
|
||||||
notification_id=n_id)
|
job_row_number=10,
|
||||||
|
client_reference="ref from client",
|
||||||
|
notification_id=n_id
|
||||||
|
)
|
||||||
assert Notification.query.count() == 1
|
assert Notification.query.count() == 1
|
||||||
assert NotificationHistory.query.count() == 1
|
assert NotificationHistory.query.count() == 1
|
||||||
persisted_notification = Notification.query.all()[0]
|
persisted_notification = Notification.query.all()[0]
|
||||||
@@ -192,6 +195,9 @@ def test_persist_notification_with_optionals(sample_job, sample_api_key, mocker)
|
|||||||
mock_service_template_cache.assert_called_once_with(cache_key_for_service_template_counter(sample_job.service_id))
|
mock_service_template_cache.assert_called_once_with(cache_key_for_service_template_counter(sample_job.service_id))
|
||||||
assert persisted_notification.client_reference == "ref from client"
|
assert persisted_notification.client_reference == "ref from client"
|
||||||
assert persisted_notification.reference is None
|
assert persisted_notification.reference is None
|
||||||
|
assert persisted_notification.international is False
|
||||||
|
assert persisted_notification.phone_prefix is None
|
||||||
|
assert persisted_notification.rate_multiplier is None
|
||||||
|
|
||||||
|
|
||||||
@freeze_time("2016-01-01 11:09:00.061258")
|
@freeze_time("2016-01-01 11:09:00.061258")
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ def test_get_provider_details_contains_correct_fields(client, notify_db):
|
|||||||
allowed_keys = {
|
allowed_keys = {
|
||||||
"id", "created_by", "display_name",
|
"id", "created_by", "display_name",
|
||||||
"identifier", "priority", 'notification_type',
|
"identifier", "priority", 'notification_type',
|
||||||
"active", "version", "updated_at"
|
"active", "version", "updated_at", "supports_international"
|
||||||
}
|
}
|
||||||
assert allowed_keys == set(json_resp[0].keys())
|
assert allowed_keys == set(json_resp[0].keys())
|
||||||
|
|
||||||
@@ -116,7 +116,7 @@ def test_get_provider_versions_contains_correct_fields(client, notify_db):
|
|||||||
allowed_keys = {
|
allowed_keys = {
|
||||||
"id", "created_by", "display_name",
|
"id", "created_by", "display_name",
|
||||||
"identifier", "priority", 'notification_type',
|
"identifier", "priority", 'notification_type',
|
||||||
"active", "version", "updated_at"
|
"active", "version", "updated_at", "supports_international"
|
||||||
}
|
}
|
||||||
assert allowed_keys == set(json_resp[0].keys())
|
assert allowed_keys == set(json_resp[0].keys())
|
||||||
|
|
||||||
|
|||||||
@@ -224,6 +224,7 @@ def test_create_service(client, sample_user):
|
|||||||
assert json_resp['data']['name'] == 'created service'
|
assert json_resp['data']['name'] == 'created service'
|
||||||
assert not json_resp['data']['research_mode']
|
assert not json_resp['data']['research_mode']
|
||||||
assert not json_resp['data']['can_send_letters']
|
assert not json_resp['data']['can_send_letters']
|
||||||
|
assert not json_resp['data']['can_send_international_sms']
|
||||||
|
|
||||||
|
|
||||||
def test_should_not_create_service_with_missing_user_id_field(notify_api, fake_uuid):
|
def test_should_not_create_service_with_missing_user_id_field(notify_api, fake_uuid):
|
||||||
@@ -419,10 +420,12 @@ def test_update_service_flags(notify_api, sample_service):
|
|||||||
assert json_resp['data']['name'] == sample_service.name
|
assert json_resp['data']['name'] == sample_service.name
|
||||||
assert json_resp['data']['research_mode'] is False
|
assert json_resp['data']['research_mode'] is False
|
||||||
assert json_resp['data']['can_send_letters'] is False
|
assert json_resp['data']['can_send_letters'] is False
|
||||||
|
assert json_resp['data']['can_send_international_sms'] is False
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'research_mode': True,
|
'research_mode': True,
|
||||||
'can_send_letters': True
|
'can_send_letters': True,
|
||||||
|
'can_send_international_sms': True,
|
||||||
}
|
}
|
||||||
|
|
||||||
auth_header = create_authorization_header()
|
auth_header = create_authorization_header()
|
||||||
@@ -436,6 +439,7 @@ def test_update_service_flags(notify_api, sample_service):
|
|||||||
assert resp.status_code == 200
|
assert resp.status_code == 200
|
||||||
assert result['data']['research_mode'] is True
|
assert result['data']['research_mode'] is True
|
||||||
assert result['data']['can_send_letters'] is True
|
assert result['data']['can_send_letters'] is True
|
||||||
|
assert result['data']['can_send_international_sms'] is True
|
||||||
|
|
||||||
|
|
||||||
def test_update_service_research_mode_throws_validation_error(notify_api, sample_service):
|
def test_update_service_research_mode_throws_validation_error(notify_api, sample_service):
|
||||||
|
|||||||
Reference in New Issue
Block a user