diff --git a/apiary.apib b/apiary.apib index 9408f22..7efa5b5 100644 --- a/apiary.apib +++ b/apiary.apib @@ -240,7 +240,7 @@ The user will become the maintainer of the group once it is created. + Request (application/json) - { "token": "userToken", "name": "Group name" } + { "name": "Group name" } + Response 200 (application/json) diff --git a/luncho/blueprints/groups.py b/luncho/blueprints/groups.py index ac37b18..4d45da5 100644 --- a/luncho/blueprints/groups.py +++ b/luncho/blueprints/groups.py @@ -3,28 +3,33 @@ """Group management.""" +import logging + from flask import Blueprint -# from flask import request +from flask import request from flask import jsonify -# from luncho.helpers import ForceJSON +from sqlalchemy.exc import IntegrityError + +from luncho.helpers import ForceJSON from luncho.helpers import JSONError +from luncho.helpers import user_or_error from luncho.server import User -# from luncho.server import Group +from luncho.server import Group +from luncho.server import db groups = Blueprint('groups', __name__) +LOG = logging.getLogger('luncho.blueprints.groups') + @groups.route('/', methods=['GET']) def user_groups(token): """Return a list of the groups the user belongs or it's the owner.""" - user = User.query.filter_by(token=token).first() - if not user: - return JSONError(404, 'User not found (via token)') - - if not user.valid_token(token): - return JSONError(400, 'Invalid token') + (user, error) = user_or_error(token) + if error: + return error groups = {} for group in user.groups: @@ -34,3 +39,31 @@ def user_groups(token): return jsonify(status='OK', groups=groups.values()) + +@groups.route('/', methods=['PUT']) +@ForceJSON(required=['name']) +def create_group(token): + """Create a new group belonging to the user.""" + (user, error) = user_or_error(token) + if error: + return error + + LOG.debug('User status: {verified}'.format(verified=user.verified)) + + if not user.verified: + return JSONError(412, 'Account not verified') + + json = request.get_json(force=True) + try: + new_group = Group(name=json['name'], + owner=user.username) + + user.groups.append(new_group) + + db.session.add(new_group) + db.session.commit() + except IntegrityError: + return JSONError(500, 'Unknown error') + + return jsonify(status='OK', + id=new_group.id) diff --git a/luncho/blueprints/users.py b/luncho/blueprints/users.py index f505e93..1d3792b 100644 --- a/luncho/blueprints/users.py +++ b/luncho/blueprints/users.py @@ -33,7 +33,7 @@ def create_user(): new_user = User(username=json['username'], fullname=json['full_name'], passhash=json['password'], - validated=False) + verified=False) db.session.add(new_user) db.session.commit() diff --git a/luncho/helpers.py b/luncho/helpers.py index f9370ff..679da66 100644 --- a/luncho/helpers.py +++ b/luncho/helpers.py @@ -8,6 +8,8 @@ from functools import wraps from flask import request from flask import jsonify +from luncho.server import User + class ForceJSON(object): def __init__(self, required=None): @@ -58,3 +60,23 @@ def JSONError(status, message, **kwargs): **kwargs) resp.status_code = status return resp + + +def user_or_error(token): + """Returns a tuple with the user that owns the token and the error. If the + token is valid, user will have the user object and error will be None; if + there is something wrong with the token, the user will be None and the + error will have a Response created with :py:func:`JSONError`. + + :param token: The token received + :type token: str + + :return: Tuple with the user and the error.""" + user = User.query.filter_by(token=token).first() + if not user: + return (None, JSONError(404, 'User not found (via token)')) + + if not user.valid_token(token): + return (None, JSONError(400, 'Invalid token')) + + return (user, None) diff --git a/luncho/server.py b/luncho/server.py index 820dc62..ad08e33 100644 --- a/luncho/server.py +++ b/luncho/server.py @@ -54,12 +54,12 @@ class User(db.Model): backref=db.backref('groups', lazy='dynamic')) def __init__(self, username, fullname, passhash, token=None, - issued_date=None, validated=False): + issued_date=None, verified=False): self.username = username self.fullname = fullname self.passhash = passhash self.token = token - self.validated = validated + self.verified = verified self.created_at = datetime.datetime.now() def get_token(self): @@ -85,6 +85,10 @@ class Group(db.Model): name = db.Column(db.String, nullable=False) owner = db.Column(db.String, db.ForeignKey('user.username')) + def __init__(self, name, owner): + self.name = name + self.owner = owner + # ---------------------------------------------------------------------- # Blueprints # ---------------------------------------------------------------------- diff --git a/tests/group_tests.py b/tests/group_tests.py index ffe718d..094b9f3 100644 --- a/tests/group_tests.py +++ b/tests/group_tests.py @@ -19,6 +19,7 @@ class TestGroups(LunchoTests): self.user = User(username='test', fullname='Test User', passhash='hash') + self.user.verified = True server.db.session.add(self.user) server.db.session.commit() self.user.get_token() @@ -32,6 +33,30 @@ class TestGroups(LunchoTests): self.assertStatusCode(rv, 200) self.assertJson(expected, rv.data) + def test_create_group(self): + """Test creating a group.""" + request = {'name': 'Test group'} + rv = self.put('/group/{token}/'.format(token=self.user.token), + request) + + expected = {'status': 'OK', 'id': 1} # always 1 'cause the database + # is erased on every test + self.assertStatusCode(rv, 200) + self.assertJson(expected, rv.data) + + def test_create_group_unverified_account(self): + """Try creating a group with an account that's not verified yet.""" + self.user.verified = False + server.db.session.commit() + + request = {'name': 'Test group'} + rv = self.put('/group/{token}/'.format(token=self.user.token), + request) + + expected = {'status': 'ERROR', + 'error': 'Account not verified'} + self.assertStatusCode(rv, 412) + self.assertJson(expected, rv.data) if __name__ == '__main__': unittest.main()