mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Restructure Silences components for better separation of concerns
This commit is contained in:
committed by
Tom Ratcliffe
parent
11ed882c84
commit
a1d2a9670b
@@ -2159,11 +2159,8 @@ exports[`better eslint`] = {
|
|||||||
[0, 0, 0, "Styles should be written using objects.", "4"]
|
[0, 0, 0, "Styles should be written using objects.", "4"]
|
||||||
],
|
],
|
||||||
"public/app/features/alerting/unified/components/silences/SilencesTable.tsx:5381": [
|
"public/app/features/alerting/unified/components/silences/SilencesTable.tsx:5381": [
|
||||||
[0, 0, 0, "Styles should be written using objects.", "0"],
|
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||||
[0, 0, 0, "Styles should be written using objects.", "1"],
|
[0, 0, 0, "Do not use any type assertions.", "1"]
|
||||||
[0, 0, 0, "Styles should be written using objects.", "2"],
|
|
||||||
[0, 0, 0, "Styles should be written using objects.", "3"],
|
|
||||||
[0, 0, 0, "Styles should be written using objects.", "4"]
|
|
||||||
],
|
],
|
||||||
"public/app/features/alerting/unified/hooks/useAlertmanagerConfig.ts:5381": [
|
"public/app/features/alerting/unified/hooks/useAlertmanagerConfig.ts:5381": [
|
||||||
[0, 0, 0, "Do not use any type assertions.", "0"]
|
[0, 0, 0, "Do not use any type assertions.", "0"]
|
||||||
|
|||||||
@@ -1,59 +1,18 @@
|
|||||||
import React, { useCallback, useEffect } from 'react';
|
import React from 'react';
|
||||||
import { Route, RouteChildrenProps, Switch } from 'react-router-dom';
|
import { Route, RouteChildrenProps, Switch } from 'react-router-dom';
|
||||||
|
|
||||||
import { Alert, withErrorBoundary } from '@grafana/ui';
|
import { withErrorBoundary } from '@grafana/ui';
|
||||||
import { Silence } from 'app/plugins/datasource/alertmanager/types';
|
|
||||||
import { useDispatch } from 'app/types';
|
|
||||||
|
|
||||||
import { featureDiscoveryApi } from './api/featureDiscoveryApi';
|
|
||||||
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
|
import { AlertmanagerPageWrapper } from './components/AlertingPageWrapper';
|
||||||
import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning';
|
import { GrafanaAlertmanagerDeliveryWarning } from './components/GrafanaAlertmanagerDeliveryWarning';
|
||||||
import SilencesEditor from './components/silences/SilencesEditor';
|
import SilencesEditor from './components/silences/SilencesEditor';
|
||||||
import SilencesTable from './components/silences/SilencesTable';
|
import SilencesTable from './components/silences/SilencesTable';
|
||||||
import { useSilenceNavData } from './hooks/useSilenceNavData';
|
import { useSilenceNavData } from './hooks/useSilenceNavData';
|
||||||
import { useUnifiedAlertingSelector } from './hooks/useUnifiedAlertingSelector';
|
|
||||||
import { useAlertmanager } from './state/AlertmanagerContext';
|
import { useAlertmanager } from './state/AlertmanagerContext';
|
||||||
import { fetchAmAlertsAction, fetchSilencesAction } from './state/actions';
|
|
||||||
import { SILENCES_POLL_INTERVAL_MS } from './utils/constants';
|
|
||||||
import { AsyncRequestState, initialAsyncRequestState } from './utils/redux';
|
|
||||||
|
|
||||||
const Silences = () => {
|
const Silences = () => {
|
||||||
const { selectedAlertmanager } = useAlertmanager();
|
const { selectedAlertmanager } = useAlertmanager();
|
||||||
|
|
||||||
const dispatch = useDispatch();
|
|
||||||
const silences = useUnifiedAlertingSelector((state) => state.silences);
|
|
||||||
const alertsRequests = useUnifiedAlertingSelector((state) => state.amAlerts);
|
|
||||||
const alertsRequest = selectedAlertmanager
|
|
||||||
? alertsRequests[selectedAlertmanager] || initialAsyncRequestState
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
const { currentData: amFeatures } = featureDiscoveryApi.useDiscoverAmFeaturesQuery(
|
|
||||||
{ amSourceName: selectedAlertmanager ?? '' },
|
|
||||||
{ skip: !selectedAlertmanager }
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function fetchAll() {
|
|
||||||
if (selectedAlertmanager) {
|
|
||||||
dispatch(fetchSilencesAction(selectedAlertmanager));
|
|
||||||
dispatch(fetchAmAlertsAction(selectedAlertmanager));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fetchAll();
|
|
||||||
const interval = setInterval(() => fetchAll, SILENCES_POLL_INTERVAL_MS);
|
|
||||||
return () => {
|
|
||||||
clearInterval(interval);
|
|
||||||
};
|
|
||||||
}, [selectedAlertmanager, dispatch]);
|
|
||||||
|
|
||||||
const { result, loading, error }: AsyncRequestState<Silence[]> =
|
|
||||||
(selectedAlertmanager && silences[selectedAlertmanager]) || initialAsyncRequestState;
|
|
||||||
|
|
||||||
const getSilenceById = useCallback((id: string) => result && result.find((silence) => silence.id === id), [result]);
|
|
||||||
|
|
||||||
const mimirLazyInitError =
|
|
||||||
error?.message?.includes('the Alertmanager is not configured') && amFeatures?.lazyConfigInit;
|
|
||||||
|
|
||||||
if (!selectedAlertmanager) {
|
if (!selectedAlertmanager) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -62,48 +21,23 @@ const Silences = () => {
|
|||||||
<>
|
<>
|
||||||
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={selectedAlertmanager} />
|
<GrafanaAlertmanagerDeliveryWarning currentAlertmanager={selectedAlertmanager} />
|
||||||
|
|
||||||
{mimirLazyInitError && (
|
<Switch>
|
||||||
<Alert title="The selected Alertmanager has no configuration" severity="warning">
|
<Route exact path="/alerting/silences">
|
||||||
Create a new contact point to create a configuration using the default values or contact your administrator to
|
<SilencesTable alertManagerSourceName={selectedAlertmanager} />
|
||||||
set up the Alertmanager.
|
</Route>
|
||||||
</Alert>
|
<Route exact path="/alerting/silence/new">
|
||||||
)}
|
<SilencesEditor alertManagerSourceName={selectedAlertmanager} />
|
||||||
{error && !loading && !mimirLazyInitError && (
|
</Route>
|
||||||
<Alert severity="error" title="Error loading silences">
|
<Route exact path="/alerting/silence/:id/edit">
|
||||||
{error.message || 'Unknown error.'}
|
{({ match }: RouteChildrenProps<{ id: string }>) => {
|
||||||
</Alert>
|
return (
|
||||||
)}
|
match?.params.id && (
|
||||||
{alertsRequest?.error && !alertsRequest?.loading && !mimirLazyInitError && (
|
<SilencesEditor silenceId={match.params.id} alertManagerSourceName={selectedAlertmanager} />
|
||||||
<Alert severity="error" title="Error loading Alertmanager alerts">
|
)
|
||||||
{alertsRequest.error?.message || 'Unknown error.'}
|
);
|
||||||
</Alert>
|
}}
|
||||||
)}
|
</Route>
|
||||||
{result && !error && (
|
</Switch>
|
||||||
<Switch>
|
|
||||||
<Route exact path="/alerting/silences">
|
|
||||||
<SilencesTable
|
|
||||||
silences={result}
|
|
||||||
alertManagerAlerts={alertsRequest?.result ?? []}
|
|
||||||
alertManagerSourceName={selectedAlertmanager}
|
|
||||||
/>
|
|
||||||
</Route>
|
|
||||||
<Route exact path="/alerting/silence/new">
|
|
||||||
<SilencesEditor alertManagerSourceName={selectedAlertmanager} />
|
|
||||||
</Route>
|
|
||||||
<Route exact path="/alerting/silence/:id/edit">
|
|
||||||
{({ match }: RouteChildrenProps<{ id: string }>) => {
|
|
||||||
return (
|
|
||||||
match?.params.id && (
|
|
||||||
<SilencesEditor
|
|
||||||
silence={getSilenceById(match.params.id)}
|
|
||||||
alertManagerSourceName={selectedAlertmanager}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
</Route>
|
|
||||||
</Switch>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { isEqual, pickBy } from 'lodash';
|
import { isEqual, pickBy } from 'lodash';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useEffect, useMemo, useState } from 'react';
|
||||||
import { FormProvider, useForm } from 'react-hook-form';
|
import { FormProvider, useForm } from 'react-hook-form';
|
||||||
|
import { useHistory } from 'react-router';
|
||||||
import { useDebounce } from 'react-use';
|
import { useDebounce } from 'react-use';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -15,25 +16,22 @@ import {
|
|||||||
} from '@grafana/data';
|
} from '@grafana/data';
|
||||||
import { config } from '@grafana/runtime';
|
import { config } from '@grafana/runtime';
|
||||||
import { Button, Field, FieldSet, Input, LinkButton, TextArea, useStyles2 } from '@grafana/ui';
|
import { Button, Field, FieldSet, Input, LinkButton, TextArea, useStyles2 } from '@grafana/ui';
|
||||||
import { useCleanup } from 'app/core/hooks/useCleanup';
|
import { alertSilencesApi } from 'app/features/alerting/unified/api/alertSilencesApi';
|
||||||
|
import { getDatasourceAPIUid } from 'app/features/alerting/unified/utils/datasource';
|
||||||
import { Matcher, MatcherOperator, Silence, SilenceCreatePayload } from 'app/plugins/datasource/alertmanager/types';
|
import { Matcher, MatcherOperator, Silence, SilenceCreatePayload } from 'app/plugins/datasource/alertmanager/types';
|
||||||
import { useDispatch } from 'app/types';
|
|
||||||
|
|
||||||
import { useURLSearchParams } from '../../hooks/useURLSearchParams';
|
import { useURLSearchParams } from '../../hooks/useURLSearchParams';
|
||||||
import { useUnifiedAlertingSelector } from '../../hooks/useUnifiedAlertingSelector';
|
|
||||||
import { createOrUpdateSilenceAction } from '../../state/actions';
|
|
||||||
import { SilenceFormFields } from '../../types/silence-form';
|
import { SilenceFormFields } from '../../types/silence-form';
|
||||||
import { matcherFieldToMatcher, matcherToMatcherField } from '../../utils/alertmanager';
|
import { matcherFieldToMatcher, matcherToMatcherField } from '../../utils/alertmanager';
|
||||||
import { parseQueryParamMatchers } from '../../utils/matchers';
|
import { parseQueryParamMatchers } from '../../utils/matchers';
|
||||||
import { makeAMLink } from '../../utils/misc';
|
import { makeAMLink } from '../../utils/misc';
|
||||||
import { initialAsyncRequestState } from '../../utils/redux';
|
|
||||||
|
|
||||||
import MatchersField from './MatchersField';
|
import MatchersField from './MatchersField';
|
||||||
import { SilencePeriod } from './SilencePeriod';
|
import { SilencePeriod } from './SilencePeriod';
|
||||||
import { SilencedInstancesPreview } from './SilencedInstancesPreview';
|
import { SilencedInstancesPreview } from './SilencedInstancesPreview';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
silence?: Silence;
|
silenceId?: string;
|
||||||
alertManagerSourceName: string;
|
alertManagerSourceName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,24 +95,24 @@ const getDefaultFormValues = (searchParams: URLSearchParams, silence?: Silence):
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SilencesEditor = ({ silence, alertManagerSourceName }: Props) => {
|
export const SilencesEditor = ({ silenceId, alertManagerSourceName }: Props) => {
|
||||||
|
const history = useHistory();
|
||||||
|
const [getSilence, { data: silence, isLoading: getSilenceIsLoading }] =
|
||||||
|
alertSilencesApi.endpoints.getSilence.useLazyQuery();
|
||||||
|
const [createSilence, { isLoading }] = alertSilencesApi.endpoints.createSilence.useMutation();
|
||||||
const [urlSearchParams] = useURLSearchParams();
|
const [urlSearchParams] = useURLSearchParams();
|
||||||
|
|
||||||
const defaultValues = useMemo(() => getDefaultFormValues(urlSearchParams, silence), [silence, urlSearchParams]);
|
const defaultValues = useMemo(() => getDefaultFormValues(urlSearchParams, silence), [silence, urlSearchParams]);
|
||||||
const formAPI = useForm({ defaultValues });
|
const formAPI = useForm({ defaultValues });
|
||||||
const dispatch = useDispatch();
|
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const [matchersForPreview, setMatchersForPreview] = useState<Matcher[]>(
|
const [matchersForPreview, setMatchersForPreview] = useState<Matcher[]>(
|
||||||
defaultValues.matchers.map(matcherFieldToMatcher)
|
defaultValues.matchers.map(matcherFieldToMatcher)
|
||||||
);
|
);
|
||||||
|
|
||||||
const { loading } = useUnifiedAlertingSelector((state) => state.updateSilence);
|
const { register, handleSubmit, formState, watch, setValue, clearErrors, reset } = formAPI;
|
||||||
|
|
||||||
useCleanup((state) => (state.unifiedAlerting.updateSilence = initialAsyncRequestState));
|
const onSubmit = async (data: SilenceFormFields) => {
|
||||||
|
|
||||||
const { register, handleSubmit, formState, watch, setValue, clearErrors } = formAPI;
|
|
||||||
|
|
||||||
const onSubmit = (data: SilenceFormFields) => {
|
|
||||||
const { id, startsAt, endsAt, comment, createdBy, matchers: matchersFields } = data;
|
const { id, startsAt, endsAt, comment, createdBy, matchers: matchersFields } = data;
|
||||||
const matchers = matchersFields.map(matcherFieldToMatcher);
|
const matchers = matchersFields.map(matcherFieldToMatcher);
|
||||||
const payload = pickBy(
|
const payload = pickBy(
|
||||||
@@ -128,14 +126,11 @@ export const SilencesEditor = ({ silence, alertManagerSourceName }: Props) => {
|
|||||||
},
|
},
|
||||||
(value) => !!value
|
(value) => !!value
|
||||||
) as SilenceCreatePayload;
|
) as SilenceCreatePayload;
|
||||||
dispatch(
|
await createSilence({ datasourceUid: getDatasourceAPIUid(alertManagerSourceName), payload })
|
||||||
createOrUpdateSilenceAction({
|
.unwrap()
|
||||||
alertManagerSourceName,
|
.then(() => {
|
||||||
payload,
|
history.push(makeAMLink('/alerting/silences', alertManagerSourceName));
|
||||||
exitOnSave: true,
|
});
|
||||||
successMessage: `Silence ${payload.id ? 'updated' : 'created'}`,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const duration = watch('duration');
|
const duration = watch('duration');
|
||||||
@@ -143,6 +138,16 @@ export const SilencesEditor = ({ silence, alertManagerSourceName }: Props) => {
|
|||||||
const endsAt = watch('endsAt');
|
const endsAt = watch('endsAt');
|
||||||
const matcherFields = watch('matchers');
|
const matcherFields = watch('matchers');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
reset(getDefaultFormValues(urlSearchParams, silence));
|
||||||
|
}, [reset, silence, urlSearchParams]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (silenceId) {
|
||||||
|
getSilence({ id: silenceId, datasourceUid: getDatasourceAPIUid(alertManagerSourceName) });
|
||||||
|
}
|
||||||
|
}, [alertManagerSourceName, getSilence, silenceId]);
|
||||||
|
|
||||||
// Keep duration and endsAt in sync
|
// Keep duration and endsAt in sync
|
||||||
const [prevDuration, setPrevDuration] = useState(duration);
|
const [prevDuration, setPrevDuration] = useState(duration);
|
||||||
useDebounce(
|
useDebounce(
|
||||||
@@ -183,10 +188,14 @@ export const SilencesEditor = ({ silence, alertManagerSourceName }: Props) => {
|
|||||||
|
|
||||||
const userLogged = Boolean(config.bootData.user.isSignedIn && config.bootData.user.name);
|
const userLogged = Boolean(config.bootData.user.isSignedIn && config.bootData.user.name);
|
||||||
|
|
||||||
|
if (getSilenceIsLoading) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...formAPI}>
|
<FormProvider {...formAPI}>
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
<form onSubmit={handleSubmit(onSubmit)}>
|
||||||
<FieldSet label={`${silence ? 'Recreate silence' : 'Create silence'}`}>
|
<FieldSet label={`${silenceId ? 'Recreate silence' : 'Create silence'}`}>
|
||||||
<div className={cx(styles.flexRow, styles.silencePeriod)}>
|
<div className={cx(styles.flexRow, styles.silencePeriod)}>
|
||||||
<SilencePeriod />
|
<SilencePeriod />
|
||||||
<Field
|
<Field
|
||||||
@@ -241,12 +250,12 @@ export const SilencesEditor = ({ silence, alertManagerSourceName }: Props) => {
|
|||||||
<SilencedInstancesPreview amSourceName={alertManagerSourceName} matchers={matchersForPreview} />
|
<SilencedInstancesPreview amSourceName={alertManagerSourceName} matchers={matchersForPreview} />
|
||||||
</FieldSet>
|
</FieldSet>
|
||||||
<div className={styles.flexRow}>
|
<div className={styles.flexRow}>
|
||||||
{loading && (
|
{isLoading && (
|
||||||
<Button disabled={true} icon="spinner" variant="primary">
|
<Button disabled={true} icon="spinner" variant="primary">
|
||||||
Saving...
|
Saving...
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{!loading && <Button type="submit">Save silence</Button>}
|
{!isLoading && <Button type="submit">Save silence</Button>}
|
||||||
<LinkButton href={makeAMLink('alerting/silences', alertManagerSourceName)} variant={'secondary'}>
|
<LinkButton href={makeAMLink('alerting/silences', alertManagerSourceName)} variant={'secondary'}>
|
||||||
Cancel
|
Cancel
|
||||||
</LinkButton>
|
</LinkButton>
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useEffect, useMemo } from 'react';
|
||||||
|
|
||||||
import { dateMath, GrafanaTheme2 } from '@grafana/data';
|
import { dateMath, GrafanaTheme2 } from '@grafana/data';
|
||||||
import { CollapsableSection, Icon, Link, LinkButton, useStyles2, Stack } from '@grafana/ui';
|
import { CollapsableSection, Icon, Link, LinkButton, useStyles2, Stack, Alert } from '@grafana/ui';
|
||||||
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
import { useQueryParams } from 'app/core/hooks/useQueryParams';
|
||||||
|
import { alertSilencesApi } from 'app/features/alerting/unified/api/alertSilencesApi';
|
||||||
|
import { alertmanagerApi } from 'app/features/alerting/unified/api/alertmanagerApi';
|
||||||
|
import { featureDiscoveryApi } from 'app/features/alerting/unified/api/featureDiscoveryApi';
|
||||||
|
import { SILENCES_POLL_INTERVAL_MS } from 'app/features/alerting/unified/utils/constants';
|
||||||
|
import { getDatasourceAPIUid } from 'app/features/alerting/unified/utils/datasource';
|
||||||
import { AlertmanagerAlert, Silence, SilenceState } from 'app/plugins/datasource/alertmanager/types';
|
import { AlertmanagerAlert, Silence, SilenceState } from 'app/plugins/datasource/alertmanager/types';
|
||||||
import { useDispatch } from 'app/types';
|
|
||||||
|
|
||||||
import { AlertmanagerAction, useAlertmanagerAbility } from '../../hooks/useAbilities';
|
import { AlertmanagerAction, useAlertmanagerAbility } from '../../hooks/useAbilities';
|
||||||
import { expireSilenceAction } from '../../state/actions';
|
|
||||||
import { parseMatchers } from '../../utils/alertmanager';
|
import { parseMatchers } from '../../utils/alertmanager';
|
||||||
import { getSilenceFiltersFromUrlParams, makeAMLink } from '../../utils/misc';
|
import { getSilenceFiltersFromUrlParams, makeAMLink } from '../../utils/misc';
|
||||||
import { Authorize } from '../Authorize';
|
import { Authorize } from '../Authorize';
|
||||||
@@ -29,12 +32,30 @@ export interface SilenceTableItem extends Silence {
|
|||||||
type SilenceTableColumnProps = DynamicTableColumnProps<SilenceTableItem>;
|
type SilenceTableColumnProps = DynamicTableColumnProps<SilenceTableItem>;
|
||||||
type SilenceTableItemProps = DynamicTableItemProps<SilenceTableItem>;
|
type SilenceTableItemProps = DynamicTableItemProps<SilenceTableItem>;
|
||||||
interface Props {
|
interface Props {
|
||||||
silences: Silence[];
|
|
||||||
alertManagerAlerts: AlertmanagerAlert[];
|
|
||||||
alertManagerSourceName: string;
|
alertManagerSourceName: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SilencesTable = ({ silences, alertManagerAlerts, alertManagerSourceName }: Props) => {
|
const SilencesTable = ({ alertManagerSourceName }: Props) => {
|
||||||
|
const [getAmAlerts, { data: alertManagerAlerts, isLoading: amAlertsIsLoading }] =
|
||||||
|
alertmanagerApi.endpoints.getAlertmanagerAlerts.useLazyQuery({ pollingInterval: SILENCES_POLL_INTERVAL_MS });
|
||||||
|
const [getSilences, { data: silences = [], isLoading, error }] = alertSilencesApi.endpoints.getSilences.useLazyQuery({
|
||||||
|
pollingInterval: SILENCES_POLL_INTERVAL_MS,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { currentData: amFeatures } = featureDiscoveryApi.useDiscoverAmFeaturesQuery(
|
||||||
|
{ amSourceName: alertManagerSourceName ?? '' },
|
||||||
|
{ skip: !alertManagerSourceName }
|
||||||
|
);
|
||||||
|
|
||||||
|
const mimirLazyInitError =
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
(error as any)?.message?.includes('the Alertmanager is not configured') && amFeatures?.lazyConfigInit;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getSilences({ datasourceUid: getDatasourceAPIUid(alertManagerSourceName) });
|
||||||
|
getAmAlerts({ amSourceName: alertManagerSourceName });
|
||||||
|
}, [alertManagerSourceName, getAmAlerts, getSilences]);
|
||||||
|
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const [queryParams] = useQueryParams();
|
const [queryParams] = useQueryParams();
|
||||||
const filteredSilencesNotExpired = useFilteredSilences(silences, false);
|
const filteredSilencesNotExpired = useFilteredSilences(silences, false);
|
||||||
@@ -45,7 +66,7 @@ const SilencesTable = ({ silences, alertManagerAlerts, alertManagerSourceName }:
|
|||||||
|
|
||||||
const itemsNotExpired = useMemo((): SilenceTableItemProps[] => {
|
const itemsNotExpired = useMemo((): SilenceTableItemProps[] => {
|
||||||
const findSilencedAlerts = (id: string) => {
|
const findSilencedAlerts = (id: string) => {
|
||||||
return alertManagerAlerts.filter((alert) => alert.status.silencedBy.includes(id));
|
return (alertManagerAlerts || []).filter((alert) => alert.status.silencedBy.includes(id));
|
||||||
};
|
};
|
||||||
return filteredSilencesNotExpired.map((silence) => {
|
return filteredSilencesNotExpired.map((silence) => {
|
||||||
const silencedAlerts = findSilencedAlerts(silence.id);
|
const silencedAlerts = findSilencedAlerts(silence.id);
|
||||||
@@ -58,7 +79,7 @@ const SilencesTable = ({ silences, alertManagerAlerts, alertManagerSourceName }:
|
|||||||
|
|
||||||
const itemsExpired = useMemo((): SilenceTableItemProps[] => {
|
const itemsExpired = useMemo((): SilenceTableItemProps[] => {
|
||||||
const findSilencedAlerts = (id: string) => {
|
const findSilencedAlerts = (id: string) => {
|
||||||
return alertManagerAlerts.filter((alert) => alert.status.silencedBy.includes(id));
|
return (alertManagerAlerts || []).filter((alert) => alert.status.silencedBy.includes(id));
|
||||||
};
|
};
|
||||||
return filteredSilencesExpired.map((silence) => {
|
return filteredSilencesExpired.map((silence) => {
|
||||||
const silencedAlerts = findSilencedAlerts(silence.id);
|
const silencedAlerts = findSilencedAlerts(silence.id);
|
||||||
@@ -69,6 +90,29 @@ const SilencesTable = ({ silences, alertManagerAlerts, alertManagerSourceName }:
|
|||||||
});
|
});
|
||||||
}, [filteredSilencesExpired, alertManagerAlerts]);
|
}, [filteredSilencesExpired, alertManagerAlerts]);
|
||||||
|
|
||||||
|
if (isLoading || amAlertsIsLoading) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mimirLazyInitError) {
|
||||||
|
return (
|
||||||
|
<Alert title="The selected Alertmanager has no configuration" severity="warning">
|
||||||
|
Create a new contact point to create a configuration using the default values or contact your administrator to
|
||||||
|
set up the Alertmanager.
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const errMessage = (error as any)?.message || 'Unknown error.';
|
||||||
|
return (
|
||||||
|
<Alert severity="error" title="Error loading silences">
|
||||||
|
{errMessage}
|
||||||
|
</Alert>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="silences-table">
|
<div data-testid="silences-table">
|
||||||
{!!silences.length && (
|
{!!silences.length && (
|
||||||
@@ -169,43 +213,43 @@ const useFilteredSilences = (silences: Silence[], expired = false) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
const getStyles = (theme: GrafanaTheme2) => ({
|
||||||
topButtonContainer: css`
|
topButtonContainer: css({
|
||||||
display: flex;
|
display: 'flex',
|
||||||
flex-direction: row;
|
flexDirection: 'row',
|
||||||
justify-content: flex-end;
|
justifyContent: 'flex-end',
|
||||||
`,
|
}),
|
||||||
addNewSilence: css`
|
addNewSilence: css({
|
||||||
margin: ${theme.spacing(2, 0)};
|
margin: theme.spacing(2, 0),
|
||||||
`,
|
}),
|
||||||
callout: css`
|
callout: css({
|
||||||
background-color: ${theme.colors.background.secondary};
|
backgroundColor: theme.colors.background.secondary,
|
||||||
border-top: 3px solid ${theme.colors.info.border};
|
borderTop: `3px solid ${theme.colors.info.border}`,
|
||||||
border-radius: ${theme.shape.radius.default};
|
borderRadius: theme.shape.radius.default,
|
||||||
height: 62px;
|
height: '62px',
|
||||||
display: flex;
|
display: 'flex',
|
||||||
flex-direction: row;
|
flexDirection: 'row',
|
||||||
align-items: center;
|
alignItems: 'center',
|
||||||
|
|
||||||
& > * {
|
'& > *': {
|
||||||
margin-left: ${theme.spacing(1)};
|
marginLeft: theme.spacing(1),
|
||||||
}
|
},
|
||||||
`,
|
}),
|
||||||
calloutIcon: css`
|
calloutIcon: css({
|
||||||
color: ${theme.colors.info.text};
|
color: theme.colors.info.text,
|
||||||
`,
|
}),
|
||||||
editButton: css`
|
editButton: css({
|
||||||
margin-left: ${theme.spacing(0.5)};
|
marginLeft: theme.spacing(0.5),
|
||||||
`,
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
function useColumns(alertManagerSourceName: string) {
|
function useColumns(alertManagerSourceName: string) {
|
||||||
const dispatch = useDispatch();
|
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const [updateSupported, updateAllowed] = useAlertmanagerAbility(AlertmanagerAction.UpdateSilence);
|
const [updateSupported, updateAllowed] = useAlertmanagerAbility(AlertmanagerAction.UpdateSilence);
|
||||||
|
const [expireSilence] = alertSilencesApi.endpoints.expireSilence.useMutation();
|
||||||
|
|
||||||
return useMemo((): SilenceTableColumnProps[] => {
|
return useMemo((): SilenceTableColumnProps[] => {
|
||||||
const handleExpireSilenceClick = (id: string) => {
|
const handleExpireSilenceClick = (silenceId: string) => {
|
||||||
dispatch(expireSilenceAction(alertManagerSourceName, id));
|
expireSilence({ datasourceUid: getDatasourceAPIUid(alertManagerSourceName), silenceId });
|
||||||
};
|
};
|
||||||
const columns: SilenceTableColumnProps[] = [
|
const columns: SilenceTableColumnProps[] = [
|
||||||
{
|
{
|
||||||
@@ -281,6 +325,6 @@ function useColumns(alertManagerSourceName: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return columns;
|
return columns;
|
||||||
}, [alertManagerSourceName, dispatch, styles.editButton, updateAllowed, updateSupported]);
|
}, [alertManagerSourceName, expireSilence, styles.editButton, updateAllowed, updateSupported]);
|
||||||
}
|
}
|
||||||
export default SilencesTable;
|
export default SilencesTable;
|
||||||
|
|||||||
Reference in New Issue
Block a user