Merge pull request #3036 from alphagov/cbc-proxy-refactor

Cbc proxy refactor
This commit is contained in:
Leo Hemsted
2020-11-19 15:50:02 +00:00
committed by GitHub
7 changed files with 49 additions and 61 deletions

View File

@@ -22,7 +22,7 @@ from werkzeug.exceptions import HTTPException as WerkzeugHTTPException
from werkzeug.local import LocalProxy from werkzeug.local import LocalProxy
from app.celery.celery import NotifyCelery from app.celery.celery import NotifyCelery
from app.clients import Clients from app.clients import NotificationProviderClients
from app.clients.cbc_proxy import CBCProxyClient, CBCProxyNoopClient from app.clients.cbc_proxy import CBCProxyClient, CBCProxyNoopClient
from app.clients.document_download import DocumentDownloadClient from app.clients.document_download import DocumentDownloadClient
from app.clients.email.aws_ses import AwsSesClient from app.clients.email.aws_ses import AwsSesClient
@@ -65,7 +65,7 @@ cbc_proxy_client = CBCProxyNoopClient()
document_download_client = DocumentDownloadClient() document_download_client = DocumentDownloadClient()
metrics = GDSMetrics() metrics = GDSMetrics()
clients = Clients() notification_provider_clients = NotificationProviderClients()
api_user = LocalProxy(lambda: _request_ctx_stack.top.api_user) api_user = LocalProxy(lambda: _request_ctx_stack.top.api_user)
authenticated_service = LocalProxy(lambda: _request_ctx_stack.top.authenticated_service) authenticated_service = LocalProxy(lambda: _request_ctx_stack.top.authenticated_service)
@@ -106,7 +106,7 @@ def create_app(application):
) )
# If a stub url is provided for SES, then use the stub client rather than the real SES boto client # If a stub url is provided for SES, then use the stub client rather than the real SES boto client
email_clients = [aws_ses_stub_client] if application.config['SES_STUB_URL'] else [aws_ses_client] email_clients = [aws_ses_stub_client] if application.config['SES_STUB_URL'] else [aws_ses_client]
clients.init_app(sms_clients=[firetext_client, mmg_client], email_clients=email_clients) notification_provider_clients.init_app(sms_clients=[firetext_client, mmg_client], email_clients=email_clients)
notify_celery.init_app(application) notify_celery.init_app(application)
encryption.init_app(application) encryption.init_app(application)

View File

@@ -17,7 +17,7 @@ STATISTICS_DELIVERED = 'delivered'
STATISTICS_FAILURE = 'failure' STATISTICS_FAILURE = 'failure'
class Clients(object): class NotificationProviderClients(object):
sms_clients = {} sms_clients = {}
email_clients = {} email_clients = {}

View File

@@ -20,6 +20,10 @@ import boto3
# the preceeding Alert message in the references field # the preceeding Alert message in the references field
class CBCProxyException(Exception):
pass
# Noop = no operation # Noop = no operation
class CBCProxyNoopClient: class CBCProxyNoopClient:
@@ -72,72 +76,53 @@ class CBCProxyClient:
aws_secret_access_key=app.config['CBC_PROXY_AWS_SECRET_ACCESS_KEY'], aws_secret_access_key=app.config['CBC_PROXY_AWS_SECRET_ACCESS_KEY'],
) )
def send_canary( def _invoke_lambda(self, function_name, payload):
self, payload_bytes = bytes(json.dumps(payload), encoding='utf8')
identifier,
):
payload_bytes = bytes(json.dumps({
'identifier': identifier,
}), encoding='utf8')
result = self._lambda_client.invoke( result = self._lambda_client.invoke(
FunctionName='canary', FunctionName=function_name,
InvocationType='RequestResponse', InvocationType='RequestResponse',
Payload=payload_bytes, Payload=payload_bytes,
) )
if result['StatusCode'] > 299: if result['StatusCode'] > 299:
raise Exception('Could not invoke lambda') raise CBCProxyException('Could not invoke lambda')
if 'FunctionError' in result: if 'FunctionError' in result:
raise Exception('Function exited with unhandled exception') raise CBCProxyException('Function exited with unhandled exception')
return result
def send_canary(
self,
identifier,
):
self._invoke_lambda(function_name='canary', payload={'identifier': identifier})
def send_link_test( def send_link_test(
self, self,
identifier, identifier,
): ):
payload_bytes = bytes(json.dumps({ payload = {'message_type': 'test', 'identifier': identifier}
'message_type': 'test',
'identifier': identifier,
}), encoding='utf8')
result = self._lambda_client.invoke( self._invoke_lambda(function_name='bt-ee-1-proxy', payload=payload)
FunctionName='bt-ee-1-proxy',
InvocationType='RequestResponse',
Payload=payload_bytes,
)
if result['StatusCode'] > 299:
raise Exception('Could not invoke lambda')
if 'FunctionError' in result:
raise Exception('Function exited with unhandled exception')
def create_and_send_broadcast( def create_and_send_broadcast(
self, self,
identifier, headline, description, areas, identifier, headline, description, areas,
sent, expires, sent, expires,
): ):
payload_bytes = bytes(json.dumps({ payload = {
'message_type': 'alert', 'message_type': 'alert',
'identifier': identifier, 'identifier': identifier,
'headline': headline, 'headline': headline,
'description': description, 'description': description,
'areas': areas, 'areas': areas,
'sent': sent, 'expires': expires, 'sent': sent,
}), encoding='utf8') 'expires': expires,
}
result = self._lambda_client.invoke( self._invoke_lambda(function_name='bt-ee-1-proxy', payload=payload)
FunctionName='bt-ee-1-proxy',
InvocationType='RequestResponse',
Payload=payload_bytes,
)
if result['StatusCode'] > 299:
raise Exception('Could not invoke lambda')
if 'FunctionError' in result:
raise Exception('Function exited with unhandled exception')
# We have not implementated updating a broadcast # We have not implementated updating a broadcast
def update_and_send_broadcast( def update_and_send_broadcast(

View File

@@ -9,7 +9,7 @@ from notifications_utils.recipients import (
) )
from notifications_utils.template import HTMLEmailTemplate, PlainTextEmailTemplate, SMSMessageTemplate from notifications_utils.template import HTMLEmailTemplate, PlainTextEmailTemplate, SMSMessageTemplate
from app import clients, statsd_client, create_uuid from app import notification_provider_clients, statsd_client, create_uuid
from app.dao.notifications_dao import ( from app.dao.notifications_dao import (
dao_update_notification dao_update_notification
) )
@@ -144,7 +144,7 @@ def provider_to_use(notification_type, international=False):
chosen_provider = random.choices(active_providers, weights=[p.priority for p in active_providers])[0] chosen_provider = random.choices(active_providers, weights=[p.priority for p in active_providers])[0]
return clients.get_client_by_name_and_type(chosen_provider.identifier, notification_type) return notification_provider_clients.get_client_by_name_and_type(chosen_provider.identifier, notification_type)
def get_logo_url(base_url, logo_file): def get_logo_url(base_url, logo_file):

View File

@@ -3,7 +3,7 @@ import uuid
import pytest import pytest
from app.clients.cbc_proxy import CBCProxyClient from app.clients.cbc_proxy import CBCProxyClient, CBCProxyException
@pytest.fixture(scope='function') @pytest.fixture(scope='function')
@@ -116,7 +116,7 @@ def test_cbc_proxy_create_and_send_handles_invoke_error(mocker, cbc_proxy):
'StatusCode': 400, 'StatusCode': 400,
} }
with pytest.raises(Exception) as e: with pytest.raises(CBCProxyException) as e:
cbc_proxy.create_and_send_broadcast( cbc_proxy.create_and_send_broadcast(
identifier=identifier, identifier=identifier,
headline=headline, headline=headline,
@@ -165,7 +165,7 @@ def test_cbc_proxy_create_and_send_handles_function_error(mocker, cbc_proxy):
'FunctionError': 'something', 'FunctionError': 'something',
} }
with pytest.raises(Exception) as e: with pytest.raises(CBCProxyException) as e:
cbc_proxy.create_and_send_broadcast( cbc_proxy.create_and_send_broadcast(
identifier=identifier, identifier=identifier,
headline=headline, headline=headline,
@@ -174,7 +174,7 @@ def test_cbc_proxy_create_and_send_handles_function_error(mocker, cbc_proxy):
sent=sent, expires=expires, sent=sent, expires=expires,
) )
assert e.match('Function exited with unhandled exception') assert e.match('Function exited with unhandled exception')
ld_client_mock.invoke.assert_called_once_with( ld_client_mock.invoke.assert_called_once_with(
FunctionName='bt-ee-1-proxy', FunctionName='bt-ee-1-proxy',
@@ -226,12 +226,12 @@ def test_cbc_proxy_send_canary_handles_invoke_error(mocker, cbc_proxy):
'StatusCode': 400, 'StatusCode': 400,
} }
with pytest.raises(Exception) as e: with pytest.raises(CBCProxyException) as e:
cbc_proxy.send_canary( cbc_proxy.send_canary(
identifier=identifier, identifier=identifier,
) )
assert e.match('Function exited with unhandled exception') assert e.match('Could not invoke lambda')
ld_client_mock.invoke.assert_called_once_with( ld_client_mock.invoke.assert_called_once_with(
FunctionName='canary', FunctionName='canary',
@@ -254,12 +254,12 @@ def test_cbc_proxy_send_canary_handles_function_error(mocker, cbc_proxy):
'FunctionError': 'something', 'FunctionError': 'something',
} }
with pytest.raises(Exception) as e: with pytest.raises(CBCProxyException) as e:
cbc_proxy.send_canary( cbc_proxy.send_canary(
identifier=identifier, identifier=identifier,
) )
assert e.match('Could not invoke lambda') assert e.match('Function exited with unhandled exception')
ld_client_mock.invoke.assert_called_once_with( ld_client_mock.invoke.assert_called_once_with(
FunctionName='canary', FunctionName='canary',
@@ -312,12 +312,12 @@ def test_cbc_proxy_send_link_test_handles_invoke_error(mocker, cbc_proxy):
'StatusCode': 400, 'StatusCode': 400,
} }
with pytest.raises(Exception) as e: with pytest.raises(CBCProxyException) as e:
cbc_proxy.send_link_test( cbc_proxy.send_link_test(
identifier=identifier, identifier=identifier,
) )
assert e.match('Function exited with unhandled exception') assert e.match('Could not invoke lambda')
ld_client_mock.invoke.assert_called_once_with( ld_client_mock.invoke.assert_called_once_with(
FunctionName='bt-ee-1-proxy', FunctionName='bt-ee-1-proxy',
@@ -340,12 +340,12 @@ def test_cbc_proxy_send_link_test_handles_function_error(mocker, cbc_proxy):
'FunctionError': 'something', 'FunctionError': 'something',
} }
with pytest.raises(Exception) as e: with pytest.raises(CBCProxyException) as e:
cbc_proxy.send_link_test( cbc_proxy.send_link_test(
identifier=identifier, identifier=identifier,
) )
assert e.match('Could not invoke lambda') assert e.match('Function exited with unhandled exception')
ld_client_mock.invoke.assert_called_once_with( ld_client_mock.invoke.assert_called_once_with(
FunctionName='bt-ee-1-proxy', FunctionName='bt-ee-1-proxy',

View File

@@ -5,7 +5,7 @@ from freezegun import freeze_time
from sqlalchemy.sql import desc from sqlalchemy.sql import desc
from app.models import ProviderDetails, ProviderDetailsHistory from app.models import ProviderDetails, ProviderDetailsHistory
from app import clients from app import notification_provider_clients
from app.dao.provider_details_dao import ( from app.dao.provider_details_dao import (
get_alternative_sms_provider, get_alternative_sms_provider,
get_provider_details_by_identifier, get_provider_details_by_identifier,
@@ -76,7 +76,7 @@ def test_can_get_email_providers(notify_db_session):
def test_should_not_error_if_any_provider_in_code_not_in_database(restore_provider_details): def test_should_not_error_if_any_provider_in_code_not_in_database(restore_provider_details):
ProviderDetails.query.filter_by(identifier='mmg').delete() ProviderDetails.query.filter_by(identifier='mmg').delete()
assert clients.get_sms_client('mmg') assert notification_provider_clients.get_sms_client('mmg')
@freeze_time('2000-01-01T00:00:00') @freeze_time('2000-01-01T00:00:00')

View File

@@ -9,7 +9,7 @@ from notifications_utils.recipients import validate_and_format_phone_number
from requests import HTTPError from requests import HTTPError
import app import app
from app import clients, mmg_client, firetext_client from app import notification_provider_clients, mmg_client, firetext_client
from app.dao import notifications_dao from app.dao import notifications_dao
from app.dao.provider_details_dao import get_provider_details_by_identifier from app.dao.provider_details_dao import get_provider_details_by_identifier
from app.delivery import send_to_providers from app.delivery import send_to_providers
@@ -535,7 +535,10 @@ def test_update_notification_to_sending_does_not_update_status_from_a_final_stat
): ):
template = create_template(sample_service) template = create_template(sample_service)
notification = create_notification(template=template, status=starting_status) notification = create_notification(template=template, status=starting_status)
send_to_providers.update_notification_to_sending(notification, clients.get_client_by_name_and_type("mmg", "sms")) send_to_providers.update_notification_to_sending(
notification,
notification_provider_clients.get_client_by_name_and_type("mmg", "sms")
)
assert notification.status == expected_status assert notification.status == expected_status