mirror of
https://github.com/GSA/notifications-api.git
synced 2026-02-05 02:41:14 -05:00
Made SMS messages go through celery
- twilio client pulled in from delivery app - made method to perform task
This commit is contained in:
@@ -12,12 +12,14 @@ from config import configs
|
|||||||
from utils import logging
|
from utils import logging
|
||||||
from notify_client import NotifyAPIClient
|
from notify_client import NotifyAPIClient
|
||||||
from app.celery.celery import NotifyCelery
|
from app.celery.celery import NotifyCelery
|
||||||
|
from app.clients.sms.twilio import TwilioClient
|
||||||
|
|
||||||
db = SQLAlchemy()
|
db = SQLAlchemy()
|
||||||
ma = Marshmallow()
|
ma = Marshmallow()
|
||||||
notify_alpha_client = NotifyAPIClient()
|
notify_alpha_client = NotifyAPIClient()
|
||||||
celery = NotifyCelery()
|
celery = NotifyCelery()
|
||||||
|
twilio_client = TwilioClient()
|
||||||
|
|
||||||
|
|
||||||
api_user = LocalProxy(lambda: _request_ctx_stack.top.api_user)
|
api_user = LocalProxy(lambda: _request_ctx_stack.top.api_user)
|
||||||
|
|
||||||
@@ -32,7 +34,7 @@ def create_app(config_name, config_overrides=None):
|
|||||||
ma.init_app(application)
|
ma.init_app(application)
|
||||||
init_app(application, config_overrides)
|
init_app(application, config_overrides)
|
||||||
logging.init_app(application)
|
logging.init_app(application)
|
||||||
|
twilio_client.init_app(application)
|
||||||
celery.init_app(application)
|
celery.init_app(application)
|
||||||
|
|
||||||
from app.service.rest import service as service_blueprint
|
from app.service.rest import service as service_blueprint
|
||||||
|
|||||||
@@ -1,9 +1,33 @@
|
|||||||
from app import celery
|
from itsdangerous import URLSafeSerializer
|
||||||
from app.dao.services_dao import get_model_services
|
from app import celery, twilio_client, db
|
||||||
|
from app.clients.sms.twilio import TwilioClientException
|
||||||
|
from app.dao.templates_dao import get_model_templates
|
||||||
|
from app.models import Notification
|
||||||
|
from flask import current_app
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="refresh-services")
|
@celery.task(name="send-sms", bind="True")
|
||||||
def refresh_services():
|
def send_sms(service_id, notification_id, encrypted_notification, secret_key, salt):
|
||||||
print(get_model_services())
|
serializer = URLSafeSerializer(secret_key)
|
||||||
for service in get_model_services():
|
|
||||||
celery.control.add_consumer(str(service.id))
|
notification = serializer.loads(encrypted_notification, salt=salt)
|
||||||
|
template = get_model_templates(notification['template'])
|
||||||
|
|
||||||
|
status = 'sent'
|
||||||
|
|
||||||
|
try:
|
||||||
|
twilio_client.send_sms(notification, template.content)
|
||||||
|
except TwilioClientException as e:
|
||||||
|
current_app.logger.info(e)
|
||||||
|
status = 'failed'
|
||||||
|
|
||||||
|
notification_db_object = Notification(
|
||||||
|
id=notification_id,
|
||||||
|
template_id=notification['template'],
|
||||||
|
to=notification['to'],
|
||||||
|
service_id=service_id,
|
||||||
|
status=status
|
||||||
|
)
|
||||||
|
|
||||||
|
db.session.add(notification_db_object)
|
||||||
|
db.session.commit()
|
||||||
|
|||||||
13
app/clients/__init__.py
Normal file
13
app/clients/__init__.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
class ClientException(Exception):
|
||||||
|
'''
|
||||||
|
Base Exceptions for sending notifications that fail
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Client(object):
|
||||||
|
'''
|
||||||
|
Base client for sending notifications.
|
||||||
|
'''
|
||||||
|
pass
|
||||||
17
app/clients/sms/__init__.py
Normal file
17
app/clients/sms/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
from app.clients import (Client, ClientException)
|
||||||
|
|
||||||
|
|
||||||
|
class SmsClientException(ClientException):
|
||||||
|
'''
|
||||||
|
Base Exception for SmsClients
|
||||||
|
'''
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SmsClient(Client):
|
||||||
|
'''
|
||||||
|
Base Sms client for sending smss.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def send_sms(self, *args, **kwargs):
|
||||||
|
raise NotImplemented('TODO Need to implement.')
|
||||||
48
app/clients/sms/twilio.py
Normal file
48
app/clients/sms/twilio.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import logging
|
||||||
|
from app.clients.sms import (
|
||||||
|
SmsClient, SmsClientException)
|
||||||
|
from twilio.rest import TwilioRestClient
|
||||||
|
from twilio import TwilioRestException
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class TwilioClientException(SmsClientException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class TwilioClient(SmsClient):
|
||||||
|
'''
|
||||||
|
Twilio sms client.
|
||||||
|
'''
|
||||||
|
def init_app(self, config, *args, **kwargs):
|
||||||
|
super(TwilioClient, self).__init__(*args, **kwargs)
|
||||||
|
self.client = TwilioRestClient(
|
||||||
|
config.config.get('TWILIO_ACCOUNT_SID'),
|
||||||
|
config.config.get('TWILIO_AUTH_TOKEN'))
|
||||||
|
self.from_number = config.config.get('TWILIO_NUMBER')
|
||||||
|
print(config.config)
|
||||||
|
|
||||||
|
|
||||||
|
def send_sms(self, notification, content):
|
||||||
|
try:
|
||||||
|
response = self.client.messages.create(
|
||||||
|
body=content,
|
||||||
|
to=notification['to'],
|
||||||
|
from_=self.from_number
|
||||||
|
)
|
||||||
|
return response.sid
|
||||||
|
except TwilioRestException as e:
|
||||||
|
logger.exception(e)
|
||||||
|
raise TwilioClientException(e)
|
||||||
|
|
||||||
|
def status(self, message_id):
|
||||||
|
try:
|
||||||
|
response = self.client.messages.get(message_id)
|
||||||
|
if response.status in ('delivered', 'undelivered', 'failed'):
|
||||||
|
return response.status
|
||||||
|
return None
|
||||||
|
except TwilioRestException as e:
|
||||||
|
logger.exception(e)
|
||||||
|
raise TwilioClientException(e)
|
||||||
@@ -3,43 +3,50 @@ import uuid
|
|||||||
from flask import (
|
from flask import (
|
||||||
Blueprint,
|
Blueprint,
|
||||||
jsonify,
|
jsonify,
|
||||||
request
|
request,
|
||||||
)
|
current_app)
|
||||||
|
from itsdangerous import URLSafeSerializer
|
||||||
|
|
||||||
from app import api_user
|
from app import api_user
|
||||||
from app.aws_sqs import add_notification_to_queue
|
from app.aws_sqs import add_notification_to_queue
|
||||||
from app.dao import (templates_dao)
|
from app.dao import (templates_dao)
|
||||||
from app.schemas import (
|
from app.schemas import (
|
||||||
email_notification_schema, sms_template_notification_schema)
|
email_notification_schema, sms_template_notification_schema)
|
||||||
from app import celery
|
from app.celery.tasks import send_sms
|
||||||
|
|
||||||
|
|
||||||
notifications = Blueprint('notifications', __name__)
|
notifications = Blueprint('notifications', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
def create_notification_id():
|
||||||
|
return str(uuid.uuid4())
|
||||||
|
|
||||||
|
|
||||||
@notifications.route('/<notification_id>', methods=['GET'])
|
@notifications.route('/<notification_id>', methods=['GET'])
|
||||||
def get_notifications(notification_id):
|
def get_notifications(notification_id):
|
||||||
# TODO return notification id details
|
# TODO return notification id details
|
||||||
return jsonify({'id': notification_id}), 200
|
return jsonify({'id': notification_id}), 200
|
||||||
|
|
||||||
|
|
||||||
@celery.task(name="make-sms", bind="True")
|
|
||||||
def send_sms(self):
|
|
||||||
print('Executing task id {0.id}, args: {0.args!r} kwargs: {0.kwargs!r}'.format(self.request))
|
|
||||||
from time import sleep
|
|
||||||
sleep(0.5)
|
|
||||||
print('finished')
|
|
||||||
|
|
||||||
|
|
||||||
@notifications.route('/sms', methods=['POST'])
|
@notifications.route('/sms', methods=['POST'])
|
||||||
def create_sms_notification():
|
def create_sms_notification():
|
||||||
|
serializer = URLSafeSerializer(current_app.config.get('SECRET_KEY'))
|
||||||
|
|
||||||
resp_json = request.get_json()
|
resp_json = request.get_json()
|
||||||
|
|
||||||
notification, errors = sms_template_notification_schema.load(resp_json)
|
notification, errors = sms_template_notification_schema.load(resp_json)
|
||||||
if errors:
|
if errors:
|
||||||
return jsonify(result="error", message=errors), 400
|
return jsonify(result="error", message=errors), 400
|
||||||
|
|
||||||
send_sms.delay()
|
notification_id = create_notification_id()
|
||||||
notification_id = add_notification_to_queue(api_user['client'], notification['template'], 'sms', notification)
|
encrypted_notification = serializer.dumps(notification, current_app.config.get('DANGEROUS_SALT'))
|
||||||
|
|
||||||
|
send_sms.apply_async((
|
||||||
|
api_user['client'],
|
||||||
|
notification_id,
|
||||||
|
encrypted_notification,
|
||||||
|
current_app.config.get('SECRET_KEY'),
|
||||||
|
current_app.config.get('DANGEROUS_SALT')))
|
||||||
return jsonify({'notification_id': notification_id}), 201
|
return jsonify({'notification_id': notification_id}), 201
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
17
config.py
17
config.py
@@ -22,7 +22,7 @@ class Config(object):
|
|||||||
# Notification Queue names are a combination of a prefx plus a name
|
# Notification Queue names are a combination of a prefx plus a name
|
||||||
NOTIFICATION_QUEUE_PREFIX = 'notification'
|
NOTIFICATION_QUEUE_PREFIX = 'notification'
|
||||||
|
|
||||||
BROKER_URL = 'amqp://guest:guest@localhost:5672//'
|
BROKER_URL = 'sqs://'
|
||||||
BROKER_TRANSPORT_OPTIONS = {
|
BROKER_TRANSPORT_OPTIONS = {
|
||||||
'region': 'eu-west-1',
|
'region': 'eu-west-1',
|
||||||
'polling_interval': 10, # 1 second
|
'polling_interval': 10, # 1 second
|
||||||
@@ -33,13 +33,16 @@ class Config(object):
|
|||||||
CELERY_TIMEZONE = 'Europe/London'
|
CELERY_TIMEZONE = 'Europe/London'
|
||||||
CELERY_ACCEPT_CONTENT = ['json']
|
CELERY_ACCEPT_CONTENT = ['json']
|
||||||
CELERY_TASK_SERIALIZER = 'json'
|
CELERY_TASK_SERIALIZER = 'json'
|
||||||
CELERYBEAT_SCHEDULE = {
|
# CELERYBEAT_SCHEDULE = {
|
||||||
'refresh-queues': {
|
# 'refresh-queues': {
|
||||||
'task': 'refresh-services',
|
# 'task': 'refresh-services',
|
||||||
'schedule': timedelta(seconds=5)
|
# 'schedule': timedelta(seconds=5)
|
||||||
}
|
# }
|
||||||
}
|
# }
|
||||||
CELERY_IMPORTS = ('app.celery.tasks',)
|
CELERY_IMPORTS = ('app.celery.tasks',)
|
||||||
|
TWILIO_ACCOUNT_SID = os.getenv('TWILIO_ACCOUNT_SID')
|
||||||
|
TWILIO_AUTH_TOKEN = os.getenv('TWILIO_AUTH_TOKEN')
|
||||||
|
TWILIO_NUMBER = os.getenv('TWILIO_NUMBER')
|
||||||
|
|
||||||
|
|
||||||
class Development(Config):
|
class Development(Config):
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ Flask-Bcrypt==0.6.2
|
|||||||
credstash==1.8.0
|
credstash==1.8.0
|
||||||
boto3==1.2.3
|
boto3==1.2.3
|
||||||
celery==3.1.20
|
celery==3.1.20
|
||||||
redis==2.10.5
|
twilio==4.6.0
|
||||||
|
|
||||||
|
|
||||||
git+https://github.com/alphagov/notifications-python-client.git@0.2.6#egg=notifications-python-client==0.2.6
|
git+https://github.com/alphagov/notifications-python-client.git@0.2.6#egg=notifications-python-client==0.2.6
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import os
|
import os
|
||||||
from app import celery, create_app
|
from app import celery, create_app
|
||||||
from app.celery.tasks import refresh_services
|
|
||||||
|
|
||||||
application = create_app(os.getenv('NOTIFY_API_ENVIRONMENT') or 'development')
|
application = create_app(os.getenv('NOTIFY_API_ENVIRONMENT') or 'development')
|
||||||
application.app_context().push()
|
application.app_context().push()
|
||||||
|
|||||||
Reference in New Issue
Block a user