mirror of
https://github.com/GSA/notifications-api.git
synced 2026-06-25 01:39:30 -04:00
Register a before_request event for all blueprints, that defines the authentication requirement.
There are three authentication methods: - requires_no_auth - public endpoint that does not require an Authorisation header - requires_auth - public endpoints that need an API key in the Authorisation header - requires_admin_auth - private endpoint that requires an Authorisation header which contains the API key for the defined as the client admin user
This commit is contained in:
@@ -2,7 +2,7 @@ import os
|
||||
import uuid
|
||||
|
||||
from flask import Flask, _request_ctx_stack
|
||||
from flask import request, url_for, g, jsonify
|
||||
from flask import request, g, jsonify
|
||||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
from flask_marshmallow import Marshmallow
|
||||
from monotonic import monotonic
|
||||
@@ -20,7 +20,6 @@ from app.clients.sms.mmg import MMGClient
|
||||
from app.clients.performance_platform.performance_platform_client import PerformancePlatformClient
|
||||
from app.encryption import Encryption
|
||||
|
||||
|
||||
DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%fZ"
|
||||
DATE_FORMAT = "%Y-%m-%d"
|
||||
|
||||
@@ -77,10 +76,10 @@ def create_app(app_name=None):
|
||||
|
||||
def register_blueprint(application):
|
||||
from app.service.rest import service_blueprint
|
||||
from app.user.rest import user as user_blueprint
|
||||
from app.template.rest import template as template_blueprint
|
||||
from app.user.rest import user_blueprint
|
||||
from app.template.rest import template_blueprint
|
||||
from app.status.healthcheck import status as status_blueprint
|
||||
from app.job.rest import job as job_blueprint
|
||||
from app.job.rest import job_blueprint
|
||||
from app.notifications.rest import notifications as notifications_blueprint
|
||||
from app.invite.rest import invite as invite_blueprint
|
||||
from app.accept_invite.rest import accept_invite
|
||||
@@ -90,49 +89,100 @@ def register_blueprint(application):
|
||||
from app.spec.rest import spec as spec_blueprint
|
||||
from app.organisation.rest import organisation_blueprint
|
||||
from app.delivery.rest import delivery_blueprint
|
||||
from app.notifications.receive_notifications import receive_notifications_blueprint
|
||||
from app.notifications.notifications_ses_callback import ses_callback_blueprint
|
||||
from app.notifications.notifications_sms_callback import sms_callback_blueprint
|
||||
from app.status.healthcheck import status
|
||||
from app.authentication.auth import requires_admin_auth, requires_auth, requires_no_auth
|
||||
|
||||
service_blueprint.before_request(requires_admin_auth)
|
||||
application.register_blueprint(service_blueprint, url_prefix='/service')
|
||||
|
||||
user_blueprint.before_request(requires_admin_auth)
|
||||
application.register_blueprint(user_blueprint, url_prefix='/user')
|
||||
|
||||
template_blueprint.before_request(requires_admin_auth)
|
||||
application.register_blueprint(template_blueprint)
|
||||
|
||||
status_blueprint.before_request(requires_no_auth)
|
||||
application.register_blueprint(status_blueprint)
|
||||
|
||||
notifications_blueprint.before_request(requires_auth)
|
||||
receive_notifications_blueprint.before_request(requires_no_auth)
|
||||
|
||||
ses_callback_blueprint.before_request(requires_no_auth)
|
||||
application.register_blueprint(ses_callback_blueprint)
|
||||
|
||||
sms_callback_blueprint.before_request(requires_no_auth)
|
||||
application.register_blueprint(sms_callback_blueprint)
|
||||
|
||||
receive_notifications_blueprint.before_request(requires_no_auth)
|
||||
application.register_blueprint(receive_notifications_blueprint)
|
||||
|
||||
notifications_blueprint.before_request(requires_auth)
|
||||
application.register_blueprint(notifications_blueprint)
|
||||
|
||||
job_blueprint.before_request(requires_admin_auth)
|
||||
application.register_blueprint(job_blueprint)
|
||||
|
||||
invite_blueprint.before_request(requires_admin_auth)
|
||||
application.register_blueprint(invite_blueprint)
|
||||
|
||||
delivery_blueprint.before_request(requires_admin_auth)
|
||||
application.register_blueprint(delivery_blueprint)
|
||||
|
||||
accept_invite.before_request(requires_admin_auth)
|
||||
application.register_blueprint(accept_invite, url_prefix='/invite')
|
||||
|
||||
template_statistics_blueprint.before_request(requires_admin_auth)
|
||||
application.register_blueprint(template_statistics_blueprint)
|
||||
|
||||
events_blueprint.before_request(requires_admin_auth)
|
||||
application.register_blueprint(events_blueprint)
|
||||
|
||||
provider_details_blueprint.before_request(requires_admin_auth)
|
||||
application.register_blueprint(provider_details_blueprint, url_prefix='/provider-details')
|
||||
|
||||
spec_blueprint.before_request(requires_no_auth)
|
||||
application.register_blueprint(spec_blueprint, url_prefix='/spec')
|
||||
|
||||
organisation_blueprint.before_request(requires_admin_auth)
|
||||
application.register_blueprint(organisation_blueprint, url_prefix='/organisation')
|
||||
|
||||
status.before_request(requires_no_auth)
|
||||
application.register_blueprint(status)
|
||||
|
||||
|
||||
def register_v2_blueprints(application):
|
||||
from app.v2.notifications.post_notifications import notification_blueprint as post_notifications
|
||||
from app.v2.notifications.get_notifications import notification_blueprint as get_notifications
|
||||
from app.v2.notifications.post_notifications import v2_notification_blueprint as post_notifications
|
||||
from app.v2.notifications.get_notifications import v2_notification_blueprint as get_notifications
|
||||
from app.authentication.auth import requires_auth
|
||||
|
||||
post_notifications.before_request(requires_auth)
|
||||
application.register_blueprint(post_notifications)
|
||||
|
||||
get_notifications.before_request(requires_auth)
|
||||
application.register_blueprint(get_notifications)
|
||||
|
||||
|
||||
def init_app(app):
|
||||
@app.before_request
|
||||
def required_authentication():
|
||||
no_auth_req = [
|
||||
url_for('status.show_status'),
|
||||
url_for('notifications.process_ses_response'),
|
||||
url_for('notifications.process_firetext_response'),
|
||||
url_for('notifications.process_mmg_response'),
|
||||
url_for('notifications.receive_mmg_sms'),
|
||||
url_for('status.show_delivery_status'),
|
||||
url_for('spec.get_spec')
|
||||
]
|
||||
|
||||
if request.path not in no_auth_req:
|
||||
from app.authentication import auth
|
||||
error = auth.requires_auth()
|
||||
if error:
|
||||
return error
|
||||
# @app.before_request
|
||||
# def required_authentication():
|
||||
# no_auth_req = [
|
||||
# url_for('status.show_status'),
|
||||
# url_for('notifications.process_ses_response'),
|
||||
# url_for('notifications.process_firetext_response'),
|
||||
# url_for('notifications.process_mmg_response'),
|
||||
# url_for('notifications.receive_mmg_sms'),
|
||||
# url_for('status.show_delivery_status'),
|
||||
# url_for('spec.get_spec')
|
||||
# ]
|
||||
#
|
||||
# if request.path not in no_auth_req:
|
||||
# from app.authentication import auth
|
||||
# error = auth.requires_auth()
|
||||
# if error:
|
||||
# return error
|
||||
|
||||
@app.before_request
|
||||
def record_user_agent():
|
||||
|
||||
@@ -39,14 +39,13 @@ def get_auth_token(req):
|
||||
return auth_header[7:]
|
||||
|
||||
|
||||
def requires_no_auth():
|
||||
pass
|
||||
|
||||
|
||||
def requires_admin_auth():
|
||||
auth_token = get_auth_token(request)
|
||||
try:
|
||||
client = get_token_issuer(auth_token)
|
||||
except TokenDecodeError as e:
|
||||
raise AuthError(e.message, 403)
|
||||
except TokenIssuerError:
|
||||
raise AuthError("Invalid token: iss not provided", 403)
|
||||
client = __get_token_issuer(auth_token)
|
||||
|
||||
if client == current_app.config.get('ADMIN_CLIENT_USER_NAME'):
|
||||
g.service_id = current_app.config.get('ADMIN_CLIENT_USER_NAME')
|
||||
@@ -57,16 +56,7 @@ def requires_admin_auth():
|
||||
|
||||
def requires_auth():
|
||||
auth_token = get_auth_token(request)
|
||||
try:
|
||||
client = get_token_issuer(auth_token)
|
||||
except TokenDecodeError as e:
|
||||
raise AuthError(e.message, 403)
|
||||
except TokenIssuerError:
|
||||
raise AuthError("Invalid token: iss not provided", 403)
|
||||
|
||||
if client == current_app.config.get('ADMIN_CLIENT_USER_NAME'):
|
||||
g.service_id = current_app.config.get('ADMIN_CLIENT_USER_NAME')
|
||||
return handle_admin_key(auth_token, current_app.config.get('ADMIN_CLIENT_SECRET'))
|
||||
client = __get_token_issuer(auth_token)
|
||||
|
||||
try:
|
||||
service = dao_fetch_service_by_id(client)
|
||||
@@ -98,12 +88,22 @@ def requires_auth():
|
||||
raise AuthError("Invalid token: signature, api token is not valid", 403)
|
||||
|
||||
|
||||
def __get_token_issuer(auth_token):
|
||||
try:
|
||||
client = get_token_issuer(auth_token)
|
||||
except TokenIssuerError:
|
||||
raise AuthError("Invalid token: iss field not provided", 403)
|
||||
except TokenDecodeError as e:
|
||||
raise AuthError("Invalid token: signature, api token is not valid", 403)
|
||||
return client
|
||||
|
||||
|
||||
def handle_admin_key(auth_token, secret):
|
||||
try:
|
||||
get_decode_errors(auth_token, secret)
|
||||
return
|
||||
except TokenDecodeError as e:
|
||||
raise AuthError(e.message, 403)
|
||||
raise AuthError("Invalid token: signature, api token is not valid", 403)
|
||||
|
||||
|
||||
def get_decode_errors(auth_token, unsigned_secret):
|
||||
|
||||
@@ -34,17 +34,17 @@ from app.models import JOB_STATUS_SCHEDULED, JOB_STATUS_PENDING, JOB_STATUS_CANC
|
||||
|
||||
from app.utils import pagination_links
|
||||
|
||||
job = Blueprint('job', __name__, url_prefix='/service/<uuid:service_id>/job')
|
||||
job_blueprint = Blueprint('job', __name__, url_prefix='/service/<uuid:service_id>/job')
|
||||
|
||||
from app.errors import (
|
||||
register_errors,
|
||||
InvalidRequest
|
||||
)
|
||||
|
||||
register_errors(job)
|
||||
register_errors(job_blueprint)
|
||||
|
||||
|
||||
@job.route('/<job_id>', methods=['GET'])
|
||||
@job_blueprint.route('/<job_id>', methods=['GET'])
|
||||
def get_job_by_service_and_job_id(service_id, job_id):
|
||||
job = dao_get_job_by_service_id_and_job_id(service_id, job_id)
|
||||
statistics = dao_get_notification_outcomes_for_job(service_id, job_id)
|
||||
@@ -55,7 +55,7 @@ def get_job_by_service_and_job_id(service_id, job_id):
|
||||
return jsonify(data=data)
|
||||
|
||||
|
||||
@job.route('/<job_id>/cancel', methods=['POST'])
|
||||
@job_blueprint.route('/<job_id>/cancel', methods=['POST'])
|
||||
def cancel_job(service_id, job_id):
|
||||
job = dao_get_future_scheduled_job_by_id_and_service_id(job_id, service_id)
|
||||
job.job_status = JOB_STATUS_CANCELLED
|
||||
@@ -64,7 +64,7 @@ def cancel_job(service_id, job_id):
|
||||
return get_job_by_service_and_job_id(service_id, job_id)
|
||||
|
||||
|
||||
@job.route('/<job_id>/notifications', methods=['GET'])
|
||||
@job_blueprint.route('/<job_id>/notifications', methods=['GET'])
|
||||
def get_all_notifications_for_service_job(service_id, job_id):
|
||||
data = notifications_filter_schema.load(request.args).data
|
||||
page = data['page'] if 'page' in data else 1
|
||||
@@ -100,7 +100,7 @@ def get_all_notifications_for_service_job(service_id, job_id):
|
||||
), 200
|
||||
|
||||
|
||||
@job.route('', methods=['GET'])
|
||||
@job_blueprint.route('', methods=['GET'])
|
||||
def get_jobs_by_service(service_id):
|
||||
if request.args.get('limit_days'):
|
||||
try:
|
||||
@@ -117,7 +117,7 @@ def get_jobs_by_service(service_id):
|
||||
return jsonify(**get_paginated_jobs(service_id, limit_days, statuses, page))
|
||||
|
||||
|
||||
@job.route('', methods=['POST'])
|
||||
@job_blueprint.route('', methods=['POST'])
|
||||
def create_job(service_id):
|
||||
service = dao_fetch_service_by_id(service_id)
|
||||
if not service.active:
|
||||
|
||||
101
app/notifications/notifications_ses_callback.py
Normal file
101
app/notifications/notifications_ses_callback.py
Normal file
@@ -0,0 +1,101 @@
|
||||
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
|
||||
|
||||
ses_callback_blueprint = Blueprint('notifications_ses_callback_', __name__)
|
||||
|
||||
from app.errors import (
|
||||
register_errors,
|
||||
InvalidRequest
|
||||
)
|
||||
register_errors(ses_callback_blueprint)
|
||||
|
||||
|
||||
@ses_callback_blueprint.route('/notifications/email/ses', methods=['POST'])
|
||||
def process_ses_response():
|
||||
client_name = 'SES'
|
||||
try:
|
||||
ses_request = json.loads(request.data)
|
||||
|
||||
if 'Type' in ses_request and ses_request['Type'] == 'SubscriptionConfirmation':
|
||||
current_app.logger.info("SNS subscription confirmation url: {}".format(ses_request['SubscribeURL']))
|
||||
|
||||
errors = validate_callback_data(data=ses_request, fields=['Message'], client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
|
||||
ses_message = json.loads(ses_request['Message'])
|
||||
errors = validate_callback_data(data=ses_message, fields=['notificationType'], client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
|
||||
notification_type = ses_message['notificationType']
|
||||
if notification_type == 'Bounce':
|
||||
if ses_message['bounce']['bounceType'] == 'Permanent':
|
||||
notification_type = ses_message['bounce']['bounceType'] # permanent or not
|
||||
else:
|
||||
notification_type = 'Temporary'
|
||||
try:
|
||||
aws_response_dict = get_aws_responses(notification_type)
|
||||
except KeyError:
|
||||
error = "{} callback failed: status {} not found".format(client_name, notification_type)
|
||||
raise InvalidRequest(error, status_code=400)
|
||||
|
||||
notification_status = aws_response_dict['notification_status']
|
||||
|
||||
try:
|
||||
reference = ses_message['mail']['messageId']
|
||||
notification = notifications_dao.update_notification_status_by_reference(
|
||||
reference,
|
||||
notification_status
|
||||
)
|
||||
if not notification:
|
||||
error = "SES callback failed: notification either not found or already updated " \
|
||||
"from sending. Status {} for notification reference {}".format(notification_status, reference)
|
||||
raise InvalidRequest(error, status_code=404)
|
||||
|
||||
if not aws_response_dict['success']:
|
||||
current_app.logger.info(
|
||||
"SES delivery failed: notification id {} and reference {} has error found. Status {}".format(
|
||||
notification.id,
|
||||
reference,
|
||||
aws_response_dict['message']
|
||||
)
|
||||
)
|
||||
else:
|
||||
current_app.logger.info('{} callback return status of {} for notification: {}'.format(
|
||||
client_name,
|
||||
notification_status,
|
||||
notification.id))
|
||||
statsd_client.incr('callback.ses.{}'.format(notification_status))
|
||||
if notification.sent_at:
|
||||
statsd_client.timing_with_dates(
|
||||
'callback.ses.elapsed-time'.format(client_name.lower()),
|
||||
datetime.utcnow(),
|
||||
notification.sent_at
|
||||
)
|
||||
return jsonify(
|
||||
result="success", message="SES callback succeeded"
|
||||
), 200
|
||||
|
||||
except KeyError:
|
||||
message = "SES callback failed: messageId missing"
|
||||
raise InvalidRequest(message, status_code=400)
|
||||
|
||||
except ValueError as ex:
|
||||
error = "{} callback failed: invalid json".format(client_name)
|
||||
raise InvalidRequest(error, status_code=400)
|
||||
47
app/notifications/notifications_sms_callback.py
Normal file
47
app/notifications/notifications_sms_callback.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from flask import Blueprint
|
||||
from flask import json
|
||||
from flask import request, jsonify
|
||||
|
||||
from app.errors import InvalidRequest, register_errors
|
||||
from app.notifications.process_client_response import validate_callback_data, process_sms_client_response
|
||||
|
||||
sms_callback_blueprint = Blueprint("sms_callback_blueprint", __name__, url_prefix="/notifications/sms")
|
||||
register_errors(sms_callback_blueprint)
|
||||
|
||||
|
||||
@sms_callback_blueprint.route('/mmg', methods=['POST'])
|
||||
def process_mmg_response():
|
||||
client_name = 'MMG'
|
||||
data = json.loads(request.data)
|
||||
errors = validate_callback_data(data=data,
|
||||
fields=['status', 'CID'],
|
||||
client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
|
||||
success, errors = process_sms_client_response(status=str(data.get('status')),
|
||||
reference=data.get('CID'),
|
||||
client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
else:
|
||||
return jsonify(result='success', message=success), 200
|
||||
|
||||
|
||||
@sms_callback_blueprint.route('/firetext', methods=['POST'])
|
||||
def process_firetext_response():
|
||||
client_name = 'Firetext'
|
||||
errors = validate_callback_data(data=request.form,
|
||||
fields=['status', 'reference'],
|
||||
client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
|
||||
status = request.form.get('status')
|
||||
success, errors = process_sms_client_response(status=status,
|
||||
reference=request.form.get('reference'),
|
||||
client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
else:
|
||||
return jsonify(result='success', message=success), 200
|
||||
17
app/notifications/receive_notifications.py
Normal file
17
app/notifications/receive_notifications.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from flask import Blueprint
|
||||
from flask import current_app
|
||||
from flask import request
|
||||
|
||||
from app.errors import register_errors
|
||||
|
||||
receive_notifications_blueprint = Blueprint('receive_notifications', __name__)
|
||||
register_errors(receive_notifications_blueprint)
|
||||
|
||||
|
||||
@receive_notifications_blueprint.route('/notifications/sms/receive/mmg', methods=['POST'])
|
||||
def receive_mmg_sms():
|
||||
post_data = request.get_json()
|
||||
post_data.pop('MSISDN', None)
|
||||
current_app.logger.info("Recieve notification form data: {}".format(post_data))
|
||||
|
||||
return "RECEIVED"
|
||||
@@ -1,5 +1,3 @@
|
||||
from datetime import datetime
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
jsonify,
|
||||
@@ -8,8 +6,7 @@ from flask import (
|
||||
json
|
||||
)
|
||||
|
||||
from app import api_user, statsd_client
|
||||
from app.clients.email.aws_ses import get_aws_responses
|
||||
from app import api_user
|
||||
from app.dao import (
|
||||
templates_dao,
|
||||
services_dao,
|
||||
@@ -49,129 +46,6 @@ from app.errors import (
|
||||
register_errors(notifications)
|
||||
|
||||
|
||||
@notifications.route('/notifications/sms/receive/mmg', methods=['POST'])
|
||||
def receive_mmg_sms():
|
||||
post_data = request.get_json()
|
||||
post_data.pop('MSISDN', None)
|
||||
current_app.logger.info("Recieve notification form data: {}".format(post_data))
|
||||
|
||||
return "RECEIVED"
|
||||
|
||||
|
||||
@notifications.route('/notifications/email/ses', methods=['POST'])
|
||||
def process_ses_response():
|
||||
client_name = 'SES'
|
||||
try:
|
||||
ses_request = json.loads(request.data)
|
||||
|
||||
if 'Type' in ses_request and ses_request['Type'] == 'SubscriptionConfirmation':
|
||||
current_app.logger.info("SNS subscription confirmation url: {}".format(ses_request['SubscribeURL']))
|
||||
|
||||
errors = validate_callback_data(data=ses_request, fields=['Message'], client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
|
||||
ses_message = json.loads(ses_request['Message'])
|
||||
errors = validate_callback_data(data=ses_message, fields=['notificationType'], client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
|
||||
notification_type = ses_message['notificationType']
|
||||
if notification_type == 'Bounce':
|
||||
if ses_message['bounce']['bounceType'] == 'Permanent':
|
||||
notification_type = ses_message['bounce']['bounceType'] # permanent or not
|
||||
else:
|
||||
notification_type = 'Temporary'
|
||||
try:
|
||||
aws_response_dict = get_aws_responses(notification_type)
|
||||
except KeyError:
|
||||
error = "{} callback failed: status {} not found".format(client_name, notification_type)
|
||||
raise InvalidRequest(error, status_code=400)
|
||||
|
||||
notification_status = aws_response_dict['notification_status']
|
||||
|
||||
try:
|
||||
reference = ses_message['mail']['messageId']
|
||||
notification = notifications_dao.update_notification_status_by_reference(
|
||||
reference,
|
||||
notification_status
|
||||
)
|
||||
if not notification:
|
||||
error = "SES callback failed: notification either not found or already updated " \
|
||||
"from sending. Status {} for notification reference {}".format(notification_status, reference)
|
||||
raise InvalidRequest(error, status_code=404)
|
||||
|
||||
if not aws_response_dict['success']:
|
||||
current_app.logger.info(
|
||||
"SES delivery failed: notification id {} and reference {} has error found. Status {}".format(
|
||||
notification.id,
|
||||
reference,
|
||||
aws_response_dict['message']
|
||||
)
|
||||
)
|
||||
else:
|
||||
current_app.logger.info('{} callback return status of {} for notification: {}'.format(
|
||||
client_name,
|
||||
notification_status,
|
||||
notification.id))
|
||||
statsd_client.incr('callback.ses.{}'.format(notification_status))
|
||||
if notification.sent_at:
|
||||
statsd_client.timing_with_dates(
|
||||
'callback.ses.elapsed-time'.format(client_name.lower()),
|
||||
datetime.utcnow(),
|
||||
notification.sent_at
|
||||
)
|
||||
return jsonify(
|
||||
result="success", message="SES callback succeeded"
|
||||
), 200
|
||||
|
||||
except KeyError:
|
||||
message = "SES callback failed: messageId missing"
|
||||
raise InvalidRequest(message, status_code=400)
|
||||
|
||||
except ValueError as ex:
|
||||
error = "{} callback failed: invalid json".format(client_name)
|
||||
raise InvalidRequest(error, status_code=400)
|
||||
|
||||
|
||||
@notifications.route('/notifications/sms/mmg', methods=['POST'])
|
||||
def process_mmg_response():
|
||||
client_name = 'MMG'
|
||||
data = json.loads(request.data)
|
||||
errors = validate_callback_data(data=data,
|
||||
fields=['status', 'CID'],
|
||||
client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
|
||||
success, errors = process_sms_client_response(status=str(data.get('status')),
|
||||
reference=data.get('CID'),
|
||||
client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
else:
|
||||
return jsonify(result='success', message=success), 200
|
||||
|
||||
|
||||
@notifications.route('/notifications/sms/firetext', methods=['POST'])
|
||||
def process_firetext_response():
|
||||
client_name = 'Firetext'
|
||||
errors = validate_callback_data(data=request.form,
|
||||
fields=['status', 'reference'],
|
||||
client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
|
||||
status = request.form.get('status')
|
||||
success, errors = process_sms_client_response(status=status,
|
||||
reference=request.form.get('reference'),
|
||||
client_name=client_name)
|
||||
if errors:
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
else:
|
||||
return jsonify(result='success', message=success), 200
|
||||
|
||||
|
||||
@notifications.route('/notifications/<uuid:notification_id>', methods=['GET'])
|
||||
def get_notification_by_id(notification_id):
|
||||
notification = notifications_dao.get_notification_with_personalisation(str(api_user.service_id),
|
||||
|
||||
@@ -41,8 +41,7 @@ from app.dao import notifications_dao
|
||||
from app.dao.provider_statistics_dao import get_fragment_count
|
||||
from app.dao.users_dao import get_user_by_id
|
||||
from app.errors import (
|
||||
InvalidRequest,
|
||||
register_errors)
|
||||
InvalidRequest, register_errors)
|
||||
from app.service import statistics
|
||||
from app.service.utils import get_whitelist_objects
|
||||
from app.schemas import (
|
||||
@@ -57,11 +56,8 @@ from app.schemas import (
|
||||
from app.utils import pagination_links, get_london_midnight_in_utc
|
||||
from flask import Blueprint
|
||||
|
||||
from app.authentication.auth import requires_admin_auth
|
||||
|
||||
service_blueprint = Blueprint('service', __name__)
|
||||
|
||||
service_blueprint.before_request(requires_admin_auth)
|
||||
register_errors(service_blueprint)
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ from app.dao.services_dao import dao_fetch_service_by_id
|
||||
from app.models import SMS_TYPE
|
||||
from app.schemas import (template_schema, template_history_schema)
|
||||
|
||||
template = Blueprint('template', __name__, url_prefix='/service/<uuid:service_id>/template')
|
||||
template_blueprint = Blueprint('template', __name__, url_prefix='/service/<uuid:service_id>/template')
|
||||
|
||||
from app.errors import (
|
||||
register_errors,
|
||||
@@ -25,7 +25,7 @@ from app.errors import (
|
||||
)
|
||||
from app.utils import get_template_instance
|
||||
|
||||
register_errors(template)
|
||||
register_errors(template_blueprint)
|
||||
|
||||
|
||||
def _content_count_greater_than_limit(content, template_type):
|
||||
@@ -35,7 +35,7 @@ def _content_count_greater_than_limit(content, template_type):
|
||||
return template.content_count > current_app.config.get('SMS_CHAR_COUNT_LIMIT')
|
||||
|
||||
|
||||
@template.route('', methods=['POST'])
|
||||
@template_blueprint.route('', methods=['POST'])
|
||||
def create_template(service_id):
|
||||
fetched_service = dao_fetch_service_by_id(service_id=service_id)
|
||||
new_template = template_schema.load(request.get_json()).data
|
||||
@@ -51,7 +51,7 @@ def create_template(service_id):
|
||||
return jsonify(data=template_schema.dump(new_template).data), 201
|
||||
|
||||
|
||||
@template.route('/<uuid:template_id>', methods=['POST'])
|
||||
@template_blueprint.route('/<uuid:template_id>', methods=['POST'])
|
||||
def update_template(service_id, template_id):
|
||||
fetched_template = dao_get_template_by_id_and_service_id(template_id=template_id, service_id=service_id)
|
||||
|
||||
@@ -73,21 +73,21 @@ def update_template(service_id, template_id):
|
||||
return jsonify(data=template_schema.dump(update_dict).data), 200
|
||||
|
||||
|
||||
@template.route('', methods=['GET'])
|
||||
@template_blueprint.route('', methods=['GET'])
|
||||
def get_all_templates_for_service(service_id):
|
||||
templates = dao_get_all_templates_for_service(service_id=service_id)
|
||||
data = template_schema.dump(templates, many=True).data
|
||||
return jsonify(data=data)
|
||||
|
||||
|
||||
@template.route('/<uuid:template_id>', methods=['GET'])
|
||||
@template_blueprint.route('/<uuid:template_id>', methods=['GET'])
|
||||
def get_template_by_id_and_service_id(service_id, template_id):
|
||||
fetched_template = dao_get_template_by_id_and_service_id(template_id=template_id, service_id=service_id)
|
||||
data = template_schema.dump(fetched_template).data
|
||||
return jsonify(data=data)
|
||||
|
||||
|
||||
@template.route('/<uuid:template_id>/preview', methods=['GET'])
|
||||
@template_blueprint.route('/<uuid:template_id>/preview', methods=['GET'])
|
||||
def preview_template_by_id_and_service_id(service_id, template_id):
|
||||
fetched_template = dao_get_template_by_id_and_service_id(template_id=template_id, service_id=service_id)
|
||||
data = template_schema.dump(fetched_template).data
|
||||
@@ -106,7 +106,7 @@ def preview_template_by_id_and_service_id(service_id, template_id):
|
||||
return jsonify(data)
|
||||
|
||||
|
||||
@template.route('/<uuid:template_id>/version/<int:version>')
|
||||
@template_blueprint.route('/<uuid:template_id>/version/<int:version>')
|
||||
def get_template_version(service_id, template_id, version):
|
||||
data = template_history_schema.dump(
|
||||
dao_get_template_by_id_and_service_id(
|
||||
@@ -118,7 +118,7 @@ def get_template_version(service_id, template_id, version):
|
||||
return jsonify(data=data)
|
||||
|
||||
|
||||
@template.route('/<uuid:template_id>/versions')
|
||||
@template_blueprint.route('/<uuid:template_id>/versions')
|
||||
def get_template_versions(service_id, template_id):
|
||||
data = template_history_schema.dump(
|
||||
dao_get_template_versions(service_id=service_id, template_id=template_id),
|
||||
|
||||
@@ -41,11 +41,11 @@ from app.errors import (
|
||||
)
|
||||
from app.utils import url_with_token
|
||||
|
||||
user = Blueprint('user', __name__)
|
||||
register_errors(user)
|
||||
user_blueprint = Blueprint('user', __name__)
|
||||
register_errors(user_blueprint)
|
||||
|
||||
|
||||
@user.route('', methods=['POST'])
|
||||
@user_blueprint.route('', methods=['POST'])
|
||||
def create_user():
|
||||
user_to_create, errors = user_schema.load(request.get_json())
|
||||
req_json = request.get_json()
|
||||
@@ -56,7 +56,7 @@ def create_user():
|
||||
return jsonify(data=user_schema.dump(user_to_create).data), 201
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>', methods=['PUT'])
|
||||
@user_blueprint.route('/<uuid:user_id>', methods=['PUT'])
|
||||
def update_user(user_id):
|
||||
user_to_update = get_user_by_id(user_id=user_id)
|
||||
req_json = request.get_json()
|
||||
@@ -73,7 +73,7 @@ def update_user(user_id):
|
||||
return jsonify(data=user_schema.dump(user_to_update).data), 200
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>', methods=['POST'])
|
||||
@user_blueprint.route('/<uuid:user_id>', methods=['POST'])
|
||||
def update_user_attribute(user_id):
|
||||
user_to_update = get_user_by_id(user_id=user_id)
|
||||
req_json = request.get_json()
|
||||
@@ -84,14 +84,14 @@ def update_user_attribute(user_id):
|
||||
return jsonify(data=user_schema.dump(user_to_update).data), 200
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>/reset-failed-login-count', methods=['POST'])
|
||||
@user_blueprint.route('/<uuid:user_id>/reset-failed-login-count', methods=['POST'])
|
||||
def user_reset_failed_login_count(user_id):
|
||||
user_to_update = get_user_by_id(user_id=user_id)
|
||||
reset_failed_login_count(user_to_update)
|
||||
return jsonify(data=user_schema.dump(user_to_update).data), 200
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>/verify/password', methods=['POST'])
|
||||
@user_blueprint.route('/<uuid:user_id>/verify/password', methods=['POST'])
|
||||
def verify_user_password(user_id):
|
||||
user_to_verify = get_user_by_id(user_id=user_id)
|
||||
|
||||
@@ -112,7 +112,7 @@ def verify_user_password(user_id):
|
||||
raise InvalidRequest(errors, status_code=400)
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>/verify/code', methods=['POST'])
|
||||
@user_blueprint.route('/<uuid:user_id>/verify/code', methods=['POST'])
|
||||
def verify_user_code(user_id):
|
||||
user_to_verify = get_user_by_id(user_id=user_id)
|
||||
|
||||
@@ -151,7 +151,7 @@ def verify_user_code(user_id):
|
||||
return jsonify({}), 204
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>/sms-code', methods=['POST'])
|
||||
@user_blueprint.route('/<uuid:user_id>/sms-code', methods=['POST'])
|
||||
def send_user_sms_code(user_id):
|
||||
user_to_send_to = get_user_by_id(user_id=user_id)
|
||||
verify_code, errors = request_verify_code_schema.load(request.get_json())
|
||||
@@ -187,7 +187,7 @@ def send_user_sms_code(user_id):
|
||||
return jsonify({}), 204
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>/change-email-verification', methods=['POST'])
|
||||
@user_blueprint.route('/<uuid:user_id>/change-email-verification', methods=['POST'])
|
||||
def send_user_confirm_new_email(user_id):
|
||||
user_to_send_to = get_user_by_id(user_id=user_id)
|
||||
email, errors = email_data_request_schema.load(request.get_json())
|
||||
@@ -216,7 +216,7 @@ def send_user_confirm_new_email(user_id):
|
||||
return jsonify({}), 204
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>/email-verification', methods=['POST'])
|
||||
@user_blueprint.route('/<uuid:user_id>/email-verification', methods=['POST'])
|
||||
def send_user_email_verification(user_id):
|
||||
user_to_send_to = get_user_by_id(user_id=user_id)
|
||||
secret_code = create_secret_code()
|
||||
@@ -244,7 +244,7 @@ def send_user_email_verification(user_id):
|
||||
return jsonify({}), 204
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>/email-already-registered', methods=['POST'])
|
||||
@user_blueprint.route('/<uuid:user_id>/email-already-registered', methods=['POST'])
|
||||
def send_already_registered_email(user_id):
|
||||
to, errors = email_data_request_schema.load(request.get_json())
|
||||
template = dao_get_template_by_id(current_app.config['ALREADY_REGISTERED_EMAIL_TEMPLATE_ID'])
|
||||
@@ -270,15 +270,15 @@ def send_already_registered_email(user_id):
|
||||
return jsonify({}), 204
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>', methods=['GET'])
|
||||
@user.route('', methods=['GET'])
|
||||
@user_blueprint.route('/<uuid:user_id>', methods=['GET'])
|
||||
@user_blueprint.route('', methods=['GET'])
|
||||
def get_user(user_id=None):
|
||||
users = get_user_by_id(user_id=user_id)
|
||||
result = user_schema.dump(users, many=True) if isinstance(users, list) else user_schema.dump(users)
|
||||
return jsonify(data=result.data)
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>/service/<uuid:service_id>/permission', methods=['POST'])
|
||||
@user_blueprint.route('/<uuid:user_id>/service/<uuid:service_id>/permission', methods=['POST'])
|
||||
def set_permissions(user_id, service_id):
|
||||
# TODO fix security hole, how do we verify that the user
|
||||
# who is making this request has permission to make the request.
|
||||
@@ -293,7 +293,7 @@ def set_permissions(user_id, service_id):
|
||||
return jsonify({}), 204
|
||||
|
||||
|
||||
@user.route('/email', methods=['GET'])
|
||||
@user_blueprint.route('/email', methods=['GET'])
|
||||
def get_by_email():
|
||||
email = request.args.get('email')
|
||||
if not email:
|
||||
@@ -305,7 +305,7 @@ def get_by_email():
|
||||
return jsonify(data=result.data)
|
||||
|
||||
|
||||
@user.route('/reset-password', methods=['POST'])
|
||||
@user_blueprint.route('/reset-password', methods=['POST'])
|
||||
def send_user_reset_password():
|
||||
email, errors = email_data_request_schema.load(request.get_json())
|
||||
|
||||
@@ -332,7 +332,7 @@ def send_user_reset_password():
|
||||
return jsonify({}), 204
|
||||
|
||||
|
||||
@user.route('/<uuid:user_id>/update-password', methods=['POST'])
|
||||
@user_blueprint.route('/<uuid:user_id>/update-password', methods=['POST'])
|
||||
def update_password(user_id):
|
||||
user = get_user_by_id(user_id=user_id)
|
||||
req_json = request.get_json()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from flask import Blueprint
|
||||
|
||||
from app.v2.errors import register_errors
|
||||
|
||||
notification_blueprint = Blueprint("v2_notifications", __name__, url_prefix='/v2/notifications')
|
||||
v2_notification_blueprint = Blueprint("v2_notifications", __name__, url_prefix='/v2/notifications')
|
||||
|
||||
register_errors(notification_blueprint)
|
||||
register_errors(v2_notification_blueprint)
|
||||
|
||||
@@ -6,11 +6,11 @@ from werkzeug.exceptions import abort
|
||||
from app import api_user
|
||||
from app.dao import notifications_dao
|
||||
from app.schema_validation import validate
|
||||
from app.v2.notifications import notification_blueprint
|
||||
from app.v2.notifications import v2_notification_blueprint
|
||||
from app.v2.notifications.notification_schemas import get_notifications_request
|
||||
|
||||
|
||||
@notification_blueprint.route("/<id>", methods=['GET'])
|
||||
@v2_notification_blueprint.route("/<id>", methods=['GET'])
|
||||
def get_notification_by_id(id):
|
||||
try:
|
||||
casted_id = uuid.UUID(id)
|
||||
@@ -23,7 +23,7 @@ def get_notification_by_id(id):
|
||||
return jsonify(notification.serialize()), 200
|
||||
|
||||
|
||||
@notification_blueprint.route("", methods=['GET'])
|
||||
@v2_notification_blueprint.route("", methods=['GET'])
|
||||
def get_notifications():
|
||||
_data = request.args.to_dict(flat=False)
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ from app.notifications.validators import (check_service_message_limit,
|
||||
validate_and_format_recipient)
|
||||
from app.schema_validation import validate
|
||||
from app.v2.errors import BadRequestError
|
||||
from app.v2.notifications import notification_blueprint
|
||||
from app.v2.notifications import v2_notification_blueprint
|
||||
from app.v2.notifications.notification_schemas import (post_sms_request,
|
||||
create_post_sms_response_from_notification, post_email_request,
|
||||
create_post_email_response_from_notification)
|
||||
|
||||
|
||||
@notification_blueprint.route('/<notification_type>', methods=['POST'])
|
||||
@v2_notification_blueprint.route('/<notification_type>', methods=['POST'])
|
||||
def post_notification(notification_type):
|
||||
if notification_type == EMAIL_TYPE:
|
||||
form = validate(request.get_json(), post_email_request)
|
||||
|
||||
@@ -35,7 +35,7 @@ def test_should_not_allow_request_with_incorrect_token(client, url):
|
||||
response = client.get(url, headers={'Authorization': 'Bearer 1234'})
|
||||
assert response.status_code == 403
|
||||
data = json.loads(response.get_data())
|
||||
assert data['message'] == {"token": ['Invalid token: signature']}
|
||||
assert data['message'] == {"token": ['Invalid token: signature, api token is not valid']}
|
||||
|
||||
|
||||
@pytest.mark.parametrize('url', ['/service', '/notifications'])
|
||||
@@ -59,8 +59,14 @@ def test_should_not_allow_request_with_no_iss(client, url):
|
||||
assert data['message'] == {"token": ['Invalid token: iss field not provided']}
|
||||
|
||||
|
||||
@pytest.mark.parametrize('url', ['/service', '/notifications'])
|
||||
def test_should_not_allow_request_with_no_iat(client, sample_api_key, url):
|
||||
@pytest.mark.parametrize('url, auth_method',
|
||||
[('/service', 'requires_admin_auth'),
|
||||
('/notifications', 'requires_auth')])
|
||||
def test_should_not_allow_request_with_no_iat(client, sample_api_key, url, auth_method):
|
||||
if auth_method == 'requires_admin_auth':
|
||||
iss = current_app.config['ADMIN_CLIENT_USER_NAME']
|
||||
if auth_method == 'requires_auth':
|
||||
iss = str(sample_api_key.service_id)
|
||||
# code copied from notifications_python_client.authentication.py::create_jwt_token
|
||||
headers = {
|
||||
"typ": 'JWT',
|
||||
@@ -68,7 +74,7 @@ def test_should_not_allow_request_with_no_iat(client, sample_api_key, url):
|
||||
}
|
||||
|
||||
claims = {
|
||||
'iss': str(sample_api_key.service_id)
|
||||
'iss': iss
|
||||
# 'iat': not provided
|
||||
}
|
||||
|
||||
@@ -80,13 +86,12 @@ def test_should_not_allow_request_with_no_iat(client, sample_api_key, url):
|
||||
assert data['message'] == {"token": ['Invalid token: signature, api token is not valid']}
|
||||
|
||||
|
||||
@pytest.mark.parametrize('url', ['/service', '/notifications'])
|
||||
def test_should_not_allow_invalid_secret(client, sample_api_key, url):
|
||||
def test_should_not_allow_invalid_secret(client, sample_api_key):
|
||||
token = create_jwt_token(
|
||||
secret="not-so-secret",
|
||||
client_id=str(sample_api_key.service_id))
|
||||
response = client.get(
|
||||
url,
|
||||
'/notifications',
|
||||
headers={'Authorization': "Bearer {}".format(token)}
|
||||
)
|
||||
assert response.status_code == 403
|
||||
@@ -101,12 +106,11 @@ def test_should_allow_valid_token(client, sample_api_key, scheme):
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.parametrize('url', ['/service', '/notifications'])
|
||||
def test_should_not_allow_service_id_that_is_not_the_wrong_data_type(client, sample_api_key, url):
|
||||
def test_should_not_allow_service_id_that_is_not_the_wrong_data_type(client, sample_api_key):
|
||||
token = create_jwt_token(secret=get_unsigned_secrets(sample_api_key.service_id)[0],
|
||||
client_id=str('not-a-valid-id'))
|
||||
response = client.get(
|
||||
url,
|
||||
'/notifications',
|
||||
headers={'Authorization': "Bearer {}".format(token)}
|
||||
)
|
||||
assert response.status_code == 403
|
||||
@@ -208,7 +212,23 @@ def test_authentication_returns_error_when_admin_client_has_no_secrets(client):
|
||||
headers={'Authorization': 'Bearer {}'.format(token)})
|
||||
assert response.status_code == 403
|
||||
error_message = json.loads(response.get_data())
|
||||
assert error_message['message'] == {"token": ['Invalid token: signature']}
|
||||
assert error_message['message'] == {"token": ["Invalid token: signature, api token is not valid"]}
|
||||
current_app.config['ADMIN_CLIENT_SECRET'] = api_secret
|
||||
|
||||
|
||||
def test_authentication_returns_error_when_admin_client_secret_is_invalid(client):
|
||||
api_secret = current_app.config.get('ADMIN_CLIENT_SECRET')
|
||||
token = create_jwt_token(
|
||||
secret=api_secret,
|
||||
client_id=current_app.config.get('ADMIN_CLIENT_USER_NAME')
|
||||
)
|
||||
current_app.config['ADMIN_CLIENT_SECRET'] = 'something-wrong'
|
||||
response = client.get(
|
||||
'/service',
|
||||
headers={'Authorization': 'Bearer {}'.format(token)})
|
||||
assert response.status_code == 403
|
||||
error_message = json.loads(response.get_data())
|
||||
assert error_message['message'] == {"token": ["Invalid token: signature, api token is not valid"]}
|
||||
current_app.config['ADMIN_CLIENT_SECRET'] = api_secret
|
||||
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ from app.dao.templates_dao import dao_update_template
|
||||
from app.models import NOTIFICATION_STATUS_TYPES, JOB_STATUS_TYPES, JOB_STATUS_PENDING
|
||||
|
||||
|
||||
def test_get_job_with_invalid_service_id_returns404(notify_api, sample_api_key, sample_service):
|
||||
def test_get_job_with_invalid_service_id_returns404(notify_api, sample_service):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
path = '/service/{}/job'.format(sample_service.id)
|
||||
auth_header = create_authorization_header(service_id=sample_service.id)
|
||||
auth_header = create_authorization_header()
|
||||
response = client.get(path, headers=[auth_header])
|
||||
assert response.status_code == 200
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -33,7 +33,7 @@ def test_get_job_with_invalid_job_id_returns404(notify_api, sample_template):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
path = '/service/{}/job/{}'.format(service_id, "bad-id")
|
||||
auth_header = create_authorization_header(service_id=sample_template.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
response = client.get(path, headers=[auth_header])
|
||||
assert response.status_code == 404
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -46,7 +46,7 @@ def test_get_job_with_unknown_id_returns404(notify_api, sample_template, fake_uu
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
path = '/service/{}/job/{}'.format(service_id, fake_uuid)
|
||||
auth_header = create_authorization_header(service_id=sample_template.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
response = client.get(path, headers=[auth_header])
|
||||
assert response.status_code == 404
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -62,7 +62,7 @@ def test_get_job_by_id(notify_api, sample_job):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
path = '/service/{}/job/{}'.format(service_id, job_id)
|
||||
auth_header = create_authorization_header(service_id=sample_job.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
response = client.get(path, headers=[auth_header])
|
||||
assert response.status_code == 200
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -75,7 +75,7 @@ def test_cancel_job(notify_api, sample_scheduled_job):
|
||||
service_id = sample_scheduled_job.service.id
|
||||
with notify_api.test_request_context(), notify_api.test_client() as client:
|
||||
path = '/service/{}/job/{}/cancel'.format(service_id, job_id)
|
||||
auth_header = create_authorization_header(service_id=service_id)
|
||||
auth_header = create_authorization_header()
|
||||
response = client.post(path, headers=[auth_header])
|
||||
assert response.status_code == 200
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -89,7 +89,7 @@ def test_cant_cancel_normal_job(notify_api, sample_job, mocker):
|
||||
with notify_api.test_request_context(), notify_api.test_client() as client:
|
||||
mock_update = mocker.patch('app.dao.jobs_dao.dao_update_job')
|
||||
path = '/service/{}/job/{}/cancel'.format(service_id, job_id)
|
||||
auth_header = create_authorization_header(service_id=service_id)
|
||||
auth_header = create_authorization_header()
|
||||
response = client.post(path, headers=[auth_header])
|
||||
assert response.status_code == 404
|
||||
assert mock_update.call_count == 0
|
||||
@@ -108,7 +108,7 @@ def test_create_unscheduled_job(notify_api, sample_template, mocker, fake_uuid):
|
||||
'created_by': str(sample_template.created_by.id)
|
||||
}
|
||||
path = '/service/{}/job'.format(sample_template.service.id)
|
||||
auth_header = create_authorization_header(service_id=sample_template.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
headers = [('Content-Type', 'application/json'), auth_header]
|
||||
|
||||
response = client.post(
|
||||
@@ -149,7 +149,7 @@ def test_create_scheduled_job(notify_api, sample_template, mocker, fake_uuid):
|
||||
'scheduled_for': scheduled_date
|
||||
}
|
||||
path = '/service/{}/job'.format(sample_template.service.id)
|
||||
auth_header = create_authorization_header(service_id=sample_template.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
headers = [('Content-Type', 'application/json'), auth_header]
|
||||
|
||||
response = client.post(
|
||||
@@ -202,7 +202,7 @@ def test_should_not_create_scheduled_job_more_then_24_hours_hence(notify_api, sa
|
||||
'scheduled_for': scheduled_date
|
||||
}
|
||||
path = '/service/{}/job'.format(sample_template.service.id)
|
||||
auth_header = create_authorization_header(service_id=sample_template.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
headers = [('Content-Type', 'application/json'), auth_header]
|
||||
|
||||
print(json.dumps(data))
|
||||
@@ -237,7 +237,7 @@ def test_should_not_create_scheduled_job_in_the_past(notify_api, sample_template
|
||||
'scheduled_for': scheduled_date
|
||||
}
|
||||
path = '/service/{}/job'.format(sample_template.service.id)
|
||||
auth_header = create_authorization_header(service_id=sample_template.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
headers = [('Content-Type', 'application/json'), auth_header]
|
||||
|
||||
print(json.dumps(data))
|
||||
@@ -263,7 +263,7 @@ def test_create_job_returns_400_if_missing_data(notify_api, sample_template, moc
|
||||
'template': str(sample_template.id)
|
||||
}
|
||||
path = '/service/{}/job'.format(sample_template.service.id)
|
||||
auth_header = create_authorization_header(service_id=sample_template.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
headers = [('Content-Type', 'application/json'), auth_header]
|
||||
response = client.post(
|
||||
path,
|
||||
@@ -288,7 +288,7 @@ def test_create_job_returns_404_if_template_does_not_exist(notify_api, sample_se
|
||||
'template': str(sample_service.id)
|
||||
}
|
||||
path = '/service/{}/job'.format(sample_service.id)
|
||||
auth_header = create_authorization_header(service_id=sample_service.id)
|
||||
auth_header = create_authorization_header()
|
||||
headers = [('Content-Type', 'application/json'), auth_header]
|
||||
response = client.post(
|
||||
path,
|
||||
@@ -310,7 +310,7 @@ def test_create_job_returns_404_if_missing_service(notify_api, sample_template,
|
||||
random_id = str(uuid.uuid4())
|
||||
data = {'template': str(sample_template.id)}
|
||||
path = '/service/{}/job'.format(random_id)
|
||||
auth_header = create_authorization_header(service_id=sample_template.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
headers = [('Content-Type', 'application/json'), auth_header]
|
||||
response = client.post(
|
||||
path,
|
||||
@@ -335,7 +335,7 @@ def test_create_job_returns_400_if_archived_template(notify_api, sample_template
|
||||
'template': str(sample_template.id)
|
||||
}
|
||||
path = '/service/{}/job'.format(sample_template.service.id)
|
||||
auth_header = create_authorization_header(service_id=sample_template.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
headers = [('Content-Type', 'application/json'), auth_header]
|
||||
response = client.post(
|
||||
path,
|
||||
@@ -455,7 +455,7 @@ def test_get_job_by_id(notify_api, sample_job):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
path = '/service/{}/job/{}'.format(service_id, job_id)
|
||||
auth_header = create_authorization_header(service_id=sample_job.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
response = client.get(path, headers=[auth_header])
|
||||
assert response.status_code == 200
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -480,7 +480,7 @@ def test_get_job_by_id_should_return_statistics(notify_db, notify_db_session, no
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
path = '/service/{}/job/{}'.format(service_id, job_id)
|
||||
auth_header = create_authorization_header(service_id=sample_job.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
response = client.get(path, headers=[auth_header])
|
||||
assert response.status_code == 200
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -514,7 +514,7 @@ def test_get_job_by_id_should_return_summed_statistics(notify_db, notify_db_sess
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
path = '/service/{}/job/{}'.format(service_id, job_id)
|
||||
auth_header = create_authorization_header(service_id=sample_job.service.id)
|
||||
auth_header = create_authorization_header()
|
||||
response = client.get(path, headers=[auth_header])
|
||||
assert response.status_code == 200
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -535,7 +535,7 @@ def test_get_jobs(notify_api, notify_db, notify_db_session, sample_template):
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
path = '/service/{}/job'.format(service_id)
|
||||
auth_header = create_authorization_header(service_id=service_id)
|
||||
auth_header = create_authorization_header()
|
||||
response = client.get(path, headers=[auth_header])
|
||||
assert response.status_code == 200
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -561,7 +561,7 @@ def test_get_jobs_with_limit_days(notify_api, notify_db, notify_db_session, samp
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
path = '/service/{}/job'.format(service_id)
|
||||
auth_header = create_authorization_header(service_id=service_id)
|
||||
auth_header = create_authorization_header()
|
||||
response = client.get(path, headers=[auth_header], query_string={'limit_days': 5})
|
||||
assert response.status_code == 200
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -584,7 +584,7 @@ def test_get_jobs_should_return_statistics(notify_db, notify_db_session, notify_
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
path = '/service/{}/job'.format(sample_service.id)
|
||||
auth_header = create_authorization_header(service_id=str(sample_service.id))
|
||||
auth_header = create_authorization_header()
|
||||
response = client.get(path, headers=[auth_header])
|
||||
assert response.status_code == 200
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -609,7 +609,7 @@ def test_get_jobs_should_return_no_stats_if_no_rows_in_notifications(
|
||||
with notify_api.test_request_context():
|
||||
with notify_api.test_client() as client:
|
||||
path = '/service/{}/job'.format(sample_service.id)
|
||||
auth_header = create_authorization_header(service_id=str(sample_service.id))
|
||||
auth_header = create_authorization_header()
|
||||
response = client.get(path, headers=[auth_header])
|
||||
assert response.status_code == 200
|
||||
resp_json = json.loads(response.get_data(as_text=True))
|
||||
@@ -629,7 +629,7 @@ def test_get_jobs_should_paginate(
|
||||
create_10_jobs(notify_db, notify_db_session, sample_template.service, sample_template)
|
||||
|
||||
path = '/service/{}/job'.format(sample_template.service_id)
|
||||
auth_header = create_authorization_header(service_id=str(sample_template.service_id))
|
||||
auth_header = create_authorization_header()
|
||||
|
||||
with set_config(client.application, 'PAGE_SIZE', 2):
|
||||
response = client.get(path, headers=[auth_header])
|
||||
@@ -654,7 +654,7 @@ def test_get_jobs_accepts_page_parameter(
|
||||
create_10_jobs(notify_db, notify_db_session, sample_template.service, sample_template)
|
||||
|
||||
path = '/service/{}/job'.format(sample_template.service_id)
|
||||
auth_header = create_authorization_header(service_id=str(sample_template.service_id))
|
||||
auth_header = create_authorization_header()
|
||||
|
||||
with set_config(client.application, 'PAGE_SIZE', 2):
|
||||
response = client.get(path, headers=[auth_header], query_string={'page': 2})
|
||||
|
||||
@@ -399,230 +399,6 @@ def test_mmg_callback_returns_200_when_notification_id_not_found_or_already_upda
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_ses_callback_should_not_need_auth(client):
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_notification_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_ses_callback_should_fail_if_invalid_json(client):
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data="nonsense",
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 400
|
||||
assert json_resp['result'] == 'error'
|
||||
assert json_resp['message'] == 'SES callback failed: invalid json'
|
||||
|
||||
|
||||
def test_ses_callback_should_fail_if_invalid_notification_type(client):
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_invalid_notification_type_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 400
|
||||
assert json_resp['result'] == 'error'
|
||||
assert json_resp['message'] == 'SES callback failed: status Unknown not found'
|
||||
|
||||
|
||||
def test_ses_callback_should_fail_if_missing_message_id(client):
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_missing_notification_id_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 400
|
||||
assert json_resp['result'] == 'error'
|
||||
assert json_resp['message'] == 'SES callback failed: messageId missing'
|
||||
|
||||
|
||||
def test_ses_callback_should_fail_if_notification_cannot_be_found(notify_db, notify_db_session, client):
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_invalid_notification_id_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 404
|
||||
assert json_resp['result'] == 'error'
|
||||
assert json_resp['message'] == 'SES callback failed: notification either not found or already updated from sending. Status delivered for notification reference missing' # noqa
|
||||
|
||||
|
||||
def test_ses_callback_should_update_notification_status(
|
||||
client,
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
sample_email_template,
|
||||
mocker):
|
||||
with freeze_time('2001-01-01T12:00:00'):
|
||||
mocker.patch('app.statsd_client.incr')
|
||||
mocker.patch('app.statsd_client.timing_with_dates')
|
||||
notification = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref',
|
||||
status='sending',
|
||||
sent_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
assert get_notification_by_id(notification.id).status == 'sending'
|
||||
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_notification_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 200
|
||||
assert json_resp['result'] == 'success'
|
||||
assert json_resp['message'] == 'SES callback succeeded'
|
||||
assert get_notification_by_id(notification.id).status == 'delivered'
|
||||
app.statsd_client.timing_with_dates.assert_any_call(
|
||||
"callback.ses.elapsed-time", datetime.utcnow(), notification.sent_at
|
||||
)
|
||||
app.statsd_client.incr.assert_any_call("callback.ses.delivered")
|
||||
|
||||
|
||||
def test_ses_callback_should_update_multiple_notification_status_sent(
|
||||
client,
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
sample_email_template,
|
||||
mocker):
|
||||
notification1 = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref1',
|
||||
sent_at=datetime.utcnow(),
|
||||
status='sending')
|
||||
|
||||
notification2 = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref2',
|
||||
sent_at=datetime.utcnow(),
|
||||
status='sending')
|
||||
|
||||
notification3 = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref3',
|
||||
sent_at=datetime.utcnow(),
|
||||
status='sending')
|
||||
|
||||
resp1 = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_notification_callback(ref='ref1'),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
resp2 = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_notification_callback(ref='ref2'),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
resp3 = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_notification_callback(ref='ref3'),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
|
||||
assert resp1.status_code == 200
|
||||
assert resp2.status_code == 200
|
||||
assert resp3.status_code == 200
|
||||
|
||||
|
||||
def test_ses_callback_should_set_status_to_temporary_failure(client,
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
sample_email_template):
|
||||
notification = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref',
|
||||
status='sending',
|
||||
sent_at=datetime.utcnow()
|
||||
)
|
||||
assert get_notification_by_id(notification.id).status == 'sending'
|
||||
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_soft_bounce_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 200
|
||||
assert json_resp['result'] == 'success'
|
||||
assert json_resp['message'] == 'SES callback succeeded'
|
||||
assert get_notification_by_id(notification.id).status == 'temporary-failure'
|
||||
|
||||
|
||||
def test_ses_callback_should_not_set_status_once_status_is_delivered(client,
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
sample_email_template):
|
||||
notification = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref',
|
||||
status='delivered',
|
||||
sent_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
assert get_notification_by_id(notification.id).status == 'delivered'
|
||||
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_soft_bounce_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 404
|
||||
assert json_resp['result'] == 'error'
|
||||
assert json_resp['message'] == 'SES callback failed: notification either not found or already updated from sending. Status temporary-failure for notification reference ref' # noqa
|
||||
assert get_notification_by_id(notification.id).status == 'delivered'
|
||||
|
||||
|
||||
def test_ses_callback_should_set_status_to_permanent_failure(client,
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
sample_email_template):
|
||||
notification = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref',
|
||||
status='sending',
|
||||
sent_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
assert get_notification_by_id(notification.id).status == 'sending'
|
||||
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_hard_bounce_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 200
|
||||
assert json_resp['result'] == 'success'
|
||||
assert json_resp['message'] == 'SES callback succeeded'
|
||||
assert get_notification_by_id(notification.id).status == 'permanent-failure'
|
||||
|
||||
|
||||
def test_process_mmg_response_records_statsd(notify_db, notify_db_session, client, mocker):
|
||||
with freeze_time('2001-01-01T12:00:00'):
|
||||
|
||||
@@ -670,31 +446,5 @@ def test_firetext_callback_should_record_statsd(client, notify_db, notify_db_ses
|
||||
app.statsd_client.incr.assert_any_call("callback.firetext.delivered")
|
||||
|
||||
|
||||
def ses_notification_callback(ref='ref'):
|
||||
return str.encode(
|
||||
'{\n "Type" : "Notification",\n "MessageId" : "%(ref)s",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Delivery\\",\\"mail\\":{\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"messageId\\":\\"%(ref)s\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' % {'ref': ref} # noqa
|
||||
)
|
||||
|
||||
|
||||
def ses_invalid_notification_id_callback():
|
||||
return b'{\n "Type" : "Notification",\n "MessageId" : "missing",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Delivery\\",\\"mail\\":{\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"messageId\\":\\"missing\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' # noqa
|
||||
|
||||
|
||||
def ses_missing_notification_id_callback():
|
||||
return b'{\n "Type" : "Notification",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Delivery\\",\\"mail\\":{\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' # noqa
|
||||
|
||||
|
||||
def ses_invalid_notification_type_callback():
|
||||
return b'{\n "Type" : "Notification",\n "MessageId" : "ref",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Unknown\\",\\"mail\\":{\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' # noqa
|
||||
|
||||
|
||||
def ses_hard_bounce_callback():
|
||||
return b'{\n "Type" : "Notification",\n "MessageId" : "ref",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Bounce\\",\\"bounce\\":{\\"bounceType\\":\\"Permanent\\",\\"bounceSubType\\":\\"General\\"}, \\"mail\\":{\\"messageId\\":\\"ref\\",\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' # noqa
|
||||
|
||||
|
||||
def ses_soft_bounce_callback():
|
||||
return b'{\n "Type" : "Notification",\n "MessageId" : "ref",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Bounce\\",\\"bounce\\":{\\"bounceType\\":\\"Undetermined\\",\\"bounceSubType\\":\\"General\\"}, \\"mail\\":{\\"messageId\\":\\"ref\\",\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' # noqa
|
||||
|
||||
|
||||
def get_notification_stats(service_id):
|
||||
return NotificationStatistics.query.filter_by(service_id=service_id).one()
|
||||
|
||||
258
tests/app/notifications/test_notifications_ses_callback.py
Normal file
258
tests/app/notifications/test_notifications_ses_callback.py
Normal file
@@ -0,0 +1,258 @@
|
||||
from datetime import datetime
|
||||
|
||||
from flask import json
|
||||
from freezegun import freeze_time
|
||||
|
||||
from app import statsd_client
|
||||
from app.dao.notifications_dao import get_notification_by_id
|
||||
from tests.app.conftest import sample_notification as create_sample_notification
|
||||
|
||||
|
||||
def test_ses_callback_should_not_need_auth(client):
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_notification_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
assert response.status_code == 404
|
||||
|
||||
|
||||
def test_ses_callback_should_fail_if_invalid_json(client):
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data="nonsense",
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 400
|
||||
assert json_resp['result'] == 'error'
|
||||
assert json_resp['message'] == 'SES callback failed: invalid json'
|
||||
|
||||
|
||||
def test_ses_callback_should_fail_if_invalid_notification_type(client):
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_invalid_notification_type_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 400
|
||||
assert json_resp['result'] == 'error'
|
||||
assert json_resp['message'] == 'SES callback failed: status Unknown not found'
|
||||
|
||||
|
||||
def test_ses_callback_should_fail_if_missing_message_id(client):
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_missing_notification_id_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 400
|
||||
assert json_resp['result'] == 'error'
|
||||
assert json_resp['message'] == 'SES callback failed: messageId missing'
|
||||
|
||||
|
||||
def test_ses_callback_should_fail_if_notification_cannot_be_found(notify_db, notify_db_session, client):
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_invalid_notification_id_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 404
|
||||
assert json_resp['result'] == 'error'
|
||||
assert json_resp['message'] == 'SES callback failed: notification either not found or already updated from sending. Status delivered for notification reference missing' # noqa
|
||||
|
||||
|
||||
def test_ses_callback_should_update_notification_status(
|
||||
client,
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
sample_email_template,
|
||||
mocker):
|
||||
with freeze_time('2001-01-01T12:00:00'):
|
||||
mocker.patch('app.statsd_client.incr')
|
||||
mocker.patch('app.statsd_client.timing_with_dates')
|
||||
notification = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref',
|
||||
status='sending',
|
||||
sent_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
assert get_notification_by_id(notification.id).status == 'sending'
|
||||
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_notification_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 200
|
||||
assert json_resp['result'] == 'success'
|
||||
assert json_resp['message'] == 'SES callback succeeded'
|
||||
assert get_notification_by_id(notification.id).status == 'delivered'
|
||||
statsd_client.timing_with_dates.assert_any_call(
|
||||
"callback.ses.elapsed-time", datetime.utcnow(), notification.sent_at
|
||||
)
|
||||
statsd_client.incr.assert_any_call("callback.ses.delivered")
|
||||
|
||||
|
||||
def test_ses_callback_should_update_multiple_notification_status_sent(
|
||||
client,
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
sample_email_template,
|
||||
mocker):
|
||||
notification1 = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref1',
|
||||
sent_at=datetime.utcnow(),
|
||||
status='sending')
|
||||
|
||||
notification2 = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref2',
|
||||
sent_at=datetime.utcnow(),
|
||||
status='sending')
|
||||
|
||||
notification3 = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref3',
|
||||
sent_at=datetime.utcnow(),
|
||||
status='sending')
|
||||
|
||||
resp1 = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_notification_callback(ref='ref1'),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
resp2 = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_notification_callback(ref='ref2'),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
resp3 = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_notification_callback(ref='ref3'),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
|
||||
assert resp1.status_code == 200
|
||||
assert resp2.status_code == 200
|
||||
assert resp3.status_code == 200
|
||||
|
||||
|
||||
def test_ses_callback_should_set_status_to_temporary_failure(client,
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
sample_email_template):
|
||||
notification = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref',
|
||||
status='sending',
|
||||
sent_at=datetime.utcnow()
|
||||
)
|
||||
assert get_notification_by_id(notification.id).status == 'sending'
|
||||
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_soft_bounce_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 200
|
||||
assert json_resp['result'] == 'success'
|
||||
assert json_resp['message'] == 'SES callback succeeded'
|
||||
assert get_notification_by_id(notification.id).status == 'temporary-failure'
|
||||
|
||||
|
||||
def test_ses_callback_should_not_set_status_once_status_is_delivered(client,
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
sample_email_template):
|
||||
notification = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref',
|
||||
status='delivered',
|
||||
sent_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
assert get_notification_by_id(notification.id).status == 'delivered'
|
||||
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_soft_bounce_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 404
|
||||
assert json_resp['result'] == 'error'
|
||||
assert json_resp['message'] == 'SES callback failed: notification either not found or already updated from sending. Status temporary-failure for notification reference ref' # noqa
|
||||
assert get_notification_by_id(notification.id).status == 'delivered'
|
||||
|
||||
|
||||
def test_ses_callback_should_set_status_to_permanent_failure(client,
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
sample_email_template):
|
||||
notification = create_sample_notification(
|
||||
notify_db,
|
||||
notify_db_session,
|
||||
template=sample_email_template,
|
||||
reference='ref',
|
||||
status='sending',
|
||||
sent_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
assert get_notification_by_id(notification.id).status == 'sending'
|
||||
|
||||
response = client.post(
|
||||
path='/notifications/email/ses',
|
||||
data=ses_hard_bounce_callback(),
|
||||
headers=[('Content-Type', 'text/plain; charset=UTF-8')]
|
||||
)
|
||||
json_resp = json.loads(response.get_data(as_text=True))
|
||||
assert response.status_code == 200
|
||||
assert json_resp['result'] == 'success'
|
||||
assert json_resp['message'] == 'SES callback succeeded'
|
||||
assert get_notification_by_id(notification.id).status == 'permanent-failure'
|
||||
|
||||
|
||||
def ses_notification_callback(ref='ref'):
|
||||
return str.encode(
|
||||
'{\n "Type" : "Notification",\n "MessageId" : "%(ref)s",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Delivery\\",\\"mail\\":{\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"messageId\\":\\"%(ref)s\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' % {'ref': ref} # noqa
|
||||
)
|
||||
|
||||
|
||||
def ses_invalid_notification_id_callback():
|
||||
return b'{\n "Type" : "Notification",\n "MessageId" : "missing",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Delivery\\",\\"mail\\":{\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"messageId\\":\\"missing\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' # noqa
|
||||
|
||||
|
||||
def ses_missing_notification_id_callback():
|
||||
return b'{\n "Type" : "Notification",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Delivery\\",\\"mail\\":{\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' # noqa
|
||||
|
||||
|
||||
def ses_invalid_notification_type_callback():
|
||||
return b'{\n "Type" : "Notification",\n "MessageId" : "ref",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Unknown\\",\\"mail\\":{\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' # noqa
|
||||
|
||||
|
||||
def ses_hard_bounce_callback():
|
||||
return b'{\n "Type" : "Notification",\n "MessageId" : "ref",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Bounce\\",\\"bounce\\":{\\"bounceType\\":\\"Permanent\\",\\"bounceSubType\\":\\"General\\"}, \\"mail\\":{\\"messageId\\":\\"ref\\",\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' # noqa
|
||||
|
||||
|
||||
def ses_soft_bounce_callback():
|
||||
return b'{\n "Type" : "Notification",\n "MessageId" : "ref",\n "TopicArn" : "arn:aws:sns:eu-west-1:123456789012:testing",\n "Message" : "{\\"notificationType\\":\\"Bounce\\",\\"bounce\\":{\\"bounceType\\":\\"Undetermined\\",\\"bounceSubType\\":\\"General\\"}, \\"mail\\":{\\"messageId\\":\\"ref\\",\\"timestamp\\":\\"2016-03-14T12:35:25.909Z\\",\\"source\\":\\"test@test-domain.com\\",\\"sourceArn\\":\\"arn:aws:ses:eu-west-1:123456789012:identity/testing-notify\\",\\"sendingAccountId\\":\\"123456789012\\",\\"destination\\":[\\"testing@digital.cabinet-office.gov.uk\\"]},\\"delivery\\":{\\"timestamp\\":\\"2016-03-14T12:35:26.567Z\\",\\"processingTimeMillis\\":658,\\"recipients\\":[\\"testing@digital.cabinet-office.gov.uk\\"],\\"smtpResponse\\":\\"250 2.0.0 OK 1457958926 uo5si26480932wjc.221 - gsmtp\\",\\"reportingMTA\\":\\"a6-238.smtp-out.eu-west-1.amazonses.com\\"}}",\n "Timestamp" : "2016-03-14T12:35:26.665Z",\n "SignatureVersion" : "1",\n "Signature" : "X8d7eTAOZ6wlnrdVVPYanrAlsX0SMPfOzhoTEBnQqYkrNWTqQY91C0f3bxtPdUhUtOowyPAOkTQ4KnZuzphfhVb2p1MyVYMxNKcBFB05/qaCX99+92fjw4x9LeUOwyGwMv5F0Vkfi5qZCcEw69uVrhYLVSTFTrzi/yCtru+yFULMQ6UhbY09GwiP6hjxZMVr8aROQy5lLHglqQzOuSZ4KeD85JjifHdKzlx8jjQ+uj+FLzHXPMAPmPU1JK9kpoHZ1oPshAFgPDpphJe+HwcJ8ezmk+3AEUr3wWli3xF+49y8Z2anASSVp6YI2YP95UT8Rlh3qT3T+V9V8rbSVislxA==",\n "SigningCertURL" : "https://sns.eu-west-1.amazonaws.com/SimpleNotificationService-bb750dd426d95ee9390147a5624348ee.pem",\n "UnsubscribeURL" : "https://sns.eu-west-1.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:eu-west-1:302763885840:preview-emails:d6aad3ef-83d6-4cf3-a470-54e2e75916da"\n}' # noqa
|
||||
@@ -1,6 +1,5 @@
|
||||
import uuid
|
||||
|
||||
from app.models import NotificationStatistics
|
||||
from app.notifications.process_client_response import (
|
||||
validate_callback_data,
|
||||
process_sms_client_response
|
||||
|
||||
@@ -13,31 +13,41 @@ from tests import create_authorization_header
|
||||
from tests.app.conftest import sample_notification as create_sample_notification
|
||||
|
||||
|
||||
def test_get_sms_notification_by_id(client, sample_notification):
|
||||
auth_header = create_authorization_header(service_id=sample_notification.service_id)
|
||||
@pytest.mark.parametrize('type', ('email', 'sms'))
|
||||
def test_get_notification_by_id(client, sample_notification, sample_email_notification, type):
|
||||
if type == 'email':
|
||||
notification_to_get = sample_email_notification
|
||||
if type == 'sms':
|
||||
notification_to_get = sample_notification
|
||||
|
||||
auth_header = create_authorization_header(service_id=notification_to_get.service_id)
|
||||
response = client.get(
|
||||
'/notifications/{}'.format(sample_notification.id),
|
||||
'/notifications/{}'.format(notification_to_get.id),
|
||||
headers=[auth_header])
|
||||
|
||||
assert response.status_code == 200
|
||||
notification = json.loads(response.get_data(as_text=True))['data']['notification']
|
||||
assert notification['status'] == 'created'
|
||||
assert notification['template'] == {
|
||||
'id': str(sample_notification.template.id),
|
||||
'name': sample_notification.template.name,
|
||||
'template_type': sample_notification.template.template_type,
|
||||
'id': str(notification_to_get.template.id),
|
||||
'name': notification_to_get.template.name,
|
||||
'template_type': notification_to_get.template.template_type,
|
||||
'version': 1
|
||||
}
|
||||
assert notification['to'] == '+447700900855'
|
||||
assert notification['service'] == str(sample_notification.service_id)
|
||||
assert notification['body'] == "This is a template:\nwith a newline"
|
||||
assert not notification.get('subject')
|
||||
assert notification['to'] == notification_to_get.to
|
||||
assert notification['service'] == str(notification_to_get.service_id)
|
||||
assert notification['body'] == notification_to_get.template.content
|
||||
assert notification.get('subject', None) == notification_to_get.subject
|
||||
|
||||
|
||||
@pytest.mark.parametrize("id", ["1234-badly-formatted-id-7890", "0"])
|
||||
def test_get_sms_notification_by_invalid_id(client, sample_notification, id):
|
||||
auth_header = create_authorization_header(service_id=sample_notification.service_id)
|
||||
@pytest.mark.parametrize('type', ('email', 'sms'))
|
||||
def test_get_notification_by_invalid_id(client, sample_notification, sample_email_notification, id, type):
|
||||
if type == 'email':
|
||||
notification_to_get = sample_email_notification
|
||||
if type == 'sms':
|
||||
notification_to_get = sample_notification
|
||||
auth_header = create_authorization_header(service_id=notification_to_get.service_id)
|
||||
|
||||
response = client.get(
|
||||
'/notifications/{}'.format(id),
|
||||
@@ -46,37 +56,6 @@ def test_get_sms_notification_by_invalid_id(client, sample_notification, id):
|
||||
assert response.status_code == 405
|
||||
|
||||
|
||||
def test_get_email_notification_by_id(client, notify_db, notify_db_session, sample_email_template):
|
||||
|
||||
email_notification = create_sample_notification(notify_db,
|
||||
notify_db_session,
|
||||
to_field="sample_email@example.com",
|
||||
service=sample_email_template.service,
|
||||
template=sample_email_template,
|
||||
status='sending')
|
||||
|
||||
auth_header = create_authorization_header(service_id=email_notification.service_id)
|
||||
|
||||
response = client.get(
|
||||
'/notifications/{}'.format(email_notification.id),
|
||||
headers=[auth_header])
|
||||
|
||||
notification = json.loads(response.get_data(as_text=True))['data']['notification']
|
||||
|
||||
assert notification['status'] == 'sending'
|
||||
assert notification['template'] == {
|
||||
'id': str(email_notification.template.id),
|
||||
'name': email_notification.template.name,
|
||||
'template_type': email_notification.template.template_type,
|
||||
'version': 1
|
||||
}
|
||||
assert notification['to'] == 'sample_email@example.com'
|
||||
assert notification['service'] == str(email_notification.service_id)
|
||||
assert response.status_code == 200
|
||||
assert notification['body'] == sample_email_template.content
|
||||
assert notification['subject'] == sample_email_template.subject
|
||||
|
||||
|
||||
def test_get_notifications_empty_result(client, sample_api_key):
|
||||
auth_header = create_authorization_header(service_id=sample_api_key.service_id)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user