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:
parent
e79d35fdaa
commit
fa1ca0dfa6
3 changed files with 85 additions and 27 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue