Working tests and provider stats table.

Fix for tests and import error.

Added tests and updated for code review comments.
This commit is contained in:
Nicholas Staples
2016-04-25 10:38:37 +01:00
parent ff61223d97
commit b56e324a4c
14 changed files with 365 additions and 90 deletions

View File

@@ -1,16 +1,25 @@
import uuid
from app import db
from app.models import (Template, Service)
from sqlalchemy import asc
from app.dao.dao_utils import (
transactional,
version_class
)
@transactional
@version_class(Template)
def dao_create_template(template):
template.id = uuid.uuid4() # must be set now so version history model can use same id
db.session.add(template)
db.session.commit()
@transactional
@version_class(Template)
def dao_update_template(template):
db.session.add(template)
db.session.commit()
def dao_get_template_by_id_and_service_id(template_id, service_id):

View File

@@ -59,4 +59,11 @@ def register_errors(blueprint):
@blueprint.app_errorhandler(SQLAlchemyError)
def db_error(e):
current_app.logger.exception(e)
return jsonify(result='error', message=str(e)), 500
if e.orig.pgerror and \
('duplicate key value violates unique constraint "services_name_key"' in e.orig.pgerror or
'duplicate key value violates unique constraint "services_email_from_key"' in e.orig.pgerror):
return jsonify(
result='error',
message={'name': ["Duplicate service name '{}'".format(e.params.get('name', ''))]}
), 400
return jsonify(result='error', message="Internal server error"), 500

View File

@@ -180,7 +180,6 @@ def create_history(obj):
obj_state = attributes.instance_state(obj)
data = {}
for prop in obj_mapper.iterate_properties:
# expired object attributes and also deferred cols might not

View File

@@ -152,7 +152,7 @@ TEMPLATE_TYPE_LETTER = 'letter'
TEMPLATE_TYPES = [TEMPLATE_TYPE_SMS, TEMPLATE_TYPE_EMAIL, TEMPLATE_TYPE_LETTER]
class Template(db.Model):
class Template(db.Model, Versioned):
__tablename__ = 'templates'
id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
@@ -174,6 +174,8 @@ class Template(db.Model):
service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, unique=False, nullable=False)
service = db.relationship('Service', backref=db.backref('templates', lazy='dynamic'))
subject = db.Column(db.Text, index=False, unique=True, nullable=True)
created_by_id = db.Column(UUID(as_uuid=True), db.ForeignKey('users.id'), index=True, nullable=False)
created_by = db.relationship('User')
MMG_PROVIDER = "mmg"

View File

@@ -7,8 +7,9 @@ from marshmallow import (
validates_schema,
pre_load
)
from sqlalchemy.dialects.postgresql import UUID
from marshmallow_sqlalchemy import field_for
from marshmallow_sqlalchemy.convert import ModelConverter
from notifications_utils.recipients import (
validate_email_address,
@@ -35,17 +36,6 @@ class BaseSchema(ma.ModelSchema):
self.load_json = load_json
super(BaseSchema, self).__init__(*args, **kwargs)
__envelope__ = {
'single': None,
'many': None
}
def get_envelope_key(self, many):
"""Helper to get the envelope key."""
key = self.__envelope__['many'] if many else self.__envelope__['single']
assert key is not None, "Envelope key undefined"
return key
@post_load
def make_instance(self, data):
"""Deserialize data to an instance of the model. Update an existing row
@@ -58,6 +48,25 @@ class BaseSchema(ma.ModelSchema):
return super(BaseSchema, self).make_instance(data)
class CreatedBySchema(ma.Schema):
created_by = fields.Str(required=True, load_only=True)
@validates_schema
def validates_created_by(self, data):
try:
if not isinstance(data.get('created_by'), models.User):
created_by = models.User.query.filter_by(id=data.get('created_by')).one()
except:
raise ValidationError('Invalid template created_by: {}'.format(data))
@post_load
def format_created_by(self, item):
if not isinstance(item.get('created_by'), models.User):
item['created_by'] = models.User.query.filter_by(id=item.get('created_by')).one()
return item
class UserSchema(BaseSchema):
permissions = fields.Method("user_permissions", dump_only=True)
@@ -80,7 +89,7 @@ class UserSchema(BaseSchema):
"_password", "verify_codes")
class ServiceSchema(BaseSchema):
class ServiceSchema(BaseSchema, CreatedBySchema):
class Meta:
model = models.Service
exclude = ("updated_at", "created_at", "api_keys", "templates", "jobs", 'old_id')
@@ -92,12 +101,13 @@ class NotificationModelSchema(BaseSchema):
class BaseTemplateSchema(BaseSchema):
class Meta:
model = models.Template
exclude = ("updated_at", "created_at", "service_id", "jobs")
class TemplateSchema(BaseTemplateSchema):
class TemplateSchema(BaseTemplateSchema, CreatedBySchema):
@validates_schema
def validate_type(self, data):
@@ -113,7 +123,7 @@ class NotificationsStatisticsSchema(BaseSchema):
model = models.NotificationStatistics
class ApiKeySchema(BaseSchema):
class ApiKeySchema(BaseSchema, CreatedBySchema):
class Meta:
model = models.ApiKey
exclude = ("service", "secret")
@@ -303,6 +313,18 @@ class ApiKeyHistorySchema(ma.Schema):
created_by_id = fields.UUID()
class TemplateHistorySchema(ma.Schema):
id = fields.UUID()
name = fields.String()
template_type = fields.String()
created_at = fields.DateTime()
updated_at = fields.DateTime()
content = fields.String()
service_id = fields.UUID()
subject = fields.String()
created_by_id = fields.UUID()
user_schema = UserSchema()
user_schema_load_json = UserSchema(load_json=True)
service_schema = ServiceSchema()
@@ -329,3 +351,4 @@ notifications_filter_schema = NotificationsFilterSchema()
template_statistics_schema = TemplateStatisticsSchema()
service_history_schema = ServiceHistorySchema()
api_key_history_schema = ApiKeyHistorySchema()
template_history_schema = TemplateHistorySchema()

View File

@@ -185,9 +185,13 @@ def _process_permissions(user, service, permission_groups):
# goes into how we want to fetch and view various items in history
# tables. This is so product owner can pass stories as done
@service.route('/<uuid:service_id>/history', methods=['GET'])
def get_service_and_api_key_history(service_id):
from app.models import Service, ApiKey
from app.schemas import service_history_schema, api_key_history_schema
def get_service_history(service_id):
from app.models import (Service, ApiKey, Template)
from app.schemas import (
service_history_schema,
api_key_history_schema,
template_history_schema
)
service_history = Service.get_history_model().query.filter_by(id=service_id).all()
service_data, errors = service_history_schema.dump(service_history, many=True)
@@ -200,6 +204,12 @@ def get_service_and_api_key_history(service_id):
if errors:
return jsonify(result="error", message=errors), 400
data = {'service_history': service_data, 'api_key_history': api_keys_data}
template_history = Template.get_history_model().query.filter_by(service_id=service_id).all()
template_data, errors = template_history_schema.dump(template_history, many=True)
data = {
'service_history': service_data,
'api_key_history': api_keys_data,
'template_history': template_data}
return jsonify(data=data)

View File

@@ -28,7 +28,6 @@ register_errors(template)
@template.route('', methods=['POST'])
def create_template(service_id):
fetched_service = dao_fetch_service_by_id(service_id=service_id)
new_template, errors = template_schema.load(request.get_json())
if errors:
return jsonify(result="error", message=errors), 400