Skip to content

Commit

Permalink
[Uptime] Added trends chart and deep link to exp view on waterfall st…
Browse files Browse the repository at this point in the history
…ep metric markers (elastic#114068)

* added trend chart and link to exp view

* Added step level filtering/breakdowns

* fix type

* make step and monitor name single select

* fix test

* added step filters

* pr feedback

* hide step filter for non step metrics

* pr feedback

* remove step def when it's not applicable

* ^^also for step name breakdown

* Update x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/breakdown/breakdowns.tsx

Co-authored-by: Dominique Clarke <[email protected]>

* use side effct

* update monitor filter

* update all_value usage

* refactor

* fix type

* use last value operation

* use last 48 intervals

Co-authored-by: Dominique Clarke <[email protected]>
  • Loading branch information
2 people authored and kibanamachine committed Oct 19, 2021
1 parent c7b98c5 commit 38b0da9
Show file tree
Hide file tree
Showing 13 changed files with 293 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
AvgIndexPatternColumn,
MedianIndexPatternColumn,
PercentileIndexPatternColumn,
LastValueIndexPatternColumn,
OperationType,
PersistedIndexPatternLayer,
RangeIndexPatternColumn,
Expand Down Expand Up @@ -219,13 +220,52 @@ export class LensAttributes {
columnFilter,
});
}
if (operationType === 'last_value') {
return this.getLastValueOperationColumn({
sourceField,
operationType,
label,
seriesConfig,
columnFilter,
});
}
if (operationType?.includes('th')) {
return this.getPercentileNumberColumn(sourceField, operationType, seriesConfig!);
}
}
return this.getNumberRangeColumn(sourceField, seriesConfig!, label);
}

getLastValueOperationColumn({
sourceField,
label,
seriesConfig,
operationType,
columnFilter,
}: {
sourceField: string;
operationType: 'last_value';
label?: string;
seriesConfig: SeriesConfig;
columnFilter?: ColumnFilter;
}): LastValueIndexPatternColumn {
return {
...buildNumberColumn(sourceField),
operationType,
label: i18n.translate('xpack.observability.expView.columns.operation.label', {
defaultMessage: '{operationType} of {sourceField}',
values: {
sourceField: label || seriesConfig.labels[sourceField],
operationType: capitalize(operationType),
},
}),
filter: columnFilter,
params: {
sortField: '@timestamp',
},
};
}

getNumberOperationColumn({
sourceField,
label,
Expand Down Expand Up @@ -622,6 +662,12 @@ export class LensAttributes {

const label = timeShift ? `${mainYAxis.label}(${timeShift})` : mainYAxis.label;

let filterQuery = columnFilter || mainYAxis.filter?.query;

if (columnFilter && mainYAxis.filter?.query) {
filterQuery = `${columnFilter} and ${mainYAxis.filter.query}`;
}

layers[layerId] = {
columnOrder: [
`x-axis-column-${layerId}`,
Expand All @@ -637,9 +683,7 @@ export class LensAttributes {
...mainYAxis,
label,
filter: {
query: mainYAxis.filter
? `${columnFilter} and ${mainYAxis.filter.query}`
: columnFilter,
query: filterQuery ?? '',
language: 'kuery',
},
...(timeShift ? { timeShift } : {}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,44 +111,48 @@ export function getSyntheticsKPIConfig({ indexPattern }: ConfigProps): SeriesCon
field: SYNTHETICS_LCP,
id: SYNTHETICS_LCP,
columnType: OPERATION_COLUMN,
columnFilters: [STEP_METRIC_FILTER],
columnFilters: getStepMetricColumnFilter(SYNTHETICS_LCP),
},
{
label: FCP_LABEL,
field: SYNTHETICS_FCP,
id: SYNTHETICS_FCP,
columnType: OPERATION_COLUMN,
columnFilters: [STEP_METRIC_FILTER],
columnFilters: getStepMetricColumnFilter(SYNTHETICS_FCP),
},
{
label: DCL_LABEL,
field: SYNTHETICS_DCL,
id: SYNTHETICS_DCL,
columnType: OPERATION_COLUMN,
columnFilters: [STEP_METRIC_FILTER],
columnFilters: getStepMetricColumnFilter(SYNTHETICS_DCL),
},
{
label: DOCUMENT_ONLOAD_LABEL,
field: SYNTHETICS_DOCUMENT_ONLOAD,
id: SYNTHETICS_DOCUMENT_ONLOAD,
columnType: OPERATION_COLUMN,
columnFilters: [STEP_METRIC_FILTER],
columnFilters: getStepMetricColumnFilter(SYNTHETICS_DOCUMENT_ONLOAD),
},
{
label: CLS_LABEL,
field: SYNTHETICS_CLS,
id: SYNTHETICS_CLS,
columnType: OPERATION_COLUMN,
columnFilters: [STEP_METRIC_FILTER],
columnFilters: getStepMetricColumnFilter(SYNTHETICS_CLS),
},
],
labels: { ...FieldLabels, [SUMMARY_UP]: UP_LABEL, [SUMMARY_DOWN]: DOWN_LABEL },
};
}

const STEP_METRIC_FILTER: ColumnFilter = {
language: 'kuery',
query: `synthetics.type: step/metrics`,
const getStepMetricColumnFilter = (field: string): ColumnFilter[] => {
return [
{
language: 'kuery',
query: `synthetics.type: step/metrics and ${field}: *`,
},
];
};

const STEP_END_FILTER: ColumnFilter = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { AllSeries, useTheme } from '../../../..';
import { LayerConfig, LensAttributes } from '../configurations/lens_attributes';
import { ReportViewType } from '../types';
import { getLayerConfigs } from '../hooks/use_lens_attributes';
import { LensPublicStart } from '../../../../../../lens/public';
import { LensPublicStart, XYState } from '../../../../../../lens/public';
import { OperationTypeComponent } from '../series_editor/columns/operation_type_select';
import { IndexPatternState } from '../hooks/use_app_index_pattern';

Expand All @@ -22,6 +22,8 @@ export interface ExploratoryEmbeddableProps {
appendTitle?: JSX.Element;
title: string | JSX.Element;
showCalculationMethod?: boolean;
axisTitlesVisibility?: XYState['axisTitlesVisibilitySettings'];
legendIsVisible?: boolean;
}

export interface ExploratoryEmbeddableComponentProps extends ExploratoryEmbeddableProps {
Expand All @@ -37,6 +39,8 @@ export default function Embeddable({
appendTitle,
indexPatterns,
lens,
axisTitlesVisibility,
legendIsVisible,
showCalculationMethod = false,
}: ExploratoryEmbeddableComponentProps) {
const LensComponent = lens?.EmbeddableComponent;
Expand All @@ -57,11 +61,20 @@ export default function Embeddable({
return <EuiText>No lens component</EuiText>;
}

const attributesJSON = lensAttributes.getJSON();

(attributesJSON.state.visualization as XYState).axisTitlesVisibilitySettings =
axisTitlesVisibility;

if (typeof legendIsVisible !== 'undefined') {
(attributesJSON.state.visualization as XYState).legend.isVisible = legendIsVisible;
}

return (
<Wrapper>
<EuiFlexGroup>
<EuiFlexGroup alignItems="center">
<EuiFlexItem>
<EuiTitle size="s">
<EuiTitle size="xs">
<h3>{title}</h3>
</EuiTitle>
</EuiFlexItem>
Expand All @@ -81,7 +94,7 @@ export default function Embeddable({
id="exploratoryView"
style={{ height: '100%' }}
timeRange={series?.time}
attributes={lensAttributes.getJSON()}
attributes={attributesJSON}
onBrushEnd={({ range }) => {}}
/>
</Wrapper>
Expand All @@ -92,7 +105,7 @@ const Wrapper = styled.div`
height: 100%;
&&& {
> :nth-child(2) {
height: calc(100% - 56px);
height: calc(100% - 32px);
}
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ export function OperationTypeComponent({
defaultMessage: 'Sum',
}),
},
{
value: 'last_value' as OperationType,
inputDisplay: i18n.translate('xpack.observability.expView.operationType.lastValue', {
defaultMessage: 'Last value',
}),
},
{
value: '75th' as OperationType,
inputDisplay: i18n.translate('xpack.observability.expView.operationType.75thPercentile', {
Expand Down
4 changes: 4 additions & 0 deletions x-pack/plugins/uptime/common/runtime_types/ping/synthetics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ export const JourneyStepType = t.intersection([
name: t.string,
status: t.string,
type: t.string,
timespan: t.type({
gte: t.string,
lt: t.string,
}),
}),
synthetics: t.partial({
error: t.partial({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ export const StepDetailContainer: React.FC<Props> = ({ checkGroup, stepIndex })
</EuiFlexGroup>
)}
{journey && activeStep && !journey.loading && (
<WaterfallChartContainer checkGroup={checkGroup} stepIndex={stepIndex} />
<WaterfallChartContainer
checkGroup={checkGroup}
stepIndex={stepIndex}
activeStep={activeStep}
/>
)}
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,19 @@ import { networkEventsSelector } from '../../../../../state/selectors';
import { WaterfallChartWrapper } from './waterfall_chart_wrapper';
import { extractItems } from './data_formatting';
import { useStepWaterfallMetrics } from '../use_step_waterfall_metrics';
import { JourneyStep } from '../../../../../../common/runtime_types';

export const NO_DATA_TEXT = i18n.translate('xpack.uptime.synthetics.stepDetail.waterfallNoData', {
defaultMessage: 'No waterfall data could be found for this step',
});

interface Props {
checkGroup: string;
activeStep?: JourneyStep;
stepIndex: number;
}

export const WaterfallChartContainer: React.FC<Props> = ({ checkGroup, stepIndex }) => {
export const WaterfallChartContainer: React.FC<Props> = ({ checkGroup, stepIndex, activeStep }) => {
const dispatch = useDispatch();

useEffect(() => {
Expand Down Expand Up @@ -79,6 +81,7 @@ export const WaterfallChartContainer: React.FC<Props> = ({ checkGroup, stepIndex
data={extractItems(networkEvents.events)}
markerItems={metrics}
total={networkEvents.total}
activeStep={activeStep}
/>
)}
{waterfallLoaded && hasEvents && !isWaterfallSupported && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { WaterfallFilter } from './waterfall_filter';
import { WaterfallFlyout } from './waterfall_flyout';
import { WaterfallSidebarItem } from './waterfall_sidebar_item';
import { MarkerItems } from '../../waterfall/context/waterfall_chart';
import { JourneyStep } from '../../../../../../common/runtime_types';

export const renderLegendItem: RenderItem<LegendItem> = (item) => {
return (
Expand All @@ -26,11 +27,17 @@ export const renderLegendItem: RenderItem<LegendItem> = (item) => {

interface Props {
total: number;
activeStep?: JourneyStep;
data: NetworkItems;
markerItems?: MarkerItems;
}

export const WaterfallChartWrapper: React.FC<Props> = ({ data, total, markerItems }) => {
export const WaterfallChartWrapper: React.FC<Props> = ({
data,
total,
markerItems,
activeStep,
}) => {
const [query, setQuery] = useState<string>('');
const [activeFilters, setActiveFilters] = useState<string[]>([]);
const [onlyHighlighted, setOnlyHighlighted] = useState(false);
Expand Down Expand Up @@ -109,6 +116,7 @@ export const WaterfallChartWrapper: React.FC<Props> = ({ data, total, markerItem

return (
<WaterfallProvider
activeStep={activeStep}
markerItems={markerItems}
totalNetworkRequests={total}
fetchedNetworkRequests={networkData.length}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* 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 { EuiButtonIcon, EuiIcon, EuiPopover } from '@elastic/eui';
import { WaterfallMarkerTrend } from './waterfall_marker_trend';

export function WaterfallMarkerIcon({ field, label }: { field: string; label: string }) {
const [isOpen, setIsOpen] = useState(false);

if (!field) {
return <EuiIcon type="dot" size="l" />;
}

return (
<EuiPopover
isOpen={isOpen}
closePopover={() => setIsOpen(false)}
anchorPosition="downLeft"
panelStyle={{ paddingBottom: 0, paddingLeft: 4 }}
zIndex={100}
button={
<EuiButtonIcon
iconType="dot"
iconSize="l"
color="text"
onClick={() => setIsOpen((prevState) => !prevState)}
/>
}
>
<WaterfallMarkerTrend title={label} field={field} />
</EuiPopover>
);
}
Loading

0 comments on commit 38b0da9

Please sign in to comment.