improve REST path

This commit is contained in:
Davide Alberani 2017-02-12 11:47:28 +01:00
parent 7ee23b3b7e
commit 05dac7f4fb
5 changed files with 82 additions and 28 deletions

View file

@ -21,10 +21,13 @@ Web server
- /attendees POST - write a new entry - /attendees POST - write a new entry
- /attendees/:id GET - a single entry - /attendees/:id GET - a single entry
- /attendees/:id PUT - update an entry - /attendees/:id PUT - update an entry
- /attendees/:id DELETE - delete an entry
- /days GET - all entries, grouped by day and by group - /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 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 GET - list of all users
- /users POST - create a new user - /users POST - create a new user
- /users/:id GET - a single user - /users/:id GET - a single user

70
ibt2.py
View file

@ -261,8 +261,7 @@ class AttendeesHandler(BaseHandler):
@gen.coroutine @gen.coroutine
def delete(self, id_=None, **kwargs): def delete(self, id_=None, **kwargs):
if id_ is None: if id_ is None:
self.write({'success': False}) return self.build_error(status=404, message='unable to access the resource')
return
doc = self.db.getOne(self.collection, {'_id': id_}) or {} doc = self.db.getOne(self.collection, {'_id': id_}) or {}
if not doc: if not doc:
return self.build_error(status=404, message='unable to access the resource') return self.build_error(status=404, message='unable to access the resource')
@ -274,7 +273,6 @@ class AttendeesHandler(BaseHandler):
class DaysHandler(BaseHandler): class DaysHandler(BaseHandler):
"""Handle requests for Days.""" """Handle requests for Days."""
def _summarize(self, days): def _summarize(self, days):
res = [] res = []
for day in days: for day in days:
@ -342,12 +340,15 @@ class DaysHandler(BaseHandler):
else: else:
self.write(base) self.write(base)
class DaysInfoHandler(BaseHandler):
"""Handle requests for Days info."""
@gen.coroutine @gen.coroutine
def put(self, **kwargs): def put(self, day='', **kwargs):
data = self.clean_body day = day.strip()
day = (data.get('day') or '').strip()
if not day: if not day:
return self.build_error(status=404, message='unable to access the resource') return self.build_error(status=404, message='unable to access the resource')
data = self.clean_body
data['day'] = day data['day'] = day
self.add_access_info(data) self.add_access_info(data)
merged, doc = self.db.update('days', {'day': day}, data) merged, doc = self.db.update('days', {'day': day}, data)
@ -357,23 +358,26 @@ class DaysHandler(BaseHandler):
class GroupsHandler(BaseHandler): class GroupsHandler(BaseHandler):
"""Handle requests for Groups.""" """Handle requests for Groups."""
@gen.coroutine @gen.coroutine
def put(self, **kwargs): def put(self, day='', group='', **kwargs):
data = self.clean_body day = day.strip()
day = (data.get('day') or '').strip() group = group.strip()
group = (data.get('group') or '').strip()
if not (day and group): if not (day and group):
return self.build_error(status=404, message='unable to access the resource') return self.build_error(status=404, message='unable to access the resource')
data['day'] = day data = self.clean_body
data['group'] = group newName = (data.get('newName') or '').strip()
self.add_access_info(data) if newName:
merged, doc = self.db.update('groups', {'day': day, 'group': group}, data) query = {'day': day, 'group': group}
self.write(doc) 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 @gen.coroutine
def delete(self, **kwargs): def delete(self, day='', group='', **kwargs):
data = self.clean_arguments day = day.strip()
day = (data.get('day') or '').strip() group = group.strip()
group = (data.get('group') or '').strip()
if not (day and group): if not (day and group):
return self.build_error(status=404, message='unable to access the resource') return self.build_error(status=404, message='unable to access the resource')
if not self.current_user_info.get('isAdmin'): if not self.current_user_info.get('isAdmin'):
@ -385,6 +389,22 @@ class GroupsHandler(BaseHandler):
self.write({'success': True, 'deleted entries': howMany.get('n')}) 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): class UsersHandler(BaseHandler):
"""Handle requests for Users.""" """Handle requests for Users."""
document = 'user' document = 'user'
@ -574,17 +594,23 @@ def run():
{'setting': 'server_cookie_secret', 'cookie_secret': cookie_secret}) {'setting': 'server_cookie_secret', 'cookie_secret': cookie_secret})
_days_path = r"/days/?(?P<day>[\d_-]+)?" _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_-]+)?" _attendees_path = r"/attendees/?(?P<id_>[\w\d_-]+)?"
_current_user_path = r"/users/current/?" _current_user_path = r"/users/current/?"
_users_path = r"/users/?(?P<id_>[\w\d_-]+)?/?(?P<resource>[\w\d_-]+)?/?(?P<resource_id>[\w\d_-]+)?" _users_path = r"/users/?(?P<id_>[\w\d_-]+)?/?(?P<resource>[\w\d_-]+)?/?(?P<resource_id>[\w\d_-]+)?"
application = tornado.web.Application([ application = tornado.web.Application([
(_attendees_path, AttendeesHandler, init_params), (_attendees_path, AttendeesHandler, init_params),
(r'/v%s%s' % (API_VERSION, _attendees_path), AttendeesHandler, init_params), (r'/v%s%s' % (API_VERSION, _attendees_path), AttendeesHandler, init_params),
(_days_path, DaysHandler, init_params), (_groups_info_path, GroupsInfoHandler, init_params),
(r'/v%s%s' % (API_VERSION, _groups_path), GroupsHandler, init_params), (r'/v%s%s' % (API_VERSION, _groups_info_path), GroupsInfoHandler, init_params),
(_groups_path, GroupsHandler, 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), (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), (_current_user_path, CurrentUserHandler, init_params),
(r'/v%s%s' % (API_VERSION, _current_user_path), CurrentUserHandler, init_params), (r'/v%s%s' % (API_VERSION, _current_user_path), CurrentUserHandler, init_params),
(_users_path, UsersHandler, init_params), (_users_path, UsersHandler, init_params),

View file

@ -262,6 +262,30 @@ class Monco(object):
lastErrorObject = res.get('lastErrorObject') or {} lastErrorObject = res.get('lastErrorObject') or {}
return lastErrorObject.get('updatedExisting', False), res.get('value') 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): def delete(self, collection, _id_or_query=None, force=False):
"""Remove one or more documents from a collection. """Remove one or more documents from a collection.

View file

@ -86,6 +86,7 @@ export default {
beforeCreate: function() { beforeCreate: function() {
this.daysUrl = this.$resource('days{/day}'); this.daysUrl = this.$resource('days{/day}');
this.daysInfoUrl = this.$resource('days{/day}/info');
}, },
mounted: function() { mounted: function() {
@ -184,7 +185,7 @@ export default {
return; return;
} }
var data = {day: this.day.day, notes: this.dayNotes}; 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(); return response.json();
}, (response) => { }, (response) => {
this.$refs.dialogObj.show({text: 'unable to edit day notes'}); this.$refs.dialogObj.show({text: 'unable to edit day notes'});

View file

@ -187,7 +187,7 @@ class Ibt2Tests(unittest.TestCase):
def test_put_day(self): def test_put_day(self):
day = {'day': '2017-01-16', 'notes': 'A day note'} day = {'day': '2017-01-16', 'notes': 'A day note'}
self.add_attendee({'day': '2017-01-16', 'name': 'A new name', 'group': 'group C'}) 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() r.raise_for_status()
rj = r.json() rj = r.json()
self.assertTrue(dictInDict(day, rj)) self.assertTrue(dictInDict(day, rj))
@ -199,7 +199,7 @@ class Ibt2Tests(unittest.TestCase):
def test_put_group(self): def test_put_group(self):
self.add_attendee({'day': '2017-01-16', 'name': 'A new name', 'group': 'A group'}) 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'} 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() r.raise_for_status()
rj = r.json() rj = r.json()
self.assertTrue(dictInDict(group, rj)) self.assertTrue(dictInDict(group, rj))
@ -211,7 +211,7 @@ class Ibt2Tests(unittest.TestCase):
def test_delete_group(self): def test_delete_group(self):
self.add_attendee({'day': '2017-01-16', 'name': 'A new name', 'group': 'A group'}) self.add_attendee({'day': '2017-01-16', 'name': 'A new name', 'group': 'A group'})
s = self.login('admin', 'ibt2') 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() r.raise_for_status()
rj = r.json() rj = r.json()
r = requests.get(BASE_URL + 'days/2017-01-16') r = requests.get(BASE_URL + 'days/2017-01-16')