Skip to content

Commit

Permalink
[Synthetics] Fix/refactor usage of default url params, fixes loading …
Browse files Browse the repository at this point in the history
…states (#150367)

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
shahzad31 and kibanamachine authored Feb 21, 2023
1 parent c074504 commit db251ed
Show file tree
Hide file tree
Showing 69 changed files with 651 additions and 235 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { AddToCaseAction } from '../header/add_to_case_action';
import { observabilityFeatureId } from '../../../../../common';

export interface ExploratoryEmbeddableProps {
id?: string;
appId?: 'securitySolutionUI' | 'observability';
appendTitle?: JSX.Element;
attributes?: AllSeries;
Expand All @@ -46,6 +47,7 @@ export interface ExploratoryEmbeddableProps {
legendPosition?: Position;
hideTicks?: boolean;
onBrushEnd?: (param: { range: number[] }) => void;
onLoad?: (loading: boolean) => void;
caseOwner?: string;
reportConfigMap?: ReportConfigMap;
reportType: ReportViewType;
Expand All @@ -58,6 +60,7 @@ export interface ExploratoryEmbeddableProps {
fontSize?: number;
lineHeight?: number;
dataTestSubj?: string;
searchSessionId?: string;
}

export interface ExploratoryEmbeddableComponentProps extends ExploratoryEmbeddableProps {
Expand Down Expand Up @@ -93,6 +96,8 @@ export default function Embeddable({
noLabel,
fontSize = 27,
lineHeight = 32,
searchSessionId,
onLoad,
}: ExploratoryEmbeddableComponentProps) {
const LensComponent = lens?.EmbeddableComponent;
const LensSaveModalComponent = lens?.SaveModalComponent;
Expand Down Expand Up @@ -188,6 +193,9 @@ export default function Embeddable({
return <EuiText>No lens component</EuiText>;
}

attributesJSON.state.searchSessionId = searchSessionId;
attributesJSON.searchSessionId = searchSessionId;

return (
<Wrapper
$customHeight={customHeight}
Expand Down Expand Up @@ -229,6 +237,8 @@ export default function Embeddable({
withDefaultActions={Boolean(withActions)}
extraActions={actions}
viewMode={ViewMode.VIEW}
searchSessionId={searchSessionId}
onLoad={onLoad}
/>
{isSaveOpen && attributesJSON && (
<LensSaveModalComponent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
import { EuiThemeProvider } from '@kbn/kibana-react-plugin/common';
import type { CoreStart } from '@kbn/core/public';
Expand Down Expand Up @@ -38,7 +38,26 @@ export function getExploratoryViewEmbeddable(

const lenStateHelperPromise: Promise<{ formula: FormulaPublicApi }> | null = null;

const lastRefreshed: Record<string, { from: string; to: string }> = {};

const hasSameTimeRange = (props: ExploratoryEmbeddableProps) => {
const { attributes } = props;
if (!attributes || attributes?.length === 0) {
return false;
}
const series = attributes[0];
const { time } = series;
const { from, to } = time;
return attributes.every((seriesT) => {
const { time: timeT } = seriesT;
return timeT.from === from && timeT.to === to;
});
};

return (props: ExploratoryEmbeddableProps) => {
if (!services.data.search.session.getSessionId()) {
services.data.search.session.start();
}
const { dataTypesIndexPatterns, attributes, customHeight } = props;

if (!dataViewsService || !lens || !attributes || attributes?.length === 0) {
Expand All @@ -56,13 +75,43 @@ export function getExploratoryViewEmbeddable(
return lens.stateHelperApi();
}, []);

const [loadCount, setLoadCount] = useState(0);

const onLensLoaded = useCallback(
(lensLoaded: boolean) => {
if (lensLoaded && props.id && hasSameTimeRange(props) && !lastRefreshed[props.id]) {
lastRefreshed[props.id] = series.time;
}
setLoadCount((prev) => prev + 1);
},
[props, series.time]
);

const { dataViews, loading } = useAppDataView({
dataViewCache,
dataViewsService,
dataTypesIndexPatterns,
seriesDataType: series?.dataType,
});

const embedProps = useMemo(() => {
const newProps = { ...props };
if (props.sparklineMode) {
newProps.axisTitlesVisibility = { x: false, yRight: false, yLeft: false };
newProps.legendIsVisible = false;
newProps.hideTicks = true;
}
if (props.id && lastRefreshed[props.id] && loadCount < 2) {
newProps.attributes = props.attributes?.map((seriesT) => ({
...seriesT,
time: lastRefreshed[props.id!],
}));
} else if (props.id) {
lastRefreshed[props.id] = series.time;
}
return newProps;
}, [loadCount, props, series.time]);

if (Object.keys(dataViews).length === 0 || loading || !lensHelper || lensLoading) {
return (
<LoadingWrapper customHeight={customHeight}>
Expand All @@ -75,13 +124,6 @@ export function getExploratoryViewEmbeddable(
return <EmptyState height={props.customHeight} />;
}

const embedProps = { ...props };
if (props.sparklineMode) {
embedProps.axisTitlesVisibility = { x: false, yRight: false, yLeft: false };
embedProps.legendIsVisible = false;
embedProps.hideTicks = true;
}

return (
<EuiErrorBoundary>
<EuiThemeProvider darkMode={isDarkMode}>
Expand All @@ -92,6 +134,8 @@ export function getExploratoryViewEmbeddable(
dataViewState={dataViews}
lens={lens}
lensFormulaHelper={lensHelper.formula}
searchSessionId={services.data.search.session.getSessionId()}
onLoad={onLensLoaded}
/>
</Wrapper>
</KibanaContextProvider>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ export const CLIENT_DEFAULTS_SYNTHETICS = {
* The end of the default date range is now.
*/
DATE_RANGE_END: 'now',

/**
* The application auto refreshes every 30s by default.
*/
AUTOREFRESH_INTERVAL_SECONDS: 60,
/**
* The application's autorefresh feature is enabled.
*/
AUTOREFRESH_IS_PAUSED: false,
};
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ journey(`DefaultStatusAlert`, async ({ page, params }) => {
});

step('Go to monitors page', async () => {
await syntheticsApp.navigateToOverview(true);
await syntheticsApp.navigateToOverview(true, 15);
});

step('should create default status alert', async () => {
Expand Down Expand Up @@ -194,6 +194,7 @@ journey(`DefaultStatusAlert`, async ({ page, params }) => {

await page.click(byTestId('alert-status-filter-active-button'));
await syntheticsApp.waitForLoadingToFinish();
await page.waitForTimeout(10 * 1000);

await page.click('[aria-label="View in app"]');
await page.click(byTestId('syntheticsMonitorOverviewTab'));
Expand Down
2 changes: 1 addition & 1 deletion x-pack/plugins/synthetics/e2e/journeys/synthetics/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export * from './private_locations.journey';
export * from './alerting_default.journey';
export * from './global_parameters.journey';
export * from './detail_flyout';
// export * from './alert_rules/default_status_alert.journey';
export * from './alert_rules/default_status_alert.journey';
export * from './test_now_mode.journey';
export * from './data_retention.journey';
export * from './monitor_details_page/monitor_summary.journey';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ journey('OverviewSorting', async ({ page, params }) => {
});

step('Go to monitor-management', async () => {
await syntheticsApp.navigateToOverview(true);
await syntheticsApp.navigateToOverview(true, 15);
});

step('sort alphabetical asc', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,14 @@ export function syntheticsAppPageProvider({ page, kibanaUrl }: { page: Page; kib
await this.waitForMonitorManagementLoadingToFinish();
},

async navigateToOverview(doLogin = false) {
await page.goto(overview, { waitUntil: 'networkidle' });
async navigateToOverview(doLogin = false, refreshInterval?: number) {
if (refreshInterval) {
await page.goto(`${overview}?refreshInterval=${refreshInterval}`, {
waitUntil: 'networkidle',
});
} else {
await page.goto(overview, { waitUntil: 'networkidle' });
}
if (doLogin) {
await this.loginToKibana();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* 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, { useEffect, useRef } from 'react';
import { EuiAutoRefreshButton, OnRefreshChangeProps } from '@elastic/eui';
import { useDispatch, useSelector } from 'react-redux';
import { CLIENT_DEFAULTS_SYNTHETICS } from '../../../../../../common/constants/synthetics/client_defaults';
import { SyntheticsUrlParams } from '../../../utils/url_params';
import { useUrlParams } from '../../../hooks';
import {
selectRefreshInterval,
selectRefreshPaused,
setRefreshIntervalAction,
setRefreshPausedAction,
} from '../../../state';
const { AUTOREFRESH_INTERVAL_SECONDS, AUTOREFRESH_IS_PAUSED } = CLIENT_DEFAULTS_SYNTHETICS;

const replaceDefaults = ({ refreshPaused, refreshInterval }: Partial<SyntheticsUrlParams>) => {
return {
refreshInterval: refreshInterval === AUTOREFRESH_INTERVAL_SECONDS ? undefined : refreshInterval,
refreshPaused: refreshPaused === AUTOREFRESH_IS_PAUSED ? undefined : refreshPaused,
};
};
export const AutoRefreshButton = () => {
const dispatch = useDispatch();

const refreshPaused = useSelector(selectRefreshPaused);
const refreshInterval = useSelector(selectRefreshInterval);

const [getUrlsParams, updateUrlParams] = useUrlParams();

const { refreshInterval: urlRefreshInterval, refreshPaused: urlIsPaused } = getUrlsParams();

const isFirstRender = useRef(true);

useEffect(() => {
if (isFirstRender.current) {
// sync url state with redux state on first render
dispatch(setRefreshIntervalAction(urlRefreshInterval));
dispatch(setRefreshPausedAction(urlIsPaused));
isFirstRender.current = false;
} else {
// sync redux state with url state on subsequent renders
if (urlRefreshInterval !== refreshInterval || urlIsPaused !== refreshPaused) {
updateUrlParams(
replaceDefaults({
refreshInterval,
refreshPaused,
}),
true
);
}
}
}, [updateUrlParams, refreshInterval, refreshPaused, urlRefreshInterval, urlIsPaused, dispatch]);

const onRefreshChange = (newProps: OnRefreshChangeProps) => {
dispatch(setRefreshIntervalAction(newProps.refreshInterval / 1000));
dispatch(setRefreshPausedAction(newProps.isPaused));

updateUrlParams(
replaceDefaults({
refreshInterval: newProps.refreshInterval / 1000,
refreshPaused: newProps.isPaused,
}),
true
);
};

return (
<EuiAutoRefreshButton
size="m"
isPaused={refreshPaused}
refreshInterval={refreshInterval * 1000}
onRefreshChange={onRefreshChange}
shortHand
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* 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, { useEffect, useState } from 'react';
import moment from 'moment';
import { EuiText } from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n-react';
import { useSelector } from 'react-redux';
import { SHORT_TIMESPAN_LOCALE, SHORT_TS_LOCALE } from '../../../../../../common/constants';
import { useSyntheticsRefreshContext } from '../../../contexts';
import { selectRefreshPaused } from '../../../state';

export function LastRefreshed() {
const { lastRefresh: lastRefreshed } = useSyntheticsRefreshContext();
const [refresh, setRefresh] = useState(() => Date.now());

const refreshPaused = useSelector(selectRefreshPaused);

useEffect(() => {
const interVal = setInterval(() => {
setRefresh(Date.now());
}, 5000);

return () => {
clearInterval(interVal);
};
}, []);

useEffect(() => {
setRefresh(Date.now());
}, [lastRefreshed]);

if (!lastRefreshed || refreshPaused) {
return null;
}

const isWarning = moment().diff(moment(lastRefreshed), 'minutes') > 1;
const isDanger = moment().diff(moment(lastRefreshed), 'minutes') > 5;

const prevLocal: string = moment.locale() ?? 'en';

const shortLocale = moment.locale(SHORT_TS_LOCALE) === SHORT_TS_LOCALE;
if (!shortLocale) {
moment.defineLocale(SHORT_TS_LOCALE, SHORT_TIMESPAN_LOCALE);
}

const updatedDate = moment(lastRefreshed).from(refresh);

// Need to reset locale so it doesn't effect other parts of the app
moment.locale(prevLocal);

return (
<EuiText
color={isDanger ? 'danger' : isWarning ? 'warning' : 'subdued'}
size="s"
css={{ lineHeight: '40px', fontWeight: isWarning ? 'bold' : undefined }}
>
<FormattedMessage
id="xpack.synthetics.lastUpdated.label"
defaultMessage="Updated {updatedDate}"
values={{
updatedDate,
}}
/>
</EuiText>
);
}
Loading

0 comments on commit db251ed

Please sign in to comment.