mirror of
https://github.com/GSA/notifications-api.git
synced 2026-08-15 07:18:09 -04:00
New task to send the inbound sms to the service.
If the service has not set the url then nothing happens. If the request to the service url returns with 500 or greater the task is retries. The task is created when the SMS provider post the inbound SMS.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
from datetime import (datetime)
|
||||
from collections import namedtuple
|
||||
|
||||
@@ -6,6 +7,8 @@ from notifications_utils.recipients import (
|
||||
RecipientCSV
|
||||
)
|
||||
from notifications_utils.template import SMSMessageTemplate, WithSubjectTemplate, LetterDVLATemplate
|
||||
from requests import HTTPError
|
||||
from requests import request
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from app import (
|
||||
create_uuid,
|
||||
@@ -17,6 +20,7 @@ from app import (
|
||||
from app.aws import s3
|
||||
from app.celery import provider_tasks
|
||||
from app.config import QueueNames
|
||||
from app.dao.inbound_sms_dao import dao_get_inbound_sms_by_id
|
||||
from app.dao.jobs_dao import (
|
||||
dao_update_job,
|
||||
dao_get_job_by_id,
|
||||
@@ -25,6 +29,7 @@ from app.dao.jobs_dao import (
|
||||
dao_update_job_status)
|
||||
from app.dao.notifications_dao import get_notification_by_id, dao_update_notifications_sent_to_dvla
|
||||
from app.dao.provider_details_dao import get_current_provider
|
||||
from app.dao.service_inbound_api_dao import get_service_inbound_api_for_service
|
||||
from app.dao.services_dao import dao_fetch_service_by_id, fetch_todays_total_message_count
|
||||
from app.dao.templates_dao import dao_get_template_by_id
|
||||
from app.models import (
|
||||
@@ -371,3 +376,43 @@ def process_updates_from_file(response_file):
|
||||
NotificationUpdate = namedtuple('NotificationUpdate', ['reference', 'status', 'page_count', 'cost_threshold'])
|
||||
notification_updates = [NotificationUpdate(*line.split('|')) for line in response_file.splitlines()]
|
||||
return notification_updates
|
||||
|
||||
|
||||
@notify_celery.task(bind=True, name="send-inbound-sms", max_retries=5, default_retry_delay=300)
|
||||
@statsd(namespace="tasks")
|
||||
def send_inbound_sms_to_service(self, inbound_sms_id, service_id):
|
||||
inbound_api = get_service_inbound_api_for_service(service_id=service_id)
|
||||
if not inbound_api:
|
||||
# No API data has been set for this service
|
||||
return
|
||||
|
||||
inbound_sms = dao_get_inbound_sms_by_id(service_id=service_id,
|
||||
inbound_id=inbound_sms_id)
|
||||
data = {
|
||||
"id": str(inbound_sms.id),
|
||||
"from_number": inbound_sms.user_number,
|
||||
"content": inbound_sms.content,
|
||||
"date_received": inbound_sms.provider_date.strftime(DATETIME_FORMAT)
|
||||
}
|
||||
|
||||
response = request(
|
||||
method="POST",
|
||||
url=inbound_api.url,
|
||||
data=json.dumps(data),
|
||||
headers={
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer {}'.format(inbound_api.bearer_token)
|
||||
},
|
||||
timeout=60
|
||||
)
|
||||
try:
|
||||
response.raise_for_status()
|
||||
except HTTPError as e:
|
||||
current_app.logger.exception("Exception raised in send_inbound_sms_to_service for service_id: {} and url: {}. "
|
||||
"\n{}".format(service_id, inbound_api.url, e))
|
||||
if e.response.status_code >= 500:
|
||||
self.retry(queue=QueueNames.RETRY,
|
||||
exc='Unable to send_inbound_sms_to_service for service_id: {} and url: {}. \n{}'.format(
|
||||
service_id, inbound_api.url, e))
|
||||
except self.MaxRetriesExceededError:
|
||||
current_app.logger.exception('Retry: send_inbound_sms_to_service has retried the max number of times')
|
||||
|
||||
@@ -5,6 +5,8 @@ from flask import jsonify, Blueprint, current_app, request
|
||||
from notifications_utils.recipients import validate_and_format_phone_number
|
||||
|
||||
from app import statsd_client, firetext_client, mmg_client
|
||||
from app.celery import tasks
|
||||
from app.config import QueueNames
|
||||
from app.dao.services_dao import dao_fetch_services_by_sms_sender
|
||||
from app.dao.inbound_sms_dao import dao_create_inbound_sms
|
||||
from app.models import InboundSms
|
||||
@@ -30,28 +32,59 @@ def receive_mmg_sms():
|
||||
|
||||
inbound_number = strip_leading_forty_four(post_data['Number'])
|
||||
|
||||
potential_services = dao_fetch_services_by_sms_sender(inbound_number)
|
||||
|
||||
if len(potential_services) != 1:
|
||||
current_app.logger.error('Inbound number "{}" from MMG not associated with exactly one service'.format(
|
||||
post_data['Number']
|
||||
))
|
||||
statsd_client.incr('inbound.mmg.failed')
|
||||
potential_services = fetch_potential_services(inbound_number, 'mmg')
|
||||
if not potential_services:
|
||||
# since this is an issue with our service <-> number mapping, we should still tell MMG that we received
|
||||
# succesfully
|
||||
# successfully
|
||||
return 'RECEIVED', 200
|
||||
|
||||
statsd_client.incr('inbound.mmg.successful')
|
||||
|
||||
service = potential_services[0]
|
||||
|
||||
inbound = create_inbound_mmg_sms_object(service, post_data)
|
||||
inbound = create_inbound_sms_object(service,
|
||||
content=format_mmg_message(post_data["Message"]),
|
||||
from_number=post_data['MSISDN'],
|
||||
provider_ref=post_data["ID"],
|
||||
date_received=post_data.get('DateRecieved'),
|
||||
provider_name="mmg")
|
||||
|
||||
tasks.send_inbound_sms_to_service.apply_async([str(inbound.id), str(service.id)], queue=QueueNames.NOTIFY)
|
||||
current_app.logger.info('{} received inbound SMS with reference {}'.format(service.id, inbound.provider_reference))
|
||||
|
||||
return 'RECEIVED', 200
|
||||
|
||||
|
||||
@receive_notifications_blueprint.route('/notifications/sms/receive/firetext', methods=['POST'])
|
||||
def receive_firetext_sms():
|
||||
post_data = request.form
|
||||
|
||||
inbound_number = strip_leading_forty_four(post_data['destination'])
|
||||
|
||||
potential_services = fetch_potential_services(inbound_number, 'firetext')
|
||||
if not potential_services:
|
||||
return jsonify({
|
||||
"status": "ok"
|
||||
}), 200
|
||||
|
||||
service = potential_services[0]
|
||||
|
||||
inbound = create_inbound_sms_object(service=service,
|
||||
content=post_data["message"],
|
||||
from_number=post_data['source'],
|
||||
provider_ref=None,
|
||||
date_received=post_data['time'],
|
||||
provider_name="firetext")
|
||||
|
||||
statsd_client.incr('inbound.firetext.successful')
|
||||
|
||||
tasks.send_inbound_sms_to_service.apply_async([str(inbound.id), str(service.id)], queue=QueueNames.NOTIFY)
|
||||
|
||||
return jsonify({
|
||||
"status": "ok"
|
||||
}), 200
|
||||
|
||||
|
||||
def format_mmg_message(message):
|
||||
return unquote(message.replace('+', ' '))
|
||||
|
||||
@@ -66,11 +99,10 @@ def format_mmg_datetime(date):
|
||||
return convert_bst_to_utc(parsed_datetime)
|
||||
|
||||
|
||||
def create_inbound_mmg_sms_object(service, json):
|
||||
message = format_mmg_message(json['Message'])
|
||||
user_number = validate_and_format_phone_number(json['MSISDN'], international=True)
|
||||
def create_inbound_sms_object(service, content, from_number, provider_ref, date_received, provider_name):
|
||||
user_number = validate_and_format_phone_number(from_number, international=True)
|
||||
|
||||
provider_date = json.get('DateRecieved')
|
||||
provider_date = date_received
|
||||
if provider_date:
|
||||
provider_date = format_mmg_datetime(provider_date)
|
||||
|
||||
@@ -79,52 +111,24 @@ def create_inbound_mmg_sms_object(service, json):
|
||||
notify_number=service.sms_sender,
|
||||
user_number=user_number,
|
||||
provider_date=provider_date,
|
||||
provider_reference=json.get('ID'),
|
||||
content=message,
|
||||
provider=mmg_client.name
|
||||
provider_reference=provider_ref,
|
||||
content=content,
|
||||
provider=provider_name
|
||||
)
|
||||
dao_create_inbound_sms(inbound)
|
||||
return inbound
|
||||
|
||||
|
||||
@receive_notifications_blueprint.route('/notifications/sms/receive/firetext', methods=['POST'])
|
||||
def receive_firetext_sms():
|
||||
post_data = request.form
|
||||
|
||||
inbound_number = strip_leading_forty_four(post_data['destination'])
|
||||
|
||||
def fetch_potential_services(inbound_number, provider_name):
|
||||
potential_services = dao_fetch_services_by_sms_sender(inbound_number)
|
||||
|
||||
if len(potential_services) != 1:
|
||||
current_app.logger.error('Inbound number "{}" from firetext not associated with exactly one service'.format(
|
||||
post_data['destination']
|
||||
current_app.logger.error('Inbound number "{}" from {} not associated with exactly one service'.format(
|
||||
inbound_number, provider_name
|
||||
))
|
||||
statsd_client.incr('inbound.firetext.failed')
|
||||
return jsonify({
|
||||
"status": "ok"
|
||||
}), 200
|
||||
|
||||
service = potential_services[0]
|
||||
|
||||
user_number = validate_and_format_phone_number(post_data['source'], international=True)
|
||||
message = post_data['message']
|
||||
timestamp = post_data['time']
|
||||
|
||||
dao_create_inbound_sms(
|
||||
InboundSms(
|
||||
service=service,
|
||||
notify_number=service.sms_sender,
|
||||
user_number=user_number,
|
||||
provider_date=timestamp,
|
||||
content=message,
|
||||
provider=firetext_client.name
|
||||
)
|
||||
)
|
||||
|
||||
statsd_client.incr('inbound.firetext.successful')
|
||||
|
||||
return jsonify({
|
||||
"status": "ok"
|
||||
}), 200
|
||||
statsd_client.incr('inbound.{}.failed'.format(provider_name))
|
||||
return False
|
||||
return potential_services
|
||||
|
||||
|
||||
def strip_leading_forty_four(number):
|
||||
|
||||
@@ -591,9 +591,3 @@ def handle_sql_errror(e):
|
||||
return jsonify(result='error', message="No result found"), 404
|
||||
else:
|
||||
raise e
|
||||
|
||||
|
||||
@service_blueprint.route('/<uuid:service_id>/inbound-sms', methods=["POST"])
|
||||
def push_inbound_sms_to_service(service_id):
|
||||
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user