mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
🌊 Streams: Move datepicker into chart panel on overview page (#217014)
This PR moves the datepicker on the overview page into the chart panel to claim some additional vertical screen space and to make it clearer what the stats on top of the page mean: <img width="1016" alt="Screenshot 2025-04-03 at 14 58 27" src="https://github.com/user-attachments/assets/b0100a3e-e9c4-419e-9803-45558b8a0fad" /> It also refactors the code a bit and reduces prop drilling in some areas. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
6a0c173b1a
commit
10358d6f9e
9 changed files with 223 additions and 286 deletions
|
@ -7,18 +7,19 @@
|
|||
|
||||
import React from 'react';
|
||||
import { EuiLink } from '@elastic/eui';
|
||||
import { IlmLocatorParams } from '@kbn/index-lifecycle-management-common-shared';
|
||||
import { LocatorPublic } from '@kbn/share-plugin/common';
|
||||
import { ILM_LOCATOR_ID, IlmLocatorParams } from '@kbn/index-lifecycle-management-common-shared';
|
||||
import { IngestStreamLifecycleILM } from '@kbn/streams-schema';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useKibana } from '../../../hooks/use_kibana';
|
||||
|
||||
export function IlmLink({
|
||||
ilmLocator,
|
||||
lifecycle,
|
||||
}: {
|
||||
ilmLocator?: LocatorPublic<IlmLocatorParams>;
|
||||
lifecycle: IngestStreamLifecycleILM;
|
||||
}) {
|
||||
export function IlmLink({ lifecycle }: { lifecycle: IngestStreamLifecycleILM }) {
|
||||
const {
|
||||
dependencies: {
|
||||
start: { share },
|
||||
},
|
||||
} = useKibana();
|
||||
|
||||
const ilmLocator = share.url.locators.get<IlmLocatorParams>(ILM_LOCATOR_ID);
|
||||
return (
|
||||
<EuiLink
|
||||
target="_blank"
|
||||
|
|
|
@ -28,8 +28,6 @@ import {
|
|||
formatNumber,
|
||||
useEuiTheme,
|
||||
} from '@elastic/eui';
|
||||
import { LocatorPublic } from '@kbn/share-plugin/common';
|
||||
import { IlmLocatorParams } from '@kbn/index-lifecycle-management-common-shared';
|
||||
import { useStreamsAppFetch } from '../../../hooks/use_streams_app_fetch';
|
||||
import { useKibana } from '../../../hooks/use_kibana';
|
||||
import { orderIlmPhases, parseDurationInSeconds } from './helpers';
|
||||
|
@ -39,11 +37,9 @@ import { useIlmPhasesColorAndDescription } from './hooks/use_ilm_phases_color_an
|
|||
export function IlmSummary({
|
||||
definition,
|
||||
lifecycle,
|
||||
ilmLocator,
|
||||
}: {
|
||||
definition: IngestStreamGetResponse;
|
||||
lifecycle: IngestStreamLifecycleILM;
|
||||
ilmLocator?: LocatorPublic<IlmLocatorParams>;
|
||||
}) {
|
||||
const {
|
||||
dependencies: {
|
||||
|
@ -104,7 +100,7 @@ export function IlmSummary({
|
|||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<IlmLink lifecycle={lifecycle} ilmLocator={ilmLocator} />
|
||||
<IlmLink lifecycle={lifecycle} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
|
|
|
@ -16,11 +16,7 @@ import {
|
|||
isUnwiredStreamGetResponse,
|
||||
isWiredStreamGetResponse,
|
||||
} from '@kbn/streams-schema';
|
||||
import {
|
||||
ILM_LOCATOR_ID,
|
||||
IlmLocatorParams,
|
||||
PolicyFromES,
|
||||
} from '@kbn/index-lifecycle-management-common-shared';
|
||||
import { PolicyFromES } from '@kbn/index-lifecycle-management-common-shared';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { useAbortController } from '@kbn/react-hooks';
|
||||
import { useKibana } from '../../../hooks/use_kibana';
|
||||
|
@ -98,7 +94,6 @@ export function StreamDetailLifecycle({
|
|||
core: { http, notifications },
|
||||
dependencies: {
|
||||
start: {
|
||||
share,
|
||||
streams: { streamsRepositoryClient },
|
||||
},
|
||||
},
|
||||
|
@ -122,8 +117,6 @@ export function StreamDetailLifecycle({
|
|||
|
||||
const { signal } = useAbortController();
|
||||
|
||||
const ilmLocator = share.url.locators.get<IlmLocatorParams>(ILM_LOCATOR_ID);
|
||||
|
||||
const getIlmPolicies = () =>
|
||||
http.get<PolicyFromES[]>('/api/index_lifecycle_management/policies', {
|
||||
signal,
|
||||
|
@ -177,7 +170,6 @@ export function StreamDetailLifecycle({
|
|||
updateLifecycle={updateLifecycle}
|
||||
getIlmPolicies={getIlmPolicies}
|
||||
updateInProgress={updateInProgress}
|
||||
ilmLocator={ilmLocator}
|
||||
/>
|
||||
|
||||
<EuiPanel grow={false} hasShadow={false} hasBorder paddingSize="s">
|
||||
|
@ -190,7 +182,6 @@ export function StreamDetailLifecycle({
|
|||
<RetentionMetadata
|
||||
definition={definition}
|
||||
lifecycleActions={lifecycleActions}
|
||||
ilmLocator={ilmLocator}
|
||||
openEditModal={(action) => setOpenEditModal(action)}
|
||||
isLoadingStats={isLoadingStats}
|
||||
stats={stats}
|
||||
|
@ -218,11 +209,7 @@ export function StreamDetailLifecycle({
|
|||
{isIlmLifecycle(definition.effective_lifecycle) ? (
|
||||
<EuiFlexItem grow={3}>
|
||||
<EuiPanel grow={true} hasShadow={false} hasBorder paddingSize="s">
|
||||
<IlmSummary
|
||||
definition={definition}
|
||||
lifecycle={definition.effective_lifecycle}
|
||||
ilmLocator={ilmLocator}
|
||||
/>
|
||||
<IlmSummary definition={definition} lifecycle={definition.effective_lifecycle} />
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
|
|
|
@ -5,8 +5,6 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { IlmLocatorParams } from '@kbn/index-lifecycle-management-common-shared';
|
||||
import { LocatorPublic } from '@kbn/share-plugin/common';
|
||||
import {
|
||||
IngestStreamGetResponse,
|
||||
isDisabledLifecycle,
|
||||
|
@ -42,7 +40,6 @@ import { formatIngestionRate } from './helpers/format_bytes';
|
|||
|
||||
export function RetentionMetadata({
|
||||
definition,
|
||||
ilmLocator,
|
||||
lifecycleActions,
|
||||
openEditModal,
|
||||
stats,
|
||||
|
@ -50,7 +47,6 @@ export function RetentionMetadata({
|
|||
statsError,
|
||||
}: {
|
||||
definition: IngestStreamGetResponse;
|
||||
ilmLocator?: LocatorPublic<IlmLocatorParams>;
|
||||
lifecycleActions: Array<{ name: string; action: LifecycleEditAction }>;
|
||||
openEditModal: (action: LifecycleEditAction) => void;
|
||||
stats?: DataStreamStats;
|
||||
|
@ -99,7 +95,7 @@ export function RetentionMetadata({
|
|||
|
||||
const ilmLink = isIlmLifecycle(lifecycle) ? (
|
||||
<EuiBadge color="hollow">
|
||||
<IlmLink lifecycle={lifecycle} ilmLocator={ilmLocator} />
|
||||
<IlmLink lifecycle={lifecycle} />
|
||||
</EuiBadge>
|
||||
) : null;
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
ILM_LOCATOR_ID,
|
||||
IlmLocatorParams,
|
||||
Phases,
|
||||
PolicyFromES,
|
||||
} from '@kbn/index-lifecycle-management-common-shared';
|
||||
import { LocatorPublic } from '@kbn/share-plugin/common';
|
||||
import {
|
||||
IngestStreamGetResponse,
|
||||
IngestStreamLifecycle,
|
||||
|
@ -55,6 +55,7 @@ import {
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { useBoolean } from '@kbn/react-hooks';
|
||||
import useToggle from 'react-use/lib/useToggle';
|
||||
import { useKibana } from '../../../hooks/use_kibana';
|
||||
import { rolloverCondition } from './helpers/rollover_condition';
|
||||
import { useStreamsAppRouter } from '../../../hooks/use_streams_app_router';
|
||||
import { useWiredStreams } from '../../../hooks/use_wired_streams';
|
||||
|
@ -69,7 +70,6 @@ interface ModalOptions {
|
|||
getIlmPolicies: () => Promise<PolicyFromES[]>;
|
||||
definition: IngestStreamGetResponse;
|
||||
updateInProgress: boolean;
|
||||
ilmLocator?: LocatorPublic<IlmLocatorParams>;
|
||||
}
|
||||
|
||||
export function EditLifecycleModal({
|
||||
|
@ -231,9 +231,15 @@ function IlmModal({
|
|||
updateLifecycle,
|
||||
updateInProgress,
|
||||
getIlmPolicies,
|
||||
ilmLocator,
|
||||
definition,
|
||||
}: ModalOptions) {
|
||||
const {
|
||||
dependencies: {
|
||||
start: { share },
|
||||
},
|
||||
} = useKibana();
|
||||
|
||||
const ilmLocator = share.url.locators.get<IlmLocatorParams>(ILM_LOCATOR_ID);
|
||||
const existingLifecycle = definition.stream.ingest.lifecycle;
|
||||
const [selectedPolicy, setSelectedPolicy] = useState(
|
||||
isIlmLifecycle(existingLifecycle) ? existingLifecycle.ilm.policy : undefined
|
||||
|
|
|
@ -171,6 +171,7 @@ export function ControlledEsqlChart<T extends string>({
|
|||
id="y-axis"
|
||||
ticks={3}
|
||||
position={Position.Left}
|
||||
hide
|
||||
tickFormat={yTickFormat}
|
||||
labelFormat={yLabelFormat}
|
||||
/>
|
||||
|
|
|
@ -4,33 +4,152 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { EuiButtonEmpty, EuiFlexGroup, EuiFlexItem, EuiPanel, EuiText } from '@elastic/eui';
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiPanel,
|
||||
EuiText,
|
||||
formatNumber,
|
||||
} from '@elastic/eui';
|
||||
import { css } from '@emotion/css';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import React from 'react';
|
||||
import type { DomainRange } from '@elastic/charts';
|
||||
|
||||
import { AbortableAsyncState } from '@kbn/react-hooks';
|
||||
import type { UnparsedEsqlResponse } from '@kbn/traced-es-client';
|
||||
import React, { useMemo } from 'react';
|
||||
import { IngestStreamGetResponse } from '@kbn/streams-schema';
|
||||
import { computeInterval } from '@kbn/visualization-utils';
|
||||
import moment, { DurationInputArg1, DurationInputArg2 } from 'moment';
|
||||
import { useKibana } from '../../../hooks/use_kibana';
|
||||
import { ControlledEsqlChart } from '../../esql_chart/controlled_esql_chart';
|
||||
import { getIndexPatterns } from '../../../util/hierarchy_helpers';
|
||||
import { StreamsAppSearchBar } from '../../streams_app_search_bar';
|
||||
import { useStreamsAppFetch } from '../../../hooks/use_streams_app_fetch';
|
||||
|
||||
interface StreamChartPanelProps {
|
||||
histogramQueryFetch: AbortableAsyncState<UnparsedEsqlResponse | undefined>;
|
||||
discoverLink?: string;
|
||||
xDomain?: DomainRange;
|
||||
definition: IngestStreamGetResponse;
|
||||
}
|
||||
|
||||
export function StreamChartPanel({
|
||||
histogramQueryFetch,
|
||||
discoverLink,
|
||||
xDomain,
|
||||
}: StreamChartPanelProps) {
|
||||
export function StreamChartPanel({ definition }: StreamChartPanelProps) {
|
||||
const {
|
||||
dependencies: {
|
||||
start: {
|
||||
data,
|
||||
dataViews,
|
||||
streams: { streamsRepositoryClient },
|
||||
share,
|
||||
},
|
||||
},
|
||||
} = useKibana();
|
||||
const {
|
||||
timeRange,
|
||||
setTimeRange,
|
||||
absoluteTimeRange: { start, end },
|
||||
refreshAbsoluteTimeRange,
|
||||
} = data.query.timefilter.timefilter.useTimefilter();
|
||||
|
||||
const indexPatterns = useMemo(() => {
|
||||
return getIndexPatterns(definition?.stream);
|
||||
}, [definition]);
|
||||
|
||||
const discoverLocator = useMemo(
|
||||
() => share.url.locators.get('DISCOVER_APP_LOCATOR'),
|
||||
[share.url.locators]
|
||||
);
|
||||
|
||||
const bucketSize = useMemo(() => computeInterval(timeRange, data), [data, timeRange]);
|
||||
|
||||
const queries = useMemo(() => {
|
||||
if (!indexPatterns) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const baseQuery = `FROM ${indexPatterns.join(', ')}`;
|
||||
|
||||
const histogramQuery = `${baseQuery} | STATS metric = COUNT(*) BY @timestamp = BUCKET(@timestamp, ${bucketSize})`;
|
||||
|
||||
return {
|
||||
baseQuery,
|
||||
histogramQuery,
|
||||
};
|
||||
}, [bucketSize, indexPatterns]);
|
||||
|
||||
const discoverLink = useMemo(() => {
|
||||
if (!discoverLocator || !queries?.baseQuery) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return discoverLocator.getRedirectUrl({
|
||||
query: {
|
||||
esql: queries.baseQuery,
|
||||
},
|
||||
});
|
||||
}, [queries?.baseQuery, discoverLocator]);
|
||||
|
||||
const histogramQueryFetch = useStreamsAppFetch(
|
||||
async ({ signal }) => {
|
||||
if (!queries?.histogramQuery || !indexPatterns) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const existingIndices = await dataViews.getExistingIndices(indexPatterns);
|
||||
|
||||
if (existingIndices.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return streamsRepositoryClient.fetch('POST /internal/streams/esql', {
|
||||
params: {
|
||||
body: {
|
||||
operationName: 'get_histogram_for_stream',
|
||||
query: queries.histogramQuery,
|
||||
start,
|
||||
end,
|
||||
},
|
||||
},
|
||||
signal,
|
||||
});
|
||||
},
|
||||
[indexPatterns, dataViews, streamsRepositoryClient, queries?.histogramQuery, start, end]
|
||||
);
|
||||
|
||||
const docCountFetch = useStreamsAppFetch(
|
||||
async ({ signal }) => {
|
||||
if (!definition) {
|
||||
return undefined;
|
||||
}
|
||||
return streamsRepositoryClient.fetch('GET /internal/streams/{name}/_details', {
|
||||
signal,
|
||||
params: {
|
||||
path: {
|
||||
name: definition.stream.name,
|
||||
},
|
||||
query: {
|
||||
start: String(start),
|
||||
end: String(end),
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
[definition, streamsRepositoryClient, start, end]
|
||||
);
|
||||
|
||||
const [value, unit] = bucketSize.split(' ') as [DurationInputArg1, DurationInputArg2];
|
||||
|
||||
const xDomain = {
|
||||
min: start,
|
||||
max: end,
|
||||
minInterval: moment.duration(value, unit).asMilliseconds(),
|
||||
};
|
||||
|
||||
const docCount = docCountFetch?.value?.details.count;
|
||||
const formattedDocCount = docCount ? formatNumber(docCount, 'decimal0') : '-';
|
||||
|
||||
return (
|
||||
<EuiPanel hasShadow={false} hasBorder>
|
||||
<EuiFlexGroup
|
||||
direction="column"
|
||||
className={css`
|
||||
height: 100%;
|
||||
min-height: 300px;
|
||||
`}
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -38,20 +157,60 @@ export function StreamChartPanel({
|
|||
<EuiFlexItem grow={false}>
|
||||
<EuiText size="s">
|
||||
{i18n.translate('xpack.streams.streamDetailOverview.logRate', {
|
||||
defaultMessage: 'Documents',
|
||||
defaultMessage: '{number} documents',
|
||||
values: {
|
||||
number: formattedDocCount,
|
||||
},
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="streamsDetailOverviewOpenInDiscoverButton"
|
||||
iconType="discoverApp"
|
||||
href={discoverLink}
|
||||
isDisabled={!discoverLink}
|
||||
>
|
||||
{i18n.translate('xpack.streams.streamDetailOverview.openInDiscoverButtonLabel', {
|
||||
defaultMessage: 'Open in Discover',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup>
|
||||
<EuiButtonEmpty
|
||||
data-test-subj="streamsDetailOverviewOpenInDiscoverButton"
|
||||
iconType="discoverApp"
|
||||
href={discoverLink}
|
||||
isDisabled={!discoverLink}
|
||||
>
|
||||
{i18n.translate('xpack.streams.streamDetailOverview.openInDiscoverButtonLabel', {
|
||||
defaultMessage: 'Open in Discover',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
<StreamsAppSearchBar
|
||||
onQuerySubmit={({ dateRange }, isUpdate) => {
|
||||
if (!isUpdate) {
|
||||
if (!refreshAbsoluteTimeRange()) {
|
||||
// if absolute time range didn't change, we need to manually refresh the histogram
|
||||
// otherwise it will be refreshed by the changed absolute time range
|
||||
histogramQueryFetch.refresh();
|
||||
docCountFetch.refresh();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (dateRange) {
|
||||
setTimeRange({
|
||||
from: dateRange.from,
|
||||
to: dateRange?.to,
|
||||
mode: dateRange.mode,
|
||||
});
|
||||
}
|
||||
}}
|
||||
onRefresh={() => {
|
||||
histogramQueryFetch.refresh();
|
||||
docCountFetch.refresh();
|
||||
}}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.streams.entityDetailOverview.searchBarPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Filter data by using KQL',
|
||||
}
|
||||
)}
|
||||
dateRangeFrom={timeRange.from}
|
||||
dateRangeTo={timeRange.to}
|
||||
/>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow>
|
||||
|
|
|
@ -17,31 +17,19 @@ import { css } from '@emotion/css';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import React, { ReactNode } from 'react';
|
||||
import { IngestStreamGetResponse, isDslLifecycle, isIlmLifecycle } from '@kbn/streams-schema';
|
||||
import { IlmLocatorParams } from '@kbn/index-lifecycle-management-common-shared';
|
||||
|
||||
import { LocatorPublic } from '@kbn/share-plugin/public';
|
||||
import type { StreamDetailsResponse } from '@kbn/streams-plugin/server/routes/internal/streams/crud/route';
|
||||
import { IlmLink } from '../../data_management/stream_detail_lifecycle/ilm_link';
|
||||
import {
|
||||
formatBytes,
|
||||
formatIngestionRate,
|
||||
} from '../../data_management/stream_detail_lifecycle/helpers/format_bytes';
|
||||
import { DataStreamStats } from '../../data_management/stream_detail_lifecycle/hooks/use_data_stream_stats';
|
||||
import { useDataStreamStats } from '../../data_management/stream_detail_lifecycle/hooks/use_data_stream_stats';
|
||||
|
||||
interface StreamStatsPanelProps {
|
||||
definition?: IngestStreamGetResponse;
|
||||
dataStreamStats?: DataStreamStats;
|
||||
docCount?: StreamDetailsResponse;
|
||||
ilmLocator?: LocatorPublic<IlmLocatorParams>;
|
||||
definition: IngestStreamGetResponse;
|
||||
}
|
||||
|
||||
const RetentionDisplay = ({
|
||||
definition,
|
||||
ilmLocator,
|
||||
}: {
|
||||
definition?: IngestStreamGetResponse;
|
||||
ilmLocator?: LocatorPublic<IlmLocatorParams>;
|
||||
}) => {
|
||||
const RetentionDisplay = ({ definition }: { definition: IngestStreamGetResponse }) => {
|
||||
if (!definition) return <>-</>;
|
||||
|
||||
if (isDslLifecycle(definition.effective_lifecycle)) {
|
||||
|
@ -56,7 +44,7 @@ const RetentionDisplay = ({
|
|||
}
|
||||
|
||||
if (isIlmLifecycle(definition.effective_lifecycle)) {
|
||||
return <IlmLink lifecycle={definition.effective_lifecycle} ilmLocator={ilmLocator} />;
|
||||
return <IlmLink lifecycle={definition.effective_lifecycle} />;
|
||||
}
|
||||
|
||||
return <>-</>;
|
||||
|
@ -97,12 +85,8 @@ const StatItem = ({ label, value, withBorder = false }: StatItemProps) => {
|
|||
);
|
||||
};
|
||||
|
||||
export function StreamStatsPanel({
|
||||
definition,
|
||||
dataStreamStats,
|
||||
docCount,
|
||||
ilmLocator,
|
||||
}: StreamStatsPanelProps) {
|
||||
export function StreamStatsPanel({ definition }: StreamStatsPanelProps) {
|
||||
const dataStreamStats = useDataStreamStats({ definition }).stats;
|
||||
const retentionLabel = i18n.translate('xpack.streams.entityDetailOverview.retention', {
|
||||
defaultMessage: 'Data retention',
|
||||
});
|
||||
|
@ -128,7 +112,7 @@ export function StreamStatsPanel({
|
|||
{retentionLabel}
|
||||
</EuiText>
|
||||
<EuiText size="m">
|
||||
<RetentionDisplay definition={definition} ilmLocator={ilmLocator} />
|
||||
<RetentionDisplay definition={definition} />
|
||||
</EuiText>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
|
@ -138,24 +122,15 @@ export function StreamStatsPanel({
|
|||
<EuiFlexGroup>
|
||||
<StatItem
|
||||
label={documentCountLabel}
|
||||
value={docCount ? formatNumber(docCount.details.count || 0, 'decimal0') : '-'}
|
||||
value={
|
||||
dataStreamStats ? formatNumber(dataStreamStats.totalDocs || 0, 'decimal0') : '-'
|
||||
}
|
||||
/>
|
||||
<StatItem
|
||||
label={
|
||||
<>
|
||||
{storageSizeLabel}
|
||||
<EuiIconTip
|
||||
content={i18n.translate('xpack.streams.streamDetailOverview.sizeTip', {
|
||||
defaultMessage:
|
||||
'Estimated size based on the number of documents in the current time range and the total size of the stream.',
|
||||
})}
|
||||
position="right"
|
||||
/>
|
||||
</>
|
||||
}
|
||||
label={storageSizeLabel}
|
||||
value={
|
||||
dataStreamStats && docCount
|
||||
? formatBytes(getStorageSizeForTimeRange(dataStreamStats, docCount))
|
||||
dataStreamStats && dataStreamStats.sizeBytes
|
||||
? formatBytes(dataStreamStats.sizeBytes)
|
||||
: '-'
|
||||
}
|
||||
withBorder
|
||||
|
@ -187,19 +162,3 @@ export function StreamStatsPanel({
|
|||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
function getStorageSizeForTimeRange(
|
||||
dataStreamStats: DataStreamStats,
|
||||
docCount: StreamDetailsResponse
|
||||
) {
|
||||
const storageSize = dataStreamStats.sizeBytes;
|
||||
const totalCount = dataStreamStats.totalDocs;
|
||||
const countForTimeRange = docCount.details.count;
|
||||
if (!storageSize || !totalCount || !countForTimeRange) {
|
||||
return 0;
|
||||
}
|
||||
if (!totalCount) {
|
||||
return 0;
|
||||
}
|
||||
return storageSize * (countForTimeRange / totalCount);
|
||||
}
|
||||
|
|
|
@ -8,15 +8,7 @@ import { EuiFlexGroup, EuiFlexItem } from '@elastic/eui';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import React, { useMemo } from 'react';
|
||||
import { IngestStreamGetResponse, isWiredStreamDefinition } from '@kbn/streams-schema';
|
||||
import { ILM_LOCATOR_ID, IlmLocatorParams } from '@kbn/index-lifecycle-management-common-shared';
|
||||
|
||||
import { computeInterval } from '@kbn/visualization-utils';
|
||||
import moment, { DurationInputArg1, DurationInputArg2 } from 'moment';
|
||||
import { useKibana } from '../../hooks/use_kibana';
|
||||
import { useStreamsAppFetch } from '../../hooks/use_streams_app_fetch';
|
||||
import { StreamsAppSearchBar } from '../streams_app_search_bar';
|
||||
import { getIndexPatterns } from '../../util/hierarchy_helpers';
|
||||
import { useDataStreamStats } from '../data_management/stream_detail_lifecycle/hooks/use_data_stream_stats';
|
||||
import { QuickLinks } from './quick_links';
|
||||
import { ChildStreamList } from './child_stream_list';
|
||||
import { StreamStatsPanel } from './components/stream_stats_panel';
|
||||
|
@ -24,112 +16,6 @@ import { StreamChartPanel } from './components/stream_chart_panel';
|
|||
import { TabsPanel } from './components/tabs_panel';
|
||||
|
||||
export function StreamDetailOverview({ definition }: { definition: IngestStreamGetResponse }) {
|
||||
const {
|
||||
dependencies: {
|
||||
start: {
|
||||
data,
|
||||
dataViews,
|
||||
streams: { streamsRepositoryClient },
|
||||
share,
|
||||
},
|
||||
},
|
||||
} = useKibana();
|
||||
|
||||
const {
|
||||
timeRange,
|
||||
setTimeRange,
|
||||
absoluteTimeRange: { start, end },
|
||||
refreshAbsoluteTimeRange,
|
||||
} = data.query.timefilter.timefilter.useTimefilter();
|
||||
|
||||
const indexPatterns = useMemo(() => {
|
||||
return getIndexPatterns(definition?.stream);
|
||||
}, [definition]);
|
||||
|
||||
const discoverLocator = useMemo(
|
||||
() => share.url.locators.get('DISCOVER_APP_LOCATOR'),
|
||||
[share.url.locators]
|
||||
);
|
||||
|
||||
const bucketSize = useMemo(() => computeInterval(timeRange, data), [data, timeRange]);
|
||||
|
||||
const queries = useMemo(() => {
|
||||
if (!indexPatterns) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const baseQuery = `FROM ${indexPatterns.join(', ')}`;
|
||||
|
||||
const histogramQuery = `${baseQuery} | STATS metric = COUNT(*) BY @timestamp = BUCKET(@timestamp, ${bucketSize})`;
|
||||
|
||||
return {
|
||||
baseQuery,
|
||||
histogramQuery,
|
||||
};
|
||||
}, [bucketSize, indexPatterns]);
|
||||
|
||||
const discoverLink = useMemo(() => {
|
||||
if (!discoverLocator || !queries?.baseQuery) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return discoverLocator.getRedirectUrl({
|
||||
query: {
|
||||
esql: queries.baseQuery,
|
||||
},
|
||||
});
|
||||
}, [queries?.baseQuery, discoverLocator]);
|
||||
|
||||
const histogramQueryFetch = useStreamsAppFetch(
|
||||
async ({ signal }) => {
|
||||
if (!queries?.histogramQuery || !indexPatterns) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const existingIndices = await dataViews.getExistingIndices(indexPatterns);
|
||||
|
||||
if (existingIndices.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return streamsRepositoryClient.fetch('POST /internal/streams/esql', {
|
||||
params: {
|
||||
body: {
|
||||
operationName: 'get_histogram_for_stream',
|
||||
query: queries.histogramQuery,
|
||||
start,
|
||||
end,
|
||||
},
|
||||
},
|
||||
signal,
|
||||
});
|
||||
},
|
||||
[indexPatterns, dataViews, streamsRepositoryClient, queries?.histogramQuery, start, end]
|
||||
);
|
||||
|
||||
const docCountFetch = useStreamsAppFetch(
|
||||
async ({ signal }) => {
|
||||
if (!definition) {
|
||||
return undefined;
|
||||
}
|
||||
return streamsRepositoryClient.fetch('GET /internal/streams/{name}/_details', {
|
||||
signal,
|
||||
params: {
|
||||
path: {
|
||||
name: definition.stream.name,
|
||||
},
|
||||
query: {
|
||||
start: String(start),
|
||||
end: String(end),
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
[definition, streamsRepositoryClient, start, end]
|
||||
);
|
||||
|
||||
const dataStreamStats = useDataStreamStats({ definition });
|
||||
|
||||
const tabs = useMemo(
|
||||
() => [
|
||||
...(definition && isWiredStreamDefinition(definition.stream)
|
||||
|
@ -154,72 +40,18 @@ export function StreamDetailOverview({ definition }: { definition: IngestStreamG
|
|||
[definition]
|
||||
);
|
||||
|
||||
const ilmLocator = share.url.locators.get<IlmLocatorParams>(ILM_LOCATOR_ID);
|
||||
|
||||
const [value, unit] = bucketSize.split(' ') as [DurationInputArg1, DurationInputArg2];
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiFlexGroup direction="column" gutterSize="m">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup direction="row" justifyContent="flexEnd">
|
||||
<EuiFlexItem grow>
|
||||
<StreamsAppSearchBar
|
||||
onQuerySubmit={({ dateRange }, isUpdate) => {
|
||||
dataStreamStats.refresh();
|
||||
if (!isUpdate) {
|
||||
if (!refreshAbsoluteTimeRange()) {
|
||||
// if absolute time range didn't change, we need to manually refresh the histogram
|
||||
// otherwise it will be refreshed by the changed absolute time range
|
||||
histogramQueryFetch.refresh();
|
||||
docCountFetch.refresh();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (dateRange) {
|
||||
setTimeRange({ from: dateRange.from, to: dateRange?.to, mode: dateRange.mode });
|
||||
}
|
||||
}}
|
||||
onRefresh={() => {
|
||||
histogramQueryFetch.refresh();
|
||||
docCountFetch.refresh();
|
||||
}}
|
||||
placeholder={i18n.translate(
|
||||
'xpack.streams.entityDetailOverview.searchBarPlaceholder',
|
||||
{
|
||||
defaultMessage: 'Filter data by using KQL',
|
||||
}
|
||||
)}
|
||||
dateRangeFrom={timeRange.from}
|
||||
dateRangeTo={timeRange.to}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<StreamStatsPanel
|
||||
definition={definition}
|
||||
dataStreamStats={dataStreamStats.stats}
|
||||
docCount={docCountFetch.value}
|
||||
ilmLocator={ilmLocator}
|
||||
/>
|
||||
<StreamStatsPanel definition={definition} />
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem grow>
|
||||
<EuiFlexGroup direction="row" gutterSize="m">
|
||||
<EuiFlexItem grow={4}>{definition && <TabsPanel tabs={tabs} />}</EuiFlexItem>
|
||||
<EuiFlexItem grow={8}>
|
||||
<StreamChartPanel
|
||||
histogramQueryFetch={histogramQueryFetch}
|
||||
discoverLink={discoverLink}
|
||||
xDomain={{
|
||||
min: start,
|
||||
max: end,
|
||||
minInterval: moment.duration(value, unit).asMilliseconds(),
|
||||
}}
|
||||
/>
|
||||
<StreamChartPanel definition={definition} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue