[SLO] Add Auto-Refresh button on SLO List (#152460)

This commit is contained in:
Coen Warmer 2023-03-02 09:04:36 +01:00 committed by GitHub
parent b3c3461fe1
commit cb96fea0d9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 165 additions and 13 deletions

View file

@ -20,6 +20,8 @@ export const useFetchHistoricalSummary = ({
return {
isLoading: false,
isInitialLoading: false,
isRefetching: false,
isSuccess: false,
isError: false,
sloHistoricalSummaryResponse: data,

View file

@ -10,10 +10,10 @@ import { FetchHistoricalSummaryResponse } from '@kbn/slo-schema';
import { useKibana } from '../../utils/kibana_react';
const EMPTY_RESPONSE: FetchHistoricalSummaryResponse = {};
export interface UseFetchHistoricalSummaryResponse {
sloHistoricalSummaryResponse: FetchHistoricalSummaryResponse;
sloHistoricalSummaryResponse: FetchHistoricalSummaryResponse | undefined;
isInitialLoading: boolean;
isRefetching: boolean;
isLoading: boolean;
isSuccess: boolean;
isError: boolean;
@ -49,8 +49,10 @@ export function useFetchHistoricalSummary({
});
return {
sloHistoricalSummaryResponse: isInitialLoading ? EMPTY_RESPONSE : data ?? EMPTY_RESPONSE,
isLoading: isInitialLoading || isLoading || isRefetching,
sloHistoricalSummaryResponse: data,
isLoading,
isRefetching,
isInitialLoading,
isSuccess,
isError,
};

View file

@ -5,11 +5,13 @@
* 2.0.
*/
import { useState } from 'react';
import {
QueryObserverResult,
RefetchOptions,
RefetchQueryFilters,
useQuery,
useQueryClient,
} from '@tanstack/react-query';
import { FindSLOResponse } from '@kbn/slo-schema';
@ -20,6 +22,7 @@ interface SLOListParams {
page?: number;
sortBy?: string;
indicatorTypes?: string[];
shouldRefetch?: boolean;
}
export interface UseFetchSloListResponse {
@ -33,13 +36,20 @@ export interface UseFetchSloListResponse {
) => Promise<QueryObserverResult<FindSLOResponse | undefined, unknown>>;
}
const SHORT_REFETCH_INTERVAL = 1000 * 5; // 5 seconds
const LONG_REFETCH_INTERVAL = 1000 * 60; // 1 minute
export function useFetchSloList({
name = '',
page = 1,
sortBy = 'name',
indicatorTypes = [],
shouldRefetch,
}: SLOListParams | undefined = {}): UseFetchSloListResponse {
const { http } = useKibana().services;
const queryClient = useQueryClient();
const [stateRefetchInterval, setStateRefetchInterval] = useState(SHORT_REFETCH_INTERVAL);
const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data, refetch } = useQuery(
{
@ -64,9 +74,29 @@ export function useFetchSloList({
// ignore error
}
},
refetchOnWindowFocus: false,
keepPreviousData: true,
refetchOnWindowFocus: false,
refetchInterval: shouldRefetch ? stateRefetchInterval : undefined,
staleTime: 1000,
onSuccess: ({ results }: FindSLOResponse) => {
if (!shouldRefetch) {
return;
}
if (results.find((slo) => slo.summary.status === 'NO_DATA')) {
setStateRefetchInterval(SHORT_REFETCH_INTERVAL);
} else {
setStateRefetchInterval(LONG_REFETCH_INTERVAL);
}
queryClient.invalidateQueries(['fetchHistoricalSummary'], {
exact: false,
});
queryClient.invalidateQueries(['fetchActiveAlerts'], {
exact: false,
});
},
}
);

View file

@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useState } from 'react';
import { ComponentStory } from '@storybook/react';
import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator';
import { AutoRefreshButton as Component } from './auto_refresh_button';
export default {
component: Component,
title: 'app/SLO/ListPage/AutoRefreshButton',
decorators: [KibanaReactStorybookDecorator],
};
const Template: ComponentStory<typeof Component> = () => {
const [isAutoRefreshing, setIsAutoRefreshing] = useState(true);
const toggleEnabled = () => {
setIsAutoRefreshing(!isAutoRefreshing);
};
return <Component isAutoRefreshing={isAutoRefreshing} onClick={toggleEnabled} />;
};
const defaultProps = {
enabled: true,
disabled: false,
};
export const AutoRefreshButton = Template.bind({});
AutoRefreshButton.args = defaultProps;

View file

@ -0,0 +1,49 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import { EuiButtonEmpty } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
interface Props {
isAutoRefreshing: boolean;
dataTestSubj?: string;
disabled?: boolean;
onClick: () => void;
}
export function AutoRefreshButton({
dataTestSubj = 'autoRefreshButton',
disabled,
isAutoRefreshing,
onClick,
}: Props) {
return isAutoRefreshing ? (
<EuiButtonEmpty
data-test-subj={dataTestSubj}
disabled={disabled}
iconSide="left"
iconType="pause"
onClick={onClick}
>
{i18n.translate('xpack.observability.slosPage.stopRefreshingButtonLabel', {
defaultMessage: 'Stop refreshing',
})}
</EuiButtonEmpty>
) : (
<EuiButtonEmpty
data-test-subj={dataTestSubj}
disabled={disabled}
iconSide="left"
iconType="play"
onClick={onClick}
>
{i18n.translate('xpack.observability.slosPage.autoRefreshButtonLabel', {
defaultMessage: 'Auto-refresh',
})}
</EuiButtonEmpty>
);
}

View file

@ -9,7 +9,7 @@ import React from 'react';
import { ComponentStory } from '@storybook/react';
import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator';
import { SloList as Component } from './slo_list';
import { SloList as Component, Props } from './slo_list';
export default {
component: Component,
@ -18,9 +18,11 @@ export default {
decorators: [KibanaReactStorybookDecorator],
};
const Template: ComponentStory<typeof Component> = () => <Component />;
const Template: ComponentStory<typeof Component> = (props: Props) => <Component {...props} />;
const defaultProps = {};
const defaultProps = {
autoRefresh: true,
};
export const SloList = Template.bind({});
SloList.args = defaultProps;

View file

@ -18,7 +18,11 @@ import {
} from './slo_list_search_filter_sort_bar';
import { SloListItems } from './slo_list_items';
export function SloList() {
export interface Props {
autoRefresh: boolean;
}
export function SloList({ autoRefresh }: Props) {
const [activePage, setActivePage] = useState(0);
const [query, setQuery] = useState('');
@ -30,6 +34,7 @@ export function SloList() {
name: query,
sortBy: sort,
indicatorTypes: indicatorTypeFilter,
shouldRefetch: autoRefresh,
});
const { results = [], total = 0, perPage = 0 } = sloList || {};

View file

@ -41,7 +41,7 @@ export function SloListItems({ sloList, loading, error }: Props) {
<EuiFlexItem key={slo.id}>
<SloListItem
slo={slo}
historicalSummary={sloHistoricalSummaryResponse[slo.id]}
historicalSummary={sloHistoricalSummaryResponse?.[slo.id]}
historicalSummaryLoading={historicalSummaryLoading}
activeAlerts={activeAlertsBySlo[slo.id]}
/>

View file

@ -155,6 +155,21 @@ describe('SLOs Page', () => {
expect(screen.getByText('Create new SLO')).toBeTruthy();
});
it('should have an Auto Refresh button', async () => {
useFetchSloListMock.mockReturnValue({ isLoading: false, sloList });
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: false,
sloHistoricalSummaryResponse: historicalSummaryData,
});
await act(async () => {
render(<SlosPage />, config);
});
expect(screen.getByTestId('autoRefreshButton')).toBeTruthy();
});
describe('when API has returned results', () => {
it('renders the SLO list with SLO items', async () => {
useFetchSloListMock.mockReturnValue({ isLoading: false, sloList });

View file

@ -5,7 +5,7 @@
* 2.0.
*/
import React from 'react';
import React, { useState } from 'react';
import { EuiButton } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
@ -17,6 +17,7 @@ import { useCapabilities } from '../../hooks/slo/use_capabilities';
import { useFetchSloList } from '../../hooks/slo/use_fetch_slo_list';
import { SloList } from './components/slo_list';
import { SloListWelcomePrompt } from './components/slo_list_welcome_prompt';
import { AutoRefreshButton } from './components/auto_refresh_button';
import PageNotFound from '../404';
import { paths } from '../../config';
import { isSloFeatureEnabled } from './helpers/is_slo_feature_enabled';
@ -35,6 +36,8 @@ export function SlosPage() {
const { total } = sloList || {};
const [isAutoRefreshing, setIsAutoRefreshing] = useState<boolean>(true);
useBreadcrumbs([
{
href: basePath.prepend(paths.observability.slos),
@ -48,6 +51,10 @@ export function SlosPage() {
navigateToUrl(basePath.prepend(paths.observability.sloCreate));
};
const handleToggleAutoRefresh = () => {
setIsAutoRefreshing(!isAutoRefreshing);
};
if (!isSloFeatureEnabled(config)) {
return <PageNotFound />;
}
@ -78,12 +85,16 @@ export function SlosPage() {
defaultMessage: 'Create new SLO',
})}
</EuiButton>,
<AutoRefreshButton
isAutoRefreshing={isAutoRefreshing}
onClick={handleToggleAutoRefresh}
/>,
],
bottomBorder: false,
}}
data-test-subj="slosPage"
>
<SloList />
<SloList autoRefresh={isAutoRefreshing} />
</ObservabilityPageTemplate>
);
}