Skip to content

Commit

Permalink
adjust to unified slack (#4776)
Browse files Browse the repository at this point in the history
# What this PR does

Introduce OnCall UI for Unified Slack migration. It's mostly banners and
text adjustments. Changes are behind feature flag.

<!--
*Note*: If you want the issue to be auto-closed once the PR is merged,
change "Related to" to "Closes" in the line above.
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

- [ ] Unit, integration, and e2e (if applicable) tests updated
- [ ] Documentation added (or `pr:no public docs` PR label added if not
required)
- [ ] 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.

---------

Co-authored-by: Innokentii Konstantinov <[email protected]>
  • Loading branch information
brojd and Konstantinov-Innokentii authored Aug 8, 2024
1 parent fbd68b1 commit a332011
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 42 deletions.
4 changes: 4 additions & 0 deletions engine/apps/api/views/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
class Feature(enum.StrEnum):
MSTEAMS = "msteams"
SLACK = "slack"
UNIFIED_SLACK = "unified_slack"
TELEGRAM = "telegram"
LIVE_SETTINGS = "live_settings"
GRAFANA_CLOUD_NOTIFICATIONS = "grafana_cloud_notifications"
Expand Down Expand Up @@ -46,6 +47,9 @@ def _get_enabled_features(self, request):
if settings.FEATURE_SLACK_INTEGRATION_ENABLED:
enabled_features.append(Feature.SLACK)

if settings.UNIFIED_SLACK_APP_ENABLED:
enabled_features.append(Feature.UNIFIED_SLACK)

if settings.FEATURE_TELEGRAM_INTEGRATION_ENABLED:
enabled_features.append(Feature.TELEGRAM)

Expand Down
2 changes: 1 addition & 1 deletion grafana-plugin/src/models/slack/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,10 @@ export class SlackStore extends BaseStore {
window.location = url_for_redirect;
}

@action.bound
async installSlackIntegration() {
try {
const response = await makeRequestRaw('/login/slack-install-free/', {});

if (response.status === 201) {
this.rootStore.organizationStore.loadCurrentOrganization();
} else if (response.status === 200) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,18 @@
.infoblock-icon {
margin-top: 24px;
}

.upgradeSlackBtn {
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
}

.upgradeSlackAlert svg {
display: none;
}

.linkToIncidentWrapper {
margin-top: 16px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import {
InlineField,
Input,
Legend,
ConfirmModal,
} from '@grafana/ui';
import cn from 'classnames/bind';
import { observer } from 'mobx-react';

import { Block } from 'components/GBlock/Block';
import { PluginBridge, SupportedPlugin } from 'components/PluginBridge/PluginBridge';
import { PluginLink } from 'components/PluginLink/PluginLink';
import { Text } from 'components/Text/Text';
import { WithConfirm } from 'components/WithConfirm/WithConfirm';
Expand All @@ -26,9 +28,11 @@ import { PRIVATE_CHANNEL_NAME } from 'models/slack_channel/slack_channel.config'
import { SlackChannel } from 'models/slack_channel/slack_channel.types';
import { AppFeature } from 'state/features';
import { WithStoreProps } from 'state/types';
import { useStore } from 'state/useStore';
import { withMobXProviderContext } from 'state/withStore';
import { UserActions } from 'utils/authorization/authorization';
import { DOCS_SLACK_SETUP, getPluginId } from 'utils/consts';
import { useConfirmModal } from 'utils/hooks';
import { showApiError } from 'utils/utils';

import styles from './SlackSettings.module.css';
Expand Down Expand Up @@ -116,9 +120,12 @@ class _SlackSettings extends Component<SlackProps, SlackState> {
slackChannelStore: { items: slackChannelItems },
} = store;

const isUnifiedSlackInstalled = !currentOrganization.slack_team_identity.needs_reinstall;

return (
<div className={cx('root')}>
<Legend>Slack App settings</Legend>
{currentOrganization.slack_team_identity.needs_reinstall && <UpgradeToUnifiedSlackBanner />}
<InlineField label="Slack Workspace" grow disabled>
<Input value={currentOrganization?.slack_team_identity?.cached_name} />
</InlineField>
Expand Down Expand Up @@ -147,33 +154,58 @@ class _SlackSettings extends Component<SlackProps, SlackState> {
/>
<InlineField>
<WithPermissionControlTooltip userAction={UserActions.ChatOpsUpdateSettings}>
<WithConfirm
title="Remove Slack Integration for all of OnCall"
description={
<Alert severity="error" title="WARNING">
<p>Are you sure to delete this Slack Integration?</p>
<p>
Removing the integration will also irreverisbly remove the following data for your OnCall plugin:
</p>
<ul style={{ marginLeft: '20px' }}>
<li>default organization Slack channel</li>
<li>default Slack channels for OnCall Integrations</li>
<li>Slack channels & Slack user groups for OnCall Schedules</li>
<li>linked Slack usernames for OnCall Users</li>
</ul>
<br />
<p>
If you would like to instead remove your linked Slack username, please head{' '}
<PluginLink query={{ page: 'users/me' }}>here</PluginLink>.
</p>
</Alert>
}
confirmationText="DELETE"
>
<Button variant="destructive" onClick={() => this.removeSlackIntegration()}>
Disconnect Slack App
</Button>
</WithConfirm>
{isUnifiedSlackInstalled ? (
<WithConfirm
title="Remove IRM Slack integration"
description={
<Alert severity="error" title="WARNING">
<p>Are you sure to delete this Slack Integration? It will affect both OnCall & Incident.</p>
<p>Removing the integration will irreverisbly remove the following data for IRM;</p>
<ul style={{ marginLeft: '20px' }}>
<li>OnCall default Slack channel</li>
<li>Slack channels for OnCall escalation policies</li>
<li>Slack channels & Slack user groups for OnCall Schedules</li>
<li>linked Slack usernames for OnCall Users</li>
<li>Incident hooks</li>
</ul>
<br />
</Alert>
}
confirmationText="DELETE"
>
<Button variant="destructive" onClick={() => this.removeSlackIntegration()}>
Disconnect Slack App
</Button>
</WithConfirm>
) : (
<WithConfirm
title="Remove Slack Integration for all of OnCall"
description={
<Alert severity="error" title="WARNING">
<p>Are you sure to delete this Slack Integration?</p>
<p>
Removing the integration will also irreverisbly remove the following data for your OnCall plugin:
</p>
<ul style={{ marginLeft: '20px' }}>
<li>default organization Slack channel</li>
<li>default Slack channels for OnCall Integrations</li>
<li>Slack channels & Slack user groups for OnCall Schedules</li>
<li>linked Slack usernames for OnCall Users</li>
</ul>
<br />
<p>
If you would like to instead remove your linked Slack username, please head{' '}
<PluginLink query={{ page: 'users/me' }}>here</PluginLink>.
</p>
</Alert>
}
confirmationText="DELETE"
>
<Button variant="destructive" onClick={() => this.removeSlackIntegration()}>
Disconnect Slack App
</Button>
</WithConfirm>
)}
</WithPermissionControlTooltip>
</InlineField>
<Legend>Additional settings</Legend>
Expand Down Expand Up @@ -201,19 +233,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>
</>
{isUnifiedSlackInstalled && (
<div className={styles.linkToIncidentWrapper}>
<PluginBridge plugin={SupportedPlugin.Incident}>
<Text type="secondary">
<a
href={`/a/${SupportedPlugin.Incident}/integrations/grate.irm.slack`}
target="_blank"
rel="noreferrer"
>
<Text type="link">Open Slack Incident settings</Text>
</a>
</Text>
</PluginBridge>
</div>
)}
</div>
);
Expand Down Expand Up @@ -251,6 +284,7 @@ class _SlackSettings extends Component<SlackProps, SlackState> {
const { store } = this.props;
const { showENVVariablesButton } = this.state;
const isLiveSettingAvailable = store.hasFeature(AppFeature.LiveSettings) && showENVVariablesButton;
const isUnifiedSlackEnabled = store.hasFeature(AppFeature.UnifiedSlack);

return (
<VerticalGroup spacing="lg">
Expand All @@ -261,7 +295,9 @@ class _SlackSettings extends Component<SlackProps, SlackState> {
<SlackNewIcon />
</div>
<Text className={cx('infoblock-text')}>
Connecting Slack App will allow you to manage alert groups in your team Slack workspace.
{isUnifiedSlackEnabled
? 'Connecting Slack App will allow you to manage alert groups and incidents in your team Slack workspace.'
: 'Connecting Slack App will allow you to manage alert groups in your team Slack workspace.'}
</Text>
<Text className={cx('infoblock-text')}>
After a basic workspace connection your team members need to connect their personal Slack accounts in
Expand Down Expand Up @@ -305,4 +341,61 @@ class _SlackSettings extends Component<SlackProps, SlackState> {
};
}

const UpgradeToUnifiedSlackBanner = observer(() => {
const {
slackStore: { installSlackIntegration },
} = useStore();
const { modalProps, openModal } = useConfirmModal();

return (
<>
<ConfirmModal {...modalProps} />
<Alert
className={styles.upgradeSlackAlert}
severity="success"
title="Upgrade to Grafana IRM unified Slack app"
buttonContent={<div>Upgrade</div>}
>
We've rebranded the OnCall Slack app as the Grafana IRM Slack app, now with incident management features.
<p>Click "Upgrade" to reviewn and approve the new permissions and complete the process.</p>
<p>For more details, check our documentation.</p>
<Button
variant="secondary"
className={styles.upgradeSlackBtn}
onClick={() =>
openModal({
confirmText: 'Confirm',
onConfirm: installSlackIntegration,
confirmButtonVariant: 'primary',
title: `Upgrade to Grafana IRM Slack app`,
description: (
<div>
<p>
You will be redirected to Slack to approve additional permissions for the Grafana IRM Slack app.{' '}
</p>
<p>
These permissions are necessary for incident management. You can view the detailed list of new
permissions here.[LINK]
</p>
<p>After the upgrade, you'll be able to manage incidents in Slack using the Grafana IRM Slack app.</p>
<ul style={{ marginLeft: '20px' }}>
<li>Your OnCall Slack configuration will remain unchanged. </li>
<li>
Your Incident Slack integration will be upgraded to use the Grafana IRM Slack app. Please refer to
the documentation for more details.[LINK]
</li>
</ul>
</div>
),
confirmVariant: 'secondary',
})
}
>
Upgrade
</Button>
</Alert>
</>
);
});

export const SlackSettings = withMobXProviderContext(_SlackSettings);
1 change: 1 addition & 0 deletions grafana-plugin/src/state/features.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export enum AppFeature {
Slack = 'slack',
UnifiedSlack = 'unified_slack',
Telegram = 'telegram',
LiveSettings = 'live_settings',
CloudNotifications = 'grafana_cloud_notifications',
Expand Down

0 comments on commit a332011

Please sign in to comment.