You've already forked oncall
mirror of
https://github.com/linkedin/oncall.git
synced 2025-11-27 23:18:38 +02:00
MYSQL SCHEMA CHANGE - add api managed teams that disable UI info changes (#396)
* MYSQL SCHEMQA CHANGE - add api_managed_roster * whitespace formatting * test * test * update changelog * update test * update schema-update file name
This commit is contained in:
13
CHANGELOG.md
Normal file
13
CHANGELOG.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Change Log
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
|
||||||
|
## [2.0.0] - 2023-06-06
|
||||||
|
WARNING: this version adds a change to the MYSQL schema! Make changes to the schema before deploying new 2.0.0 version.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- MINOR added the ability to designate teams as "api managed" which will prevent changes to team info from being done via the UI
|
||||||
|
### Changed
|
||||||
|
- MAJOR added the `api_managed_roster` column to the `team` table in the MYSQL schema. Before running 2.0.0 the MYSQL schema must be updated with the new column to avoid errors, to do so run `mysql -u root -p oncall < ./db/schema-update.v2.0.0_2023-06-06.sql`
|
||||||
|
|
||||||
|
### Fixed
|
||||||
6
db/schema-update.v2.0.0_2023-06-06.sql
Normal file
6
db/schema-update.v2.0.0_2023-06-06.sql
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
-- -----------------------------------------------------
|
||||||
|
-- Update to Table `team`
|
||||||
|
-- -----------------------------------------------------
|
||||||
|
|
||||||
|
ALTER TABLE `team`
|
||||||
|
ADD `api_managed_roster` BOOLEAN NOT NULL DEFAULT FALSE;
|
||||||
@@ -15,6 +15,7 @@ CREATE TABLE IF NOT EXISTS `team` (
|
|||||||
`iris_plan` VARCHAR(255),
|
`iris_plan` VARCHAR(255),
|
||||||
`iris_enabled` BOOLEAN NOT NULL DEFAULT FALSE,
|
`iris_enabled` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
`override_phone_number` VARCHAR(255),
|
`override_phone_number` VARCHAR(255),
|
||||||
|
`api_managed_roster` BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE INDEX `name_unique` (`name` ASC));
|
UNIQUE INDEX `name_unique` (`name` ASC));
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ def test_api_v0_get_team(team, role, roster, schedule):
|
|||||||
team = re.json()
|
team = re.json()
|
||||||
assert isinstance(team, dict)
|
assert isinstance(team, dict)
|
||||||
expected_set = {'users', 'admins', 'services', 'rosters', 'name', 'id', 'slack_channel', 'slack_channel_notifications', 'email',
|
expected_set = {'users', 'admins', 'services', 'rosters', 'name', 'id', 'slack_channel', 'slack_channel_notifications', 'email',
|
||||||
'scheduling_timezone', 'iris_plan', 'iris_enabled', 'override_phone_number'}
|
'scheduling_timezone', 'iris_plan', 'iris_enabled', 'override_phone_number', 'api_managed_roster'}
|
||||||
assert expected_set == set(team.keys())
|
assert expected_set == set(team.keys())
|
||||||
|
|
||||||
# it should also support filter by fields
|
# it should also support filter by fields
|
||||||
@@ -79,7 +79,7 @@ def test_api_v0_get_team(team, role, roster, schedule):
|
|||||||
team = re.json()
|
team = re.json()
|
||||||
assert isinstance(team, dict)
|
assert isinstance(team, dict)
|
||||||
expected_set = {'users', 'admins', 'services', 'name', 'id', 'slack_channel', 'slack_channel_notifications', 'email',
|
expected_set = {'users', 'admins', 'services', 'name', 'id', 'slack_channel', 'slack_channel_notifications', 'email',
|
||||||
'scheduling_timezone', 'iris_plan', 'iris_enabled', 'override_phone_number'}
|
'scheduling_timezone', 'iris_plan', 'iris_enabled', 'override_phone_number', 'api_managed_roster'}
|
||||||
assert expected_set == set(team.keys())
|
assert expected_set == set(team.keys())
|
||||||
|
|
||||||
|
|
||||||
@@ -113,6 +113,7 @@ def test_api_v0_update_team(team):
|
|||||||
# edit team name/email/slack
|
# edit team name/email/slack
|
||||||
re = requests.put(api_v0('teams/'+team_name), json={'name': new_team_name,
|
re = requests.put(api_v0('teams/'+team_name), json={'name': new_team_name,
|
||||||
'email': email,
|
'email': email,
|
||||||
|
'api_managed_roster': True,
|
||||||
'slack_channel': slack,
|
'slack_channel': slack,
|
||||||
'slack_channel_notifications': slack_notifications,
|
'slack_channel_notifications': slack_notifications,
|
||||||
'override_phone_number': override_num})
|
'override_phone_number': override_num})
|
||||||
@@ -128,6 +129,7 @@ def test_api_v0_update_team(team):
|
|||||||
assert data['slack_channel'] == slack
|
assert data['slack_channel'] == slack
|
||||||
assert data['slack_channel_notifications'] == slack_notifications
|
assert data['slack_channel_notifications'] == slack_notifications
|
||||||
assert data['override_phone_number'] == override_num
|
assert data['override_phone_number'] == override_num
|
||||||
|
assert data['api_managed_roster'] == 1
|
||||||
|
|
||||||
|
|
||||||
@prefix('test_v0_team_admin')
|
@prefix('test_v0_team_admin')
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
__version__ = '1.5.5'
|
__version__ = '2.0.0'
|
||||||
|
|||||||
@@ -11,11 +11,11 @@ from .users import get_user_data
|
|||||||
from .rosters import get_roster_by_team_id
|
from .rosters import get_roster_by_team_id
|
||||||
from ...auth import login_required, check_team_auth
|
from ...auth import login_required, check_team_auth
|
||||||
from ...utils import load_json_body, invalid_char_reg, create_audit
|
from ...utils import load_json_body, invalid_char_reg, create_audit
|
||||||
from ...constants import TEAM_DELETED, TEAM_EDITED
|
from ...constants import TEAM_DELETED, TEAM_EDITED, SUPPORTED_TIMEZONES
|
||||||
|
|
||||||
# Columns which may be modified
|
# Columns which may be modified
|
||||||
cols = set(['name', 'slack_channel', 'slack_channel_notifications', 'email', 'scheduling_timezone',
|
cols = set(['name', 'slack_channel', 'slack_channel_notifications', 'email', 'scheduling_timezone',
|
||||||
'iris_plan', 'iris_enabled', 'override_phone_number'])
|
'iris_plan', 'iris_enabled', 'override_phone_number', 'api_managed_roster'])
|
||||||
|
|
||||||
|
|
||||||
def populate_team_users(cursor, team_dict):
|
def populate_team_users(cursor, team_dict):
|
||||||
@@ -148,7 +148,7 @@ def on_get(req, resp, team):
|
|||||||
connection = db.connect()
|
connection = db.connect()
|
||||||
cursor = connection.cursor(db.DictCursor)
|
cursor = connection.cursor(db.DictCursor)
|
||||||
cursor.execute('''SELECT `id`, `name`, `email`, `slack_channel`, `slack_channel_notifications`,
|
cursor.execute('''SELECT `id`, `name`, `email`, `slack_channel`, `slack_channel_notifications`,
|
||||||
`scheduling_timezone`, `iris_plan`, `iris_enabled`, `override_phone_number`
|
`scheduling_timezone`, `iris_plan`, `iris_enabled`, `override_phone_number`, `api_managed_roster`
|
||||||
FROM `team` WHERE `name`=%s AND `active` = %s''', (team, active))
|
FROM `team` WHERE `name`=%s AND `active` = %s''', (team, active))
|
||||||
results = cursor.fetchall()
|
results = cursor.fetchall()
|
||||||
if not results:
|
if not results:
|
||||||
@@ -172,7 +172,8 @@ def on_get(req, resp, team):
|
|||||||
@login_required
|
@login_required
|
||||||
def on_put(req, resp, team):
|
def on_put(req, resp, team):
|
||||||
'''
|
'''
|
||||||
Edit a team's information. Allows edit of: name, slack_channel, email, scheduling_timezone, iris_plan.
|
Edit a team's information. Allows edit of: 'name', 'slack_channel', 'slack_channel_notifications', 'email', 'scheduling_timezone',
|
||||||
|
'iris_plan', 'iris_enabled', 'override_phone_number', 'api_managed_roster'
|
||||||
|
|
||||||
**Example request:**
|
**Example request:**
|
||||||
|
|
||||||
@@ -213,9 +214,18 @@ def on_put(req, resp, team):
|
|||||||
plan_resp = iris.client.get(iris.client.url + 'plans?name=%s&active=1' % iris_plan)
|
plan_resp = iris.client.get(iris.client.url + 'plans?name=%s&active=1' % iris_plan)
|
||||||
if plan_resp.status_code != 200 or plan_resp.json() == []:
|
if plan_resp.status_code != 200 or plan_resp.json() == []:
|
||||||
raise HTTPBadRequest('invalid iris escalation plan', 'no iris plan named %s exists' % iris_plan)
|
raise HTTPBadRequest('invalid iris escalation plan', 'no iris plan named %s exists' % iris_plan)
|
||||||
|
if 'iris_enabled' in data:
|
||||||
|
if not type(data['iris_enabled']) == bool:
|
||||||
|
raise HTTPBadRequest('invalid payload', 'iris_enabled must be boolean')
|
||||||
|
if 'api_managed_roster' in data:
|
||||||
|
if not type(data['api_managed_roster']) == bool:
|
||||||
|
raise HTTPBadRequest('invalid payload', 'api_managed_roster must be boolean')
|
||||||
|
if 'scheduling_timezone' in data:
|
||||||
|
if data['scheduling_timezone'] not in SUPPORTED_TIMEZONES:
|
||||||
|
raise HTTPBadRequest('invalid payload', 'requested scheduling_timezone is not supported. Supported timezones: %s' % str(SUPPORTED_TIMEZONES))
|
||||||
|
|
||||||
set_clause = ', '.join(['`{0}`=%s'.format(d) for d in data_cols if d in cols])
|
set_clause = ', '.join(['`{0}`=%s'.format(d) for d in data_cols if d in cols])
|
||||||
query_params = tuple(data[d] for d in data_cols) + (team,)
|
query_params = tuple(data[d] for d in data_cols if d in cols) + (team,)
|
||||||
try:
|
try:
|
||||||
update_query = 'UPDATE `team` SET {0} WHERE name=%s'.format(set_clause)
|
update_query = 'UPDATE `team` SET {0} WHERE name=%s'.format(set_clause)
|
||||||
cursor.execute(update_query, query_params)
|
cursor.execute(update_query, query_params)
|
||||||
|
|||||||
@@ -1364,7 +1364,8 @@ var oncall = {
|
|||||||
data.isAdmin = true;
|
data.isAdmin = true;
|
||||||
} else {
|
} else {
|
||||||
for (var i in data.admins) {
|
for (var i in data.admins) {
|
||||||
if (data.admins[i].name === oncall.data.user) {
|
// if team api managed and user is not superadmin then disable editing of team info
|
||||||
|
if (data.admins[i].name === oncall.data.user && !data.api_managed_roster) {
|
||||||
data.isAdmin = true;
|
data.isAdmin = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -333,6 +333,11 @@
|
|||||||
<a href="https://{% endraw %}{{slack_instance}}{% raw %}.slack.com/messages/{{stripHash slack_channel_notifications}}/" target="_blank">{{slack_channel_notifications}}</a>
|
<a href="https://{% endraw %}{{slack_instance}}{% raw %}.slack.com/messages/{{stripHash slack_channel_notifications}}/" target="_blank">{{slack_channel_notifications}}</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</h4>
|
</h4>
|
||||||
|
<h4>
|
||||||
|
{{#if api_managed_roster}}
|
||||||
|
Managed team - this team is managed via API
|
||||||
|
{{/if}}
|
||||||
|
</h4>
|
||||||
{% endraw %} {% endif %} {% raw %}
|
{% endraw %} {% endif %} {% raw %}
|
||||||
</div>
|
</div>
|
||||||
<div class="pull-right">
|
<div class="pull-right">
|
||||||
|
|||||||
Reference in New Issue
Block a user