mirror of
https://github.com/GSA/notifications-api.git
synced 2026-02-01 15:46:07 -05:00
Add process_mmg_responses
Refactor process_firetext_responses Removed the abstract ClientResponses for firetext and mmg. There is a map for each response to handle the status codes sent by each client. Since MMG has about 20 different status code, none of which seem to be a pending state (unlike firetext that has 3 status one for pending - network delay). For MMG status codes, look for 00 as successful, everything else is assumed to be a failure.
This commit is contained in:
@@ -80,7 +80,8 @@ def init_app(app):
|
||||
no_auth_req = [
|
||||
url_for('status.show_status'),
|
||||
url_for('notifications.process_ses_response'),
|
||||
url_for('notifications.process_firetext_response')
|
||||
url_for('notifications.process_firetext_response'),
|
||||
url_for('notifications.process_mmg_response')
|
||||
]
|
||||
if request.path not in no_auth_req:
|
||||
from app.authentication import auth
|
||||
|
||||
@@ -313,9 +313,6 @@ def send_email(service_id, notification_id, subject, from_address, encrypted_not
|
||||
@notify_celery.task(name='send-sms-code')
|
||||
def send_sms_code(encrypted_verification):
|
||||
verification_message = encryption.decrypt(encrypted_verification)
|
||||
# send_sms_via_firetext(validate_and_format_phone_number(verification_message['to']),
|
||||
# verification_message['secret_code'],
|
||||
# 'send-sms-code')
|
||||
try:
|
||||
mmg_client.send_sms(validate_and_format_phone_number(verification_message['to']),
|
||||
verification_message['secret_code'],
|
||||
|
||||
@@ -6,34 +6,34 @@ from app.clients.sms import (
|
||||
)
|
||||
from flask import current_app
|
||||
from requests import request, RequestException, HTTPError
|
||||
from app.clients import ClientResponse, STATISTICS_DELIVERED, STATISTICS_FAILURE
|
||||
from app.clients import STATISTICS_DELIVERED, STATISTICS_FAILURE
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
firetext_responses = {
|
||||
'0': {
|
||||
"message": 'Delivered',
|
||||
"notification_statistics_status": STATISTICS_DELIVERED,
|
||||
"success": True,
|
||||
"notification_status": 'delivered'
|
||||
},
|
||||
'1': {
|
||||
"message": 'Declined',
|
||||
"success": False,
|
||||
"notification_statistics_status": STATISTICS_FAILURE,
|
||||
"notification_status": 'failed'
|
||||
},
|
||||
'2': {
|
||||
"message": 'Undelivered (Pending with Network)',
|
||||
"success": False,
|
||||
"notification_statistics_status": None,
|
||||
"notification_status": 'sent'
|
||||
}
|
||||
}
|
||||
|
||||
class FiretextResponses(ClientResponse):
|
||||
def __init__(self):
|
||||
ClientResponse.__init__(self)
|
||||
self.__response_model__ = {
|
||||
'0': {
|
||||
"message": 'Delivered',
|
||||
"notification_statistics_status": STATISTICS_DELIVERED,
|
||||
"success": True,
|
||||
"notification_status": 'delivered'
|
||||
},
|
||||
'1': {
|
||||
"message": 'Declined',
|
||||
"success": False,
|
||||
"notification_statistics_status": STATISTICS_FAILURE,
|
||||
"notification_status": 'failed'
|
||||
},
|
||||
'2': {
|
||||
"message": 'Undelivered (Pending with Network)',
|
||||
"success": False,
|
||||
"notification_statistics_status": None,
|
||||
"notification_status": 'sent'
|
||||
}
|
||||
}
|
||||
|
||||
def get_firetext_responses(status):
|
||||
return firetext_responses[status]
|
||||
|
||||
|
||||
class FiretextClientException(SmsClientException):
|
||||
|
||||
@@ -1,33 +1,27 @@
|
||||
from flask import current_app
|
||||
from monotonic import monotonic
|
||||
from requests import (request, RequestException, HTTPError)
|
||||
from app.clients import (ClientResponse, STATISTICS_DELIVERED, STATISTICS_FAILURE)
|
||||
from app.clients import (STATISTICS_DELIVERED, STATISTICS_FAILURE)
|
||||
from app.clients.sms import (SmsClient, SmsClientException)
|
||||
|
||||
mmg_response_map = {
|
||||
'00': {
|
||||
"message": 'Delivered',
|
||||
"notification_statistics_status": STATISTICS_DELIVERED,
|
||||
"success": True,
|
||||
"notification_status": 'delivered'
|
||||
},
|
||||
'default': {
|
||||
"message": 'Declined',
|
||||
"success": False,
|
||||
"notification_statistics_status": STATISTICS_FAILURE,
|
||||
"notification_status": 'failed'
|
||||
}
|
||||
}
|
||||
|
||||
class FiretextResponses(ClientResponse):
|
||||
def __init__(self):
|
||||
ClientResponse.__init__(self)
|
||||
self.__response_model__ = {
|
||||
'0': {
|
||||
"message": 'Delivered',
|
||||
"notification_statistics_status": STATISTICS_DELIVERED,
|
||||
"success": True,
|
||||
"notification_status": 'delivered'
|
||||
},
|
||||
'1': {
|
||||
"message": 'Declined',
|
||||
"success": False,
|
||||
"notification_statistics_status": STATISTICS_FAILURE,
|
||||
"notification_status": 'failed'
|
||||
},
|
||||
'2': {
|
||||
"message": 'Undelivered (Pending with Network)',
|
||||
"success": False,
|
||||
"notification_statistics_status": None,
|
||||
"notification_status": 'sent'
|
||||
}
|
||||
}
|
||||
|
||||
def get_mmg_responses(status):
|
||||
return mmg_response_map.get(status, mmg_response_map.get('default'))
|
||||
|
||||
|
||||
class MMGClientException(SmsClientException):
|
||||
|
||||
71
app/notifications/process_client_response.py
Normal file
71
app/notifications/process_client_response.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import uuid
|
||||
from flask import current_app
|
||||
from app.dao import notifications_dao
|
||||
from app.clients.sms.firetext import get_firetext_responses
|
||||
from app.clients.sms.mmg import get_mmg_responses
|
||||
|
||||
sms_response_mapper = {'MMG': get_mmg_responses,
|
||||
'Firetext': get_firetext_responses
|
||||
}
|
||||
|
||||
|
||||
def validate_callback_data(data, fields, client_name):
|
||||
errors = []
|
||||
for f in fields:
|
||||
if len(data.get(f, '')) <= 0:
|
||||
error = "{} callback failed: {} missing".format(client_name, f)
|
||||
errors.append(error)
|
||||
return errors if len(errors) > 0 else None
|
||||
|
||||
|
||||
def process_sms_client_response(status, reference, client_name):
|
||||
success = None
|
||||
errors = None
|
||||
# validate reference
|
||||
if reference == 'send-sms-code':
|
||||
success = "{} callback succeeded: send-sms-code".format(client_name)
|
||||
return success, errors
|
||||
|
||||
try:
|
||||
uuid.UUID(reference, version=4)
|
||||
except ValueError:
|
||||
message = "{} callback with invalid reference {}".format(client_name, reference)
|
||||
return success, message
|
||||
|
||||
try:
|
||||
response_parser = sms_response_mapper[client_name]
|
||||
except KeyError:
|
||||
return success, 'unknown sms client: {}'.format(client_name)
|
||||
|
||||
# validate status
|
||||
try:
|
||||
response_dict = response_parser(status)
|
||||
current_app.logger.info('{} callback return status of {} for reference: {}'.format(client_name,
|
||||
status, reference))
|
||||
except KeyError:
|
||||
msg = "{} callback failed: status {} not found.".format(client_name, status)
|
||||
return success, msg
|
||||
|
||||
notification_status = response_dict['notification_status']
|
||||
notification_statistics_status = response_dict['notification_statistics_status']
|
||||
notification_status_message = response_dict['message']
|
||||
notification_success = response_dict['success']
|
||||
|
||||
# record stats
|
||||
update_success = notifications_dao.update_notification_status_by_id(reference,
|
||||
notification_status,
|
||||
notification_statistics_status)
|
||||
if update_success == 0:
|
||||
status_error = "{} callback failed: notification {} not found. Status {}".format(client_name,
|
||||
reference,
|
||||
notification_status_message)
|
||||
return success, status_error
|
||||
|
||||
if not notification_success:
|
||||
current_app.logger.info(
|
||||
"{} delivery failed: notification {} has error found. Status {}".format(client_name,
|
||||
reference,
|
||||
notification_status_message))
|
||||
|
||||
success = "{} callback succeeded. reference {} updated".format(client_name, reference)
|
||||
return success, errors
|
||||
@@ -1,5 +1,4 @@
|
||||
from datetime import datetime
|
||||
import uuid
|
||||
|
||||
from flask import (
|
||||
Blueprint,
|
||||
@@ -11,7 +10,6 @@ from flask import (
|
||||
)
|
||||
|
||||
from utils.template import Template
|
||||
from app.clients.sms.firetext import FiretextResponses
|
||||
from app.clients.email.aws_ses import AwsSesResponses
|
||||
from app import api_user, encryption, create_uuid, DATETIME_FORMAT, DATE_FORMAT
|
||||
from app.authentication.auth import require_admin
|
||||
@@ -20,7 +18,10 @@ from app.dao import (
|
||||
services_dao,
|
||||
notifications_dao
|
||||
)
|
||||
|
||||
from app.notifications.process_client_response import (
|
||||
validate_callback_data,
|
||||
process_sms_client_response
|
||||
)
|
||||
from app.schemas import (
|
||||
email_notification_schema,
|
||||
sms_template_notification_schema,
|
||||
@@ -35,9 +36,7 @@ notifications = Blueprint('notifications', __name__)
|
||||
from app.errors import register_errors
|
||||
|
||||
register_errors(notifications)
|
||||
|
||||
aws_response = AwsSesResponses()
|
||||
firetext_response = FiretextResponses()
|
||||
|
||||
|
||||
@notifications.route('/notifications/email/ses', methods=['POST'])
|
||||
@@ -145,90 +144,43 @@ def is_not_a_notification(source):
|
||||
|
||||
@notifications.route('/notifications/sms/mmg', methods=['POST'])
|
||||
def process_mmg_response():
|
||||
current_app.logger.info('MMG client callback form{}'.format(request.form))
|
||||
status, error1 = _get_from_response(form=request.form, field='status', client_name='MMG')
|
||||
reference, error2 = _get_from_response(form=request.form, field='reference', client_name='MMG')
|
||||
errors = [error1, error2]
|
||||
errors.remove(None)
|
||||
if len(errors) > 0:
|
||||
client_name = 'MMG'
|
||||
data = json.loads(request.data)
|
||||
validation_errors = validate_callback_data(data=data,
|
||||
fields=['status', 'CID'],
|
||||
client_name=client_name)
|
||||
if validation_errors:
|
||||
[current_app.logger.info(e) for e in validation_errors]
|
||||
return jsonify(result='error', message=validation_errors), 400
|
||||
|
||||
success, errors = process_sms_client_response(status=data.get('status'),
|
||||
reference=data.get('CID'),
|
||||
client_name='MMG')
|
||||
if errors:
|
||||
[current_app.logger.info(e) for e in errors]
|
||||
return jsonify(result='error', message=errors), 400
|
||||
if reference == 'send-sms-code':
|
||||
return jsonify(result="success", message="MMG callback succeeded: send-sms-code"), 200
|
||||
|
||||
|
||||
def _get_from_response(form, field, client_name):
|
||||
error = None
|
||||
form_field = None
|
||||
if len(form.get(field, '')) <= 0:
|
||||
current_app.logger.info(
|
||||
"{} callback failed: {} missing".format(client_name, field)
|
||||
)
|
||||
error = "{} callback failed: {} missing".format(client_name, field)
|
||||
else:
|
||||
form_field = form[field]
|
||||
return form_field, error
|
||||
return jsonify(result='success', message=success), 200
|
||||
|
||||
|
||||
@notifications.route('/notifications/sms/firetext', methods=['POST'])
|
||||
def process_firetext_response():
|
||||
status, error1 = _get_from_response(form=request.form, field='status', client_name='Firetext')
|
||||
reference, error2 = _get_from_response(form=request.form, field='reference', client_name='Firetext')
|
||||
errors = [error1, error2]
|
||||
errors = errors.remove(None)
|
||||
client_name = 'Firetext'
|
||||
validation_errors = validate_callback_data(data=request.form,
|
||||
fields=['status', 'reference'],
|
||||
client_name=client_name)
|
||||
if validation_errors:
|
||||
current_app.logger.info(validation_errors)
|
||||
return jsonify(result='error', message=validation_errors), 400
|
||||
|
||||
if len(errors) > 0:
|
||||
success, errors = process_sms_client_response(status=request.form.get('status'),
|
||||
reference=request.form.get('reference'),
|
||||
client_name=client_name)
|
||||
if errors:
|
||||
[current_app.logger.info(e) for e in errors]
|
||||
return jsonify(result='error', message=errors), 400
|
||||
|
||||
if reference == 'send-sms-code':
|
||||
return jsonify(result="success", message="Firetext callback succeeded: send-sms-code"), 200
|
||||
|
||||
try:
|
||||
uuid.UUID(reference, version=4)
|
||||
except ValueError:
|
||||
current_app.logger.info(
|
||||
"Firetext callback with invalid reference {}".format(reference)
|
||||
)
|
||||
return jsonify(
|
||||
result="error", message="Firetext callback with invalid reference {}".format(reference)
|
||||
), 400
|
||||
|
||||
try:
|
||||
firetext_response.response_code_to_object(status)
|
||||
except KeyError:
|
||||
current_app.logger.info(
|
||||
"Firetext callback failed: status {} not found.".format(status)
|
||||
)
|
||||
return jsonify(result="error", message="Firetext callback failed: status {} not found.".format(status)), 400
|
||||
|
||||
notification_status = firetext_response.response_code_to_notification_status(status)
|
||||
notification_statistics_status = firetext_response.response_code_to_notification_statistics_status(status)
|
||||
|
||||
if notifications_dao.update_notification_status_by_id(
|
||||
reference,
|
||||
notification_status,
|
||||
notification_statistics_status
|
||||
) == 0:
|
||||
current_app.logger.info(
|
||||
"Firetext callback failed: notification {} not found. Status {}".format(reference, status)
|
||||
)
|
||||
return jsonify(
|
||||
result="error",
|
||||
message="Firetext callback failed: notification {} not found. Status {}".format(
|
||||
reference,
|
||||
firetext_response.response_code_to_message(status)
|
||||
)
|
||||
), 404
|
||||
|
||||
if not firetext_response.response_code_to_notification_success(status):
|
||||
current_app.logger.info(
|
||||
"Firetext delivery failed: notification {} has error found. Status {}".format(
|
||||
reference,
|
||||
FiretextResponses().response_code_to_message(status)
|
||||
)
|
||||
)
|
||||
return jsonify(
|
||||
result="success", message="Firetext callback succeeded. reference {} updated".format(reference)
|
||||
), 200
|
||||
else:
|
||||
return jsonify(result='success', message=success), 200
|
||||
|
||||
|
||||
@notifications.route('/notifications/<uuid:notification_id>', methods=['GET'])
|
||||
|
||||
Reference in New Issue
Block a user