From 636e887c7de477a2605155d3c35331841bd58087 Mon Sep 17 00:00:00 2001 From: Nicholas Eidler Date: Mon, 2 Oct 2023 08:40:49 -0700 Subject: [PATCH] Add new query param "excludedTeams" to iCal API endpoint (#406) * Add new query param "excludedTeams" to iCal API endpoint * stylecheck * assert number of events --- e2e/test_ical.py | 38 ++++++++++++++++++++++++++++++++ src/oncall/api/v0/public_ical.py | 3 ++- src/oncall/api/v0/teams.py | 11 +++++++++ src/oncall/api/v0/user_ical.py | 15 ++++++++++--- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/e2e/test_ical.py b/e2e/test_ical.py index 8b8870d7..170b78c0 100644 --- a/e2e/test_ical.py +++ b/e2e/test_ical.py @@ -36,6 +36,44 @@ def test_user_ical(event, team, user, role): assert start == calendar.timegm(component.get('dtstart').dt.timetuple()) assert end == calendar.timegm(component.get('dtend').dt.timetuple()) +@prefix('test_user_ical_exclude_team') +def test_user_ical_exclude_team(event, team, user, role): + team_name = team.create() + team_name_2 = team.create() + user_name = user.create() + role_name = role.create() + user.add_to_team(user_name, team_name) + user.add_to_team(user_name, team_name_2) + + start = int(time.time()) + 100 + end = start + 1000 + + ev1 = event.create({'start': start, + 'end': end, + 'user': user_name, + 'team': team_name, + 'role': role_name}) + ev2 = event.create({'start': start + 100, + 'end': end + 100, + 'user': user_name, + 'team': team_name_2, + 'role': role_name}) + + re = requests.get(api_v0('users/%s/ical?excludedTeams=%s' % (user_name, team_name_2))) + cal = re.content + # Parse icalendar, make sure event info is correct (excluded team's event should not be present) + ical = icalendar.Calendar.from_ical(re.content) + num_events = 0 + for component in ical.walk(): + if component.name == 'VEVENT': + num_events += 1 + assert user_name in component.get('description') + assert team_name_2 not in component.get('summary') + assert start == calendar.timegm(component.get('dtstart').dt.timetuple()) + assert end == calendar.timegm(component.get('dtend').dt.timetuple()) + # Make sure that there is only 1 event parsed (excluded team event not included) + assert num_events == 1 + @prefix('test_team_ical') def test_team_ical(event, team, user, role): diff --git a/src/oncall/api/v0/public_ical.py b/src/oncall/api/v0/public_ical.py index c2df899f..11e86623 100644 --- a/src/oncall/api/v0/public_ical.py +++ b/src/oncall/api/v0/public_ical.py @@ -17,6 +17,7 @@ def on_get(req, resp, key): """ roles = req.get_param_as_list('roles') + excluded_teams = req.get_param_as_list('excludedTeams') name_and_type = get_name_and_type_from_key(key) if name_and_type is None: @@ -26,7 +27,7 @@ def on_get(req, resp, key): start = int(time.time()) events = [] if type == 'user': - events = get_user_events(name, start, roles=roles) + events = get_user_events(name, start, roles=roles, excluded_teams=excluded_teams) elif type == 'team': events = get_team_events(name, start, roles=roles, include_subscribed=True) diff --git a/src/oncall/api/v0/teams.py b/src/oncall/api/v0/teams.py index 131c53f8..4a96037f 100755 --- a/src/oncall/api/v0/teams.py +++ b/src/oncall/api/v0/teams.py @@ -27,6 +27,17 @@ } +def get_team_ids(cursor, team_names): + if not team_names: + return [] + + team_query = 'SELECT DISTINCT `id` FROM `team` WHERE `name` IN ({0})'.format( + ','.join(['%s'] * len(team_names))) + # we need prepared statements here because team_names come from user input + cursor.execute(team_query, team_names) + return [row['id'] for row in cursor] + + def on_get(req, resp): ''' Search for team names. Allows filtering based on a number of parameters, detailed below. diff --git a/src/oncall/api/v0/user_ical.py b/src/oncall/api/v0/user_ical.py index 5b3762f9..e06f7843 100644 --- a/src/oncall/api/v0/user_ical.py +++ b/src/oncall/api/v0/user_ical.py @@ -4,10 +4,11 @@ import time from . import ical from .roles import get_role_ids +from .teams import get_team_ids from ... import db -def get_user_events(user_name, start, roles=None): +def get_user_events(user_name, start, roles=None, excluded_teams=None): connection = db.connect() cursor = connection.cursor(db.DictCursor) @@ -17,6 +18,13 @@ def get_user_events(user_name, start, roles=None): role_condition = ' AND `event`.`role_id` IN ({0})'.format( ','.join(map(str, role_ids))) + excluded_teams_condition = '' + excluded_team_ids = get_team_ids(cursor, excluded_teams) + if excluded_team_ids: + excluded_teams_condition = ' AND `event`.`team_id` NOT IN ({0})'.format( + ','.join(map(str, excluded_team_ids)) + ) + query = ''' SELECT `event`.`id`, @@ -32,7 +40,7 @@ def get_user_events(user_name, start, roles=None): WHERE `event`.`end` > %s AND `user`.`name` = %s - ''' + role_condition + ''' + role_condition + excluded_teams_condition cursor.execute(query, (start, user_name)) @@ -66,7 +74,8 @@ def on_get(req, resp, user_name): if contact is None: contact = True roles = req.get_param_as_list('roles') + excluded_teams = req.get_param_as_list('excludedTeams') - events = get_user_events(user_name, start, roles=roles) + events = get_user_events(user_name, start, roles=roles, excluded_teams=excluded_teams) resp.body = ical.events_to_ical(events, user_name, contact) resp.set_header('Content-Type', 'text/calendar')