diff --git a/public/app/features/alerting/unified/api/alertingApi.ts b/public/app/features/alerting/unified/api/alertingApi.ts index 906a9b12084..9f368f4f966 100644 --- a/public/app/features/alerting/unified/api/alertingApi.ts +++ b/public/app/features/alerting/unified/api/alertingApi.ts @@ -44,6 +44,7 @@ export const alertingApi = createApi({ 'GrafanaLabels', 'CombinedAlertRule', 'GrafanaRulerRule', + 'GrafanaSlo', ], endpoints: () => ({}), }); diff --git a/public/app/features/alerting/unified/api/sloApi.ts b/public/app/features/alerting/unified/api/sloApi.ts new file mode 100644 index 00000000000..86f26794cec --- /dev/null +++ b/public/app/features/alerting/unified/api/sloApi.ts @@ -0,0 +1,35 @@ +import { SupportedPlugin } from '../types/pluginBridges'; + +import { alertingApi } from './alertingApi'; + +export interface Label { + key: string; + value: string; +} + +export interface SloAlertingMetadata { + annotations?: Label[]; + labels?: Label[]; +} + +export interface SloAlerting { + annotations?: Label[]; + fastBurn?: SloAlertingMetadata; + labels?: Label[]; + slowBurn?: SloAlertingMetadata; +} + +export interface Slo { + alerting?: SloAlerting; +} + +const SLO_API_PATH = `/api/plugins/${SupportedPlugin.Slo}/resources/v1`; + +export const sloApi = alertingApi.injectEndpoints({ + endpoints: (build) => ({ + getSlos: build.query<{ slos: Slo[] }, void>({ + query: () => ({ url: `${SLO_API_PATH}/slo`, showErrorAlert: false }), + providesTags: ['GrafanaSlo'], + }), + }), +}); diff --git a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts index 6b65cc39322..8a316af702d 100644 --- a/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts +++ b/public/app/features/alerting/unified/components/receivers/grafanaAppReceivers/types.ts @@ -12,4 +12,5 @@ export const GRAFANA_APP_RECEIVERS_SOURCE_IMAGE: Record [SupportedPlugin.Incident]: '', [SupportedPlugin.MachineLearning]: '', [SupportedPlugin.Labels]: '', + [SupportedPlugin.Slo]: '', }; diff --git a/public/app/features/alerting/unified/types/pluginBridges.ts b/public/app/features/alerting/unified/types/pluginBridges.ts index fbe809743b6..926a75ff41d 100644 --- a/public/app/features/alerting/unified/types/pluginBridges.ts +++ b/public/app/features/alerting/unified/types/pluginBridges.ts @@ -3,4 +3,5 @@ export enum SupportedPlugin { OnCall = 'grafana-oncall-app', MachineLearning = 'grafana-ml-app', Labels = 'grafana-labels-app', + Slo = 'grafana-slo-app', } diff --git a/public/app/features/gops/configuration-tracker/irmHooks.ts b/public/app/features/gops/configuration-tracker/irmHooks.ts index b845de6033b..f182633940f 100644 --- a/public/app/features/gops/configuration-tracker/irmHooks.ts +++ b/public/app/features/gops/configuration-tracker/irmHooks.ts @@ -13,6 +13,7 @@ import { isContactPointReady } from './alerting/utils'; import { ConfigurationStepsEnum, DataSourceConfigurationData, IrmCardConfiguration } from './components/ConfigureIRM'; import { useGetIncidentPluginConfig } from './incidents/hooks'; import { useOnCallChatOpsConnections, useOnCallOptions } from './onCall/hooks'; +import { useSloChecks } from './slo/hooks'; interface UrlLink { url: string; @@ -68,13 +69,17 @@ function useGetConfigurationForApps() { is_integration_chatops_connected, isLoading: isOnCallConfigLoading, } = useOnCallChatOpsConnections(); + // configuration checks for slo + const { hasSlo, hasSloWithAlert, isLoading: isSloLoading } = useSloChecks(); + // check if any of the configurations are loading const isLoading = isLoadingContactPoints || isLoadingDefaultContactPoint || isLoadingAlertCreatedDone || isIncidentsConfigLoading || - isOnCallConfigLoading; + isOnCallConfigLoading || + isSloLoading; return { alerting: { @@ -91,6 +96,10 @@ function useGetConfigurationForApps() { is_chatops_connected, is_integration_chatops_connected, }, + slo: { + hasSlo, + hasSloWithAlert, + }, isLoading, }; } @@ -100,6 +109,7 @@ export function useGetEssentialsConfiguration(): EssentialsConfigurationData { alerting: { contactPoints, defaultContactpoint, isCreateAlertRuleDone }, incidents: { isChatOpsInstalled, isIncidentsInstalled }, onCall: { onCallOptions, is_chatops_connected, is_integration_chatops_connected }, + slo: { hasSlo, hasSloWithAlert }, isLoading, } = useGetConfigurationForApps(); @@ -165,6 +175,40 @@ export function useGetEssentialsConfiguration(): EssentialsConfigurationData { }, done: isCreateAlertRuleDone, }, + { + title: 'Create your first SLO', + description: 'Create SLOs to monitor your service.', + button: { + type: 'openLink', + urlLink: { + url: '/a/grafana-slo-app/wizard/new', + }, + label: 'Create', + urlLinkOnDone: { + url: '/a/grafana-slo-app/manage-slos', + }, + labelOnDone: 'View', + }, + done: hasSlo, + }, + { + title: 'Enable SLO alerting', + description: 'Configure SLO alerting to receive notifications when your SLOs are breached.', + button: { + type: 'openLink', + urlLink: { + queryParams: { alertsEnabled: 'disabled' }, + url: '/a/grafana-slo-app/manage-slos', + }, + label: 'Enable', + urlLinkOnDone: { + queryParams: { alertsEnabled: 'enabled' }, + url: '/a/grafana-slo-app/manage-slos', + }, + labelOnDone: 'View', + }, + done: hasSloWithAlert, + }, ], }, { diff --git a/public/app/features/gops/configuration-tracker/slo/hooks.ts b/public/app/features/gops/configuration-tracker/slo/hooks.ts new file mode 100644 index 00000000000..e260fc68067 --- /dev/null +++ b/public/app/features/gops/configuration-tracker/slo/hooks.ts @@ -0,0 +1,20 @@ +import { sloApi } from 'app/features/alerting/unified/api/sloApi'; +import { usePluginBridge } from 'app/features/alerting/unified/hooks/usePluginBridge'; +import { SupportedPlugin } from 'app/features/alerting/unified/types/pluginBridges'; + +export function useSloChecks() { + const { installed: sloPluginInstalled } = usePluginBridge(SupportedPlugin.Slo); + + const { data, isLoading } = sloApi.endpoints.getSlos.useQuery(undefined, { + skip: !sloPluginInstalled, + refetchOnFocus: true, + refetchOnReconnect: true, + refetchOnMountOrArgChange: true, + }); + + return { + isLoading, + hasSlo: Boolean(data?.slos.length), + hasSloWithAlert: Boolean(data?.slos?.some((slo) => slo.alerting)), + }; +}