Support stack header from chatops-proxy (#4578)
This PR supports new flow of selecting org to run command in a slack workspace if several stacks are using it. In this case user selects default stack to run commands or pass a --stack flag. Both handled by chatops-proxy which injects selected stack as a header. On a side note - I found one ScenarioStep with incompatible set of arguments with parent class. I didn't fixed it, just left TODO https://github.com/grafana/oncall/pull/4578/files#diff-e323b5f38ed665f73d5da3fa0575958ed54ab587f6521b4cd87e1491b5430f8bR364 Related to https://github.com/grafana/oncall-gateway/issues/256 --------- Co-authored-by: Vadim Stepanov <vadimkerr@gmail.com>
This commit is contained in:
parent
8d82b078d3
commit
1465db36e5
20 changed files with 319 additions and 82 deletions
|
|
@ -18,6 +18,7 @@ from .step_mixins import AlertGroupActionsMixin
|
|||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
|
||||
class OpenAlertAppearanceDialogStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
||||
|
|
@ -27,7 +28,8 @@ class OpenAlertAppearanceDialogStep(AlertGroupActionsMixin, scenario_step.Scenar
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
|
|
@ -75,7 +77,8 @@ class UpdateAppearanceStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
from apps.alerts.models import AlertGroup
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from apps.slack.types import (
|
|||
PayloadType,
|
||||
ScenarioRoute,
|
||||
)
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
from .step_mixins import AlertGroupActionsMixin
|
||||
|
||||
|
|
@ -28,6 +29,7 @@ class OpenAlertGroupTimelineDialogStep(AlertGroupActionsMixin, scenario_step.Sce
|
|||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from apps.slack.types import BlockActionType, EventPayload, PayloadType, Scenari
|
|||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
|
||||
class DeclareIncidentStep(scenario_step.ScenarioStep):
|
||||
|
|
@ -12,7 +13,8 @@ class DeclareIncidentStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Slack sends a POST request to the backend upon clicking a button with a redirect link to Incident.
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ from .step_mixins import AlertGroupActionsMixin
|
|||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
ATTACH_TO_ALERT_GROUPS_LIMIT = 20
|
||||
|
||||
|
|
@ -222,7 +223,8 @@ class AlertShootingStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
|
@ -239,7 +241,8 @@ class InviteOtherPersonToIncident(AlertGroupActionsMixin, scenario_step.Scenario
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
from apps.user_management.models import User
|
||||
|
||||
|
|
@ -275,7 +278,8 @@ class SilenceGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
|
|
@ -304,7 +308,8 @@ class UnSilenceGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
|
|
@ -324,7 +329,8 @@ class SelectAttachGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
|
|
@ -490,7 +496,8 @@ class AttachGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
# submit selection in modal window
|
||||
if payload["type"] == PayloadType.VIEW_SUBMISSION:
|
||||
|
|
@ -542,7 +549,8 @@ class UnAttachGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
|
|
@ -567,7 +575,8 @@ class StopInvitationProcess(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
|
|
@ -594,7 +603,8 @@ class ResolveGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
ResolutionNoteModalStep = scenario_step.ScenarioStep.get_step("resolution_note", "ResolutionNoteModalStep")
|
||||
|
||||
|
|
@ -635,7 +645,8 @@ class UnResolveGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
|
|
@ -655,7 +666,8 @@ class AcknowledgeGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
|
|
@ -675,7 +687,8 @@ class UnAcknowledgeGroupStep(AlertGroupActionsMixin, scenario_step.ScenarioStep)
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
|
|
@ -736,7 +749,8 @@ class AcknowledgeConfirmationStep(AcknowledgeGroupStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
from apps.alerts.models import AlertGroup
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from apps.slack.types import EventPayload, EventType, PayloadType, ScenarioRoute
|
|||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
|
@ -19,7 +20,8 @@ class InvitedToChannelStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
if payload["event"]["user"] == slack_team_identity.bot_user_id:
|
||||
channel_id = payload["event"]["channel"]
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ from apps.slack.types import Block, BlockActionType, EventPayload, ModalView, Pa
|
|||
if typing.TYPE_CHECKING:
|
||||
from apps.alerts.models import AlertGroup
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import User
|
||||
from apps.user_management.models import Organization, User
|
||||
|
||||
|
||||
MANAGE_RESPONDERS_USER_SELECT_ID = "responders_user_select"
|
||||
|
||||
|
|
@ -35,7 +36,8 @@ class StartManageResponders(AlertGroupActionsMixin, scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = self.get_alert_group(slack_team_identity, payload)
|
||||
if not self.is_authorized(alert_group):
|
||||
|
|
@ -53,7 +55,8 @@ class ManageRespondersUserChange(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = _get_alert_group_from_payload(payload)
|
||||
selected_user = _get_selected_user_from_payload(payload)
|
||||
|
|
@ -99,7 +102,8 @@ class ManageRespondersConfirmUserChange(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = _get_alert_group_from_payload(payload)
|
||||
selected_user = _get_selected_user_from_payload(payload)
|
||||
|
|
@ -132,7 +136,8 @@ class ManageRespondersRemoveUser(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
alert_group = _get_alert_group_from_payload(payload)
|
||||
selected_user = _get_selected_user_from_payload(payload)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from apps.slack.types import BlockActionType, EventPayload, PayloadType, Scenari
|
|||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -20,7 +21,8 @@ class NotifiedUserNotInChannelStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
logger.info("Gracefully handle NotifiedUserNotInChannelStep. Do nothing.")
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from apps.slack.types import EventPayload, EventType, PayloadType, ScenarioRoute
|
|||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -19,7 +20,8 @@ class ImOpenStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
logger.info("InOpenStep, doing nothing.")
|
||||
|
||||
|
|
@ -29,7 +31,8 @@ class AppHomeOpenedStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,8 @@ class StartDirectPaging(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
input_id_prefix = _generate_input_id_prefix()
|
||||
|
||||
|
|
@ -136,6 +137,11 @@ class StartDirectPaging(scenario_step.ScenarioStep):
|
|||
"submit_routing_uid": FinishDirectPaging.routing_uid(),
|
||||
DataKey.USERS: {},
|
||||
}
|
||||
# We have access to predefined org only in StartDirectPaging, since it's a slash command.
|
||||
# Chatops-Proxy adds a special header to slash commands payload to define the organization.
|
||||
# Other Paging steps are triggered by buttons and actions,
|
||||
# so we don't have access to predefined org and use private metadata instead.
|
||||
private_metadata = _inject_predefined_org_to_private_metadata(predefined_org, private_metadata)
|
||||
initial_payload = {"view": {"private_metadata": json.dumps(private_metadata)}}
|
||||
view = render_dialog(slack_user_identity, slack_team_identity, initial_payload, initial=True)
|
||||
self._slack_client.views_open(
|
||||
|
|
@ -153,13 +159,15 @@ class FinishDirectPaging(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
message = _get_message_from_payload(payload)
|
||||
private_metadata = json.loads(payload["view"]["private_metadata"])
|
||||
predefined_org = _get_predefined_org_from_private_metadata(private_metadata, slack_team_identity)
|
||||
channel_id = private_metadata["channel_id"]
|
||||
input_id_prefix = private_metadata["input_id_prefix"]
|
||||
selected_organization = _get_selected_org_from_payload(
|
||||
selected_organization = predefined_org or _get_selected_org_from_payload(
|
||||
payload, input_id_prefix, slack_team_identity, slack_user_identity
|
||||
)
|
||||
|
||||
|
|
@ -245,7 +253,8 @@ class OnPagingOrgChange(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
updated_payload = reset_items(payload)
|
||||
view = render_dialog(slack_user_identity, slack_team_identity, updated_payload)
|
||||
|
|
@ -263,7 +272,8 @@ class OnPagingTeamChange(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
view = render_dialog(slack_user_identity, slack_team_identity, payload)
|
||||
self._slack_client.views_update(
|
||||
|
|
@ -283,7 +293,8 @@ class OnPagingUserChange(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
private_metadata = json.loads(payload["view"]["private_metadata"])
|
||||
selected_user = _get_selected_user_from_payload(payload, private_metadata["input_id_prefix"])
|
||||
|
|
@ -336,7 +347,8 @@ class OnPagingItemActionChange(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
policy, key, user_pk = self._parse_action(payload)
|
||||
|
||||
|
|
@ -361,7 +373,8 @@ class OnPagingConfirmUserChange(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
metadata = json.loads(payload["view"]["private_metadata"])
|
||||
|
||||
|
|
@ -409,7 +422,7 @@ def render_dialog(
|
|||
private_metadata = json.loads(payload["view"]["private_metadata"])
|
||||
submit_routing_uid = private_metadata.get("submit_routing_uid")
|
||||
|
||||
# Get organizations available to user
|
||||
predefined_org = _get_predefined_org_from_private_metadata(private_metadata, slack_team_identity)
|
||||
available_organizations = _get_available_organizations(slack_team_identity, slack_user_identity)
|
||||
|
||||
if initial:
|
||||
|
|
@ -417,15 +430,17 @@ def render_dialog(
|
|||
new_input_id_prefix = _generate_input_id_prefix()
|
||||
new_private_metadata = private_metadata
|
||||
new_private_metadata["input_id_prefix"] = new_input_id_prefix
|
||||
selected_organization = available_organizations.first()
|
||||
selected_organization = predefined_org if predefined_org else available_organizations.first()
|
||||
is_team_selected, selected_team = False, None
|
||||
else:
|
||||
# setup form using data/state
|
||||
old_input_id_prefix, new_input_id_prefix, new_private_metadata = _get_and_change_input_id_prefix_from_metadata(
|
||||
private_metadata
|
||||
)
|
||||
selected_organization = _get_selected_org_from_payload(
|
||||
payload, old_input_id_prefix, slack_team_identity, slack_user_identity
|
||||
selected_organization = (
|
||||
predefined_org
|
||||
if predefined_org
|
||||
else _get_selected_org_from_payload(payload, old_input_id_prefix, slack_team_identity, slack_user_identity)
|
||||
)
|
||||
is_team_selected, selected_team = _get_selected_team_from_payload(payload, old_input_id_prefix)
|
||||
|
||||
|
|
@ -441,8 +456,9 @@ def render_dialog(
|
|||
|
||||
blocks.append(_get_message_input(payload))
|
||||
|
||||
# Add organization select if more than one organization available for user
|
||||
if len(available_organizations) > 1:
|
||||
# Add organization select if org is not defined on chatops-proxy (it's should happen only in OSS)
|
||||
# and user has access to multiple orgs.
|
||||
if not predefined_org and len(available_organizations) > 1:
|
||||
organization_select = _get_organization_select(
|
||||
available_organizations, selected_organization, new_input_id_prefix
|
||||
)
|
||||
|
|
@ -570,6 +586,32 @@ def _get_selected_org_from_payload(
|
|||
return Organization.objects.filter(pk=selected_org_id).first()
|
||||
|
||||
|
||||
def _inject_predefined_org_to_private_metadata(
|
||||
predefined_org: typing.Optional["Organization"], private_metadata: dict
|
||||
) -> dict:
|
||||
"""
|
||||
Injects predefined organization to private metadata.
|
||||
Predefined org is org defined by chatops-proxy for slash commands.
|
||||
"""
|
||||
if predefined_org:
|
||||
private_metadata["organization_id"] = predefined_org.pk
|
||||
return private_metadata
|
||||
|
||||
|
||||
def _get_predefined_org_from_private_metadata(
|
||||
private_metadata: dict,
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
) -> typing.Optional["Organization"]:
|
||||
"""
|
||||
Returns organization from private metadata.
|
||||
"""
|
||||
org_id = private_metadata.get("organization_id")
|
||||
if not org_id:
|
||||
return None
|
||||
|
||||
return slack_team_identity.organizations.filter(pk=org_id).first()
|
||||
|
||||
|
||||
def _get_team_select_blocks(
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
organization: "Organization",
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ from apps.slack.types import EventPayload, EventType, PayloadType, ScenarioRoute
|
|||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
|
||||
class ProfileUpdateStep(scenario_step.ScenarioStep):
|
||||
|
|
@ -13,7 +14,8 @@ class ProfileUpdateStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Triggered by action: Any update in Slack Profile.
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ from .step_mixins import AlertGroupActionsMixin
|
|||
if typing.TYPE_CHECKING:
|
||||
from apps.alerts.models import AlertGroup, ResolutionNote, ResolutionNoteSlackMessage
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
|
@ -65,7 +67,8 @@ class AddToResolutionNoteStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
from apps.alerts.models import ResolutionNote, ResolutionNoteSlackMessage
|
||||
from apps.slack.models import SlackMessage, SlackUserIdentity
|
||||
|
|
@ -357,7 +360,8 @@ class ResolutionNoteModalStep(AlertGroupActionsMixin, scenario_step.ScenarioStep
|
|||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
data: ScenarioData | None = None,
|
||||
# TODO: data is incompatible override, parent class has a different set of arguments
|
||||
data: ScenarioData | None = None, # type: ignore
|
||||
) -> None:
|
||||
if data:
|
||||
# Argument "data" is used when step is called from other step, e.g. AddRemoveThreadMessageStep
|
||||
|
|
@ -642,7 +646,8 @@ class AddRemoveThreadMessageStep(UpdateResolutionNoteStep, scenario_step.Scenari
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
from apps.alerts.models import AlertGroup, ResolutionNote, ResolutionNoteSlackMessage
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,18 @@ class ScenarioStep(object):
|
|||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
"""
|
||||
process_scenario executes the logic of the step on slack interaction.
|
||||
Args:
|
||||
slack_user_identity: SlackUserIdentity who interacted with slack
|
||||
slack_team_identity: Slack Workspace where interaction happened
|
||||
payload: EventPayload from slack
|
||||
predefined_org:
|
||||
Organization where interaction happened.
|
||||
It's optionally defined by chatops-proxy for slash commands and should be used only in SlashCommands steps
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from common.insight_log import EntityEvent, write_resource_insight_log
|
|||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
|
||||
class EditScheduleShiftNotifyStep(scenario_step.ScenarioStep):
|
||||
|
|
@ -32,7 +33,8 @@ class EditScheduleShiftNotifyStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
action_type = payload["actions"][0]["type"]
|
||||
if action_type == BlockActionType.BUTTON:
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from apps.slack.utils import SlackDateFormat, format_datetime_to_slack, format_d
|
|||
if typing.TYPE_CHECKING:
|
||||
from apps.schedules.models import ShiftSwapRequest
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
|
@ -197,7 +198,8 @@ class AcceptShiftSwapRequestStep(BaseShiftSwapRequestStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
from apps.schedules import exceptions
|
||||
from apps.schedules.models import ShiftSwapRequest
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from apps.slack.types import EventPayload, EventType, PayloadType, ScenarioRoute
|
|||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
|
||||
class SlackChannelCreatedOrRenamedEventStep(scenario_step.ScenarioStep):
|
||||
|
|
@ -16,7 +17,8 @@ class SlackChannelCreatedOrRenamedEventStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Triggered by action: Create or rename channel
|
||||
|
|
@ -41,7 +43,8 @@ class SlackChannelDeletedEventStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Triggered by action: Delete channel
|
||||
|
|
@ -63,7 +66,8 @@ class SlackChannelArchivedEventStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Triggered by action: Archive channel
|
||||
|
|
@ -84,7 +88,8 @@ class SlackChannelUnArchivedEventStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Triggered by action: UnArchive channel
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from apps.slack.types import EventPayload, EventType, MessageEventSubtype, Paylo
|
|||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
|
@ -17,7 +18,8 @@ class SlackChannelMessageEventStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Triggered by action: Any new message in channel.
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from apps.slack.types import EventPayload, EventType, PayloadType, ScenarioRoute
|
|||
|
||||
if typing.TYPE_CHECKING:
|
||||
from apps.slack.models import SlackTeamIdentity, SlackUserIdentity
|
||||
from apps.user_management.models import Organization
|
||||
|
||||
|
||||
class SlackUserGroupEventStep(scenario_step.ScenarioStep):
|
||||
|
|
@ -14,7 +15,8 @@ class SlackUserGroupEventStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Triggered by action: creation user groups or changes in user groups except its members.
|
||||
|
|
@ -45,7 +47,8 @@ class SlackUserGroupMembersChangedEventStep(scenario_step.ScenarioStep):
|
|||
self,
|
||||
slack_user_identity: "SlackUserIdentity",
|
||||
slack_team_identity: "SlackTeamIdentity",
|
||||
payload: EventPayload,
|
||||
payload: "EventPayload",
|
||||
predefined_org: typing.Optional["Organization"] = None,
|
||||
) -> None:
|
||||
"""
|
||||
Triggered by action: changed members in user group.
|
||||
|
|
|
|||
|
|
@ -29,15 +29,18 @@ SLACK_BOT_USER_ID = "mncvnmvcmnvcmncv,,cx,"
|
|||
SLACK_USER_ID = "iurtiurituritu"
|
||||
|
||||
|
||||
def _make_request(payload):
|
||||
def _make_request(payload, predefined_org=None):
|
||||
headers = {
|
||||
"HTTP_X_SLACK_SIGNATURE": "asdfasdf",
|
||||
"HTTP_X_SLACK_REQUEST_TIMESTAMP": "xxcxcvx",
|
||||
}
|
||||
if predefined_org:
|
||||
headers["HTTP_X_CHATOPS_STACK_ID"] = predefined_org.stack_id
|
||||
return APIClient().post(
|
||||
"/slack/interactive_api_endpoint/",
|
||||
format="json",
|
||||
data=payload,
|
||||
**{
|
||||
"HTTP_X_SLACK_SIGNATURE": "asdfasdf",
|
||||
"HTTP_X_SLACK_REQUEST_TIMESTAMP": "xxcxcvx",
|
||||
},
|
||||
**headers,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -312,7 +315,52 @@ def test_grafana_escalate(
|
|||
response = _make_request(payload)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
mock_process_scenario.assert_called_once_with(slack_user_identity, slack_team_identity, payload)
|
||||
mock_process_scenario.assert_called_once_with(
|
||||
slack_user_identity, slack_team_identity, payload, predefined_org=None
|
||||
)
|
||||
|
||||
|
||||
@patch("apps.slack.views.SlackEventApiEndpointView.verify_signature", return_value=True)
|
||||
@patch.object(StartDirectPaging, "process_scenario")
|
||||
@pytest.mark.django_db
|
||||
def test_grafana_escalate_with_org_from_chatops_proxy_defines_org(
|
||||
mock_process_scenario,
|
||||
_mock_verify_signature,
|
||||
make_organization,
|
||||
make_slack_user_identity,
|
||||
make_user,
|
||||
slack_team_identity,
|
||||
):
|
||||
"""
|
||||
Check StartDirectPaging.process_scenario gets called when a user types /grafana escalate.
|
||||
UnifiedSlackApp commands are prefixed with /grafana.
|
||||
"""
|
||||
organization = make_organization(slack_team_identity=slack_team_identity)
|
||||
slack_user_identity = make_slack_user_identity(slack_team_identity=slack_team_identity, slack_id=SLACK_USER_ID)
|
||||
make_user(organization=organization, slack_user_identity=slack_user_identity)
|
||||
|
||||
payload = {
|
||||
"token": "gIkuvaNzQIHg97ATvDxqgjtO",
|
||||
"team_id": slack_team_identity.slack_id,
|
||||
"team_domain": "example",
|
||||
"enterprise_id": "E0001",
|
||||
"enterprise_name": "Globular%20Construct%20Inc",
|
||||
"channel_id": "C2147483705",
|
||||
"channel_name": "test",
|
||||
"user_id": slack_user_identity.slack_id,
|
||||
"user_name": "Steve",
|
||||
"command": "/grafana",
|
||||
"text": "escalate",
|
||||
"response_url": "https://hooks.slack.com/commands/1234/5678",
|
||||
"trigger_id": "13345224609.738474920.8088930838d88f008e0",
|
||||
"api": "api_value",
|
||||
}
|
||||
response = _make_request(payload, predefined_org=organization)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
mock_process_scenario.assert_called_once_with(
|
||||
slack_user_identity, slack_team_identity, payload, predefined_org=organization
|
||||
)
|
||||
|
||||
|
||||
@patch("apps.slack.views.SlackEventApiEndpointView.verify_signature", return_value=True)
|
||||
|
|
@ -353,4 +401,6 @@ def test_escalate(
|
|||
response = _make_request(payload)
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
mock_process_scenario.assert_called_once_with(slack_user_identity, slack_team_identity, payload)
|
||||
mock_process_scenario.assert_called_once_with(
|
||||
slack_user_identity, slack_team_identity, payload, predefined_org=None
|
||||
)
|
||||
|
|
|
|||
|
|
@ -27,26 +27,43 @@ from apps.slack.scenarios.paging import (
|
|||
from apps.user_management.models import Organization
|
||||
|
||||
|
||||
def make_slack_payload(organization, team=None, user=None, current_users=None, actions=None):
|
||||
def make_paging_view_slack_payload(
|
||||
selected_org=None, predefined_org=None, team=None, user=None, current_users=None, actions=None
|
||||
):
|
||||
"""
|
||||
Helper function to create a payload for paging view.
|
||||
Args:
|
||||
selected_org: selected organization
|
||||
predefined_org: predefined organization parsed from chatops-proxy headers
|
||||
team: selected team object.
|
||||
user: selected user object.
|
||||
current_users: Dictionary of current users.
|
||||
actions: List of actions.
|
||||
"""
|
||||
organization = selected_org or predefined_org
|
||||
if organization is None:
|
||||
raise Exception("either selected or predifined org must be defined")
|
||||
private_metadata = {
|
||||
"input_id_prefix": "",
|
||||
"channel_id": "123",
|
||||
"submit_routing_uid": "FinishStepUID",
|
||||
DataKey.USERS: current_users or {},
|
||||
}
|
||||
if predefined_org:
|
||||
private_metadata["organization_id"] = str(predefined_org.pk)
|
||||
payload = {
|
||||
"channel_id": "123",
|
||||
"trigger_id": "111",
|
||||
"view": {
|
||||
"id": "view-id",
|
||||
"private_metadata": make_private_metadata(
|
||||
{
|
||||
"input_id_prefix": "",
|
||||
"channel_id": "123",
|
||||
"submit_routing_uid": "FinishStepUID",
|
||||
DataKey.USERS: current_users or {},
|
||||
},
|
||||
organization,
|
||||
),
|
||||
"private_metadata": make_private_metadata(private_metadata, organization),
|
||||
"state": {
|
||||
"values": {
|
||||
DIRECT_PAGING_ORG_SELECT_ID: {
|
||||
OnPagingOrgChange.routing_uid(): {
|
||||
"selected_option": {"value": make_value({"id": organization.pk}, organization)}
|
||||
"selected_option": {
|
||||
"value": make_value({"id": organization.pk if selected_org else None}, organization)
|
||||
}
|
||||
}
|
||||
},
|
||||
DIRECT_PAGING_TEAM_SELECT_ID: {
|
||||
|
|
@ -84,6 +101,50 @@ def test_initial_state(
|
|||
assert metadata[DataKey.USERS] == {}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_org_predefined(
|
||||
make_organization_and_user_with_slack_identities,
|
||||
):
|
||||
"""
|
||||
See get_org_from_chatops_proxy_header function.
|
||||
"""
|
||||
org, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
|
||||
payload = {"channel_id": "123", "trigger_id": "111"}
|
||||
|
||||
step = StartDirectPaging(slack_team_identity, user=user)
|
||||
with patch.object(step._slack_client, "views_open") as mock_slack_api_call:
|
||||
step.process_scenario(slack_user_identity, slack_team_identity, payload, predefined_org=org)
|
||||
|
||||
view = mock_slack_api_call.call_args.kwargs["view"]
|
||||
metadata = json.loads(view["private_metadata"])
|
||||
# Test that organization is injected to private metadata if it is defined by chatops-proxy.
|
||||
assert metadata["organization_id"] == org.pk
|
||||
# Test that organization select is not present if org defined by chatops-proxy.
|
||||
for block in view["blocks"]:
|
||||
if block.get("block_id") == DIRECT_PAGING_ORG_SELECT_ID:
|
||||
raise AssertionError("Organization select block should not be present in the view")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_page_team_with_predefined_org(make_organization_and_user_with_slack_identities, make_team):
|
||||
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
|
||||
team = make_team(organization)
|
||||
payload = make_paging_view_slack_payload(predefined_org=organization, team=team)
|
||||
|
||||
step = FinishDirectPaging(slack_team_identity)
|
||||
with patch("apps.slack.scenarios.paging.direct_paging") as mock_direct_paging:
|
||||
with patch.object(step._slack_client, "api_call"):
|
||||
step.process_scenario(slack_user_identity, slack_team_identity, payload)
|
||||
|
||||
mock_direct_paging.assert_called_once_with(
|
||||
organization=organization,
|
||||
from_user=user,
|
||||
message="The Message",
|
||||
team=team,
|
||||
users=[],
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("role", (LegacyAccessControlRole.VIEWER, LegacyAccessControlRole.NONE))
|
||||
@pytest.mark.django_db
|
||||
def test_initial_unauthorized(make_organization_and_user_with_slack_identities, role):
|
||||
|
|
@ -126,7 +187,7 @@ def test_add_user_no_warning(make_organization_and_user_with_slack_identities, m
|
|||
on_call_shift.add_rolling_users([[user]])
|
||||
schedule.refresh_ical_file()
|
||||
|
||||
payload = make_slack_payload(organization=organization, user=user)
|
||||
payload = make_paging_view_slack_payload(selected_org=organization, user=user)
|
||||
|
||||
step = OnPagingUserChange(slack_team_identity)
|
||||
with patch.object(step._slack_client, "views_update") as mock_slack_api_call:
|
||||
|
|
@ -161,7 +222,7 @@ def test_add_user_maximum_exceeded(make_organization_and_user_with_slack_identit
|
|||
on_call_shift.add_rolling_users([[user]])
|
||||
schedule.refresh_ical_file()
|
||||
|
||||
payload = make_slack_payload(organization=organization, user=user)
|
||||
payload = make_paging_view_slack_payload(selected_org=organization, user=user)
|
||||
|
||||
step = OnPagingUserChange(slack_team_identity)
|
||||
with patch("apps.slack.scenarios.paging.PRIVATE_METADATA_MAX_LENGTH", 100):
|
||||
|
|
@ -188,7 +249,7 @@ def test_add_user_maximum_exceeded(make_organization_and_user_with_slack_identit
|
|||
def test_add_user_raise_warning(make_organization_and_user_with_slack_identities):
|
||||
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
|
||||
# user is not on call
|
||||
payload = make_slack_payload(organization=organization, user=user)
|
||||
payload = make_paging_view_slack_payload(selected_org=organization, user=user)
|
||||
|
||||
step = OnPagingUserChange(slack_team_identity)
|
||||
with patch.object(step._slack_client, "views_push") as mock_slack_api_call:
|
||||
|
|
@ -209,8 +270,8 @@ def test_add_user_raise_warning(make_organization_and_user_with_slack_identities
|
|||
@pytest.mark.django_db
|
||||
def test_change_user_policy(make_organization_and_user_with_slack_identities):
|
||||
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
|
||||
payload = make_slack_payload(
|
||||
organization=organization,
|
||||
payload = make_paging_view_slack_payload(
|
||||
selected_org=organization,
|
||||
actions=[
|
||||
{
|
||||
"selected_option": {
|
||||
|
|
@ -231,8 +292,8 @@ def test_change_user_policy(make_organization_and_user_with_slack_identities):
|
|||
@pytest.mark.django_db
|
||||
def test_remove_user(make_organization_and_user_with_slack_identities):
|
||||
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
|
||||
payload = make_slack_payload(
|
||||
organization=organization,
|
||||
payload = make_paging_view_slack_payload(
|
||||
selected_org=organization,
|
||||
actions=[
|
||||
{
|
||||
"selected_option": {
|
||||
|
|
@ -255,7 +316,7 @@ def test_remove_user(make_organization_and_user_with_slack_identities):
|
|||
@pytest.mark.django_db
|
||||
def test_trigger_paging_no_team_or_user_selected(make_organization_and_user_with_slack_identities):
|
||||
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
|
||||
payload = make_slack_payload(organization=organization)
|
||||
payload = make_paging_view_slack_payload(selected_org=organization)
|
||||
|
||||
step = FinishDirectPaging(slack_team_identity, user=user)
|
||||
|
||||
|
|
@ -277,7 +338,7 @@ def test_trigger_paging_unauthorized(make_organization_and_user_with_slack_ident
|
|||
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities(
|
||||
role=role
|
||||
)
|
||||
payload = make_slack_payload(organization=organization)
|
||||
payload = make_paging_view_slack_payload(selected_org=organization)
|
||||
|
||||
step = FinishDirectPaging(slack_team_identity)
|
||||
with patch.object(step._slack_client, "api_call"):
|
||||
|
|
@ -294,7 +355,9 @@ def test_trigger_paging_unauthorized(make_organization_and_user_with_slack_ident
|
|||
def test_trigger_paging_additional_responders(make_organization_and_user_with_slack_identities, make_team):
|
||||
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
|
||||
team = make_team(organization)
|
||||
payload = make_slack_payload(organization=organization, team=team, current_users={str(user.pk): Policy.IMPORTANT})
|
||||
payload = make_paging_view_slack_payload(
|
||||
selected_org=organization, team=team, current_users={str(user.pk): Policy.IMPORTANT}
|
||||
)
|
||||
|
||||
step = FinishDirectPaging(slack_team_identity)
|
||||
with patch("apps.slack.scenarios.paging.direct_paging") as mock_direct_paging:
|
||||
|
|
@ -314,7 +377,7 @@ def test_trigger_paging_additional_responders(make_organization_and_user_with_sl
|
|||
def test_page_team(make_organization_and_user_with_slack_identities, make_team):
|
||||
organization, user, slack_team_identity, slack_user_identity = make_organization_and_user_with_slack_identities()
|
||||
team = make_team(organization)
|
||||
payload = make_slack_payload(organization=organization, team=team)
|
||||
payload = make_paging_view_slack_payload(selected_org=organization, team=team)
|
||||
|
||||
step = FinishDirectPaging(slack_team_identity)
|
||||
with patch("apps.slack.scenarios.paging.direct_paging") as mock_direct_paging:
|
||||
|
|
|
|||
|
|
@ -365,7 +365,8 @@ class SlackEventApiEndpointView(APIView):
|
|||
Step = route["step"]
|
||||
logger.info("Routing to {}".format(Step))
|
||||
step = Step(slack_team_identity, organization, user)
|
||||
step.process_scenario(slack_user_identity, slack_team_identity, payload)
|
||||
org = get_org_from_chatops_proxy_header(request, slack_team_identity)
|
||||
step.process_scenario(slack_user_identity, slack_team_identity, payload, predefined_org=org)
|
||||
step_was_found = True
|
||||
|
||||
if payload_type == route_payload_type:
|
||||
|
|
@ -592,3 +593,19 @@ class ResetSlackView(APIView):
|
|||
return Response({"error": e.error_message}, status=400)
|
||||
|
||||
return Response(status=200)
|
||||
|
||||
|
||||
def get_org_from_chatops_proxy_header(request, slack_team_identity) -> Organization | None:
|
||||
"""
|
||||
get_org_from_chatops_proxy_header extracts organization from the X-Chatops-Stack-ID header injected by chatops-proxy
|
||||
"""
|
||||
stack_id = request.META.get("HTTP_X_CHATOPS_STACK_ID")
|
||||
if not stack_id:
|
||||
return None
|
||||
|
||||
try:
|
||||
# get only orgs linked to the slack workspace to avoid tampering
|
||||
return slack_team_identity.organizations.get(stack_id=stack_id)
|
||||
except Organization.DoesNotExist:
|
||||
logger.info(f"SlackEventApiEndpointView: get_org_from_header: organization with stack_id {stack_id} not found")
|
||||
return None
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue