From 05dac7f4fbaf69275aed72359dfffb10fea90199 Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sun, 12 Feb 2017 11:47:28 +0100 Subject: [PATCH] improve REST path --- docs/DEVELOPMENT.md | 7 +++-- ibt2.py | 70 +++++++++++++++++++++++++++++++-------------- monco.py | 24 ++++++++++++++++ src/App.vue | 3 +- tests/ibt2_tests.py | 6 ++-- 5 files changed, 82 insertions(+), 28 deletions(-) diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 9cd21f7..eb0437b 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -21,10 +21,13 @@ Web server - /attendees POST - write a new entry - /attendees/:id GET - a single entry - /attendees/:id PUT - update an entry +- /attendees/:id DELETE - delete an entry - /days GET - all entries, grouped by day and by group -- /days PUT - write or update information about the day -- /groups PUT - write or update information about a group - /days/:day GET - a single day entries, grouped by group (yyyy-mm-dd format) +- /days/:day/info PUT - write or update information about a day +- /days/:day/groups/:group PUT - used to rename a group (with the newName key) +- /days/:day/groups/:group DELETE - delete a group +- /days/:day/groups/:group/info PUT - write or update information about a group (the :group key is its name) - /users GET - list of all users - /users POST - create a new user - /users/:id GET - a single user diff --git a/ibt2.py b/ibt2.py index e56a9e3..2c1a07f 100755 --- a/ibt2.py +++ b/ibt2.py @@ -261,8 +261,7 @@ class AttendeesHandler(BaseHandler): @gen.coroutine def delete(self, id_=None, **kwargs): if id_ is None: - self.write({'success': False}) - return + return self.build_error(status=404, message='unable to access the resource') doc = self.db.getOne(self.collection, {'_id': id_}) or {} if not doc: return self.build_error(status=404, message='unable to access the resource') @@ -274,7 +273,6 @@ class AttendeesHandler(BaseHandler): class DaysHandler(BaseHandler): """Handle requests for Days.""" - def _summarize(self, days): res = [] for day in days: @@ -342,12 +340,15 @@ class DaysHandler(BaseHandler): else: self.write(base) + +class DaysInfoHandler(BaseHandler): + """Handle requests for Days info.""" @gen.coroutine - def put(self, **kwargs): - data = self.clean_body - day = (data.get('day') or '').strip() + def put(self, day='', **kwargs): + day = day.strip() if not day: return self.build_error(status=404, message='unable to access the resource') + data = self.clean_body data['day'] = day self.add_access_info(data) merged, doc = self.db.update('days', {'day': day}, data) @@ -357,23 +358,26 @@ class DaysHandler(BaseHandler): class GroupsHandler(BaseHandler): """Handle requests for Groups.""" @gen.coroutine - def put(self, **kwargs): - data = self.clean_body - day = (data.get('day') or '').strip() - group = (data.get('group') or '').strip() + def put(self, day='', group='', **kwargs): + day = day.strip() + group = group.strip() if not (day and group): return self.build_error(status=404, message='unable to access the resource') - data['day'] = day - data['group'] = group - self.add_access_info(data) - merged, doc = self.db.update('groups', {'day': day, 'group': group}, data) - self.write(doc) + data = self.clean_body + newName = (data.get('newName') or '').strip() + if newName: + query = {'day': day, 'group': group} + data = {'group': newName} + self.db.updateMany('attendees', query, data) + self.db.updateMany('groups', query, data) + self.write({'success': True}) + else: + self.write({'success': False}) @gen.coroutine - def delete(self, **kwargs): - data = self.clean_arguments - day = (data.get('day') or '').strip() - group = (data.get('group') or '').strip() + def delete(self, day='', group='', **kwargs): + day = day.strip() + group = group.strip() if not (day and group): return self.build_error(status=404, message='unable to access the resource') if not self.current_user_info.get('isAdmin'): @@ -385,6 +389,22 @@ class GroupsHandler(BaseHandler): self.write({'success': True, 'deleted entries': howMany.get('n')}) +class GroupsInfoHandler(BaseHandler): + """Handle requests for Groups Info.""" + @gen.coroutine + def put(self, day='', group='', **kwargs): + day = day.strip() + group = group.strip() + if not (day and group): + return self.build_error(status=404, message='unable to access the resource') + data = self.clean_body + data['day'] = day + data['group'] = group + self.add_access_info(data) + merged, doc = self.db.update('groups', {'day': day, 'group': group}, data) + self.write(doc) + + class UsersHandler(BaseHandler): """Handle requests for Users.""" document = 'user' @@ -574,17 +594,23 @@ def run(): {'setting': 'server_cookie_secret', 'cookie_secret': cookie_secret}) _days_path = r"/days/?(?P[\d_-]+)?" - _groups_path = r"/groups/?" + _days_info_path = r"/days/(?P[\d_-]+)/info" + _groups_path = r"/days/(?P[\d_-]+)/groups/(?P.+?)" + _groups_info_path = r"/days/(?P[\d_-]+)/groups/(?P.+?)/info" _attendees_path = r"/attendees/?(?P[\w\d_-]+)?" _current_user_path = r"/users/current/?" _users_path = r"/users/?(?P[\w\d_-]+)?/?(?P[\w\d_-]+)?/?(?P[\w\d_-]+)?" application = tornado.web.Application([ (_attendees_path, AttendeesHandler, init_params), (r'/v%s%s' % (API_VERSION, _attendees_path), AttendeesHandler, init_params), - (_days_path, DaysHandler, init_params), - (r'/v%s%s' % (API_VERSION, _groups_path), GroupsHandler, init_params), + (_groups_info_path, GroupsInfoHandler, init_params), + (r'/v%s%s' % (API_VERSION, _groups_info_path), GroupsInfoHandler, init_params), (_groups_path, GroupsHandler, init_params), + (r'/v%s%s' % (API_VERSION, _groups_path), GroupsHandler, init_params), + (_days_path, DaysHandler, init_params), (r'/v%s%s' % (API_VERSION, _days_path), DaysHandler, init_params), + (_days_info_path, DaysInfoHandler, init_params), + (r'/v%s%s' % (API_VERSION, _days_info_path), DaysInfoHandler, init_params), (_current_user_path, CurrentUserHandler, init_params), (r'/v%s%s' % (API_VERSION, _current_user_path), CurrentUserHandler, init_params), (_users_path, UsersHandler, init_params), diff --git a/monco.py b/monco.py index f2ce6db..d3d3b0b 100644 --- a/monco.py +++ b/monco.py @@ -262,6 +262,30 @@ class Monco(object): lastErrorObject = res.get('lastErrorObject') or {} return lastErrorObject.get('updatedExisting', False), res.get('value') or {} + def updateMany(self, collection, query, data): + """Update multiple existing documents. + + query can be an ID or a dict representing a query. + + :param collection: update documents in this collection + :type collection: str + :param query: a query or a list of attributes in the data that must match + :type query: str or :class:`~bson.objectid.ObjectId` or iterable + :param data: the updated information to store + :type data: dict + + :returns: a dict with the success state and number of updated items + :rtype: dict + """ + db = self.connect() + data = convert(data or {}) + query = convert(query) + if not isinstance(query, dict): + query = {'_id': query} + if '_id' in data: + del data['_id'] + return db[collection].update(query, {'$set': data}, multi=True) + def delete(self, collection, _id_or_query=None, force=False): """Remove one or more documents from a collection. diff --git a/src/App.vue b/src/App.vue index a1a88c7..692d469 100644 --- a/src/App.vue +++ b/src/App.vue @@ -86,6 +86,7 @@ export default { beforeCreate: function() { this.daysUrl = this.$resource('days{/day}'); + this.daysInfoUrl = this.$resource('days{/day}/info'); }, mounted: function() { @@ -184,7 +185,7 @@ export default { return; } var data = {day: this.day.day, notes: this.dayNotes}; - this.daysUrl.update(data).then((response) => { + this.daysInfoUrl.update({day: this.day.day}, data).then((response) => { return response.json(); }, (response) => { this.$refs.dialogObj.show({text: 'unable to edit day notes'}); diff --git a/tests/ibt2_tests.py b/tests/ibt2_tests.py index 581ea4e..2c0ec30 100755 --- a/tests/ibt2_tests.py +++ b/tests/ibt2_tests.py @@ -187,7 +187,7 @@ class Ibt2Tests(unittest.TestCase): def test_put_day(self): day = {'day': '2017-01-16', 'notes': 'A day note'} self.add_attendee({'day': '2017-01-16', 'name': 'A new name', 'group': 'group C'}) - r = requests.put(BASE_URL + 'days', json=day) + r = requests.put(BASE_URL + 'days/2017-01-16/info', json=day) r.raise_for_status() rj = r.json() self.assertTrue(dictInDict(day, rj)) @@ -199,7 +199,7 @@ class Ibt2Tests(unittest.TestCase): def test_put_group(self): self.add_attendee({'day': '2017-01-16', 'name': 'A new name', 'group': 'A group'}) group = {'group': 'A group', 'day': '2017-01-16', 'notes': 'A group note'} - r = requests.put(BASE_URL + 'groups', json=group) + r = requests.put(BASE_URL + 'days/2017-01-16/groups/A group/info', json=group) r.raise_for_status() rj = r.json() self.assertTrue(dictInDict(group, rj)) @@ -211,7 +211,7 @@ class Ibt2Tests(unittest.TestCase): def test_delete_group(self): self.add_attendee({'day': '2017-01-16', 'name': 'A new name', 'group': 'A group'}) s = self.login('admin', 'ibt2') - r = s.delete(BASE_URL + 'groups', params={'day': '2017-01-16', 'group': 'A group'}) + r = s.delete(BASE_URL + 'days/2017-01-16/groups/A group', params={'day': '2017-01-16', 'group': 'A group'}) r.raise_for_status() rj = r.json() r = requests.get(BASE_URL + 'days/2017-01-16')