Skip to content

Commit ca5a5f5

Browse files
Merge pull request #4898 from grafana/dev
1.9.10
2 parents 9a81c8b + b5c5225 commit ca5a5f5

File tree

14 files changed

+438
-121
lines changed

14 files changed

+438
-121
lines changed

docs/sources/configure/integrations/references/slack/index.md

+339
Large diffs are not rendered by default.

docs/sources/manage/notify/slack/index.md

+13
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,22 @@ refs:
2626
destination: /docs/oncall/<ONCALL_VERSION>/set-up/open-source/#slack-setup
2727
- pattern: /docs/grafana-cloud/
2828
destination: /docs/grafana-cloud/alerting-and-irm/oncall/set-up/open-source/#slack-setup
29+
irm-slack:
30+
- pattern: /docs/oncall/
31+
destination: /docs/oncall/<ONCALL_VERSION>/configure/integrations/references/slack/
32+
- pattern: /docs/grafana-cloud/
33+
destination: /docs/grafana-cloud/alerting-and-irm/oncall/configure/integrations/references/slack/
2934
---
3035

3136
# Slack integration for Grafana OnCall
3237

38+
{{< admonition type="warning" >}}
39+
The OnCall Slack app is now a Grafana IRM app with new incident management features. Migrate now to access the enhanced capabilities.
40+
41+
Refer to the [Grafana IRM Slack integration](ref:irm-slack) documentation to learn more.
42+
{{< /admonition >}}
43+
44+
{{< collapse title="Legacy Slack integration for Grafana OnCall" >}}
3345
The Slack integration for Grafana OnCall incorporates your Slack workspace directly into your incident response workflow
3446
to help your team focus on alert resolution with less friction.
3547

@@ -210,3 +222,4 @@ Use message shortcuts to add resolution notes directly from Slack. Message short
210222
1. Hover over the message and select **More actions** from the menu options.
211223
1. Select **Add as resolution note**.
212224
1. The Grafana OnCall app will react to the message in Slack with the memo emoji and add the message to the alert group timeline.
225+
{{< /collapse >}}

docs/sources/set-up/get-started/index.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ refs:
3434
destination: /docs/grafana-cloud/alerting-and-irm/oncall/configure/escalation-chains-and-routes/#escalation-chains
3535
slack-integration-for-grafana-oncall:
3636
- pattern: /docs/oncall/
37-
destination: /docs/oncall/<ONCALL_VERSION>/manage/notify/slack/
37+
destination: /docs/oncall/<ONCALL_VERSION>/configure/integrations/references/slack/
3838
- pattern: /docs/grafana-cloud/
39-
destination: /docs/grafana-cloud/alerting-and-irm/oncall/manage/notify/slack/
39+
destination: /docs/grafana-cloud/alerting-and-irm/oncall/configure/integrations/references/slack/
4040
user-and-team-management:
4141
- pattern: /docs/oncall/
4242
destination: /docs/oncall/<ONCALL_VERSION>/manage/user-and-team-management/
@@ -233,7 +233,7 @@ To configure Slack for Grafana OnCall:
233233
6. Ensure users verify their Slack accounts in their user profile in Grafana OnCall.
234234

235235
For further instruction on connecting to your Slack workspace, refer to
236-
[Slack integration for Grafana OnCall](ref:slack-integration-for-grafana-oncall)
236+
[Slack integration for Grafana IRM](ref:slack-integration-for-grafana-oncall).
237237

238238
Grafana OnCall also supports other ChatOps integration like Microsoft Teams and Telegram.
239239
For a full list of supported integrations, refer to [Notify people](ref:notify-people).

engine/apps/slack/scenarios/paging.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -145,12 +145,16 @@ def process_scenario(
145145
if settings.UNIFIED_SLACK_APP_ENABLED:
146146
if slack_team_identity.needs_reinstall:
147147
organizations = _get_available_organizations(slack_team_identity, slack_user_identity)
148-
# Provide a link to web if user has access only to one organization
149148
if len(organizations) == 1:
149+
# Provide a link to web if user has access only to one organization
150150
link = urljoin(organizations[0].web_link, "settings?tab=ChatOps&chatOpsTab=Slack")
151-
upgrade = f"<{link}|Upgrade>"
152151
else:
153-
upgrade = "Upgrade" # TODO: Add link to docs are available
152+
# Otherwise, provide a link to the documentation
153+
link = (
154+
"https://grafana.com/docs/grafana-cloud/alerting-and-irm/oncall/configure/integrations"
155+
"/references/slack/#migrate-to-the-grafana-irm-slack-integration"
156+
)
157+
upgrade = f"<{link}|Upgrade>"
154158
msg = (
155159
f"The new Slack IRM integration is now available. f{upgrade} for a more powerful and flexible "
156160
f"way to interact with Grafana IRM on Slack."

engine/settings/base.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
METRIC_USER_WAS_NOTIFIED_OF_ALERT_GROUPS_NAME,
118118
]
119119
# List of metrics to collect. Collect all available application metrics by default
120-
METRICS_TO_COLLECT = os.environ.get("METRICS_TO_COLLECT", METRICS_ALL)
120+
METRICS_TO_COLLECT = getenv_list("METRICS_TO_COLLECT", METRICS_ALL)
121121

122122

123123
# Database

grafana-plugin/src/containers/ApiTokenSettings/ApiTokenForm.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export const ApiTokenForm = observer((props: TokenCreationModalProps) => {
120120
<VerticalGroup>
121121
<Label>Curl command example</Label>
122122
<SourceCode noMinHeight showClipboardIconOnly>
123-
{getCurlExample(token, store.onCallApiUrl)}
123+
{getCurlExample(token, store.pluginStore.apiUrlFromStatus)}
124124
</SourceCode>
125125
</VerticalGroup>
126126
);

grafana-plugin/src/models/plugin/plugin.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { OnCallPluginMetaJSONData } from 'types';
55
import { ActionKey } from 'models/loader/action-keys';
66
import { GrafanaApiClient } from 'network/grafana-api/http-client';
77
import { makeRequest } from 'network/network';
8-
import { PluginConnection, PostStatusResponse } from 'network/oncall-api/api.types';
8+
import { PluginConnection, StatusResponse } from 'network/oncall-api/api.types';
99
import { RootBaseStore } from 'state/rootBaseStore/RootBaseStore';
1010
import { waitInMs } from 'utils/async';
1111
import { AutoLoadingState } from 'utils/decorators';
@@ -31,6 +31,7 @@ On Cloud:
3131
export class PluginStore {
3232
rootStore: RootBaseStore;
3333
connectionStatus?: PluginConnection;
34+
apiUrlFromStatus?: string;
3435
isPluginConnected = false;
3536
appliedOnCallApiUrl = '';
3637

@@ -53,9 +54,10 @@ export class PluginStore {
5354

5455
@AutoLoadingState(ActionKey.PLUGIN_VERIFY_CONNECTION)
5556
async verifyPluginConnection() {
56-
const { pluginConnection } = await makeRequest<PostStatusResponse>(`/plugin/status`, {});
57+
const { pluginConnection, api_url } = await makeRequest<StatusResponse>(`/plugin/status`, {});
5758
runInAction(() => {
5859
this.connectionStatus = pluginConnection;
60+
this.apiUrlFromStatus = api_url;
5961
this.isPluginConnected = Object.keys(pluginConnection).every(
6062
(key) => pluginConnection[key as keyof PluginConnection]?.ok
6163
);

grafana-plugin/src/network/oncall-api/api.types.d.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,10 @@ type PluginConnection = {
1616
grafana_url_from_engine: PluginConnectionCheck;
1717
};
1818

19-
export type PostStatusResponse = {
19+
export type StatusResponse = {
2020
pluginConnection: PluginConnection;
21-
allow_signup: boolean;
2221
api_url: string;
2322
currently_undergoing_maintenance_message: string | null;
24-
is_installed: boolean;
25-
is_user_anonymous: boolean;
2623
license: string;
27-
recaptcha_site_key: string;
28-
token_ok: boolean;
2924
version: string;
3025
};

grafana-plugin/src/pages/settings/tabs/ChatOps/tabs/SlackSettings/SlackSettings.tsx

+5-9
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { WithStoreProps } from 'state/types';
3131
import { useStore } from 'state/useStore';
3232
import { withMobXProviderContext } from 'state/withStore';
3333
import { UserActions } from 'utils/authorization/authorization';
34-
import { DOCS_ROOT, DOCS_SLACK_SETUP, getPluginId } from 'utils/consts';
34+
import { DOCS_SLACK_SETUP, getPluginId } from 'utils/consts';
3535
import { useConfirmModal } from 'utils/hooks';
3636
import { showApiError } from 'utils/utils';
3737

@@ -353,15 +353,16 @@ const UpgradeToUnifiedSlackBanner = observer(() => {
353353
} = useStore();
354354
const { modalProps, openModal } = useConfirmModal();
355355

356+
const SLACK_MIGRATION_DOCS =
357+
'https://grafana.com/docs/grafana-cloud/alerting-and-irm/oncall/configure/integrations/references/slack/#migrate-to-the-grafana-irm-slack-integration';
356358
return (
357359
<>
358360
<ConfirmModal {...modalProps} />
359361
<Alert severity="warning" title="This integration is outdated" buttonContent="Migrate">
360362
<div className={styles.upgradeSlackAlertText}>
361363
The OnCall Slack app is now a Grafana IRM app with new incident management features. Migrate now to access the
362364
enhanced capabilities.{' '}
363-
<a href={`${DOCS_ROOT}`} target="_blank" rel="noreferrer">
364-
{/* TODO: update link to docs */}
365+
<a href={`${SLACK_MIGRATION_DOCS}`} target="_blank" rel="noreferrer">
365366
Learn more
366367
</a>
367368
</div>
@@ -390,12 +391,7 @@ const UpgradeToUnifiedSlackBanner = observer(() => {
390391
</li>
391392
</ul>
392393
</p>
393-
<a
394-
href={`${DOCS_ROOT}`} // TODO: update link to docs
395-
target="_blank"
396-
rel="noreferrer"
397-
className={styles.marginTop}
398-
>
394+
<a href={`${SLACK_MIGRATION_DOCS}`} target="_blank" rel="noreferrer" className={styles.marginTop}>
399395
<Text type="link">
400396
<span>Learn more in the docs</span>
401397
<Icon name="external-link-alt" className="u-margin-left-xs u-margin-bottom-xxs" />

grafana-plugin/src/pages/settings/tabs/MainSettings/MainSettings.module.css

-7
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22

3-
import { Field, Input, Switch } from '@grafana/ui';
4-
import cn from 'classnames/bind';
3+
import { css } from '@emotion/css';
4+
import { Field, Input, Switch, useStyles2 } from '@grafana/ui';
55
import { observer } from 'mobx-react';
66
import { LegacyNavHeading } from 'navbar/LegacyNavHeading';
77

@@ -10,86 +10,71 @@ import { ApiTokenSettings } from 'containers/ApiTokenSettings/ApiTokenSettings';
1010
import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip';
1111
import { TeamsSettings } from 'pages/settings/tabs/TeamsSettings/TeamsSettings';
1212
import { isTopNavbar } from 'plugin/GrafanaPluginRootPage.helpers';
13-
import { WithStoreProps } from 'state/types';
14-
import { withMobXProviderContext } from 'state/withStore';
13+
import { useStore } from 'state/useStore';
1514
import { UserActions } from 'utils/authorization/authorization';
1615

17-
import styles from './MainSettings.module.css';
16+
export const MainSettings = observer(() => {
17+
const styles = useStyles2(getStyles);
18+
const {
19+
organizationStore: { currentOrganization, saveCurrentOrganization },
20+
pluginStore: { apiUrlFromStatus },
21+
} = useStore();
1822

19-
const cx = cn.bind(styles);
20-
21-
interface SettingsPageProps extends WithStoreProps {}
22-
23-
interface SettingsPageState {
24-
apiUrl?: string;
25-
}
26-
27-
@observer
28-
class Settings extends React.Component<SettingsPageProps, SettingsPageState> {
29-
state: SettingsPageState = {
30-
apiUrl: '',
31-
};
32-
33-
async componentDidMount() {
34-
const { store } = this.props;
35-
const url = await store.getApiUrlForSettings();
36-
this.setState({ apiUrl: url });
37-
}
38-
39-
render() {
40-
const { organizationStore } = this.props.store;
41-
const { currentOrganization } = organizationStore;
42-
const { apiUrl } = this.state;
43-
44-
return (
45-
<div className={cx('root')}>
46-
<LegacyNavHeading>
47-
<Text.Title level={3} className={cx('title')}>
48-
Organization settings
49-
</Text.Title>
50-
</LegacyNavHeading>
23+
return (
24+
<div>
25+
<LegacyNavHeading>
26+
<Text.Title level={3} className={styles.title}>
27+
Organization settings
28+
</Text.Title>
29+
</LegacyNavHeading>
5130

52-
<div className={cx('settings')}>
53-
<Text.Title level={3} className={cx('title')}>
54-
Resolution Note
55-
</Text.Title>
56-
<Field
57-
loading={!currentOrganization}
58-
label="Require a resolution note when resolving Alert Groups"
59-
description={`Once user clicks "Resolve" for an Alert Group, they will be required to fill in a resolution note about the Alert Group`}
60-
>
61-
<WithPermissionControlTooltip userAction={UserActions.OtherSettingsWrite}>
62-
<Switch
63-
value={currentOrganization?.is_resolution_note_required}
64-
onChange={(event) => {
65-
organizationStore.saveCurrentOrganization({
66-
is_resolution_note_required: event.currentTarget.checked,
67-
});
68-
}}
69-
/>
70-
</WithPermissionControlTooltip>
71-
</Field>
72-
</div>
73-
{!isTopNavbar() && (
74-
<div style={{ marginBottom: '20px' }}>
75-
<Text.Title level={3} className={cx('title')}>
76-
Teams and Access Settings
77-
</Text.Title>
78-
<TeamsSettings />
79-
</div>
80-
)}
81-
<Text.Title level={3} className={cx('title')}>
82-
API URL
31+
<div className={styles.settings}>
32+
<Text.Title level={3} className={styles.title}>
33+
Resolution Note
8334
</Text.Title>
84-
<div>
85-
<Field>
86-
<Input value={apiUrl} disabled />
87-
</Field>
35+
<Field
36+
loading={!currentOrganization}
37+
label="Require a resolution note when resolving Alert Groups"
38+
description={`Once user clicks "Resolve" for an Alert Group, they will be required to fill in a resolution note about the Alert Group`}
39+
>
40+
<WithPermissionControlTooltip userAction={UserActions.OtherSettingsWrite}>
41+
<Switch
42+
value={currentOrganization?.is_resolution_note_required}
43+
onChange={(event) => {
44+
saveCurrentOrganization({
45+
is_resolution_note_required: event.currentTarget.checked,
46+
});
47+
}}
48+
/>
49+
</WithPermissionControlTooltip>
50+
</Field>
51+
</div>
52+
{!isTopNavbar() && (
53+
<div style={{ marginBottom: '20px' }}>
54+
<Text.Title level={3} className={styles.title}>
55+
Teams and Access Settings
56+
</Text.Title>
57+
<TeamsSettings />
8858
</div>
89-
<ApiTokenSettings />
59+
)}
60+
<Text.Title level={3} className={styles.title}>
61+
API URL
62+
</Text.Title>
63+
<div>
64+
<Field>
65+
<Input value={apiUrlFromStatus} disabled />
66+
</Field>
9067
</div>
91-
);
92-
}
93-
}
68+
<ApiTokenSettings />
69+
</div>
70+
);
71+
});
9472

95-
export const MainSettings = withMobXProviderContext(Settings);
73+
const getStyles = () => ({
74+
settings: css`
75+
width: fit-content;
76+
`,
77+
title: css`
78+
margin-bottom: 20px;
79+
`,
80+
});

grafana-plugin/src/state/rootBaseStore/RootBaseStore.ts

-8
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,6 @@ export class RootBaseStore {
6969
@observable
7070
pageTitle = '';
7171

72-
@observable
73-
onCallApiUrl: string;
74-
7572
@observable
7673
insightsDatasource = 'grafanacloud-usage';
7774

@@ -186,11 +183,6 @@ export class RootBaseStore {
186183
this.pageTitle = title;
187184
}
188185

189-
@action.bound
190-
async getApiUrlForSettings() {
191-
return this.onCallApiUrl;
192-
}
193-
194186
@action.bound
195187
async loadRecaptcha() {
196188
const { recaptcha_site_key } = await makeRequest<{ recaptcha_site_key: string }>('/plugin/recaptcha');

grafana-plugin/src/types.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AppRootProps as BaseAppRootProps, AppPluginMeta, PluginConfigPageProps, BootData } from '@grafana/data';
1+
import { AppRootProps as BaseAppRootProps, AppPluginMeta, PluginConfigPageProps } from '@grafana/data';
22

33
import { getPluginId } from 'utils/consts';
44

@@ -30,8 +30,6 @@ export type OnCallPluginExtensionPoints =
3030

3131
declare global {
3232
export interface Window {
33-
// https://github.com/grafana/grafana/blob/78bef7a26a799209b5307d6bde8e25fcb4fbde7d/public/views/index-template.html#L251-L258
34-
grafanaBootData?: BootData;
3533
RECAPTCHA_SITE_KEY: string;
3634
grecaptcha: any;
3735
dataLayer: any;

tools/migrators/lib/network.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def api_call(method: str, base_url: str, path: str, **kwargs) -> requests.Respon
2424
if e.response.status_code == 429:
2525
cooldown_seconds = int(e.response.headers["Retry-After"])
2626
sleep(cooldown_seconds)
27-
return api_call(method, path, **kwargs)
27+
return api_call(method, base_url, path, **kwargs)
2828
elif e.response.status_code == 400:
2929
resp_json = None
3030
with suppress(requests.exceptions.JSONDecodeError):

0 commit comments

Comments
 (0)