Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
packages=setuptools.find_packages('src'),
include_package_data=True,
install_requires=[
'falcon==3.1.1',
'falcon',
'falcon-cors',
'greenlet==2.0.1',
'gevent==22.10.2',
Expand Down
2 changes: 1 addition & 1 deletion src/oncall/api/v0/audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,4 @@ def on_get(req, resp):
results = cursor.fetchall()
cursor.close()
connection.close()
resp.body = json_dumps(results)
resp.text = json_dumps(results)
4 changes: 2 additions & 2 deletions src/oncall/api/v0/bonus_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def on_get(self, req, resp):

oncall_bonus_teams = bonus_response.json()

for event in json.loads(resp.body):
for event in json.loads(resp.text):
if event['role'].lower() == 'manager':
continue

Expand All @@ -133,4 +133,4 @@ def on_get(self, req, resp):
ldap_grouping[event['user']].append(event)

resp.status = HTTP_200
resp.body = json_dumps(ldap_grouping)
resp.text = json_dumps(ldap_grouping)
29 changes: 21 additions & 8 deletions src/oncall/api/v0/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def on_get(req, resp, event_id):
connection.close()
if num_found == 0:
raise HTTPNotFound()
resp.body = json_dumps(data)
resp.text = json_dumps(data)


@login_required
Expand Down Expand Up @@ -101,12 +101,18 @@ def on_put(req, resp, event_id):
data = load_json_body(req)

if 'end' in data and 'start' in data and data['start'] >= data['end']:
raise HTTPBadRequest('Invalid event update', 'Event must start before it ends')
raise HTTPBadRequest(
title='Invalid event update',
description='Event must start before it ends'
)

try:
update_cols = ', '.join(update_columns[col] for col in data)
except KeyError:
raise HTTPBadRequest('Invalid event update', 'Invalid column')
raise HTTPBadRequest(
title='Invalid event update',
description='Invalid column'
)

connection = db.connect()
cursor = connection.cursor(db.DictCursor)
Expand Down Expand Up @@ -143,12 +149,17 @@ def on_put(req, resp, event_id):
try:
check_team_auth(event_data['team'], req)
except HTTPUnauthorized:
raise HTTPBadRequest('Invalid event update',
'Editing events in the past not allowed')
raise HTTPBadRequest(
title='Invalid event update',
description='Editing events in the past not allowed'
)

check_calendar_auth(event_data['team'], req)
if not user_in_team_by_name(cursor, new_event['user'], event_data['team']):
raise HTTPBadRequest('Invalid event update', 'Event user must be part of the team')
raise HTTPBadRequest(
title='Invalid event update',
description='Event user must be part of the team'
)

update_cols += ', `link_id` = NULL'
update = 'UPDATE `event` SET ' + update_cols + (' WHERE `id`=%d' % int(event_id))
Expand Down Expand Up @@ -208,8 +219,10 @@ def on_delete(req, resp, event_id):
ev = cursor.fetchone()
check_calendar_auth(ev['team'], req)
if ev['start'] < time.time() - constants.GRACE_PERIOD:
raise HTTPBadRequest('Invalid event update',
'Deleting events in the past not allowed')
raise HTTPBadRequest(
title='Invalid event update',
description='Deleting events in the past not allowed'
)

cursor.execute('DELETE FROM `event` WHERE `id`=%s', event_id)

Expand Down
22 changes: 16 additions & 6 deletions src/oncall/api/v0/event_link.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ def on_delete(req, resp, link_id):
event_start = min(data, key=itemgetter('start'))['start']
check_calendar_auth(ev['team'], req)
if event_start < time.time() - constants.GRACE_PERIOD:
raise HTTPBadRequest('Invalid event update',
'Deleting events in the past not allowed')
raise HTTPBadRequest(
title='Invalid event update',
description='Deleting events in the past not allowed'
)
cursor.execute('DELETE FROM `event` WHERE `link_id`=%s', link_id)

context = {'team': ev['team'], 'full_name': ev['full_name'], 'role': ev['role']}
Expand Down Expand Up @@ -90,7 +92,10 @@ def on_put(req, resp, link_id):
try:
update_cols = ', '.join(update_columns[col] for col in data)
except KeyError:
raise HTTPBadRequest('Invalid event update', 'Invalid column')
raise HTTPBadRequest(
title='Invalid event update',
description='Invalid column'
)
connection = db.connect()
cursor = connection.cursor(db.DictCursor)

Expand Down Expand Up @@ -119,12 +124,17 @@ def on_put(req, resp, link_id):
event_summary['start'] = min(event_data, key=itemgetter('start'))['start']
user = data.get('user', event_summary['user'])
if not user_in_team_by_name(cursor, user, event_summary['team']):
raise HTTPBadRequest('Invalid event update', 'Event user must be part of the team')
raise HTTPBadRequest(
title='Invalid event update',
description='Event user must be part of the team'
)

now = time.time()
if event_summary['start'] < now - constants.GRACE_PERIOD:
raise HTTPBadRequest('Invalid event update',
'Editing events in the past not allowed')
raise HTTPBadRequest(
title='Invalid event update',
description='Editing events in the past not allowed'
)
check_calendar_auth(event_summary['team'], req)

update = 'UPDATE `event` SET %s WHERE link_id = %%(link_id)s' % update_cols
Expand Down
41 changes: 32 additions & 9 deletions src/oncall/api/v0/event_override.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,27 +86,44 @@ def on_post(req, resp):
cursor.execute('SELECT `id` FROM `user` WHERE `name` = %s', user)
user_id = cursor.fetchone()
if not (events and user_id):
raise HTTPBadRequest('Invalid name or list of events')
raise HTTPBadRequest(
title='Invalid name or list of events'
)
else:
user_id = user_id['id']
team_id = events[0]['team_id']

check_calendar_auth_by_id(team_id, req)
# Check that events are not in the past
if start < now - constants.GRACE_PERIOD:
raise HTTPBadRequest('Invalid override request', 'Cannot edit events in the past')
raise HTTPBadRequest(
title='Invalid override request',
description='Cannot edit events in the past'
)
# Check that events are from the same team
if any([ev['team_id'] != team_id for ev in events]):
raise HTTPBadRequest('Invalid override request', 'Events must be from the same team')
raise HTTPBadRequest(
title='Invalid override request',
description='Events must be from the same team'
)
# Check override user's membership in the team
if not user_in_team(cursor, user_id, team_id):
raise HTTPBadRequest('Invalid override request', 'Substituting user must be part of the team')
raise HTTPBadRequest(
title='Invalid override request',
description='Substituting user must be part of the team'
)
# Check events have the same role
if len(set([ev['role_id'] for ev in events])) > 1:
raise HTTPBadRequest('Invalid override request', 'events must have the same role')
raise HTTPBadRequest(
title='Invalid override request',
description='events must have the same role'
)
# Check events have same user
if len(set([ev['user_id'] for ev in events])) > 1:
raise HTTPBadRequest('Invalid override request', 'events must have the same role')
raise HTTPBadRequest(
title='Invalid override request',
description='events must have the same role'
)

edit_start = []
edit_end = []
Expand All @@ -121,7 +138,10 @@ def on_post(req, resp):
for idx, e in enumerate(events):
# Check for consecutive events
if idx != 0 and e['start'] != events[idx - 1]['end']:
raise HTTPBadRequest('Invalid override request', 'events must be consecutive')
raise HTTPBadRequest(
title='Invalid override request',
description='events must be consecutive'
)

# Sort events into lists according to how they need to be edited
if start <= e['start'] and end >= e['end']:
Expand All @@ -133,7 +153,10 @@ def on_post(req, resp):
elif start > e['start'] and end < e['end']:
split.append(e)
else:
raise HTTPBadRequest('Invalid override request', 'events must overlap with override time range')
raise HTTPBadRequest(
title='Invalid override request',
description='events must overlap with override time range'
)

# Edit events
if edit_start:
Expand Down Expand Up @@ -189,7 +212,7 @@ def on_post(req, resp):
[user_id, events[0]['user_id']], cursor, start_time=start, end_time=end)
create_audit({'new_events': ret_data, 'request_body': data}, ret_data[0]['team'],
EVENT_SUBSTITUTED, req, cursor)
resp.body = json_dumps(ret_data)
resp.text = json_dumps(ret_data)
except HTTPError:
raise
else:
Expand Down
29 changes: 20 additions & 9 deletions src/oncall/api/v0/event_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ def on_post(req, resp):
try:
ev_0, ev_1 = data['events']
except ValueError:
raise HTTPBadRequest('Invalid event swap request',
'Must provide 2 events')
raise HTTPBadRequest(
title='Invalid event swap request',
description='Must provide 2 events'
)

connection = db.connect()
cursor = connection.cursor(db.DictCursor)
Expand All @@ -58,8 +60,10 @@ def on_post(req, resp):
events = [None, None]
for i, ev in enumerate([ev_0, ev_1]):
if not ev.get('id'):
raise HTTPBadRequest('Invalid event swap request',
'Invalid event id: %s' % ev.get('id'))
raise HTTPBadRequest(
title='Invalid event swap request',
description='Invalid event id: %s' % ev.get('id')
)
if ev.get('linked'):
cursor.execute('SELECT `id`, `start`, `end`, `team_id`, `user_id`, `role_id`, '
'`link_id` FROM `event` WHERE `link_id` = %s',
Expand All @@ -77,14 +81,21 @@ def on_post(req, resp):
# Validation checks
now = time.time()
if any([ev['start'] < now - constants.GRACE_PERIOD for ev in events]):
raise HTTPBadRequest('Invalid event swap request',
'Cannot edit events in the past')
raise HTTPBadRequest(
title='Invalid event swap request',
description='Cannot edit events in the past'
)
if len(set(ev['team_id'] for ev in events)) > 1:
raise HTTPBadRequest('Event swap not allowed',
'Swapped events must come from the same team')
raise HTTPBadRequest(
title='Event swap not allowed',
description='Swapped events must come from the same team'
)
for ev_list in [events_0, events_1]:
if len(set([ev['user_id'] for ev in ev_list])) != 1:
raise HTTPBadRequest('', 'all linked events must have the same user')
raise HTTPBadRequest(
title='',
description='all linked events must have the same user'
)

check_calendar_auth_by_id(events[0]['team_id'], req)

Expand Down
29 changes: 22 additions & 7 deletions src/oncall/api/v0/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,9 @@ def on_get(req, resp):
req.params.pop('include_subscribed', None)
cols = ', '.join(fields) if fields else all_columns
if any(key not in constraints for key in req.params):
raise HTTPBadRequest('Bad constraint param')
raise HTTPBadRequest(
title='Bad constraint param'
)
query = '''SELECT %s FROM `event`
JOIN `user` ON `user`.`id` = `event`.`user_id`
JOIN `team` ON `team`.`id` = `event`.`team_id`
Expand Down Expand Up @@ -202,7 +204,7 @@ def on_get(req, resp):
data = cursor.fetchall()
cursor.close()
connection.close()
resp.body = json_dumps(data)
resp.text = json_dumps(data)


@login_required
Expand Down Expand Up @@ -251,9 +253,15 @@ def on_post(req, resp):
data = load_json_body(req)
now = time.time()
if data['start'] < now - constants.GRACE_PERIOD:
raise HTTPBadRequest('Invalid event', 'Creating events in the past not allowed')
raise HTTPBadRequest(
title='Invalid event',
description='Creating events in the past not allowed'
)
if data['start'] >= data['end']:
raise HTTPBadRequest('Invalid event', 'Event must start before it ends')
raise HTTPBadRequest(
title='Invalid event',
description='Event must start before it ends'
)
check_calendar_auth(data['team'], req)

columns = ['`start`', '`end`', '`user_id`', '`team_id`', '`role_id`']
Expand All @@ -274,7 +282,10 @@ def on_post(req, resp):
cursor = connection.cursor(db.DictCursor)

if not user_in_team_by_name(cursor, data['user'], data['team']):
raise HTTPBadRequest('Invalid event', 'User must be part of the team')
raise HTTPBadRequest(
title='Invalid event',
description='User must be part of the team'
)

try:
query = 'INSERT INTO `event` (%s) VALUES (%s)' % (','.join(columns), ','.join(values))
Expand Down Expand Up @@ -309,10 +320,14 @@ def on_post(req, resp):
err_msg = 'user "%s" not found' % data['user']
elif err_msg == 'Column \'team_id\' cannot be null':
err_msg = 'team "%s" not found' % data['team']
raise HTTPError('422 Unprocessable Entity', 'IntegrityError', err_msg)
raise HTTPError(
'422 Unprocessable Entity',
title='IntegrityError',
description=err_msg
)
finally:
cursor.close()
connection.close()

resp.status = HTTP_201
resp.body = json_dumps(event_id)
resp.text = json_dumps(event_id)
Loading