Unified Slack app reinstall (#4682)
# What this PR does Adds a button to reinstall the Slack app to migrate to Unified Slack App. Also adds backend support for this. <img width="1204" alt="Screenshot 2024-07-18 at 18 33 08" src="https://github.com/user-attachments/assets/a326b4a2-fc65-4b88-98c0-2955e3717e3a"> ## Which issue(s) this PR closes Related to https://github.com/grafana/oncall-gateway/issues/252 <!-- *Note*: if you have more than one GitHub issue that this PR closes, be sure to preface each issue link with a [closing keyword](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/using-keywords-in-issues-and-pull-requests#linking-a-pull-request-to-an-issue). This ensures that the issue(s) are auto-closed once the PR has been merged. --> ## 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] Added the relevant release notes label (see labels prefixed w/ `release:`). These labels dictate how your PR will show up in the autogenerated release notes.
This commit is contained in:
parent
1465db36e5
commit
87d7982250
7 changed files with 68 additions and 7 deletions
|
|
@ -13,7 +13,7 @@ from common.api_helpers.mixins import EagerLoadingMixin
|
|||
class FastSlackTeamIdentitySerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = SlackTeamIdentity
|
||||
fields = ["cached_name"]
|
||||
fields = ["cached_name", "needs_reinstall"]
|
||||
|
||||
|
||||
class OrganizationSerializer(EagerLoadingMixin, serializers.ModelSerializer):
|
||||
|
|
|
|||
|
|
@ -32,13 +32,11 @@ def install_slack_integration(organization, user, oauth_response):
|
|||
"""
|
||||
from apps.slack.models import SlackTeamIdentity
|
||||
|
||||
if organization.slack_team_identity is not None:
|
||||
if organization.slack_team_identity and not organization.slack_team_identity.needs_reinstall:
|
||||
raise SlackInstallationExc("Organization already has Slack integration")
|
||||
|
||||
slack_team_id = oauth_response["team"]["id"]
|
||||
slack_team_identity, is_slack_team_identity_created = SlackTeamIdentity.objects.get_or_create(
|
||||
slack_id=slack_team_id,
|
||||
)
|
||||
slack_team_identity, _ = SlackTeamIdentity.objects.get_or_create(slack_id=slack_team_id)
|
||||
# update slack oauth fields by data from response
|
||||
slack_team_identity.update_oauth_fields(user, organization, oauth_response)
|
||||
write_chatops_insight_log(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 4.2.11 on 2024-07-16 16:11
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('slack', '0004_auto_20230913_1020'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='slackteamidentity',
|
||||
name='_unified_slack_app_installed',
|
||||
field=models.BooleanField(default=False, null=True),
|
||||
),
|
||||
]
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
import logging
|
||||
import typing
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.db.models import JSONField
|
||||
|
||||
|
|
@ -48,6 +49,9 @@ class SlackTeamIdentity(models.Model):
|
|||
# response after oauth.access. This field is used to reinstall app to another OnCall workspace
|
||||
cached_reinstall_data = JSONField(null=True, default=None)
|
||||
|
||||
# Do not use directly, use the "needs_reinstall" property instead
|
||||
_unified_slack_app_installed = models.BooleanField(null=True, default=False)
|
||||
|
||||
class Meta:
|
||||
ordering = ("datetime",)
|
||||
|
||||
|
|
@ -74,6 +78,10 @@ class SlackTeamIdentity(models.Model):
|
|||
self.installed_by = slack_user_identity
|
||||
self.cached_reinstall_data = None
|
||||
self.installed_via_granular_permissions = True
|
||||
|
||||
if settings.UNIFIED_SLACK_APP_ENABLED:
|
||||
self._unified_slack_app_installed = True
|
||||
|
||||
self.save()
|
||||
|
||||
def get_cached_channels(self, search_term=None, slack_id=None):
|
||||
|
|
@ -129,6 +137,10 @@ class SlackTeamIdentity(models.Model):
|
|||
self.save(update_fields=["cached_app_id"])
|
||||
return self.cached_app_id
|
||||
|
||||
@property
|
||||
def needs_reinstall(self):
|
||||
return settings.UNIFIED_SLACK_APP_ENABLED and not self._unified_slack_app_installed
|
||||
|
||||
def get_users_from_slack_conversation_for_organization(self, channel_id, organization):
|
||||
sc = SlackClient(self)
|
||||
members = self.get_conversation_members(sc, channel_id)
|
||||
|
|
|
|||
|
|
@ -95,6 +95,26 @@ def test_install_slack_integration_raises_exception_for_existing_integration(
|
|||
install_slack_integration(organization, user, SLACK_OAUTH_ACCESS_RESPONSE)
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_install_slack_integration_legacy(settings, make_organization_and_user, make_slack_team_identity):
|
||||
settings.UNIFIED_SLACK_APP_ENABLED = True
|
||||
|
||||
slack_team_identity = make_slack_team_identity(
|
||||
slack_id=SLACK_OAUTH_ACCESS_RESPONSE["team"]["id"], _unified_slack_app_installed=False
|
||||
)
|
||||
organization, user = make_organization_and_user()
|
||||
organization.slack_team_identity = slack_team_identity
|
||||
organization.save()
|
||||
|
||||
install_slack_integration(organization, user, SLACK_OAUTH_ACCESS_RESPONSE)
|
||||
slack_team_identity.refresh_from_db()
|
||||
assert slack_team_identity.needs_reinstall is False
|
||||
|
||||
# raises exception if organization already re-installed the app
|
||||
with pytest.raises(SlackInstallationExc):
|
||||
install_slack_integration(organization, user, SLACK_OAUTH_ACCESS_RESPONSE)
|
||||
|
||||
|
||||
@patch("apps.slack.tasks.clean_slack_integration_leftovers.apply_async", return_value=None)
|
||||
@pytest.mark.django_db
|
||||
def test_uninstall_slack_integration(
|
||||
|
|
|
|||
|
|
@ -15,9 +15,8 @@ export interface Organization {
|
|||
name: string;
|
||||
stack_slug: string;
|
||||
slack_team_identity: {
|
||||
general_log_channel_id: string;
|
||||
general_log_channel_pk: string;
|
||||
cached_name: string;
|
||||
needs_reinstall: boolean;
|
||||
};
|
||||
slack_channel: SlackChannel | null;
|
||||
is_resolution_note_required: boolean;
|
||||
|
|
|
|||
|
|
@ -201,6 +201,20 @@ class _SlackSettings extends Component<SlackProps, SlackState> {
|
|||
</WithPermissionControlTooltip>
|
||||
</HorizontalGroup>
|
||||
</InlineField>
|
||||
{currentOrganization.slack_team_identity.needs_reinstall && (
|
||||
<>
|
||||
<Legend>Unified Slack App</Legend>
|
||||
<InlineField>
|
||||
<WithPermissionControlTooltip userAction={UserActions.ChatOpsUpdateSettings}>
|
||||
<Button onClick={this.handleOpenSlackInstructions}>
|
||||
<HorizontalGroup spacing="xs" align="center">
|
||||
<Icon name="external-link-alt" className={cx('external-link-style')} /> Reinstall Slack App
|
||||
</HorizontalGroup>
|
||||
</Button>
|
||||
</WithPermissionControlTooltip>
|
||||
</InlineField>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue