Service and User API added, working with tests. Still need to polish the edges and add more tests.

This commit is contained in:
Nicholas Staples
2016-01-11 15:07:13 +00:00
parent 0bc4d02713
commit 49e98c21e7
14 changed files with 282 additions and 94 deletions

View File

@@ -3,12 +3,14 @@ import os
from flask._compat import string_types
from flask import Flask, _request_ctx_stack
from flask.ext.sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from werkzeug.local import LocalProxy
from config import configs
from utils import logging
db = SQLAlchemy()
ma = Marshmallow()
api_user = LocalProxy(lambda: _request_ctx_stack.top.api_user)
@@ -20,6 +22,7 @@ def create_app(config_name):
application.config.from_object(configs[config_name])
db.init_app(application)
ma.init_app(application)
init_app(application)
logging.init_app(application)

View File

@@ -1,31 +1,32 @@
import json
from datetime import datetime
from sqlalchemy.orm import load_only
from sqlalchemy.exc import SQLAlchemyError
from app import db
from app.models import Service
def create_service(service_name,
user,
limit=1000,
active=False,
restricted=True):
service = Service(name=service_name,
created_at=datetime.now(),
limit=limit,
active=active,
restricted=restricted)
# Should I use SQLAlchemyError?
class DAOException(SQLAlchemyError):
pass
def create_model_service(service):
users_list = getattr(service, 'users', [])
if not users_list:
error_msg = {'users': 'Missing data for required attribute'}
raise DAOException(json.dumps(error_msg))
db.session.add(service)
service.users.append(user)
db.session.commit()
return service.id
def get_services(service_id=None, user_id=None):
def get_model_services(service_id=None, user_id=None):
# TODO need better mapping from function params to sql query.
if user_id and service_id:
return Service.query.filter(Service.users.any(id=user_id), id=service_id).one()
return Service.query.filter(
Service.users.any(id=user_id), id=service_id).one()
elif service_id:
return Service.query.filter_by(id=service_id).one()
elif user_id:

View File

@@ -6,15 +6,12 @@ from app import db
from app.models import User
def create_user(email_address):
user = User(email_address=email_address,
created_at=datetime.now())
db.session.add(user)
def create_model_user(usr):
db.session.add(usr)
db.session.commit()
return user.id
def get_users(user_id=None):
def get_model_users(user_id=None):
if user_id:
return User.query.filter_by(id=user_id).one()
return User.query.filter_by().all()

View File

@@ -1,4 +1,5 @@
from . import db
import datetime
def filter_null_value_fields(obj):
@@ -12,22 +13,18 @@ class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
email_address = db.Column(db.String(255), nullable=False, index=True, unique=True)
created_at = db.Column(db.DateTime, index=False, unique=False, nullable=False)
updated_at = db.Column(db.DateTime, index=False, unique=False, nullable=True)
# def serialize(self):
# serialized = {
# 'id': self.id,
# 'name': self.name,
# 'emailAddress': self.email_address,
# 'locked': self.failed_login_count > current_app.config['MAX_FAILED_LOGIN_COUNT'],
# 'createdAt': self.created_at.strftime(DATETIME_FORMAT),
# 'updatedAt': self.updated_at.strftime(DATETIME_FORMAT),
# 'role': self.role,
# 'passwordChangedAt': self.password_changed_at.strftime(DATETIME_FORMAT),
# 'failedLoginCount': self.failed_login_count
# }
# return filter_null_value_fields(serialized)
created_at = db.Column(
db.DateTime,
index=False,
unique=False,
nullable=False,
default=datetime.datetime.now)
updated_at = db.Column(
db.DateTime,
index=False,
unique=False,
nullable=True,
onupdate=datetime.datetime.now)
user_to_service = db.Table(
@@ -43,21 +40,22 @@ class Service(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False)
created_at = db.Column(db.DateTime, index=False, unique=False, nullable=False)
created_at = db.Column(
db.DateTime,
index=False,
unique=False,
nullable=False,
default=datetime.datetime.now)
updated_at = db.Column(
db.DateTime,
index=False,
unique=False,
nullable=True,
onupdate=datetime.datetime.now)
active = db.Column(db.Boolean, index=False, unique=False, nullable=False)
limit = db.Column(db.BigInteger, index=False, unique=False, nullable=False)
users = db.relationship('User', secondary=user_to_service, backref=db.backref('user_to_service', lazy='dynamic'))
users = db.relationship(
'User',
secondary=user_to_service,
backref=db.backref('user_to_service', lazy='dynamic'))
restricted = db.Column(db.Boolean, index=False, unique=False, nullable=False)
# def serialize(self):
# serialized = {
# 'id': self.id,
# 'name': self.name,
# 'createdAt': self.created_at.strftime(DATETIME_FORMAT),
# 'active': self.active,
# 'restricted': self.restricted,
# 'limit': self.limit,
# 'user': self.users.serialize()
# }
# return filter_null_value_fields(serialized)

View File

@@ -1,20 +1,43 @@
from marshmallow_sqlalchemy import ModelSchema
from . import ma
from . import models
from marshmallow import post_load
# TODO I think marshmallow provides a better integration and error handling.
# Would be better to replace functionality in dao with the marshmallow supported
# functionality.
# http://marshmallow.readthedocs.org/en/latest/api_reference.html
class UserSchema(ModelSchema):
class UserSchema(ma.ModelSchema):
class Meta:
model = models.User
exclude = ("updated_at", "created_at", "user_to_service")
def make_object(self, data):
# TODO possibly override to handle instance creation
return super(UserSchema, self).make_object(data)
# def dump(self, obj, many=None, update_fields=True, **kwargs):
# retval = super(UserSchema, self).dump(
# obj, many=many, update_fields=update_fields, **kwargs)
# if not many and 'email_address' not in retval.data:
# retval.data['email_address'] = obj.email_address
# return retval
# TODO process users list, to return a list of user.id
# Should that list be restricted??
class ServiceSchema(ModelSchema):
# Should that list be restricted by the auth parsed??
class ServiceSchema(ma.ModelSchema):
class Meta:
model = models.Service
exclude = ("updated_at", "created_at")
def make_object(self, data):
# TODO possibly override to handle instance creation
return super(ServiceSchema, self).make_object(data)
user_schema = ServiceSchema()
user_schema = UserSchema()
users_schema = UserSchema(many=True)
service_schema = ServiceSchema()
services_schema = ServiceSchema(many=True)

View File

@@ -1,8 +1,8 @@
from flask import jsonify
from flask import (jsonify, request)
from sqlalchemy.exc import DataError
from sqlalchemy.orm.exc import NoResultFound
from app.dao.services_dao import (create_service, get_services)
from app.dao.users_dao import (get_users)
from app.dao.services_dao import (create_model_service, get_model_services)
from app.dao.users_dao import get_model_users
from .. import service
from app.schemas import (services_schema, service_schema)
@@ -10,16 +10,19 @@ from app.schemas import (services_schema, service_schema)
# TODO auth to be added.
@service.route('/', methods=['POST'])
def create_service():
# Be lenient with args passed in
parsed_data = service_schema(request.args)
return jsonify(result="created"), 201
# TODO what exceptions get passed from schema parsing?
service = service_schema.load(request.get_json()).data
print(service_schema.dump(service).data)
# Some magic here, it automatically creates the service object.
# Cool but need to understand how this works.
return jsonify(data=service_schema.dump(service).data), 201
# TODO auth to be added
@service.route('/<int:service_id>', methods=['PUT'])
def update_service(service_id):
service = get_services(service_id=service_id)
return jsonify(data=service_schema.dump(service))
return jsonify(data=service_schema.dump(service).data)
# TODO auth to be added.
@@ -27,7 +30,7 @@ def update_service(service_id):
@service.route('/', methods=['GET'])
def get_service(service_id=None):
try:
services = get_services(service_id=service_id)
services = get_model_services(service_id=service_id)
except DataError:
return jsonify(result="error", message="Invalid service id"), 400
except NoResultFound:

View File

@@ -1,27 +1,58 @@
from flask import jsonify
from flask import (jsonify, request)
from sqlalchemy.exc import DataError
from sqlalchemy.orm.exc import NoResultFound
from app.dao.services_dao import get_services
from app.dao.users_dao import get_users
from app.dao.services_dao import get_model_services
from app.dao.users_dao import (get_model_users, create_model_user)
from app.schemas import (
user_schema, users_schema, service_schema, services_schema)
from .. import user
# TODO auth to be added
@user.route('/', methods=['POST'])
def create_user():
user = user_schema.load(request.get_json()).data
create_model_user(user)
return jsonify(data=user_schema.dump(user).data), 201
# TODO auth to be added
@user.route('/<int:user_id>', methods=['PUT'])
def update_user(user_id):
user = get_model_users(user_id=user_id)
return jsonify(data=user_schema.dump(user).data)
# TODO auth to be added.
@user.route('/<int:user_id>', methods=['GET'])
@user.route('/', methods=['GET'])
def get_user(user_id=None):
try:
users = get_model_users(user_id=user_id)
except DataError:
return jsonify(result="error", message="Invalid user id"), 400
except NoResultFound:
return jsonify(result="error", message="User doesn't exist"), 404
result = users_schema.dump(users) if isinstance(users, list) else user_schema.dump(users)
return jsonify(data=result.data)
# TODO auth to be added
@user.route('/<int:user_id>/service', methods=['GET'])
@user.route('/<int:user_id>/service/<int:service_id>', methods=['GET'])
def get_service_by_user_id(user_id, service_id=None):
try:
user = get_users(user_id=user_id)
user = get_model_users(user_id=user_id)
except DataError:
return jsonify(result="error", message="Invalid user id"), 400
except NoResultFound:
return jsonify(result="error", message="User doesn't exist"), 400
try:
services = get_services(user_id=user.id, service_id=service_id)
services = get_model_services(user_id=user.id, service_id=service_id)
except DataError:
return jsonify(result="error", message="Invalid service id"), 400
except NoResultFound:
return jsonify(result="error", message="Service doesn't exist"), 404
return jsonify(data=services)
result = services_schema.dump(services) if isinstance(services, list) else service_schema.dump(services)
return jsonify(data=result.data)