mirror of
https://github.com/GSA/notifications-api.git
synced 2026-08-19 05:58:53 -04:00
Merge branch 'master' into split-sms-and-retry
This commit is contained in:
@@ -19,7 +19,7 @@ def send_sms_response(provider, reference, to):
|
||||
body = mmg_callback(reference, to)
|
||||
headers = {"Content-type": "application/json"}
|
||||
else:
|
||||
headers = {"Content-type": "text/plain"}
|
||||
headers = {"Content-type": "application/x-www-form-urlencoded"}
|
||||
body = firetext_callback(reference, to)
|
||||
make_request('sms', provider, body, headers)
|
||||
|
||||
@@ -92,7 +92,12 @@ def firetext_callback(notification_id, to):
|
||||
status = "1"
|
||||
else:
|
||||
status = "0"
|
||||
return 'mobile={}&status={}&time=2016-03-10 14:17:00&reference={}'.format(to, status, notification_id)
|
||||
return {
|
||||
'mobile': to,
|
||||
'status': status,
|
||||
'time': '2016-03-10 14:17:00',
|
||||
'reference': notification_id
|
||||
}
|
||||
|
||||
|
||||
def ses_notification_callback(reference):
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import itertools
|
||||
from datetime import datetime
|
||||
from datetime import (datetime, timedelta)
|
||||
|
||||
from flask import current_app
|
||||
from monotonic import monotonic
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from app import clients, statsd_client
|
||||
from app.clients import STATISTICS_FAILURE
|
||||
from app.clients.email import EmailClientException
|
||||
from app.clients.sms import SmsClientException
|
||||
from app.dao.services_dao import dao_fetch_service_by_id
|
||||
@@ -40,7 +41,9 @@ from app.dao.notifications_dao import (
|
||||
dao_update_notification,
|
||||
delete_notifications_created_more_than_a_week_ago,
|
||||
dao_get_notification_statistics_for_service_and_day,
|
||||
update_provider_stats
|
||||
update_provider_stats,
|
||||
get_notifications,
|
||||
update_notification_status_by_id
|
||||
)
|
||||
|
||||
from app.dao.jobs_dao import (
|
||||
@@ -337,20 +340,6 @@ def send_email(service_id, notification_id, from_address, encrypted_notification
|
||||
current_app.logger.exception(e)
|
||||
|
||||
|
||||
@notify_celery.task(name='send-sms-code')
|
||||
def send_sms_code(encrypted_verification):
|
||||
provider = provider_to_use('sms', 'send-sms-code')
|
||||
|
||||
verification_message = encryption.decrypt(encrypted_verification)
|
||||
try:
|
||||
provider.send_sms(validate_and_format_phone_number(verification_message['to']),
|
||||
"{} is your Notify authentication code".format(
|
||||
verification_message['secret_code']),
|
||||
'send-sms-code')
|
||||
except SmsClientException as e:
|
||||
current_app.logger.exception(e)
|
||||
|
||||
|
||||
# TODO: when placeholders in templates work, this will be a real template
|
||||
def invitation_template(user_name, service_name, url, expiry_date):
|
||||
from string import Template
|
||||
@@ -489,3 +478,23 @@ def provider_to_use(notification_type, notification_id):
|
||||
raise Exception("No active {} providers".format(notification_type))
|
||||
|
||||
return clients.get_client_by_name_and_type(active_providers_in_order[0].identifier, notification_type)
|
||||
|
||||
|
||||
@notify_celery.task(name='timeout-sending-notifications')
|
||||
def timeout_notifications():
|
||||
notifications = get_notifications(filter_dict={'status': 'sending'})
|
||||
now = datetime.utcnow()
|
||||
for noti in notifications:
|
||||
try:
|
||||
if (now - noti.created_at) > timedelta(
|
||||
seconds=current_app.config.get('SENDING_NOTIFICATIONS_TIMEOUT_PERIOD')
|
||||
):
|
||||
update_notification_status_by_id(noti.id, 'temporary-failure', STATISTICS_FAILURE)
|
||||
current_app.logger.info((
|
||||
"Timeout period reached for notification ({})"
|
||||
", status has been updated.").format(noti.id))
|
||||
except Exception as e:
|
||||
current_app.logger.exception(e)
|
||||
current_app.logger.error((
|
||||
"Exception raised trying to timeout notification ({})"
|
||||
", skipping notification update.").format(noti.id))
|
||||
|
||||
@@ -60,7 +60,7 @@ class FiretextClient(SmsClient):
|
||||
super(SmsClient, self).__init__(*args, **kwargs)
|
||||
self.current_app = current_app
|
||||
self.api_key = current_app.config.get('FIRETEXT_API_KEY')
|
||||
self.from_number = current_app.config.get('FIRETEXT_NUMBER')
|
||||
self.from_number = current_app.config.get('FROM_NUMBER')
|
||||
self.name = 'firetext'
|
||||
self.statsd_client = statsd_client
|
||||
|
||||
@@ -93,7 +93,7 @@ class FiretextClient(SmsClient):
|
||||
"POST",
|
||||
"https://www.firetext.co.uk/api/sendsms",
|
||||
response.status_code,
|
||||
firetext_response
|
||||
firetext_response.items()
|
||||
)
|
||||
)
|
||||
except RequestException as e:
|
||||
|
||||
@@ -60,7 +60,7 @@ class MMGClient(SmsClient):
|
||||
super(SmsClient, self).__init__(*args, **kwargs)
|
||||
self.current_app = current_app
|
||||
self.api_key = current_app.config.get('MMG_API_KEY')
|
||||
self.from_number = current_app.config.get('MMG_FROM_NUMBER')
|
||||
self.from_number = current_app.config.get('FROM_NUMBER')
|
||||
self.name = 'mmg'
|
||||
self.statsd_client = statsd_client
|
||||
|
||||
@@ -91,7 +91,7 @@ class MMGClient(SmsClient):
|
||||
"POST",
|
||||
"https://www.mmgrp.co.uk/API/json/api.php",
|
||||
response.status_code,
|
||||
response.json()
|
||||
response.json().items()
|
||||
)
|
||||
)
|
||||
except RequestException as e:
|
||||
|
||||
@@ -139,6 +139,14 @@ def dao_get_template_statistics_for_service(service_id, limit_days=None):
|
||||
desc(TemplateStatistics.updated_at)).all()
|
||||
|
||||
|
||||
def dao_get_template_statistics_for_template(template_id):
|
||||
return TemplateStatistics.query.filter(
|
||||
TemplateStatistics.template_id == template_id
|
||||
).order_by(
|
||||
desc(TemplateStatistics.updated_at)
|
||||
).all()
|
||||
|
||||
|
||||
@transactional
|
||||
def dao_create_notification(notification, notification_type):
|
||||
if notification.job_id:
|
||||
@@ -332,6 +340,10 @@ def get_notification_by_id(notification_id):
|
||||
return Notification.query.filter_by(id=notification_id).first()
|
||||
|
||||
|
||||
def get_notifications(filter_dict=None):
|
||||
return _filter_query(Notification.query, filter_dict=filter_dict)
|
||||
|
||||
|
||||
def get_notifications_for_service(service_id,
|
||||
filter_dict=None,
|
||||
page=1,
|
||||
|
||||
@@ -32,6 +32,8 @@ class PermissionDAO(DAOClass):
|
||||
class Meta:
|
||||
model = Permission
|
||||
|
||||
# TODO rework this as last filter wins, whereas what is needed is
|
||||
# append to filter so that semantics are 'and'
|
||||
def get_query(self, filter_by_dict=None):
|
||||
if filter_by_dict is None:
|
||||
filter_by_dict = MultiDict()
|
||||
@@ -60,13 +62,14 @@ class PermissionDAO(DAOClass):
|
||||
self.create_instance(permission, _commit=False)
|
||||
|
||||
def remove_user_service_permissions(self, user, service):
|
||||
query = self.get_query(filter_by_dict={'user': user.id, 'service': service.id})
|
||||
query = self.Meta.model.query.filter_by(user=user, service=service)
|
||||
query.delete()
|
||||
|
||||
def set_user_service_permission(self, user, service, permissions, _commit=False):
|
||||
def set_user_service_permission(self, user, service, permissions, _commit=False, replace=False):
|
||||
try:
|
||||
query = self.get_query(filter_by_dict={'user': user.id, 'service': service.id})
|
||||
query.delete()
|
||||
if replace:
|
||||
query = self.Meta.model.query.filter_by(user=user, service=service)
|
||||
query.delete()
|
||||
for p in permissions:
|
||||
p.user = user
|
||||
p.service = service
|
||||
|
||||
@@ -176,9 +176,12 @@ def process_firetext_response():
|
||||
current_app.logger.info(validation_errors)
|
||||
return jsonify(result='error', message=validation_errors), 400
|
||||
|
||||
statsd_client.incr('notifications.callback.firetext.code.{}'.format(request.form.get('code')))
|
||||
response_code = request.form.get('code')
|
||||
status = request.form.get('status')
|
||||
statsd_client.incr('notifications.callback.firetext.code.{}'.format(response_code))
|
||||
current_app.logger.info('Firetext status: {}, extended error code: {}'.format(status, response_code))
|
||||
|
||||
success, errors = process_sms_client_response(status=request.form.get('status'),
|
||||
success, errors = process_sms_client_response(status=status,
|
||||
reference=request.form.get('reference'),
|
||||
client_name=client_name)
|
||||
if errors:
|
||||
|
||||
@@ -160,6 +160,7 @@ class JobSchema(BaseSchema):
|
||||
|
||||
class Meta:
|
||||
model = models.Job
|
||||
exclude = ('notifications',)
|
||||
|
||||
|
||||
class RequestVerifyCodeSchema(ma.Schema):
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
from datetime import (datetime, date)
|
||||
from datetime import (
|
||||
datetime,
|
||||
date
|
||||
)
|
||||
|
||||
from flask import Blueprint
|
||||
from flask import (
|
||||
jsonify,
|
||||
request
|
||||
request,
|
||||
abort,
|
||||
Blueprint
|
||||
)
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
|
||||
@@ -152,6 +156,8 @@ def add_user_to_service(service_id, user_id):
|
||||
message='User id: {} already part of service id: {}'.format(user_id, service_id)), 400
|
||||
|
||||
permissions, errors = permission_schema.load(request.get_json(), many=True)
|
||||
if errors:
|
||||
abort(400, errors)
|
||||
|
||||
dao_add_user_to_service(service, user, permissions)
|
||||
data, errors = service_schema.dump(service)
|
||||
@@ -174,15 +180,6 @@ def remove_user_from_service(service_id, user_id):
|
||||
return jsonify({}), 204
|
||||
|
||||
|
||||
def _process_permissions(user, service, permission_groups):
|
||||
from app.permissions_utils import get_permissions_by_group
|
||||
permissions = get_permissions_by_group(permission_groups)
|
||||
for permission in permissions:
|
||||
permission.user = user
|
||||
permission.service = service
|
||||
return permissions
|
||||
|
||||
|
||||
@service.route('/<uuid:service_id>/fragment/aggregate_statistics')
|
||||
def get_service_provider_aggregate_statistics(service_id):
|
||||
service = dao_fetch_service_by_id(service_id)
|
||||
|
||||
@@ -5,7 +5,10 @@ from flask import (
|
||||
current_app
|
||||
)
|
||||
|
||||
from app.dao.notifications_dao import dao_get_template_statistics_for_service
|
||||
from app.dao.notifications_dao import (
|
||||
dao_get_template_statistics_for_service,
|
||||
dao_get_template_statistics_for_template
|
||||
)
|
||||
|
||||
from app.schemas import template_statistics_schema
|
||||
|
||||
@@ -34,3 +37,12 @@ def get_template_statistics_for_service(service_id):
|
||||
if errors:
|
||||
return jsonify(result="error", message=errors), 400
|
||||
return jsonify(data=data)
|
||||
|
||||
|
||||
@template_statistics.route('/<template_id>')
|
||||
def get_template_statistics_for_template_id(service_id, template_id):
|
||||
stats = dao_get_template_statistics_for_template(template_id)
|
||||
data, errors = template_statistics_schema.dump(stats, many=True)
|
||||
if errors:
|
||||
return jsonify(result="error", message=errors), 400
|
||||
return jsonify(data=data)
|
||||
|
||||
@@ -188,7 +188,7 @@ def set_permissions(user_id, service_id):
|
||||
for p in permissions:
|
||||
p.user = user
|
||||
p.service = service
|
||||
permission_dao.set_user_service_permission(user, service, permissions, _commit=True)
|
||||
permission_dao.set_user_service_permission(user, service, permissions, _commit=True, replace=True)
|
||||
return jsonify({}), 204
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user