diff --git a/apiary.apib b/apiary.apib index a0e7676..9408f22 100644 --- a/apiary.apib +++ b/apiary.apib @@ -227,8 +227,8 @@ Return groups in the system. Only groups in which the user belongs will be retur + Response 200 (application/json) - { "status": "OK", "groups": [{"id": 1, "name": "Group 1"}, - {"id": 2, "name": "Group 2"}] } + { "status": "OK", "groups": [{"id": 1, "name": "Group 1", "admin": true}, + {"id": 2, "name": "Group 2", "admin": false}] } + Response 400 (application/json) diff --git a/luncho/blueprints/groups.py b/luncho/blueprints/groups.py new file mode 100644 index 0000000..ac37b18 --- /dev/null +++ b/luncho/blueprints/groups.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +"""Group management.""" + +from flask import Blueprint +# from flask import request +from flask import jsonify + +# from luncho.helpers import ForceJSON +from luncho.helpers import JSONError + +from luncho.server import User +# from luncho.server import Group + +groups = Blueprint('groups', __name__) + + +@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') + + groups = {} + for group in user.groups: + groups[group.id] = {'id': group.id, + 'name': group.name, + 'admin': group.owner.username == user.username} + + return jsonify(status='OK', + groups=groups.values()) diff --git a/luncho/blueprints/users.py b/luncho/blueprints/users.py index 79035ce..f505e93 100644 --- a/luncho/blueprints/users.py +++ b/luncho/blueprints/users.py @@ -37,11 +37,12 @@ def create_user(): db.session.add(new_user) db.session.commit() - - return jsonify(status='OK') except IntegrityError: return JSONError(409, 'Username already exists') + return jsonify(status='OK') + + @users.route('/', methods=['POST']) @ForceJSON() def update_user(token): diff --git a/luncho/server.py b/luncho/server.py index bf991fe..820dc62 100644 --- a/luncho/server.py +++ b/luncho/server.py @@ -32,6 +32,14 @@ app.config.from_envvar('LUCNHO_CONFIG', True) from flask.ext.sqlalchemy import SQLAlchemy db = SQLAlchemy(app) +userGroups = db.Table('user_groups', + db.Column('username', + db.String, + db.ForeignKey('user.username')), + db.Column('group_id', + db.Integer, + db.ForeignKey('group.id'))) + class User(db.Model): username = db.Column(db.String, primary_key=True) @@ -41,6 +49,9 @@ class User(db.Model): issued_date = db.Column(db.Date) validated = db.Column(db.Boolean, default=False) created_at = db.Column(db.DateTime, nullable=False) + groups = db.relationship('Group', + secondary=userGroups, + backref=db.backref('groups', lazy='dynamic')) def __init__(self, username, fullname, passhash, token=None, issued_date=None, validated=False): @@ -69,16 +80,23 @@ class User(db.Model): return hmac.new(self.created_at.isoformat(), phrase).hexdigest() +class Group(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String, nullable=False) + owner = db.Column(db.String, db.ForeignKey('user.username')) + # ---------------------------------------------------------------------- # Blueprints # ---------------------------------------------------------------------- from blueprints.index import index from blueprints.users import users from blueprints.token import token +from blueprints.groups import groups app.register_blueprint(index, url_prefix='/') app.register_blueprint(token, url_prefix='/token/') app.register_blueprint(users, url_prefix='/user/') +app.register_blueprint(groups, url_prefix='/group/') # ---------------------------------------------------------------------- diff --git a/tests/base.py b/tests/base.py index f5b6f31..2879a85 100644 --- a/tests/base.py +++ b/tests/base.py @@ -39,7 +39,7 @@ class LunchoTests(unittest.TestCase): key=key)) if response[key] != expected[key]: - self.fail('Key {key} differs: Expected "{expected}", ' + self.fail('Key "{key}" differs: Expected "{expected}", ' 'response "{response}"'.format( key=key, expected=expected[key], @@ -68,3 +68,7 @@ class LunchoTests(unittest.TestCase): def delete(self, url): """Send a DELETE request to the URL. There is no data to be send.""" return self.app.delete(url) + + def get(self, url): + """Send a GET request to the URL. There is no data to be send.""" + return self.app.get(url) diff --git a/tests/group_tests.py b/tests/group_tests.py new file mode 100644 index 0000000..ffe718d --- /dev/null +++ b/tests/group_tests.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# -*- encoding: utf-8 -*- + +import unittest + +from luncho import server + +from luncho.server import User + +from base import LunchoTests + + +class TestGroups(LunchoTests): + """Test groups requests.""" + + def setUp(self): + super(TestGroups, self).setUp() + # create a user to have a token + self.user = User(username='test', + fullname='Test User', + passhash='hash') + server.db.session.add(self.user) + server.db.session.commit() + self.user.get_token() + return + + def test_empty_list(self): + """Get an empty list from a user without groups.""" + rv = self.get('/group/{token}/'.format(token=self.user.token)) + expected = {'status': 'OK', + 'groups': []} + self.assertStatusCode(rv, 200) + self.assertJson(expected, rv.data) + + +if __name__ == '__main__': + unittest.main()