Fix Slack direct paging issue when there are >100 schedules (#2594)

# What this PR does

Fix Slack direct paging issue when there are >100 schedules.

## Checklist

- [x] Unit, integration, and e2e (if applicable) tests updated
- [x] Documentation added (or `pr:no public docs` PR label added if not
required)
- [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not
required)
This commit is contained in:
Vadim Stepanov 2023-07-19 14:52:05 +01:00 committed by GitHub
parent e79d35fdaa
commit fa1ca0dfa6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 85 additions and 27 deletions

View file

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Fixed
- Fix Slack direct paging issue when there are more than 100 schedules by @vadimkerr ([#2594](https://github.com/grafana/oncall/pull/2594))
## v1.3.15 (2023-07-19)
### Changed

View file

@ -43,6 +43,9 @@ ITEM_ACTIONS = (
SCHEDULES_DATA_KEY = "schedules"
USERS_DATA_KEY = "users"
# https://api.slack.com/reference/block-kit/block-elements#static_select
MAX_STATIC_SELECT_OPTIONS = 100
def add_or_update_item(payload, key, item_pk, policy):
metadata = json.loads(payload["view"]["private_metadata"])
@ -639,7 +642,7 @@ def _get_additional_responders_blocks(
return blocks
def _get_users_select(organization, input_id_prefix, action_id):
def _get_users_select(organization, input_id_prefix, action_id, max_options_per_group=MAX_STATIC_SELECT_OPTIONS):
users = organization.users.all()
user_options = [
@ -667,27 +670,16 @@ def _get_users_select(organization, input_id_prefix, action_id):
"action_id": action_id,
},
}
MAX_STATIC_SELECT_OPTIONS = 100
if len(user_options) > MAX_STATIC_SELECT_OPTIONS:
# paginate user options in groups
max_length = MAX_STATIC_SELECT_OPTIONS
chunks = [user_options[x : x + max_length] for x in range(0, len(user_options), max_length)]
option_groups = [
{
"label": {"type": "plain_text", "text": f"({(i * max_length)+1}-{(i * max_length)+max_length})"},
"options": group,
}
for i, group in enumerate(chunks)
]
user_select["accessory"]["option_groups"] = option_groups
if len(user_options) > max_options_per_group:
user_select["accessory"]["option_groups"] = _get_option_groups(user_options, max_options_per_group)
else:
user_select["accessory"]["options"] = user_options
return user_select
def _get_schedules_select(organization, input_id_prefix, action_id):
def _get_schedules_select(organization, input_id_prefix, action_id, max_options_per_group=MAX_STATIC_SELECT_OPTIONS):
schedules = organization.oncall_schedules.all()
schedule_options = [
@ -701,23 +693,46 @@ def _get_schedules_select(organization, input_id_prefix, action_id):
}
for schedule in schedules
]
if not schedule_options:
schedule_select = {"type": "context", "elements": [{"type": "mrkdwn", "text": "No schedules available"}]}
return {"type": "context", "elements": [{"type": "mrkdwn", "text": "No schedules available"}]}
schedule_select = {
"type": "section",
"text": {"type": "mrkdwn", "text": "Notify schedule"},
"block_id": input_id_prefix + DIRECT_PAGING_SCHEDULE_SELECT_ID,
"accessory": {
"type": "static_select",
"placeholder": {"type": "plain_text", "text": "Select schedule", "emoji": True},
"action_id": action_id,
},
}
if len(schedule_options) > max_options_per_group:
schedule_select["accessory"]["option_groups"] = _get_option_groups(schedule_options, max_options_per_group)
else:
schedule_select = {
"type": "section",
"text": {"type": "mrkdwn", "text": "Notify schedule"},
"block_id": input_id_prefix + DIRECT_PAGING_SCHEDULE_SELECT_ID,
"accessory": {
"type": "static_select",
"placeholder": {"type": "plain_text", "text": "Select schedule", "emoji": True},
"options": schedule_options,
"action_id": action_id,
},
}
schedule_select["accessory"]["options"] = schedule_options
return schedule_select
def _get_option_groups(options, max_options_per_group):
chunks = [options[x : x + max_options_per_group] for x in range(0, len(options), max_options_per_group)]
option_groups = []
for idx, group in enumerate(chunks):
start = idx * max_options_per_group + 1
end = idx * max_options_per_group + max_options_per_group
option_groups.append(
{
"label": {"type": "plain_text", "text": f"({start}-{end})"},
"options": group,
}
)
return option_groups
def _get_selected_entries_list(input_id_prefix, key, entries):
current_entries = []
for entry, policy in entries:

View file

@ -16,6 +16,7 @@ from apps.slack.scenarios.manage_responders import (
ManageRespondersUserChange,
StartManageResponders,
)
from apps.slack.scenarios.paging import _get_schedules_select, _get_users_select
ORGANIZATION_ID = 12
ALERT_GROUP_ID = 42
@ -205,3 +206,41 @@ def test_remove_user(manage_responders_setup):
assert mock_slack_api_call.call_args.args == ("views.update",)
# check there's no list of users in the view
assert mock_slack_api_call.call_args.kwargs["view"]["blocks"][0]["accessory"]["type"] != "button"
@pytest.mark.django_db
def test_get_users_select(make_organization, make_user):
organization = make_organization()
for _ in range(3):
make_user(organization=organization)
select_options = _get_users_select(organization=organization, input_id_prefix="test", action_id="test")
assert len(select_options["accessory"]["options"]) == 3
assert "option_groups" not in select_options["accessory"]
select_option_groups = _get_users_select(
organization=organization, input_id_prefix="test", action_id="test", max_options_per_group=2
)
assert len(select_option_groups["accessory"]["option_groups"]) == 2
assert len(select_option_groups["accessory"]["option_groups"][0]["options"]) == 2
assert len(select_option_groups["accessory"]["option_groups"][1]["options"]) == 1
assert "options" not in select_option_groups["accessory"]
@pytest.mark.django_db
def test_get_schedules_select(make_organization, make_schedule):
organization = make_organization()
for _ in range(3):
make_schedule(organization, schedule_class=OnCallScheduleWeb)
select_options = _get_schedules_select(organization=organization, input_id_prefix="test", action_id="test")
assert len(select_options["accessory"]["options"]) == 3
assert "option_groups" not in select_options["accessory"]
select_option_groups = _get_schedules_select(
organization=organization, input_id_prefix="test", action_id="test", max_options_per_group=2
)
assert len(select_option_groups["accessory"]["option_groups"]) == 2
assert len(select_option_groups["accessory"]["option_groups"][0]["options"]) == 2
assert len(select_option_groups["accessory"]["option_groups"][1]["options"]) == 1
assert "options" not in select_option_groups["accessory"]