Browse Source

improve REST path

Davide Alberani 7 years ago
parent
commit
05dac7f4fb
5 changed files with 82 additions and 28 deletions
  1. 5 2
      docs/DEVELOPMENT.md
  2. 48 22
      ibt2.py
  3. 24 0
      monco.py
  4. 2 1
      src/App.vue
  5. 3 3
      tests/ibt2_tests.py

+ 5 - 2
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

+ 48 - 22
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<day>[\d_-]+)?"
-    _groups_path = r"/groups/?"
+    _days_info_path = r"/days/(?P<day>[\d_-]+)/info"
+    _groups_path = r"/days/(?P<day>[\d_-]+)/groups/(?P<group>.+?)"
+    _groups_info_path = r"/days/(?P<day>[\d_-]+)/groups/(?P<group>.+?)/info"
     _attendees_path = r"/attendees/?(?P<id_>[\w\d_-]+)?"
     _current_user_path = r"/users/current/?"
     _users_path = r"/users/?(?P<id_>[\w\d_-]+)?/?(?P<resource>[\w\d_-]+)?/?(?P<resource_id>[\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),

+ 24 - 0
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.
 

+ 2 - 1
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'});

+ 3 - 3
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')