Alerting: use SupportedPlugin.OnCall detecting OnCall types as a single source of truth (#61473)

Use SupportedPlugin.OnCall detecting OnCall types as a single source of truth
This commit is contained in:
Sonia Aguilar
2023-01-13 13:34:39 +01:00
committed by GitHub
parent da969e7d48
commit fb5a033282
11 changed files with 66 additions and 59 deletions

View File

@@ -3,7 +3,7 @@ import { setupServer } from 'msw/node';
// bit of setup to mock HTTP request responses // bit of setup to mock HTTP request responses
import 'whatwg-fetch'; import 'whatwg-fetch';
import { SupportedPlugin } from './PluginBridge'; import { SupportedPlugin } from '../types/pluginBridges';
export const NON_EXISTING_PLUGIN = '__does_not_exist__'; export const NON_EXISTING_PLUGIN = '__does_not_exist__';

View File

@@ -4,7 +4,9 @@ import React from 'react';
import { setBackendSrv } from '@grafana/runtime'; import { setBackendSrv } from '@grafana/runtime';
import { backendSrv } from 'app/core/services/backend_srv'; import { backendSrv } from 'app/core/services/backend_srv';
import { createBridgeURL, PluginBridge, SupportedPlugin } from './PluginBridge'; import { SupportedPlugin } from '../types/pluginBridges';
import { createBridgeURL, PluginBridge } from './PluginBridge';
import { server, NON_EXISTING_PLUGIN } from './PluginBridge.mock'; import { server, NON_EXISTING_PLUGIN } from './PluginBridge.mock';
beforeAll(() => { beforeAll(() => {

View File

@@ -1,14 +1,7 @@
import React, { FC, ReactElement } from 'react'; import React, { FC, ReactElement } from 'react';
import { useAsync } from 'react-use';
import { PluginMeta } from '@grafana/data'; import { usePluginBridge } from '../hooks/usePluginBridge';
import { getPluginSettings } from 'app/features/plugins/pluginSettings'; import { SupportedPlugin } from '../types/pluginBridges';
export enum SupportedPlugin {
Incident = 'grafana-incident-app',
OnCall = 'grafana-oncall-app',
MachineLearning = 'grafana-ml-app',
}
export type PluginID = SupportedPlugin | string; export type PluginID = SupportedPlugin | string;
@@ -20,13 +13,6 @@ export interface PluginBridgeProps {
loadingComponent?: ReactElement; loadingComponent?: ReactElement;
} }
interface PluginBridgeHookResponse {
loading: boolean;
installed?: boolean;
error?: Error;
settings?: PluginMeta<{}>;
}
export const PluginBridge: FC<PluginBridgeProps> = ({ children, plugin, loadingComponent, notInstalledFallback }) => { export const PluginBridge: FC<PluginBridgeProps> = ({ children, plugin, loadingComponent, notInstalledFallback }) => {
const { loading, installed } = usePluginBridge(plugin); const { loading, installed } = usePluginBridge(plugin);
@@ -41,24 +27,6 @@ export const PluginBridge: FC<PluginBridgeProps> = ({ children, plugin, loadingC
return <>{children}</>; return <>{children}</>;
}; };
export function usePluginBridge(plugin: PluginID): PluginBridgeHookResponse {
const { loading, error, value } = useAsync(() => getPluginSettings(plugin, { showErrorAlert: false }));
const installed = value && !error && !loading;
const enabled = value?.enabled;
const isLoading = loading && !value;
if (isLoading) {
return { loading: true };
}
if (!installed || !enabled) {
return { loading: false, installed: false };
}
return { loading, installed: true, settings: value };
}
export function createBridgeURL(plugin: PluginID, path?: string, options?: Record<string, string>) { export function createBridgeURL(plugin: PluginID, path?: string, options?: Record<string, string>) {
const searchParams = new URLSearchParams(options).toString(); const searchParams = new URLSearchParams(options).toString();
const pluginPath = `/a/${plugin}${path}`; const pluginPath = `/a/${plugin}${path}`;

View File

@@ -21,6 +21,7 @@ import {
import { useMuteTimingOptions } from '../../hooks/useMuteTimingOptions'; import { useMuteTimingOptions } from '../../hooks/useMuteTimingOptions';
import { FormAmRoute } from '../../types/amroutes'; import { FormAmRoute } from '../../types/amroutes';
import { SupportedPlugin } from '../../types/pluginBridges';
import { matcherFieldOptions } from '../../utils/alertmanager'; import { matcherFieldOptions } from '../../utils/alertmanager';
import { import {
emptyArrayFieldMatcher, emptyArrayFieldMatcher,
@@ -32,7 +33,7 @@ import {
commonGroupByOptions, commonGroupByOptions,
} from '../../utils/amroutes'; } from '../../utils/amroutes';
import { timeOptions } from '../../utils/time'; import { timeOptions } from '../../utils/time';
import { AmRouteReceiver, GrafanaAppReceiverEnum } from '../receivers/grafanaAppReceivers/types'; import { AmRouteReceiver } from '../receivers/grafanaAppReceivers/types';
import { getFormStyles } from './formStyles'; import { getFormStyles } from './formStyles';
@@ -50,7 +51,7 @@ export const AmRoutesExpandedForm: FC<AmRoutesExpandedFormProps> = ({ onCancel,
const muteTimingOptions = useMuteTimingOptions(); const muteTimingOptions = useMuteTimingOptions();
const receiversWithOnCallOnTop = receivers.sort((receiver1, receiver2) => { const receiversWithOnCallOnTop = receivers.sort((receiver1, receiver2) => {
if (receiver1.grafanaAppReceiverType === GrafanaAppReceiverEnum.GRAFANA_ONCALL) { if (receiver1.grafanaAppReceiverType === SupportedPlugin.OnCall) {
return -1; return -1;
} else { } else {
return 0; return 0;

View File

@@ -2,7 +2,9 @@ import React, { FC } from 'react';
import { Button, LinkButton, Tooltip } from '@grafana/ui'; import { Button, LinkButton, Tooltip } from '@grafana/ui';
import { createBridgeURL, usePluginBridge, SupportedPlugin } from '../PluginBridge'; import { usePluginBridge } from '../../hooks/usePluginBridge';
import { SupportedPlugin } from '../../types/pluginBridges';
import { createBridgeURL } from '../PluginBridge';
interface Props { interface Props {
title?: string; title?: string;

View File

@@ -14,6 +14,7 @@ import { Authorize } from '../../components/Authorize';
import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector'; import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector';
import { deleteReceiverAction } from '../../state/actions'; import { deleteReceiverAction } from '../../state/actions';
import { getAlertTableStyles } from '../../styles/table'; import { getAlertTableStyles } from '../../styles/table';
import { SupportedPlugin } from '../../types/pluginBridges';
import { getNotificationsPermissions } from '../../utils/access-control'; import { getNotificationsPermissions } from '../../utils/access-control';
import { isReceiverUsed } from '../../utils/alertmanager'; import { isReceiverUsed } from '../../utils/alertmanager';
import { isVanillaPrometheusAlertManagerDataSource } from '../../utils/datasource'; import { isVanillaPrometheusAlertManagerDataSource } from '../../utils/datasource';
@@ -26,7 +27,7 @@ import { ActionIcon } from '../rules/ActionIcon';
import { ReceiversSection } from './ReceiversSection'; import { ReceiversSection } from './ReceiversSection';
import { GrafanaAppBadge } from './grafanaAppReceivers/GrafanaAppBadge'; import { GrafanaAppBadge } from './grafanaAppReceivers/GrafanaAppBadge';
import { useGetReceiversWithGrafanaAppTypes } from './grafanaAppReceivers/grafanaApp'; import { useGetReceiversWithGrafanaAppTypes } from './grafanaAppReceivers/grafanaApp';
import { GrafanaAppReceiverEnum, ReceiverWithTypes } from './grafanaAppReceivers/types'; import { ReceiverWithTypes } from './grafanaAppReceivers/types';
interface UpdateActionProps extends ActionProps { interface UpdateActionProps extends ActionProps {
onClickDeleteReceiver: (receiverName: string) => void; onClickDeleteReceiver: (receiverName: string) => void;
@@ -131,7 +132,7 @@ interface ReceiverItem {
name: string; name: string;
types: string[]; types: string[];
provisioned?: boolean; provisioned?: boolean;
grafanaAppReceiverType?: GrafanaAppReceiverEnum; grafanaAppReceiverType?: SupportedPlugin;
} }
interface NotifierStatus { interface NotifierStatus {

View File

@@ -4,9 +4,11 @@ import React from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { GrafanaTheme2 } from '@grafana/data';
import { HorizontalGroup, useStyles2 } from '@grafana/ui'; import { HorizontalGroup, useStyles2 } from '@grafana/ui';
import { GRAFANA_APP_RECEIVERS_SOURCE_IMAGE, GrafanaAppReceiverEnum } from './types'; import { SupportedPlugin } from '../../../types/pluginBridges';
export const GrafanaAppBadge = ({ grafanaAppType }: { grafanaAppType: GrafanaAppReceiverEnum }) => { import { GRAFANA_APP_RECEIVERS_SOURCE_IMAGE } from './types';
export const GrafanaAppBadge = ({ grafanaAppType }: { grafanaAppType: SupportedPlugin }) => {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
return ( return (
<div className={styles.wrapper}> <div className={styles.wrapper}>

View File

@@ -1,21 +1,22 @@
import { Receiver } from 'app/plugins/datasource/alertmanager/types'; import { Receiver } from 'app/plugins/datasource/alertmanager/types';
import { useGetOnCallIntegrationsQuery } from '../../../api/onCallApi'; import { useGetOnCallIntegrationsQuery } from '../../../api/onCallApi';
import { SupportedPlugin, usePluginBridge } from '../../PluginBridge'; import { usePluginBridge } from '../../../hooks/usePluginBridge';
import { SupportedPlugin } from '../../../types/pluginBridges';
import { isOnCallReceiver } from './onCall/onCall'; import { isOnCallReceiver } from './onCall/onCall';
import { AmRouteReceiver, GrafanaAppReceiverEnum, ReceiverWithTypes } from './types'; import { AmRouteReceiver, ReceiverWithTypes } from './types';
export const useGetGrafanaReceiverTypeChecker = () => { export const useGetGrafanaReceiverTypeChecker = () => {
const { installed: isOnCallEnabled } = usePluginBridge(SupportedPlugin.OnCall); const { installed: isOnCallEnabled } = usePluginBridge(SupportedPlugin.OnCall);
const { data } = useGetOnCallIntegrationsQuery(undefined, { const { data } = useGetOnCallIntegrationsQuery(undefined, {
skip: !isOnCallEnabled, skip: !isOnCallEnabled,
}); });
const getGrafanaReceiverType = (receiver: Receiver): GrafanaAppReceiverEnum | undefined => { const getGrafanaReceiverType = (receiver: Receiver): SupportedPlugin | undefined => {
//CHECK FOR ONCALL PLUGIN //CHECK FOR ONCALL PLUGIN
const onCallIntegrations = data ?? []; const onCallIntegrations = data ?? [];
if (isOnCallEnabled && isOnCallReceiver(receiver, onCallIntegrations)) { if (isOnCallEnabled && isOnCallReceiver(receiver, onCallIntegrations)) {
return GrafanaAppReceiverEnum.GRAFANA_ONCALL; return SupportedPlugin.OnCall;
} }
//WE WILL ADD IN HERE IF THERE ARE MORE TYPES TO CHECK //WE WILL ADD IN HERE IF THERE ARE MORE TYPES TO CHECK
return undefined; return undefined;

View File

@@ -1,23 +1,18 @@
import { Receiver } from '../../../../../../plugins/datasource/alertmanager/types'; import { Receiver } from '../../../../../../plugins/datasource/alertmanager/types';
// we will add in here more types if needed import { SupportedPlugin } from '../../../types/pluginBridges';
export enum GrafanaAppReceiverEnum {
GRAFANA_ONCALL = 'Grafana OnCall',
}
export interface AmRouteReceiver { export interface AmRouteReceiver {
label: string; label: string;
value: string; value: string;
grafanaAppReceiverType?: GrafanaAppReceiverEnum; grafanaAppReceiverType?: SupportedPlugin;
} }
export interface ReceiverWithTypes extends Receiver { export interface ReceiverWithTypes extends Receiver {
grafanaAppReceiverType?: GrafanaAppReceiverEnum; grafanaAppReceiverType?: SupportedPlugin;
} }
export const GRAFANA_APP_RECEIVERS_SOURCE_IMAGE: Record<SupportedPlugin, string> = {
[SupportedPlugin.OnCall]: 'public/img/alerting/oncall_logo.svg',
export const GRAFANA_APP_RECEIVERS_SOURCE_IMAGE = { [SupportedPlugin.Incident]: '',
'Grafana OnCall': 'public/img/alerting/oncall_logo.svg', [SupportedPlugin.MachineLearning]: '',
}; };
export enum GRAFANA_APP_PLUGIN_IDS {
'Grafana OnCall' = 'grafana-oncall-app',
}

View File

@@ -0,0 +1,30 @@
import { useAsync } from 'react-use';
import { PluginMeta } from '@grafana/data';
import { getPluginSettings } from 'app/features/plugins/pluginSettings';
import { PluginID } from '../components/PluginBridge';
interface PluginBridgeHookResponse {
loading: boolean;
installed?: boolean;
error?: Error;
settings?: PluginMeta<{}>;
}
export function usePluginBridge(plugin: PluginID): PluginBridgeHookResponse {
const { loading, error, value } = useAsync(() => getPluginSettings(plugin, { showErrorAlert: false }));
const installed = value && !error && !loading;
const enabled = value?.enabled;
const isLoading = loading && !value;
if (isLoading) {
return { loading: true };
}
if (!installed || !enabled) {
return { loading: false, installed: false };
}
return { loading, installed: true, settings: value };
}

View File

@@ -0,0 +1,5 @@
export enum SupportedPlugin {
Incident = 'grafana-incident-app',
OnCall = 'grafana-oncall-app',
MachineLearning = 'grafana-ml-app',
}