Skip to content

Commit

Permalink
fix: show api url in main settings (#4896)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
brojd authored Aug 22, 2024
1 parent 070abb9 commit b5c5225
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export const ApiTokenForm = observer((props: TokenCreationModalProps) => {
<VerticalGroup>
<Label>Curl command example</Label>
<SourceCode noMinHeight showClipboardIconOnly>
{getCurlExample(token, store.onCallApiUrl)}
{getCurlExample(token, store.pluginStore.apiUrlFromStatus)}
</SourceCode>
</VerticalGroup>
);
Expand Down
6 changes: 4 additions & 2 deletions grafana-plugin/src/models/plugin/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { OnCallPluginMetaJSONData } from 'types';
import { ActionKey } from 'models/loader/action-keys';
import { GrafanaApiClient } from 'network/grafana-api/http-client';
import { makeRequest } from 'network/network';
import { PluginConnection, PostStatusResponse } from 'network/oncall-api/api.types';
import { PluginConnection, StatusResponse } from 'network/oncall-api/api.types';
import { RootBaseStore } from 'state/rootBaseStore/RootBaseStore';
import { waitInMs } from 'utils/async';
import { AutoLoadingState } from 'utils/decorators';
Expand All @@ -31,6 +31,7 @@ On Cloud:
export class PluginStore {
rootStore: RootBaseStore;
connectionStatus?: PluginConnection;
apiUrlFromStatus?: string;
isPluginConnected = false;
appliedOnCallApiUrl = '';

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

@AutoLoadingState(ActionKey.PLUGIN_VERIFY_CONNECTION)
async verifyPluginConnection() {
const { pluginConnection } = await makeRequest<PostStatusResponse>(`/plugin/status`, {});
const { pluginConnection, api_url } = await makeRequest<StatusResponse>(`/plugin/status`, {});
runInAction(() => {
this.connectionStatus = pluginConnection;
this.apiUrlFromStatus = api_url;
this.isPluginConnected = Object.keys(pluginConnection).every(
(key) => pluginConnection[key as keyof PluginConnection]?.ok
);
Expand Down
7 changes: 1 addition & 6 deletions grafana-plugin/src/network/oncall-api/api.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,10 @@ type PluginConnection = {
grafana_url_from_engine: PluginConnectionCheck;
};

export type PostStatusResponse = {
export type StatusResponse = {
pluginConnection: PluginConnection;
allow_signup: boolean;
api_url: string;
currently_undergoing_maintenance_message: string | null;
is_installed: boolean;
is_user_anonymous: boolean;
license: string;
recaptcha_site_key: string;
token_ok: boolean;
version: string;
};

This file was deleted.

139 changes: 62 additions & 77 deletions grafana-plugin/src/pages/settings/tabs/MainSettings/MainSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';

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

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

import styles from './MainSettings.module.css';
export const MainSettings = observer(() => {
const styles = useStyles2(getStyles);
const {
organizationStore: { currentOrganization, saveCurrentOrganization },
pluginStore: { apiUrlFromStatus },
} = useStore();

const cx = cn.bind(styles);

interface SettingsPageProps extends WithStoreProps {}

interface SettingsPageState {
apiUrl?: string;
}

@observer
class Settings extends React.Component<SettingsPageProps, SettingsPageState> {
state: SettingsPageState = {
apiUrl: '',
};

async componentDidMount() {
const { store } = this.props;
const url = await store.getApiUrlForSettings();
this.setState({ apiUrl: url });
}

render() {
const { organizationStore } = this.props.store;
const { currentOrganization } = organizationStore;
const { apiUrl } = this.state;

return (
<div className={cx('root')}>
<LegacyNavHeading>
<Text.Title level={3} className={cx('title')}>
Organization settings
</Text.Title>
</LegacyNavHeading>
return (
<div>
<LegacyNavHeading>
<Text.Title level={3} className={styles.title}>
Organization settings
</Text.Title>
</LegacyNavHeading>

<div className={cx('settings')}>
<Text.Title level={3} className={cx('title')}>
Resolution Note
</Text.Title>
<Field
loading={!currentOrganization}
label="Require a resolution note when resolving Alert Groups"
description={`Once user clicks "Resolve" for an Alert Group, they will be required to fill in a resolution note about the Alert Group`}
>
<WithPermissionControlTooltip userAction={UserActions.OtherSettingsWrite}>
<Switch
value={currentOrganization?.is_resolution_note_required}
onChange={(event) => {
organizationStore.saveCurrentOrganization({
is_resolution_note_required: event.currentTarget.checked,
});
}}
/>
</WithPermissionControlTooltip>
</Field>
</div>
{!isTopNavbar() && (
<div style={{ marginBottom: '20px' }}>
<Text.Title level={3} className={cx('title')}>
Teams and Access Settings
</Text.Title>
<TeamsSettings />
</div>
)}
<Text.Title level={3} className={cx('title')}>
API URL
<div className={styles.settings}>
<Text.Title level={3} className={styles.title}>
Resolution Note
</Text.Title>
<div>
<Field>
<Input value={apiUrl} disabled />
</Field>
<Field
loading={!currentOrganization}
label="Require a resolution note when resolving Alert Groups"
description={`Once user clicks "Resolve" for an Alert Group, they will be required to fill in a resolution note about the Alert Group`}
>
<WithPermissionControlTooltip userAction={UserActions.OtherSettingsWrite}>
<Switch
value={currentOrganization?.is_resolution_note_required}
onChange={(event) => {
saveCurrentOrganization({
is_resolution_note_required: event.currentTarget.checked,
});
}}
/>
</WithPermissionControlTooltip>
</Field>
</div>
{!isTopNavbar() && (
<div style={{ marginBottom: '20px' }}>
<Text.Title level={3} className={styles.title}>
Teams and Access Settings
</Text.Title>
<TeamsSettings />
</div>
<ApiTokenSettings />
)}
<Text.Title level={3} className={styles.title}>
API URL
</Text.Title>
<div>
<Field>
<Input value={apiUrlFromStatus} disabled />
</Field>
</div>
);
}
}
<ApiTokenSettings />
</div>
);
});

export const MainSettings = withMobXProviderContext(Settings);
const getStyles = () => ({
settings: css`
width: fit-content;
`,
title: css`
margin-bottom: 20px;
`,
});
8 changes: 0 additions & 8 deletions grafana-plugin/src/state/rootBaseStore/RootBaseStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,6 @@ export class RootBaseStore {
@observable
pageTitle = '';

@observable
onCallApiUrl: string;

@observable
insightsDatasource = 'grafanacloud-usage';

Expand Down Expand Up @@ -186,11 +183,6 @@ export class RootBaseStore {
this.pageTitle = title;
}

@action.bound
async getApiUrlForSettings() {
return this.onCallApiUrl;
}

@action.bound
async loadRecaptcha() {
const { recaptcha_site_key } = await makeRequest<{ recaptcha_site_key: string }>('/plugin/recaptcha');
Expand Down
4 changes: 1 addition & 3 deletions grafana-plugin/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AppRootProps as BaseAppRootProps, AppPluginMeta, PluginConfigPageProps, BootData } from '@grafana/data';
import { AppRootProps as BaseAppRootProps, AppPluginMeta, PluginConfigPageProps } from '@grafana/data';

import { getPluginId } from 'utils/consts';

Expand Down Expand Up @@ -30,8 +30,6 @@ export type OnCallPluginExtensionPoints =

declare global {
export interface Window {
// https://github.com/grafana/grafana/blob/78bef7a26a799209b5307d6bde8e25fcb4fbde7d/public/views/index-template.html#L251-L258
grafanaBootData?: BootData;
RECAPTCHA_SITE_KEY: string;
grecaptcha: any;
dataLayer: any;
Expand Down

0 comments on commit b5c5225

Please sign in to comment.