2016-07-01 14:06:32 +01:00
|
|
|
import re
|
2016-06-14 15:07:23 +01:00
|
|
|
from datetime import (
|
|
|
|
|
datetime,
|
2016-08-24 16:00:21 +01:00
|
|
|
date,
|
|
|
|
|
timedelta)
|
2016-02-01 10:48:33 +00:00
|
|
|
from flask_marshmallow.fields import fields
|
2016-04-04 12:21:38 +01:00
|
|
|
from marshmallow import (
|
|
|
|
|
post_load,
|
|
|
|
|
ValidationError,
|
|
|
|
|
validates,
|
|
|
|
|
validates_schema,
|
2016-06-14 15:07:23 +01:00
|
|
|
pre_load,
|
2016-06-20 16:23:56 +01:00
|
|
|
pre_dump,
|
|
|
|
|
post_dump
|
2016-04-04 12:21:38 +01:00
|
|
|
)
|
2016-03-01 14:21:28 +00:00
|
|
|
from marshmallow_sqlalchemy import field_for
|
2016-04-04 12:21:38 +01:00
|
|
|
|
2016-04-13 15:31:08 +01:00
|
|
|
from notifications_utils.recipients import (
|
2016-04-04 12:21:38 +01:00
|
|
|
validate_email_address,
|
|
|
|
|
InvalidEmailError,
|
|
|
|
|
validate_phone_number,
|
|
|
|
|
InvalidPhoneError,
|
2016-03-24 16:57:25 +00:00
|
|
|
validate_and_format_phone_number
|
2016-03-06 13:21:46 +00:00
|
|
|
)
|
2016-02-22 17:17:29 +00:00
|
|
|
|
2016-04-04 12:21:38 +01:00
|
|
|
from app import ma
|
|
|
|
|
from app import models
|
2017-05-22 11:33:24 +01:00
|
|
|
from app.models import ServicePermission
|
2016-04-04 12:21:38 +01:00
|
|
|
from app.dao.permissions_dao import permission_dao
|
2016-12-09 15:56:25 +00:00
|
|
|
from app.utils import get_template_instance
|
2016-04-04 12:21:38 +01:00
|
|
|
|
2016-01-08 17:51:46 +00:00
|
|
|
|
2016-05-05 10:45:47 +01:00
|
|
|
def _validate_positive_number(value, msg="Not a positive integer"):
|
|
|
|
|
try:
|
|
|
|
|
page_int = int(value)
|
|
|
|
|
if page_int < 1:
|
|
|
|
|
raise ValidationError(msg)
|
|
|
|
|
except:
|
|
|
|
|
raise ValidationError(msg)
|
|
|
|
|
|
|
|
|
|
|
2016-10-12 07:45:31 +01:00
|
|
|
def _validate_datetime_not_more_than_96_hours_in_future(dte, msg="Date cannot be more than 96hrs in the future"):
|
|
|
|
|
if dte > datetime.utcnow() + timedelta(hours=96):
|
2016-08-24 16:00:21 +01:00
|
|
|
raise ValidationError(msg)
|
|
|
|
|
|
|
|
|
|
|
2016-05-05 10:45:47 +01:00
|
|
|
def _validate_not_in_future(dte, msg="Date cannot be in the future"):
|
|
|
|
|
if dte > date.today():
|
|
|
|
|
raise ValidationError(msg)
|
|
|
|
|
|
|
|
|
|
|
2016-08-24 16:00:21 +01:00
|
|
|
def _validate_not_in_past(dte, msg="Date cannot be in the past"):
|
2016-08-30 12:47:33 +01:00
|
|
|
if dte < date.today():
|
|
|
|
|
raise ValidationError(msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _validate_datetime_not_in_future(dte, msg="Date cannot be in the future"):
|
|
|
|
|
if dte > datetime.utcnow():
|
|
|
|
|
raise ValidationError(msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _validate_datetime_not_in_past(dte, msg="Date cannot be in the past"):
|
|
|
|
|
if dte < datetime.utcnow():
|
2016-08-24 16:00:21 +01:00
|
|
|
raise ValidationError(msg)
|
|
|
|
|
|
|
|
|
|
|
2016-01-29 11:11:00 +00:00
|
|
|
class BaseSchema(ma.ModelSchema):
|
2016-06-14 15:07:23 +01:00
|
|
|
|
2016-04-04 12:21:38 +01:00
|
|
|
def __init__(self, load_json=False, *args, **kwargs):
|
2016-01-28 11:41:21 +00:00
|
|
|
self.load_json = load_json
|
2016-01-29 11:11:00 +00:00
|
|
|
super(BaseSchema, self).__init__(*args, **kwargs)
|
2016-01-11 15:07:13 +00:00
|
|
|
|
2016-01-28 11:41:21 +00:00
|
|
|
@post_load
|
|
|
|
|
def make_instance(self, data):
|
|
|
|
|
"""Deserialize data to an instance of the model. Update an existing row
|
|
|
|
|
if specified in `self.instance` or loaded by primary key(s) in the data;
|
|
|
|
|
else create a new row.
|
|
|
|
|
:param data: Data to deserialize.
|
|
|
|
|
"""
|
|
|
|
|
if self.load_json:
|
|
|
|
|
return data
|
2016-01-29 11:11:00 +00:00
|
|
|
return super(BaseSchema, self).make_instance(data)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class UserSchema(BaseSchema):
|
2016-02-26 15:57:24 +00:00
|
|
|
|
|
|
|
|
permissions = fields.Method("user_permissions", dump_only=True)
|
2016-03-08 14:33:06 +00:00
|
|
|
password_changed_at = field_for(models.User, 'password_changed_at', format='%Y-%m-%d %H:%M:%S.%f')
|
|
|
|
|
created_at = field_for(models.User, 'created_at', format='%Y-%m-%d %H:%M:%S.%f')
|
2016-02-26 15:57:24 +00:00
|
|
|
|
|
|
|
|
def user_permissions(self, usr):
|
|
|
|
|
retval = {}
|
2016-06-15 16:32:52 +01:00
|
|
|
for x in permission_dao.get_permissions_by_user_id(usr.id):
|
2016-02-26 15:57:24 +00:00
|
|
|
service_id = str(x.service_id)
|
|
|
|
|
if service_id not in retval:
|
|
|
|
|
retval[service_id] = []
|
|
|
|
|
retval[service_id].append(x.permission)
|
|
|
|
|
return retval
|
|
|
|
|
|
2016-01-29 11:11:00 +00:00
|
|
|
class Meta:
|
|
|
|
|
model = models.User
|
|
|
|
|
exclude = (
|
|
|
|
|
"updated_at", "created_at", "user_to_service",
|
|
|
|
|
"_password", "verify_codes")
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
2016-01-28 11:41:21 +00:00
|
|
|
|
2016-01-08 17:51:46 +00:00
|
|
|
|
2016-11-07 17:41:49 +00:00
|
|
|
class UserUpdateAttributeSchema(BaseSchema):
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = models.User
|
|
|
|
|
exclude = (
|
2016-11-10 13:09:25 +00:00
|
|
|
'id', 'updated_at', 'created_at', 'user_to_service',
|
|
|
|
|
'_password', 'verify_codes', 'logged_in_at', 'password_changed_at',
|
|
|
|
|
'failed_login_count', 'state', 'platform_admin')
|
2016-11-07 17:41:49 +00:00
|
|
|
strict = True
|
|
|
|
|
|
|
|
|
|
@validates('name')
|
|
|
|
|
def validate_name(self, value):
|
|
|
|
|
if not value:
|
|
|
|
|
raise ValidationError('Invalid name')
|
|
|
|
|
|
|
|
|
|
@validates('email_address')
|
|
|
|
|
def validate_email_address(self, value):
|
|
|
|
|
try:
|
|
|
|
|
validate_email_address(value)
|
|
|
|
|
except InvalidEmailError as e:
|
2017-01-09 16:22:27 +00:00
|
|
|
raise ValidationError(str(e))
|
2016-11-07 17:41:49 +00:00
|
|
|
|
|
|
|
|
@validates('mobile_number')
|
|
|
|
|
def validate_mobile_number(self, value):
|
|
|
|
|
try:
|
|
|
|
|
validate_phone_number(value)
|
|
|
|
|
except InvalidPhoneError as error:
|
|
|
|
|
raise ValidationError('Invalid phone number: {}'.format(error))
|
|
|
|
|
|
|
|
|
|
@validates_schema(pass_original=True)
|
|
|
|
|
def check_unknown_fields(self, data, original_data):
|
|
|
|
|
for key in original_data:
|
|
|
|
|
if key not in self.fields:
|
|
|
|
|
raise ValidationError('Unknown field name {}'.format(key))
|
|
|
|
|
|
|
|
|
|
|
2017-02-07 11:27:13 +00:00
|
|
|
class UserUpdatePasswordSchema(BaseSchema):
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = models.User
|
|
|
|
|
only = ('password')
|
|
|
|
|
strict = True
|
|
|
|
|
|
|
|
|
|
@validates_schema(pass_original=True)
|
|
|
|
|
def check_unknown_fields(self, data, original_data):
|
|
|
|
|
for key in original_data:
|
|
|
|
|
if key not in self.fields:
|
|
|
|
|
raise ValidationError('Unknown field name {}'.format(key))
|
|
|
|
|
|
|
|
|
|
|
2016-05-10 09:04:22 +01:00
|
|
|
class ProviderDetailsSchema(BaseSchema):
|
2017-03-08 23:15:56 +00:00
|
|
|
created_by = fields.Nested(UserSchema, only=['id', 'name', 'email_address'], dump_only=True)
|
2017-03-02 18:10:33 +00:00
|
|
|
|
2016-05-10 09:04:22 +01:00
|
|
|
class Meta:
|
|
|
|
|
model = models.ProviderDetails
|
|
|
|
|
exclude = ("provider_rates", "provider_stats")
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
2016-05-10 09:04:22 +01:00
|
|
|
|
|
|
|
|
|
2017-03-08 17:21:02 +00:00
|
|
|
class ProviderDetailsHistorySchema(BaseSchema):
|
2017-03-08 23:15:56 +00:00
|
|
|
created_by = fields.Nested(UserSchema, only=['id', 'name', 'email_address'], dump_only=True)
|
2017-03-08 17:21:02 +00:00
|
|
|
|
2017-03-02 18:10:33 +00:00
|
|
|
class Meta:
|
|
|
|
|
model = models.ProviderDetailsHistory
|
2017-03-08 17:21:02 +00:00
|
|
|
exclude = ("provider_rates", "provider_stats")
|
|
|
|
|
strict = True
|
2017-03-02 18:10:33 +00:00
|
|
|
|
|
|
|
|
|
2016-04-29 12:24:43 +01:00
|
|
|
class ServiceSchema(BaseSchema):
|
|
|
|
|
|
|
|
|
|
created_by = field_for(models.Service, 'created_by', required=True)
|
2016-08-15 10:54:26 +01:00
|
|
|
organisation = field_for(models.Service, 'organisation')
|
2016-08-12 11:40:57 +01:00
|
|
|
branding = field_for(models.Service, 'branding')
|
2017-04-21 12:39:42 +01:00
|
|
|
dvla_organisation = field_for(models.Service, 'dvla_organisation')
|
2017-05-22 11:33:24 +01:00
|
|
|
permissions = fields.Method("service_permissions")
|
|
|
|
|
|
|
|
|
|
def service_permissions(self, service):
|
|
|
|
|
permissions = []
|
|
|
|
|
|
|
|
|
|
from app.dao.service_permissions_dao import dao_fetch_service_permissions
|
|
|
|
|
perms = dao_fetch_service_permissions(service.id)
|
|
|
|
|
for p in perms:
|
|
|
|
|
permission = {
|
|
|
|
|
"service_id": service.id,
|
|
|
|
|
"permission": p.permission
|
|
|
|
|
}
|
|
|
|
|
permissions.append(permission)
|
|
|
|
|
return permissions
|
2016-04-29 12:24:43 +01:00
|
|
|
|
2016-01-08 17:51:46 +00:00
|
|
|
class Meta:
|
|
|
|
|
model = models.Service
|
2017-05-22 11:33:24 +01:00
|
|
|
exclude = (
|
|
|
|
|
'updated_at',
|
|
|
|
|
'created_at',
|
|
|
|
|
'api_keys',
|
|
|
|
|
'templates',
|
|
|
|
|
'jobs',
|
|
|
|
|
'old_id',
|
|
|
|
|
'template_statistics',
|
|
|
|
|
'service_provider_stats',
|
|
|
|
|
'service_notification_stats',
|
|
|
|
|
)
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
2016-01-13 11:04:13 +00:00
|
|
|
|
2016-07-01 14:06:32 +01:00
|
|
|
@validates('sms_sender')
|
|
|
|
|
def validate_sms_sender(self, value):
|
2016-08-04 12:35:47 +01:00
|
|
|
if value and not re.match(r'^[a-zA-Z0-9\s]+$', value):
|
2016-07-01 14:06:32 +01:00
|
|
|
raise ValidationError('Only alphanumeric characters allowed')
|
|
|
|
|
|
2017-05-22 11:33:24 +01:00
|
|
|
@validates('permissions')
|
|
|
|
|
def validate_permissions(self, value):
|
|
|
|
|
for v in [val.permission for val in value]:
|
|
|
|
|
if v not in models.SERVICE_PERMISSION_TYPES:
|
|
|
|
|
raise ValidationError("Invalid Service Permission: '{}'".format(v))
|
|
|
|
|
|
|
|
|
|
@pre_load()
|
|
|
|
|
def format_permissions_for_data_model(self, in_data):
|
|
|
|
|
if isinstance(in_data, dict) and 'permissions' in in_data:
|
|
|
|
|
permissions = []
|
|
|
|
|
for p in in_data.get('permissions'):
|
|
|
|
|
permission = models.ServicePermission(service_id=in_data["id"], permission=p)
|
|
|
|
|
permissions.append(permission)
|
|
|
|
|
in_data['permissions'] = permissions
|
|
|
|
|
|
|
|
|
|
@post_dump
|
|
|
|
|
def format_permissions_as_string_array(self, in_data):
|
|
|
|
|
if isinstance(in_data, dict) and 'permissions' in in_data:
|
|
|
|
|
in_data['permissions'] = [p.get('permission') for p in in_data.get('permissions')]
|
|
|
|
|
return in_data
|
|
|
|
|
|
2016-01-13 11:04:13 +00:00
|
|
|
|
2016-07-18 12:03:44 +01:00
|
|
|
class DetailedServiceSchema(BaseSchema):
|
|
|
|
|
statistics = fields.Dict()
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = models.Service
|
|
|
|
|
exclude = (
|
|
|
|
|
'api_keys',
|
|
|
|
|
'templates',
|
|
|
|
|
'users',
|
|
|
|
|
'created_by',
|
|
|
|
|
'jobs',
|
|
|
|
|
'template_statistics',
|
|
|
|
|
'service_provider_stats',
|
2016-08-04 12:35:47 +01:00
|
|
|
'service_notification_stats',
|
|
|
|
|
'organisation'
|
2016-07-18 12:03:44 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2016-04-04 13:13:29 +01:00
|
|
|
class NotificationModelSchema(BaseSchema):
|
|
|
|
|
class Meta:
|
|
|
|
|
model = models.Notification
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
2017-05-10 17:30:09 +01:00
|
|
|
exclude = ('_personalisation', 'job', 'service', 'template', 'api_key', '_status_enum', '_status_fkey')
|
|
|
|
|
|
|
|
|
|
status = fields.String(required=False)
|
2016-04-04 13:13:29 +01:00
|
|
|
|
|
|
|
|
|
2016-04-08 13:34:46 +01:00
|
|
|
class BaseTemplateSchema(BaseSchema):
|
2016-04-25 10:38:37 +01:00
|
|
|
|
2016-01-13 11:04:13 +00:00
|
|
|
class Meta:
|
|
|
|
|
model = models.Template
|
2016-05-19 17:02:55 +01:00
|
|
|
exclude = ("service_id", "jobs")
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
2016-01-11 15:07:13 +00:00
|
|
|
|
2016-01-08 17:51:46 +00:00
|
|
|
|
2016-04-29 12:24:43 +01:00
|
|
|
class TemplateSchema(BaseTemplateSchema):
|
|
|
|
|
|
|
|
|
|
created_by = field_for(models.Template, 'created_by', required=True)
|
2017-01-17 15:48:51 +00:00
|
|
|
process_type = field_for(models.Template, 'process_type')
|
2016-04-08 13:34:46 +01:00
|
|
|
|
|
|
|
|
@validates_schema
|
|
|
|
|
def validate_type(self, data):
|
2016-11-07 15:21:48 +00:00
|
|
|
if data.get('template_type') in [models.EMAIL_TYPE, models.LETTER_TYPE]:
|
2016-04-08 13:34:46 +01:00
|
|
|
subject = data.get('subject')
|
|
|
|
|
if not subject or subject.strip() == '':
|
|
|
|
|
raise ValidationError('Invalid template subject', 'subject')
|
|
|
|
|
|
|
|
|
|
|
2016-05-10 14:55:59 +01:00
|
|
|
class TemplateHistorySchema(BaseSchema):
|
2016-05-06 15:42:43 +01:00
|
|
|
|
2016-08-08 16:57:39 +01:00
|
|
|
created_by = fields.Nested(UserSchema, only=['id', 'name', 'email_address'], dump_only=True)
|
2016-05-10 14:55:59 +01:00
|
|
|
created_at = field_for(models.Template, 'created_at', format='%Y-%m-%d %H:%M:%S.%f')
|
|
|
|
|
|
2016-08-08 16:57:39 +01:00
|
|
|
class Meta:
|
|
|
|
|
model = models.TemplateHistory
|
2016-05-06 15:42:43 +01:00
|
|
|
|
|
|
|
|
|
2016-03-08 16:34:03 +00:00
|
|
|
class NotificationsStatisticsSchema(BaseSchema):
|
|
|
|
|
class Meta:
|
|
|
|
|
model = models.NotificationStatistics
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
|
|
|
|
|
|
|
|
|
@pre_dump
|
|
|
|
|
def handle_date_str(self, in_data):
|
|
|
|
|
if isinstance(in_data, dict) and 'day' in in_data:
|
|
|
|
|
in_data['day'] = datetime.strptime(in_data['day'], '%Y-%m-%d').date()
|
|
|
|
|
return in_data
|
2016-03-08 16:34:03 +00:00
|
|
|
|
|
|
|
|
|
2016-04-29 12:24:43 +01:00
|
|
|
class ApiKeySchema(BaseSchema):
|
|
|
|
|
|
|
|
|
|
created_by = field_for(models.ApiKey, 'created_by', required=True)
|
2016-06-27 16:55:51 +01:00
|
|
|
key_type = field_for(models.ApiKey, 'key_type', required=True)
|
2016-04-29 12:24:43 +01:00
|
|
|
|
2016-01-13 09:25:46 +00:00
|
|
|
class Meta:
|
2016-01-19 12:07:00 +00:00
|
|
|
model = models.ApiKey
|
2016-01-20 15:23:32 +00:00
|
|
|
exclude = ("service", "secret")
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
2016-01-14 11:30:45 +00:00
|
|
|
|
2016-01-13 09:25:46 +00:00
|
|
|
|
2016-01-29 11:11:00 +00:00
|
|
|
class JobSchema(BaseSchema):
|
2016-05-11 12:03:25 +01:00
|
|
|
created_by_user = fields.Nested(UserSchema, attribute="created_by",
|
|
|
|
|
dump_to="created_by", only=["id", "name"], dump_only=True)
|
|
|
|
|
created_by = field_for(models.Job, 'created_by', required=True, load_only=True)
|
|
|
|
|
|
2016-08-24 14:35:22 +01:00
|
|
|
job_status = field_for(models.JobStatus, 'name', required=False)
|
|
|
|
|
|
2016-08-24 16:00:21 +01:00
|
|
|
scheduled_for = fields.DateTime()
|
2017-04-07 14:36:00 +01:00
|
|
|
service_name = fields.Nested(
|
|
|
|
|
ServiceSchema, attribute="service", dump_to="service_name", only=["name"], dump_only=True)
|
2016-08-24 16:00:21 +01:00
|
|
|
|
|
|
|
|
@validates('scheduled_for')
|
|
|
|
|
def validate_scheduled_for(self, value):
|
2016-08-30 12:47:33 +01:00
|
|
|
_validate_datetime_not_in_past(value)
|
2016-10-12 07:45:31 +01:00
|
|
|
_validate_datetime_not_more_than_96_hours_in_future(value)
|
2016-08-24 16:00:21 +01:00
|
|
|
|
2016-01-15 15:48:05 +00:00
|
|
|
class Meta:
|
|
|
|
|
model = models.Job
|
2016-08-23 16:46:58 +01:00
|
|
|
exclude = (
|
|
|
|
|
'notifications',
|
|
|
|
|
'notifications_sent',
|
|
|
|
|
'notifications_delivered',
|
|
|
|
|
'notifications_failed')
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
2016-01-15 15:48:05 +00:00
|
|
|
|
|
|
|
|
|
2016-02-19 11:37:35 +00:00
|
|
|
class RequestVerifyCodeSchema(ma.Schema):
|
2016-06-14 15:07:23 +01:00
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
strict = True
|
|
|
|
|
|
2016-02-19 11:37:35 +00:00
|
|
|
to = fields.Str(required=False)
|
|
|
|
|
|
|
|
|
|
|
2016-02-03 13:16:19 +00:00
|
|
|
class NotificationSchema(ma.Schema):
|
2016-06-14 15:07:23 +01:00
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
strict = True
|
|
|
|
|
|
2017-05-10 17:30:09 +01:00
|
|
|
status = fields.String(required=False)
|
2016-02-29 11:23:34 +00:00
|
|
|
personalisation = fields.Dict(required=False)
|
2016-02-03 13:16:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class SmsNotificationSchema(NotificationSchema):
|
|
|
|
|
to = fields.Str(required=True)
|
|
|
|
|
|
|
|
|
|
@validates('to')
|
|
|
|
|
def validate_to(self, value):
|
2016-03-06 12:58:28 +00:00
|
|
|
try:
|
2017-04-26 15:56:45 +01:00
|
|
|
validate_phone_number(value, international=True)
|
2016-03-06 12:58:28 +00:00
|
|
|
except InvalidPhoneError as error:
|
|
|
|
|
raise ValidationError('Invalid phone number: {}'.format(error))
|
2016-02-03 13:16:19 +00:00
|
|
|
|
2016-03-06 13:21:46 +00:00
|
|
|
@post_load
|
|
|
|
|
def format_phone_number(self, item):
|
2017-04-26 15:56:45 +01:00
|
|
|
item['to'] = validate_and_format_phone_number(item['to'], international=True)
|
2016-03-06 13:21:46 +00:00
|
|
|
return item
|
2016-02-03 13:16:19 +00:00
|
|
|
|
|
|
|
|
|
2016-02-22 17:17:29 +00:00
|
|
|
class EmailNotificationSchema(NotificationSchema):
|
|
|
|
|
to = fields.Str(required=True)
|
2016-04-08 13:34:46 +01:00
|
|
|
template = fields.Str(required=True)
|
2016-02-22 17:17:29 +00:00
|
|
|
|
|
|
|
|
@validates('to')
|
|
|
|
|
def validate_to(self, value):
|
2016-03-06 12:58:28 +00:00
|
|
|
try:
|
|
|
|
|
validate_email_address(value)
|
2016-03-08 17:46:00 +00:00
|
|
|
except InvalidEmailError as e:
|
2017-01-09 16:22:27 +00:00
|
|
|
raise ValidationError(str(e))
|
2016-02-22 17:17:29 +00:00
|
|
|
|
|
|
|
|
|
2016-02-03 13:16:19 +00:00
|
|
|
class SmsTemplateNotificationSchema(SmsNotificationSchema):
|
2016-04-08 13:34:46 +01:00
|
|
|
template = fields.Str(required=True)
|
2016-02-08 14:54:15 +00:00
|
|
|
job = fields.String()
|
2016-02-03 13:16:19 +00:00
|
|
|
|
|
|
|
|
|
2016-02-23 17:30:50 +00:00
|
|
|
class JobSmsTemplateNotificationSchema(SmsNotificationSchema):
|
2016-04-08 13:34:46 +01:00
|
|
|
template = fields.Str(required=True)
|
2016-02-23 17:30:50 +00:00
|
|
|
job = fields.String(required=True)
|
|
|
|
|
|
|
|
|
|
|
2016-02-24 09:55:05 +00:00
|
|
|
class JobEmailTemplateNotificationSchema(EmailNotificationSchema):
|
2016-04-08 13:34:46 +01:00
|
|
|
template = fields.Str(required=True)
|
2016-02-24 09:55:05 +00:00
|
|
|
job = fields.String(required=True)
|
|
|
|
|
|
|
|
|
|
|
2016-02-03 13:16:19 +00:00
|
|
|
class SmsAdminNotificationSchema(SmsNotificationSchema):
|
|
|
|
|
content = fields.Str(required=True)
|
|
|
|
|
|
|
|
|
|
|
2016-07-26 12:34:39 +01:00
|
|
|
class NotificationWithTemplateSchema(BaseSchema):
|
2016-07-26 14:33:14 +01:00
|
|
|
class Meta:
|
|
|
|
|
model = models.Notification
|
|
|
|
|
strict = True
|
2017-05-10 17:30:09 +01:00
|
|
|
exclude = ('_personalisation', '_status_enum', '_status_fkey')
|
2016-02-09 14:17:42 +00:00
|
|
|
|
2016-08-09 16:53:09 +01:00
|
|
|
template = fields.Nested(
|
|
|
|
|
TemplateSchema,
|
|
|
|
|
only=['id', 'version', 'name', 'template_type', 'content', 'subject'],
|
|
|
|
|
dump_only=True
|
|
|
|
|
)
|
2016-03-16 16:47:18 +00:00
|
|
|
job = fields.Nested(JobSchema, only=["id", "original_file_name"], dump_only=True)
|
2017-05-10 17:30:09 +01:00
|
|
|
status = fields.String(required=False)
|
2016-06-20 16:23:56 +01:00
|
|
|
personalisation = fields.Dict(required=False)
|
2016-09-23 14:44:15 +01:00
|
|
|
key_type = field_for(models.Notification, 'key_type', required=True)
|
|
|
|
|
key_name = fields.String()
|
|
|
|
|
|
|
|
|
|
@pre_dump
|
|
|
|
|
def add_api_key_name(self, in_data):
|
|
|
|
|
if in_data.api_key:
|
|
|
|
|
in_data.key_name = in_data.api_key.name
|
|
|
|
|
else:
|
|
|
|
|
in_data.key_name = None
|
|
|
|
|
return in_data
|
2016-03-15 14:24:10 +00:00
|
|
|
|
2016-06-20 16:23:56 +01:00
|
|
|
|
2016-07-26 14:33:14 +01:00
|
|
|
class NotificationWithPersonalisationSchema(NotificationWithTemplateSchema):
|
2016-08-10 16:57:27 +01:00
|
|
|
template_history = fields.Nested(TemplateHistorySchema,
|
|
|
|
|
only=['id', 'name', 'template_type', 'content', 'subject', 'version'],
|
|
|
|
|
dump_only=True)
|
2016-08-09 16:53:09 +01:00
|
|
|
|
2016-08-10 10:59:22 +01:00
|
|
|
class Meta(NotificationWithTemplateSchema.Meta):
|
|
|
|
|
# mark as many fields as possible as required since this is a public api.
|
|
|
|
|
# WARNING: Does _not_ reference fields computed in handle_template_merge, such as
|
|
|
|
|
# 'body', 'subject' [for emails], and 'content_char_count'
|
|
|
|
|
fields = (
|
|
|
|
|
# db rows
|
|
|
|
|
'id', 'to', 'job_row_number', 'template_version', 'billable_units', 'notification_type', 'created_at',
|
|
|
|
|
'sent_at', 'sent_by', 'updated_at', 'status', 'reference',
|
|
|
|
|
# computed fields
|
|
|
|
|
'personalisation',
|
|
|
|
|
# relationships
|
2016-08-10 16:57:27 +01:00
|
|
|
'service', 'job', 'api_key', 'template_history'
|
2016-08-10 10:59:22 +01:00
|
|
|
)
|
|
|
|
|
|
2016-06-20 16:23:56 +01:00
|
|
|
@pre_dump
|
|
|
|
|
def handle_personalisation_property(self, in_data):
|
2016-06-23 15:35:35 +01:00
|
|
|
self.personalisation = in_data.personalisation
|
2016-06-20 16:23:56 +01:00
|
|
|
return in_data
|
|
|
|
|
|
|
|
|
|
@post_dump
|
|
|
|
|
def handle_template_merge(self, in_data):
|
2016-08-10 16:57:27 +01:00
|
|
|
in_data['template'] = in_data.pop('template_history')
|
2016-12-09 15:56:25 +00:00
|
|
|
template = get_template_instance(in_data['template'], in_data['personalisation'])
|
|
|
|
|
in_data['body'] = str(template)
|
|
|
|
|
if in_data['template']['template_type'] == models.EMAIL_TYPE:
|
|
|
|
|
in_data['subject'] = template.subject
|
2016-08-09 17:28:55 +01:00
|
|
|
in_data['content_char_count'] = None
|
|
|
|
|
else:
|
2016-12-09 15:56:25 +00:00
|
|
|
in_data['content_char_count'] = template.content_count
|
2016-08-09 17:28:55 +01:00
|
|
|
|
2016-06-23 15:35:35 +01:00
|
|
|
in_data.pop('personalisation', None)
|
2016-06-20 16:23:56 +01:00
|
|
|
in_data['template'].pop('content', None)
|
2016-06-21 16:45:13 +01:00
|
|
|
in_data['template'].pop('subject', None)
|
2016-06-20 16:23:56 +01:00
|
|
|
return in_data
|
2016-02-09 14:17:42 +00:00
|
|
|
|
|
|
|
|
|
2016-02-24 14:01:19 +00:00
|
|
|
class InvitedUserSchema(BaseSchema):
|
2016-02-29 09:49:12 +00:00
|
|
|
|
2016-02-24 14:01:19 +00:00
|
|
|
class Meta:
|
|
|
|
|
model = models.InvitedUser
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
2016-02-24 14:01:19 +00:00
|
|
|
|
|
|
|
|
@validates('email_address')
|
|
|
|
|
def validate_to(self, value):
|
2016-03-06 12:58:28 +00:00
|
|
|
try:
|
|
|
|
|
validate_email_address(value)
|
2016-03-08 17:46:00 +00:00
|
|
|
except InvalidEmailError as e:
|
2017-01-09 16:22:27 +00:00
|
|
|
raise ValidationError(str(e))
|
2016-02-24 14:01:19 +00:00
|
|
|
|
|
|
|
|
|
2016-02-26 12:00:16 +00:00
|
|
|
class PermissionSchema(BaseSchema):
|
|
|
|
|
|
2016-03-01 14:21:28 +00:00
|
|
|
# Override generated fields
|
|
|
|
|
user = field_for(models.Permission, 'user', dump_only=True)
|
|
|
|
|
service = field_for(models.Permission, 'service', dump_only=True)
|
|
|
|
|
permission = field_for(models.Permission, 'permission')
|
|
|
|
|
|
2016-02-26 12:00:16 +00:00
|
|
|
__envelope__ = {
|
|
|
|
|
'single': 'permission',
|
|
|
|
|
'many': 'permissions',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = models.Permission
|
|
|
|
|
exclude = ("created_at",)
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
2016-02-26 12:00:16 +00:00
|
|
|
|
|
|
|
|
|
2016-03-07 15:21:05 +00:00
|
|
|
class EmailDataSchema(ma.Schema):
|
2016-06-14 15:07:23 +01:00
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
strict = True
|
|
|
|
|
|
2016-07-08 10:57:20 +01:00
|
|
|
email = fields.Str(required=True)
|
2016-03-07 15:21:05 +00:00
|
|
|
|
|
|
|
|
@validates('email')
|
2016-03-08 15:47:35 +00:00
|
|
|
def validate_email(self, value):
|
|
|
|
|
try:
|
|
|
|
|
validate_email_address(value)
|
2016-03-08 17:46:00 +00:00
|
|
|
except InvalidEmailError as e:
|
2017-01-09 16:22:27 +00:00
|
|
|
raise ValidationError(str(e))
|
2016-03-07 15:21:05 +00:00
|
|
|
|
2016-03-21 12:37:34 +00:00
|
|
|
|
|
|
|
|
class NotificationsFilterSchema(ma.Schema):
|
2016-06-14 15:07:23 +01:00
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
strict = True
|
|
|
|
|
|
2016-04-08 13:34:46 +01:00
|
|
|
template_type = fields.Nested(BaseTemplateSchema, only=['template_type'], many=True)
|
|
|
|
|
status = fields.Nested(NotificationModelSchema, only=['status'], many=True)
|
2016-03-21 12:37:34 +00:00
|
|
|
page = fields.Int(required=False)
|
2016-04-19 10:52:52 +01:00
|
|
|
page_size = fields.Int(required=False)
|
2016-04-28 16:10:35 +01:00
|
|
|
limit_days = fields.Int(required=False)
|
2016-09-15 15:59:34 +01:00
|
|
|
include_jobs = fields.Boolean(required=False)
|
2016-09-23 10:35:31 +01:00
|
|
|
include_from_test_key = fields.Boolean(required=False)
|
2016-11-23 11:44:38 +00:00
|
|
|
older_than = fields.UUID(required=False)
|
2017-04-20 12:31:49 +01:00
|
|
|
format_for_csv = fields.String()
|
2017-05-08 17:20:21 +01:00
|
|
|
to = fields.String()
|
2016-03-21 12:37:34 +00:00
|
|
|
|
2016-04-04 13:13:29 +01:00
|
|
|
@pre_load
|
|
|
|
|
def handle_multidict(self, in_data):
|
|
|
|
|
if isinstance(in_data, dict) and hasattr(in_data, 'getlist'):
|
|
|
|
|
out_data = dict([(k, in_data.get(k)) for k in in_data.keys()])
|
|
|
|
|
if 'template_type' in in_data:
|
|
|
|
|
out_data['template_type'] = [{'template_type': x} for x in in_data.getlist('template_type')]
|
|
|
|
|
if 'status' in in_data:
|
|
|
|
|
out_data['status'] = [{"status": x} for x in in_data.getlist('status')]
|
|
|
|
|
|
|
|
|
|
return out_data
|
|
|
|
|
|
|
|
|
|
@post_load
|
|
|
|
|
def convert_schema_object_to_field(self, in_data):
|
|
|
|
|
if 'template_type' in in_data:
|
|
|
|
|
in_data['template_type'] = [x.template_type for x in in_data['template_type']]
|
|
|
|
|
if 'status' in in_data:
|
|
|
|
|
in_data['status'] = [x.status for x in in_data['status']]
|
|
|
|
|
return in_data
|
|
|
|
|
|
2016-04-19 10:52:52 +01:00
|
|
|
@validates('page')
|
|
|
|
|
def validate_page(self, value):
|
2016-05-05 10:45:47 +01:00
|
|
|
_validate_positive_number(value)
|
2016-04-19 10:52:52 +01:00
|
|
|
|
|
|
|
|
@validates('page_size')
|
|
|
|
|
def validate_page_size(self, value):
|
2016-05-05 10:45:47 +01:00
|
|
|
_validate_positive_number(value)
|
2016-04-19 10:52:52 +01:00
|
|
|
|
2016-04-04 13:13:29 +01:00
|
|
|
|
2016-04-04 12:21:38 +01:00
|
|
|
class TemplateStatisticsSchema(BaseSchema):
|
|
|
|
|
|
2016-08-23 12:05:47 +01:00
|
|
|
template = fields.Nested(TemplateSchema, only=["id", "name", "template_type"], dump_only=True)
|
2016-04-04 12:21:38 +01:00
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
model = models.TemplateStatistics
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
2016-04-04 12:21:38 +01:00
|
|
|
|
|
|
|
|
|
2016-04-21 16:32:20 +01:00
|
|
|
class ServiceHistorySchema(ma.Schema):
|
|
|
|
|
id = fields.UUID()
|
|
|
|
|
name = fields.String()
|
|
|
|
|
created_at = fields.DateTime()
|
|
|
|
|
updated_at = fields.DateTime()
|
|
|
|
|
active = fields.Boolean()
|
|
|
|
|
message_limit = fields.Integer()
|
|
|
|
|
restricted = fields.Boolean()
|
|
|
|
|
email_from = fields.String()
|
|
|
|
|
created_by_id = fields.UUID()
|
|
|
|
|
version = fields.Integer()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ApiKeyHistorySchema(ma.Schema):
|
|
|
|
|
id = fields.UUID()
|
|
|
|
|
name = fields.String()
|
|
|
|
|
service_id = fields.UUID()
|
|
|
|
|
expiry_date = fields.DateTime()
|
|
|
|
|
created_at = fields.DateTime()
|
|
|
|
|
updated_at = fields.DateTime()
|
|
|
|
|
created_by_id = fields.UUID()
|
|
|
|
|
|
|
|
|
|
|
2016-04-27 10:27:05 +01:00
|
|
|
class EventSchema(BaseSchema):
|
|
|
|
|
class Meta:
|
|
|
|
|
model = models.Event
|
2016-06-14 15:07:23 +01:00
|
|
|
strict = True
|
2016-04-27 10:27:05 +01:00
|
|
|
|
|
|
|
|
|
2016-08-05 15:48:06 +01:00
|
|
|
class OrganisationSchema(BaseSchema):
|
|
|
|
|
class Meta:
|
|
|
|
|
model = models.Organisation
|
|
|
|
|
strict = True
|
|
|
|
|
|
|
|
|
|
|
2016-05-20 15:16:53 +01:00
|
|
|
class DaySchema(ma.Schema):
|
2016-06-14 15:07:23 +01:00
|
|
|
|
|
|
|
|
class Meta:
|
|
|
|
|
strict = True
|
|
|
|
|
|
2016-05-20 15:16:53 +01:00
|
|
|
day = fields.Date(required=True)
|
|
|
|
|
|
|
|
|
|
@validates('day')
|
|
|
|
|
def validate_day(self, value):
|
|
|
|
|
_validate_not_in_future(value)
|
|
|
|
|
|
|
|
|
|
|
2016-05-23 15:44:56 +01:00
|
|
|
class UnarchivedTemplateSchema(BaseSchema):
|
|
|
|
|
archived = fields.Boolean(required=True)
|
|
|
|
|
|
|
|
|
|
@validates_schema
|
|
|
|
|
def validate_archived(self, data):
|
|
|
|
|
if data['archived']:
|
|
|
|
|
raise ValidationError('Template has been deleted', 'template')
|
|
|
|
|
|
|
|
|
|
|
2016-01-11 15:07:13 +00:00
|
|
|
user_schema = UserSchema()
|
2016-01-28 11:41:21 +00:00
|
|
|
user_schema_load_json = UserSchema(load_json=True)
|
2016-11-07 17:41:49 +00:00
|
|
|
user_update_schema_load_json = UserUpdateAttributeSchema(load_json=True, partial=True)
|
2017-02-07 11:27:13 +00:00
|
|
|
user_update_password_schema_load_json = UserUpdatePasswordSchema(load_json=True, partial=True)
|
2016-01-08 17:51:46 +00:00
|
|
|
service_schema = ServiceSchema()
|
2016-01-29 11:11:00 +00:00
|
|
|
service_schema_load_json = ServiceSchema(load_json=True)
|
2016-07-18 12:03:44 +01:00
|
|
|
detailed_service_schema = DetailedServiceSchema()
|
2016-01-13 11:04:13 +00:00
|
|
|
template_schema = TemplateSchema()
|
2016-01-29 11:11:00 +00:00
|
|
|
template_schema_load_json = TemplateSchema(load_json=True)
|
2016-01-19 12:07:00 +00:00
|
|
|
api_key_schema = ApiKeySchema()
|
2016-01-29 11:11:00 +00:00
|
|
|
api_key_schema_load_json = ApiKeySchema(load_json=True)
|
2016-01-15 15:48:05 +00:00
|
|
|
job_schema = JobSchema()
|
2016-01-29 11:11:00 +00:00
|
|
|
job_schema_load_json = JobSchema(load_json=True)
|
2016-02-01 10:48:33 +00:00
|
|
|
request_verify_code_schema = RequestVerifyCodeSchema()
|
2016-02-03 13:16:19 +00:00
|
|
|
sms_admin_notification_schema = SmsAdminNotificationSchema()
|
|
|
|
|
sms_template_notification_schema = SmsTemplateNotificationSchema()
|
2016-02-23 17:30:50 +00:00
|
|
|
job_sms_template_notification_schema = JobSmsTemplateNotificationSchema()
|
2016-02-03 13:16:19 +00:00
|
|
|
email_notification_schema = EmailNotificationSchema()
|
2016-02-24 09:55:05 +00:00
|
|
|
job_email_template_notification_schema = JobEmailTemplateNotificationSchema()
|
2016-07-26 12:34:39 +01:00
|
|
|
notification_schema = NotificationModelSchema()
|
|
|
|
|
notification_with_template_schema = NotificationWithTemplateSchema()
|
2016-07-26 14:33:14 +01:00
|
|
|
notification_with_personalisation_schema = NotificationWithPersonalisationSchema()
|
2016-02-24 14:01:19 +00:00
|
|
|
invited_user_schema = InvitedUserSchema()
|
2016-02-26 12:00:16 +00:00
|
|
|
permission_schema = PermissionSchema()
|
2016-03-07 15:21:05 +00:00
|
|
|
email_data_request_schema = EmailDataSchema()
|
2016-03-08 16:34:03 +00:00
|
|
|
notifications_statistics_schema = NotificationsStatisticsSchema()
|
2016-03-21 12:37:34 +00:00
|
|
|
notifications_filter_schema = NotificationsFilterSchema()
|
2016-04-04 12:21:38 +01:00
|
|
|
template_statistics_schema = TemplateStatisticsSchema()
|
2016-04-21 16:32:20 +01:00
|
|
|
service_history_schema = ServiceHistorySchema()
|
|
|
|
|
api_key_history_schema = ApiKeyHistorySchema()
|
2016-04-25 10:38:37 +01:00
|
|
|
template_history_schema = TemplateHistorySchema()
|
2016-04-27 10:27:05 +01:00
|
|
|
event_schema = EventSchema()
|
2016-08-05 15:48:06 +01:00
|
|
|
organisation_schema = OrganisationSchema()
|
2016-05-10 09:04:22 +01:00
|
|
|
provider_details_schema = ProviderDetailsSchema()
|
2017-03-02 18:10:33 +00:00
|
|
|
provider_details_history_schema = ProviderDetailsHistorySchema()
|
2016-05-20 15:16:53 +01:00
|
|
|
day_schema = DaySchema()
|
2016-05-23 15:44:56 +01:00
|
|
|
unarchived_template_schema = UnarchivedTemplateSchema()
|