diff --git a/luncho/blueprints/groups.py b/luncho/blueprints/groups.py index 44e8eea..4f3327b 100644 --- a/luncho/blueprints/groups.py +++ b/luncho/blueprints/groups.py @@ -19,38 +19,8 @@ from luncho.server import db from luncho.exceptions import LunchoException from luncho.exceptions import ElementNotFoundException from luncho.exceptions import AccountNotVerifiedException - - -class NewMaintainerDoesNotExistException(LunchoException): - """The account for the new admin does not exist. - - .. sourcecode:: http - - HTTP/1.1 404 Not found - Content-Type: test/json - - { "status": "ERROR", "message": "New admin not found" } - """ - def __init__(self): - super(NewMaintainerDoesNotExistException, self).__init__() - self.status = 404 - self.message = 'New admin not found' - - -class UserIsNotAdminException(LunchoException): - """The user is not the admin of the group. - - .. sourcecode:: http - - HTTP/1.1 403 Forbidden - Content-Type: test/json - - { "status": "ERROR", "message": "User is not admin" } - """ - def __init__(self): - super(UserIsNotAdminException, self).__init__() - self.status = 403 - self.message = 'User is not admin' +from luncho.exceptions import NewMaintainerDoesNotExistException +from luncho.exceptions import UserIsNotAdminException class UserIsNotMemberException(LunchoException): @@ -178,7 +148,7 @@ def create_group(): json = request.get_json(force=True) new_group = Group(name=json['name'], - owner=user.username) + owner=user) LOG.debug('Current user groups: {groups}'.format(groups=user.groups)) user.groups.append(new_group) diff --git a/luncho/blueprints/places.py b/luncho/blueprints/places.py index 8885d6e..5b4a55f 100644 --- a/luncho/blueprints/places.py +++ b/luncho/blueprints/places.py @@ -6,12 +6,14 @@ from flask import request from flask import jsonify from luncho.server import Place +from luncho.server import User from luncho.server import db from luncho.helpers import auth from luncho.helpers import ForceJSON from luncho.exceptions import AccountNotVerifiedException +from luncho.exceptions import ElementNotFoundException places = Blueprint('places', __name__) @@ -51,7 +53,7 @@ def create_place(): raise AccountNotVerifiedException() json = request.get_json(force=True) - new_place = Place(name=json['name'], owner=request.user.username) + new_place = Place(name=json['name'], owner=request.user) db.session.add(new_place) db.session.commit() @@ -101,3 +103,65 @@ def get_places(): return jsonify(status='OK', places=places.values()) + + +@places.route('/', methods=['PUT']) +@ForceJSON() +@auth +def update_place(placeId): + """*Authenticated request* Update the place information. The user must be + the maintainer of the place to change any information. Partial requests + are accepted and missing fields will not be changed. + + **Example request**: + + .. sourcecode:: http + + { "name": "New name", "admin": "newAdmin" } + + **Success (200)**: + + .. sourcecode:: http + + HTTP/1.1 200 OK + Content-Type: text/json + + **Request not in JSON format (400)**: + :py:class:`RequestMustBeJSONException` + + **User is not administrator of the group (403)**: + :py:class:`UserIsNotAdminException` + + **User not found (via token) (404)**: + :py:class:`UserNotFoundException` + + **The new admin does not exist (404)**: + :py:class:`NewMaintainerDoesNotExistException` + + **The place does not exist (404)**: + :py:class:`ElementNotFoundException` + + **Authorization required (412)**: + :py:class:`AuthorizationRequiredException` + """ + place = Place.query.get(placeId) + if not place: + raise ElementNotFoundException('Place') + + if not place.owner == request.user.username: + raise UserIsNotAdminException() + + name = request.as_json.get('name') + if name: + place.name = name + + admin = request.as_json.get('admin') + if admin: + new_maintainer = User.query.get(admin) + if not new_maintainer: + raise NewMaintainerDoesNotExistException() + + place.owner = new_maintainer.username + + db.session.commit() + return jsonify(status='OK') diff --git a/luncho/exceptions.py b/luncho/exceptions.py index cffe06a..d7f9ec4 100644 --- a/luncho/exceptions.py +++ b/luncho/exceptions.py @@ -138,3 +138,35 @@ class AccountNotVerifiedException(LunchoException): super(AccountNotVerifiedException, self).__init__() self.status = 412 self.message = 'Account not verified' + + +class NewMaintainerDoesNotExistException(LunchoException): + """The account for the new admin does not exist. + + .. sourcecode:: http + + HTTP/1.1 404 Not found + Content-Type: test/json + + { "status": "ERROR", "message": "New admin not found" } + """ + def __init__(self): + super(NewMaintainerDoesNotExistException, self).__init__() + self.status = 404 + self.message = 'New admin not found' + + +class UserIsNotAdminException(LunchoException): + """The user is not the admin of the group. + + .. sourcecode:: http + + HTTP/1.1 403 Forbidden + Content-Type: test/json + + { "status": "ERROR", "message": "User is not admin" } + """ + def __init__(self): + super(UserIsNotAdminException, self).__init__() + self.status = 403 + self.message = 'User is not admin' diff --git a/luncho/server.py b/luncho/server.py index 408108a..36c0ab6 100644 --- a/luncho/server.py +++ b/luncho/server.py @@ -104,7 +104,7 @@ class Group(db.Model): def __init__(self, name, owner): self.name = name - self.owner = owner + self.owner = owner.username def __repr__(self): return 'Group {id}-{name}-{owner}'.format(id=self.id, @@ -119,7 +119,7 @@ class Place(db.Model): def __init__(self, name, owner=None): self.name = name - self.owner = owner + self.owner = owner.username # ---------------------------------------------------------------------- # Blueprints diff --git a/tests/base.py b/tests/base.py index fe7369e..fa379fd 100644 --- a/tests/base.py +++ b/tests/base.py @@ -7,6 +7,8 @@ import base64 from luncho import server +from luncho.server import User + def _token_header(token=None): """Generate the headers required for using the token as an auth.""" @@ -32,9 +34,44 @@ class LunchoTests(unittest.TestCase): self.app = server.app.test_client() server.db.create_all() + return def tearDown(self): + if hasattr(self, 'user'): + server.db.session.delete(self.user) + server.db.session.commit() server.db.drop_all(bind=None) + return + + # ------------------------------------------------------------ + # Common data + # ------------------------------------------------------------ + def create_user(self, name='test', fullname='Test User', passhash='hash', + verified=True, create_token=False): + """Add a user. + + :param name: The name for the user + :param fullname: The full name of the user + :param passhash: The user passhash + + :return: The user + :rtype: :py:class:`server.User`""" + user = User(username=name, + fullname=fullname, + passhash=passhash) + user.verified = verified + server.db.session.add(user) + server.db.session.commit() + + if create_token: + user.get_token() + return user + + def default_user(self): + """Add the default user in the database; it will be stored in 'self' + for access.""" + self.user = self.create_user(create_token=True) + return # ------------------------------------------------------------ # Common assertions for lunch-o @@ -59,10 +96,12 @@ class LunchoTests(unittest.TestCase): key=key, expected=expected[key], response=response[key])) + return def assertStatusCode(self, response, status): """Check the status code of the response.""" self.assertEqual(response.status_code, status) + return def assertJsonOk(self, response, **extras): """Assert the the response is an OK. Extra fields can be expected @@ -72,6 +111,7 @@ class LunchoTests(unittest.TestCase): expected.update(extras) self.assertStatusCode(response, 200) self.assertJson(response, expected) + return def assertJsonError(self, response, status, message, **extras): """Assert that the response is an error. Extra fields returned in @@ -82,6 +122,7 @@ class LunchoTests(unittest.TestCase): self.assertStatusCode(response, status) self.assertJson(response, expected) + return # ------------------------------------------------------------ # Easy way to convert the data to JSON and do requests diff --git a/tests/group_tests.py b/tests/group_tests.py index 71db201..5dc77cc 100644 --- a/tests/group_tests.py +++ b/tests/group_tests.py @@ -106,7 +106,7 @@ class TestExistingGroups(LunchoTests): # create a group for the user self.group = Group(name='Test group', - owner=self.user.username) + owner=self.user) server.db.session.add(self.group) server.db.session.commit() self.user.get_token() @@ -231,7 +231,7 @@ class TestUsersInGroup(LunchoTests): # create a group for the user self.group = Group(name='Test group', - owner=self.user.username) + owner=self.user) server.db.session.add(self.group) self.user.groups.append(self.group) diff --git a/tests/place_tests.py b/tests/place_tests.py index 5bd8f65..be66116 100644 --- a/tests/place_tests.py +++ b/tests/place_tests.py @@ -8,6 +8,7 @@ from json import loads from luncho import server from luncho.server import User +from luncho.server import Place from base import LunchoTests @@ -17,13 +18,12 @@ class TestPlaces(LunchoTests): def setUp(self): super(TestPlaces, self).setUp() - 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() + self.default_user() + return + + def tearDown(self): + super(TestPlaces, self).tearDown() + return def test_create_place(self): """Try to create a place.""" @@ -47,5 +47,46 @@ class TestPlaces(LunchoTests): self.assertEqual(len(json['places']), 1) # just the new place +class TestExistingPlaces(LunchoTests): + """Tests for existing places.""" + def setUp(self): + super(TestExistingPlaces, self).setUp() + self.default_user() + self.place = Place(name='Place', + owner=self.user) + server.db.session.add(self.place) + server.db.session.commit() + + def test_update_name(self): + """Try to update a place.""" + request = {'name': 'New name'} + placeId = self.place.id + rv = self.put('/place/{placeId}/'.format(placeId=placeId), + request, + token=self.user.token) + self.assertJsonOk(rv) + + # check the database + place = Place.query.get(placeId) + self.assertEqual(place.name, request['name']) + + def test_update_owner(self): + """Update the owner of the group.""" + new_user = self.create_user(name='newUser', + fullname='New User', + passhash='hash', + verified=True) + placeId = self.place.id + request = {'admin': new_user.username} + rv = self.put('/place/{placeId}/'.format(placeId=placeId), + request, + token=self.user.token) + self.assertJsonOk(rv) + + # check the database + place = Place.query.get(placeId) + self.assertEqual(place.owner, 'newUser') + + if __name__ == '__main__': unittest.main()