Skip to content

Commit b5c5225

Browse files
authored
fix: show api url in main settings (#4896)
# What this PR does - show api_url from GET /status endpoint on settings page - refactor MainSettings to be functional component ## Which issue(s) this PR closes https://raintank-corp.slack.com/archives/C0713BYQB0W/p1724249719392329 <!-- *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 - [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.
1 parent 070abb9 commit b5c5225

File tree

7 files changed

+69
-104
lines changed

7 files changed

+69
-104
lines changed

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/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;

0 commit comments

Comments
 (0)